{Real file}
unit fmt_Real;
{$include compilers.inc}

interface
uses wat_api;

function ReadReal(var Info:tSongInfo):boolean; cdecl;

implementation
uses windows,common,io,tags,srv_format;

const
  blk_RMF = $464D522E; // '.RMF'
  blkPROP = $504F5250; // 'PROP'
  blkCONT = $544E4F43; // 'CONT' - content
  blkMDPR = $5250444D; // 'MDPR'
  blkDATA = $41544144; // 'DATA'
  blkINDX = $58444E49; // 'INDX'
  blkRMMD = $444D4D52; // 'RMMD' - comment block
  blkRMJD = $444A4D52; // 'RMJD'
  blkRMJE = $454A4D52; // 'RMJE'
type
  tChunk = packed record
    ID:dword;
    len:dword; //with Chunk;
  end;

type
  pPROP = ^tPROP;
  tPROP = packed record
    w1          :word;
    l1,l2       :dword;
    l3,l4       :dword;
    un1         :dword; // or 2+2
    filetotal   :dword; // msec
    l5          :dword;
    InfoDataSize:dword;
    Infosize    :dword;
    w2          :word;  // always 2 ?
    w           :word;  // chunks+1?
  end;

procedure SkipStr(var p:PAnsiChar;alen:integer);
var
  llen:integer;
begin
  if alen=2 then
    llen:=(ord(p[0]) shl 8)+ord(p[1])
  else
    llen:=ord(p[0]);
  inc(p,alen);
//  if llen>0 then
    inc(p,llen);
end;

function ReadStr(var p:PAnsiChar;alen:integer):PAnsiChar;
var
  llen:integer;
begin
  if alen=2 then
    llen:=(ord(p[0]) shl 8)+ord(p[1])
  else
    llen:=ord(p[0]);
  inc(p,alen);
  if llen>0 then
  begin
    mGetMem(result,llen+1);
    move(p^,result^,llen);
    result[llen]:=#0;
    inc(p,llen);
  end
  else
    result:=nil;
end;

function GetWord(var p:PAnsiChar):word;
begin
  result:=(ord(p[0]) shl 8)+ord(p[1]);
  inc(p,2);
end;

function GetLong(var p:PAnsiChar):dword;
begin
  result:=(ord(p[0]) shl 24)+(ord(p[1]) shl 16)+(ord(p[2]) shl 8)+ord(p[3]);
  inc(p,4);
end;

function ReadReal(var Info:tSongInfo):boolean; cdecl;
var
  f:THANDLE;
  chunk:tChunk;
  p,buf:PAnsiChar;
  ls:PAnsiChar;
  ver:integer;
  fsize:cardinal;
begin
  result:=false;
  f:=Reset(Info.mfile);
  if f=THANDLE(INVALID_HANDLE_VALUE) then
    exit;
  fsize:=FileSize(f);
  while FilePos(f)<fsize do
  begin
    BlockRead(f,chunk,SizeOf(chunk));
    chunk.len:=BSwap(chunk.len);
    if (not (AnsiChar(chunk.ID and $FF) in ['A'..'Z','a'..'z','.'])) or
      (chunk.len<SizeOf(chunk)) then
      break;
    if (chunk.ID=blkPROP) or (chunk.ID=blkCONT) or (chunk.ID=blkMDPR) then
    begin
      mGetMem(buf,chunk.len-SizeOf(chunk));
      p:=buf;
      BlockRead(f,buf^,chunk.len-SizeOf(chunk));
      if chunk.ID=blkPROP then
      begin
        inc(p,22);
{
        GetWord(p); // 0
        GetLong(p); // min total bps?
        GetLong(p); // max total bps?
        GetLong(p); // a samples?
        GetLong(p); // b samples?
        GetLong(p); // c (samplesize?)
}
        Info.total:=GetLong(p) div 1000;
{
        GetLong(p); // X
        GetLong(p); // used data size (w/o INDX and tags)
        GetLong(p); // offset to DATA chunk
        GetWord(p); // number of MDPR chunks
        GetWord(p); // 2-9, 3-11
}
      end
      else if chunk.ID=blkCONT then
      begin
        SkipStr(p,2); // rating?
        ls:=ReadStr(p,2); // title
        AnsiToWide(ls,Info.title);
        mFreeMem(ls);
        ls:=ReadStr(p,2); // author
        AnsiToWide(ls,Info.artist);
        mFreeMem(ls);
{
        SkipStr(p,2); // copyright
        SkipStr(p,2); // description
}
      end
      else if chunk.ID=blkMDPR then
      begin //stream or logical info
        GetLong(p); // MDPR block number (from 0)
        if Info.kbps=0 then
          Info.kbps:=GetLong(p) div 1000 // a stream bps
        else
          GetLong(p); // a stream bps
        inc(p,24);
{
        GetLong(p); // a stream bps
        GetLong(p); // b smp
        GetLong(p); // b smp
        GetLong(p); // 0
        GetLong(p); // X
        GetLong(p); //StreamLen
}
        SkipStr(p,1);     //BlockName - usually 'Audio Stream'
        ls:=ReadStr(p,1); //BlockMime
        if StrCmp(ls,'audio/x-pn-realaudio')=0 then
        begin
          inc(p,20);
{
          GetLong(p); // stream dataLen;
          GetLong(p); // type = $2E$72$61$FD
          GetWord(p); // binary version? [4/5]
          GetWord(p); // 0
          GetLong(p); // last byte = ASC version $2E$72$61$($30+ver.)
          GetWord(p);
          GetWord(p);
}
          ver:=GetWord(p); // =version?
          inc(p,30);
{
          GetLong(p); // datalen incl +2 (ver?)
          GetWord(p); // ? 18,19,1,7
          GetWord(p); // 0
          GetWord(p); // un1
          GetLong(p); //
          GetLong(p); //
          GetLong(p); //
          GetWord(p); //
          GetWord(p); // un2=un1
          GetWord(p); //
          GetWord(p); // 60 [93] (0 for ra4)
}
          if ver=5 then
          begin
            Info.khz:=GetLong(p) div 1000;
            inc(p,8);
{
            GetLong(p); // equ KHZ
            GetLong(p); // bits/channel?
}
            Info.channels:=GetWord(p);
{
            GetLong(p); // 'genr'
            GetLong(p); // codec name
            GetWord(p); // $01 $07
            GetLong(p); // 0
            GetWord(p); // channel data len (16-stereo,8-mono)
            GetWord(p); // $01 $00
            GetWord(p); // $00 $03[mono-2]
            GetWord(p); // $04 [mono-2] $00
            GetWord(p); //
            if Info.channels=2 then
            begin
              GetLong(p); // 0
              GetWord(p); // 01
              GetWord(p); // 03
            end
}
          end
          else
          begin
            Info.khz:=GetWord(p) div 1000;
            GetLong(p); // bits/channel?
            Info.channels:=GetWord(p);
{
            SkipStr(p,1); // codec
            SkipStr(p,1); // codec
            GetWord(p); // $01 $07
            inc(p,5);
}
          end
        end;
{
        if StrCmp(tmpstr,'logical-fileinfo')=0 then
        begin
          GetLong(p); // a block len w/o
          GetLong(p); // a block len with
          GetLong(p); // 0
          GetLong(p); // number of nodes
          for i:=0 to Nodes-1 do
          begin
            GetLong(p);     // node len with len dword
            GetWord(p);
            SkipStr(p,1);     // node name
            GetLong(p);     // value type? 2 - asciiz
            SkipStr(p,2); //node value
          end;
        end;
}

