1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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}
|