line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
/* |
2
|
|
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify |
3
|
|
|
|
|
|
|
* it under the terms of the GNU General Public License as published by |
4
|
|
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
5
|
|
|
|
|
|
|
* (at your option) any later version. |
6
|
|
|
|
|
|
|
* |
7
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful, |
8
|
|
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
9
|
|
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10
|
|
|
|
|
|
|
* GNU General Public License for more details. |
11
|
|
|
|
|
|
|
* |
12
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License |
13
|
|
|
|
|
|
|
* along with this program; if not, write to the Free Software |
14
|
|
|
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
15
|
|
|
|
|
|
|
*/ |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
#include "id3.h" |
18
|
|
|
|
|
|
|
#include "id3_genre.dat" |
19
|
|
|
|
|
|
|
#include "id3_compat.c" |
20
|
|
|
|
|
|
|
#include "id3_frametype.c" |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#define NGENRES (sizeof(genre_table) / sizeof(genre_table[0])) |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
// Read an int from a variable number of bytes |
25
|
|
|
|
|
|
|
static int |
26
|
500
|
|
|
|
|
|
_varint(unsigned char *buf, int length) |
27
|
|
|
|
|
|
|
{ |
28
|
500
|
|
|
|
|
|
int i, b, number = 0; |
29
|
|
|
|
|
|
|
|
30
|
500
|
50
|
|
|
|
|
if (buf) { |
31
|
2484
|
100
|
|
|
|
|
for ( i = 0; i < length; i++ ) { |
32
|
1984
|
|
|
|
|
|
b = length - 1 - i; |
33
|
1984
|
|
|
|
|
|
number = number | (unsigned int)( buf[i] & 0xff ) << ( 8*b ); |
34
|
|
|
|
|
|
|
} |
35
|
500
|
|
|
|
|
|
return number; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
else { |
38
|
0
|
|
|
|
|
|
return 0; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
int |
43
|
98
|
|
|
|
|
|
parse_id3(PerlIO *infile, char *file, HV *info, HV *tags, off_t seek, off_t file_size) |
44
|
|
|
|
|
|
|
{ |
45
|
98
|
|
|
|
|
|
int err = 0; |
46
|
|
|
|
|
|
|
unsigned char *bptr; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
id3info *id3; |
49
|
98
|
|
|
|
|
|
Newz(0, id3, sizeof(id3info), id3info); |
50
|
98
|
|
|
|
|
|
Newz(0, id3->buf, sizeof(Buffer), Buffer); |
51
|
98
|
|
|
|
|
|
Newz(0, id3->utf8, sizeof(Buffer), Buffer); |
52
|
|
|
|
|
|
|
|
53
|
98
|
|
|
|
|
|
id3->infile = infile; |
54
|
98
|
|
|
|
|
|
id3->file = file; |
55
|
98
|
|
|
|
|
|
id3->info = info; |
56
|
98
|
|
|
|
|
|
id3->tags = tags; |
57
|
98
|
|
|
|
|
|
id3->offset = seek; |
58
|
|
|
|
|
|
|
|
59
|
98
|
|
|
|
|
|
buffer_init(id3->buf, ID3_BLOCK_SIZE); |
60
|
|
|
|
|
|
|
|
61
|
98
|
100
|
|
|
|
|
if ( !seek ) { |
62
|
|
|
|
|
|
|
// Check for ID3v1 tag first |
63
|
92
|
|
|
|
|
|
PerlIO_seek(infile, file_size - 128, SEEK_SET); |
64
|
92
|
50
|
|
|
|
|
if ( !_check_buf(infile, id3->buf, 128, 128) ) { |
65
|
0
|
|
|
|
|
|
err = -1; |
66
|
0
|
|
|
|
|
|
goto out; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
92
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
70
|
92
|
100
|
|
|
|
|
if (bptr[0] == 'T' && bptr[1] == 'A' && bptr[2] == 'G') { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
71
|
16
|
|
|
|
|
|
_id3_parse_v1(id3); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
// Check for ID3v2 tag |
76
|
98
|
|
|
|
|
|
PerlIO_seek(infile, seek, SEEK_SET); |
77
|
98
|
|
|
|
|
|
buffer_clear(id3->buf); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
// Read enough for header (10) + extended header size (4) |
80
|
98
|
50
|
|
|
|
|
if ( !_check_buf(infile, id3->buf, 14, ID3_BLOCK_SIZE) ) { |
81
|
0
|
|
|
|
|
|
err = -1; |
82
|
0
|
|
|
|
|
|
goto out; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
98
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
86
|
98
|
100
|
|
|
|
|
if (bptr[0] == 'I' && bptr[1] == 'D' && bptr[2] == '3') { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
87
|
83
|
|
|
|
|
|
_id3_parse_v2(id3); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
out: |
91
|
98
|
|
|
|
|
|
buffer_free(id3->buf); |
92
|
98
|
|
|
|
|
|
Safefree(id3->buf); |
93
|
|
|
|
|
|
|
|
94
|
98
|
100
|
|
|
|
|
if (id3->utf8->alloc) |
95
|
85
|
|
|
|
|
|
buffer_free(id3->utf8); |
96
|
98
|
|
|
|
|
|
Safefree(id3->utf8); |
97
|
|
|
|
|
|
|
|
98
|
98
|
|
|
|
|
|
Safefree(id3); |
99
|
|
|
|
|
|
|
|
100
|
98
|
|
|
|
|
|
return err; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
int |
104
|
16
|
|
|
|
|
|
_id3_parse_v1(id3info *id3) |
105
|
|
|
|
|
|
|
{ |
106
|
16
|
|
|
|
|
|
SV *tmp = NULL; |
107
|
16
|
|
|
|
|
|
uint8_t read = 0; |
108
|
|
|
|
|
|
|
unsigned char *bptr; |
109
|
|
|
|
|
|
|
uint8_t comment_len; |
110
|
|
|
|
|
|
|
uint8_t genre; |
111
|
|
|
|
|
|
|
|
112
|
16
|
|
|
|
|
|
buffer_consume(id3->buf, 3); // TAG |
113
|
|
|
|
|
|
|
|
114
|
16
|
|
|
|
|
|
read = _id3_get_v1_utf8_string(id3, &tmp, 30); |
115
|
16
|
50
|
|
|
|
|
if (tmp && SvPOK(tmp) && sv_len(tmp)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
116
|
|
|
|
|
|
|
DEBUG_TRACE("ID3v1 title: %s\n", SvPVX(tmp)); |
117
|
9
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_TITLE, tmp ); |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
else { |
120
|
7
|
50
|
|
|
|
|
if (tmp) SvREFCNT_dec(tmp); |
121
|
|
|
|
|
|
|
} |
122
|
16
|
100
|
|
|
|
|
if (read < 30) { |
123
|
13
|
|
|
|
|
|
buffer_consume(id3->buf, 30 - read); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
16
|
|
|
|
|
|
tmp = NULL; |
127
|
16
|
|
|
|
|
|
read = _id3_get_v1_utf8_string(id3, &tmp, 30); |
128
|
16
|
50
|
|
|
|
|
if (tmp && SvPOK(tmp) && sv_len(tmp)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
129
|
|
|
|
|
|
|
DEBUG_TRACE("ID3v1 artist: %s\n", SvPVX(tmp)); |
130
|
9
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_ARTIST, tmp ); |
131
|
9
|
|
|
|
|
|
tmp = NULL; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
else { |
134
|
7
|
50
|
|
|
|
|
if (tmp) SvREFCNT_dec(tmp); |
135
|
|
|
|
|
|
|
} |
136
|
16
|
100
|
|
|
|
|
if (read < 30) { |
137
|
13
|
|
|
|
|
|
buffer_consume(id3->buf, 30 - read); |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
16
|
|
|
|
|
|
tmp = NULL; |
141
|
16
|
|
|
|
|
|
read = _id3_get_v1_utf8_string(id3, &tmp, 30); |
142
|
16
|
50
|
|
|
|
|
if (tmp && SvPOK(tmp) && sv_len(tmp)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
143
|
|
|
|
|
|
|
DEBUG_TRACE("ID3v1 album: %s\n", SvPVX(tmp)); |
144
|
9
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_ALBUM, tmp ); |
145
|
9
|
|
|
|
|
|
tmp = NULL; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
else { |
148
|
7
|
50
|
|
|
|
|
if (tmp) SvREFCNT_dec(tmp); |
149
|
|
|
|
|
|
|
} |
150
|
16
|
100
|
|
|
|
|
if (read < 30) { |
151
|
13
|
|
|
|
|
|
buffer_consume(id3->buf, 30 - read); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
16
|
|
|
|
|
|
tmp = NULL; |
155
|
16
|
|
|
|
|
|
read = _id3_get_v1_utf8_string(id3, &tmp, 4); |
156
|
16
|
50
|
|
|
|
|
if (tmp && SvPOK(tmp) && sv_len(tmp)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
157
|
|
|
|
|
|
|
DEBUG_TRACE("ID3v1 year: %s\n", SvPVX(tmp)); |
158
|
8
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_YEAR, tmp ); |
159
|
8
|
|
|
|
|
|
tmp = NULL; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
else { |
162
|
8
|
50
|
|
|
|
|
if (tmp) SvREFCNT_dec(tmp); |
163
|
|
|
|
|
|
|
} |
164
|
16
|
100
|
|
|
|
|
if (read < 4) { |
165
|
7
|
|
|
|
|
|
buffer_consume(id3->buf, 4 - read); |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
16
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
169
|
16
|
100
|
|
|
|
|
if (bptr[28] == 0 && bptr[29] != 0) { |
|
|
100
|
|
|
|
|
|
170
|
|
|
|
|
|
|
// ID3v1.1 track number is present |
171
|
4
|
|
|
|
|
|
comment_len = 28; |
172
|
4
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_TRACK, newSVuv(bptr[29]) ); |
173
|
4
|
|
|
|
|
|
my_hv_store( id3->info, "id3_version", newSVpv( "ID3v1.1", 0 ) ); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
else { |
176
|
12
|
|
|
|
|
|
comment_len = 30; |
177
|
12
|
|
|
|
|
|
my_hv_store( id3->info, "id3_version", newSVpv( "ID3v1", 0 ) ); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
16
|
|
|
|
|
|
tmp = NULL; |
181
|
16
|
|
|
|
|
|
read = _id3_get_v1_utf8_string(id3, &tmp, comment_len); |
182
|
21
|
50
|
|
|
|
|
if (tmp && SvPOK(tmp) && sv_len(tmp)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
183
|
5
|
|
|
|
|
|
AV *comment_array = newAV(); |
184
|
5
|
|
|
|
|
|
av_push( comment_array, newSVpvn("XXX", 3) ); |
185
|
5
|
|
|
|
|
|
av_push( comment_array, newSVpvn("", 0) ); |
186
|
5
|
|
|
|
|
|
av_push( comment_array, tmp ); |
187
|
|
|
|
|
|
|
DEBUG_TRACE("ID3v1 comment: %s\n", SvPVX(tmp)); |
188
|
5
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_COMMENT, newRV_noinc( (SV *)comment_array ) ); |
189
|
5
|
|
|
|
|
|
tmp = NULL; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
else { |
192
|
11
|
50
|
|
|
|
|
if (tmp) SvREFCNT_dec(tmp); |
193
|
|
|
|
|
|
|
} |
194
|
16
|
100
|
|
|
|
|
if (read < 30) { |
195
|
14
|
|
|
|
|
|
buffer_consume(id3->buf, 30 - read); |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
16
|
|
|
|
|
|
genre = buffer_get_char(id3->buf); |
199
|
16
|
100
|
|
|
|
|
if (genre < NGENRES) { |
200
|
8
|
|
|
|
|
|
char const *genre_string = _id3_genre_index(genre); |
201
|
8
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_GENRE, newSVpv(genre_string, 0) ); |
202
|
|
|
|
|
|
|
} |
203
|
8
|
50
|
|
|
|
|
else if (genre < 255) { |
204
|
0
|
|
|
|
|
|
my_hv_store( id3->tags, ID3_FRAME_GENRE, newSVpvf("Unknown/%d", genre) ); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
16
|
|
|
|
|
|
return 1; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
int |
211
|
83
|
|
|
|
|
|
_id3_parse_v2(id3info *id3) |
212
|
|
|
|
|
|
|
{ |
213
|
83
|
|
|
|
|
|
int ret = 1; |
214
|
|
|
|
|
|
|
unsigned char *bptr; |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
// Verify we have a valid tag |
217
|
83
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
218
|
166
|
50
|
|
|
|
|
if ( !( |
|
|
50
|
|
|
|
|
|
219
|
83
|
50
|
|
|
|
|
bptr[3] < 0xff && bptr[4] < 0xff && |
|
|
50
|
|
|
|
|
|
220
|
83
|
50
|
|
|
|
|
bptr[6] < 0x80 && bptr[7] < 0x80 && bptr[8] < 0x80 && bptr[9] < 0x80 |
|
|
50
|
|
|
|
|
|
221
|
|
|
|
|
|
|
) ) { |
222
|
0
|
|
|
|
|
|
PerlIO_printf(PerlIO_stderr(), "Invalid ID3v2 tag in %s\n", id3->file); |
223
|
0
|
|
|
|
|
|
return 0; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
83
|
|
|
|
|
|
buffer_consume(id3->buf, 3); // ID3 |
227
|
|
|
|
|
|
|
|
228
|
83
|
|
|
|
|
|
id3->version_major = buffer_get_char(id3->buf); |
229
|
83
|
|
|
|
|
|
id3->version_minor = buffer_get_char(id3->buf); |
230
|
83
|
|
|
|
|
|
id3->flags = buffer_get_char(id3->buf); |
231
|
83
|
|
|
|
|
|
id3->size = 10 + buffer_get_syncsafe(id3->buf, 4); |
232
|
|
|
|
|
|
|
|
233
|
83
|
|
|
|
|
|
id3->size_remain = id3->size - 10; |
234
|
|
|
|
|
|
|
|
235
|
83
|
50
|
|
|
|
|
if (id3->flags & ID3_TAG_FLAG_FOOTERPRESENT) { |
236
|
0
|
|
|
|
|
|
id3->size += 10; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
DEBUG_TRACE("Parsing ID3v2.%d.%d tag, flags %x, size %d\n", id3->version_major, id3->version_minor, id3->flags, id3->size); |
240
|
|
|
|
|
|
|
|
241
|
83
|
100
|
|
|
|
|
if (id3->flags & ID3_TAG_FLAG_UNSYNCHRONISATION) { |
242
|
13
|
100
|
|
|
|
|
if (id3->version_major < 4) { |
243
|
|
|
|
|
|
|
// It's unclear but the v2.4.0-changes document seems to say that v2.4 should |
244
|
|
|
|
|
|
|
// ignore the tag-level unsync flag and only worry about frame-level unsync |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
// For v2.2/v2.3, unsync the entire tag. This is unfortunate due to |
247
|
|
|
|
|
|
|
// increased memory usage but the only way to do it, as frame size values only |
248
|
|
|
|
|
|
|
// indicate the post-unsync size, so it's not possible to unsync each frame individually |
249
|
|
|
|
|
|
|
// tested with v2.3-unsync.mp3 |
250
|
4
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, id3->size, id3->size) ) { |
251
|
0
|
|
|
|
|
|
ret = 0; |
252
|
0
|
|
|
|
|
|
goto out; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
4
|
|
|
|
|
|
id3->size_remain = _id3_deunsync( buffer_ptr(id3->buf), id3->size ); |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
DEBUG_TRACE(" Un-synchronized tag, new_size %d\n", id3->size_remain); |
258
|
|
|
|
|
|
|
|
259
|
4
|
|
|
|
|
|
my_hv_store( id3->info, "id3_was_unsynced", newSVuv(1) ); |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
else { |
262
|
|
|
|
|
|
|
DEBUG_TRACE(" Ignoring v2.4 tag un-synchronize flag\n"); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
83
|
100
|
|
|
|
|
if (id3->flags & ID3_TAG_FLAG_EXTENDEDHEADER) { |
267
|
|
|
|
|
|
|
uint32_t ehsize; |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
// If the tag is v2.2, this bit is actually the compression bit and the tag should be ignored |
270
|
3
|
50
|
|
|
|
|
if (id3->version_major == 2) { |
271
|
0
|
|
|
|
|
|
ret = 0; |
272
|
0
|
|
|
|
|
|
goto out; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
// tested with v2.3-ext-header.mp3 |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
// We don't care about the value of the extended flags or CRC, so just read the size and skip it |
278
|
3
|
|
|
|
|
|
ehsize = buffer_get_int(id3->buf); |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
// ehsize may be invalid, tested with v2.3-ext-header-invalid.mp3 |
281
|
3
|
100
|
|
|
|
|
if (ehsize > id3->size_remain - 4) { |
282
|
1
|
|
|
|
|
|
warn("Error: Invalid ID3 extended header size (%s)\n", id3->file); |
283
|
1
|
|
|
|
|
|
ret = 0; |
284
|
1
|
|
|
|
|
|
goto out; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
DEBUG_TRACE(" Skipping extended header, size %d\n", ehsize); |
288
|
|
|
|
|
|
|
|
289
|
2
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, ehsize, ID3_BLOCK_SIZE) ) { |
290
|
0
|
|
|
|
|
|
ret = 0; |
291
|
0
|
|
|
|
|
|
goto out; |
292
|
|
|
|
|
|
|
} |
293
|
2
|
|
|
|
|
|
buffer_consume(id3->buf, ehsize); |
294
|
|
|
|
|
|
|
|
295
|
2
|
|
|
|
|
|
id3->size_remain -= ehsize + 4; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
// Parse frames |
299
|
1008
|
100
|
|
|
|
|
while (id3->size_remain > 0) { |
300
|
|
|
|
|
|
|
//DEBUG_TRACE(" remain: %d\n", id3->size_remain); |
301
|
998
|
100
|
|
|
|
|
if ( !_id3_parse_v2_frame(id3) ) { |
302
|
72
|
|
|
|
|
|
break; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
82
|
100
|
|
|
|
|
if (id3->version_major < 4) { |
307
|
|
|
|
|
|
|
// map old year/date/time (TYER/TDAT/TIME) frames to TDRC |
308
|
|
|
|
|
|
|
// tested in v2.3-xsop.mp3 |
309
|
50
|
|
|
|
|
|
_id3_convert_tdrc(id3); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
// Set id3_version info element, which contains all tag versions found |
313
|
|
|
|
|
|
|
{ |
314
|
82
|
|
|
|
|
|
SV *version = newSVpvf( "ID3v2.%d.%d", id3->version_major, id3->version_minor ); |
315
|
|
|
|
|
|
|
|
316
|
82
|
100
|
|
|
|
|
if ( my_hv_exists(id3->info, "id3_version") ) { |
317
|
11
|
|
|
|
|
|
SV **entry = my_hv_fetch(id3->info, "id3_version"); |
318
|
11
|
50
|
|
|
|
|
if (entry != NULL) { |
319
|
11
|
|
|
|
|
|
sv_catpv( version, ", " ); |
320
|
11
|
|
|
|
|
|
sv_catsv( version, *entry ); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
82
|
|
|
|
|
|
my_hv_store( id3->info, "id3_version", version ); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
out: |
328
|
83
|
|
|
|
|
|
return ret; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
int |
332
|
998
|
|
|
|
|
|
_id3_parse_v2_frame(id3info *id3) |
333
|
|
|
|
|
|
|
{ |
334
|
998
|
|
|
|
|
|
int ret = 1; |
335
|
|
|
|
|
|
|
char id[5]; |
336
|
998
|
|
|
|
|
|
uint16_t flags = 0; |
337
|
998
|
|
|
|
|
|
uint32_t size = 0; |
338
|
998
|
|
|
|
|
|
uint32_t decoded_size = 0; |
339
|
998
|
|
|
|
|
|
uint32_t unsync_extra = 0; |
340
|
|
|
|
|
|
|
id3_frametype const *frametype; |
341
|
998
|
|
|
|
|
|
Buffer *tmp_buf = 0; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
// If the frame is compressed, it will be decompressed here |
344
|
998
|
|
|
|
|
|
Buffer *decompressed = 0; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
// tag_data_safe flag is used if skipping artwork and artwork is not raw image data (needs unsync) |
347
|
998
|
|
|
|
|
|
id3->tag_data_safe = 1; |
348
|
|
|
|
|
|
|
|
349
|
998
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, 10, ID3_BLOCK_SIZE) ) { |
350
|
0
|
|
|
|
|
|
ret = 0; |
351
|
0
|
|
|
|
|
|
goto out; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
998
|
100
|
|
|
|
|
if (id3->version_major == 2) { |
355
|
|
|
|
|
|
|
// v2.2 |
356
|
|
|
|
|
|
|
id3_compat const *compat; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
// Read 3-letter id |
359
|
88
|
|
|
|
|
|
buffer_get(id3->buf, &id, 3); |
360
|
88
|
|
|
|
|
|
id[3] = 0; |
361
|
|
|
|
|
|
|
|
362
|
88
|
100
|
|
|
|
|
if (id[0] == 0) { |
363
|
|
|
|
|
|
|
// padding |
364
|
|
|
|
|
|
|
DEBUG_TRACE(" Found start of padding, aborting\n"); |
365
|
4
|
|
|
|
|
|
ret = 0; |
366
|
4
|
|
|
|
|
|
goto out; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
84
|
|
|
|
|
|
size = buffer_get_int24(id3->buf); |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
DEBUG_TRACE(" %s, size %d\n", id, size); |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
// map 3-char id to 4-char id |
374
|
84
|
|
|
|
|
|
compat = _id3_compat_lookup((char *)&id, 3); |
375
|
84
|
50
|
|
|
|
|
if (compat && compat->equiv) { |
|
|
50
|
|
|
|
|
|
376
|
84
|
|
|
|
|
|
strncpy(id, compat->equiv, 4); |
377
|
84
|
|
|
|
|
|
id[4] = 0; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
DEBUG_TRACE(" compat -> %s\n", id); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
else { |
382
|
|
|
|
|
|
|
// no compat mapping (obsolete), prepend 'Y' to id |
383
|
0
|
|
|
|
|
|
id[4] = 0; |
384
|
0
|
|
|
|
|
|
id[3] = id[2]; |
385
|
0
|
|
|
|
|
|
id[2] = id[1]; |
386
|
0
|
|
|
|
|
|
id[1] = id[0]; |
387
|
0
|
|
|
|
|
|
id[0] = 'Y'; |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
DEBUG_TRACE(" obsolete/unknown -> %s\n", id); |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
84
|
|
|
|
|
|
id3->size_remain -= 6; |
393
|
|
|
|
|
|
|
|
394
|
84
|
50
|
|
|
|
|
if (size > id3->size_remain) { |
395
|
|
|
|
|
|
|
DEBUG_TRACE(" frame size too big, aborting\n"); |
396
|
0
|
|
|
|
|
|
ret = 0; |
397
|
84
|
|
|
|
|
|
goto out; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
else { |
401
|
|
|
|
|
|
|
// Read 4-letter id |
402
|
910
|
|
|
|
|
|
buffer_get(id3->buf, &id, 4); |
403
|
910
|
|
|
|
|
|
id[4] = 0; |
404
|
|
|
|
|
|
|
|
405
|
910
|
100
|
|
|
|
|
if (id[0] == 0) { |
406
|
|
|
|
|
|
|
// padding |
407
|
|
|
|
|
|
|
DEBUG_TRACE(" Found start of padding, aborting\n"); |
408
|
66
|
|
|
|
|
|
ret = 0; |
409
|
66
|
|
|
|
|
|
goto out; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
844
|
|
|
|
|
|
id3->size_remain -= 4; |
413
|
|
|
|
|
|
|
|
414
|
844
|
100
|
|
|
|
|
if (id3->version_major == 3) { |
415
|
|
|
|
|
|
|
// v2.3 |
416
|
|
|
|
|
|
|
id3_compat const *compat; |
417
|
|
|
|
|
|
|
|
418
|
373
|
|
|
|
|
|
size = buffer_get_int(id3->buf); |
419
|
373
|
|
|
|
|
|
flags = buffer_get_short(id3->buf); |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
DEBUG_TRACE(" %s, frame flags %x, size %d\n", id, flags, size); |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
// map to v2.4 id |
424
|
373
|
100
|
|
|
|
|
if (id[3] == ' ') { |
425
|
|
|
|
|
|
|
// iTunes writes bad frame IDs such as 'TSA ', these should be run through compat |
426
|
|
|
|
|
|
|
// as 3-char frames |
427
|
6
|
|
|
|
|
|
compat = _id3_compat_lookup((char *)&id, 3); |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
else { |
430
|
367
|
|
|
|
|
|
compat = _id3_compat_lookup((char *)&id, 4); |
431
|
|
|
|
|
|
|
} |
432
|
373
|
100
|
|
|
|
|
if (compat && compat->equiv) { |
|
|
100
|
|
|
|
|
|
433
|
44
|
|
|
|
|
|
strncpy(id, compat->equiv, 4); |
434
|
44
|
|
|
|
|
|
id[4] = 0; |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
DEBUG_TRACE(" compat -> %s\n", id); |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
373
|
|
|
|
|
|
id3->size_remain -= 6; |
440
|
|
|
|
|
|
|
|
441
|
373
|
50
|
|
|
|
|
if (size > id3->size_remain) { |
442
|
|
|
|
|
|
|
DEBUG_TRACE(" frame size too big, aborting\n"); |
443
|
0
|
|
|
|
|
|
ret = 0; |
444
|
0
|
|
|
|
|
|
goto out; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
373
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V23_COMPRESSION) { |
448
|
|
|
|
|
|
|
// tested with v2.3-compressed-frame.mp3 |
449
|
1
|
|
|
|
|
|
decoded_size = buffer_get_int(id3->buf); |
450
|
1
|
|
|
|
|
|
id3->size_remain -= 4; |
451
|
1
|
|
|
|
|
|
size -= 4; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
373
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V23_ENCRYPTION) { |
455
|
|
|
|
|
|
|
// tested with v2.3-encrypted-frame.mp3 |
456
|
|
|
|
|
|
|
#ifdef AUDIO_SCAN_DEBUG |
457
|
|
|
|
|
|
|
DEBUG_TRACE(" encrypted, method %d\n", buffer_get_char(id3->buf)); |
458
|
|
|
|
|
|
|
#else |
459
|
1
|
|
|
|
|
|
buffer_consume(id3->buf, 1); |
460
|
|
|
|
|
|
|
#endif |
461
|
|
|
|
|
|
|
|
462
|
1
|
|
|
|
|
|
id3->size_remain--; |
463
|
1
|
|
|
|
|
|
size--; |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
DEBUG_TRACE(" skipping encrypted frame\n"); |
466
|
1
|
|
|
|
|
|
_id3_skip(id3, size); |
467
|
1
|
|
|
|
|
|
id3->size_remain -= size; |
468
|
1
|
|
|
|
|
|
goto out; |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
372
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V23_GROUPINGIDENTITY) { |
472
|
|
|
|
|
|
|
// tested with v2.3-group-id.mp3 |
473
|
|
|
|
|
|
|
#ifdef AUDIO_SCAN_DEBUG |
474
|
|
|
|
|
|
|
DEBUG_TRACE(" group_id %d\n", buffer_get_char(id3->buf)); |
475
|
|
|
|
|
|
|
#else |
476
|
1
|
|
|
|
|
|
buffer_consume(id3->buf, 1); |
477
|
|
|
|
|
|
|
#endif |
478
|
|
|
|
|
|
|
|
479
|
1
|
|
|
|
|
|
id3->size_remain--; |
480
|
1
|
|
|
|
|
|
size--; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
// Perform decompression if necessary after all optional extra bytes have been read |
484
|
|
|
|
|
|
|
// XXX need test for compressed + unsync |
485
|
372
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V23_COMPRESSION && decoded_size) { |
|
|
50
|
|
|
|
|
|
486
|
|
|
|
|
|
|
unsigned long tmp_size; |
487
|
|
|
|
|
|
|
|
488
|
1
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, size, ID3_BLOCK_SIZE) ) { |
489
|
0
|
|
|
|
|
|
ret = 0; |
490
|
0
|
|
|
|
|
|
goto out; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
DEBUG_TRACE(" decompressing, decoded_size %d\n", decoded_size); |
494
|
|
|
|
|
|
|
|
495
|
1
|
|
|
|
|
|
Newz(0, decompressed, sizeof(Buffer), Buffer); |
496
|
1
|
|
|
|
|
|
buffer_init(decompressed, decoded_size); |
497
|
|
|
|
|
|
|
|
498
|
1
|
|
|
|
|
|
tmp_size = decoded_size; |
499
|
1
|
50
|
|
|
|
|
if ( |
500
|
1
|
|
|
|
|
|
uncompress(buffer_ptr(decompressed), &tmp_size, buffer_ptr(id3->buf), size) != Z_OK |
501
|
1
|
50
|
|
|
|
|
|| |
502
|
1
|
|
|
|
|
|
tmp_size != decoded_size |
503
|
|
|
|
|
|
|
) { |
504
|
|
|
|
|
|
|
DEBUG_TRACE(" unable to decompress frame\n"); |
505
|
0
|
|
|
|
|
|
buffer_free(decompressed); |
506
|
0
|
|
|
|
|
|
Safefree(decompressed); |
507
|
0
|
|
|
|
|
|
decompressed = 0; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
else { |
510
|
|
|
|
|
|
|
// Hack buffer so it knows we've added data directly |
511
|
372
|
|
|
|
|
|
decompressed->end = decoded_size; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
else { |
516
|
|
|
|
|
|
|
// v2.4 |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
// iTunes writes non-syncsafe length integers, check for this here |
519
|
471
|
100
|
|
|
|
|
if ( _varint(buffer_ptr(id3->buf), 4) & 0x80 ) { |
520
|
2
|
|
|
|
|
|
size = buffer_get_int(id3->buf); |
521
|
|
|
|
|
|
|
DEBUG_TRACE(" found non-syncsafe iTunes size for %s, size adjusted to %d\n", id, size); |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
else { |
524
|
469
|
|
|
|
|
|
size = buffer_get_syncsafe(id3->buf, 4); |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
471
|
|
|
|
|
|
flags = buffer_get_short(id3->buf); |
528
|
|
|
|
|
|
|
|
529
|
471
|
|
|
|
|
|
id3->size_remain -= 6; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
DEBUG_TRACE(" %s, frame flags %x, size %d\n", id, flags, size); |
532
|
|
|
|
|
|
|
|
533
|
471
|
100
|
|
|
|
|
if (size > id3->size_remain) { |
534
|
|
|
|
|
|
|
DEBUG_TRACE(" frame size too big, aborting\n"); |
535
|
2
|
|
|
|
|
|
ret = 0; |
536
|
2
|
|
|
|
|
|
goto out; |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
// iTunes writes bad frame IDs such as 'TSA ', these should be run through compat |
540
|
|
|
|
|
|
|
// as 3-char frames |
541
|
469
|
100
|
|
|
|
|
if (id[3] == ' ') { |
542
|
|
|
|
|
|
|
id3_compat const *compat; |
543
|
3
|
|
|
|
|
|
compat = _id3_compat_lookup((char *)&id, 3); |
544
|
3
|
50
|
|
|
|
|
if (compat && compat->equiv) { |
|
|
50
|
|
|
|
|
|
545
|
3
|
|
|
|
|
|
strncpy(id, compat->equiv, 4); |
546
|
3
|
|
|
|
|
|
id[4] = 0; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
DEBUG_TRACE(" bad iTunes v2.4 tag, compat -> %s\n", id); |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
|
552
|
469
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V24_GROUPINGIDENTITY) { |
553
|
|
|
|
|
|
|
// tested with v2.4-group-id.mp3 |
554
|
|
|
|
|
|
|
#ifdef AUDIO_SCAN_DEBUG |
555
|
|
|
|
|
|
|
DEBUG_TRACE(" group_id %d\n", buffer_get_char(id3->buf)); |
556
|
|
|
|
|
|
|
#else |
557
|
1
|
|
|
|
|
|
buffer_consume(id3->buf, 1); |
558
|
|
|
|
|
|
|
#endif |
559
|
1
|
|
|
|
|
|
id3->size_remain--; |
560
|
1
|
|
|
|
|
|
size--; |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
|
563
|
469
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V24_ENCRYPTION) { |
564
|
|
|
|
|
|
|
// tested with v2.4-encrypted-frame.mp3 |
565
|
|
|
|
|
|
|
#ifdef AUDIO_SCAN_DEBUG |
566
|
|
|
|
|
|
|
DEBUG_TRACE(" encrypted, method %d\n", buffer_get_char(id3->buf)); |
567
|
|
|
|
|
|
|
#else |
568
|
1
|
|
|
|
|
|
buffer_consume(id3->buf, 1); |
569
|
|
|
|
|
|
|
#endif |
570
|
|
|
|
|
|
|
|
571
|
1
|
|
|
|
|
|
id3->size_remain--; |
572
|
1
|
|
|
|
|
|
size--; |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
DEBUG_TRACE(" skipping encrypted frame\n"); |
575
|
1
|
|
|
|
|
|
_id3_skip(id3, size); |
576
|
1
|
|
|
|
|
|
id3->size_remain -= size; |
577
|
1
|
|
|
|
|
|
goto out; |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
468
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V24_DATALENGTHINDICATOR) { |
581
|
12
|
|
|
|
|
|
decoded_size = buffer_get_syncsafe(id3->buf, 4); |
582
|
12
|
|
|
|
|
|
id3->size_remain -= 4; |
583
|
12
|
|
|
|
|
|
size -= 4; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
DEBUG_TRACE(" data length indicator, size %d\n", decoded_size); |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
468
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V24_UNSYNCHRONISATION) { |
589
|
|
|
|
|
|
|
// Special case, do not unsync an APIC frame if not reading artwork, |
590
|
|
|
|
|
|
|
// FF's are not likely to appear in the part we care about anyway |
591
|
12
|
100
|
|
|
|
|
if ( !strcmp(id, "APIC") && _env_true("AUDIO_SCAN_NO_ARTWORK") ) { |
|
|
100
|
|
|
|
|
|
592
|
|
|
|
|
|
|
DEBUG_TRACE(" Would un-synchronize APIC frame, but ignoring because of AUDIO_SCAN_NO_ARTWORK\n"); |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
// Reset decoded_size to 0 since we aren't actually decoding. |
595
|
|
|
|
|
|
|
// XXX this would break if we have a compressed + unsync APIC frame but not very likely in the real world |
596
|
1
|
|
|
|
|
|
decoded_size = 0; |
597
|
|
|
|
|
|
|
|
598
|
1
|
|
|
|
|
|
id3->tag_data_safe = 0; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
else { |
601
|
|
|
|
|
|
|
// tested with v2.4-unsync.mp3 |
602
|
11
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, size, ID3_BLOCK_SIZE) ) { |
603
|
0
|
|
|
|
|
|
ret = 0; |
604
|
0
|
|
|
|
|
|
goto out; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
11
|
|
|
|
|
|
decoded_size = _id3_deunsync( buffer_ptr(id3->buf), size ); |
608
|
|
|
|
|
|
|
|
609
|
11
|
|
|
|
|
|
unsync_extra = size - decoded_size; |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
DEBUG_TRACE(" Un-synchronized frame, new_size %d\n", decoded_size); |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
468
|
100
|
|
|
|
|
if (flags & ID3_FRAME_FLAG_V24_COMPRESSION) { |
616
|
|
|
|
|
|
|
// tested with v2.4-compressed-frame.mp3 |
617
|
|
|
|
|
|
|
// XXX need test for compressed + unsync |
618
|
|
|
|
|
|
|
unsigned long tmp_size; |
619
|
|
|
|
|
|
|
|
620
|
1
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, size, ID3_BLOCK_SIZE) ) { |
621
|
0
|
|
|
|
|
|
ret = 0; |
622
|
0
|
|
|
|
|
|
goto out; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
DEBUG_TRACE(" decompressing\n"); |
626
|
|
|
|
|
|
|
|
627
|
1
|
|
|
|
|
|
Newz(0, decompressed, sizeof(Buffer), Buffer); |
628
|
1
|
|
|
|
|
|
buffer_init(decompressed, decoded_size); |
629
|
|
|
|
|
|
|
|
630
|
1
|
|
|
|
|
|
tmp_size = decoded_size; |
631
|
1
|
50
|
|
|
|
|
if ( |
632
|
1
|
|
|
|
|
|
uncompress(buffer_ptr(decompressed), &tmp_size, buffer_ptr(id3->buf), size) != Z_OK |
633
|
1
|
50
|
|
|
|
|
|| |
634
|
1
|
|
|
|
|
|
tmp_size != decoded_size |
635
|
|
|
|
|
|
|
) { |
636
|
|
|
|
|
|
|
DEBUG_TRACE(" unable to decompress frame\n"); |
637
|
0
|
|
|
|
|
|
buffer_free(decompressed); |
638
|
0
|
|
|
|
|
|
Safefree(decompressed); |
639
|
0
|
|
|
|
|
|
decompressed = 0; |
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
else { |
642
|
|
|
|
|
|
|
// Hack buffer so it knows we've added data directly |
643
|
1
|
|
|
|
|
|
decompressed->end = decoded_size; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
} |
646
|
|
|
|
|
|
|
} |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
// Special case, completely skip XHD3 frame (mp3HD) as it will be large |
650
|
|
|
|
|
|
|
// Also skip NCON, a large tag written by MusicMatch |
651
|
924
|
100
|
|
|
|
|
if ( !strcmp(id, "XHD3") || !strcmp(id, "NCON") ) { |
|
|
50
|
|
|
|
|
|
652
|
|
|
|
|
|
|
DEBUG_TRACE(" skipping large binary %s frame\n", id); |
653
|
1
|
|
|
|
|
|
_id3_skip(id3, size); |
654
|
1
|
|
|
|
|
|
id3->size_remain -= size; |
655
|
1
|
|
|
|
|
|
goto out; |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
923
|
|
|
|
|
|
frametype = _id3_frametype_lookup(id, 4); |
659
|
923
|
100
|
|
|
|
|
if (frametype == 0) { |
660
|
62
|
|
|
|
|
|
switch ( id[0] ) { |
661
|
|
|
|
|
|
|
case 'T': |
662
|
58
|
|
|
|
|
|
frametype = &id3_frametype_text; |
663
|
58
|
|
|
|
|
|
break; |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
case 'W': |
666
|
0
|
|
|
|
|
|
frametype = &id3_frametype_url; |
667
|
0
|
|
|
|
|
|
break; |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
case 'X': |
670
|
|
|
|
|
|
|
case 'Y': |
671
|
|
|
|
|
|
|
case 'Z': |
672
|
0
|
|
|
|
|
|
frametype = &id3_frametype_experimental; |
673
|
0
|
|
|
|
|
|
break; |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
default: |
676
|
4
|
|
|
|
|
|
frametype = &id3_frametype_unknown; |
677
|
4
|
|
|
|
|
|
break; |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
#ifdef AUDIO_SCAN_DEBUG |
682
|
|
|
|
|
|
|
{ |
683
|
|
|
|
|
|
|
int i; |
684
|
|
|
|
|
|
|
DEBUG_TRACE(" nfields %d:", frametype->nfields); |
685
|
|
|
|
|
|
|
for (i = 0; i < frametype->nfields; ++i) { |
686
|
|
|
|
|
|
|
DEBUG_TRACE(" %d", frametype->fields[i]); |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
DEBUG_TRACE("\n"); |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
#endif |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
// If frame was compressed, temporarily set the id3 buffer to use the decompressed buffer |
693
|
923
|
100
|
|
|
|
|
if (decompressed) { |
694
|
2
|
|
|
|
|
|
tmp_buf = id3->buf; |
695
|
2
|
|
|
|
|
|
id3->buf = decompressed; |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
|
698
|
923
|
100
|
|
|
|
|
if ( !_id3_parse_v2_frame_data(id3, (char *)&id, decoded_size ? decoded_size : size, frametype) ) { |
|
|
50
|
|
|
|
|
|
699
|
|
|
|
|
|
|
DEBUG_TRACE(" error parsing frame, aborting\n"); |
700
|
0
|
|
|
|
|
|
ret = 0; |
701
|
0
|
|
|
|
|
|
goto out; |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
923
|
100
|
|
|
|
|
if (id3->size_remain > size) { |
705
|
913
|
|
|
|
|
|
id3->size_remain -= size; |
706
|
|
|
|
|
|
|
} |
707
|
|
|
|
|
|
|
else { |
708
|
10
|
|
|
|
|
|
id3->size_remain = 0; |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
// Consume extra bytes if we had to unsync this frame |
712
|
923
|
100
|
|
|
|
|
if (unsync_extra) { |
713
|
|
|
|
|
|
|
DEBUG_TRACE(" consuming extra bytes after unsync: %d\n", unsync_extra); |
714
|
11
|
|
|
|
|
|
buffer_consume(id3->buf, unsync_extra); |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
out: |
718
|
998
|
100
|
|
|
|
|
if (decompressed) { |
719
|
|
|
|
|
|
|
// Reset id3 buffer and consume rest of compressed frame |
720
|
2
|
|
|
|
|
|
id3->buf = tmp_buf; |
721
|
2
|
|
|
|
|
|
buffer_consume(id3->buf, size); |
722
|
|
|
|
|
|
|
|
723
|
2
|
|
|
|
|
|
buffer_free(decompressed); |
724
|
2
|
|
|
|
|
|
Safefree(decompressed); |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
|
727
|
998
|
|
|
|
|
|
return ret; |
728
|
|
|
|
|
|
|
} |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
int |
731
|
923
|
|
|
|
|
|
_id3_parse_v2_frame_data(id3info *id3, char const *id, uint32_t size, id3_frametype const *frametype) |
732
|
|
|
|
|
|
|
{ |
733
|
923
|
|
|
|
|
|
int ret = 1; |
734
|
923
|
|
|
|
|
|
uint32_t read = 0; |
735
|
923
|
|
|
|
|
|
int8_t encoding = -1; |
736
|
|
|
|
|
|
|
|
737
|
923
|
|
|
|
|
|
uint8_t buffer_art = ( !strcmp(id, "APIC") ) ? 1 : 0; |
738
|
923
|
100
|
|
|
|
|
uint8_t skip_art = ( buffer_art && _env_true("AUDIO_SCAN_NO_ARTWORK") ) ? 1 : 0; |
|
|
100
|
|
|
|
|
|
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
// Bug 16703, a completely empty frame is against the rules, skip it |
741
|
923
|
100
|
|
|
|
|
if (!size) |
742
|
1
|
|
|
|
|
|
return 1; |
743
|
|
|
|
|
|
|
|
744
|
922
|
100
|
|
|
|
|
if (skip_art) { |
745
|
|
|
|
|
|
|
// Only buffer enough for the APIC header fields, this is only a rough guess |
746
|
|
|
|
|
|
|
// because the description could technically be very long |
747
|
5
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, 128, ID3_BLOCK_SIZE) ) { |
748
|
0
|
|
|
|
|
|
return 0; |
749
|
|
|
|
|
|
|
} |
750
|
|
|
|
|
|
|
DEBUG_TRACE(" partial read due to AUDIO_SCAN_NO_ARTWORK\n"); |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
else { |
753
|
|
|
|
|
|
|
// Use a special buffering mode for binary artwork, to avoid |
754
|
|
|
|
|
|
|
// using 2x the memory of the APIC frame (once for buffer, once for SV) |
755
|
917
|
100
|
|
|
|
|
if (buffer_art) { |
756
|
|
|
|
|
|
|
// Buffer enough for encoding/MIME/picture type/description |
757
|
22
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, 128, ID3_BLOCK_SIZE) ) { |
758
|
0
|
|
|
|
|
|
return 0; |
759
|
|
|
|
|
|
|
} |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
else { |
762
|
|
|
|
|
|
|
// Buffer the entire frame |
763
|
895
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, size, ID3_BLOCK_SIZE) ) { |
764
|
0
|
|
|
|
|
|
return 0; |
765
|
|
|
|
|
|
|
} |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
|
769
|
922
|
100
|
|
|
|
|
if ( frametype->fields[0] == ID3_FIELD_TYPE_TEXTENCODING ) { |
770
|
|
|
|
|
|
|
// many frames have an encoding byte, read it here |
771
|
827
|
|
|
|
|
|
encoding = buffer_get_char(id3->buf); |
772
|
827
|
|
|
|
|
|
read++; |
773
|
|
|
|
|
|
|
DEBUG_TRACE(" encoding: %d\n", encoding); |
774
|
|
|
|
|
|
|
|
775
|
827
|
50
|
|
|
|
|
if (encoding < 0 || encoding > 3) { |
|
|
100
|
|
|
|
|
|
776
|
|
|
|
|
|
|
DEBUG_TRACE(" invalid encoding, skipping frame\n"); |
777
|
|
|
|
|
|
|
goto out; |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
} |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
// Special handling for TXXX/WXXX frames |
782
|
1069
|
100
|
|
|
|
|
if ( !strcmp(id, "TXXX") || !strcmp(id, "WXXX") ) { |
|
|
100
|
|
|
|
|
|
783
|
|
|
|
|
|
|
// Read key and uppercase it |
784
|
150
|
|
|
|
|
|
SV *key = NULL; |
785
|
150
|
|
|
|
|
|
SV *value = NULL; |
786
|
|
|
|
|
|
|
|
787
|
150
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &key, size - read, encoding); |
788
|
|
|
|
|
|
|
|
789
|
150
|
50
|
|
|
|
|
if (key != NULL && SvPOK(key) && sv_len(key)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
790
|
145
|
|
|
|
|
|
upcase(SvPVX(key)); |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
// Read value |
793
|
145
|
100
|
|
|
|
|
if (frametype->fields[2] == ID3_FIELD_TYPE_LATIN1) { |
794
|
|
|
|
|
|
|
// WXXX frames have a latin1 value field regardless of encoding byte |
795
|
14
|
|
|
|
|
|
encoding = ISO_8859_1; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
|
798
|
145
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, encoding); |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
// (T|W)XXX frames don't support multiple strings separated by nulls, even in v2.4 |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
// Only one tag per unique key value is allowed, that's why there is no array support here |
803
|
145
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value) && sv_len(value)) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
804
|
145
|
|
|
|
|
|
my_hv_store_ent( id3->tags, key, value ); |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
else { |
807
|
0
|
|
|
|
|
|
my_hv_store_ent( id3->tags, key, &PL_sv_undef ); |
808
|
145
|
0
|
|
|
|
|
if (value) SvREFCNT_dec(value); |
809
|
|
|
|
|
|
|
} |
810
|
|
|
|
|
|
|
} |
811
|
|
|
|
|
|
|
else { |
812
|
|
|
|
|
|
|
DEBUG_TRACE(" invalid/empty (T|W)XXX key, skipping frame\n"); |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
|
815
|
150
|
50
|
|
|
|
|
if (key) SvREFCNT_dec(key); |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
// Special handling for TCON genre frame |
819
|
769
|
100
|
|
|
|
|
else if ( !strcmp(id, "TCON") ) { |
820
|
71
|
|
|
|
|
|
AV *genres = newAV(); |
821
|
|
|
|
|
|
|
char *sptr, *end, *tmp; |
822
|
|
|
|
|
|
|
|
823
|
143
|
100
|
|
|
|
|
while (read < size) { |
824
|
72
|
|
|
|
|
|
SV *value = NULL; |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
// v2.4 handles multiple genres using null char separators (or $00 $00 in UTF-16), |
827
|
|
|
|
|
|
|
// this is handled by _id3_get_utf8_string |
828
|
72
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, encoding); |
829
|
72
|
100
|
|
|
|
|
if (value != NULL && SvPOK(value)) { |
|
|
50
|
|
|
|
|
|
830
|
71
|
|
|
|
|
|
sptr = SvPVX(value); |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
// Test if the string contains only a number, |
833
|
|
|
|
|
|
|
// strtol will set tmp to end in this case |
834
|
71
|
|
|
|
|
|
end = sptr + sv_len(value); |
835
|
71
|
|
|
|
|
|
strtol(sptr, &tmp, 0); |
836
|
|
|
|
|
|
|
|
837
|
71
|
100
|
|
|
|
|
if ( tmp == end ) { |
838
|
|
|
|
|
|
|
// Convert raw number to genre string |
839
|
2
|
|
|
|
|
|
av_push( genres, newSVpv( _id3_genre_name((char *)sptr), 0 ) ); |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
// value as an SV won't be used, must drop refcnt |
842
|
2
|
|
|
|
|
|
SvREFCNT_dec(value); |
843
|
|
|
|
|
|
|
} |
844
|
69
|
100
|
|
|
|
|
else if ( *sptr == '(' ) { |
845
|
|
|
|
|
|
|
// Handle (26), (26)Ambient, etc, only the number portion will be read |
846
|
|
|
|
|
|
|
|
847
|
30
|
100
|
|
|
|
|
if (id3->version_major < 4) { |
848
|
|
|
|
|
|
|
// v2.2/v2.3 handle multiple genres using parens for some reason, i.e. (51)(39) or (55)(Text) |
849
|
17
|
|
|
|
|
|
char *ptr = sptr; |
850
|
17
|
|
|
|
|
|
char *end = sptr + sv_len(value); |
851
|
|
|
|
|
|
|
|
852
|
97
|
100
|
|
|
|
|
while (end - ptr > 0) { |
853
|
80
|
100
|
|
|
|
|
if ( *ptr++ == '(' ) { |
854
|
20
|
|
|
|
|
|
char *paren = strchr(ptr, ')'); |
855
|
20
|
50
|
|
|
|
|
if (paren == NULL) |
856
|
0
|
|
|
|
|
|
paren = end; |
857
|
|
|
|
|
|
|
|
858
|
20
|
100
|
|
|
|
|
if ( isdigit(*ptr) || !strncmp((char *)ptr, "RX", 2) || !strncmp((char *)ptr, "CR", 2) ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
859
|
19
|
|
|
|
|
|
av_push( genres, newSVpv( _id3_genre_name((char *)ptr), 0 ) ); |
860
|
|
|
|
|
|
|
} |
861
|
|
|
|
|
|
|
else { |
862
|
|
|
|
|
|
|
// Handle text within parens |
863
|
1
|
|
|
|
|
|
av_push( genres, newSVpvn(ptr, paren - ptr) ); |
864
|
|
|
|
|
|
|
} |
865
|
20
|
|
|
|
|
|
ptr = paren; |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
else { |
870
|
|
|
|
|
|
|
// v2.4, the (51) method is no longer valid but we will support it anyway |
871
|
13
|
|
|
|
|
|
sptr++; |
872
|
13
|
50
|
|
|
|
|
if ( isdigit(*sptr) || !strncmp(sptr, "RX", 2) || !strncmp(sptr, "CR", 2) ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
873
|
13
|
|
|
|
|
|
av_push( genres, newSVpv( _id3_genre_name((char *)sptr), 0 ) ); |
874
|
|
|
|
|
|
|
} |
875
|
|
|
|
|
|
|
else { |
876
|
0
|
|
|
|
|
|
av_push( genres, newSVpv( (char *)sptr, 0 ) ); |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
} |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
// value as an SV won't be used, must drop refcnt |
881
|
30
|
|
|
|
|
|
SvREFCNT_dec(value); |
882
|
|
|
|
|
|
|
} |
883
|
|
|
|
|
|
|
else { |
884
|
|
|
|
|
|
|
// Support raw RX/CR value |
885
|
39
|
50
|
|
|
|
|
if ( !strncmp(sptr, "RX", 2) || !strncmp(sptr, "CR", 2) ) { |
|
|
50
|
|
|
|
|
|
886
|
0
|
|
|
|
|
|
av_push( genres, newSVpv( _id3_genre_name((char *)sptr), 0 ) ); |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
// value as an SV won't be used, must drop refcnt |
889
|
0
|
|
|
|
|
|
SvREFCNT_dec(value); |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
else { |
892
|
|
|
|
|
|
|
// Store plain text genre |
893
|
72
|
|
|
|
|
|
av_push( genres, value ); |
894
|
|
|
|
|
|
|
} |
895
|
|
|
|
|
|
|
} |
896
|
|
|
|
|
|
|
} |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
|
899
|
71
|
100
|
|
|
|
|
if (av_len(genres) > 0) { |
900
|
6
|
|
|
|
|
|
my_hv_store( id3->tags, id, newRV_noinc( (SV *)genres ) ); |
901
|
|
|
|
|
|
|
} |
902
|
65
|
100
|
|
|
|
|
else if (av_len(genres) == 0) { |
903
|
62
|
|
|
|
|
|
my_hv_store( id3->tags, id, av_shift(genres) ); |
904
|
62
|
|
|
|
|
|
SvREFCNT_dec(genres); |
905
|
|
|
|
|
|
|
} |
906
|
|
|
|
|
|
|
else { |
907
|
71
|
|
|
|
|
|
SvREFCNT_dec(genres); |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
// 1-field frames: MCDI, PCNT, SEEK (unsupported), T* (text), W* (url), unknown |
912
|
|
|
|
|
|
|
// and 2-field frames where the first field is encoding |
913
|
|
|
|
|
|
|
// are mapped to plain hash entries |
914
|
698
|
100
|
|
|
|
|
else if ( |
915
|
658
|
100
|
|
|
|
|
frametype->nfields == 1 || |
916
|
553
|
100
|
|
|
|
|
(frametype->nfields == 2 && frametype->fields[0] == ID3_FIELD_TYPE_TEXTENCODING) |
917
|
555
|
|
|
|
|
|
) { |
918
|
555
|
|
|
|
|
|
int i = frametype->nfields - 1; |
919
|
555
|
|
|
|
|
|
AV *array = NULL; |
920
|
555
|
|
|
|
|
|
SV *value = NULL; |
921
|
555
|
|
|
|
|
|
int count = 0; |
922
|
|
|
|
|
|
|
|
923
|
555
|
|
|
|
|
|
switch ( frametype->fields[i] ) { |
924
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_LATIN1: // W* frames |
925
|
28
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, ISO_8859_1); |
926
|
28
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value)) |
|
|
50
|
|
|
|
|
|
927
|
28
|
|
|
|
|
|
my_hv_store( id3->tags, id, value ); |
928
|
28
|
|
|
|
|
|
break; |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_STRINGLIST: // T* frames |
931
|
|
|
|
|
|
|
// XXX technically in v2.2/v2.3 we should ignore multiple strings separated by nulls, but |
932
|
|
|
|
|
|
|
// allowing it is fine I think |
933
|
1044
|
100
|
|
|
|
|
while (read < size) { |
934
|
529
|
100
|
|
|
|
|
if (count++ == 1 && value != NULL) { |
|
|
50
|
|
|
|
|
|
935
|
|
|
|
|
|
|
// we're reading the second string in the list, move first value to new array |
936
|
4
|
|
|
|
|
|
array = newAV(); |
937
|
4
|
|
|
|
|
|
av_push(array, value); |
938
|
|
|
|
|
|
|
} |
939
|
529
|
|
|
|
|
|
value = NULL; |
940
|
|
|
|
|
|
|
|
941
|
529
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, encoding); |
942
|
|
|
|
|
|
|
|
943
|
529
|
100
|
|
|
|
|
if (array != NULL && value != NULL && SvPOK(value)) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
944
|
|
|
|
|
|
|
// second+ string, add to array |
945
|
|
|
|
|
|
|
// Bug 16452, do not add a null string |
946
|
34
|
100
|
|
|
|
|
if (sv_len(value) > 0) |
947
|
4
|
|
|
|
|
|
av_push(array, value); |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
} |
950
|
|
|
|
|
|
|
|
951
|
515
|
100
|
|
|
|
|
if (array != NULL) { |
952
|
4
|
100
|
|
|
|
|
if (av_len(array) == 0) { |
953
|
|
|
|
|
|
|
// Handle the case where we have multiple empty strings leaving an array of 1 |
954
|
2
|
|
|
|
|
|
my_hv_store( id3->tags, id, av_shift(array) ); |
955
|
2
|
|
|
|
|
|
SvREFCNT_dec(array); |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
else { |
958
|
4
|
|
|
|
|
|
my_hv_store( id3->tags, id, newRV_noinc( (SV *)array ) ); |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
} |
961
|
511
|
100
|
|
|
|
|
else if (value != NULL && SvPOK(value)) { |
|
|
50
|
|
|
|
|
|
962
|
488
|
|
|
|
|
|
my_hv_store( id3->tags, id, value ); |
963
|
|
|
|
|
|
|
} |
964
|
515
|
|
|
|
|
|
break; |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT32: // SEEK (unsupported, XXX need test) |
967
|
0
|
|
|
|
|
|
my_hv_store( id3->tags, id, newSViv( buffer_get_int(id3->buf) ) ); |
968
|
0
|
|
|
|
|
|
read += 4; |
969
|
0
|
|
|
|
|
|
break; |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT32PLUS: // PCNT |
972
|
7
|
|
|
|
|
|
my_hv_store( id3->tags, id, newSViv( _varint( buffer_ptr(id3->buf), size - read ) ) ); |
973
|
7
|
|
|
|
|
|
buffer_consume(id3->buf, size - read); |
974
|
7
|
|
|
|
|
|
read = size; |
975
|
7
|
|
|
|
|
|
break; |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_BINARYDATA: // unknown/obsolete frames |
978
|
|
|
|
|
|
|
// Special handling for RVA(D), tested in v2.2-itunes81.mp3, v2.3-itunes81.mp3 |
979
|
5
|
100
|
|
|
|
|
if ( !strcmp(id, "RVAD") ) { |
980
|
3
|
|
|
|
|
|
read += _id3_parse_rvad(id3, id, size - read); |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
// Special handling for RGAD (non-standard replaygain frame), tested in v2.3-rgad.mp3 |
984
|
|
|
|
|
|
|
// Based on some code found at http://getid3.sourceforge.net/source/module.tag.id3v2.phps |
985
|
2
|
100
|
|
|
|
|
else if ( !strcmp(id, "RGAD") ) { |
986
|
1
|
|
|
|
|
|
read += _id3_parse_rgad(id3); |
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
// Other unknown binary data |
990
|
|
|
|
|
|
|
else { |
991
|
|
|
|
|
|
|
// Y* obsolete frames |
992
|
1
|
|
|
|
|
|
my_hv_store( id3->tags, id, newSVpvn( buffer_ptr(id3->buf), size - read ) ); |
993
|
1
|
|
|
|
|
|
buffer_consume(id3->buf, size - read); |
994
|
1
|
|
|
|
|
|
read = size; |
995
|
|
|
|
|
|
|
} |
996
|
5
|
|
|
|
|
|
break; |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
default: |
999
|
|
|
|
|
|
|
// XXX |
1000
|
0
|
|
|
|
|
|
warn(" !!! unhandled field type %d\n", frametype->fields[i]); |
1001
|
0
|
|
|
|
|
|
buffer_consume(id3->buf, size - read); |
1002
|
0
|
|
|
|
|
|
read += size - read; |
1003
|
0
|
|
|
|
|
|
break; |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
// 2+ field frames are mapped to arrayrefs: |
1008
|
|
|
|
|
|
|
// The following frames have tests: |
1009
|
|
|
|
|
|
|
// ETCO, UFID, USLT, SYLT, COMM, RVA2, APIC, GEOB, POPM, LINK, PRIV |
1010
|
|
|
|
|
|
|
// |
1011
|
|
|
|
|
|
|
// XXX The following frames need tests: |
1012
|
|
|
|
|
|
|
// MLLT, SYTC, EQU2, RVRB, AENC, POSS, USER, OWNE, |
1013
|
|
|
|
|
|
|
// COMR, ENCR, GRID, SIGN, ASPI, LINK (v2.4) |
1014
|
|
|
|
|
|
|
else { |
1015
|
143
|
|
|
|
|
|
int i = 0; |
1016
|
143
|
|
|
|
|
|
AV *framedata = newAV(); |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
// If we read an initial encoding byte, start at field 2 |
1019
|
143
|
100
|
|
|
|
|
if (encoding >= 0) |
1020
|
88
|
|
|
|
|
|
i = 1; |
1021
|
|
|
|
|
|
|
|
1022
|
567
|
100
|
|
|
|
|
for (; i < frametype->nfields; i++) { |
1023
|
424
|
|
|
|
|
|
SV *value = NULL; |
1024
|
|
|
|
|
|
|
|
1025
|
424
|
|
|
|
|
|
switch ( frametype->fields[i] ) { |
1026
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_LATIN1: |
1027
|
|
|
|
|
|
|
// Special case, fix v2.2 PIC frame fields as they don't match APIC |
1028
|
|
|
|
|
|
|
// This is a rather hackish place to put this, but there's not really any other place |
1029
|
84
|
100
|
|
|
|
|
if ( id3->version_major == 2 && !strcmp(id, "APIC") ) { |
|
|
100
|
|
|
|
|
|
1030
|
3
|
|
|
|
|
|
av_push( framedata, newSVpvn( buffer_ptr(id3->buf), 3 ) ); |
1031
|
3
|
|
|
|
|
|
buffer_consume(id3->buf, 3); |
1032
|
3
|
|
|
|
|
|
read += 3; |
1033
|
|
|
|
|
|
|
DEBUG_TRACE(" PIC image format, read %d\n", read); |
1034
|
|
|
|
|
|
|
} |
1035
|
|
|
|
|
|
|
else { |
1036
|
81
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, ISO_8859_1); |
1037
|
81
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value)) |
|
|
50
|
|
|
|
|
|
1038
|
81
|
|
|
|
|
|
av_push( framedata, value ); |
1039
|
|
|
|
|
|
|
} |
1040
|
84
|
|
|
|
|
|
break; |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
// ID3_FIELD_TYPE_LATIN1FULL - not used |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_LATIN1LIST: // LINK |
1045
|
3
|
100
|
|
|
|
|
while (read < size) { |
1046
|
1
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, ISO_8859_1); |
1047
|
1
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value)) |
|
|
50
|
|
|
|
|
|
1048
|
1
|
|
|
|
|
|
av_push( framedata, value ); |
1049
|
1
|
|
|
|
|
|
value = NULL; |
1050
|
|
|
|
|
|
|
DEBUG_TRACE(" latin1list, read %d\n", read); |
1051
|
|
|
|
|
|
|
} |
1052
|
2
|
|
|
|
|
|
break; |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_STRING: |
1055
|
91
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, encoding); |
1056
|
91
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value)) { |
|
|
50
|
|
|
|
|
|
1057
|
91
|
|
|
|
|
|
av_push( framedata, value ); |
1058
|
|
|
|
|
|
|
DEBUG_TRACE(" string, read %d: %s\n", read, SvPVX(value)); |
1059
|
|
|
|
|
|
|
} |
1060
|
|
|
|
|
|
|
else { |
1061
|
0
|
|
|
|
|
|
av_push( framedata, &PL_sv_undef ); |
1062
|
0
|
0
|
|
|
|
|
if (value) SvREFCNT_dec(value); |
1063
|
|
|
|
|
|
|
} |
1064
|
91
|
|
|
|
|
|
break; |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_STRINGFULL: // USLT, COMM, read entire string until end of frame |
1067
|
|
|
|
|
|
|
{ |
1068
|
57
|
|
|
|
|
|
SV *tmp = newSVpvn( "", 0 ); |
1069
|
119
|
100
|
|
|
|
|
while (read < size) { |
1070
|
62
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, size - read, encoding); |
1071
|
62
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value)) { |
|
|
50
|
|
|
|
|
|
1072
|
62
|
|
|
|
|
|
sv_catsv( tmp, value ); |
1073
|
62
|
|
|
|
|
|
SvREFCNT_dec(value); |
1074
|
|
|
|
|
|
|
} |
1075
|
62
|
|
|
|
|
|
value = NULL; |
1076
|
|
|
|
|
|
|
} |
1077
|
57
|
|
|
|
|
|
av_push( framedata, tmp ); |
1078
|
|
|
|
|
|
|
DEBUG_TRACE(" stringfull, read %d: %s\n", read, SvPVX(tmp)); |
1079
|
57
|
|
|
|
|
|
break; |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
// ID3_FIELD_TYPE_STRINGLIST - only used for text frames, handled above |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_LANGUAGE: // USLT, SYLT, COMM, USER, 3-byte language code |
1085
|
58
|
50
|
|
|
|
|
if (size - read >= 3) { |
1086
|
58
|
|
|
|
|
|
av_push( framedata, newSVpvn( buffer_ptr(id3->buf), 3 ) ); |
1087
|
58
|
|
|
|
|
|
buffer_consume(id3->buf, 3); |
1088
|
58
|
|
|
|
|
|
read += 3; |
1089
|
|
|
|
|
|
|
DEBUG_TRACE(" language, read %d\n", read); |
1090
|
|
|
|
|
|
|
} |
1091
|
58
|
|
|
|
|
|
break; |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_FRAMEID: // LINK, 3-byte frame id (v2.3, must be a bug in the spec?), |
1094
|
|
|
|
|
|
|
// 4-byte frame id (v2.4) XXX need test |
1095
|
|
|
|
|
|
|
{ |
1096
|
2
|
50
|
|
|
|
|
uint8_t len = (id3->version_major == 3) ? 3 : 4; |
1097
|
2
|
50
|
|
|
|
|
if (size - read >= len) { |
1098
|
2
|
|
|
|
|
|
av_push( framedata, newSVpvn( buffer_ptr(id3->buf), len ) ); |
1099
|
2
|
|
|
|
|
|
buffer_consume(id3->buf, len); |
1100
|
2
|
|
|
|
|
|
read += len; |
1101
|
|
|
|
|
|
|
DEBUG_TRACE(" frameid, read %d\n", read); |
1102
|
|
|
|
|
|
|
} |
1103
|
2
|
|
|
|
|
|
break; |
1104
|
|
|
|
|
|
|
} |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_DATE: // OWNE, COMR, XXX need test, YYYYMMDD |
1107
|
0
|
0
|
|
|
|
|
if (size - read >= 8) { |
1108
|
0
|
|
|
|
|
|
av_push( framedata, newSVpvn( buffer_ptr(id3->buf), 8 ) ); |
1109
|
0
|
|
|
|
|
|
buffer_consume(id3->buf, 8); |
1110
|
0
|
|
|
|
|
|
read += 8; |
1111
|
|
|
|
|
|
|
DEBUG_TRACE(" date, read %d\n", read); |
1112
|
|
|
|
|
|
|
} |
1113
|
0
|
|
|
|
|
|
break; |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT8: // ETCO, MLLT, SYTC, SYLT, EQU2, RVRB, APIC, |
1116
|
|
|
|
|
|
|
// POPM, RBUF, POSS, COMR, ENCR, GRID, SIGN, ASPI |
1117
|
44
|
50
|
|
|
|
|
if (size - read >= 1) { |
1118
|
44
|
|
|
|
|
|
av_push( framedata, newSViv( buffer_get_char(id3->buf) ) ); |
1119
|
44
|
|
|
|
|
|
read += 1; |
1120
|
|
|
|
|
|
|
DEBUG_TRACE(" int8, read %d\n", read); |
1121
|
|
|
|
|
|
|
} |
1122
|
44
|
|
|
|
|
|
break; |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT16: // MLLT, RVRB, AENC, ASPI |
1125
|
2
|
50
|
|
|
|
|
if (size - read >= 2) { |
1126
|
0
|
|
|
|
|
|
av_push( framedata, newSViv( buffer_get_short(id3->buf) ) ); |
1127
|
0
|
|
|
|
|
|
read += 2; |
1128
|
|
|
|
|
|
|
DEBUG_TRACE(" int16, read %d\n", read); |
1129
|
|
|
|
|
|
|
} |
1130
|
2
|
|
|
|
|
|
break; |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT24: // MLLT, RBUF |
1133
|
0
|
0
|
|
|
|
|
if (size - read >= 3) { |
1134
|
0
|
|
|
|
|
|
av_push( framedata, newSViv( buffer_get_int24(id3->buf) ) ); |
1135
|
0
|
|
|
|
|
|
read += 3; |
1136
|
|
|
|
|
|
|
DEBUG_TRACE(" int24, read %d\n", read); |
1137
|
|
|
|
|
|
|
} |
1138
|
0
|
|
|
|
|
|
break; |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT32: // RBUF, SEEK, ASPI |
1141
|
0
|
0
|
|
|
|
|
if (size - read >= 4) { |
1142
|
0
|
|
|
|
|
|
av_push( framedata, newSViv( buffer_get_int(id3->buf) ) ); |
1143
|
0
|
|
|
|
|
|
read += 4; |
1144
|
|
|
|
|
|
|
DEBUG_TRACE(" int32, read %d\n", read); |
1145
|
|
|
|
|
|
|
} |
1146
|
0
|
|
|
|
|
|
break; |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_INT32PLUS: // POPM |
1149
|
14
|
50
|
|
|
|
|
if (size - read >= 4) { |
1150
|
14
|
|
|
|
|
|
av_push( framedata, newSViv( _varint( buffer_ptr(id3->buf), size - read ) ) ); |
1151
|
14
|
|
|
|
|
|
buffer_consume(id3->buf, size - read); |
1152
|
14
|
|
|
|
|
|
read = size; |
1153
|
|
|
|
|
|
|
DEBUG_TRACE(" int32plus, read %d\n", read); |
1154
|
|
|
|
|
|
|
} |
1155
|
14
|
|
|
|
|
|
break; |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
case ID3_FIELD_TYPE_BINARYDATA: // ETCO, MLLT, SYTC, SYLT, RVA2, EQU2, APIC, |
1158
|
|
|
|
|
|
|
// GEOB, AENC, POSS, COMR, ENCR, GRID, PRIV, SIGN, ASPI |
1159
|
|
|
|
|
|
|
// Special handling for APIC tags when in skip_art mode |
1160
|
70
|
100
|
|
|
|
|
if (skip_art) { |
1161
|
5
|
|
|
|
|
|
av_push( framedata, newSVuv(size - read) ); |
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
// I don't think it's possible to obtain an APIC offset when a tag has been unsync'ed, |
1164
|
|
|
|
|
|
|
// so we can't support skip_art mode in this case. See v2.3-unsync-apic-bad-offset.mp3 |
1165
|
5
|
100
|
|
|
|
|
if (id3->flags & ID3_TAG_FLAG_UNSYNCHRONISATION && id3->version_major < 4) { |
|
|
100
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
DEBUG_TRACE(" cannot obtain APIC offset due to v2.3 unsync tag\n"); |
1167
|
|
|
|
|
|
|
} |
1168
|
|
|
|
|
|
|
else { |
1169
|
|
|
|
|
|
|
// Record offset of APIC image data too, unless the data needs to be unsynchronized or is empty |
1170
|
4
|
100
|
|
|
|
|
if (id3->tag_data_safe && (size - read) > 0) |
|
|
50
|
|
|
|
|
|
1171
|
3
|
|
|
|
|
|
av_push( framedata, newSVuv(id3->offset + (id3->size - id3->size_remain) + read) ); |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
|
1174
|
5
|
|
|
|
|
|
_id3_skip(id3, size - read); |
1175
|
5
|
|
|
|
|
|
read = size; |
1176
|
|
|
|
|
|
|
} |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
// Special buffering mode for APIC data, avoids a large buffer allocation |
1179
|
65
|
100
|
|
|
|
|
else if (buffer_art) { |
1180
|
22
|
|
|
|
|
|
uint32_t remain = size - read; |
1181
|
|
|
|
|
|
|
uint32_t chunk_size; |
1182
|
22
|
|
|
|
|
|
SV *artwork = newSVpv("", 0); |
1183
|
|
|
|
|
|
|
|
1184
|
125
|
100
|
|
|
|
|
while (read < size) { |
1185
|
103
|
50
|
|
|
|
|
if ( !_check_buf(id3->infile, id3->buf, 1, ID3_BLOCK_SIZE) ) { |
1186
|
0
|
|
|
|
|
|
return 0; |
1187
|
|
|
|
|
|
|
} |
1188
|
|
|
|
|
|
|
|
1189
|
103
|
100
|
|
|
|
|
chunk_size = remain < buffer_len(id3->buf) ? remain : buffer_len(id3->buf); |
1190
|
|
|
|
|
|
|
|
1191
|
103
|
|
|
|
|
|
read += chunk_size; |
1192
|
103
|
|
|
|
|
|
remain -= chunk_size; |
1193
|
|
|
|
|
|
|
|
1194
|
103
|
|
|
|
|
|
sv_catpvn( artwork, buffer_ptr(id3->buf), chunk_size ); |
1195
|
103
|
|
|
|
|
|
buffer_consume(id3->buf, chunk_size); |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
DEBUG_TRACE(" buffered %d bytes of APIC data (remaining %d)\n", chunk_size, remain); |
1198
|
|
|
|
|
|
|
} |
1199
|
|
|
|
|
|
|
|
1200
|
22
|
|
|
|
|
|
av_push( framedata, artwork ); |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
// Special handling for RVA2 tags |
1204
|
43
|
100
|
|
|
|
|
else if ( !strcmp(id, "RVA2") ) { |
1205
|
10
|
|
|
|
|
|
read += _id3_parse_rva2(id3, size, framedata); |
1206
|
|
|
|
|
|
|
} |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
// Special handling for SYLT tags |
1209
|
33
|
100
|
|
|
|
|
else if ( !strcmp(id, "SYLT") ) { |
1210
|
1
|
|
|
|
|
|
read += _id3_parse_sylt(id3, encoding, size - read, framedata); |
1211
|
|
|
|
|
|
|
} |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
// Special handling for ETCO tags |
1214
|
32
|
100
|
|
|
|
|
else if ( !strcmp(id, "ETCO") ) { |
1215
|
1
|
|
|
|
|
|
read += _id3_parse_etco(id3, size - read, framedata); |
1216
|
|
|
|
|
|
|
} |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
// All other binary frames, copy as-is |
1219
|
|
|
|
|
|
|
else { |
1220
|
31
|
100
|
|
|
|
|
if (size - read > 1) { |
1221
|
30
|
|
|
|
|
|
av_push( framedata, newSVpvn( buffer_ptr(id3->buf), size - read ) ); |
1222
|
30
|
|
|
|
|
|
buffer_consume(id3->buf, size - read); |
1223
|
30
|
|
|
|
|
|
read = size; |
1224
|
|
|
|
|
|
|
DEBUG_TRACE(" binarydata, read %d\n", read); |
1225
|
|
|
|
|
|
|
} |
1226
|
|
|
|
|
|
|
} |
1227
|
70
|
|
|
|
|
|
break; |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
default: |
1230
|
0
|
|
|
|
|
|
break; |
1231
|
|
|
|
|
|
|
} |
1232
|
|
|
|
|
|
|
} |
1233
|
|
|
|
|
|
|
|
1234
|
143
|
|
|
|
|
|
_id3_set_array_tag(id3, id, framedata); |
1235
|
|
|
|
|
|
|
} |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
out: |
1238
|
922
|
100
|
|
|
|
|
if (read < size) { |
1239
|
8
|
|
|
|
|
|
buffer_consume(id3->buf, size - read); |
1240
|
|
|
|
|
|
|
DEBUG_TRACE(" !!! consuming extra bytes in frame: %d\n", size - read); |
1241
|
|
|
|
|
|
|
} |
1242
|
|
|
|
|
|
|
|
1243
|
922
|
|
|
|
|
|
return ret; |
1244
|
|
|
|
|
|
|
} |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
void |
1247
|
143
|
|
|
|
|
|
_id3_set_array_tag(id3info *id3, char const *id, AV *framedata) |
1248
|
|
|
|
|
|
|
{ |
1249
|
143
|
50
|
|
|
|
|
if ( av_len(framedata) != -1 ) { |
1250
|
143
|
100
|
|
|
|
|
if ( my_hv_exists( id3->tags, id ) ) { |
1251
|
|
|
|
|
|
|
// If tag already exists, move it to an arrayref |
1252
|
35
|
|
|
|
|
|
SV **entry = my_hv_fetch( id3->tags, id ); |
1253
|
35
|
50
|
|
|
|
|
if (entry != NULL) { |
1254
|
35
|
50
|
|
|
|
|
if ( SvTYPE( SvRV(*entry) ) == SVt_PV ) { |
1255
|
|
|
|
|
|
|
// A normal string entry, convert to array |
1256
|
0
|
|
|
|
|
|
AV *ref = newAV(); |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
// XXX need test, this may be illegal because you can't have multiple duplicate frames? |
1259
|
|
|
|
|
|
|
DEBUG_TRACE(" !!! converting normal string tag to array\n"); |
1260
|
|
|
|
|
|
|
|
1261
|
0
|
|
|
|
|
|
av_push( ref, *entry ); |
1262
|
0
|
|
|
|
|
|
av_push( ref, newRV_noinc( (SV *)framedata ) ); |
1263
|
0
|
|
|
|
|
|
my_hv_store( id3->tags, id, newRV_noinc( (SV *)ref ) ); |
1264
|
|
|
|
|
|
|
} |
1265
|
35
|
50
|
|
|
|
|
else if ( SvTYPE( SvRV(*entry) ) == SVt_PVAV ) { |
1266
|
|
|
|
|
|
|
// If type of first item is array, add new item to entry |
1267
|
35
|
|
|
|
|
|
SV **first = av_fetch( (AV *)SvRV(*entry), 0, 0 ); |
1268
|
35
|
50
|
|
|
|
|
if ( first == NULL || ( SvROK(*first) && SvTYPE( SvRV(*first) ) == SVt_PVAV ) ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1269
|
14
|
|
|
|
|
|
av_push( (AV *)SvRV(*entry), newRV_noinc( (SV *)framedata ) ); |
1270
|
|
|
|
|
|
|
} |
1271
|
|
|
|
|
|
|
else { |
1272
|
21
|
|
|
|
|
|
AV *ref = newAV(); |
1273
|
21
|
|
|
|
|
|
av_push( ref, SvREFCNT_inc(*entry) ); |
1274
|
21
|
|
|
|
|
|
av_push( ref, newRV_noinc( (SV *)framedata) ); |
1275
|
35
|
|
|
|
|
|
my_hv_store( id3->tags, id, newRV_noinc( (SV *)ref ) ); |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
} |
1278
|
|
|
|
|
|
|
} |
1279
|
|
|
|
|
|
|
} |
1280
|
|
|
|
|
|
|
else { |
1281
|
143
|
|
|
|
|
|
my_hv_store( id3->tags, id, newRV_noinc( (SV *)framedata ) ); |
1282
|
|
|
|
|
|
|
} |
1283
|
|
|
|
|
|
|
} |
1284
|
|
|
|
|
|
|
else { |
1285
|
0
|
|
|
|
|
|
SvREFCNT_dec(framedata); |
1286
|
|
|
|
|
|
|
} |
1287
|
143
|
|
|
|
|
|
} |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
// Read a latin1 or UTF-8 string from an ID3v1 tag |
1290
|
|
|
|
|
|
|
// This function handles trimming spaces off the end |
1291
|
|
|
|
|
|
|
uint32_t |
1292
|
80
|
|
|
|
|
|
_id3_get_v1_utf8_string(id3info *id3, SV **string, uint32_t len) |
1293
|
|
|
|
|
|
|
{ |
1294
|
80
|
|
|
|
|
|
uint32_t read = 0; |
1295
|
|
|
|
|
|
|
char *ptr; |
1296
|
|
|
|
|
|
|
char *str; |
1297
|
|
|
|
|
|
|
|
1298
|
80
|
|
|
|
|
|
read = _id3_get_utf8_string(id3, string, len, ISO_8859_1); |
1299
|
|
|
|
|
|
|
|
1300
|
80
|
50
|
|
|
|
|
if (read) { |
1301
|
|
|
|
|
|
|
// Trim spaces from end |
1302
|
80
|
50
|
|
|
|
|
if (*string != NULL) { |
1303
|
80
|
|
|
|
|
|
str = SvPVX(*string); |
1304
|
80
|
|
|
|
|
|
ptr = str + sv_len(*string); |
1305
|
|
|
|
|
|
|
|
1306
|
324
|
100
|
|
|
|
|
while (ptr > str && ptr[-1] == ' ') |
|
|
100
|
|
|
|
|
|
1307
|
244
|
|
|
|
|
|
--ptr; |
1308
|
|
|
|
|
|
|
|
1309
|
80
|
|
|
|
|
|
*ptr = 0; |
1310
|
80
|
|
|
|
|
|
SvCUR_set(*string, ptr - str); |
1311
|
|
|
|
|
|
|
} |
1312
|
|
|
|
|
|
|
} |
1313
|
|
|
|
|
|
|
|
1314
|
80
|
|
|
|
|
|
return read; |
1315
|
|
|
|
|
|
|
} |
1316
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
uint32_t |
1318
|
1255
|
|
|
|
|
|
_id3_get_utf8_string(id3info *id3, SV **string, uint32_t len, uint8_t encoding) |
1319
|
|
|
|
|
|
|
{ |
1320
|
1255
|
|
|
|
|
|
uint8_t byteorder = UTF16_BYTEORDER_ANY; |
1321
|
1255
|
|
|
|
|
|
uint32_t read = 0; |
1322
|
|
|
|
|
|
|
unsigned char *bptr; |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
// Init scratch buffer if necessary |
1325
|
1255
|
100
|
|
|
|
|
if ( !id3->utf8->alloc ) { |
1326
|
|
|
|
|
|
|
// Use a larger initial buffer if reading ISO-8859-1 to avoid |
1327
|
|
|
|
|
|
|
// always having to allocate a second time |
1328
|
85
|
100
|
|
|
|
|
buffer_init( id3->utf8, encoding == ISO_8859_1 ? len * 2 : len ); |
1329
|
|
|
|
|
|
|
} |
1330
|
|
|
|
|
|
|
else { |
1331
|
|
|
|
|
|
|
// Reset scratch buffer |
1332
|
1170
|
|
|
|
|
|
buffer_clear(id3->utf8); |
1333
|
|
|
|
|
|
|
} |
1334
|
|
|
|
|
|
|
|
1335
|
1255
|
50
|
|
|
|
|
if ( *string != NULL ) { |
1336
|
0
|
|
|
|
|
|
warn(" !!! string SV is not null: %s\n", SvPVX(*string)); |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
|
1339
|
1255
|
|
|
|
|
|
switch (encoding) { |
1340
|
|
|
|
|
|
|
case ISO_8859_1: |
1341
|
797
|
|
|
|
|
|
read += buffer_get_latin1_as_utf8(id3->buf, id3->utf8, len); |
1342
|
797
|
|
|
|
|
|
break; |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
case UTF_16BE: |
1345
|
13
|
|
|
|
|
|
byteorder = UTF16_BYTEORDER_BE; |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
case UTF_16: |
1348
|
138
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
1349
|
|
|
|
|
|
|
|
1350
|
138
|
|
|
|
|
|
switch ( (bptr[0] << 8) | bptr[1] ) { |
1351
|
|
|
|
|
|
|
case 0xfeff: |
1352
|
|
|
|
|
|
|
DEBUG_TRACE(" UTF-16 BOM is big-endian\n"); |
1353
|
0
|
|
|
|
|
|
byteorder = UTF16_BYTEORDER_BE; |
1354
|
0
|
|
|
|
|
|
buffer_consume(id3->buf, 2); |
1355
|
0
|
|
|
|
|
|
read += 2; |
1356
|
0
|
|
|
|
|
|
break; |
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
case 0xfffe: |
1359
|
|
|
|
|
|
|
DEBUG_TRACE(" UTF-16 BOM is little-endian\n"); |
1360
|
116
|
|
|
|
|
|
byteorder = UTF16_BYTEORDER_LE; |
1361
|
116
|
|
|
|
|
|
buffer_consume(id3->buf, 2); |
1362
|
116
|
|
|
|
|
|
read += 2; |
1363
|
116
|
|
|
|
|
|
break; |
1364
|
|
|
|
|
|
|
} |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
/* Bug 14728 |
1367
|
|
|
|
|
|
|
If there is no BOM, assume LE, this is what appears in the wild -andy |
1368
|
|
|
|
|
|
|
*/ |
1369
|
138
|
100
|
|
|
|
|
if (byteorder == UTF16_BYTEORDER_ANY) { |
1370
|
|
|
|
|
|
|
DEBUG_TRACE(" UTF-16 byte order defaulting to little-endian, no BOM\n"); |
1371
|
9
|
|
|
|
|
|
byteorder = UTF16_BYTEORDER_LE; |
1372
|
|
|
|
|
|
|
} |
1373
|
|
|
|
|
|
|
|
1374
|
138
|
|
|
|
|
|
read += buffer_get_utf16_as_utf8(id3->buf, id3->utf8, len - read, byteorder); |
1375
|
138
|
|
|
|
|
|
break; |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
case UTF_8: |
1378
|
320
|
|
|
|
|
|
read += buffer_get_utf8(id3->buf, id3->utf8, len); |
1379
|
320
|
|
|
|
|
|
break; |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
default: |
1382
|
0
|
|
|
|
|
|
break; |
1383
|
|
|
|
|
|
|
} |
1384
|
|
|
|
|
|
|
|
1385
|
1255
|
50
|
|
|
|
|
if (read) { |
1386
|
1255
|
100
|
|
|
|
|
if ( buffer_len(id3->utf8) ) { |
1387
|
1251
|
|
|
|
|
|
*string = newSVpv( buffer_ptr(id3->utf8), 0 ); |
1388
|
1251
|
|
|
|
|
|
sv_utf8_decode(*string); |
1389
|
|
|
|
|
|
|
DEBUG_TRACE(" read utf8 string of %d bytes: %s\n", buffer_len(id3->utf8), SvPVX(*string)); |
1390
|
|
|
|
|
|
|
} |
1391
|
|
|
|
|
|
|
else { |
1392
|
|
|
|
|
|
|
DEBUG_TRACE(" empty string\n"); |
1393
|
|
|
|
|
|
|
} |
1394
|
|
|
|
|
|
|
} |
1395
|
|
|
|
|
|
|
|
1396
|
1255
|
|
|
|
|
|
return read; |
1397
|
|
|
|
|
|
|
} |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
uint32_t |
1400
|
3
|
|
|
|
|
|
_id3_parse_rvad(id3info *id3, char const *id, uint32_t size) |
1401
|
|
|
|
|
|
|
{ |
1402
|
3
|
|
|
|
|
|
unsigned char *rva = buffer_ptr(id3->buf); |
1403
|
3
|
50
|
|
|
|
|
int sign_r = rva[0] & 0x01 ? 1 : -1; |
1404
|
3
|
50
|
|
|
|
|
int sign_l = rva[0] & 0x02 ? 1 : -1; |
1405
|
3
|
|
|
|
|
|
int bytes = rva[1] / 8; |
1406
|
|
|
|
|
|
|
float vol[2]; |
1407
|
|
|
|
|
|
|
float peak[2]; |
1408
|
|
|
|
|
|
|
int i; |
1409
|
3
|
|
|
|
|
|
AV *framedata = newAV(); |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
// Sanity check, first byte must be either 0 or 1, second byte > 0 |
1412
|
3
|
100
|
|
|
|
|
if (rva[0] & 0xFE || rva[1] == 0) { |
|
|
50
|
|
|
|
|
|
1413
|
1
|
|
|
|
|
|
return 0; |
1414
|
|
|
|
|
|
|
} |
1415
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
// Calculated size must match the actual size |
1417
|
2
|
50
|
|
|
|
|
if (size != 2 + (bytes * 4)) { |
1418
|
0
|
|
|
|
|
|
return 0; |
1419
|
|
|
|
|
|
|
} |
1420
|
|
|
|
|
|
|
|
1421
|
2
|
|
|
|
|
|
rva += 2; |
1422
|
|
|
|
|
|
|
|
1423
|
2
|
|
|
|
|
|
vol[0] = _varint( rva, bytes ) * sign_r / 256.; |
1424
|
2
|
|
|
|
|
|
vol[1] = _varint( rva + bytes, bytes ) * sign_l / 256.; |
1425
|
|
|
|
|
|
|
|
1426
|
2
|
|
|
|
|
|
peak[0] = _varint( rva + (bytes * 2), bytes ); |
1427
|
2
|
|
|
|
|
|
peak[1] = _varint( rva + (bytes * 3), bytes ); |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
// iTunes uses a range of -255 to 255 |
1430
|
|
|
|
|
|
|
// to be -100% (silent) to 100% (+6dB) |
1431
|
6
|
100
|
|
|
|
|
for (i = 0; i < 2; i++) { |
1432
|
4
|
50
|
|
|
|
|
if ( vol[i] == -255 ) { |
1433
|
0
|
|
|
|
|
|
vol[i] = -96.0; |
1434
|
|
|
|
|
|
|
} |
1435
|
|
|
|
|
|
|
else { |
1436
|
4
|
|
|
|
|
|
vol[i] = 20.0 * log( ( vol[i] + 255 ) / 255 ) / log(10); |
1437
|
|
|
|
|
|
|
} |
1438
|
|
|
|
|
|
|
|
1439
|
4
|
|
|
|
|
|
av_push( framedata, newSVpvf( "%f dB", vol[i] ) ); |
1440
|
4
|
|
|
|
|
|
av_push( framedata, newSVpvf( "%f", peak[i] ) ); |
1441
|
|
|
|
|
|
|
} |
1442
|
|
|
|
|
|
|
|
1443
|
2
|
|
|
|
|
|
my_hv_store( id3->tags, id, newRV_noinc( (SV *)framedata ) ); |
1444
|
|
|
|
|
|
|
|
1445
|
2
|
|
|
|
|
|
buffer_consume(id3->buf, 2 + (bytes * 4)); |
1446
|
|
|
|
|
|
|
|
1447
|
3
|
|
|
|
|
|
return 2 + (bytes * 4); |
1448
|
|
|
|
|
|
|
} |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
uint32_t |
1451
|
1
|
|
|
|
|
|
_id3_parse_rgad(id3info *id3) |
1452
|
|
|
|
|
|
|
{ |
1453
|
1
|
|
|
|
|
|
float radio = 0.0; |
1454
|
1
|
|
|
|
|
|
float audiophile = 0.0; |
1455
|
1
|
|
|
|
|
|
uint8_t sign = 0; |
1456
|
1
|
|
|
|
|
|
HV *framedata = newHV(); |
1457
|
1
|
|
|
|
|
|
uint32_t read = 0; |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
// Peak (32-bit float) |
1460
|
1
|
|
|
|
|
|
my_hv_store( framedata, "peak", newSVpvf( "%f", (float)buffer_get_float32(id3->buf) ) ); |
1461
|
1
|
|
|
|
|
|
read += 4; |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
// Radio (16 bits) |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
// Radio Name code (3 bits, should always be 1) |
1466
|
1
|
|
|
|
|
|
buffer_get_bits(id3->buf, 3); |
1467
|
|
|
|
|
|
|
|
1468
|
1
|
|
|
|
|
|
my_hv_store( framedata, "track_originator", newSVuv( buffer_get_bits(id3->buf, 3) ) ); |
1469
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
// Sign bit (1 bit) |
1471
|
1
|
|
|
|
|
|
sign = buffer_get_bits(id3->buf, 1); |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
// Gain value (9 bits) |
1474
|
1
|
|
|
|
|
|
radio = (float)buffer_get_bits(id3->buf, 9); |
1475
|
1
|
|
|
|
|
|
radio /= 10.0; |
1476
|
1
|
50
|
|
|
|
|
if (sign == 1) radio *= -1.0; |
1477
|
1
|
|
|
|
|
|
my_hv_store( framedata, "track_gain", newSVpvf( "%f dB", radio ) ); |
1478
|
|
|
|
|
|
|
|
1479
|
1
|
|
|
|
|
|
read += 2; |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
// Audiophile (16 bits) |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
// Audiophile Name code (3 bits, should always be 2) |
1484
|
1
|
|
|
|
|
|
buffer_get_bits(id3->buf, 3); |
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
// Audiophile Originator code (3 bits) |
1487
|
1
|
|
|
|
|
|
my_hv_store( framedata, "album_originator", newSVuv( buffer_get_bits(id3->buf, 3) ) ); |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
// Sign bit (1 bit) |
1490
|
1
|
|
|
|
|
|
sign = buffer_get_bits(id3->buf, 1); |
1491
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
// Gain value (9 bits) |
1493
|
1
|
|
|
|
|
|
audiophile = (float)buffer_get_bits(id3->buf, 9); |
1494
|
1
|
|
|
|
|
|
audiophile /= 10.0; |
1495
|
1
|
50
|
|
|
|
|
if (sign == 1) audiophile *= -1.0; |
1496
|
1
|
|
|
|
|
|
my_hv_store( framedata, "album_gain", newSVpvf( "%f dB", audiophile ) ); |
1497
|
|
|
|
|
|
|
|
1498
|
1
|
|
|
|
|
|
read += 2; |
1499
|
|
|
|
|
|
|
|
1500
|
1
|
|
|
|
|
|
my_hv_store( id3->tags, "RGAD", newRV_noinc( (SV *)framedata ) ); |
1501
|
|
|
|
|
|
|
|
1502
|
1
|
|
|
|
|
|
return read; |
1503
|
|
|
|
|
|
|
} |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
uint32_t |
1506
|
10
|
|
|
|
|
|
_id3_parse_rva2(id3info *id3, uint32_t len, AV *framedata) |
1507
|
|
|
|
|
|
|
{ |
1508
|
10
|
|
|
|
|
|
float adj = 0.0; |
1509
|
|
|
|
|
|
|
int adj_fp; |
1510
|
|
|
|
|
|
|
uint8_t peakbits; |
1511
|
10
|
|
|
|
|
|
float peak = 0.0; |
1512
|
10
|
|
|
|
|
|
uint32_t read = 0; |
1513
|
|
|
|
|
|
|
unsigned char *bptr; |
1514
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
// Channel |
1516
|
10
|
|
|
|
|
|
av_push( framedata, newSViv( buffer_get_char(id3->buf) ) ); |
1517
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
// Adjustment |
1519
|
10
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
1520
|
10
|
|
|
|
|
|
adj_fp = *(signed char *)(bptr) << 8; |
1521
|
10
|
|
|
|
|
|
adj_fp |= *(unsigned char *)(bptr+1); |
1522
|
10
|
|
|
|
|
|
adj = adj_fp / 512.0; |
1523
|
10
|
|
|
|
|
|
av_push( framedata, newSVpvf( "%f dB", adj ) ); |
1524
|
10
|
|
|
|
|
|
buffer_consume(id3->buf, 2); |
1525
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
// Peak |
1527
|
|
|
|
|
|
|
// Based on code from mp3gain |
1528
|
10
|
|
|
|
|
|
peakbits = buffer_get_char(id3->buf); |
1529
|
|
|
|
|
|
|
|
1530
|
10
|
|
|
|
|
|
read += 4; |
1531
|
|
|
|
|
|
|
|
1532
|
10
|
50
|
|
|
|
|
if (4 + (peakbits + 7) / 8 <= len) { |
1533
|
|
|
|
|
|
|
DEBUG_TRACE(" peakbits: %d\n", peakbits); |
1534
|
10
|
100
|
|
|
|
|
if (peakbits > 0) { |
1535
|
2
|
|
|
|
|
|
peak += (float)buffer_get_char(id3->buf); |
1536
|
2
|
|
|
|
|
|
read++; |
1537
|
|
|
|
|
|
|
} |
1538
|
10
|
100
|
|
|
|
|
if (peakbits > 8) { |
1539
|
2
|
|
|
|
|
|
peak += (float)buffer_get_char(id3->buf) / 256.0; |
1540
|
2
|
|
|
|
|
|
read++; |
1541
|
|
|
|
|
|
|
} |
1542
|
10
|
50
|
|
|
|
|
if (peakbits > 16) { |
1543
|
0
|
|
|
|
|
|
peak += (float)buffer_get_char(id3->buf) / 65536.0; |
1544
|
0
|
|
|
|
|
|
read++; |
1545
|
|
|
|
|
|
|
} |
1546
|
|
|
|
|
|
|
|
1547
|
10
|
100
|
|
|
|
|
if (peakbits > 0) |
1548
|
2
|
|
|
|
|
|
peak /= (float)(1 << ((peakbits - 1) & 7)); |
1549
|
|
|
|
|
|
|
} |
1550
|
|
|
|
|
|
|
|
1551
|
10
|
|
|
|
|
|
av_push( framedata, newSVpvf( "%f dB", peak ) ); |
1552
|
|
|
|
|
|
|
|
1553
|
10
|
|
|
|
|
|
return read; |
1554
|
|
|
|
|
|
|
} |
1555
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
uint32_t |
1557
|
1
|
|
|
|
|
|
_id3_parse_sylt(id3info *id3, uint8_t encoding, uint32_t len, AV *framedata) |
1558
|
|
|
|
|
|
|
{ |
1559
|
1
|
|
|
|
|
|
uint32_t read = 0; |
1560
|
1
|
|
|
|
|
|
AV *content = newAV(); |
1561
|
|
|
|
|
|
|
unsigned char *bptr; |
1562
|
|
|
|
|
|
|
|
1563
|
17
|
100
|
|
|
|
|
while (read < len) { |
1564
|
16
|
|
|
|
|
|
SV *value = NULL; |
1565
|
16
|
|
|
|
|
|
HV *lyric = newHV(); |
1566
|
|
|
|
|
|
|
|
1567
|
16
|
|
|
|
|
|
read += _id3_get_utf8_string(id3, &value, len - read, encoding); |
1568
|
16
|
50
|
|
|
|
|
if (value != NULL && SvPOK(value) && sv_len(value)) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1569
|
16
|
|
|
|
|
|
my_hv_store( lyric, "text", value ); |
1570
|
|
|
|
|
|
|
} |
1571
|
|
|
|
|
|
|
else { |
1572
|
0
|
|
|
|
|
|
my_hv_store( lyric, "text", &PL_sv_undef ); |
1573
|
0
|
0
|
|
|
|
|
if (value) SvREFCNT(value); |
1574
|
|
|
|
|
|
|
} |
1575
|
|
|
|
|
|
|
|
1576
|
16
|
|
|
|
|
|
my_hv_store( lyric, "timestamp", newSVuv( buffer_get_int(id3->buf) ) ); |
1577
|
16
|
|
|
|
|
|
read += 4; |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
// A $0A newline byte may follow, for some odd reason |
1580
|
16
|
|
|
|
|
|
bptr = buffer_ptr(id3->buf); |
1581
|
16
|
50
|
|
|
|
|
if ( len - read > 0 && bptr[0] == 0x0a ) { |
|
|
50
|
|
|
|
|
|
1582
|
16
|
|
|
|
|
|
buffer_consume(id3->buf, 1); |
1583
|
16
|
|
|
|
|
|
read++; |
1584
|
|
|
|
|
|
|
} |
1585
|
|
|
|
|
|
|
|
1586
|
16
|
|
|
|
|
|
av_push( content, newRV_noinc( (SV *)lyric ) ); |
1587
|
|
|
|
|
|
|
} |
1588
|
|
|
|
|
|
|
|
1589
|
1
|
|
|
|
|
|
av_push( framedata, newRV_noinc( (SV *)content ) ); |
1590
|
|
|
|
|
|
|
|
1591
|
1
|
|
|
|
|
|
return read; |
1592
|
|
|
|
|
|
|
} |
1593
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
uint32_t |
1595
|
1
|
|
|
|
|
|
_id3_parse_etco(id3info *id3, uint32_t len, AV *framedata) |
1596
|
|
|
|
|
|
|
{ |
1597
|
1
|
|
|
|
|
|
uint32_t read = 0; |
1598
|
1
|
|
|
|
|
|
AV *content = newAV(); |
1599
|
|
|
|
|
|
|
|
1600
|
2
|
100
|
|
|
|
|
while (read < len) { |
1601
|
1
|
|
|
|
|
|
HV *event = newHV(); |
1602
|
|
|
|
|
|
|
|
1603
|
1
|
|
|
|
|
|
my_hv_store( event, "type", newSVuv( buffer_get_char(id3->buf) ) ); |
1604
|
1
|
|
|
|
|
|
my_hv_store( event, "timestamp", newSVuv( buffer_get_int(id3->buf) ) ); |
1605
|
1
|
|
|
|
|
|
read += 5; |
1606
|
|
|
|
|
|
|
|
1607
|
1
|
|
|
|
|
|
av_push( content, newRV_noinc( (SV *)event ) ); |
1608
|
|
|
|
|
|
|
} |
1609
|
|
|
|
|
|
|
|
1610
|
1
|
|
|
|
|
|
av_push( framedata, newRV_noinc( (SV *)content ) ); |
1611
|
|
|
|
|
|
|
|
1612
|
1
|
|
|
|
|
|
return read; |
1613
|
|
|
|
|
|
|
} |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
void |
1616
|
50
|
|
|
|
|
|
_id3_convert_tdrc(id3info *id3) |
1617
|
|
|
|
|
|
|
{ |
1618
|
50
|
|
|
|
|
|
char timestamp[17] = { 0 }; |
1619
|
|
|
|
|
|
|
|
1620
|
50
|
100
|
|
|
|
|
if ( my_hv_exists(id3->tags, "TYER") ) { |
1621
|
34
|
|
|
|
|
|
SV *tyer = my_hv_delete(id3->tags, "TYER"); |
1622
|
34
|
50
|
|
|
|
|
if (SvPOK(tyer) && sv_len(tyer) == 4) { |
|
|
50
|
|
|
|
|
|
1623
|
34
|
|
|
|
|
|
char *ptr = SvPVX(tyer); |
1624
|
34
|
|
|
|
|
|
timestamp[0] = ptr[0]; |
1625
|
34
|
|
|
|
|
|
timestamp[1] = ptr[1]; |
1626
|
34
|
|
|
|
|
|
timestamp[2] = ptr[2]; |
1627
|
34
|
|
|
|
|
|
timestamp[3] = ptr[3]; |
1628
|
|
|
|
|
|
|
DEBUG_TRACE(" Converted TYER (%s) to TDRC (%s)\n", SvPVX(tyer), timestamp); |
1629
|
|
|
|
|
|
|
} |
1630
|
|
|
|
|
|
|
} |
1631
|
|
|
|
|
|
|
|
1632
|
50
|
100
|
|
|
|
|
if ( my_hv_exists(id3->tags, "TDAT") ) { |
1633
|
4
|
|
|
|
|
|
SV *tdat = my_hv_delete(id3->tags, "TDAT"); |
1634
|
4
|
50
|
|
|
|
|
if (SvPOK(tdat) && sv_len(tdat) == 4) { |
|
|
50
|
|
|
|
|
|
1635
|
4
|
|
|
|
|
|
char *ptr = SvPVX(tdat); |
1636
|
4
|
|
|
|
|
|
timestamp[4] = '-'; |
1637
|
4
|
|
|
|
|
|
timestamp[5] = ptr[2]; |
1638
|
4
|
|
|
|
|
|
timestamp[6] = ptr[3]; |
1639
|
4
|
|
|
|
|
|
timestamp[7] = '-'; |
1640
|
4
|
|
|
|
|
|
timestamp[8] = ptr[0]; |
1641
|
4
|
|
|
|
|
|
timestamp[9] = ptr[1]; |
1642
|
|
|
|
|
|
|
DEBUG_TRACE(" Converted TDAT (%s) to TDRC (%s)\n", SvPVX(tdat), timestamp); |
1643
|
|
|
|
|
|
|
} |
1644
|
|
|
|
|
|
|
} |
1645
|
|
|
|
|
|
|
|
1646
|
50
|
100
|
|
|
|
|
if ( my_hv_exists(id3->tags, "TIME") ) { |
1647
|
3
|
|
|
|
|
|
SV *time = my_hv_delete(id3->tags, "TIME"); |
1648
|
3
|
50
|
|
|
|
|
if (SvPOK(time) && sv_len(time) == 4) { |
|
|
50
|
|
|
|
|
|
1649
|
3
|
|
|
|
|
|
char *ptr = SvPVX(time); |
1650
|
3
|
|
|
|
|
|
timestamp[10] = 'T'; |
1651
|
3
|
|
|
|
|
|
timestamp[11] = ptr[0]; |
1652
|
3
|
|
|
|
|
|
timestamp[12] = ptr[1]; |
1653
|
3
|
|
|
|
|
|
timestamp[13] = ':'; |
1654
|
3
|
|
|
|
|
|
timestamp[14] = ptr[2]; |
1655
|
3
|
|
|
|
|
|
timestamp[15] = ptr[3]; |
1656
|
|
|
|
|
|
|
DEBUG_TRACE(" Converted TIME (%s) to TDRC (%s)\n", SvPVX(time), timestamp); |
1657
|
|
|
|
|
|
|
} |
1658
|
|
|
|
|
|
|
} |
1659
|
|
|
|
|
|
|
|
1660
|
50
|
100
|
|
|
|
|
if (timestamp[0]) { |
1661
|
34
|
|
|
|
|
|
my_hv_store( id3->tags, "TDRC", newSVpv(timestamp, 0) ); |
1662
|
|
|
|
|
|
|
} |
1663
|
50
|
|
|
|
|
|
} |
1664
|
|
|
|
|
|
|
|
1665
|
|
|
|
|
|
|
// deunsync in-place, from libid3tag |
1666
|
|
|
|
|
|
|
uint32_t |
1667
|
15
|
|
|
|
|
|
_id3_deunsync(unsigned char *data, uint32_t length) |
1668
|
|
|
|
|
|
|
{ |
1669
|
|
|
|
|
|
|
unsigned char *old; |
1670
|
15
|
|
|
|
|
|
unsigned char *end = data + length; |
1671
|
|
|
|
|
|
|
unsigned char *new; |
1672
|
|
|
|
|
|
|
|
1673
|
15
|
50
|
|
|
|
|
if (length == 0) |
1674
|
0
|
|
|
|
|
|
return 0; |
1675
|
|
|
|
|
|
|
|
1676
|
140703
|
100
|
|
|
|
|
for (old = new = data; old < end - 1; ++old) { |
1677
|
140688
|
|
|
|
|
|
*new++ = *old; |
1678
|
140688
|
100
|
|
|
|
|
if (old[0] == 0xff && old[1] == 0x00) |
|
|
100
|
|
|
|
|
|
1679
|
1389
|
|
|
|
|
|
++old; |
1680
|
|
|
|
|
|
|
} |
1681
|
|
|
|
|
|
|
|
1682
|
15
|
|
|
|
|
|
*new++ = *old; |
1683
|
|
|
|
|
|
|
|
1684
|
15
|
|
|
|
|
|
return new - data; |
1685
|
|
|
|
|
|
|
} |
1686
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
void |
1688
|
8
|
|
|
|
|
|
_id3_skip(id3info *id3, uint32_t size) |
1689
|
|
|
|
|
|
|
{ |
1690
|
8
|
100
|
|
|
|
|
if ( buffer_len(id3->buf) >= size ) { |
1691
|
6
|
|
|
|
|
|
buffer_consume(id3->buf, size); |
1692
|
|
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
DEBUG_TRACE(" skipped buffer data size %d\n", size); |
1694
|
|
|
|
|
|
|
} |
1695
|
|
|
|
|
|
|
else { |
1696
|
2
|
|
|
|
|
|
PerlIO_seek(id3->infile, size - buffer_len(id3->buf), SEEK_CUR); |
1697
|
2
|
|
|
|
|
|
buffer_clear(id3->buf); |
1698
|
|
|
|
|
|
|
|
1699
|
|
|
|
|
|
|
DEBUG_TRACE(" seeked past %d bytes to %d\n", size, (int)PerlIO_tell(id3->infile)); |
1700
|
|
|
|
|
|
|
} |
1701
|
8
|
|
|
|
|
|
} |
1702
|
|
|
|
|
|
|
|
1703
|
|
|
|
|
|
|
// return an ID3v1 genre string indexed by number |
1704
|
|
|
|
|
|
|
char const * |
1705
|
17
|
|
|
|
|
|
_id3_genre_index(unsigned int index) |
1706
|
|
|
|
|
|
|
{ |
1707
|
17
|
50
|
|
|
|
|
return (index < NGENRES) ? genre_table[index] : 0; |
1708
|
|
|
|
|
|
|
} |
1709
|
|
|
|
|
|
|
|
1710
|
|
|
|
|
|
|
// translate an ID3v2 genre number/keyword to its full name |
1711
|
|
|
|
|
|
|
char const * |
1712
|
34
|
|
|
|
|
|
_id3_genre_name(char const *string) |
1713
|
|
|
|
|
|
|
{ |
1714
|
|
|
|
|
|
|
static char const genre_remix[] = { 'R', 'e', 'm', 'i', 'x', 0 }; |
1715
|
|
|
|
|
|
|
static char const genre_cover[] = { 'C', 'o', 'v', 'e', 'r', 0 }; |
1716
|
|
|
|
|
|
|
unsigned long number; |
1717
|
|
|
|
|
|
|
|
1718
|
34
|
50
|
|
|
|
|
if (string == 0 || *string == 0) |
|
|
50
|
|
|
|
|
|
1719
|
0
|
|
|
|
|
|
return 0; |
1720
|
|
|
|
|
|
|
|
1721
|
34
|
100
|
|
|
|
|
if (string[0] == 'R' && string[1] == 'X') |
|
|
50
|
|
|
|
|
|
1722
|
1
|
|
|
|
|
|
return genre_remix; |
1723
|
33
|
100
|
|
|
|
|
if (string[0] == 'C' && string[1] == 'R') |
|
|
50
|
|
|
|
|
|
1724
|
1
|
|
|
|
|
|
return genre_cover; |
1725
|
|
|
|
|
|
|
|
1726
|
32
|
|
|
|
|
|
number = strtol(string, NULL, 0); |
1727
|
|
|
|
|
|
|
|
1728
|
32
|
50
|
|
|
|
|
return (number < NGENRES) ? genre_table[number] : string; |
1729
|
|
|
|
|
|
|
} |