summaryrefslogtreecommitdiff
path: root/plugins/Watrack/formats
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-10-08 18:43:29 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-10-08 18:43:29 +0000
commit864081102a5f252415f41950b3039a896b4ae9c5 (patch)
treec6b764651e9dd1f8f53b98eab05f16ba4a492a79 /plugins/Watrack/formats
parentdb5149b48346c417e18add5702a9dfe7f6e28dd0 (diff)
Awkwars's plugins - welcome to our trunk
git-svn-id: http://svn.miranda-ng.org/main/trunk@1822 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Watrack/formats')
-rw-r--r--plugins/Watrack/formats/fmt_aac.pas93
-rw-r--r--plugins/Watrack/formats/fmt_ape.pas137
-rw-r--r--plugins/Watrack/formats/fmt_avi.pas295
-rw-r--r--plugins/Watrack/formats/fmt_dummy.pas46
-rw-r--r--plugins/Watrack/formats/fmt_flv.pas334
-rw-r--r--plugins/Watrack/formats/fmt_m4a.pas378
-rw-r--r--plugins/Watrack/formats/fmt_mkv.pas235
-rw-r--r--plugins/Watrack/formats/fmt_mp3.pas460
-rw-r--r--plugins/Watrack/formats/fmt_mpc.pas90
-rw-r--r--plugins/Watrack/formats/fmt_ofr.pas74
-rw-r--r--plugins/Watrack/formats/fmt_ogg.pas522
-rw-r--r--plugins/Watrack/formats/fmt_real.pas335
-rw-r--r--plugins/Watrack/formats/fmt_tta.pas65
-rw-r--r--plugins/Watrack/formats/fmt_wav.pas146
-rw-r--r--plugins/Watrack/formats/fmt_wma.pas438
-rw-r--r--plugins/Watrack/formats/tag_apev2.inc124
-rw-r--r--plugins/Watrack/formats/tag_id3v1.inc175
-rw-r--r--plugins/Watrack/formats/tag_id3v2.inc545
-rw-r--r--plugins/Watrack/formats/tags.pas21
19 files changed, 4513 insertions, 0 deletions
diff --git a/plugins/Watrack/formats/fmt_aac.pas b/plugins/Watrack/formats/fmt_aac.pas
new file mode 100644
index 0000000000..db8e03b153
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_aac.pas
@@ -0,0 +1,93 @@
+{AAC file process}
+unit fmt_AAC;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadAAC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ SampleRates:array [0..15] of dword = (
+ 96000,88200,64000,48000,44100,32000,24000,22050,
+ 16000,12000,11025,8000,0,0,0,0);
+
+procedure ReadADIFheader(f:THANDLE;var Info:tSongInfo);
+var
+ buf:array [0..29] of byte;
+ bs,sf_idx,skip:dword;
+begin
+ BlockRead(f,buf,30);
+ if (buf[0] and $80)<>0 then
+ skip:=9
+ else
+ skip:=0;
+ Info.kbps:=(((buf[0+skip] and $0F) shl 19)+(buf[1+skip] shl 11)+
+ (buf[2+skip] shl 3)+{or}((buf[3+skip] and $E0){shr 5})) div 1000;
+ bs:=buf[0+skip] and $10;
+ if bs=0 then
+ sf_idx:=(buf[7+skip] and $78) shr 3
+ else
+ sf_idx:=((buf[4+skip] and $07) shl 1)+((buf[5+skip] and $80) shr 7);
+ Info.khz:=SampleRates[sf_idx];
+end;
+
+procedure ReadADTSheader(var Info:tSongInfo;sign:dword);
+type
+ l2b=record
+ b:array [0..3] of byte;
+ end;
+var
+ sr_idx:integer;
+begin
+ Info.channels:=((l2b(sign).b[2] and $01) shl 2)+
+ ((l2b(sign).b[3] and $C0) shr 6);
+ sr_idx:=(l2b(sign).b[2] and $3C) shr 2;
+ Info.khz:=SampleRates[sr_idx] div 1000;
+end;
+
+function ReadAAC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ sign:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,sign,4);
+ Info.khz:=44;
+ Info.kbps:=128;
+ Info.channels:=2;
+ if (lobyte(sign)=$FF) and ((hibyte(sign) and $F6)=$F0) then
+ ReadADTSheader(Info,sign)
+ else if sign=$46494441 then // 'ADIF'
+ ReadADIFheader(f,Info);
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadAAC;
+ LocalFormatLink.This.ext :='AAC';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_ape.pas b/plugins/Watrack/formats/fmt_ape.pas
new file mode 100644
index 0000000000..fbabdac19c
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_ape.pas
@@ -0,0 +1,137 @@
+{APE file}
+unit fmt_APE;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadAPE(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+
+uses windows,common,io,tags,srv_format;
+
+const
+ defID = $2043414D;
+type
+(* Old Version ?
+ tMonkeyHeader = record
+ ID :dword; { Always "MAC " }
+ VersionID :word; { Version number * 1000 (3.91 = 3910) }
+ CompressionID :word; { Compression level code }
+ Flags :word; { Any format flags }
+ Channels :word; { Number of channels }
+ SampleRate :dword; { Sample rate (hz) }
+ HeaderBytes :dword; { Header length (without header ID) }
+ TerminatingBytes:dword; { Extended data }
+ Frames :dword; { Number of frames in the file }
+ FinalSamples :dword; { Number of samples in the final frame }
+ PeakLevel :dword; { Peak level (if stored) }
+ SeekElements :dword; { Number of seek elements (if stored) }
+ end;
+*)
+ tMonkeyHeader = packed record
+ ID :dword; // should equal 'MAC '
+ VersionID :dword; // version number * 1000 (3.81 = 3810)
+ nDescriptorBytes :dword; // descriptor bytes
+ nHeaderBytes :dword; // APEHeader bytes
+ nSeekTableBytes :dword; // bytes of the seek table
+ nHeaderDataBytes :dword; // header data bytes (from original file)
+ nFrameDataBytes :dword; // bytes of APE frame data
+ nFrameDataBytesHi:dword; // the high order number of APE frame data bytes
+ nTerminatingBytes:dword; // the terminating data of the file (w/o tag data)
+ cFileMD5:array [0..15] of byte;
+ end;
+type
+ tAPEHeader = packed record
+ nCompressionLevel:word; // the compression level
+ nFormatFlags :word; // any format flags (for future use)
+ nBlocksPerFrame :dword; // the number of audio blocks in one frame
+ nFinalFrameBlocks:dword; // the number of audio blocks in the final frame
+ nTotalFrames :dword; // the total number of frames
+ nBitsPerSample :word; // the bits per sample (typically 16)
+ nChannels :word; // the number of channels (1 or 2)
+ nSampleRate :dword; // the sample rate (typically 44100)
+ end;
+
+const
+ MONKEY_COMPRESSION_FAST = 1000; // Fast (poor)
+ MONKEY_COMPRESSION_NORMAL = 2000; // Normal (good)
+ MONKEY_COMPRESSION_HIGH = 3000; // High (very good)
+ MONKEY_COMPRESSION_EXTRA_HIGH = 4000; // Extra high (best)
+const
+ MONKEY_FLAG_8_BIT = 1; // Audio 8-bit
+ MONKEY_FLAG_CRC = 2; // New CRC32 error detection
+ MONKEY_FLAG_PEAK_LEVEL = 4; // Peak level stored
+ MONKEY_FLAG_24_BIT = 8; // Audio 24-bit
+ MONKEY_FLAG_SEEK_ELEMENTS = 16; // Number of seek elements stored
+ MONKEY_FLAG_WAV_NOT_STORED = 32; // WAV header not stored
+
+function ReadAPE(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ hdr:tMonkeyHeader;
+ hdr1:tAPEHeader;
+ blocks:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,hdr ,SizeOf(tMonkeyHeader));
+ BlockRead(f,hdr1,SizeOf(tAPEHeader)); //hdr.nHeaderBytes
+ if hdr1.nTotalFrames=0 then
+ blocks:=0
+ else
+ blocks:=(hdr1.nTotalFrames-1)*hdr1.nBlocksPerFrame+hdr1.nFinalFrameBlocks;
+ Info.khz :=hdr1.nSampleRate div 1000;
+ if hdr1.nSampleRate<>0 then
+ Info.total :=blocks div hdr1.nSampleRate;
+ Info.channels:=hdr1.nChannels;
+// Info.kbps:=Info.khz*deep*Info.channels/1152
+// Info.kbps:=(blocks*Info.channels*hdr1.nBitsPerSample) div (Info.total*8000);
+// Info.kbps :=((hdr1.nBitsPerSample div 8)*hdr1.nSamplerate) div 1000;
+(* Old version ?
+ if (hdr.ID<>DefID) or (hdr.SampleRate=0) or (hdr.Channels=0) then
+ exit;
+ if (hdr.VersionID>=3900) or
+ ((hdr.VersionID>=3800) and
+ (hdr.CompressionID=MONKEY_COMPRESSION_EXTRA_HIGH)) then
+ tmp:=73728
+ else
+ tmp:=9216;
+ tmp:=(hdr.Frames-1)*tmp+hdr.FinalSamples;
+ Info.total :=tmp div hdr.SampleRate;
+ Info.khz :=hdr.SampleRate div 1000;
+ Info.channels:=hdr.Channels;
+
+ Info.kbps:=tmp;//samples
+ if (hdr.Flags and MONKEY_FLAG_8_BIT)<>0 then tmp:=8
+ else if (hdr.Flags and MONKEY_FLAG_24_BIT)<>0 then tmp:=24
+ else tmp:=16;
+ Info.kbps:=((Info.kbps*tmp*hdr.Channels) div Info.Total) div 1000;
+*)
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadAPE;
+ LocalFormatLink.This.ext :='APE';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_avi.pas b/plugins/Watrack/formats/fmt_avi.pas
new file mode 100644
index 0000000000..3646236cf7
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_avi.pas
@@ -0,0 +1,295 @@
+{AVI file format}
+unit fmt_AVI;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadAVI(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format;
+
+type
+ FOURCC = array [0..3] of AnsiChar;
+type
+ tChunkHeader = packed record
+ case byte of
+ 0: (Lo,Hi:dword); {Common}
+ 1: (ID:FOURCC; {RIFF}
+ Length:dword);
+ end;
+
+const
+ sRIFF = $46464952;
+ sLIST = $5453494C;
+ savih = $68697661; { avi header }
+ sstrf = $66727473; { stream format }
+ sstrh = $68727473; { stream header }
+const
+ smovi = $69766F6D; {movi list type}
+const
+ svids = $73646976; {video}
+ sauds = $73647561; {audio}
+const
+ sIART = $54524149; {director}
+ sICMT = $544D4349; {comment}
+ sICRD = $44524349; {creation date}
+ sIGNR = $524E4749; {genre}
+ sINAM = $4D414E49; {title}
+ sIPRT = $54525049; {part}
+ sIPRO = $4F525049; {produced by}
+ sISBJ = $4A425349; {subject description}
+
+type
+ tWaveFormatEx = packed record
+ wFormatTag :word;
+ nChannels :word;
+ nSamplesPerSec :dword;
+ nAvgBytesPerSec:dword;
+ nBlockAlign :word;
+ wBitsPerSample :word;
+ cbSize :word;
+
+ Reserved1 :word;
+ wID :word;
+ fwFlags :word;
+ nBlockSize :word;
+ nFramesPerBlock:word;
+ nCodecDelay :word; {ms}
+ end;
+
+type
+ tMainAVIHeader = packed record {avih}
+ dwMicroSecPerFrame :dword;
+ dwMaxBytesPerSec :dword;
+ dwPaddingGranularity :dword;
+ dwFlags :dword;
+ dwTotalFrames :dword; { # frames in first movi list}
+ dwInitialFrames :dword;
+ dwStreams :dword;
+ dwSuggestedBufferSize:dword;
+ dwWidth :dword;
+ dwHeight :dword;
+ dwScale :dword;
+ dwRate :dword;
+ dwStart :dword;
+ dwLength :dword;
+ end;
+
+type
+ TAVIExtHeader = packed record {dmlh}
+ dwGrandFrames:dword; {total number of frames in the file}
+ dwFuture:array[0..60] of dword;
+ end;
+
+type
+ tAVIStreamHeader = packed record {strh}
+ fccType :FOURCC; {vids|auds}
+ fccHandler :FOURCC;
+ dwFlags :dword;
+ wPriority :word;
+ wLanguage :word;
+ dwInitialFrames :dword;
+ dwScale :dword;
+ dwRate :dword;
+ dwStart :dword;
+ dwLength :dword;
+ dwSuggestedBufferSize:dword;
+ dwQuality :dword;
+ dwSampleSize :dword;
+ rcFrame: packed record
+ left :word;
+ top :word;
+ right :word;
+ bottom:word;
+ end;
+ end;
+
+var
+ vora:dword;
+
+procedure Skip(f:THANDLE;bytes:dword);
+var
+ i:dword;
+begin
+ i:=FilePos(f);
+ if bytes=0 then
+ begin
+ if odd(i) then
+ Seek(f,i+1);
+ end
+ else
+ Seek(f,i+bytes+(bytes mod 2));
+end;
+
+procedure ProcessVideoFormat(f:THANDLE;Size:dword;var Info:tSongInfo);
+var
+ bih:BitmapInfoHeader;
+begin
+ BlockRead(f,bih,SizeOf(bih));
+ Info.codec :=bih.biCompression;
+ Info.width :=bih.biWidth;
+ Info.height:=bih.biHeight;
+ Skip(f,Size-SizeOf(bih));
+end;
+
+procedure ProcessAudioFormat(f:THANDLE;Size:dword;var Info:tSongInfo);
+{WAVEFORMATEX or PCMWAVEFORMAT}
+var
+ AF:tWaveFormatEx;
+begin
+ BlockRead(f,AF,SizeOf(AF));
+ Info.channels:=AF.nChannels;
+ Info.khz :=AF.nSamplesPerSec div 1000;
+ Info.kbps :=(AF.nAvgBytesPerSec*8) div 1000;
+ Skip(f,Size-SizeOf(AF));
+end;
+
+function ProcessASH(f:THANDLE;var Info:tSongInfo):dword;
+var
+ ASH:tAVIStreamHeader;
+begin
+ BlockRead(f,ASH,SizeOf(ASH));
+ with ASH do
+ begin
+ if dword(fccType)=svids then
+ begin
+ if ASH.dwScale<>0 then
+ Info.fps:=(ASH.dwRate*100) div ASH.dwScale;
+ if Info.fps<>0 then
+ Info.total:=(ASH.dwLength*100) div Info.fps;
+ ProcessASH:=1
+ end
+ else if dword(fccType)=sauds then ProcessASH:=2
+ else ProcessASH:=0;
+ end;
+end;
+
+procedure ProcessMAH(f:THANDLE;var Info:tSongInfo);
+var
+ MAH:tMainAVIHeader;
+begin
+ BlockRead(f,MAH,SizeOf(MAH));
+// Info.width:=MAH.dwWidth;
+// Info.height:=MAH.dwHeight;
+// Info.fps:=100000000 div MAH.dwMicroSecPerFrame;
+end;
+
+function ProcessChunk(f:THANDLE;var Info:tSongInfo):dword;
+var
+ lTotal:dword;
+ Chunk:tChunkHeader;
+ cType:FOURCC;
+ ls:PAnsiChar;
+begin
+ Skip(f,0);
+ if (BlockRead(f,Chunk,SizeOF(Chunk))=0) or (Chunk.Lo=0) then
+ begin
+ result:=FileSize(f);
+ Seek(f,FileSize(f));
+ exit;
+ end;
+ result:=Chunk.Length+SizeOf(Chunk);
+ case Chunk.Lo of
+ sRIFF,sLIST: begin
+ BlockRead(f,cType,SizeOf(cType));
+ if dword(cType)=smovi then
+ Skip(f,Chunk.Length-SizeOf(cType)) // result:=FileSize(f)
+ else
+ begin
+ lTotal:=SizeOf(FOURCC);
+ while lTotal<Chunk.Length do
+ inc(lTotal,ProcessChunk(f,Info));
+ end;
+ end;
+ sIART,sICMT,sICRD,sIGNR,sINAM,sIPRT,sIPRO,sISBJ: begin
+ mGetMem(ls,Chunk.Length);
+ BlockRead(f,ls^,Chunk.Length);
+ case Chunk.Lo of
+ sIART: begin
+ AnsiToWide(ls,Info.artist);
+ end;
+ sICMT: begin
+ if Info.comment=NIL then
+ AnsiToWide(ls,Info.comment);
+ end;
+ sICRD: begin
+ AnsiToWide(ls,Info.year);
+ end;
+ sIGNR: begin
+ AnsiToWide(ls,Info.genre);
+ end;
+ sINAM: begin
+ AnsiToWide(ls,Info.title);
+ end;
+ sIPRT: begin
+ Info.track:=StrToInt(ls);
+ end;
+ sIPRO: begin
+ if Info.artist=NIL then
+ AnsiToWide(ls,Info.artist);
+ end;
+ sISBJ: begin
+ AnsiToWide(ls,Info.comment);
+ end;
+ end;
+ mFreeMem(ls);
+ end;
+ savih: begin
+ ProcessMAH(f,Info);
+ end;
+ sstrh: begin
+ vora:=ProcessASH(f,Info);
+ end;
+ sstrf: begin
+ case vora of
+ 1: ProcessVideoFormat(f,Chunk.Hi,Info);
+ 2: ProcessAudioFormat(f,Chunk.Hi,Info);
+ else
+ end;
+ end;
+ else
+ Skip(f,Chunk.Length);
+ end;
+end;
+
+function ReadAVI(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ProcessChunk(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkAVI,
+ LocalFormatLinkDIVX:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkAVI.Next:=FormatLink;
+
+ LocalFormatLinkAVI.This.proc :=@ReadAVI;
+ LocalFormatLinkAVI.This.ext :='AVI';
+ LocalFormatLinkAVI.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkAVI;
+
+ LocalFormatLinkDIVX.Next:=FormatLink;
+
+ LocalFormatLinkDIVX.This.proc :=@ReadAVI;
+ LocalFormatLinkDIVX.This.ext :='DIVX';
+ LocalFormatLinkDIVX.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkDIVX;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_dummy.pas b/plugins/Watrack/formats/fmt_dummy.pas
new file mode 100644
index 0000000000..e691def279
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_dummy.pas
@@ -0,0 +1,46 @@
+{OFR file}
+unit fmt_Dummy;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadDummy(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+function ReadDummy(var Info:tSongInfo):boolean; cdecl;
+{
+var
+ f:THANDLE;
+}
+begin
+{
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+
+ CloseHandle(f);
+}
+ result:=true;
+end;
+
+var
+ LocalFormatLinkCUE:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkCUE.Next:=FormatLink;
+
+ LocalFormatLinkCUE.This.proc :=@ReadDummy;
+ LocalFormatLinkCUE.This.ext :='CUE';
+ LocalFormatLinkCUE.This.flags:=WAT_OPT_CONTAINER;
+
+ FormatLink:=@LocalFormatLinkCUE;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_flv.pas b/plugins/Watrack/formats/fmt_flv.pas
new file mode 100644
index 0000000000..b8e23f5809
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_flv.pas
@@ -0,0 +1,334 @@
+{FLV file format}
+unit fmt_FLV;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadFLV(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format;
+
+type
+ tFLVHeader = packed record
+ Signature:array [0..2] of AnsiChar; // FLV
+ Version :byte;
+ flags :byte;
+ Offset :dword; // reversed byte order
+ end;
+type
+ tFLVStream = packed record
+ PreviousTagSize :dword;
+ TagType :byte;
+ BodyLength :array [0..2] of byte;
+ Timestamp :array [0..2] of byte;
+ TimestampExtended:byte;
+ StreamID :array [0..2] of byte;
+// Body
+ end;
+{
+ twork = record
+ endptr:PAnsiChar;
+ Info :pSongInfo;
+ key :PAnsiChar;
+ len :cardinal;
+ end;
+}
+// FLVTagTypes
+const
+ FLV_AUDIO = 8;
+ FLV_VIDEO = 9;
+ FLV_META = 18;
+
+const
+ BufSize = 128*1024;
+
+type
+ pArr = ^tArr;
+ tArr = array [0..7] of byte;
+
+ transform=packed record
+ case byte of
+ 0: (txt:array [0..3] of AnsiChar);
+ 1: (num:dword);
+ end;
+ trecode=packed record
+ case byte of
+ 0: (i:int64);
+ 1: (d:double);
+ end;
+
+function Reverse(buf:int64;len:integer):int64;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to len-1 do
+ result:=(result shl 8)+tArr(buf)[i];
+end;
+
+function ProcessValue(var ptr:PAnsiChar;key:pAnsiChar;var Info:tSongInfo):integer;
+var
+ tmp:int64;
+ i,len:integer;
+ recode:trecode;
+ code:integer;
+ codec:transform;
+ value:array [0..63] of AnsiChar;
+begin
+ result:=1;
+ code:=ord(ptr^); // type of value
+ ptr^:=#0; // finalization for key name
+ inc(ptr); // value data pointer
+
+ case code of // v10.1 specification
+
+ // Numeric, Double (8 bytes)
+ 0: begin
+ move(ptr^,tmp,8);
+ recode.i:=Reverse(tmp,8);
+ i:=trunc(recode.d);
+ if StrCmp(key,'duration' )=0 then Info.total :=i
+ else if StrCmp(key,'totalduration')=0 then Info.total :=i
+ else if StrCmp(key,'width' )=0 then Info.width :=i
+ else if StrCmp(key,'height' )=0 then Info.height:=i
+ else if StrCmp(key,'audiodatarate')=0 then Info.kbps :=i
+ else if StrCmp(key,'framerate' )=0 then Info.fps :=trunc(recode.d*100)
+ else if StrCmp(key,'audiosize' )=0 then
+ begin
+ if Info.kbps=0 then
+ Info.kbps:=trunc((recode.d*8)/(Info.total*1000))
+ end
+ else if StrCmp(key,'videocodecid')=0 then
+ begin
+ case i of
+ 2: codec.txt:='H263';
+ 3: codec.txt:='Scrn';
+ 4,5: codec.txt:='VP6 ';
+ 6: codec.txt:='Src2';
+ 7: codec.txt:='AVC ';
+ end;
+ Info.codec:=codec.num;
+ end;
+
+ inc(ptr,8);
+ end;
+
+ // Boolean, UI8
+ 1: begin
+ if StrCmp(key,'stereo')=0 then Info.channels:=ORD(ptr^)+1;
+
+ inc(ptr);
+ end;
+
+ // String
+ 2: begin
+ i:=Reverse(pWord(ptr)^,2); inc(ptr,2);
+
+ if StrCmp(key,'creationdate')=0 then
+ begin
+ move(ptr^,value[0],i);
+ value[i]:=#0;
+ AnsiToWide(value,Info.year);
+ end;
+
+ inc(ptr,i);
+ end;
+
+ // Object
+ 3: begin
+ repeat
+ len:=Reverse(pWord(ptr)^,2); inc(ptr,2); // key name length
+ key:=ptr; inc(ptr,len); // key name
+
+ result:=ProcessValue(ptr,key,Info);
+ until result<=0;
+
+ if result<0 then
+ result:=1;
+ end;
+
+ // Movie clip, reserved
+ 4: begin
+ end;
+
+ // NULL
+ 5: begin
+ end;
+
+ // Undefined
+ 6: begin
+ end;
+
+ // reference, UI16
+ 7: begin
+ inc(ptr,2);
+ end;
+
+ // ECMA array
+ 8: begin
+ i:=pdword(ptr)^; inc(ptr,4);
+ i:=Reverse(i,4);
+ while i>0 do
+ begin
+
+ len:=Reverse(pWord(ptr)^,2); inc(ptr,2); // key name length
+ key:=ptr; inc(ptr,len); // key name
+
+ result:=ProcessValue(ptr,key,Info);
+
+ if result=0 then break
+ else if result<0 then
+ begin
+ result:=1;
+ break;
+ end;
+ dec(i);
+ end;
+ end;
+
+ // Object end marker, UI8[3]=0,0,9
+ 9: begin
+ result:=-1;
+ inc(ptr,3);
+ end;
+
+ 10: // array, 4 bytes - num of elements, elements
+ begin
+ i:=pdword(ptr)^; inc(ptr,4);
+ i:=Reverse(i,4);
+ while i>0 do
+ begin
+ result:=ProcessValue(ptr,nil,Info);
+ if result=0 then exit
+ else if result<0 then
+ begin
+ result:=1;
+ break;
+ end;
+ dec(i);
+ end;
+ end;
+
+ // Date, Double + UI16 (UTC)
+ 11: begin
+ inc(ptr,8);
+ inc(ptr,2);
+ end;
+
+ // LongString, 4 bytes = len, len - string
+ 12: begin
+ i:=pdword(ptr)^; inc(ptr,4);
+ i:=Reverse(i,4);
+
+ inc(ptr,i);
+ end;
+
+ end;
+end;
+
+function ReadFLV(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ codec:transform;
+ FLVHdr:tFLVHeader;
+ StrmHdr:tFLVStream;
+ i,len:integer;
+ buf,pp,p,endbuf:PAnsiChar;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+
+ mGetMem(buf,BufSize);
+ endbuf:=buf+BlockRead(f,buf^,BufSize);
+ p:=buf;
+ CloseHandle(f);
+ move(p^,FLVHdr,SizeOf(tFLVHeader));
+ if (FLVHdr.Signature[0]='F') and (FLVHdr.Signature[1]='L') and
+ (FLVHdr.Signature[2]='V') and (FLVHdr.Version=1) then
+ begin
+ inc(p,SizeOf(tFLVHeader));
+ result:=true;
+ while (p<endbuf) and ((FLVHdr.flags and 5)<>0) do
+ begin
+ move(p^,StrmHdr,SizeOf(tFLVStream));
+ inc(p,SizeOf(tFLVStream));
+ len:=(StrmHdr.BodyLength[0] shl 16)+(StrmHdr.BodyLength[1] shl 8)+
+ StrmHdr.BodyLength[2];
+ pp:=p;
+ case StrmHdr.TagType of
+
+ FLV_AUDIO: begin
+ Info.channels:=(ord(p^) and 1)+1;
+ // samplesize is (S_Byte and 2) shr 1 = 8 or 16
+ case (ord(p^) and $C) shr 2 of
+ 0: Info.khz:=5;
+ 1: Info.khz:=11;
+ 2: Info.khz:=22;
+ 3: Info.khz:=44;
+ end;
+ FLVHdr.flags:=FLVHdr.flags and not 4;
+ end;
+
+ FLV_VIDEO: begin
+ case ord(p^) and $0F of
+ 2: codec.txt:='H263';
+ 3: codec.txt:='Scrn';
+ 4,5: codec.txt:='VP6 ';
+ 6: codec.txt:='Src2';
+ 7: codec.txt:='AVC ';
+ end;
+ Info.codec:=codec.num;
+ FLVHdr.flags:=FLVHdr.flags and not 1;
+ end;
+
+ FLV_META: begin
+ if (StrmHdr.TagType and $40)=0 then // not encripted
+ begin
+ if pByte(p)^=2 then // string
+ begin
+ Inc(p);
+ i:=Reverse(pWord(p)^,2); inc(p,2);
+ if StrCmp(p,'onMetaData',i)=0 then // Metadata processing start
+ begin
+ inc(p,i);
+ ProcessValue(p,nil,Info); // metadata, no need key name, our info
+ // checking for video
+ if Info.codec<>0 then
+ FLVHdr.flags:=FLVHdr.flags and not 1;
+ // checking for audio
+ if (Info.khz<>0) and (Info.channels<>0) then
+ FLVHdr.flags:=FLVHdr.flags and not 4;
+ // break; // if metainfo is enough
+ end;
+ end;
+ end;
+ end;
+
+ end;
+ p:=pp+len;
+ end;
+ end;
+ mFreeMem(buf);
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadFLV;
+ LocalFormatLink.This.ext :='FLV';
+ LocalFormatLink.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_m4a.pas b/plugins/Watrack/formats/fmt_m4a.pas
new file mode 100644
index 0000000000..1bfcf2309a
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_m4a.pas
@@ -0,0 +1,378 @@
+{M4A code template}
+unit fmt_M4A;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadM4A(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+
+uses windows,common,io,srv_format,
+{$IFDEF KOL_MCK}KolZlibBzip{$ELSE}Zlib,zwrapper{$ENDIF};
+
+type
+ mp4Atom = record
+ len:dword;
+ name:dword;
+ end;
+
+const
+ at_moov = $766F6F6D;
+ at_mvhd = $6468766D;
+ at_udta = $61746475;
+ at_meta = $6174656D;
+ at_ilst = $74736C69;
+ at_cmov = $766F6D63;
+ at_dcom = $6D6F6364;
+ at_cmvd = $64766D63; // 4 - unpacked size, data
+ at_trak = $6B617274;
+ at_tkhd = $64686B74; // not needed
+ at_mdia = $6169646D;
+ at_minf = $666E696D;
+ at_smhd = $64686D73;
+ at_vmhd = $64686D76;
+ at_stbl = $6C627473;
+ at_stsd = $64737473;
+
+const
+ atm_nam = $6D616EA9; // title
+ atm_ART = $545241A9; // artist
+ atm_wrt = $747277A9; // writer
+ atm_alb = $626C61A9; // album
+ atm_day = $796164A9; // date
+ atm_cmt = $746D63A9; // comment
+ atm_gen = $6E6567A9; // alt.genre
+ atm_gnre = $65726E67; // genre
+ atm_trkn = $6E6B7274; // track number
+// atm_zlib = $62696C7A;
+
+type
+ pstsd = ^tstsd;
+ tstsd = packed record
+ version :byte;
+ flags :array [0..2] of byte;
+ NumEntries :dword;
+ SampleDescSize:dword; // $56
+ DataFormat :dword;
+ reserved :array [0..5] of byte;
+ RefIndex :word;
+ Ver :word;
+ Revision :word;
+ Vendor :dword;
+ Temporal :dword;
+ Spacial :dword;
+ Width :word;
+ Height :word;
+ HRes :dword; //single;
+ VRes :dword;
+ DataSize :dword;
+ FrameCount :word;
+ CompNameLen :byte;
+ Compressor :array [0..18] of AnsiChar;
+ ColorDepth :word;
+ ColorTableID :word;
+ end;
+ pastsd = ^astsd;
+ astsd = packed record
+ Version :byte;
+ Flags :array [0..2] of byte;
+ NumEntires :dword;
+ DescSize :dword;
+ CodingName :array[0..3] of AnsiChar;
+ Reserved :array[0..5] of Byte;
+ RefIndex :Word;
+ Reserved_ :array[0..1] of dword;
+ ChannelCount:Word;
+ SampleSize :Word;
+ Pre_defined :Word;
+ Reserved___ :Word;
+ Samplerate :dword;
+ end;
+ pmvhd = ^mvhd;
+ mvhd = packed record
+ Version:byte;
+ flags:array [0..2] of byte;
+ Creation:dword;
+ Modification:dword;
+ TimeScale:dword;
+ Duration:dword;
+ end;
+
+procedure ReadAtom(f:THANDLE;var atom:mp4Atom);
+begin
+ BlockRead(f,atom.len,4);
+ if atom.len>0 then
+ begin
+ BlockRead(f,atom.name,4);
+ atom.len:=BSwap(atom.len);
+ end
+ else
+ begin
+ atom.name:=0;
+ atom.len:=8;
+ end;
+end;
+
+procedure GetAtom(var p:pbyte;var atom:mp4Atom);
+begin
+ atom.len:=pdword(p)^;
+ inc(p,4);
+ if atom.len>0 then
+ begin
+ atom.name:=pdword(p)^;
+ inc(p,4);
+ atom.len:=BSwap(atom.len);
+ end
+ else
+ begin
+ atom.name:=0;
+ atom.len:=8;
+ end;
+end;
+
+function SetTree(from:mp4Atom;var p:pbyte;path:PAnsiChar;var parent:pbyte):integer;
+var
+ atom:mp4Atom;
+ len:cardinal;
+ saved:pbyte;
+begin
+ saved:=p;
+ len:=0;
+ dec(from.len,SizeOf(from));
+ parent:=p;
+ repeat
+ GetAtom(p,atom);
+ if atom.name=pdword(path)^ then
+ begin
+ inc(path,4);
+ if path^<>#0 then
+ begin
+ parent:=p;
+ inc(path);
+ len:=0;
+ from.len:=atom.len-SizeOf(atom);
+ end
+ else
+ begin
+ result:=atom.len;
+ exit;
+ end;
+ end
+ else
+ begin
+ inc(p,atom.len-SizeOf(atom));
+ inc(len,atom.len);
+ end;
+ until len>=from.len;
+ result:=-1;
+ p:=saved;
+end;
+
+function ReadInt(var p:pbyte):dword;
+var
+ len:integer;
+begin
+ len:=pdword(p)^;
+ inc(p,4);
+ len:=BSwap(len);
+ if len>0 then
+ inc(p,4); // 'data'
+ inc(p,4); // type?
+ inc(p,4); // encoding?
+ dec(len,8+8);
+ if len>4 then len:=4;
+ if len=2 then
+ begin
+ result:=p^*$100;
+ inc(p);
+ inc(result,p^);
+ inc(p);
+ end
+ else
+ begin
+ result:=BSwap(pdword(p)^);
+ inc(p,4);
+ end;
+end;
+
+procedure ReadProp(var p:pbyte;var prop:pWideChar);
+var
+ len:integer;
+ ltmp:PAnsiChar;
+ c:byte;
+begin
+ len:=pdword(p)^;
+ inc(p,4);
+ len:=BSwap(len);
+ if len>0 then
+ inc(p,4); // 'data'
+ inc(p,4); // type?
+ inc(p,4); // encoding?
+ dec(len,8+8);
+ ltmp:=pointer(p);
+ inc(p,len);
+ c:=p^;
+ p^:=0;
+ UTF8ToWide(ltmp,prop);
+ p^:=c;
+end;
+
+function ReadM4A(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ atom:mp4Atom;
+ cursize,parentsize:integer;
+ par,buf,p,pn,finish:pbyte;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ cursize:=0;
+ parentsize:=FileSize(f);
+ repeat
+ ReadAtom(f,atom);
+ if atom.name=at_moov then
+ begin
+ mGetMem(buf,atom.len);
+ BlockRead(f,buf^,atom.len);
+ p:=buf;
+ finish:=pByte(PAnsiChar(p)+atom.len-SizeOf(atom));
+ repeat
+ GetAtom(p,atom);
+ pn:=PByte(PAnsiChar(p)+atom.len-SizeOf(atom));
+
+ if atom.name=at_cmov then
+ begin
+ size:=SetTree(atom,p,'cmvd',par);
+ if size>0 then
+ begin
+ ZDecompressBuf(PAnsiChar(p)+4,size-SizeOf(mp4Atom),
+ pointer(pn),size,BSwap(pdword(p)^));
+ mFreeMem(buf);
+ buf:=pn;
+ p:=buf;
+ GetAtom(p,atom); //must be 'moov'
+ finish:=PByte(PAnsiChar(p)+atom.len-SizeOf(atom));
+ continue;
+ end;
+ end;
+
+ if atom.name=at_mvhd then
+ begin
+ if pmvhd(p)^.TimeScale<>0 then
+ Info.total:=BSwap(pmvhd(p)^.Duration) div BSwap(pmvhd(p)^.TimeScale);
+ end;
+ if atom.name=at_udta then
+ begin
+ size:=SetTree(atom,p,'meta.ilst',par);
+ if size>0 then
+ begin
+ cursize:=0;
+ repeat
+ GetAtom(p,atom);
+ if atom.name=atm_nam then ReadProp(p,Info.title)
+ else if atom.name=atm_ART then ReadProp(p,Info.artist)
+// else if atom.name=atm_wrt then ReadProp(p,Info.title)
+ else if atom.name=atm_alb then ReadProp(p,Info.album)
+ else if atom.name=atm_day then ReadProp(p,Info.year)
+ else if atom.name=atm_cmt then ReadProp(p,Info.comment)
+// else if atom.name=atm_gen then ReadProp(p,Info.genre)
+ else if atom.name=atm_gnre then Info.genre:=GenreName(ReadInt(p)-1)
+ else if atom.name=atm_trkn then Info.track:=ReadInt(p)
+ else
+ inc(p,atom.len-SizeOf(mp4Atom));
+ inc(cursize,atom.len);
+ until cursize>=size;
+ end;
+ end;
+ // video properties
+ if atom.name=at_trak then
+ begin
+ if SetTree(atom,p,'mdia.minf.vmhd',par)>0 then
+ begin
+ p:=par;
+ if SetTree(atom,p,'stbl.stsd',par)>0 then
+ begin
+ Info.width :=swap(pstsd(p)^.Width);
+ Info.height:=swap(pstsd(p)^.Height);
+ Info.codec :=pstsd(p)^.DataFormat;
+ end;
+ end
+ // audio props
+ else if SetTree(atom,p,'mdia.minf.smhd',par)>0 then
+ begin
+ p:=par;
+ if SetTree(atom,p,'stbl.stsd',par)>0 then
+ begin
+ Info.khz:=(BSwap(pastsd(p)^.Samplerate) shr 16) div 1000;
+ Info.channels:=swap(pastsd(p)^.ChannelCount);
+ end;
+ p:=par;
+ if SetTree(atom,p,'stsz',par)>0 then
+ begin
+ if pdword(PAnsiChar(p)+4)^=0 then
+ Info.vbr:=1;
+ end;
+ end;
+ end;
+ p:=pn;
+ until PAnsiChar(p)>=PAnsiChar(finish);
+ mFreeMem(buf);
+ break;
+ end
+ else
+ Skip(f,atom.len-SizeOf(mp4Atom));
+ inc(cursize,atom.len);
+ until cursize>=parentsize;
+ CloseHandle(f);
+end;
+
+var
+ LocalFormatLinkM4A,
+ LocalFormatLinkMP4,
+ LocalFormatLinkMOV,
+ LocalFormatLink3GP:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkM4A.Next:=FormatLink;
+
+ LocalFormatLinkM4A.This.proc :=@ReadM4A;
+ LocalFormatLinkM4A.This.ext :='M4A';
+ LocalFormatLinkM4A.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkM4A;
+
+ LocalFormatLinkMP4.Next:=FormatLink;
+
+ LocalFormatLinkMP4.This.proc :=@ReadM4A;
+ LocalFormatLinkMP4.This.ext :='MP4';
+ LocalFormatLinkMP4.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMP4;
+
+ LocalFormatLinkMOV.Next:=FormatLink;
+
+ LocalFormatLinkMOV.This.proc :=@ReadM4A;
+ LocalFormatLinkMOV.This.ext :='MOV';
+ LocalFormatLinkMOV.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMOV;
+
+ LocalFormatLink3GP.Next:=FormatLink;
+
+ LocalFormatLink3GP.This.proc :=@ReadM4A;
+ LocalFormatLink3GP.This.ext :='3GP';
+ LocalFormatLink3GP.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLink3GP;
+
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_mkv.pas b/plugins/Watrack/formats/fmt_mkv.pas
new file mode 100644
index 0000000000..df01a82f59
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mkv.pas
@@ -0,0 +1,235 @@
+{MKV file process}
+unit fmt_MKV;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMKV(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format;
+
+const
+ idEBML = $A45DFA3;
+ idSegment = $8538067;
+ idInfo = $549A966;
+ idTimecodeScale = $AD7B1;
+ idDuration = $489;
+ idTracks = $654AE6B;
+ idTrackEntry = $2E;
+ idTrackType = $3;
+ idCodecPrivate = $23A2;
+ idName = $136E;
+ idVideo = $60;
+ idAudio = $61;
+ idPixelWidth = $30;
+ idPixelHeight = $3A;
+ idDefaultDuration = $3E383;
+ idSamplingFrequency = $35;
+ idChannels = $1F;
+ idCluster = $F43B675;
+
+function GetNumber(var ptr:pbyte):int64;
+begin
+ if (ptr^ and $80)<>0 then
+ result:=ptr^ and $7F
+ else if (ptr^ and $40)<>0 then
+ begin
+ result:=(ptr^ and $3F) shl 8; inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $20)<>0 then
+ begin
+ result:=(ptr^ and $1F) shl 16; inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $10)<>0 then
+ begin
+ result:=(ptr^ and $0F) shl 24; inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $08)<>0 then
+ begin
+ result:=int64(ptr^ and $07) shl 32; inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $04)<>0 then
+ begin
+ result:=int64(ptr^ and $03) shl 40; inc(ptr);
+ result:=result+(int64(ptr^) shl 32); inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $02)<>0 then
+ begin
+ result:=int64(ptr^ and $01) shl 48; inc(ptr);
+ result:=result+(int64(ptr^) shl 40); inc(ptr);
+ result:=result+(int64(ptr^) shl 32); inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $01)<>0 then
+ begin
+ inc(ptr);
+ result:= (int64(ptr^) shl 48); inc(ptr);
+ result:=result+(int64(ptr^) shl 40); inc(ptr);
+ result:=result+(int64(ptr^) shl 32); inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else
+ result:=0;
+ inc(ptr);
+end;
+
+function GetInt(var ptr:pbyte;len:integer):int64;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to len-1 do
+ begin
+ result:=(result shl 8)+ptr^;
+ inc(ptr);
+ end;
+end;
+
+function GetFloat(var ptr:pbyte):single;
+var
+ i:dword;
+ f:single absolute i;
+begin
+ i:=( ptr^ shl 24); inc(ptr);
+ inc(i,ptr^ shl 16); inc(ptr);
+ inc(i,ptr^ shl 8); inc(ptr);
+ inc(i,ptr^); inc(ptr);
+ result:=f;
+end;
+
+function ReadMKV(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ id,len:integer;
+ ptr:pByte;
+ buf:array [0..16383] of byte;
+ trktype,scale:integer;
+ ls:PAnsiChar;
+ tmp:integer;
+ lTotal:real;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,buf,SizeOf(buf));
+ ptr:=@buf;
+ trktype:=0;
+ lTotal:=0;
+ scale:=1;
+ repeat
+ id :=GetNumber(ptr);
+ len:=GetNumber(ptr);
+ if id=idEBML then // just check
+ begin
+ result:=true;
+ inc(ptr,len);
+ end
+ else if id=idCluster then
+ break
+ else if id=idSegment then // do nothing
+ else if id=idInfo then // do nothing
+ else if id=idTracks then // do nothing
+ else if id=idTrackEntry then // do nothing
+ else if id=idVideo then // do nothing
+ else if id=idAudio then // do nothing
+ else if id=idTimecodeScale then
+ scale:=GetInt(ptr,len)
+ else if id=idDuration then
+ lTotal:=GetFloat(ptr)
+ else if id=idTrackType then
+ begin
+ tmp:=trktype;
+ trktype:=GetInt(ptr,len); // 1-video,2-audio
+ if (tmp=2) and (trktype=2) then
+ break;
+ end
+ else if (id=idCodecPrivate) and (trktype=1) then
+ begin
+ inc(ptr,16);
+ // 4 - ? (40=size included?)
+ // width,height
+ // 2 - ?
+ // 2 - bitperpixel?
+ Info.codec:=ptr^; inc(ptr);
+ Info.codec:=Info.codec+(ptr^ shl 8 ); inc(ptr);
+ Info.codec:=Info.codec+(ptr^ shl 16); inc(ptr);
+ Info.codec:=Info.codec+(ptr^ shl 24);
+ inc(ptr,len-19);
+ end
+ else if (id=idName) and (Info.title=NIL) then
+ begin
+ mGetMem(ls,len+1);
+ move(ptr^,ls^,len);
+ ls[len]:=#0;
+ AnsiToWide(ls,Info.title);
+ mFreeMem(ls);
+ inc(ptr,len);
+ end
+ else if id=idPixelWidth then
+ Info.width:=GetInt(ptr,len)
+ else if id=idPixelHeight then
+ Info.height:=GetInt(ptr,len)
+ else if id=idDefaultDuration then
+ begin
+ if trktype=1 then
+ begin
+ Info.fps:=(GetInt(ptr,len) div 1000);
+ if Info.fps<>0 then
+ Info.fps:=100000000 div Info.fps;
+ end
+ else
+ begin
+ GetInt(ptr,len);
+ end;
+ end
+ else if id=idSamplingFrequency then
+ Info.khz:=round(GetFloat(ptr)) div 1000
+ else if id=idChannels then
+ Info.channels:=GetInt(ptr,len)
+ else
+ inc(ptr,len);
+ until pAnsiChar(ptr)>=(PAnsiChar(@buf)+SizeOf(buf));
+ Info.total:=trunc(lTotal/(1000000000/scale));
+ CloseHandle(f);
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadMKV;
+ LocalFormatLink.This.ext :='MKV';
+ LocalFormatLink.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_mp3.pas b/plugins/Watrack/formats/fmt_mp3.pas
new file mode 100644
index 0000000000..5d6c94745a
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mp3.pas
@@ -0,0 +1,460 @@
+{MP3 file process}
+unit fmt_MP3;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMP3(var Info:tSongInfo):boolean; cdecl;
+function ReadMPG(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ ScanSize = 16*1024; // block size to search header
+type
+ tMP3FrameHdr = record
+ Version :integer;
+ Layer :cardinal;
+ Bitrate :cardinal;
+ Samplerate:cardinal;
+ Channel :cardinal; //Stereo, Joint, Dual, Mono
+ Length :cardinal;
+ CRC :boolean;
+ _Private :boolean;
+ Copyright :boolean;
+ Original :boolean;
+ isVBR :boolean;
+ end;
+
+// ........ ........ 111..... 11111111 syncword
+// ........ ........ ...xx... ........ version (11=1, 10=2, 00=2.5)
+// ........ ........ .....xx. ........ layer (01=III, 10=II, 11=I)
+// ........ ........ .......x ........ crc (0=yes, 1=no)
+// xx...... ........ ........ ........ mode (00=stereo, 10=joint, 01=dual, 11=mono)
+// ..xx.... ........ ........ ........ mode ext (only for joint stereo)
+// ....x... ........ ........ ........ copyright (0=no, 1=yes)
+// .....x.. ........ ........ ........ original (0=orig, 1=copy)
+// ......xx ........ ........ ........ emphasis (not 10)
+// ........ xxxx.... ........ ........ bitrate (not 0000 nor 1111)
+// ........ ....xx.. ........ ........ sampling rate (not 11)
+// ........ ......x. ........ ........ padded (0=no, 1=yes)
+// ........ .......x ........ ........ private bit
+
+const
+ btable:array [0..1,0..2,0..15] of word = (
+ ( //MPEG 2 & 2.5
+ (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0), //Layer III
+ (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0), //Layer II
+ (0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0) //Layer I
+ ),( //MPEG 1
+ (0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0), //Layer III
+ (0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0), //Layer II
+ (0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0) //Layer I
+ )
+ );
+ stable: array [0..3,0..2] of word = (
+ (32000, 16000, 8000), //MPEG 2.5
+ ( 0, 0, 0), //reserved
+ (22050, 24000, 16000), //MPEG 2
+ (44100, 48000, 32000) //MPEG 1
+ );
+
+procedure TranslateFrameHdr(const block:array of byte;var MP3FrameHdr:tMP3FrameHdr);
+begin
+ FillChar(MP3FrameHdr,SizeOf(MP3FrameHdr),0);
+ if block[0]=$FF then
+ begin
+ with MP3FrameHdr do
+ begin
+ Version :=(block[1] and $18) shr 3;
+ Layer :=(block[1] and $06) shr 1;
+ CRC :=not Odd(block[1]);
+ Bitrate :=btable[Version and 1][Layer-1][block[2] shr 4];
+ Samplerate:=stable[Version][(block[2] and $0C) shr 2];
+ _Private :=odd(block[2]);
+ Channel :=block[3] shr 6;
+ Copyright :=((block[3] and $08) shr 3)<>0;
+ Original :=((block[3] and $04) shr 2)<>0;
+ end;
+ end;
+end;
+
+procedure CheckVBR(f:THANDLE; var hdr:tMP3FrameHdr);
+var
+ pos,apos:cardinal;
+ sign:longint;
+ frames:longint;
+begin
+ pos:=FilePos(f);
+ hdr.Length:=0;
+ if hdr.Version=3 then
+ begin
+ if hdr.Channel=3 then
+ apos:=17
+ else
+ apos:=32;
+ end
+ else if hdr.Channel=3 then
+ apos:=9
+ else
+ apos:=17;
+ Skip(f,apos);
+ BlockRead(f,sign,4);
+ hdr.isVBR:=sign=$676E6958; //Xing
+//calculate length
+ if hdr.isVBR then
+ begin
+ if hdr.Samplerate<>0 then
+ begin
+// Seek(f,pos+36);
+ BlockRead(f,sign,4);
+ if (sign and $01000000)<>0 then
+ begin
+ BlockRead(f,frames,4);
+ frames:=BSwap(frames);
+ hdr.Length:=Round((1152/hdr.Samplerate)*frames/(4-hdr.Version)); //!
+ end;
+ end;
+ end
+ else if hdr.Bitrate<>0 then
+ hdr.Length:=((8*(FileSize(f)-(pos-4))) div 1000) div hdr.Bitrate;
+end;
+
+function SearchStart(f:THANDLE; var l:array of byte):Boolean;
+var
+ CurPos:longint;
+ Buf:array [0..ScanSize] of byte;
+ i,j:integer;
+begin
+ CurPos:=FilePos(f)-4;
+ Seek(f,CurPos);
+ j:=BlockRead(f,Buf,ScanSize);
+ i:=0;
+ while i<j do
+ begin
+ if (i<(j-2)) and (Buf[i]=$FF) and //FF FB E4
+ ((Buf[i+1] and $E0)=$E0) and
+ ((Buf[i+2] and $F0)<>$F0) then
+ begin
+ Seek(f,CurPos+i);
+ BlockRead(f,l,4);
+ result:=true;
+ Exit;
+ end;
+ inc(i);
+ end;
+ result:=false;
+end;
+
+function ReadMP3(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ l:array [0..3] of byte;
+ hdr:tMP3FrameHdr;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,l,4);
+// if l[0]<>$FF then
+ if not SearchStart(f,l) then
+ Exit;
+ TranslateFrameHdr(l,hdr);
+ CheckVBR(f,hdr);
+ Info.kbps :=hdr.Bitrate;
+ Info.khz :=hdr.Samplerate div 1000;
+ Info.total:=hdr.Length;
+ if hdr.Channel=3 then
+ Info.channels:=1
+ else
+ Info.channels:=2;
+ Info.vbr:=ord(hdr.isVBR);
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+const
+ mpgAudio = 1;
+ mpgVideo = 2;
+ mpgVersion = 4;
+
+type
+ l2b=array [0..3] of byte;
+
+function ReadDWord(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if (p+4)<endptr then
+ begin
+ result:=pDWord(p)^;
+ inc(p,4);
+ end
+ else
+ result:=-1;
+end;
+
+function ReadWord(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if (p+2)<endptr then
+ begin
+ result:=pWord(p)^;
+ inc(p,2);
+ end
+ else
+ result:=-1;
+end;
+
+function ReadByte(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if p<endptr then
+ begin
+ result:=pByte(p)^;
+ inc(p);
+ end
+ else
+ result:=-1;
+end;
+
+function ChunkRead(var p:pAnsiChar;endptr:pAnsiChar):integer;
+var
+ i:integer;
+begin
+ repeat
+ if ReadByte(p,endptr)=0 then
+ if ReadByte(p,endptr)=0 then
+ begin
+ repeat
+ i:=ReadByte(p,endptr);
+ until i<>0;
+ if i=1 then
+ begin
+ result:=ReadByte(p,endptr) or $100;
+ exit;
+ end;
+ end;
+ until p>=endptr;
+ result:=0;
+end;
+
+const
+ BufSize = 256*1024;
+
+function ReadMPG(var Info:tSongInfo):boolean; cdecl;
+var
+ endptr,buf,p:PAnsiChar;
+ f:THANDLE;
+ BlockType:integer;
+ l:dword;
+ w:word;
+ b:byte;
+ flag:integer;
+ version,layer:integer;
+// vbitrate:integer;
+// FrmCnt:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ flag:=mpgAudio+mpgVideo+mpgVersion;
+
+ mGetMem(buf,BufSize);
+ endptr:=buf+BlockRead(f,buf^,BufSize);
+ CloseHandle(f);
+ p:=buf;
+// FrmCnt:=0;
+ while (flag<>0) and (p<endptr) do
+ begin
+ BlockType:=ChunkRead(p,endptr);
+ case BlockType of
+ $1BA: begin // pack
+ if (flag and mpgVersion)<>0 then
+ begin
+ flag:=flag and not mpgVersion;
+ if (ReadByte(p,endptr) and $C0)=$40 then
+ Info.codec:=$3247504D // MPG2
+ else
+ Info.codec:=$4745504D; // MPEG
+ end;
+ end;
+ $1B3: begin // Video
+ if (flag and mpgVideo)<>0 then
+ begin
+ l:=ReadDWord(p,endptr);
+ flag:=flag and not mpgVideo;
+ Info.width :=((l2b(l)[1] and $F0) shr 4)+(l2b(l)[0] shl 4);
+ Info.height:=((l2b(l)[1] and $0F) shl 8)+l2b(l)[2];
+ case l2b(l)[3] and $F of
+ 1: Info.fps:=2397;
+ 2: Info.fps:=2400;
+ 3: Info.fps:=2500;
+ 4: Info.fps:=2997;
+ 5: Info.fps:=3000;
+ 6: Info.fps:=5000;
+ 7: Info.fps:=5994;
+ 8: Info.fps:=6000;
+ end;
+// BlockRead(f,l,4);
+// vbitrate:=(l2b(l)[0] shl 10)+(l2b(l)[1] shl 2)+(l2b(l)[2] shr 6);
+ end;
+ end;
+ 0,$1B7,$1B9: break;
+{
+ $1E0: begin
+ BlockRead(f,w,2);
+ w:=swap(w);
+ mGetMem(buf,w);
+ BlockRead(f,buf^,w);
+ p:=buf;
+ for l:=0 to w-4 do
+ begin
+ if pdword(p)^=$00010000 then
+ begin
+ inc(FrmCnt);
+ inc(p,4);
+ end
+ else
+ inc(p);
+ end;
+ mFreeMem(buf);
+ end;
+}
+ $1C0: begin // audio
+ w:=swap(ReadWord(p,endptr));
+ if flag and mpgAudio<>0 then
+ begin
+ flag:=flag and not mpgAudio;
+ b:=ReadByte(p,endptr);
+ dec(w);
+ if (b and $C0)=$80 then
+ begin
+ b:=ReadByte(p,endptr);
+ l:=ReadByte(p,endptr);
+ dec(w,2);
+ if (b and $80)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ dec(l,5);
+ if (b and $40)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ dec(l,5);
+ end;
+ end;
+ if l>0 then
+ begin
+ inc(p,l);
+ dec(w,l);
+ end;
+ end
+ else
+ begin
+ while (b and $80)<>0 do
+ begin
+ dec(w);
+ if w=0 then break;
+ b:=ReadByte(p,endptr);
+ end;
+ if (b and $40)<>0 then
+ begin
+ inc(p);
+ b:=ReadByte(p,endptr);
+ dec(w,2);
+ end;
+ if (b and $20)<>0 then
+ begin
+ inc(p,4);
+ dec(w,4);
+ if (b and $10)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ end;
+ end;
+ end;
+ l:=ReadDWord(p,endptr);
+ version:=(l2b(l)[1] and $18) shr 3;
+ layer :=(l2b(l)[1] and $06) shr 1;
+ Info.kbps :=btable[version and 1][layer-1][l2b(l)[2] shr 4];
+ Info.khz :=(stable[version][(l2b(l)[2] and $0C) shr 2]) div 1000;
+ Info.channels:=l2b(l)[3] shr 6;
+ if Info.channels=3 then
+ Info.channels:=1
+ else
+ Info.channels:=2;
+// if w>0 then inc(p,w);
+ end;
+// else
+ inc(p,w);
+ end;
+{
+ $1B5: begin
+ BlockRead(f,l,4);
+ if (l2b(l)[0] and $F0)=$10 then
+ begin
+ vbitrate:=vbitrate+
+ ((((l2b(l)[2] and $1F) shl 7)+(l2b(l)[3] shr 1)) shl 18);
+ end;
+ end;
+}
+{
+ $1BD: begin
+ end;
+}
+ $1C1..$1DF, // audio
+//?? $1E0,
+ $1E1..$1EF, // video
+ $1BB{,$1BD},$1BE,$1BF: begin // system,private,padding,private
+ inc(p,swap(ReadWord(p,endptr)));
+ end;
+ end;
+ end;
+// vbitrate:=(vbitrate*400) div 1000;
+// Info.total:=(FrmCnt*100) div Info.fps;
+ mFreeMem(buf);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkMP3,
+ LocalFormatLinkMPG,
+ LocalFormatLinkMPEG:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkMP3.Next:=FormatLink;
+
+ LocalFormatLinkMP3.This.proc :=@ReadMP3;
+ LocalFormatLinkMP3.This.ext :='MP3';
+ LocalFormatLinkMP3.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkMP3;
+
+ LocalFormatLinkMPG.Next:=FormatLink;
+
+ LocalFormatLinkMPG.This.proc :=@ReadMPG;
+ LocalFormatLinkMPG.This.ext :='MPG';
+ LocalFormatLinkMPG.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMPG;
+
+ LocalFormatLinkMPEG.Next:=FormatLink;
+
+ LocalFormatLinkMPEG.This.proc :=@ReadMPG;
+ LocalFormatLinkMPEG.This.ext :='MPEG';
+ LocalFormatLinkMPEG.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMPEG;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_mpc.pas b/plugins/Watrack/formats/fmt_mpc.pas
new file mode 100644
index 0000000000..7d8671dde6
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mpc.pas
@@ -0,0 +1,90 @@
+{MPC file format}
+unit fmt_MPC;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMPC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ DefID = $002B504D;// 'MP+'
+
+function ReadMPC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ tmp:array [0..5] of dword;
+ samples,TotalFrames:dword;
+ lastframe:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+
+ BlockRead(f,tmp,SizeOf(tmp));
+ if ((tmp[0] and $FFFFFF)=DefID) and
+ (((tmp[0] shr 24) and $0F)>=7) then // sv7-sv8
+ begin
+ Info.kbps:=0;
+ if (tmp[2] and 2)<>0 then
+ Info.channels:=2
+ else
+ Info.channels:=1;
+ case (tmp[2] and $3000) shr 12 of //C000-14?
+ 00: Info.khz:=44100;
+ 01: Info.khz:=48000;
+ 02: Info.khz:=37800;
+ 03: Info.khz:=32000;
+ end;
+ lastframe:=(tmp[5] and $FFF) shr 1;
+ samples:=tmp[1]*1152+lastframe;
+ end
+ else
+ begin //4-6
+ if not ((tmp[0] and $1FFF) and $3FF) in [4..6] then
+ exit;
+ Info.khz:=44100;
+ Info.kbps:=tmp[1] and $1F;
+ if ((tmp[0] and $1FFF) and $3FF)=4 then
+ TotalFrames:=loword(tmp[2])
+ else
+ TotalFrames:=tmp[2];
+ samples:=TotalFrames*1152;
+ end;
+
+ if Info.khz<>0 then
+ Info.total:=samples div Info.khz;
+ Info.khz:=Info.khz div 1000;
+ if (Info.kbps=0) and (samples<>0) then
+// if fs=samples*channels*deep/8 then kbps=khz*deep*channels/1152
+// Info.kbps:=(Info.khz*8)*taginfo.FileSize/1152/samples;
+
+ Info.kbps:=(Info.khz div 8)*FileSize(f) div samples; //!!
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadMPC;
+ LocalFormatLink.This.ext :='MPC';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_ofr.pas b/plugins/Watrack/formats/fmt_ofr.pas
new file mode 100644
index 0000000000..73d58b68ff
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_ofr.pas
@@ -0,0 +1,74 @@
+{OFR file}
+unit fmt_OFR;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadOFR(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+type
+ tMain = packed record
+ ID :dword; // 'OFR '
+ Size :dword; //15
+ SamplesLo :dword;
+ SamplesHi :word;
+ SampleType :byte;
+ ChannelsMap:byte;
+ Samplerate :dword;
+ Encoder :word;
+ Compression:byte;
+ end;
+
+function ReadOFR(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ Hdr:tMain;
+ Samples:int64;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,Hdr,SizeOf(Hdr));
+ Samples:=Hdr.SamplesLo+Hdr.SamplesHi*$10000;
+ Info.channels:=Hdr.ChannelsMap+1;
+ Info.khz :=Hdr.Samplerate div 1000;
+ Info.total :=(Samples div Info.channels)*Info.khz;
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkOFR,
+ LocalFormatLinkOFS:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkOFR.Next:=FormatLink;
+
+ LocalFormatLinkOFR.This.proc :=@ReadOFR;
+ LocalFormatLinkOFR.This.ext :='OFR';
+ LocalFormatLinkOFR.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOFR;
+
+ LocalFormatLinkOFS.Next:=FormatLink;
+
+ LocalFormatLinkOFS.This.proc :=@ReadOFR;
+ LocalFormatLinkOFS.This.ext :='OFS';
+ LocalFormatLinkOFS.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOFS;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_ogg.pas b/plugins/Watrack/formats/fmt_ogg.pas
new file mode 100644
index 0000000000..4b05b80c2c
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_ogg.pas
@@ -0,0 +1,522 @@
+{OGG, SPX and FLAC file formats}
+unit fmt_OGG;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadOGG(var Info:tSongInfo):boolean; cdecl;
+function ReadSPX(var Info:tSongInfo):boolean; cdecl;
+function ReadfLaC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format,base64,utils;
+
+const
+ OGGSign = $5367674F; //OggS
+const
+ SpeexID = 'Speex ';
+type
+ tSPEXHeader = packed record
+ speex_string :array [0..7] of AnsiChar;
+ speex_version :array [0..19] of AnsiChar;
+ speex_version_id:dword;
+ header_size :dword; //sizeof(tSPEXHeader)
+ rate :dword;
+ mode :dword;
+ bitstrm_version :dword;
+ nb_channels :dword;
+ bitrate :dword;
+ frame_size :dword;
+ vbr :dword;
+ fpp :dword; //frames_per_packet
+ extra_headers :dword;
+ reserved1 :dword;
+ reserved2 :dword;
+ end;
+type
+ pOGGHdr = ^tOGGHdr;
+ tOGGHdr = packed record
+ ID :dword;
+ Version :byte;
+ HdrType :byte;
+ Granule :Int64; // absolute position
+ BitStrmSN:dword;
+ PageSeqN :dword;
+ CRC :dword;
+ PageSegs :byte;
+ end;
+const
+ strmOGG = 1;
+ strmOGM = 2;
+const
+ VideoD = $65646976;
+ VideoW = $006F;
+ VorbisD = $62726F76;
+ VorbisW = $7369;
+type
+ tOGMInfo = packed record
+ padding :word; // 0
+ codec :dword;
+ size :dword;
+ time_unit :int64; // 1/10000000 sec
+ samples_per_unit:int64; // fps = 10000000*spu/time_unit
+ default_len :dword; // 1
+ buffersize :dword;
+ bit_per_sample :dword;
+ width :dword;
+ height :dword;
+ dummy :dword; // 0
+ end;
+
+//const VorbisStream:array [0..5] of byte = ($76,$6F,$72,$62,$69,$73); // 'vorbis'
+
+type
+ tOGGInfo = packed record
+ version :dword;
+ Channels :byte;
+ samplerate:dword;
+ maxkbps :dword;
+ nominal :dword;
+ minkbps :dword;
+ BlockSizes:byte;
+ dummy :byte;
+ end;
+
+//--------------- fLaC section ---------------
+const
+ fLaCSign = $43614C66; //fLaC
+{
+0 : STREAMINFO
+1 : PADDING
+2 : APPLICATION
+3 : SEEKTABLE
+4 : VORBIS_COMMENT
+5 : CUESHEET
+}
+type
+ MetaHdr = packed record
+ blocktype:byte;
+ blocklen:array [0..2] of byte;
+ end;
+type
+ StreamInfo = packed record
+ MinBlockSize:word;
+ MaxBlocksize:word;
+ MinFrameSize:array [0..2] of byte;
+ MaxFrameSize:array [0..2] of byte;
+ heap:array [0..7] of byte;
+ MD5:array [0..15] of byte;
+ end;
+
+procedure OGGGetComment(ptr:PAnsiChar;size:integer;var Info:tSongInfo);
+var
+ clen,alen,len,values:dword;
+ ls:PAnsiChar;
+ value:PAnsiChar;
+ cover:pByte;
+ ext:dword;
+ extw:int64;
+ c:AnsiChar;
+begin
+ inc(ptr,pdword(ptr)^+4); //vendor
+ values:=pdword(ptr)^; inc(ptr,4);
+ ext:=0;
+ cover:=nil;
+ clen:=0;
+ while values>0 do
+ begin
+ len:=pdword(ptr)^;
+ if len>cardinal(size) then
+ break;
+ dec(size,len);
+ inc(ptr,4);
+ ls:=ptr;
+ c:=ls[len];
+ ls[len]:=#0;
+ alen:=StrScan(ls,'=')-ls+1;
+ if alen>0 then
+ begin
+ ls[alen-1]:=#0;
+ value:=ls+alen;
+
+ if (Info.title =nil) and (lstrcmpia(ls,'TITLE' )=0) then UTF8ToWide(value,Info.title)
+ else if (Info.artist =nil) and (lstrcmpia(ls,'ARTIST' )=0) then UTF8ToWide(value,Info.artist)
+ else if (Info.album =nil) and (lstrcmpia(ls,'ALBUM' )=0) then UTF8ToWide(value,Info.album)
+ else if (Info.genre =nil) and (lstrcmpia(ls,'GENRE' )=0) then UTF8ToWide(value,Info.genre)
+ else if (Info.year =nil) and (lstrcmpia(ls,'DATE' )=0) then UTF8ToWide(value,Info.year)
+ else if (Info.comment=nil) and (lstrcmpia(ls,'COMMENT')=0) then UTF8ToWide(value,Info.comment)
+ else if (Info.lyric =nil) and (lstrcmpia(ls,'LYRICS' )=0) then UTF8ToWide(value,Info.lyric)
+
+ else if (Info.track=0) and (lstrcmpia(ls,'TRACKNUMBER')=0) then Info.track:=StrToInt(value)
+
+ else if (cover=nil) and (lstrcmpia(ls,'COVERART')=0) then clen:=Base64Decode(value,cover)
+ else if lstrcmpia(ls,'COVERARTMIME')=0 then ext:=GetImageType(nil,value);
+ end;
+ dec(values);
+ inc(ptr,len);
+ ptr^:=c;
+ end;
+
+ if cover<>nil then
+ begin
+ if ext=0 then
+ ext:=GetImageType(cover);
+ if ext<>0 then
+ begin
+ FastAnsiToWideBuf(PAnsiChar(@ext),pWideChar(@extw));
+ Info.cover:=SaveTemporaryW(cover,clen,PWideChar(@extw));
+ end;
+ mFreeMem(cover);
+ end;
+
+end;
+
+function CalcSize(num:integer;var arr:array of byte):integer;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to num-1 do
+ begin
+ inc(result,arr[i]);
+ if arr[i]<$FF then break;
+ end;
+end;
+
+function ReadSPX(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ OGGHdr:tOGGHdr;
+ SPXHdr:tSPEXHeader;
+ buf:array [0..255] of byte;
+ ptr:PAnsiChar;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ Skip(f,OGGHdr.PageSegs);
+ if OGGHdr.ID=OGGSign then
+ begin
+ BlockRead(f,SPXHdr,SizeOf(SPXHdr));
+ if SPXHdr.speex_string<>SpeexID then
+ begin
+ CloseHandle(f);
+ exit;
+ end;
+ Info.khz:=SPXHdr.rate div 1000;
+ Info.vbr:=SPXHdr.vbr;
+ if integer(SPXHdr.bitrate)<>-1 then
+ Info.kbps:=SPXHdr.bitrate div 1000;
+
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ BlockRead(f,buf,OGGHdr.PageSegs);
+ size:=CalcSize(OGGHdr.PageSegs,buf);
+ GetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ OGGGetComment(ptr,size,Info);
+ FreeMem(ptr);
+
+ result:=true;
+ end;
+ CloseHandle(f);
+end;
+
+function Compare(const sign:array of byte):integer;
+type
+ conv=packed record
+ d:dword;w:word;
+ end;
+var
+ p:^conv;
+begin
+ p:=@sign;
+ if (p^.d=VideoD) and (p^.w=VideoW) then
+ result:=strmOGM
+ else if (p^.d=VorbisD) and (p^.w=VorbisW) then
+ result:=strmOGG
+ else
+ result:=0;
+end;
+
+function ReadOGG(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ OGGHdr:tOGGHdr;
+ tmp:packed record
+ paktype:byte;
+ sign:array [0..5] of byte;
+ end;
+ OGGInfo:tOGGInfo;
+ OGMInfo:tOGMInfo;
+ fpos:dword;
+ SPXHdr:tSPEXHeader;
+ i,j:integer;
+ DataIndex:integer;
+ buf:array [0..255] of byte;
+ fsize:dword;
+ done:integer;
+ ptr:PAnsiChar;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ tmp.paktype:=0;
+ fsize:=FileSize(f);
+ done:=0;
+ while (done<>3) and (tmp.paktype<>5) and (FilePos(f)<fsize) do
+ begin
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ if OGGHdr.ID=OGGSign then
+ begin
+ BlockRead(f,buf,OGGHdr.PageSegs);
+ size:=CalcSize(OGGHdr.PageSegs,buf);
+// sum pages to size obtain and number of groups
+// for i:=0 to OGGHdr.PageSegs-1 do
+// only first fragment
+ begin
+ fpos:=FilePos(f);
+ BlockRead(f,tmp,SizeOf(tmp));
+ if tmp.paktype=5 then
+ break;
+ if tmp.paktype=1 then
+ begin
+ case Compare(tmp.sign) of
+ strmOGG: begin
+ BlockRead(f,OGGInfo,SizeOf(OGGInfo));
+ if integer(OGGInfo.nominal)>0 then
+ Info.kbps :=OGGInfo.nominal div 1000;
+ Info.khz :=OGGInfo.samplerate;
+ Info.channels:=OGGInfo.Channels;
+ done:=done or 1;
+ end;
+ strmOGM: begin
+ BlockRead(f,OGMInfo,SizeOf(OGMInfo));
+ Info.codec :=OGMInfo.codec;
+ Info.fps :=round(((10000000*OGMInfo.samples_per_unit) / OGMInfo.time_unit)*100);
+ Info.width :=OGMInfo.width;
+ Info.height:=OGMInfo.height;
+ done:=done or 1;
+ end;
+ end;
+ end
+ else if tmp.paktype=ORD('S') then //maybe SPX
+ begin
+ Seek(f,fpos);
+ BlockRead(f,SPXHdr,SizeOf(SPXHdr));
+ if SPXHdr.speex_string<>SpeexID then
+ begin
+ CloseHandle(f);
+ exit;
+ end;
+ Info.khz:=SPXHdr.rate div 1000;
+ if integer(SPXHdr.bitrate)<>-1 then
+ Info.kbps:=SPXHdr.bitrate div 1000;
+ done:=done or 1;
+ end
+ else if tmp.paktype=3 then
+ begin
+ GetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ OGGGetComment(ptr,size,Info);
+ FreeMem(ptr);
+ done:=done or 2;
+ end
+ else
+ continue;
+ result:=true;
+ end;
+ end;
+ end;
+ // try to get length
+ DataIndex:=FileSize(f)-10;
+ for i:=1 to 50 do
+ begin
+ dec(DataIndex,SizeOf(buf)-10);
+ Seek(f,DataIndex);
+ BlockRead(f,buf,SizeOf(buf));
+ { Get number of PCM samples from last Ogg packet header }
+ j:=SizeOf(buf)-10;
+ repeat
+ if pOGGHdr(@buf[j])^.ID=OGGSign then
+ begin
+ if j>(SizeOf(buf)-SizeOf(tOGGHdr)) then
+ begin
+ Seek(f,DataIndex+j);
+ BlockRead(f,buf,SizeOf(tOGGHdr));
+ j:=0;
+ end;
+ if Info.fps>0 then
+ begin
+ Info.total:=(pOGGHdr(@buf[j])^.Granule*100) div Info.fps;
+ end
+ else if Info.khz<>0 then
+ Info.total:=pOGGHdr(@buf[j])^.Granule div Info.khz;
+ break;
+ end;
+ dec(j);
+ until j=0;
+ if Info.total>0 then break;
+ end;
+ Info.khz:=Info.khz div 1000;
+ CloseHandle(f);
+end;
+
+function ReadfLaC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ data64:int64;
+ hdr:MetaHdr;
+ frm:StreamInfo;
+ id:dword;
+ flag:integer;
+ size:dword;
+ buf,ptr:PAnsiChar;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,id,SizeOf(id));
+ if id=fLaCSign then
+ begin
+ flag:=0;
+ repeat
+ BlockRead(f,hdr,SizeOf(hdr));
+ size:=hdr.blocklen[2]+(hdr.blocklen[1] shl 8)+(hdr.blocklen[0] shl 16);
+ case (hdr.blocktype and $7F) of
+ 0: begin
+ if flag=0 then
+ begin
+ BlockRead(f,frm,SizeOf(frm));
+ //samplerate eg.44100
+ Info.khz:=((frm.heap[0] shl 12)+(frm.heap[1] shl 4)+(frm.heap[2] shr 4));
+ Info.channels:=((frm.heap[2] and $F) shr 1)+1;
+ //bits per SAMPLE now
+ Info.kbps:=(frm.heap[2] and 1) shl 4+(frm.heap[3] shr 4)+1;
+ data64:=((frm.heap[3] and $F) shl 32)+(frm.heap[4] shl 24)+
+ (frm.heap[5] shl 16)+(frm.heap[6] shl 8)+frm.heap[7];
+
+ if (data64<>0) and (Info.khz<>0) then
+ Info.total:=data64 div Info.khz;
+ Info.kbps:=Info.kbps*8;
+Info.kbps:=trunc(FileSize(f)*8/1000);
+ Info.khz:=Info.khz div 1000;
+ flag:=1;
+ end;
+ end;
+ 4: begin
+ GetMem(buf,size);
+ BlockRead(f,buf^,size);
+ OGGGetComment(buf,size,Info);
+ FreeMem(buf);
+ end;
+ 6: begin
+ if Info.cover=nil then
+ begin
+ GetMem(buf,size);
+ BlockRead(f,buf^,size);
+ ptr:=buf;
+ id:=BSwap(pdword(ptr)^);
+ case id of
+ 0,3,4,6: begin
+ inc(ptr,4);
+ id:=BSwap(pdword(ptr)^); // mime size
+ inc(ptr,4);
+ flag:=GetImageType(nil,ptr);
+ inc(ptr,id+4*5); // width, height, depth etc.
+ id:=BSwap(pdword(ptr)^); // image size
+ inc(ptr,4);
+ if flag=0 then
+ flag:=GetImageType(pByte(ptr));
+ FastAnsiToWideBuf(PAnsiChar(@flag),pWideChar(@data64));
+ Info.cover:=SaveTemporaryW(ptr,id,PWideChar(@data64));
+ end;
+ end;
+ FreeMem(buf);
+ end
+ else
+ Skip(f,size);
+ end
+ else
+ begin
+ if (hdr.blocktype and $80)<>0 then
+ break;
+ Skip(f,size);
+ end;
+ end;
+ until (hdr.blocktype and $80)<>0;
+ end;
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkOGG,
+ LocalFormatLinkOGA,
+ LocalFormatLinkOGM,
+ LocalFormatLinkSPX,
+ LocalFormatLinkFLA,
+ LocalFormatLinkFLAC:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkOGG.Next:=FormatLink;
+
+ LocalFormatLinkOGG.This.proc :=@ReadOGG;
+ LocalFormatLinkOGG.This.ext :='OGG';
+ LocalFormatLinkOGG.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOGG;
+
+ LocalFormatLinkOGA.Next:=FormatLink;
+
+ LocalFormatLinkOGA.This.proc :=@ReadOGG;
+ LocalFormatLinkOGA.This.ext :='OGA';
+ LocalFormatLinkOGA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOGA;
+
+ LocalFormatLinkOGM.Next:=FormatLink;
+
+ LocalFormatLinkOGM.This.proc :=@ReadOGG;
+ LocalFormatLinkOGM.This.ext :='OGM';
+ LocalFormatLinkOGM.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkOGM;
+
+ LocalFormatLinkSPX.Next:=FormatLink;
+
+ LocalFormatLinkSPX.This.proc :=@ReadSPX;
+ LocalFormatLinkSPX.This.ext :='SPX';
+ LocalFormatLinkSPX.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkSPX;
+
+ LocalFormatLinkFLA.Next:=FormatLink;
+
+ LocalFormatLinkFLA.This.proc :=@ReadfLaC;
+ LocalFormatLinkFLA.This.ext :='FLA';
+ LocalFormatLinkFLA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkFLA;
+
+ LocalFormatLinkFLAC.Next:=FormatLink;
+
+ LocalFormatLinkFLAC.This.proc :=@ReadfLaC;
+ LocalFormatLinkFLAC.This.ext :='FLAC';
+ LocalFormatLinkFLAC.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkFLAC;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_real.pas b/plugins/Watrack/formats/fmt_real.pas
new file mode 100644
index 0000000000..8d5f5bf72d
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_real.pas
@@ -0,0 +1,335 @@
+{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
+ len:integer;
+begin
+ if alen=2 then
+ len:=(ord(p[0]) shl 8)+ord(p[1])
+ else
+ len:=ord(p[0]);
+ inc(p,alen);
+// if len>0 then
+ inc(p,len);
+end;
+
+function ReadStr(var p:PAnsiChar;alen:integer):PAnsiChar;
+var
+ len:integer;
+begin
+ if alen=2 then
+ len:=(ord(p[0]) shl 8)+ord(p[1])
+ else
+ len:=ord(p[0]);
+ inc(p,alen);
+ if len>0 then
+ begin
+ mGetMem(result,len+1);
+ move(p^,result^,len);
+ result[len]:=#0;
+ inc(p,len);
+ 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.
diff --git a/plugins/Watrack/formats/fmt_tta.pas b/plugins/Watrack/formats/fmt_tta.pas
new file mode 100644
index 0000000000..c13b329fe2
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_tta.pas
@@ -0,0 +1,65 @@
+{TTA file}
+unit fmt_TTA;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadTTA(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ TTA1_SIGN = $31415454;
+type
+ tTTAHeader = packed record
+ id :dword;
+ format :word;
+ channels :word;
+ bitspersample:word;
+ samplerate :dword;
+ datalength :dword;
+ crc32 :dword;
+ end;
+
+function ReadTTA(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ hdr:tTTAHeader;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,hdr,SizeOf(tTTAHeader));
+ if hdr.id<>TTA1_SIGN then
+ exit;
+ Info.channels:=hdr.channels;
+ Info.khz :=hdr.samplerate;
+ Info.kbps :=hdr.bitspersample div 1000; //!!
+ if hdr.samplerate<>0 then
+ Info.total:=hdr.datalength div hdr.samplerate;
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadTTA;
+ LocalFormatLink.This.ext :='TTA';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_wav.pas b/plugins/Watrack/formats/fmt_wav.pas
new file mode 100644
index 0000000000..98d8e18fb8
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_wav.pas
@@ -0,0 +1,146 @@
+{WAV processing}
+unit fmt_WAV;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadWAV(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ wavRIFF = $46464952;
+ wavWAVE = $45564157;
+ wavfmt_ = $20746D66;
+ wavfact = $74636166;
+ wavdata = $61746164;
+type
+ tWAVChunk = packed record
+ id :dword;
+ size:dword;
+ end;
+type
+ tWAVFormatChunk = packed record
+ Codec :word;
+ Channels :word;
+ SampleRate :dword;
+ AvgBPS :dword;
+ BlockAlign :word;
+ BitsPerSample:word;
+ end;
+
+const
+ WavPackID = $6B707677;
+type
+// ckID :dword; // "wvpk"
+// ckSize :dword; // size of entire frame (minus 8, of course)
+ tWavPackHeader = packed record
+ version :word; // 0x403 for now
+ track_no :byte; // track number (0 if not used, like now)
+ index_no :byte; // track sub-index (0 if not used, like now)
+ total_samples:dword; // for entire file (-1 if unknown)
+ block_index :dword; // index of first sample in block (to file begin)
+ block_samples:dword; // # samples in This block
+ flags :dword; // various flags for id and decoding
+ crc :dword; // crc for actual decoded data
+ end;
+
+function ReadWAV(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ chunk:tWAVChunk;
+ fmtchunk:tWAVFormatChunk;
+ tmp:dword;
+ WPH:tWavPackHeader;
+ fsize:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,chunk,SizeOf(chunk));
+ if chunk.id=WavPackID then
+ begin
+ BlockRead(f,WPH,SizeOf(tWavPackHeader));
+ BlockRead(f,tmp,2); //!! $1621 33,22
+ BlockRead(f,chunk,SizeOf(chunk));
+ end
+ else
+ begin
+ WPH.version:=0;
+ integer(WPH.total_samples):=-1;
+ end;
+ if chunk.id<>wavRIFF then
+ exit;
+ BlockRead(f,chunk,SizeOf(dword));
+ if chunk.id<>wavWAVE then
+ exit;
+ BlockRead(f,chunk,SizeOf(chunk));
+ if chunk.id<>wavfmt_ then
+ exit;
+ BlockRead(f,fmtchunk,SizeOf(tWAVFormatChunk));
+ Info.channels:=fmtchunk.Channels;
+ Info.khz :=fmtchunk.SampleRate div 1000;
+ if chunk.size>SizeOf(tWAVFormatChunk) then
+ Skip(f,chunk.size-SizeOf(tWAVFormatChunk));
+ fsize:=FileSize(f);
+ while FilePos(f)<fsize do
+ begin
+ BlockRead(f,chunk,SizeOf(chunk));
+ if chunk.id=wavfact then
+ begin
+ BlockRead(f,tmp,4);
+ break;
+ end;
+ if chunk.id=wavdata then
+ begin
+ tmp:=chunk.size;
+ break;
+ end;
+ Skip(f,chunk.size);
+ end;
+ if WPH.version<>0 then
+ begin
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ end;
+ if integer(WPH.total_samples)=-1 then
+ if (fmtchunk.BitsPerSample<>0) and (fmtchunk.Channels<>0) then
+ WPH.total_samples:=(tmp*8) div (fmtchunk.Channels*fmtchunk.BitsPerSample);
+ if fmtchunk.SampleRate<>0 then
+ Info.total:= WPH.total_samples div fmtchunk.SampleRate;
+ if Info.total<>0 then
+ Info.kbps:=tmp*8 div Info.total div 1000;
+
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkWAV,
+ LocalFormatLinkWV:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkWAV.Next:=FormatLink;
+
+ LocalFormatLinkWAV.This.proc :=@ReadWAV;
+ LocalFormatLinkWAV.This.ext :='WAV';
+ LocalFormatLinkWAV.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkWAV;
+
+ LocalFormatLinkWV.Next:=FormatLink;
+
+ LocalFormatLinkWV.This.proc :=@ReadWAV;
+ LocalFormatLinkWV.This.ext :='WV';
+ LocalFormatLinkWV.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkWV;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_wma.pas b/plugins/Watrack/formats/fmt_wma.pas
new file mode 100644
index 0000000000..ed575147ac
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_wma.pas
@@ -0,0 +1,438 @@
+{WMA file format}
+unit fmt_WMA;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadWMA(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format,utils;
+
+const
+ ASF_Header_Object :tGUID='{75B22630-668E-11CF-A6D9-00AA0062CE6C}';
+
+ ASF_Header_Extension_Object :tGUID='{5FBF03B5-A92E-11CF-8EE3-00C00C205365}';
+ ASF_Content_Description_Object :tGUID='{75B22633-668E-11CF-A6D9-00AA0062CE6C}';
+ ASF_Extended_Content_Description_Object:tGUID='{D2D0A440-E307-11D2-97F0-00A0C95EA850}';
+ ASF_File_Properties_Object :tGUID='{8CABDCA1-A947-11CF-8EE4-00C00C205365}';
+ ASF_Stream_Properties_Object :tGUID='{B7DC0791-A9B7-11CF-8EE6-00C00C205365}';
+
+ ASF_Metadata_Library_Object :tGUID='{44231C94-9498-49D1-A141-1D134E457054}';
+ ASF_Audio_Media :tGUID='{F8699E40-5B4D-11CF-A8FD-00805F5C442B}';
+ ASF_Video_Media :tGUID='{BC19EFC0-5B4D-11CF-A8FD-00805F5C442B}';
+
+type
+ tSize=Int64;
+
+function CompareGUID(const guid1,guid2:tGUID):boolean;
+var
+ i:integer;
+ p1,p2:PAnsiChar;
+begin
+ p1:=PAnsiChar(@guid1);
+ p2:=PAnsiChar(@guid2);
+ for i:=0 to 15 do
+ begin
+ if p1^<>p2^ then
+ begin
+ result:=false;
+ exit;
+ end;
+ inc(p1);
+ inc(p2);
+ end;
+ result:=true;
+end;
+
+function ReadGUID(var buf:PAnsiChar; var guid:pGUID):dword;
+var
+ size:tSize;
+begin
+ guid:=pointer(buf);
+ inc(buf,SizeOf(tGUID));
+ move(buf^,size,SizeOf(size));
+ inc(buf,SizeOf(size));
+ result:=size-SizeOf(tGUID)-SizeOf(size);
+end;
+
+procedure ReadWMATagStr(var dst:pWideChar;ptr:PAnsiChar;alen:word);
+begin
+ if pword(ptr)^<>0 then
+ begin
+ mGetMem(dst,alen);
+ move(pWideChar(ptr{+2})^,dst^,alen);
+ end;
+end;
+
+function ReadWMATagStr1(var dst:pWideChar;var ptr:PAnsiChar;value:boolean=true):integer;
+var
+ len,typ:word;
+begin
+ if value then
+ begin
+ typ:=pword(ptr)^;
+ inc(ptr,2); //value type
+ end
+ else
+ typ:=0;
+ len:=pword(ptr)^;
+ result:=-1;
+ dst:=nil;
+ if len<>0 then
+ begin
+ if typ=0 then
+ begin
+ mGetMem(dst,len);
+ move(PAnsiChar(ptr+2)^,PAnsiChar(dst)^,len);
+ end
+ else
+ begin
+ result:=pword(ptr+2)^;
+ if typ<5 then
+ result:=pword(ptr+4)^*$10000+result;
+ end;
+ end;
+ inc(ptr,len+2);
+end;
+
+procedure ProcessPicture(ptr:PAnsiChar;var Info:tSongInfo);
+var
+ extw:int64;
+ aSize:dword;
+begin
+ if Info.cover<>nil then exit;
+ case ptr^ of
+ #0,#3,#4,#6: ;
+ else
+ exit;
+ end;
+ inc(ptr);
+ aSize:=pdword(ptr)^; inc(ptr,4);
+ extw:=GetImageTypeW(nil,pWideChar(ptr));
+ while pWideChar(ptr)^<>#0 do inc(ptr,2); inc(ptr,2); // mime
+ while pWideChar(ptr)^<>#0 do inc(ptr,2); inc(ptr,2); // descr
+
+ if extw=0 then
+ extw:=GetImageTypeW(pByte(ptr));
+ Info.cover:=SaveTemporaryW(ptr,aSize,pWideChar(@extw));
+end;
+
+procedure ReadHdrExtended(ptr:PAnsiChar;size:dword;var Info:tSongInfo);
+var
+ buf:PAnsiChar;
+ ls:pWideChar;
+ cnt,tmp:integer;
+ tmpguid:pGUID;
+ lsize:dword;
+begin
+ inc(ptr,SizeOf(tGUID)+2);
+ size:=pdword(ptr)^; inc(ptr,4);
+ while size>0 do
+ begin
+ if Info.cover<>nil then break;
+ lsize:=ReadGUID(ptr,tmpguid);
+ dec(size,lsize+SizeOf(tGUID)+SizeOf(tSize));
+ if CompareGUID(tmpguid^,ASF_Metadata_Library_Object) then
+ begin
+ buf:=ptr;
+ cnt:=pdword(buf)^; inc(buf,2);
+ while cnt>0 do
+ begin
+ inc(buf,4); // lang & stream
+ {tmp:=pword (buf)^;} inc(buf,2); // namelen
+ {tmp:=pword (buf)^;} inc(buf,2); // datatype
+ tmp:=pdword(buf)^; inc(buf,4); // datalen
+ ls:=PWideChar(buf);
+ while pWideChar(buf)^<>#0 do inc(buf,2); inc(buf,2);
+ if lstrcmpiw(ls,'WM/Picture')=0 then
+ begin
+ ProcessPicture(buf,Info);
+ inc(buf,tmp);
+ end;
+ dec(cnt);
+ end;
+ end;
+ inc(ptr,lsize);
+ end;
+end;
+
+procedure ReadExtended(ptr:PAnsiChar;size:dword;var Info:tSongInfo);
+var
+ ls,ls1,ls2:pWideChar;
+ cnt,tmp:integer;
+begin
+ cnt:=pword(ptr)^; inc(ptr,2);
+ while cnt>0 do
+ begin
+ dec(cnt);
+ ReadWMATagStr1(ls,ptr,false);
+ if lstrcmpiw(ls,'WM/AlbumTitle')=0 then
+ ReadWMATagStr1(Info.album,ptr)
+ else if (Info.lyric=nil) and (lstrcmpiw(ls,'WM/Lyrics')=0) then
+ ReadWMATagStr1(Info.lyric,ptr)
+ else if (Info.lyric=nil) and (lstrcmpiw(ls,'WM/Lyrics_Synchronised')=0) then
+ begin
+ inc(ptr,2+2);
+ inc(ptr); // timestamp type
+ if ptr^=#1 then // lyric
+ begin
+ inc(ptr);
+ tmp:=pdword(ptr)^; inc(ptr,4);
+ mGetMem(ls2,tmp);
+ Info.lyric:=ls2;
+ ls1:=pWideChar(ptr);
+ inc(ptr,tmp);
+ while ls1^<>#0 do // description
+ begin
+ inc(ls1);
+ dec(tmp,SizeOf(WideChar));
+ end;
+ inc(ls1);
+ dec(tmp,SizeOf(WideChar));
+ while tmp>0 do
+ begin
+ if PAnsiChar(ls1)^=#$0A then
+ begin
+ inc(PAnsiChar(ls1));
+ ls2^:=#$0A;
+ dec(tmp);
+ inc(ls2);
+ end;
+ while ls1^<>#0 do
+ begin
+ ls2^:=ls1^; inc(ls2); inc(ls1);
+ dec(tmp,SizeOf(WideChar));
+ end;
+ inc(ls1,1+2); // terminator + timestamp
+ dec(tmp,SizeOf(WideChar)+4);
+ end;
+ ls2^:=#0;
+// ptr:=PAnsiChar(ls1);
+ end
+ end
+ else if lstrcmpiw(ls,'WM/Genre')=0 then
+ ReadWMATagStr1(Info.genre,ptr)
+ else if lstrcmpiw(ls,'WM/Year')=0 then
+ begin
+ tmp:=ReadWMATagStr1(Info.year,ptr);
+ if tmp<>-1 then
+ IntToStr(Info.year,tmp);
+ end
+ else if lstrcmpiw(ls,'WM/Track')=0 then
+ begin
+ tmp:=ReadWMATagStr1(ls1,ptr);
+ if tmp=-1 then
+ begin
+ Info.track:=StrToInt(ls1)+1;
+ mFreeMem(ls1);
+ end
+ else
+ Info.track:=tmp;
+ end
+ else if lstrcmpiw(ls,'WM/TrackNumber')=0 then
+ begin
+ tmp:=ReadWMATagStr1(ls1,ptr);
+ if tmp=-1 then
+ begin
+ Info.track:=StrToInt(ls1);
+ mFreeMem(ls1);
+ end
+ else
+ Info.track:=tmp;
+ end
+ else if lstrcmpiw(ls,'WM/Picture')=0 then
+ begin
+ inc(ptr,2); // data type
+ tmp:=pword(ptr)^; inc(ptr,2);
+ ProcessPicture(ptr,Info);
+ inc(ptr,tmp);
+ end
+ else
+ inc(ptr,4+pword(ptr+2)^);
+ mFreeMem(ls);
+ end;
+end;
+
+procedure ReadFileProp(ptr:PAnsiChar;var Info:tSongInfo);
+type
+ pFileProp = ^tFileProp;
+ tFileProp = packed record
+ FileGUID :tGUID;
+ FileSize :tSize;
+ Creation :tSize;
+ Packets :tSize;
+ Play :tSize;
+ Send :tSize;
+ PreRoll :tSize;
+ Flags :dword;
+ minpacket :dword;
+ maxpacket :dword;
+ maxbitrate:dword;
+ end;
+begin
+ Info.total:=pFileProp(ptr)^.Play div 10000000;
+end;
+
+procedure ReadStreamProp(ptr:PAnsiChar;size:dword;var Info:tSongInfo);
+type
+ pAudio = ^tAudio;
+ tAudio=packed record // WAVEFORMATEX
+ Codec :word;
+ Channels :word;
+ Samples :dword;
+ AvgBPS :dword;
+ BlockAlign :word;
+ BitsPerSample:word;
+ size :word;
+ end;
+ pVideo = ^tVideo;
+ tVideo = packed record
+ width :dword;
+ height :dword;
+ reserved:byte;
+ size :word;
+ bitmap :BITMAPINFOHEADER;
+ end;
+ Prefix = packed record
+ StreamType :tGUID;
+ ECGUID :tGUID; // Error Correction
+ TimeOffset :int64;
+ DataLength :dword;
+ ECDataLength:dword;
+ Flags :word;
+ Reserved :dword;
+ end;
+
+var
+ tmpguid:pGUID;
+begin
+ tmpguid:=pointer(ptr);
+ inc(ptr,SizeOf(Prefix)); //ofset to Type-Specific Data
+ if CompareGUID(tmpguid^,ASF_Audio_Media) then
+ begin
+ Info.channels:=pAudio(ptr)^.Channels;
+ Info.khz :=pAudio(ptr)^.Samples div 1000;
+ Info.kbps :=(pAudio(ptr)^.AvgBPS*8) div 1000;
+ end
+ else if CompareGUID(tmpguid^,ASF_Video_Media) then
+ begin
+ Info.width :=pVideo(ptr)^.bitmap.biWidth; // pVideo(ptr)^.width
+ Info.height:=pVideo(ptr)^.bitmap.biHeight; // pVideo(ptr)^.height
+ Info.codec :=pVideo(ptr)^.bitmap.biCompression;
+ end
+end;
+
+procedure ReadContent(ptr:PAnsiChar;var Info:tSongInfo);
+type
+ pContent = ^tContent;
+ tContent = packed record
+ TitleLength :word;
+ AuthorLength :word;
+ CopyrightLength :word;
+ DescriptionLength:word;
+ RatingLength :word;
+ end;
+var
+ cont:pContent;
+begin
+ cont:=pointer(ptr);
+ inc(ptr,SizeOf(tContent));
+ if cont^.TitleLength>0 then //title
+ begin
+ ReadWMATagStr(Info.title,ptr,cont^.TitleLength);
+ inc(ptr,cont^.TitleLength);
+ end;
+ if cont^.AuthorLength>0 then //artist
+ begin
+ ReadWMATagStr(Info.artist,ptr,cont^.AuthorLength);
+ inc(ptr,cont^.AuthorLength);
+ end;
+ inc(ptr,cont^.CopyrightLength); //copyright
+ if cont^.DescriptionLength>0 then //comment
+ ReadWMATagStr(Info.comment,ptr,cont^.DescriptionLength);
+end;
+
+function ReadWMA(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ tmpguid:pGUID;
+ size:int64;
+ buf1,buf2:PAnsiChar;
+ HdrObjects:dword;
+ base:tGUID;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+
+ BlockRead(f,base,SizeOf(tGUID));
+ if CompareGUID(base,ASF_Header_Object) then
+ begin
+ BlockRead(f,size,SizeOf(size));
+ dec(size,SizeOf(tGUID)+SizeOf(size));
+
+ GetMem(buf1,size);
+ buf2:=buf1;
+ BlockRead(f,buf1^,size);
+ HdrObjects:=pdword(buf2)^; inc(buf2,6);
+ while HdrObjects>0 do
+ begin
+ size:=ReadGUID(buf2,tmpguid);
+ if CompareGUID(tmpguid^,ASF_Content_Description_Object) then
+ ReadContent(buf2,Info)
+ else if CompareGUID(tmpguid^,ASF_Extended_Content_Description_Object) then
+ ReadExtended(buf2,size,Info)
+ else if CompareGUID(tmpguid^,ASF_Header_Extension_Object) then
+ ReadHdrExtended(buf2,size,Info)
+ else if CompareGUID(tmpguid^,ASF_File_Properties_Object) then
+ ReadFileProp(buf2,Info)
+ else if CompareGUID(tmpguid^,ASF_Stream_Properties_Object) then
+ ReadStreamProp(buf2,size,Info);
+ inc(buf2,size);
+ dec(HdrObjects);
+ end;
+ FreeMem(buf1);
+
+ result:=true;
+ end;
+ CloseHandle(f);
+end;
+
+var
+ LocalFormatLinkWMA,
+ LocalFormatLinkWMV,
+ LocalFormatLinkASF:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkWMA.Next:=FormatLink;
+
+ LocalFormatLinkWMA.This.proc :=@ReadWMA;
+ LocalFormatLinkWMA.This.ext :='WMA';
+ LocalFormatLinkWMA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkWMA;
+
+ LocalFormatLinkWMV.Next:=FormatLink;
+
+ LocalFormatLinkWMV.This.proc :=@ReadWMA;
+ LocalFormatLinkWMV.This.ext :='WMV';
+ LocalFormatLinkWMV.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkWMV;
+
+ LocalFormatLinkASF.Next:=FormatLink;
+
+ LocalFormatLinkASF.This.proc :=@ReadWMA;
+ LocalFormatLinkASF.This.ext :='ASF';
+ LocalFormatLinkASF.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkASF;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/tag_apev2.inc b/plugins/Watrack/formats/tag_apev2.inc
new file mode 100644
index 0000000000..34ab7f2ad7
--- /dev/null
+++ b/plugins/Watrack/formats/tag_apev2.inc
@@ -0,0 +1,124 @@
+{APE tag}
+{$IFDEF Interface}
+function ReadAPEv2(buf:PAnsiChar;var Info:tSongInfo;count:integer=0):longint; overload;
+function ReadAPEv2(f:THANDLE;var Info:tSongInfo):longint; overload;
+{$ELSE}
+const
+ APESign = 'APETAGEX';
+type
+ pAPEHeader = ^tAPEHeader;
+ tAPEHeader = packed record
+ ID:array [0..7] of AnsiChar;
+ Version:dword;
+ TagSize:dword; //footer + all items
+ ItemCount:dword;
+ TagFlags:dword;
+ Reserved:array [0..7] of byte;
+ end;
+
+procedure ReadAPEValue(const buf:PAnsiChar;var dst:pWideChar;ver:dword);
+begin
+ if dst=nil then
+ if ver>1000 then
+ UTF8ToWide(buf,dst)
+ else
+ AnsiToWide(buf,dst);
+end;
+
+function ReadAPEv2(buf:PAnsiChar;var Info:tSongInfo;count:integer=0):longint;
+var
+ APE:pAPEHeader;
+ len:integer;
+ ptr,key:PAnsiChar;
+ flag:dword;
+ cf:THANDLE;
+ buf0,buf1:array [0..MAX_PATH-1] of AnsiChar;
+ b:AnsiChar;
+// extw:array [0..7] of WideChar;
+begin
+ result:=0;
+ APE:=pointer(buf);
+ if APE.ID=APESign then
+ begin
+ inc(buf,SizeOf(tAPEHeader));
+ count:=APE.ItemCount;
+ end;
+ while count>0 do
+ begin
+ len :=pdword(buf)^; inc(buf,4);
+ flag:=pdword(buf)^; inc(buf,4);
+ key:=buf;
+ while buf^<>#0 do inc(buf); inc(buf);
+
+ ptr:=buf+len;
+ b:=ptr^;
+ ptr^:=#0;
+ if lstrcmpia(key,'TITLE' )=0 then ReadAPEValue(buf,Info.title ,APE.Version)
+ else if lstrcmpia(key,'ARTIST' )=0 then ReadAPEValue(buf,Info.artist ,APE.Version)
+ else if lstrcmpia(key,'ALBUM' )=0 then ReadAPEValue(buf,Info.album ,APE.Version)
+ else if lstrcmpia(key,'COMMENT')=0 then ReadAPEValue(buf,Info.comment,APE.Version)
+ else if lstrcmpia(key,'GENRE' )=0 then ReadAPEValue(buf,Info.genre ,APE.Version)
+ else if lstrcmpia(key,'YEAR' )=0 then ReadAPEValue(buf,Info.year ,APE.Version)
+ else if lstrcmpia(key,'TRACK' )=0 then if Info.track=0 then Info.track:=StrToInt(buf)
+ else if lstrcmpia(key,'LYRICS' )=0 then ReadAPEValue(buf,Info.lyric ,APE.Version)
+ //!! must preserve multipart lyric
+ else if (lstrcmpia(key,'Cover Art (Front)')=0) or
+ (lstrcmpia(key,'Cover Art (Back)' )=0) or
+ (lstrcmpia(key,'APIC' )=0) then
+ begin
+ if Info.cover=nil then
+ begin
+ while buf^<>#0 do inc(buf); inc(buf); // point to data now
+ flag:=GetImageType(pByte(buf));
+ if flag<>0 then
+ begin
+{
+ FastAnsiToWideBuf(PAnsiChar(@flag),pWideChar(@extw));
+ Info.Cover:=SaveTemporaryW(buf,ptr-buf,PWideChar(@extw));
+}
+ GetTempPathA(SizeOf(buf0),buf0);
+ GetTempFileNameA(buf0,'wat',GetCurrentTime,buf1);
+ ChangeExt(buf1,PAnsiChar(@flag));
+
+ cf:=ReWrite(PAnsiChar(@buf1));
+ BlockWrite(cf,buf^,ptr-buf);
+ CloseHandle(cf);
+ AnsiToWide(PAnsiChar(@buf1),Info.cover);
+ end;
+ end;
+ end;
+ ptr^:=b;
+ buf:=ptr;
+ dec(count);
+ end;
+end;
+
+function ReadAPEv2(f:THANDLE;var Info:tSongInfo):longint;
+var
+ APE:tAPEHeader;
+ buf:PAnsiChar;
+ fpos:dword;
+ TagID:array [1..3] of AnsiChar;
+begin
+ result:=0;
+ fpos:=FileSize(f);
+ Seek(f,fpos-SizeOf(TID3v1Tag));
+ BlockRead(f,TagID,3);
+ if TagID=TAG1Sign then
+ dec(fpos,SizeOf(TID3v1Tag));
+ Seek(f,fpos-SizeOf(APE));
+ BlockRead(f,APE,SizeOf(APE));
+ // footer must be copied as header
+ if APE.ID=APESign then
+ begin
+ if (APE.TagFlags and $20000000)=0 then //Footer
+ begin
+ Seek(f,fpos-APE.TagSize{-SizeOf(APE)});// without header but with footer
+ GetMem(buf,APE.TagSize);
+ BlockRead(f,buf^,APE.TagSize);
+ result:=ReadAPEv2(buf,Info,APE.ItemCount);
+ FreeMem(buf);
+ end;
+ end;
+end;
+{$ENDIF}
diff --git a/plugins/Watrack/formats/tag_id3v1.inc b/plugins/Watrack/formats/tag_id3v1.inc
new file mode 100644
index 0000000000..bd1db906bb
--- /dev/null
+++ b/plugins/Watrack/formats/tag_id3v1.inc
@@ -0,0 +1,175 @@
+{ID3v1 tag}
+{$IFDEF Interface}
+const
+ TAG1Sign = 'TAG';
+type
+ TID3v1Tag = packed record
+ ID: array [0..2] of AnsiChar;
+ Title: array [0..29] of AnsiChar;
+ Artist: array [0..29] of AnsiChar;
+ Album: array [0..29] of AnsiChar;
+ Year: array [0..3] of AnsiChar;
+ Comment: array [0..28] of AnsiChar;
+ Track: byte;
+ Genre: byte;
+ end;
+
+function ReadID3v1(f:THANDLE; var Info:tSongInfo):longint;
+{$ELSE}
+const
+ Lyric1End = 'LYRICSEND';
+ LyricStart = 'LYRICSBEGIN';
+ Lyric2End = 'LYRICS200';
+ LyricEndLen = Length(Lyric1End);
+const
+ fIND = $494E44;
+ fLYR = $4C5952;
+ fEAL = $45414C;
+ fEAR = $454152;
+ fETT = $455454;
+ fIMG = $494D47;
+ fINF = $494E46;
+
+procedure ID3v1_TagCorrect(var dst:pWideChar;const tag:array of AnsiChar);
+var
+ i:integer;
+ s:array [0..31] of AnsiChar;
+begin
+ i:=High(tag);
+ move(tag,s,i+1);
+ while (i>0) and (tag[i]<=' ') do dec(i);
+ if i>0 then
+ begin
+ s[i+1]:=#0;
+ AnsiToWide(s,dst);
+ end;
+end;
+
+procedure ID3v1_GetField(ptr:PAnsiChar; var dst:pWideChar; len:integer);
+var
+ txtfield:array [0..250] of AnsiChar;
+begin
+ if dst=nil then
+ begin
+ move(ptr^,txtfield,len);
+ txtfield[len]:=#0;
+ AnsiToWide(txtfield,dst);
+ end;
+end;
+
+procedure ID3v1_CheckLyric(var Info:tSongInfo;f:THANDLE;ofs:integer);
+const
+ maxlen = 5100;
+var
+ tagHdr:array [0..9] of AnsiChar;
+ buf:array [0..maxlen] of AnsiChar;
+ ptr,ptr1:PAnsiChar;
+ i,size:integer;
+ field:dword;
+ c:dword;
+begin
+ Seek(f,ofs);
+ BlockRead(f,tagHdr,LyricEndLen);
+ tagHdr[9]:=#0;
+ if StrCmp(tagHdr,Lyric1End,LyricEndLen)=0 then
+ begin
+ if Info.lyric=nil then
+ begin
+ Seek(f,ofs-maxlen);
+ BlockRead(f,buf,maxlen);
+ buf[maxlen]:=#0;
+ ptr:=@buf;
+ for i:=0 to maxlen-Length(LyricStart) do
+ begin
+ if ptr^='L' then
+ if StrCmp(ptr,LyricStart,Length(LyricStart))=0 then
+ begin
+ AnsiToWide(ptr+Length(LyricStart),Info.lyric);
+ break;
+ end;
+ inc(ptr);
+ end;
+ end;
+ end
+ else if StrCmp(tagHdr,Lyric2End,LyricEndLen)=0 then
+ begin
+ Seek(f,ofs-6);
+ BlockRead(f,buf,6);
+ size:=StrToInt(buf);
+ if size<ofs then
+ begin
+ Seek(f,ofs-size-6);
+ mGetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ if StrCmp(ptr,LyricStart,Length(LyricStart))=0 then
+ begin
+ ptr1:=ptr+Length(LyricStart);
+
+ while ptr1<ptr+size do
+ begin
+ field:=(ORD(ptr1^) shl 16)+(ORD((ptr+1)^) shl 8)+ORD((ptr1+2)^);
+ inc(ptr1,3);
+ move(ptr1^,buf,5);
+ buf[5]:=#0;
+ i:=StrToInt(buf);
+ inc(ptr1,5);
+ case field of
+ fLYR: if Info.lyric=nil then
+ begin
+ c:=pword(ptr1+i)^;
+ pword(ptr1+i)^:=0;
+ if (pword(ptr1)^=$FFFE) or ((pword(ptr1)^=$FEFF)) then
+ begin
+ StrDupW(Info.lyric,pWidechar(ptr1));
+ ChangeUnicode(Info.lyric);
+ end
+ else
+ begin
+ AnsiToWide(ptr1,Info.lyric);
+ end;
+ pword(ptr1+i)^:=c;
+ end;
+ fEAL: ID3v1_GetField(ptr1,Info.album,i);
+ fEAR: ID3v1_GetField(ptr1,Info.artist,i);
+ fETT: ID3v1_GetField(ptr1,Info.title,i);
+// fINF:
+// fIMG:
+ end;
+ inc(ptr1,i);
+ end;
+ end;
+ mFreeMem(ptr);
+ end;
+ end;
+end;
+
+function ReadID3v1(f:THANDLE; var Info:tSongInfo):longint;
+var
+ tag:TID3v1Tag;
+ ofs:integer;
+begin
+ result:=0;
+ ofs:=FileSize(f)-SizeOf(tag);
+ Seek(f,ofs);
+ BlockRead(f,tag,SizeOf(tag));
+ if tag.ID=TAG1Sign then
+ begin
+ if Info.album =nil then ID3v1_TagCorrect(Info.album ,tag.Album);
+ if Info.artist =nil then ID3v1_TagCorrect(Info.artist ,tag.Artist);
+ if Info.title =nil then ID3v1_TagCorrect(Info.title ,tag.Title);
+ if Info.comment=nil then ID3v1_TagCorrect(Info.comment,tag.Comment);
+ if Info.year =nil then ID3v1_TagCorrect(Info.year ,tag.Year);
+ if Info.genre =nil then Info.genre:=GenreName(tag.Genre);
+ if Info.track=0 then
+ begin
+ Info.track:=tag.Track;
+ if Info.track >=32 then Info.track:=0;
+ end;
+ dec(ofs,9);
+ result:=1;
+ end
+ else
+ inc(ofs,SizeOf(tag)-9);
+ ID3v1_CheckLyric(Info,f,ofs); // +skipAPEtag
+end;
+{$ENDIF}
diff --git a/plugins/Watrack/formats/tag_id3v2.inc b/plugins/Watrack/formats/tag_id3v2.inc
new file mode 100644
index 0000000000..b1f833ea2a
--- /dev/null
+++ b/plugins/Watrack/formats/tag_id3v2.inc
@@ -0,0 +1,545 @@
+{ID3v2 tag}
+
+{$IFDEF Interface}
+function ReadID3v2(f:THANDLE; var Info:tSongInfo):longint;
+{$ELSE}
+const
+ frmTRK = $4B5254;
+ frmTT2 = $325454;
+ frmTP1 = $315054;
+ frmTAL = $4C4154;
+ frmTYE = $455954;
+ frmCOM = $4D4F43;
+ frmTCO = $4F4354;
+// frmTCM = $;'; New: 'TCOM'),
+// frmTEN = $;'; New: 'TENC'),
+// frmTCR = $;'; New: 'TCOP'),
+// frmWXX = $;'; New: 'WXXX'),
+ frmTT1 = $315454;
+// frmTLA = $;'; New: 'TLAN'),
+ frmTOA = $414F54;
+ frmULT = $544C55;
+ frmSLT = $544C53;
+ frmTXX = $585854;
+ frmPIC = $434950;
+
+ frmTIT1 = $31544954; // Content group description
+ frmTIT2 = $32544954; // Title/songname/content description
+ frmTIT3 = $33544954; // Subtitle/Description refinement
+ frmTALB = $424C4154; // Album/Movie/Show title
+ frmTOAL = $4C414F54; // Original album/movie/show title
+ frmTRCK = $4B435254; // Track number/Position in set
+ frmTYER = $52455954; // Year
+ frmTDRC = $43524454; // Year
+ frmTORY = $59524F54; // Original release year
+ frmTPE1 = $31455054; // Lead performer(s)/Soloist(s)
+ frmTPE2 = $32455054; // Band/orchestra/accompaniment
+ frmTPE3 = $33455054; // Conductor/performer refinement
+ frmTPE4 = $34455054; // Interpreted, remixed, or otherwise modified by
+ frmTOPE = $45504F54; // Original artist(s)/performer(s)
+ frmTCON = $4E4F4354; // Content type
+ frmCOMM = $4D4D4F43; // Comments
+ frmUSLT = $544C5355; // Unsynchronised lyrics
+ frmSYLT = $544C5953; // Synchronised lyrics
+ frmTXXX = $58585854; // User defined text
+ frmAPIC = $43495041; // Attached picture
+const
+ TAG2Sign = 'ID3';
+const
+ ExtIDHdrMask=$40;
+ FooterPresent=$10;
+type
+ TID3v2TagHdr = packed record
+ ID :array [0..2] of AnsiChar;
+ Version:word;
+ Flags :byte;
+ TagSize:dword;
+ end;
+ PID3v2TagHdr = ^TID3v2TagHdr;
+type
+ tID3v2FrameHdr = packed record
+ ID:dword;
+ Size:dword;
+ Flags:word;
+ end;
+ pID3v2FrameHdr = ^tID3v2FrameHdr;
+ tID3v2FrameHdrOld = packed record
+ ID : array [0..2] of byte; { Frame ID }
+ Size: array [0..2] of Byte; { Size excluding header }
+ end;
+ pID3v2FrameHdrOld = ^tID3v2FrameHdrOld;
+
+var
+ Unsync:boolean;
+
+function ID3v2_Correct(data:dword):dword;
+type
+ l2b=packed record
+ b:array [0..3] of byte;
+ end;
+begin
+ result:=l2b(data).b[3];
+ inc(result,dword(l2b(data).b[0]) shl 21);
+ inc(result,dword(l2b(data).b[1]) shl 14);
+ inc(result,dword(l2b(data).b[2]) shl 7);
+end;
+
+procedure ID3v2_ReadTagStr1(var dst:PWideChar;ptr:PAnsiChar;alen:integer;enc:integer);
+var
+ buf:PAnsiChar;
+begin
+ if (enc=0) or (enc=3) then // ANSI or UTF8
+ begin
+ if ptr^=#0 then
+ alen:=0
+ else
+ while (alen>0) and (ptr[alen-1]=#0) do dec(alen);
+
+ if alen>0 then
+ begin
+{
+ if enc=0 then
+ begin
+ StrDup(buf,ptr,alen);
+ AnsiToWide(buf,dst)
+ mFreeMem(buf);
+ end
+ else
+ UTF8ToWide(buf,dst,alen);
+}
+ StrDup(buf,ptr,alen);
+ if enc=0 then
+ AnsiToWide(buf,dst)
+ else
+ UTF8ToWide(buf,dst);
+ mFreeMem(buf);
+ end
+ end
+ else {if enc<3 then} //Unicode
+ begin
+ if pword(ptr)^>0 then
+ begin
+ alen:=alen div SizeOf(WideChar);
+
+ StrDupW(dst,pWideChar(ptr),alen);
+ ChangeUnicode(dst);
+ end;
+ end;
+end;
+
+procedure ID3v2_ReadTagStr(var dst:PWideChar;ptr:PAnsiChar;alen:integer);
+var
+ enc:byte;
+begin
+ enc:=ORD(ptr^);
+ inc(ptr);
+ dec(alen);
+ if alen>0 then
+ ID3v2_ReadTagStr1(dst,ptr,alen,enc)
+ else
+ dst:=nil;
+end;
+
+procedure ID3v2_CheckLyric(tag:integer; var dst:PWideChar;ptr:PAnsiChar;len:integer);
+var
+ org,org1:PAnsiChar;
+ orgw,ptrw:pWideChar;
+ buf:array [0..127] of AnsiChar;
+ enc:byte;
+begin
+ if dst<>NIL then exit;
+ enc:=ord(ptr^);
+ inc(ptr);
+ if tag=frmUSLT then
+ begin
+ org:=ptr;
+ inc(ptr,3); // language
+ if (enc=0) or (enc=3) then
+ begin
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end
+ else
+ begin
+ while pWord(ptr)^<>0 do inc(ptr,2);
+ inc(ptr,2);
+ end;
+ dec(len,ptr-org);
+ ID3v2_ReadTagStr1(dst,ptr,len,enc);
+ end
+ else if tag=frmSYLT then
+ begin
+ inc(ptr,4);
+ if ptr^<>#1 then exit; // 1 - lyric
+ inc(ptr);
+ mGetMem(dst,len-6);
+ FillChar(dst^,len-6,0);
+
+ if (enc=0) or (enc=3) then
+ begin
+ while ptr^<>#0 do
+ begin
+ inc(ptr);
+ dec(len);
+ end;
+ inc(ptr);
+ dec(len);
+ org:=PAnsiChar(dst);
+ while len>0 do
+ begin
+ while ptr^<>#0 do
+ begin
+ org^:=ptr^; inc(org); inc(ptr);
+ dec(len);
+ end;
+ inc(ptr,1+4); // terminator+timestamp
+ dec(len,1+4);
+ end;
+ org:=PAnsiChar(dst);
+ if enc=0 then
+ AnsiToWide(org,dst)
+ else
+ UTF8ToWide(org,dst);
+ mFreeMem(org);
+ end
+ else
+ begin
+ orgw:=dst;
+ ptrw:=pWideChar(ptr);
+ while ptrw^<>#0 do
+ begin
+ inc(ptrw);
+ dec(len,SizeOf(WideChar));
+ end;
+ inc(ptrw);
+ dec(len,SizeOf(WideChar));
+ while len>0 do
+ begin
+ while ptrw^<>#0 do
+ begin
+ orgw^:=ptrw^; inc(orgw); inc(ptrw);
+ dec(len,SizeOf(WideChar));
+ end;
+ inc(ptrw,1+2); // terminator + timestamp
+ dec(len,SizeOf(WideChar)+4);
+ end;
+ end;
+ end
+ else if tag=frmTXXX then
+ begin
+ FillChar(buf,SizeOf(buf),0);
+ org1:=ptr;
+ if (enc=0) or (enc=3) then
+ begin
+ org:=@buf;
+ while ptr^<>#0 do
+ begin
+ org^:=ptr^;
+ inc(org);
+ inc(ptr);
+ end;
+ inc(ptr);
+ if StrCmp(buf,'LYRICS')<>0 then
+ exit;
+ end
+ else
+ begin
+ orgw:=@buf;
+ ptrw:=pWideChar(ptr);
+ while ptrw^<>#0 do
+ begin
+ orgw^:=ptrw^;
+ inc(orgw);
+ inc(ptrw);
+ end;
+ inc(ptrw);
+ if StrCmpW(pWideChar(@buf),'LYRICS')<>0 then
+ exit;
+ ptr:=PAnsiChar(ptrw);
+ end;
+ dec(len,ptr-org1);
+ ID3v2_ReadTagStr1(dst,ptr,len,enc);
+ end;
+end;
+
+procedure ID3v2_CheckCover(tag:integer; var dst:pWideChar;ptr:PAnsiChar;len:integer);
+var
+ org:PAnsiChar;
+ ext:dword;
+ extw:int64;
+ enc:byte;
+begin
+ if dst<>nil then exit;
+ org:=ptr;
+ enc:=ord(ptr^); inc(ptr);
+ if (pdword(ptr)^ and $FFFFFF)=$3E2D2D then exit; // as '-->'
+ if tag=frmAPIC then
+ begin
+ ext:=GetImageType(nil,ptr);
+ repeat inc(ptr) until ptr^=#0; inc(ptr);
+ end
+ else
+ begin
+ ext:=pdword(ptr)^ and $FFFFFF;
+ inc(ptr,3);
+ end;
+
+ if not ord(ptr^) in [0,3,4,6] then exit;
+ inc(ptr);
+ if (enc=0) or (enc=3) then
+ begin
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end
+ else
+ begin
+ while pWord(ptr)^<>0 do inc(ptr,2);
+ inc(ptr,2);
+ end;
+ dec(len,ptr-org);
+
+ if ext=0 then
+ ext:=GetImageType(pByte(ptr));
+ if ext<>0 then
+ begin
+ FastAnsiToWideBuf(PAnsiChar(@ext),pWideChar(@extw));
+ dst:=SaveTemporaryW(ptr,len,PWideChar(@extw));
+ end;
+end;
+
+function ID3v2_PreReadTag(var frm:tID3v2FrameHdr;var src:PAnsiChar;ver:integer):PAnsiChar;
+var
+ i:cardinal;
+ dst:PAnsiChar;
+begin
+ mGetMem(result,frm.Size);
+ if Unsync or ((frm.Flags and $0200)<>0) then
+ begin
+ dst:=result;
+ i:=0;
+ while i<frm.Size do
+ begin
+ dst^:=src^;
+ inc(src);
+ if (dst^=#$FF) and (src^=#0) then
+ begin
+ inc(src);
+ if ver=4 then inc(i);
+ end;
+ inc(dst);
+ inc(i);
+ end
+ end
+ else
+ begin
+ move(src^,result^,frm.Size);
+ inc(src,frm.Size);
+ end;
+end;
+
+procedure ID3v2_ReadTag2(ver:integer;tag:PAnsiChar;Size:integer;var Info:tSongInfo);
+type
+ a=array [0..3] of byte;
+var
+ Frm:tID3v2FrameHdr;
+ FrmOld:tID3v2FrameHdrOld;
+ tmp:integer;
+ ls:pWideChar;
+ lp:PAnsiChar;
+ ptr,buf:PAnsiChar;
+ fArtist,fTitle,fAlbum:integer;
+ enc:byte;
+begin
+ lp:=tag+Size;
+ fArtist:=0;
+ fTitle :=0;
+ fAlbum :=0;
+ while tag<lp do
+ begin
+ case ver of
+ 1,2: begin
+ move(tag^,FrmOld,SizeOf(FrmOld));
+ Frm.Flags:=0;
+ Frm.ID:=FrmOld.ID[0]+(FrmOld.ID[1] shl 8)+(FrmOld.ID[2] shl 16);
+ Frm.Size:=(FrmOld.Size[0] shl 16)+(FrmOld.Size[1] shl 8)+FrmOld.Size[2];
+ inc(tag,SizeOf(tID3v2FrameHdrOld));
+ end;
+ 3: begin
+ move(tag^,Frm,SizeOf(Frm));
+ Frm.Size:=BSwap(Frm.Size);
+ inc(tag,SizeOf(tID3v2FrameHdr));
+ end;
+ 4: begin
+ move(tag^,Frm,SizeOf(Frm));
+ Frm.Size:=ID3v2_Correct(Frm.Size);
+ inc(tag,SizeOf(tID3v2FrameHdr));
+ if (Frm.Flags and $0100)<>0 then
+ begin
+ Frm.Size:=ID3v2_Correct(pdword(tag)^);
+ inc(tag,4);
+ end;
+ end;
+ end;
+
+ if Frm.ID=0 then
+ break;
+ if Frm.Size=0 then
+ continue;
+ if (tag+Frm.Size)>lp then
+ break;
+ buf:=ID3v2_PreReadTag(Frm,tag,ver);
+
+ enc:=ord(buf^);
+ case enc of // set priority
+ 0: enc:=1;
+ 1,2: enc:=3;
+ 3: enc:=3; // or 2 if you want
+ end;
+ case Frm.ID of
+ frmUSLT,frmULT: ID3v2_CheckLyric(frmUSLT,Info.lyric,buf,Frm.Size);
+ frmSYLT,frmSLT: ID3v2_CheckLyric(frmSYLT,Info.lyric,buf,Frm.Size);
+ frmTXX,frmTXXX: ID3v2_CheckLyric(frmTXXX,Info.lyric,buf,Frm.Size);
+ frmAPIC,frmPIC: ID3v2_CheckCover(Frm.ID ,Info.cover,buf,Frm.Size);
+
+ frmTPE1,frmTP1: begin
+ if fArtist<(enc+10) then
+ begin
+ fArtist:=enc+10;
+ mFreeMem(Info.artist);
+ ID3v2_ReadTagStr(Info.artist,buf,Frm.Size);
+ end
+ end;
+ frmTIT2,frmTT2: begin
+ if fTitle<(enc+10) then
+ begin
+ fTitle:=enc+10;
+ mFreeMem(Info.title);
+ ID3v2_ReadTagStr(Info.title,buf,Frm.Size);
+ end
+ end;
+ frmTALB,frmTAL: begin
+ if fAlbum<(enc+10) then
+ begin
+ fAlbum:=enc+10;
+ mFreeMem(Info.album);
+ ID3v2_ReadTagStr(Info.album,buf,Frm.Size);
+ end
+ end;
+ frmTYER,frmTDRC,frmTYE: begin
+ if Info.year<>nil then
+ mFreeMem(Info.year);
+ ID3v2_ReadTagStr(Info.year,buf,Frm.Size);
+ end;
+
+ frmTOPE,frmTPE2,frmTOA,frmTPE4: begin
+ if fArtist<enc then
+ begin
+ fArtist:=enc;
+ mFreeMem(Info.artist);
+ ID3v2_ReadTagStr(Info.artist,buf,Frm.Size);
+ end;
+ end;
+ frmTIT1,frmTIT3,frmTT1: begin
+ if fTitle<enc then
+ begin
+ fTitle:=enc;
+ mFreeMem(Info.title);
+ ID3v2_ReadTagStr(Info.title,buf,Frm.Size);
+ end;
+ end;
+ frmTOAL: begin
+ if fAlbum<enc then
+ begin
+ fAlbum:=enc;
+ mFreeMem(Info.album);
+ ID3v2_ReadTagStr(Info.album,buf,Frm.Size);
+ end;
+ end;
+ frmTORY: begin
+ if Info.year=nil then ID3v2_ReadTagStr(Info.year,buf,Frm.Size);
+ end;
+
+ frmTCON,frmTCO: begin
+ if Info.genre=nil then
+ begin
+ ID3v2_ReadTagStr(Info.genre,buf,Frm.Size);
+
+ if Info.genre<>nil then
+ if Info.genre[0]='(' then
+ begin
+ tmp:=StrScanW(Info.genre,')')-Info.genre+1;
+ if tmp=integer(StrLenW(Info.genre)) then
+ begin
+ ls:=GenreName(StrToInt(Info.genre+1));
+ mFreeMem(Info.genre);
+ Info.genre:=ls;
+ end
+ else if tmp>0 then
+ StrCopyW(Info.genre,Info.genre+tmp);
+ end;
+ end;
+ end;
+ frmCOMM,frmCOM: begin //!!
+ if Info.comment=nil then
+ begin
+ ptr:=buf;
+ inc(ptr,3+1); // language
+ if (buf^=#0) or (buf^=#3) then
+ begin
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end
+ else
+ begin
+ while pWord(ptr)^<>0 do inc(ptr,2);
+ inc(ptr,2);
+ end;
+ dec(Frm.Size,ptr-buf);
+ ID3v2_ReadTagStr1(Info.comment,ptr,Frm.Size,ord(buf^));
+ end;
+ end;
+ frmTRCK,frmTRK: begin
+ if Info.track=0 then
+ begin
+ ID3v2_ReadTagStr(ls,buf,Frm.Size);
+ Info.track:=StrToInt(ls);
+ mFreeMem(ls);
+ end;
+ end;
+ end;
+ mFreeMem(buf);
+ end;
+end;
+
+function ReadID3v2(f:THANDLE; var Info:tSongInfo):longint;
+var
+ TagHdr:TID3v2TagHdr;
+ Tag2:PAnsiChar;
+ ExtTagSize:dword;
+begin
+ BlockRead(f,TagHdr,SizeOf(TagHdr));
+ if TagHdr.ID=TAG2Sign then
+ begin
+ TagHdr.TagSize:=ID3v2_Correct(TagHdr.TagSize);
+ Unsync:=(TagHdr.Flags and $80)<>0;
+ result:=TagHdr.TagSize;
+// if TagHdr.Version>2 then
+ begin
+ GetMem(Tag2,TagHdr.TagSize);
+ BlockRead(f,Tag2^,TagHdr.TagSize);
+ ID3v2_ReadTag2(TagHdr.Version,Tag2,TagHdr.TagSize,Info);
+ FreeMem(Tag2);
+ end;
+ if (TagHdr.Flags and ExtIDHdrMask)<>0 then
+ begin
+ BlockRead(f,ExtTagSize,SizeOf(ExtTagSize));
+ inc(result,4+ExtTagSize);
+ end;
+ if (TagHdr.Flags and FooterPresent)<>0 then
+ inc(result,10);
+ end
+ else
+ result:=0;
+ Seek(f,result);
+end;
+{$ENDIF}
diff --git a/plugins/Watrack/formats/tags.pas b/plugins/Watrack/formats/tags.pas
new file mode 100644
index 0000000000..fbe0576c59
--- /dev/null
+++ b/plugins/Watrack/formats/tags.pas
@@ -0,0 +1,21 @@
+unit tags;
+{$include compilers.inc}
+interface
+
+uses wat_api,windows;
+
+{$DEFINE Interface}
+{$include tag_id3v2.inc}
+{$include tag_id3v1.inc}
+{$include tag_apev2.inc}
+
+implementation
+
+uses common,io,utils;
+
+{$UNDEF Interface}
+{$include tag_id3v2.inc}
+{$include tag_id3v1.inc}
+{$include tag_apev2.inc}
+
+end. \ No newline at end of file