//        if StrCmp(tmpstr,'Video Stream')=0 then
        if StrCmp(ls,'video/x-pn-realvideo')=0 then
        begin
          GetLong(p); //stream dataLen;
          Info.kbps:=GetLong(p); //override kbps
          GetLong(p); //VIDO=vidtype
          Info.codec:=ord(p[0])+(ord(p[1]) shl 8)+
              (ord(p[2]) shl 16)+(ord(p[3]) shl 24); //codec ex.'RV30'
          inc(p,4);
          Info.width:=GetWord(p); //width
          Info.height:=GetWord(p); //height
          GetWord(p); //fps or colordeep
          GetWord(p); //alt.width ?
          GetWord(p); //alt. height ?
          Info.fps:=GetWord(p)*100; //fps
          {}
        end;

        mFreeMem(ls);
      end;
      mFreeMem(buf);
    end
    else if chunk.ID=blkRMMD then //comment
    begin
      Skip(f,chunk.len-SizeOf(chunk));
{
    BlockRead(f,chunk,SizeOf(chunk)); //RJMD
    chunk.len:=BSwap(chunk.len);
    BlockRead(f,tmplong,4);


    BlockRead(f,chunk,SizeOf(chunk)); //RMJE
    chunk.len:=BSwap(chunk.len);
}
    end
    else
    begin
      if chunk.ID=blk_RMF then
        if FilePos(f)<>SizeOf(chunk) then // channels-1: ofs=$0A
          break;
      Skip(f,chunk.len-SizeOf(chunk));
    end;
  end;
  ReadID3v1(f,Info);
  CloseHandle(f);
  result:=true;
end;

var
  LocalFormatLinkRM,
  LocalFormatLinkRA,
  LocalFormatLinkRAM:twFormat;

procedure InitLink;
begin
  LocalFormatLinkRM.Next:=FormatLink;

  LocalFormatLinkRM.This.proc :=@ReadReal;
  LocalFormatLinkRM.This.ext  :='RM';
  LocalFormatLinkRM.This.flags:=WAT_OPT_VIDEO;

  FormatLink:=@LocalFormatLinkRM;

  LocalFormatLinkRA.Next:=FormatLink;

  LocalFormatLinkRA.This.proc :=@ReadReal;
  LocalFormatLinkRA.This.ext  :='RA';
  LocalFormatLinkRA.This.flags:=WAT_OPT_VIDEO;

  FormatLink:=@LocalFormatLinkRA;

  LocalFormatLinkRAM.Next:=FormatLink;

  LocalFormatLinkRAM.This.proc :=@ReadReal;
  LocalFormatLinkRAM.This.ext  :='RAM';
  LocalFormatLinkRAM.This.flags:=WAT_OPT_VIDEO;

  FormatLink:=@LocalFormatLinkRAM;
end;

initialization
  InitLink;

end.