1 //==================================================================//
2 /*
3 AtomicParsley - id3v2.cpp
4
5 AtomicParsley is GPL software; you can freely distribute,
6 redistribute, modify & use under the terms of the GNU General
7 Public License; either version 2 or its successor.
8
9 AtomicParsley is distributed under the GPL "AS IS", without
10 any warranty; without the implied warranty of merchantability
11 or fitness for either an expressed or implied particular purpose.
12
13 Please see the included GNU General Public License (GPL) for
14 your rights and further details; see the file COPYING. If you
15 cannot, write to the Free Software Foundation, 59 Temple Place
16 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
17
18 Copyright (C)2006-2007 puck_lock
19 with contributions from others; see the CREDITS file
20 */
21 //==================================================================//
22
23 #include "AtomicParsley.h"
24 #include "CDtoc.h"
25 #include "id3v2defs.h"
26
27 ID3v2Tag *GlobalID3Tag = NULL;
28
29 // prefs
30 uint8_t AtomicParsley_ID3v2Tag_MajorVersion = 4;
31 uint8_t AtomicParsley_ID3v2Tag_RevisionVersion = 0;
32 uint8_t AtomicParsley_ID3v2Tag_Flags = 0;
33
34 bool ID3v2Tag_Flag_Footer =
35 false; // bit4; MPEG-4 'ID32' requires this to be false
36 bool ID3v2Tag_Flag_Experimental = true; // bit5
37 bool ID3v2Tag_Flag_ExtendedHeader = true; // bit6
38 bool ID3v2Tag_Flag_Unsyncronization = false; // bit7
39
40 ///////////////////////////////////////////////////////////////////////////////////////
41 // id3 number conversion functions //
42 ///////////////////////////////////////////////////////////////////////////////////////
43
syncsafeXX_to_UInt64(char * syncsafe_int,uint8_t syncsafe_len)44 uint64_t syncsafeXX_to_UInt64(char *syncsafe_int, uint8_t syncsafe_len) {
45 if (syncsafe_len == 5) {
46 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
47 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
48 syncsafe_int[4] & 0x80)
49 return 0;
50 return ((uint64_t)syncsafe_int[0] << 28) | (syncsafe_int[1] << 21) |
51 (syncsafe_int[2] << 14) | (syncsafe_int[3] << 7) | syncsafe_int[4];
52 } else if (syncsafe_len == 6) {
53 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
54 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
55 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80)
56 return 0;
57 return ((uint64_t)syncsafe_int[0] << 35) |
58 ((uint64_t)syncsafe_int[1] << 28) | (syncsafe_int[2] << 21) |
59 (syncsafe_int[3] << 14) | (syncsafe_int[4] << 7) | syncsafe_int[5];
60 } else if (syncsafe_len == 7) {
61 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
62 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
63 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 ||
64 syncsafe_int[6] & 0x80)
65 return 0;
66 return ((uint64_t)syncsafe_int[0] << 42) |
67 ((uint64_t)syncsafe_int[1] << 35) |
68 ((uint64_t)syncsafe_int[2] << 28) | (syncsafe_int[3] << 21) |
69 (syncsafe_int[3] << 14) | (syncsafe_int[5] << 7) | syncsafe_int[6];
70 } else if (syncsafe_len == 8) {
71 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
72 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
73 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 ||
74 syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80)
75 return 0;
76 return ((uint64_t)syncsafe_int[0] << 49) |
77 ((uint64_t)syncsafe_int[1] << 42) |
78 ((uint64_t)syncsafe_int[2] << 35) |
79 ((uint64_t)syncsafe_int[3] << 28) | (syncsafe_int[4] << 21) |
80 (syncsafe_int[5] << 14) | (syncsafe_int[6] << 7) | syncsafe_int[7];
81 } else if (syncsafe_len == 9) {
82 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
83 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
84 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 ||
85 syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80 ||
86 syncsafe_int[8] & 0x80)
87 return 0;
88 return ((uint64_t)syncsafe_int[0] << 56) |
89 ((uint64_t)syncsafe_int[1] << 49) |
90 ((uint64_t)syncsafe_int[2] << 42) |
91 ((uint64_t)syncsafe_int[3] << 35) |
92 ((uint64_t)syncsafe_int[4] << 28) | (syncsafe_int[5] << 21) |
93 (syncsafe_int[6] << 14) | (syncsafe_int[7] << 7) | syncsafe_int[8];
94 }
95 return 0;
96 }
97
syncsafe32_to_UInt32(char * syncsafe_int)98 uint32_t syncsafe32_to_UInt32(char *syncsafe_int) {
99 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
100 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80)
101 return 0;
102 return (syncsafe_int[0] << 21) | (syncsafe_int[1] << 14) |
103 (syncsafe_int[2] << 7) | syncsafe_int[3];
104 }
105
syncsafe16_to_UInt16(char * syncsafe_int)106 uint16_t syncsafe16_to_UInt16(char *syncsafe_int) {
107 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80)
108 return 0;
109 return (syncsafe_int[0] << 7) | syncsafe_int[1];
110 }
111
convert_to_syncsafe32(uint32_t in_uint,char * buffer)112 void convert_to_syncsafe32(uint32_t in_uint, char *buffer) {
113 buffer[0] = (in_uint >> 21) & 0x7F;
114 buffer[1] = (in_uint >> 14) & 0x7F;
115 buffer[2] = (in_uint >> 7) & 0x7F;
116 buffer[3] = (in_uint >> 0) & 0x7F;
117 return;
118 }
119
convert_to_syncsafeXX(uint64_t in_uint,char * buffer)120 uint8_t convert_to_syncsafeXX(uint64_t in_uint, char *buffer) {
121 if
122 #if defined(_MSC_VER)
123 (in_uint <= (uint64_t)34359738367)
124 #else
125 (in_uint <= 34359738367ULL)
126 #endif
127 {
128 buffer[0] = (in_uint >> 28) & 0x7F;
129 buffer[1] = (in_uint >> 21) & 0x7F;
130 buffer[2] = (in_uint >> 14) & 0x7F;
131 buffer[3] = (in_uint >> 7) & 0x7F;
132 buffer[4] = (in_uint >> 0) & 0x7F;
133 return 5;
134 #if defined(_MSC_VER)
135 } else if (in_uint <= (uint64_t)4398046511103) {
136 #else
137 } else if (in_uint <= 4398046511103ULL) {
138 #endif
139 buffer[0] = (in_uint >> 35) & 0x7F;
140 buffer[1] = (in_uint >> 28) & 0x7F;
141 buffer[2] = (in_uint >> 21) & 0x7F;
142 buffer[3] = (in_uint >> 14) & 0x7F;
143 buffer[4] = (in_uint >> 7) & 0x7F;
144 buffer[5] = (in_uint >> 0) & 0x7F;
145 return 6;
146 #if defined(_MSC_VER)
147 } else if (in_uint <= (uint64_t)562949953421311) {
148 #else
149 } else if (in_uint <= 562949953421311ULL) {
150 #endif
151 buffer[0] = (in_uint >> 42) & 0x7F;
152 buffer[1] = (in_uint >> 35) & 0x7F;
153 buffer[2] = (in_uint >> 28) & 0x7F;
154 buffer[3] = (in_uint >> 21) & 0x7F;
155 buffer[4] = (in_uint >> 14) & 0x7F;
156 buffer[5] = (in_uint >> 7) & 0x7F;
157 buffer[6] = (in_uint >> 0) & 0x7F;
158 return 7;
159 #if defined(_MSC_VER)
160 } else if (in_uint <= (uint64_t)72057594037927935) {
161 #else
162 } else if (in_uint <= 72057594037927935ULL) {
163 #endif
164 buffer[0] = (in_uint >> 49) & 0x7F;
165 buffer[1] = (in_uint >> 42) & 0x7F;
166 buffer[2] = (in_uint >> 35) & 0x7F;
167 buffer[3] = (in_uint >> 28) & 0x7F;
168 buffer[4] = (in_uint >> 21) & 0x7F;
169 buffer[5] = (in_uint >> 14) & 0x7F;
170 buffer[6] = (in_uint >> 7) & 0x7F;
171 buffer[7] = (in_uint >> 0) & 0x7F;
172 return 8;
173 #if defined(_MSC_VER)
174 } else if (in_uint <= (uint64_t)9223372036854775807) {
175 #else
176 } else if (in_uint <= 9223372036854775807ULL) { // that is some hardcore
177 // lovin'
178 #endif
179 buffer[0] = (in_uint >> 56) & 0x7F;
180 buffer[1] = (in_uint >> 49) & 0x7F;
181 buffer[2] = (in_uint >> 42) & 0x7F;
182 buffer[3] = (in_uint >> 35) & 0x7F;
183 buffer[4] = (in_uint >> 28) & 0x7F;
184 buffer[5] = (in_uint >> 21) & 0x7F;
185 buffer[6] = (in_uint >> 14) & 0x7F;
186 buffer[7] = (in_uint >> 7) & 0x7F;
187 buffer[8] = (in_uint >> 0) & 0x7F;
188 return 9;
189 }
190 return 0;
191 }
192
UInt24FromBigEndian(const char * string)193 uint32_t UInt24FromBigEndian(const char *string) { // v2.2 frame lengths
194 return ((0 << 24) | ((string[0] & 0xff) << 16) | ((string[1] & 0xff) << 8) |
195 (string[2] & 0xff) << 0);
196 }
197
ID3v2_desynchronize(char * buffer,uint32_t bufferlen)198 uint32_t ID3v2_desynchronize(char *buffer, uint32_t bufferlen) {
199 char *buf_ptr = buffer;
200 uint32_t desync_count = 0;
201
202 for (uint32_t i = 0; i < bufferlen; i++) {
203 if ((unsigned char)buffer[i] == 0xFF &&
204 (unsigned char)buffer[i + 1] == 0x00) {
205 buf_ptr[desync_count] = buffer[i];
206 i++;
207 } else {
208 buf_ptr[desync_count] = buffer[i];
209 }
210 desync_count++;
211 }
212 return desync_count;
213 }
214
215 ///////////////////////////////////////////////////////////////////////////////////////
216 // bit tests & generic functions //
217 ///////////////////////////////////////////////////////////////////////////////////////
218
ID3v2_PaddingTest(char * buffer)219 bool ID3v2_PaddingTest(char *buffer) {
220 if (buffer[0] & 0x00 || buffer[1] & 0x00 || buffer[2] & 0x00 ||
221 buffer[3] & 0x00)
222 return true;
223 return false;
224 }
225
ID3v2_TestFrameID_NonConformance(char * frameid)226 bool ID3v2_TestFrameID_NonConformance(char *frameid) {
227 for (uint8_t i = 0; i < 4; i++) {
228 if (!((frameid[i] >= '0' && frameid[i] <= '9') ||
229 (frameid[i] >= 'A' && frameid[i] <= 'Z'))) {
230 return true;
231 }
232 }
233 return false;
234 }
235
ID3v2_TestTagFlag(uint8_t TagFlag,uint8_t TagBit)236 bool ID3v2_TestTagFlag(uint8_t TagFlag, uint8_t TagBit) {
237 if (TagFlag & TagBit)
238 return true;
239 return false;
240 }
241
ID3v2_TestFrameFlag(uint16_t FrameFlag,uint16_t FrameBit)242 bool ID3v2_TestFrameFlag(uint16_t FrameFlag, uint16_t FrameBit) {
243 if (FrameFlag & FrameBit)
244 return true;
245 return false;
246 }
247
TextField_TestBOM(char * astring)248 uint8_t TextField_TestBOM(char *astring) {
249 if (((unsigned char *)astring)[0] == 0xFE &&
250 ((unsigned char *)astring)[1] == 0xFF)
251 return 13; // 13 looks like a B for BE
252 if (((unsigned char *)astring)[0] == 0xFF &&
253 ((unsigned char *)astring)[1] == 0xFE)
254 return 1; // 1 looks like a l for LE
255 return 0;
256 }
257
APar_LimitBufferRange(uint32_t max_allowed,uint32_t target_amount)258 void APar_LimitBufferRange(uint32_t max_allowed, uint32_t target_amount) {
259 if (target_amount > max_allowed) {
260 fprintf(
261 stderr,
262 "AtomicParsley error: insufficient memory to process ID3 tags (%" PRIu32
263 ">%" PRIu32 "). Exiting.\n",
264 target_amount,
265 max_allowed);
266 exit(target_amount - max_allowed);
267 }
268 return;
269 }
270
APar_ValidateNULLTermination8bit(ID3v2Fields * this_field)271 void APar_ValidateNULLTermination8bit(ID3v2Fields *this_field) {
272 if (this_field->field_string[0] == 0) {
273 this_field->field_length = 1;
274 } else if (this_field->field_string[this_field->field_length - 1] != 0) {
275 this_field->field_length += 1;
276 }
277 return;
278 }
279
APar_ValidateNULLTermination16bit(ID3v2Fields * this_field,uint8_t encoding)280 void APar_ValidateNULLTermination16bit(ID3v2Fields *this_field,
281 uint8_t encoding) {
282 if (this_field->field_string[0] == 0 && this_field->field_string[1] == 0) {
283 this_field->field_length = 2;
284 if (encoding == TE_UTF16LE_WITH_BOM) {
285 if (((uint8_t)(this_field->field_string[0]) != 0xFF &&
286 (uint8_t)(this_field->field_string[1]) != 0xFE) ||
287 ((uint8_t)(this_field->field_string[0]) != 0xFE &&
288 (uint8_t)(this_field->field_string[1]) != 0xFF)) {
289 memcpy(this_field->field_string, "\xFF\xFE", 2);
290 this_field->field_length = 4;
291 }
292 }
293 } else if (this_field->field_string[this_field->field_length - 2] != 0 &&
294 this_field->field_string[this_field->field_length - 1] != 0) {
295 this_field->field_length += 2;
296 }
297 return;
298 }
299
APar_EvalFrame_for_Field(int frametype,int fieldtype)300 bool APar_EvalFrame_for_Field(int frametype, int fieldtype) {
301 uint8_t frametype_idx = GetFrameCompositionDescription(frametype);
302
303 for (uint8_t fld_i = 0;
304 fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount;
305 fld_i++) {
306 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
307 fieldtype) {
308 return true;
309 }
310 }
311 return false;
312 }
313
314 uint8_t
TestCharInRange(uint8_t testchar,uint8_t lowerlimit,uint8_t upperlimit)315 TestCharInRange(uint8_t testchar, uint8_t lowerlimit, uint8_t upperlimit) {
316 if (testchar >= lowerlimit && testchar <= upperlimit) {
317 return 1;
318 }
319 return 0;
320 }
321
ImageListMembers()322 uint8_t ImageListMembers() {
323 return (uint8_t)(sizeof(ImageList) / sizeof(*ImageList));
324 }
325
326 ///////////////////////////////////////////////////////////////////////////////////////
327 // test functions //
328 ///////////////////////////////////////////////////////////////////////////////////////
329
WriteZlibData(char * buffer,uint32_t buff_len)330 void WriteZlibData(char *buffer, uint32_t buff_len) {
331 char *indy_atom_path = (char *)malloc(
332 sizeof(char) * MAXPATHLEN); // this malloc can escape memset because its
333 // only for in-house testing
334 strcat(indy_atom_path, "/Users/");
335 strcat(indy_atom_path, getenv("USER"));
336 strcat(indy_atom_path, "/Desktop/id3framedata.txt");
337
338 FILE *test_file = fopen(indy_atom_path, "wb");
339 if (test_file != NULL) {
340
341 fwrite(buffer, (size_t)buff_len, 1, test_file);
342 fclose(test_file);
343 }
344 free(indy_atom_path);
345 return;
346 }
347
348 ///////////////////////////////////////////////////////////////////////////////////////
349 // cli functions //
350 ///////////////////////////////////////////////////////////////////////////////////////
351
ReturnFrameTypeStr(int frametype)352 static const char *ReturnFrameTypeStr(int frametype) {
353 if (frametype == ID3_TEXT_FRAME) {
354 return "text frame ";
355 } else if (frametype == ID3_TEXT_FRAME_USERDEF) {
356 return "user defined text frame";
357 } else if (frametype == ID3_URL_FRAME) {
358 return "url frame ";
359 } else if (frametype == ID3_URL_FRAME_USERDEF) {
360 return "user defined url frame ";
361 } else if (frametype == ID3_UNIQUE_FILE_ID_FRAME) {
362 return "file ID ";
363 } else if (frametype == ID3_CD_ID_FRAME) {
364 return "AudioCD ID frame ";
365 } else if (frametype == ID3_DESCRIBED_TEXT_FRAME) {
366 return "described text frame ";
367 } else if (frametype == ID3_ATTACHED_PICTURE_FRAME) {
368 return "picture frame ";
369 } else if (frametype == ID3_ATTACHED_OBJECT_FRAME) {
370 return "encapuslated object frm";
371 } else if (frametype == ID3_GROUP_ID_FRAME) {
372 return "group ID frame ";
373 } else if (frametype == ID3_SIGNATURE_FRAME) {
374 return "signature frame ";
375 } else if (frametype == ID3_PRIVATE_FRAME) {
376 return "private frame ";
377 } else if (frametype == ID3_PLAYCOUNTER_FRAME) {
378 return "playcounter ";
379 } else if (frametype == ID3_POPULAR_FRAME) {
380 return "popularimeter ";
381 }
382 return "";
383 }
384
ListID3FrameIDstrings()385 void ListID3FrameIDstrings() {
386 const char *frametypestr = NULL;
387 const char *presetpadding = NULL;
388 uint16_t total_known_frames =
389 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
390 fprintf(stdout,
391 "ID3v2.4 Implemented Frames:\nframeID type "
392 " alias "
393 "Description\n-----------------------------------------------"
394 "---------------------------\n");
395 for (uint16_t i = 1; i < total_known_frames; i++) {
396 if (strlen(KnownFrames[i].ID3V2p4_FrameID) != 4)
397 continue;
398 frametypestr = ReturnFrameTypeStr(KnownFrames[i].ID3v2_FrameType);
399
400 int strpad = 12 - strlen(KnownFrames[i].CLI_frameIDpreset);
401 if (strpad == 12) {
402 presetpadding = " ";
403 } else if (strpad == 11) {
404 presetpadding = " ";
405 } else if (strpad == 10) {
406 presetpadding = " ";
407 } else if (strpad == 9) {
408 presetpadding = " ";
409 } else if (strpad == 8) {
410 presetpadding = " ";
411 } else if (strpad == 7) {
412 presetpadding = " ";
413 } else if (strpad == 6) {
414 presetpadding = " ";
415 } else if (strpad == 5) {
416 presetpadding = " ";
417 } else if (strpad == 4) {
418 presetpadding = " ";
419 } else if (strpad == 3) {
420 presetpadding = " ";
421 } else if (strpad == 2) {
422 presetpadding = " ";
423 } else if (strpad == 1) {
424 presetpadding = " ";
425 } else if (strpad <= 0) {
426 presetpadding = "";
427 }
428
429 fprintf(stdout,
430 "%s %s %s%s | %s\n",
431 KnownFrames[i].ID3V2p4_FrameID,
432 frametypestr,
433 KnownFrames[i].CLI_frameIDpreset,
434 presetpadding,
435 KnownFrames[i].ID3V2_FrameDescription);
436 }
437 fprintf(
438 stdout,
439 "------------------------------------------------------------------------"
440 "--\n"
441 "For each frame type, these parameters are available:\n"
442 " text frames: (str) [encoding]\n"
443 " user defined text frame : (str) [desc=(str)] [encoding]\n"
444 " url frame : (url)\n"
445 " user defined url frame : (url) [desc=(str)] [encoding]\n"
446 " file ID frame : (owner) "
447 "[uniqueID={\"randomUUIDstamp\",(str)}]\n"
448 #if defined(__APPLE__)
449 " AudioCD ID frame : disk(num)\n"
450 #elif defined(__linux__)
451 " AudioCD ID frame : (/path)\n"
452 #elif defined(_WIN32)
453 " AudioCD ID frame : (letter)\n"
454 #endif
455 " described text frame : (str) [desc=(str)] [encoding]\n"
456 " picture frame : (/path) [desc=(str)] [mimetype=(str)] "
457 "[imagetype=(hex)] [encoding]\n"
458 " encapuslated object frame : (/path) [desc=(str)] [mimetype=(str)] "
459 "[filename={\"FILENAMESTAMP\",(str)}] [encoding]\n"
460 " group ID frame : (owner) groupsymbol=(hex) [data=(str)]\n"
461 " signature frame : (str) groupsymbol=(hex)\n"
462 " private frame : (owner) data=(str)\n"
463 " playcounter : (num or \"+1\")\n"
464 " popularimeter : (owner) rating=(1...255) [counter=(num "
465 "or \"+1\")]\n"
466 "\n"
467 " Legend:\n"
468 " parameters in brackets[] signal an optional parameter, parens() "
469 "signal a required parameter\n"
470 " [encoding] may be one either the default UTF8, or one of { LATIN1 "
471 "UTF16BE UTF16LE }\n"
472 " (str) signals a string - like \"Suzie\"\n"
473 " (num) means a number; +1 will increment a counter by 1; (hex) "
474 "means a hexadecimal number - like 0x11)\n"
475 " (url) menas a url, in string form; (owner) means a url/email "
476 "string\n"
477 " uniqueID=randomUUIDstamp will create a high quality random uuid\n"
478 " filename=FILENAMESTAMP will embed the name of the file given in "
479 "the /path for GEOB\n"
480 "\n"
481 " All frames also take additional parameters:\n"
482 " [{root,track=(num)}] specifies file level, track level or "
483 "(default) movie level for an ID32 atom\n"
484 " [compress] compresses the given frame using zlib deflate "
485 "compression\n"
486 " [groupsymbol=(num)] associates a frame with a GRID frame of the "
487 "same group symbol\n"
488 " [lang=(3char)] (default='eng') sets the language/ID32 atom to "
489 "which the frame belongs\n"
490 " use AP --languages-list to see a list of available "
491 "languages\n");
492
493 return;
494 }
495
List_imagtype_strings()496 void List_imagtype_strings() {
497 uint8_t total_imgtyps =
498 (uint8_t)(sizeof(ImageTypeList) / sizeof(*ImageTypeList));
499 fprintf(stdout,
500 "These 'image types' are used to identify pictures embedded in "
501 "'APIC' ID3 tags:\n usage is \"AP --ID3Tag APIC /path.jpg "
502 "--imagetype=\"str\"\n str can be either the hex listing *or* "
503 "the full string\n default is 0x00 - meaning 'Other'\n Hex "
504 " Full String\n ----------------------------\n");
505 for (uint8_t i = 0; i < total_imgtyps; i++) {
506 fprintf(stdout,
507 " %s \"%s\"\n",
508 ImageTypeList[i].hexstring,
509 ImageTypeList[i].imagetype_str);
510 }
511 return;
512 }
513
ConvertCLIFrameStr_TO_frameID(const char * frame_str)514 const char *ConvertCLIFrameStr_TO_frameID(const char *frame_str) {
515 const char *discovered_frameID = NULL;
516 uint16_t total_known_frames =
517 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
518
519 for (uint16_t i = 0; i < total_known_frames; i++) {
520 if (strcmp(KnownFrames[i].CLI_frameIDpreset, frame_str) == 0) {
521 if (AtomicParsley_ID3v2Tag_MajorVersion == 2)
522 discovered_frameID = KnownFrames[i].ID3V2p2_FrameID;
523 if (AtomicParsley_ID3v2Tag_MajorVersion == 3)
524 discovered_frameID = KnownFrames[i].ID3V2p3_FrameID;
525 if (AtomicParsley_ID3v2Tag_MajorVersion == 4)
526 discovered_frameID = KnownFrames[i].ID3V2p4_FrameID;
527
528 if (strlen(discovered_frameID) == 0)
529 discovered_frameID = NULL;
530 break;
531 }
532 }
533 return discovered_frameID;
534 }
535
536 // 0 = description
537 // 1 = mimetype
538 // 2 = type
TestCLI_for_FrameParams(int frametype,uint8_t testparam)539 bool TestCLI_for_FrameParams(int frametype, uint8_t testparam) {
540 if (frametype == ID3_URL_FRAME_USERDEF && testparam == 0)
541 return true;
542
543 if (frametype == ID3_UNIQUE_FILE_ID_FRAME && testparam == 3) {
544 return true;
545 } else if (frametype == ID3_ATTACHED_OBJECT_FRAME && testparam == 4) {
546 return true;
547 } else if (frametype == ID3_POPULAR_FRAME && testparam == 5) {
548 return true;
549 } else if (frametype == ID3_POPULAR_FRAME && testparam == 6) {
550 return true;
551 } else if (frametype == ID3_GROUP_ID_FRAME && testparam == 7) {
552 return true;
553 } else if (frametype == ID3_PRIVATE_FRAME && testparam == 8) {
554 return true;
555 } else {
556 uint8_t frametype_idx = GetFrameCompositionDescription(frametype);
557
558 for (uint8_t fld_i = 0;
559 fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount;
560 fld_i++) {
561 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
562 ID3_DESCRIPTION_FIELD &&
563 testparam == 0) {
564 return true;
565 }
566 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
567 ID3_MIME_TYPE_FIELD &&
568 testparam == 1) {
569 return true;
570 }
571 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
572 ID3_PIC_TYPE_FIELD &&
573 testparam == 2) {
574 return true;
575 }
576 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
577 ID3_PIC_TYPE_FIELD &&
578 testparam == 3) {
579 return true;
580 }
581 }
582 }
583 return false;
584 }
585
586 ///////////////////////////////////////////////////////////////////////////////////////
587 // frame identity functions //
588 ///////////////////////////////////////////////////////////////////////////////////////
589
MatchID3FrameIDstr(const char * foundFrameID,uint8_t tagVersion)590 int MatchID3FrameIDstr(const char *foundFrameID, uint8_t tagVersion) {
591 uint16_t total_known_frames =
592 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
593 uint8_t frameLen = (tagVersion >= 3 ? 4 : 3) + 1;
594
595 for (int i = 0; i < total_known_frames; i++) {
596 const char *testFrameID = NULL;
597 if (tagVersion == 2)
598 testFrameID = KnownFrames[i].ID3V2p2_FrameID;
599 if (tagVersion == 3)
600 testFrameID = KnownFrames[i].ID3V2p3_FrameID;
601 if (tagVersion == 4)
602 testFrameID = KnownFrames[i].ID3V2p4_FrameID;
603
604 if (memcmp(foundFrameID, testFrameID, frameLen) == 0) {
605 return KnownFrames[i].ID3v2_InternalFrameID;
606 }
607 }
608 return ID3v2_UNKNOWN_FRAME; // return the UnknownFrame if it can't be found
609 }
610
GetFrameCompositionDescription(int ID3v2_FrameTypeID)611 uint8_t GetFrameCompositionDescription(int ID3v2_FrameTypeID) {
612 uint8_t matchingFrameDescription =
613 0; // return the UnknownFrame/UnknownField if it can't be found
614 uint8_t total_frame_descrips = (uint8_t)(sizeof(FrameTypeConstructionList) /
615 sizeof(*FrameTypeConstructionList));
616
617 for (uint8_t i = 0; i < total_frame_descrips; i++) {
618 if (FrameTypeConstructionList[i].ID3_FrameType == ID3v2_FrameTypeID) {
619 matchingFrameDescription = i;
620 break;
621 }
622 }
623 return matchingFrameDescription;
624 }
625
FrameStr_TO_FrameType(const char * frame_str)626 int FrameStr_TO_FrameType(const char *frame_str) {
627 const char *eval_framestr = NULL;
628 int frame_type = 0;
629 uint16_t total_known_frames =
630 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
631
632 for (uint16_t i = 0; i < total_known_frames; i++) {
633 if (AtomicParsley_ID3v2Tag_MajorVersion == 2)
634 eval_framestr = KnownFrames[i].ID3V2p2_FrameID;
635 if (AtomicParsley_ID3v2Tag_MajorVersion == 3)
636 eval_framestr = KnownFrames[i].ID3V2p3_FrameID;
637 if (AtomicParsley_ID3v2Tag_MajorVersion == 4)
638 eval_framestr = KnownFrames[i].ID3V2p4_FrameID;
639
640 if (strcmp(frame_str, eval_framestr) == 0) {
641 frame_type = KnownFrames[i].ID3v2_FrameType;
642 break;
643 }
644 }
645 return frame_type;
646 }
647
APar_FindLastTextField(ID3v2Frame * aFrame)648 ID3v2Fields *APar_FindLastTextField(ID3v2Frame *aFrame) {
649 ID3v2Fields *lastusedtextfield = NULL;
650 if (aFrame->textfield_tally > 0) {
651 lastusedtextfield = aFrame->ID3v2_Frame_Fields + 1;
652 while (true) {
653 if (lastusedtextfield->next_field == NULL) {
654 break;
655 }
656 lastusedtextfield = lastusedtextfield->next_field;
657 }
658 }
659 return lastusedtextfield;
660 }
661
APar_ExtraTextFieldInit(ID3v2Fields * lastField,uint32_t utf8len,uint8_t textencoding)662 bool APar_ExtraTextFieldInit(ID3v2Fields *lastField,
663 uint32_t utf8len,
664 uint8_t textencoding) {
665 ID3v2Fields *extraField = NULL;
666 lastField->next_field = (ID3v2Fields *)calloc(1, sizeof(ID3v2Fields) * 1);
667 if (lastField->next_field == NULL) {
668 fprintf(stdout,
669 "There was insufficient memory to allocate another ID3 field\n");
670 exit(12);
671 }
672 extraField = lastField->next_field;
673 extraField->ID3v2_Field_Type = ID3_TEXT_FIELD;
674 extraField->field_length = 0;
675
676 if (textencoding == TE_UTF16LE_WITH_BOM ||
677 textencoding == TE_UTF16BE_NO_BOM) {
678 extraField->alloc_length = 2 + (utf8len * 2);
679 } else {
680 extraField->alloc_length = utf8len + 1;
681 }
682 if (extraField->alloc_length > 0) {
683 extraField->field_string =
684 (char *)calloc(1, sizeof(char *) * extraField->alloc_length);
685 if (!APar_assert((extraField->field_string != NULL),
686 11,
687 "while setting an extra text field"))
688 exit(11);
689 return true;
690 }
691 return false;
692 }
693
694 ///////////////////////////////////////////////////////////////////////////////////////
695 // id3 parsing functions //
696 ///////////////////////////////////////////////////////////////////////////////////////
697
APar_ExtractField(char * buffer,uint32_t maxFieldLen,ID3v2Frame * thisFrame,ID3v2Fields * thisField,int fieldType,uint8_t textEncoding)698 uint32_t APar_ExtractField(char *buffer,
699 uint32_t maxFieldLen,
700 ID3v2Frame *thisFrame,
701 ID3v2Fields *thisField,
702 int fieldType,
703 uint8_t textEncoding) {
704 uint32_t bytes_used = 0;
705 thisField->next_field = NULL;
706 switch (fieldType) {
707 case ID3_UNKNOWN_FIELD: { // the difference between this unknown field & say a
708 // binary data field is the unknown field is always
709 // the first (and only) field
710 thisField->ID3v2_Field_Type = ID3_UNKNOWN_FIELD;
711 thisField->field_length = maxFieldLen;
712 thisField->field_string = (char *)calloc(
713 1, sizeof(char) * (maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16));
714 thisField->alloc_length =
715 sizeof(char) * (maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
716 memcpy(thisField->field_string, buffer, maxFieldLen);
717
718 bytes_used = maxFieldLen;
719 break;
720 }
721 case ID3_PIC_TYPE_FIELD:
722 case ID3_GROUPSYMBOL_FIELD:
723 case ID3_TEXT_ENCODING_FIELD: {
724 thisField->ID3v2_Field_Type = fieldType;
725 thisField->field_length = 1;
726 thisField->field_string = (char *)calloc(1, sizeof(char) * 16);
727 thisField->field_string[0] =
728 buffer[0]; // memcpy(thisField->field_string, buffer, 1);
729 thisField->alloc_length = sizeof(char) * 16;
730
731 bytes_used = 1;
732 break;
733 }
734 case ID3_LANGUAGE_FIELD: {
735 thisField->ID3v2_Field_Type = ID3_LANGUAGE_FIELD;
736 thisField->field_length = 3;
737 thisField->field_string = (char *)calloc(1, sizeof(char) * 16);
738 memcpy(thisField->field_string, buffer, 3);
739 thisField->alloc_length = sizeof(char) * 16;
740
741 bytes_used = 3;
742 break;
743 }
744 case ID3_TEXT_FIELD:
745 case ID3_URL_FIELD:
746 case ID3_COUNTER_FIELD:
747 case ID3_BINARY_DATA_FIELD: { // this class of fields may contains NULLs but
748 // is *NOT* NULL terminated in any form
749 thisField->ID3v2_Field_Type = fieldType;
750 thisField->field_length = maxFieldLen;
751 thisField->field_string = (char *)calloc(
752 1, sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
753 memcpy(thisField->field_string, buffer, maxFieldLen);
754 thisField->alloc_length =
755 (sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
756
757 if (fieldType == ID3_TEXT_FIELD) {
758 bytes_used = findstringNULLterm(buffer, textEncoding, maxFieldLen);
759 } else {
760 bytes_used = maxFieldLen;
761 }
762 break;
763 }
764 case ID3_MIME_TYPE_FIELD:
765 case ID3_OWNER_FIELD: { // difference between ID3_OWNER_FIELD &
766 // ID3_DESCRIPTION_FIELD field classes is the owner
767 // field is always 8859-1 encoded (single NULL term)
768 thisField->ID3v2_Field_Type = fieldType;
769 thisField->field_length = findstringNULLterm(buffer, 0, maxFieldLen);
770 thisField->field_string =
771 (char *)calloc(1,
772 sizeof(char) * thisField->field_length + 1 > 16
773 ? thisField->field_length + 1
774 : 16);
775 memcpy(thisField->field_string, buffer, thisField->field_length);
776 thisField->alloc_length =
777 (sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
778
779 bytes_used = thisField->field_length;
780 break;
781 }
782 case ID3_FILENAME_FIELD:
783 case ID3_DESCRIPTION_FIELD: {
784 thisField->ID3v2_Field_Type = fieldType;
785 thisField->field_length =
786 findstringNULLterm(buffer, textEncoding, maxFieldLen);
787 thisField->field_string =
788 (char *)calloc(1,
789 sizeof(char) * thisField->field_length + 1 > 16
790 ? thisField->field_length + 1
791 : 16);
792 memcpy(thisField->field_string, buffer, thisField->field_length);
793 thisField->alloc_length = (sizeof(char) * thisField->field_length + 1 > 16
794 ? thisField->field_length + 1
795 : 16);
796
797 bytes_used = thisField->field_length;
798 break;
799 }
800 }
801 // fprintf(stdout, "%" PRIu32 ", %s, %s\n", bytes_used, buffer,
802 // (thisFrame->ID3v2_Frame_Fields+fieldNum)->field_string);
803 return bytes_used;
804 }
805
APar_ScanID3Frame(ID3v2Frame * targetframe,char * frame_ptr,uint32_t frameLen)806 void APar_ScanID3Frame(ID3v2Frame *targetframe,
807 char *frame_ptr,
808 uint32_t frameLen) {
809 uint64_t offset_into_frame = 0;
810
811 switch (targetframe->ID3v2_FrameType) {
812 case ID3_UNKNOWN_FRAME: {
813 APar_ExtractField(frame_ptr,
814 frameLen,
815 targetframe,
816 targetframe->ID3v2_Frame_Fields,
817 ID3_UNKNOWN_FIELD,
818 0);
819 break;
820 }
821 case ID3_TEXT_FRAME: {
822 uint8_t textencoding = 0xFF;
823 offset_into_frame += APar_ExtractField(frame_ptr,
824 1,
825 targetframe,
826 targetframe->ID3v2_Frame_Fields,
827 ID3_TEXT_ENCODING_FIELD,
828 0);
829
830 offset_into_frame +=
831 APar_ExtractField(frame_ptr + 1,
832 frameLen - 1,
833 targetframe,
834 targetframe->ID3v2_Frame_Fields + 1,
835 ID3_TEXT_FIELD,
836 targetframe->ID3v2_Frame_Fields->field_string[0]);
837 targetframe->textfield_tally++;
838
839 if (offset_into_frame >= frameLen)
840 break;
841 textencoding = targetframe->ID3v2_Frame_Fields->field_string[0];
842
843 if (offset_into_frame < frameLen) {
844 while (true) {
845 if (offset_into_frame >= frameLen)
846 break;
847
848 // skip the required separator for multiple strings
849 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
850 offset_into_frame += 1;
851 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
852 offset_into_frame += 2;
853 }
854
855 // multiple id3v2.4 strings should be separated with a single NULL byte;
856 // some implementations might terminate the string AND use a NULL
857 // separator
858 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
859 if ((frame_ptr + offset_into_frame)[0] == 0)
860 offset_into_frame += 1;
861 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
862 if ((frame_ptr + offset_into_frame)[0] == 0 &&
863 (frame_ptr + offset_into_frame)[1] == 0)
864 offset_into_frame += 2;
865 }
866
867 // a 3rd NULL would not be good
868 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
869 if ((frame_ptr + offset_into_frame)[0] == 0)
870 break;
871 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
872 if ((frame_ptr + offset_into_frame)[0] == 0 &&
873 (frame_ptr + offset_into_frame)[1] == 0)
874 break;
875 }
876
877 ID3v2Fields *last_textfield = APar_FindLastTextField(targetframe);
878 if (APar_ExtraTextFieldInit(
879 last_textfield, frameLen - offset_into_frame, textencoding)) {
880 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
881 frameLen - offset_into_frame,
882 targetframe,
883 last_textfield->next_field,
884 ID3_TEXT_FIELD,
885 textencoding);
886 targetframe->textfield_tally++;
887 }
888 // copy the string to the new field
889 break;
890 }
891 }
892 break;
893 }
894 case ID3_URL_FRAME: {
895 APar_ExtractField(frame_ptr,
896 frameLen,
897 targetframe,
898 targetframe->ID3v2_Frame_Fields,
899 ID3_URL_FIELD,
900 0);
901 break;
902 }
903 case ID3_TEXT_FRAME_USERDEF:
904 case ID3_URL_FRAME_USERDEF: {
905 offset_into_frame += APar_ExtractField(frame_ptr,
906 1,
907 targetframe,
908 targetframe->ID3v2_Frame_Fields,
909 ID3_TEXT_ENCODING_FIELD,
910 0);
911
912 offset_into_frame +=
913 APar_ExtractField(frame_ptr + offset_into_frame,
914 frameLen - offset_into_frame,
915 targetframe,
916 targetframe->ID3v2_Frame_Fields + 1,
917 ID3_DESCRIPTION_FIELD,
918 targetframe->ID3v2_Frame_Fields->field_string[0]);
919
920 offset_into_frame +=
921 skipNULLterm(frame_ptr + offset_into_frame,
922 targetframe->ID3v2_Frame_Fields->field_string[0],
923 frameLen - offset_into_frame);
924
925 if (targetframe->ID3v2_FrameType == ID3_TEXT_FRAME_USERDEF) {
926 APar_ExtractField(frame_ptr + offset_into_frame,
927 frameLen - offset_into_frame,
928 targetframe,
929 targetframe->ID3v2_Frame_Fields + 2,
930 ID3_TEXT_FIELD,
931 targetframe->ID3v2_Frame_Fields->field_string[0]);
932 } else if (targetframe->ID3v2_FrameType == ID3_URL_FRAME_USERDEF) {
933 APar_ExtractField(frame_ptr + offset_into_frame,
934 frameLen - offset_into_frame,
935 targetframe,
936 targetframe->ID3v2_Frame_Fields + 2,
937 ID3_URL_FIELD,
938 targetframe->ID3v2_Frame_Fields->field_string[0]);
939 }
940 break;
941 }
942 case ID3_UNIQUE_FILE_ID_FRAME: {
943 offset_into_frame += APar_ExtractField(frame_ptr,
944 frameLen,
945 targetframe,
946 targetframe->ID3v2_Frame_Fields,
947 ID3_OWNER_FIELD,
948 0);
949 offset_into_frame++; // iso-8859-1 owner field is NULL terminated
950
951 APar_ExtractField(frame_ptr + offset_into_frame,
952 frameLen - offset_into_frame,
953 targetframe,
954 targetframe->ID3v2_Frame_Fields + 1,
955 ID3_BINARY_DATA_FIELD,
956 0);
957 break;
958 }
959 case ID3_CD_ID_FRAME: {
960 APar_ExtractField(frame_ptr,
961 frameLen,
962 targetframe,
963 targetframe->ID3v2_Frame_Fields,
964 ID3_BINARY_DATA_FIELD,
965 0);
966 break;
967 }
968 case ID3_DESCRIBED_TEXT_FRAME: {
969 offset_into_frame += APar_ExtractField(frame_ptr,
970 1,
971 targetframe,
972 targetframe->ID3v2_Frame_Fields,
973 ID3_TEXT_ENCODING_FIELD,
974 0);
975
976 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
977 3,
978 targetframe,
979 targetframe->ID3v2_Frame_Fields + 1,
980 ID3_LANGUAGE_FIELD,
981 0);
982
983 offset_into_frame +=
984 APar_ExtractField(frame_ptr + offset_into_frame,
985 frameLen - offset_into_frame,
986 targetframe,
987 targetframe->ID3v2_Frame_Fields + 2,
988 ID3_DESCRIPTION_FIELD,
989 targetframe->ID3v2_Frame_Fields->field_string[0]);
990
991 offset_into_frame +=
992 skipNULLterm(frame_ptr + offset_into_frame,
993 targetframe->ID3v2_Frame_Fields->field_string[0],
994 frameLen - offset_into_frame);
995
996 if (frameLen > offset_into_frame) {
997 APar_ExtractField(frame_ptr + offset_into_frame,
998 frameLen - offset_into_frame,
999 targetframe,
1000 targetframe->ID3v2_Frame_Fields + 3,
1001 ID3_TEXT_FIELD,
1002 targetframe->ID3v2_Frame_Fields->field_string[0]);
1003 }
1004 break;
1005 }
1006 case ID3_ATTACHED_OBJECT_FRAME:
1007 case ID3_ATTACHED_PICTURE_FRAME: {
1008 offset_into_frame += APar_ExtractField(frame_ptr,
1009 1,
1010 targetframe,
1011 targetframe->ID3v2_Frame_Fields,
1012 ID3_TEXT_ENCODING_FIELD,
1013 0);
1014
1015 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
1016 frameLen - 1,
1017 targetframe,
1018 targetframe->ID3v2_Frame_Fields + 1,
1019 ID3_MIME_TYPE_FIELD,
1020 0);
1021
1022 offset_into_frame += 1; // should only be 1 NULL
1023
1024 if (targetframe->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
1025 offset_into_frame +=
1026 APar_ExtractField(frame_ptr + offset_into_frame,
1027 1,
1028 targetframe,
1029 targetframe->ID3v2_Frame_Fields + 2,
1030 ID3_PIC_TYPE_FIELD,
1031 0);
1032 } else {
1033 offset_into_frame +=
1034 APar_ExtractField(frame_ptr + offset_into_frame,
1035 frameLen - offset_into_frame,
1036 targetframe,
1037 targetframe->ID3v2_Frame_Fields + 2,
1038 ID3_FILENAME_FIELD,
1039 0);
1040
1041 offset_into_frame +=
1042 skipNULLterm(frame_ptr + offset_into_frame,
1043 targetframe->ID3v2_Frame_Fields->field_string[0],
1044 frameLen - offset_into_frame);
1045 }
1046
1047 offset_into_frame +=
1048 APar_ExtractField(frame_ptr + offset_into_frame,
1049 frameLen - offset_into_frame,
1050 targetframe,
1051 targetframe->ID3v2_Frame_Fields + 3,
1052 ID3_DESCRIPTION_FIELD,
1053 targetframe->ID3v2_Frame_Fields->field_string[0]);
1054
1055 offset_into_frame +=
1056 skipNULLterm(frame_ptr + offset_into_frame,
1057 targetframe->ID3v2_Frame_Fields->field_string[0],
1058 frameLen - offset_into_frame);
1059
1060 if (frameLen > offset_into_frame) {
1061 offset_into_frame +=
1062 APar_ExtractField(frame_ptr + offset_into_frame,
1063 frameLen - offset_into_frame,
1064 targetframe,
1065 targetframe->ID3v2_Frame_Fields + 4,
1066 ID3_BINARY_DATA_FIELD,
1067 0);
1068 }
1069 break;
1070 }
1071 case ID3_PRIVATE_FRAME: { // the only difference between the 'priv' frame &
1072 // the 'ufid' frame is ufid is limited to 64 bytes
1073 offset_into_frame += APar_ExtractField(frame_ptr,
1074 frameLen,
1075 targetframe,
1076 targetframe->ID3v2_Frame_Fields,
1077 ID3_OWNER_FIELD,
1078 0);
1079 offset_into_frame++; // iso-8859-1 owner field is NULL terminated
1080
1081 APar_ExtractField(frame_ptr + offset_into_frame,
1082 frameLen - 1,
1083 targetframe,
1084 targetframe->ID3v2_Frame_Fields + 1,
1085 ID3_BINARY_DATA_FIELD,
1086 0);
1087 break;
1088 }
1089 case ID3_GROUP_ID_FRAME: {
1090 offset_into_frame += APar_ExtractField(frame_ptr,
1091 frameLen,
1092 targetframe,
1093 targetframe->ID3v2_Frame_Fields,
1094 ID3_OWNER_FIELD,
1095 0);
1096 offset_into_frame++; // iso-8859-1 owner field is NULL terminated
1097
1098 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
1099 1,
1100 targetframe,
1101 targetframe->ID3v2_Frame_Fields + 1,
1102 ID3_GROUPSYMBOL_FIELD,
1103 0);
1104
1105 if (frameLen > offset_into_frame) {
1106 APar_ExtractField(frame_ptr + offset_into_frame,
1107 frameLen - offset_into_frame,
1108 targetframe,
1109 targetframe->ID3v2_Frame_Fields + 2,
1110 ID3_BINARY_DATA_FIELD,
1111 0);
1112 }
1113 break;
1114 }
1115 case ID3_SIGNATURE_FRAME: {
1116 APar_ExtractField(frame_ptr,
1117 1,
1118 targetframe,
1119 targetframe->ID3v2_Frame_Fields,
1120 ID3_GROUPSYMBOL_FIELD,
1121 0);
1122
1123 APar_ExtractField(frame_ptr + 1,
1124 frameLen - 1,
1125 targetframe,
1126 targetframe->ID3v2_Frame_Fields + 1,
1127 ID3_BINARY_DATA_FIELD,
1128 0);
1129 break;
1130 }
1131 case ID3_PLAYCOUNTER_FRAME: {
1132 APar_ExtractField(frame_ptr,
1133 frameLen,
1134 targetframe,
1135 targetframe->ID3v2_Frame_Fields,
1136 ID3_COUNTER_FIELD,
1137 0);
1138 break;
1139 }
1140 case ID3_POPULAR_FRAME: {
1141 offset_into_frame +=
1142 APar_ExtractField(frame_ptr,
1143 frameLen,
1144 targetframe,
1145 targetframe->ID3v2_Frame_Fields,
1146 ID3_OWNER_FIELD,
1147 0); // surrogate for 'emai to user' field
1148 offset_into_frame++; // iso-8859-1 email address field is NULL terminated
1149
1150 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
1151 1,
1152 targetframe,
1153 targetframe->ID3v2_Frame_Fields + 1,
1154 ID3_BINARY_DATA_FIELD,
1155 0);
1156
1157 if (frameLen > offset_into_frame) {
1158 APar_ExtractField(frame_ptr,
1159 frameLen - offset_into_frame,
1160 targetframe,
1161 targetframe->ID3v2_Frame_Fields + 2,
1162 ID3_COUNTER_FIELD,
1163 0);
1164 }
1165 break;
1166 }
1167 case ID3_OLD_V2P2_PICTURE_FRAME: {
1168 break; // unimplemented
1169 }
1170 }
1171 return;
1172 }
1173
APar_ID32_ScanID3Tag(FILE * source_file,AtomicInfo * id32_atom)1174 void APar_ID32_ScanID3Tag(FILE *source_file, AtomicInfo *id32_atom) {
1175 char *id32_fulltag =
1176 (char *)calloc(1, sizeof(char) * id32_atom->AtomicLength);
1177 char *fulltag_ptr = id32_fulltag;
1178
1179 if (id32_atom->AtomicLength < 20)
1180 return;
1181 APar_readX(id32_fulltag,
1182 source_file,
1183 id32_atom->AtomicStart + 14,
1184 id32_atom->AtomicLength -
1185 14); //+10 = 4bytes ID32 atom length + 4bytes ID32 atom name +
1186 // 2 bytes packed lang
1187
1188 if (memcmp(id32_fulltag, "ID3", 3) != 0)
1189 return;
1190 fulltag_ptr += 3;
1191
1192 id32_atom->ID32_TagInfo = (ID3v2Tag *)calloc(1, sizeof(ID3v2Tag));
1193 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = *fulltag_ptr;
1194 fulltag_ptr++;
1195 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = *fulltag_ptr;
1196 fulltag_ptr++;
1197 id32_atom->ID32_TagInfo->ID3v2Tag_Flags = *fulltag_ptr;
1198 fulltag_ptr++;
1199
1200 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) {
1201 fprintf(stdout,
1202 "AtomicParsley warning: an ID32 atom was encountered using an "
1203 "unsupported ID3v2 tag version: %u. Skipping\n",
1204 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
1205 return;
1206 }
1207 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4 &&
1208 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion != 0) {
1209 fprintf(stdout,
1210 "AtomicParsley warning: an ID32 atom was encountered using an "
1211 "unsupported ID3v2.4 tag revision: %u. Skipping\n",
1212 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
1213 return;
1214 }
1215
1216 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1217 ID32_TAGFLAG_BIT0 + ID32_TAGFLAG_BIT1 +
1218 ID32_TAGFLAG_BIT2 + ID32_TAGFLAG_BIT3))
1219 return;
1220
1221 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1222 ID32_TAGFLAG_FOOTER)) {
1223 fprintf(stdout,
1224 "AtomicParsley error: an ID32 atom was encountered with a "
1225 "forbidden footer flag. Exiting.\n");
1226 free(id32_fulltag);
1227 id32_fulltag = NULL;
1228 return;
1229 }
1230
1231 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1232 ID32_TAGFLAG_EXPERIMENTAL)) {
1233 #if defined(DEBUG_V)
1234 fprintf(stdout,
1235 "AtomicParsley warning: an ID32 atom was encountered with "
1236 "an experimental flag set.\n");
1237 #endif
1238 }
1239
1240 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1241 id32_atom->ID32_TagInfo->ID3v2Tag_Length =
1242 syncsafe32_to_UInt32(fulltag_ptr);
1243 fulltag_ptr += 4;
1244 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
1245 id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt32FromBigEndian(
1246 fulltag_ptr); // TODO: when testing ends, this switches to syncsafe
1247 fulltag_ptr += 4;
1248 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1249 id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt24FromBigEndian(fulltag_ptr);
1250 fulltag_ptr += 3;
1251 }
1252
1253 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1254 ID32_TAGFLAG_UNSYNCRONIZATION)) {
1255 // uint32_t newtagsize = ID3v2_desynchronize(id32_fulltag,
1256 // id32_atom->ID32_TagInfo->ID3v2Tag_Length); fprintf(stdout, "New tag size
1257 // is %" PRIu32 "\n", newtagsize); WriteZlibData(id32_fulltag, newtagsize);
1258 // exit(0);
1259 fprintf(stdout,
1260 "AtomicParsley error: an ID3 tag with the unsynchronized "
1261 "flag set which is not supported. Skipping.\n");
1262 free(id32_fulltag);
1263 id32_fulltag = NULL;
1264 return;
1265 }
1266
1267 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1268 ID32_TAGFLAG_EXTENDEDHEADER)) {
1269 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1270 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length =
1271 syncsafe32_to_UInt32(fulltag_ptr);
1272 } else {
1273 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length =
1274 UInt32FromBigEndian(
1275 fulltag_ptr); // TODO: when testing ends, this switches to
1276 // syncsafe; 2.2 doesn't have it
1277 }
1278 fulltag_ptr += id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length;
1279 }
1280
1281 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL;
1282 id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL;
1283
1284 // loop through parsing frames
1285 while (fulltag_ptr < id32_fulltag + (id32_atom->AtomicLength - 14)) {
1286 uint32_t fullframesize = 0;
1287
1288 if (ID3v2_PaddingTest(fulltag_ptr))
1289 break;
1290 if (ID3v2_TestFrameID_NonConformance(fulltag_ptr))
1291 break;
1292
1293 ID3v2Frame *target_list_frameinfo =
1294 (ID3v2Frame *)calloc(1, sizeof(ID3v2Frame));
1295 target_list_frameinfo->ID3v2_NextFrame = NULL;
1296 target_list_frameinfo->ID3v2_Frame_ID = MatchID3FrameIDstr(
1297 fulltag_ptr, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
1298 target_list_frameinfo->ID3v2_FrameType =
1299 KnownFrames[target_list_frameinfo->ID3v2_Frame_ID + 1].ID3v2_FrameType;
1300
1301 uint8_t FrameCompositionList =
1302 GetFrameCompositionDescription(target_list_frameinfo->ID3v2_FrameType);
1303 target_list_frameinfo->ID3v2_FieldCount =
1304 FrameTypeConstructionList[FrameCompositionList].ID3_FieldCount;
1305 target_list_frameinfo->ID3v2_Frame_ExpandedLength = 0;
1306 target_list_frameinfo->textfield_tally = 0;
1307 target_list_frameinfo->eliminate_frame = false;
1308 uint8_t frame_offset = 0;
1309
1310 if (id32_atom->ID32_TagInfo->ID3v2_FrameList != NULL)
1311 id32_atom->ID32_TagInfo->ID3v2_FrameList->ID3v2_NextFrame =
1312 target_list_frameinfo;
1313
1314 // need to lookup how many components this Frame_ID is associated with. Do
1315 // this by using the corresponding KnownFrames.ID3v2_FrameType
1316 // ID3v2_FrameType describes the general form this frame takes (text, text
1317 // with description, attached object, attached picture) the general form is
1318 // composed of several fields; that number of fields needs to be malloced to
1319 // target_list_frameinfo->ID3v2_Frame_Fields and each
1320 // target_list_frameinfo->ID3v2_Frame_Fields+num->field_string needs to be
1321 // malloced and copied from id32_fulltag
1322
1323 memset(target_list_frameinfo->ID3v2_Frame_Namestr, 0, 5);
1324 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1325 memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 3);
1326 fulltag_ptr += 3;
1327 } else {
1328 memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 4);
1329 fulltag_ptr += 4;
1330 }
1331
1332 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1333 target_list_frameinfo->ID3v2_Frame_Length =
1334 syncsafe32_to_UInt32(fulltag_ptr);
1335 fulltag_ptr += 4;
1336 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
1337 target_list_frameinfo->ID3v2_Frame_Length = UInt32FromBigEndian(
1338 fulltag_ptr); // TODO: when testing ends, this switches to syncsafe
1339 fulltag_ptr += 4;
1340 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1341 target_list_frameinfo->ID3v2_Frame_Length =
1342 UInt24FromBigEndian(fulltag_ptr);
1343 fulltag_ptr += 3;
1344 }
1345
1346 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion >= 3) {
1347 target_list_frameinfo->ID3v2_Frame_Flags = UInt16FromBigEndian(
1348 fulltag_ptr); // v2.2 doesn't have frame level flags (but it does have
1349 // field level flags)
1350 fulltag_ptr += 2;
1351
1352 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags,
1353 ID32_FRAMEFLAG_UNSYNCED)) {
1354 // DE-UNSYNC frame
1355 fullframesize = target_list_frameinfo->ID3v2_Frame_Length;
1356 target_list_frameinfo->ID3v2_Frame_Length =
1357 ID3v2_desynchronize(fulltag_ptr + frame_offset,
1358 target_list_frameinfo->ID3v2_Frame_Length);
1359 target_list_frameinfo->ID3v2_Frame_Flags -= ID32_FRAMEFLAG_UNSYNCED;
1360 }
1361
1362 // info based on frame flags (order based on the order of flags defined by
1363 // the frame flags
1364 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags,
1365 ID32_FRAMEFLAG_GROUPING)) {
1366 #if defined(DEBUG_V)
1367 fprintf(stdout,
1368 "Frame %s has a grouping flag set\n",
1369 target_list_frameinfo->ID3v2_Frame_Namestr);
1370 #endif
1371 target_list_frameinfo->ID3v2_Frame_GroupingSymbol =
1372 *fulltag_ptr; // er, uh... wouldn't this also require
1373 // ID32_FRAMEFLAG_LENINDICATED to be set???
1374 frame_offset++;
1375 }
1376
1377 if (ID3v2_TestFrameFlag(
1378 target_list_frameinfo->ID3v2_Frame_Flags,
1379 ID32_FRAMEFLAG_COMPRESSED)) { // technically
1380 // ID32_FRAMEFLAG_LENINDICATED
1381 // should also be tested
1382 #if defined(DEBUG_V)
1383 fprintf(stdout,
1384 "Frame %s has a compressed flag set\n",
1385 target_list_frameinfo->ID3v2_Frame_Namestr);
1386 #endif
1387 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1388 target_list_frameinfo->ID3v2_Frame_ExpandedLength =
1389 syncsafe32_to_UInt32(fulltag_ptr + frame_offset);
1390 frame_offset += 4;
1391 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
1392 target_list_frameinfo
1393 ->ID3v2_Frame_ExpandedLength = UInt32FromBigEndian(
1394 fulltag_ptr +
1395 frame_offset); // TODO: when testing ends, switch this to syncsafe
1396 frame_offset += 4;
1397 }
1398 }
1399 }
1400
1401 target_list_frameinfo->ID3v2_Frame_Fields = (ID3v2Fields *)calloc(
1402 1, sizeof(ID3v2Fields) * target_list_frameinfo->ID3v2_FieldCount);
1403 char *expanded_frame = NULL;
1404 char *frame_ptr = NULL;
1405 uint32_t frameLen = 0;
1406
1407 if (target_list_frameinfo->ID3v2_Frame_ExpandedLength != 0) {
1408 #ifdef HAVE_ZLIB_H
1409 expanded_frame = (char *)calloc(
1410 1,
1411 sizeof(char) * target_list_frameinfo->ID3v2_Frame_ExpandedLength + 1);
1412 APar_zlib_inflate(fulltag_ptr + frame_offset,
1413 target_list_frameinfo->ID3v2_Frame_Length,
1414 expanded_frame,
1415 target_list_frameinfo->ID3v2_Frame_ExpandedLength);
1416
1417 WriteZlibData(expanded_frame,
1418 target_list_frameinfo->ID3v2_Frame_ExpandedLength);
1419
1420 frame_ptr = expanded_frame;
1421 frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength;
1422 #else
1423 target_list_frameinfo->ID3v2_FrameType = ID3_UNKNOWN_FRAME;
1424 frame_ptr = fulltag_ptr + frame_offset;
1425 frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength;
1426 #endif
1427 } else {
1428
1429 frame_ptr = fulltag_ptr + frame_offset;
1430 frameLen = target_list_frameinfo->ID3v2_Frame_Length;
1431 }
1432
1433 APar_ScanID3Frame(target_list_frameinfo, frame_ptr, frameLen);
1434
1435 if (expanded_frame != NULL) {
1436 free(expanded_frame);
1437 expanded_frame = NULL;
1438 }
1439
1440 if (target_list_frameinfo != NULL) {
1441 if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) {
1442 id32_atom->ID32_TagInfo->ID3v2_FirstFrame =
1443 target_list_frameinfo; // entrance to the linked list
1444 }
1445 id32_atom->ID32_TagInfo->ID3v2_FrameList =
1446 target_list_frameinfo; // this always points to the last frame that
1447 // had the scan completed
1448 }
1449
1450 if (fullframesize != 0) {
1451 fulltag_ptr += fullframesize;
1452 } else {
1453 fulltag_ptr += target_list_frameinfo->ID3v2_Frame_Length;
1454 }
1455 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags,
1456 ID32_FRAMEFLAG_GROUPING)) {
1457 fulltag_ptr++;
1458 }
1459 id32_atom->ID32_TagInfo->ID3v2_FrameCount++;
1460 }
1461
1462 id32_atom->ID32_TagInfo->modified_tag =
1463 false; // if a frame is altered/added/removed, change this to true and
1464 // render the tag & fill id32_atom-AtomicData with the tag
1465 return;
1466 }
1467
1468 ///////////////////////////////////////////////////////////////////////////////////////
1469 // id3 rendering functions //
1470 ///////////////////////////////////////////////////////////////////////////////////////
1471
APar_LocateFrameSymbol(AtomicInfo * id32_atom,ID3v2Frame * targetFrame,uint8_t groupsymbol)1472 bool APar_LocateFrameSymbol(AtomicInfo *id32_atom,
1473 ID3v2Frame *targetFrame,
1474 uint8_t groupsymbol) {
1475 ID3v2Frame *testFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1476 while (testFrame != NULL) {
1477 if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID &&
1478 testFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) {
1479 if (testFrame->ID3v2_Frame_GroupingSymbol == groupsymbol) {
1480 return true;
1481 }
1482 } else if (targetFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) {
1483 if (testFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID &&
1484 groupsymbol ==
1485 (uint8_t)(testFrame->ID3v2_Frame_Fields + 1)->field_string[0]) {
1486 return true;
1487 }
1488 }
1489 testFrame = testFrame->ID3v2_NextFrame;
1490 }
1491 return false;
1492 }
1493
APar_FrameFilter(AtomicInfo * id32_atom)1494 void APar_FrameFilter(AtomicInfo *id32_atom) {
1495 ID3v2Frame *MCDI_frame = NULL;
1496 ID3v2Frame *TRCK_frame = NULL;
1497 ID3v2Frame *thisFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1498 while (thisFrame != NULL) {
1499 if (!thisFrame->eliminate_frame) {
1500 if (thisFrame->ID3v2_FrameType == ID3_CD_ID_FRAME) {
1501 MCDI_frame = thisFrame;
1502 }
1503 if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_TRACKNUM) {
1504 TRCK_frame = thisFrame;
1505 }
1506 if (thisFrame->ID3v2_Frame_ID ==
1507 ID3v2_FRAME_GRID) { // find any frames containing this symbol; if none
1508 // are present this frame will be discarded
1509 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(
1510 id32_atom,
1511 thisFrame,
1512 (uint8_t)(thisFrame->ID3v2_Frame_Fields + 1)->field_string[0]);
1513 if (!thisFrame->eliminate_frame) {
1514 thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING;
1515 }
1516
1517 } else if (thisFrame->ID3v2_Frame_ID ==
1518 ID3v2_FRAME_SIGNATURE) { // find a GRID frame that contains
1519 // this symbol (@ field_string, not
1520 // ID3v2_Frame_GroupingSymbol)
1521 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(
1522 id32_atom,
1523 thisFrame,
1524 (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0]);
1525 // since the group symbol is carried as a field for SIGN, no need to set
1526 // the frame's grouping bit in the frame flags
1527
1528 } else if (thisFrame->ID3v2_Frame_GroupingSymbol >
1529 0) { // find a GRID frame that contains this symbol, otherwise
1530 // discard it
1531 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(
1532 id32_atom, thisFrame, thisFrame->ID3v2_Frame_GroupingSymbol);
1533 if (!thisFrame->eliminate_frame) {
1534 thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING;
1535 }
1536 }
1537 }
1538 thisFrame = thisFrame->ID3v2_NextFrame;
1539 }
1540
1541 if (MCDI_frame != NULL && TRCK_frame == NULL) {
1542 fprintf(
1543 stderr,
1544 "AP warning: the MCDI frame was skipped due to a missing TRCK frame\n");
1545 MCDI_frame->eliminate_frame = true;
1546 }
1547 return;
1548 }
1549
APar_GetTagSize(AtomicInfo * id32_atom)1550 uint32_t APar_GetTagSize(
1551 AtomicInfo
1552 *id32_atom) { // a rough approximation of how much to malloc; this will
1553 // be larger than will be ultimately required
1554 uint32_t tag_len = 0;
1555 uint16_t surviving_frame_count = 0;
1556 if (id32_atom->ID32_TagInfo->modified_tag == false)
1557 return tag_len;
1558 if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0)
1559 return tag_len; // but a frame isn't removed by AP; its just marked for
1560 // elimination
1561 if (id32_atom->ID32_TagInfo->ID3v2_FrameList == NULL)
1562 return tag_len; // something went wrong somewhere if this wasn't an entry to
1563 // a linked list of frames
1564 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4)
1565 return tag_len; // only id3 version 2.4 tags are written
1566
1567 ID3v2Frame *eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1568 while (eval_frame != NULL) {
1569 if (eval_frame->eliminate_frame == true) {
1570 eval_frame = eval_frame->ID3v2_NextFrame;
1571 continue;
1572 }
1573 tag_len += 15; // 4bytes frameID 'TCON', 4bytes frame length (syncsafe int),
1574 // 2 bytes frame flags; optional group symbol: 1byte +
1575 // decompressed length 4bytes
1576 tag_len +=
1577 2 * eval_frame->ID3v2_FieldCount; // excess amount to ensure that text
1578 // fields have utf16 BOMs & 2 byte
1579 // NULL terminations as required
1580 if (ID3v2_TestFrameFlag(eval_frame->ID3v2_Frame_Flags,
1581 ID32_FRAMEFLAG_COMPRESSED)) {
1582 tag_len += eval_frame->ID3v2_Frame_ExpandedLength;
1583 } else {
1584 tag_len += eval_frame->ID3v2_Frame_Length;
1585 }
1586 surviving_frame_count++;
1587 eval_frame = eval_frame->ID3v2_NextFrame;
1588 if (surviving_frame_count == 0 && eval_frame == NULL) {
1589 }
1590 }
1591 if (surviving_frame_count == 0)
1592 return 0; // the 'ID3' header alone isn't going to be written with 0
1593 // existing frames
1594 return tag_len;
1595 }
1596
APar_RenderFields(char * dest_buffer,uint32_t max_alloc,ID3v2Tag * id3_tag,ID3v2Frame * id3v2_frame,uint32_t * frame_header_len,uint32_t * frame_length)1597 void APar_RenderFields(char *dest_buffer,
1598 uint32_t max_alloc,
1599 ID3v2Tag *id3_tag,
1600 ID3v2Frame *id3v2_frame,
1601 uint32_t *frame_header_len,
1602 uint32_t *frame_length) {
1603 uint8_t encoding_val = 0;
1604 if (id3v2_frame->ID3v2_Frame_Fields == NULL) {
1605 *frame_header_len = 0;
1606 *frame_length = 0;
1607 return;
1608 }
1609
1610 for (uint8_t fld_idx = 0; fld_idx < id3v2_frame->ID3v2_FieldCount;
1611 fld_idx++) {
1612 ID3v2Fields *this_field = id3v2_frame->ID3v2_Frame_Fields + fld_idx;
1613 // fprintf(stdout, "Total Fields for %s: %u (this is %u, %u)\n",
1614 // id3v2_frame->ID3v2_Frame_Namestr, id3v2_frame->ID3v2_FieldCount, fld_idx,
1615 // this_field->ID3v2_Field_Type);
1616 switch (this_field->ID3v2_Field_Type) {
1617
1618 // these are raw data fields of variable/fixed length and are not NULL
1619 // terminated
1620 case ID3_UNKNOWN_FIELD:
1621 case ID3_PIC_TYPE_FIELD:
1622 case ID3_GROUPSYMBOL_FIELD:
1623 case ID3_TEXT_ENCODING_FIELD:
1624 case ID3_LANGUAGE_FIELD:
1625 case ID3_COUNTER_FIELD:
1626 case ID3_IMAGEFORMAT_FIELD:
1627 case ID3_URL_FIELD:
1628 case ID3_BINARY_DATA_FIELD: {
1629 APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length);
1630 if (this_field->field_string != NULL) {
1631 memcpy(dest_buffer + *frame_length,
1632 this_field->field_string,
1633 this_field->field_length);
1634 *frame_length += this_field->field_length;
1635 // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long (+%"
1636 // PRIu32 ")\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length,
1637 // this_field->field_length);
1638 }
1639 break;
1640 }
1641
1642 // these fields are subject to NULL byte termination - based on what the
1643 // text encoding field says the encoding of this string is
1644 case ID3_TEXT_FIELD:
1645 case ID3_FILENAME_FIELD:
1646 case ID3_DESCRIPTION_FIELD: {
1647 if (this_field->field_string == NULL) {
1648 *frame_header_len = 0;
1649 *frame_length = 0;
1650 return;
1651 } else {
1652 APar_LimitBufferRange(max_alloc,
1653 *frame_header_len + *frame_length +
1654 2); //+2 for a possible extra NULLs
1655 encoding_val =
1656 id3v2_frame->ID3v2_Frame_Fields
1657 ->field_string[0]; // ID3_TEXT_ENCODING_FIELD is always the
1658 // first field, and should have an encoding
1659 if ((id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF8) ||
1660 encoding_val == TE_LATIN1) {
1661 if (this_field->ID3v2_Field_Type != ID3_TEXT_FIELD)
1662 APar_ValidateNULLTermination8bit(this_field);
1663
1664 memcpy(dest_buffer + *frame_length,
1665 this_field->field_string,
1666 this_field->field_length);
1667 *frame_length += this_field->field_length;
1668
1669 } else if ((id3_tag->ID3v2Tag_MajorVersion == 4 &&
1670 encoding_val == TE_UTF16LE_WITH_BOM) ||
1671 encoding_val == TE_UTF16BE_NO_BOM) {
1672 APar_ValidateNULLTermination16bit(
1673 this_field, encoding_val); // TODO: shouldn't this also exclude
1674 // ID3_TEXT_FIELDs?
1675
1676 memcpy(dest_buffer + *frame_length,
1677 this_field->field_string,
1678 this_field->field_length);
1679 *frame_length += this_field->field_length;
1680
1681 } else { // well, AP didn't set this frame, so just duplicate it.
1682 memcpy(dest_buffer + *frame_length,
1683 this_field->field_string,
1684 this_field->field_length);
1685 *frame_length += this_field->field_length;
1686 }
1687 }
1688 // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n",
1689 // fld_idx, this_field->ID3v2_Field_Type, *frame_length);
1690 break;
1691 }
1692
1693 // these are iso 8859-1 encoded with a single NULL terminator
1694 // a 'LINK' url would also come here and be seperately enumerated (because
1695 // it has a terminating NULL); but in 3gp assets, external references aren't
1696 // allowed an 'OWNE'/'COMR' price field would also be here because of single
1697 // byte NULL termination
1698 case ID3_OWNER_FIELD:
1699 case ID3_MIME_TYPE_FIELD: {
1700 if (this_field->field_string == NULL) {
1701 *frame_header_len = 0;
1702 *frame_length = 0;
1703 return;
1704 } else {
1705 APar_LimitBufferRange(max_alloc,
1706 *frame_header_len + *frame_length +
1707 1); //+2 for a possible extra NULLs
1708
1709 APar_ValidateNULLTermination8bit(this_field);
1710 memcpy(dest_buffer + *frame_length,
1711 this_field->field_string,
1712 this_field->field_length);
1713 *frame_length += this_field->field_length;
1714 }
1715 // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n",
1716 // fld_idx, this_field->ID3v2_Field_Type, *frame_length);
1717 break;
1718 }
1719 default: {
1720 // fprintf(stdout, "I was unable to determine the field class. I was
1721 // provided with %u (i.e. text field: %u, text encoding: %u\n",
1722 // this_field->ID3v2_Field_Type, ID3_TEXT_FIELD, ID3_TEXT_ENCODING_FIELD);
1723 break;
1724 }
1725
1726 } // end switch
1727 }
1728 if (id3v2_frame->ID3v2_FrameType == ID3_TEXT_FRAME &&
1729 id3v2_frame->textfield_tally > 1 && id3_tag->ID3v2Tag_MajorVersion == 4) {
1730 ID3v2Fields *extra_textfield =
1731 (id3v2_frame->ID3v2_Frame_Fields + 1)->next_field;
1732 while (true) {
1733 if (extra_textfield == NULL)
1734 break;
1735
1736 if (encoding_val == TE_UTF8 || encoding_val == TE_LATIN1) {
1737 *frame_length += 1;
1738 } else if (encoding_val == TE_UTF16LE_WITH_BOM ||
1739 encoding_val == TE_UTF16BE_NO_BOM) {
1740 *frame_length += 2;
1741 }
1742
1743 memcpy(dest_buffer + *frame_length,
1744 extra_textfield->field_string,
1745 extra_textfield->field_length);
1746 *frame_length += extra_textfield->field_length;
1747
1748 extra_textfield = extra_textfield->next_field;
1749 }
1750 }
1751 return;
1752 }
1753
APar_Render_ID32_Tag(AtomicInfo * id32_atom,uint32_t max_alloc)1754 uint32_t APar_Render_ID32_Tag(AtomicInfo *id32_atom, uint32_t max_alloc) {
1755 bool contains_rendered_frames = false;
1756 APar_FrameFilter(id32_atom);
1757
1758 UInt16_TO_String2(
1759 id32_atom->AtomicLanguage,
1760 id32_atom->AtomicData); // parsedAtoms[atom_idx].AtomicLanguage
1761 uint64_t tag_offset = 2; // those first 2 bytes will hold the language
1762 uint32_t frame_length, frame_header_len; // the length in bytes this frame
1763 // consumes in AtomicData as rendered
1764 uint64_t frame_length_pos, frame_compressed_length_pos;
1765
1766 memcpy(id32_atom->AtomicData + tag_offset, "ID3", 3);
1767 tag_offset += 3;
1768
1769 id32_atom->AtomicData[tag_offset] =
1770 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion; // should be 4
1771 id32_atom->AtomicData[tag_offset + 1] =
1772 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion; // should be 0
1773 id32_atom->AtomicData[tag_offset + 2] =
1774 id32_atom->ID32_TagInfo->ID3v2Tag_Flags;
1775 tag_offset += 3;
1776
1777 // unknown full length; fill in later
1778 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 ||
1779 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1780 tag_offset += 4;
1781 if (ID3v2_TestTagFlag(
1782 id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1783 ID32_TAGFLAG_EXTENDEDHEADER)) { // currently unimplemented
1784 tag_offset += 10;
1785 }
1786 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1787 tag_offset += 3;
1788 }
1789
1790 id32_atom->ID32_TagInfo->ID3v2Tag_Length = tag_offset - 2;
1791
1792 ID3v2Frame *thisframe = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1793 while (thisframe != NULL) {
1794 frame_header_len = 0;
1795 frame_length_pos = 0;
1796 frame_compressed_length_pos = 0;
1797
1798 if (thisframe->eliminate_frame == true) {
1799 thisframe = thisframe->ID3v2_NextFrame;
1800 continue;
1801 }
1802
1803 contains_rendered_frames = true;
1804 // this won't be able to convert from 1 tag version to another because it
1805 // doesn't look up the frame id strings for the change
1806 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 ||
1807 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1808 memcpy(id32_atom->AtomicData + tag_offset,
1809 thisframe->ID3v2_Frame_Namestr,
1810 4);
1811 frame_header_len += 4;
1812
1813 // the frame length won't be determined until the end of rendering this
1814 // frame fully; for now just remember where its supposed to be:
1815 frame_length_pos = tag_offset + frame_header_len;
1816 frame_header_len += 4;
1817
1818 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1819 memcpy(id32_atom->AtomicData + tag_offset,
1820 thisframe->ID3v2_Frame_Namestr,
1821 3);
1822 frame_header_len += 3;
1823
1824 // the frame length won't be determined until the end of rendering this
1825 // frame fully; for now just remember where its supposed to be:
1826 frame_length_pos = tag_offset + frame_header_len;
1827 frame_header_len += 3;
1828 }
1829
1830 // render frame flags //TODO: compression & group symbol are the only ones
1831 // that can possibly be set here
1832 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 ||
1833 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1834 UInt16_TO_String2(thisframe->ID3v2_Frame_Flags,
1835 id32_atom->AtomicData + tag_offset + frame_header_len);
1836 frame_header_len += 2;
1837 }
1838
1839 // grouping flag? 1 byte; technically, its outside the header and before the
1840 // fields begin
1841 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags,
1842 ID32_FRAMEFLAG_GROUPING)) {
1843 id32_atom->AtomicData[tag_offset + frame_header_len] =
1844 thisframe->ID3v2_Frame_GroupingSymbol;
1845 frame_header_len++;
1846 }
1847
1848 // compression flag? 4bytes; technically, its outside the header and before
1849 // the fields begin
1850 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags,
1851 ID32_FRAMEFLAG_COMPRESSED)) {
1852 frame_compressed_length_pos =
1853 tag_offset + frame_header_len; // fill in later; remember where it is
1854 // supposed to go
1855 frame_header_len += 4;
1856 }
1857
1858 frame_length = 0;
1859 APar_RenderFields(id32_atom->AtomicData + tag_offset + frame_header_len,
1860 max_alloc - tag_offset,
1861 id32_atom->ID32_TagInfo,
1862 thisframe,
1863 &frame_header_len,
1864 &frame_length);
1865
1866 #if defined HAVE_ZLIB_H
1867 // and now that we have rendered the frame, its time to turn to compression,
1868 // if set
1869 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags,
1870 ID32_FRAMEFLAG_COMPRESSED)) {
1871 uint32_t compressed_len = 0;
1872 char *compress_buffer =
1873 (char *)calloc(1, sizeof(char) * frame_length + 20);
1874
1875 compressed_len = APar_zlib_deflate(id32_atom->AtomicData + tag_offset +
1876 frame_header_len,
1877 frame_length,
1878 compress_buffer,
1879 frame_length + 20);
1880
1881 if (compressed_len > 0) {
1882 memcpy(id32_atom->AtomicData + tag_offset + frame_header_len,
1883 compress_buffer,
1884 compressed_len + 1);
1885 convert_to_syncsafe32(
1886 frame_length, id32_atom->AtomicData + frame_compressed_length_pos);
1887 frame_length = compressed_len;
1888
1889 // WriteZlibData(id32_atom->AtomicData + tag_offset+frame_header_len,
1890 // compressed_len);
1891 }
1892 }
1893 #endif
1894
1895 convert_to_syncsafe32(frame_length,
1896 id32_atom->AtomicData + frame_length_pos);
1897 tag_offset += frame_header_len + frame_length; // advance
1898 id32_atom->ID32_TagInfo->ID3v2Tag_Length += frame_header_len + frame_length;
1899 thisframe = thisframe->ID3v2_NextFrame;
1900 }
1901 convert_to_syncsafe32(id32_atom->ID32_TagInfo->ID3v2Tag_Length - 10,
1902 id32_atom->AtomicData +
1903 8); //-10 for a v2.4 tag with no extended header
1904
1905 if (!contains_rendered_frames)
1906 id32_atom->ID32_TagInfo->ID3v2Tag_Length = 0;
1907
1908 return id32_atom->ID32_TagInfo->ID3v2Tag_Length;
1909 }
1910
1911 ///////////////////////////////////////////////////////////////////////////////////////
1912 // id3 initializing functions //
1913 ///////////////////////////////////////////////////////////////////////////////////////
1914
APar_FieldInit(ID3v2Frame * aFrame,uint8_t a_field,uint8_t frame_comp_list,const char * frame_payload)1915 void APar_FieldInit(ID3v2Frame *aFrame,
1916 uint8_t a_field,
1917 uint8_t frame_comp_list,
1918 const char *frame_payload) {
1919 uint32_t byte_allocation = 0;
1920 ID3v2Fields *this_field = NULL;
1921 int field_type =
1922 FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field];
1923
1924 switch (field_type) {
1925 // case ID3_UNKNOWN_FIELD will not be handled
1926
1927 // these are all 1 to less than 16 bytes.
1928 case ID3_GROUPSYMBOL_FIELD:
1929 case ID3_COUNTER_FIELD:
1930 case ID3_PIC_TYPE_FIELD:
1931 case ID3_LANGUAGE_FIELD:
1932 case ID3_IMAGEFORMAT_FIELD: // PIC in v2.2
1933 case ID3_TEXT_ENCODING_FIELD: {
1934 byte_allocation = 16;
1935 break;
1936 }
1937
1938 // between 16 & 100 bytes.
1939 case ID3_MIME_TYPE_FIELD: {
1940 byte_allocation = 100;
1941 break;
1942 }
1943
1944 // these are allocated with 2000 bytes
1945 case ID3_FILENAME_FIELD:
1946 case ID3_OWNER_FIELD:
1947 case ID3_DESCRIPTION_FIELD:
1948 case ID3_URL_FIELD:
1949 case ID3_TEXT_FIELD: {
1950 uint32_t string_len = strlen(frame_payload) + 1;
1951 if (string_len * 2 > 2000) {
1952 byte_allocation = string_len * 2;
1953 } else {
1954 byte_allocation = 2000;
1955 }
1956 break;
1957 }
1958
1959 case ID3_BINARY_DATA_FIELD: {
1960 if (aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_PICTURE ||
1961 aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_OBJECT) {
1962 // this will be left NULL because it would would probably have to be
1963 // realloced, so just do it later to the right size //byte_allocation =
1964 // findFileSize(frame_payload) + 1; //this should be limited to
1965 // max_sync_safe_uint28_t
1966 } else {
1967 byte_allocation = 2000;
1968 }
1969 break;
1970 }
1971
1972 // default : {
1973 // fprintf(stdout, "I am %d\n",
1974 // FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]);
1975 // break;
1976 //}
1977 }
1978 this_field = aFrame->ID3v2_Frame_Fields + a_field;
1979 this_field->ID3v2_Field_Type = field_type;
1980 if (byte_allocation > 0) {
1981 this_field->field_string =
1982 (char *)calloc(1, sizeof(char *) * byte_allocation);
1983 if (!APar_assert((this_field->field_string != NULL),
1984 11,
1985 aFrame->ID3v2_Frame_Namestr))
1986 exit(11);
1987 } else {
1988 this_field->field_string = NULL;
1989 }
1990 this_field->field_length = 0;
1991 this_field->alloc_length = byte_allocation;
1992 this_field->next_field = NULL;
1993 // fprintf(stdout, "For %u field, %" PRIu32 " bytes were allocated.\n",
1994 // this_field->ID3v2_Field_Type, byte_allocation);
1995 return;
1996 }
1997
APar_FrameInit(ID3v2Frame * aFrame,const char * frame_str,int frameID,uint8_t frame_comp_list,const char * frame_payload)1998 void APar_FrameInit(ID3v2Frame *aFrame,
1999 const char *frame_str,
2000 int frameID,
2001 uint8_t frame_comp_list,
2002 const char *frame_payload) {
2003 aFrame->ID3v2_FieldCount =
2004 FrameTypeConstructionList[frame_comp_list].ID3_FieldCount;
2005 if (aFrame->ID3v2_FieldCount > 0) {
2006 aFrame->ID3v2_Frame_Fields = (ID3v2Fields *)calloc(
2007 1, sizeof(ID3v2Fields) * aFrame->ID3v2_FieldCount);
2008 aFrame->ID3v2_Frame_ID = frameID;
2009 aFrame->ID3v2_FrameType =
2010 FrameTypeConstructionList[frame_comp_list].ID3_FrameType;
2011 aFrame->ID3v2_Frame_ExpandedLength = 0;
2012 aFrame->ID3v2_Frame_GroupingSymbol = 0;
2013 aFrame->ID3v2_Frame_Flags = 0;
2014 aFrame->ID3v2_Frame_Length = 0;
2015 aFrame->textfield_tally = 0;
2016 aFrame->eliminate_frame = false;
2017 memcpy(aFrame->ID3v2_Frame_Namestr, frame_str, 5);
2018
2019 for (uint8_t fld = 0; fld < aFrame->ID3v2_FieldCount; fld++) {
2020 APar_FieldInit(aFrame, fld, frame_comp_list, frame_payload);
2021 }
2022
2023 // fprintf(stdout, "(%u = %d) Type %d\n", frameID,
2024 // KnownFrames[frameID+1].ID3v2_InternalFrameID, aFrame->ID3v2_FrameType);
2025 }
2026 // fprintf(stdout, "Retrieved frame for '%s': %s (%u fields)\n", frame_str,
2027 // KnownFrames[frameID].ID3V2p4_FrameID, aFrame->ID3v2_FieldCount);
2028 return;
2029 }
2030
APar_ID3Tag_Init(AtomicInfo * id32_atom)2031 void APar_ID3Tag_Init(AtomicInfo *id32_atom) {
2032 id32_atom->ID32_TagInfo = (ID3v2Tag *)calloc(1, sizeof(ID3v2Tag));
2033 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion =
2034 AtomicParsley_ID3v2Tag_MajorVersion;
2035 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion =
2036 AtomicParsley_ID3v2Tag_RevisionVersion;
2037 id32_atom->ID32_TagInfo->ID3v2Tag_Flags = AtomicParsley_ID3v2Tag_Flags;
2038 id32_atom->ID32_TagInfo->ID3v2Tag_Length = 10; // this would be 9 for v2.2
2039 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = 0;
2040 id32_atom->ID32_TagInfo->ID3v2_FrameCount = 0;
2041 id32_atom->ID32_TagInfo->modified_tag =
2042 false; // this will have to change when a frame is added/modified/removed
2043 // because this id3 header won't be written with 0 frames
2044
2045 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL;
2046 id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL;
2047 return;
2048 }
2049
APar_realloc_memcpy(ID3v2Fields * thisField,uint32_t new_size)2050 void APar_realloc_memcpy(ID3v2Fields *thisField, uint32_t new_size) {
2051 if (new_size > thisField->alloc_length) {
2052 char *new_alloc = (char *)calloc(1, sizeof(char *) * new_size + 1);
2053 // memcpy(new_alloc, thisField->field_string, thisField->field_length);
2054 thisField->field_length = 0;
2055 free(thisField->field_string);
2056 thisField->field_string = new_alloc;
2057 thisField->alloc_length = new_size;
2058 }
2059 return;
2060 }
2061
2062 ///////////////////////////////////////////////////////////////////////////////////////
2063 // id3 frame setting/finding functions //
2064 ///////////////////////////////////////////////////////////////////////////////////////
2065
APar_TextFieldDataPut(ID3v2Fields * thisField,const char * this_payload,uint8_t str_encoding,bool multistringtext=false)2066 uint32_t APar_TextFieldDataPut(ID3v2Fields *thisField,
2067 const char *this_payload,
2068 uint8_t str_encoding,
2069 bool multistringtext = false) {
2070 uint32_t bytes_used = 0;
2071
2072 if (multistringtext == false) {
2073 thisField->field_length = 0;
2074 }
2075
2076 if (str_encoding == TE_UTF8) {
2077 bytes_used = strlen(
2078 this_payload); // no NULL termination is provided until render time
2079 if (bytes_used + thisField->field_length > thisField->alloc_length) {
2080 APar_realloc_memcpy(thisField, (bytes_used > 2000 ? bytes_used : 2000));
2081 }
2082 memcpy(thisField->field_string + thisField->field_length,
2083 this_payload,
2084 bytes_used);
2085 thisField->field_length += bytes_used;
2086
2087 } else if (str_encoding == TE_LATIN1) {
2088 int string_length = strlen(this_payload);
2089 if (string_length + thisField->field_length > thisField->alloc_length) {
2090 APar_realloc_memcpy(thisField,
2091 (string_length > 2000 ? string_length : 2000));
2092 }
2093 int converted_bytes = UTF8Toisolat1(
2094 (unsigned char *)thisField->field_string + thisField->field_length,
2095 (int)thisField->alloc_length,
2096 (unsigned char *)this_payload,
2097 string_length);
2098 if (converted_bytes > 0) {
2099 thisField->field_length += converted_bytes;
2100 bytes_used = converted_bytes;
2101 // fprintf(stdout, "string %s, %" PRIu32 "=%" PRIu32 "\n",
2102 // thisField->field_string, thisField->field_length, bytes_used);
2103 }
2104
2105 } else if (str_encoding == TE_UTF16BE_NO_BOM) {
2106 int string_length =
2107 (int)utf8_length(this_payload, strlen(this_payload)) + 1;
2108 if (2 * string_length + thisField->field_length > thisField->alloc_length) {
2109 APar_realloc_memcpy(thisField,
2110 (2 * string_length + thisField->field_length > 2000
2111 ? 2 * string_length + thisField->field_length
2112 : 2000));
2113 }
2114 int converted_bytes = UTF8ToUTF16BE(
2115 (unsigned char *)thisField->field_string + thisField->field_length,
2116 (int)thisField->alloc_length,
2117 (unsigned char *)this_payload,
2118 string_length);
2119 if (converted_bytes > 0) {
2120 thisField->field_length += converted_bytes;
2121 bytes_used = converted_bytes;
2122 }
2123
2124 } else if (str_encoding == TE_UTF16LE_WITH_BOM) {
2125 int string_length =
2126 (int)utf8_length(this_payload, strlen(this_payload)) + 1;
2127 uint64_t bom_offset = 0;
2128
2129 if (2 * string_length + thisField->field_length >
2130 thisField->alloc_length) { // important: realloc before BOM testing!!!
2131 APar_realloc_memcpy(thisField,
2132 (2 * string_length + thisField->field_length > 2000
2133 ? 2 * string_length + thisField->field_length
2134 : 2000));
2135 }
2136 if (thisField->field_length == 0 && multistringtext == false) {
2137 memcpy(thisField->field_string, "\xFF\xFE", 2);
2138 }
2139
2140 uint8_t field_encoding = TextField_TestBOM(thisField->field_string);
2141 if (field_encoding > 0) {
2142 bom_offset = 2;
2143 }
2144 int converted_bytes =
2145 UTF8ToUTF16LE((unsigned char *)thisField->field_string +
2146 thisField->field_length + bom_offset,
2147 (int)thisField->alloc_length,
2148 (unsigned char *)this_payload,
2149 string_length);
2150 if (converted_bytes > 0) {
2151 thisField->field_length += converted_bytes + bom_offset;
2152 bytes_used = converted_bytes;
2153 }
2154 }
2155
2156 if (multistringtext != false) {
2157 if (str_encoding == TE_UTF16LE_WITH_BOM ||
2158 str_encoding == TE_UTF16BE_NO_BOM) {
2159 bytes_used += 2;
2160 } else {
2161 bytes_used += 1;
2162 }
2163 }
2164 return bytes_used;
2165 }
2166
APar_BinaryFieldPut(ID3v2Fields * thisField,uint32_t a_number,const char * this_payload,uint32_t payload_len)2167 uint32_t APar_BinaryFieldPut(ID3v2Fields *thisField,
2168 uint32_t a_number,
2169 const char *this_payload,
2170 uint32_t payload_len) {
2171 if (thisField->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD ||
2172 thisField->ID3v2_Field_Type == ID3_PIC_TYPE_FIELD ||
2173 thisField->ID3v2_Field_Type == ID3_GROUPSYMBOL_FIELD) {
2174 thisField->field_string[0] = (unsigned char)a_number;
2175 thisField->field_length = 1;
2176 // fprintf(stdout, "My (TE/PT) content is 0x%02X\n",
2177 // thisField->field_string[0]);
2178 return 1;
2179
2180 } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD &&
2181 payload_len == 0) { // contents of a file
2182 uint64_t file_length = findFileSize(this_payload);
2183 thisField->field_string =
2184 (char *)calloc(1, sizeof(char *) * file_length + 16);
2185
2186 FILE *binfile = APar_OpenFile(this_payload, "rb");
2187 APar_ReadFile(thisField->field_string, binfile, file_length);
2188 fclose(binfile);
2189
2190 thisField->field_length = file_length;
2191 thisField->alloc_length = file_length + 16;
2192 thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
2193 return file_length;
2194
2195 } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD ||
2196 thisField->ID3v2_Field_Type == ID3_COUNTER_FIELD) {
2197 thisField->field_string =
2198 (char *)calloc(1, sizeof(char *) * payload_len + 16);
2199 memcpy(thisField->field_string, this_payload, payload_len);
2200
2201 thisField->field_length = payload_len;
2202 thisField->alloc_length = payload_len + 16;
2203 thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
2204 return payload_len;
2205 }
2206 return 0;
2207 }
2208
APar_FrameDataPut(ID3v2Frame * thisFrame,const char * frame_payload,AdjunctArgs * adjunct_payload,uint8_t str_encoding)2209 void APar_FrameDataPut(ID3v2Frame *thisFrame,
2210 const char *frame_payload,
2211 AdjunctArgs *adjunct_payload,
2212 uint8_t str_encoding) {
2213 if (adjunct_payload->multistringtext == false &&
2214 !APar_EvalFrame_for_Field(thisFrame->ID3v2_FrameType, ID3_COUNTER_FIELD))
2215 thisFrame->ID3v2_Frame_Length = 0;
2216 switch (thisFrame->ID3v2_FrameType) {
2217 case ID3_TEXT_FRAME: {
2218 if (adjunct_payload->multistringtext && thisFrame->textfield_tally >= 1) {
2219 ID3v2Fields *last_textfield = APar_FindLastTextField(thisFrame);
2220 if (APar_ExtraTextFieldInit(
2221 last_textfield,
2222 strlen(frame_payload),
2223 (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0])) {
2224 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2225 last_textfield->next_field,
2226 frame_payload,
2227 (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0],
2228 true);
2229 }
2230 } else {
2231 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2232 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2233 thisFrame->ID3v2_Frame_Length +=
2234 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2235 frame_payload,
2236 str_encoding,
2237 false); // text field
2238 GlobalID3Tag->ID3v2_FrameCount++;
2239 }
2240 modified_atoms = true;
2241 GlobalID3Tag->modified_tag = true;
2242 // GlobalID3Tag->ID3v2_FrameCount++; //don't do this for all text frames
2243 // because the multiple text field support of id3v2.4; only when the frame
2244 // is initially set
2245 thisFrame->textfield_tally++;
2246 break;
2247 }
2248 case ID3_TEXT_FRAME_USERDEF: {
2249 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2250 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2251 thisFrame->ID3v2_Frame_Length +=
2252 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2253 adjunct_payload->descripArg,
2254 str_encoding); // language
2255 thisFrame->ID3v2_Frame_Length +=
2256 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2257 frame_payload,
2258 str_encoding); // text field
2259 modified_atoms = true;
2260 GlobalID3Tag->modified_tag = true;
2261 GlobalID3Tag->ID3v2_FrameCount++;
2262 break;
2263 }
2264 case ID3_DESCRIBED_TEXT_FRAME: {
2265 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2266 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2267 thisFrame->ID3v2_Frame_Length +=
2268 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2269 adjunct_payload->targetLang,
2270 0); // language
2271 thisFrame->ID3v2_Frame_Length +=
2272 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2273 adjunct_payload->descripArg,
2274 str_encoding); // description
2275 thisFrame->ID3v2_Frame_Length +=
2276 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3,
2277 frame_payload,
2278 str_encoding,
2279 adjunct_payload->multistringtext); // text field
2280 modified_atoms = true;
2281 GlobalID3Tag->modified_tag = true;
2282 GlobalID3Tag->ID3v2_FrameCount++;
2283 break;
2284 }
2285 case ID3_URL_FRAME: {
2286 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2287 thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // url field
2288 modified_atoms = true;
2289 GlobalID3Tag->modified_tag = true;
2290 GlobalID3Tag->ID3v2_FrameCount++;
2291 break;
2292 }
2293 case ID3_URL_FRAME_USERDEF: {
2294 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2295 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2296 thisFrame->ID3v2_Frame_Length +=
2297 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2298 adjunct_payload->descripArg,
2299 str_encoding); // language
2300 thisFrame->ID3v2_Frame_Length +=
2301 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2302 frame_payload,
2303 TE_LATIN1); // url field
2304 modified_atoms = true;
2305 GlobalID3Tag->modified_tag = true;
2306 GlobalID3Tag->ID3v2_FrameCount++;
2307 break;
2308 }
2309 case ID3_UNIQUE_FILE_ID_FRAME: {
2310 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2311 thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // owner field
2312
2313 if (memcmp(adjunct_payload->dataArg, "randomUUIDstamp", 16) == 0) {
2314 char uuid_binary_str[25];
2315 memset(uuid_binary_str, 0, 25);
2316 APar_generate_random_uuid(uuid_binary_str);
2317 (thisFrame->ID3v2_Frame_Fields + 1)->field_string =
2318 (char *)calloc(1, sizeof(char *) * 40);
2319 APar_sprintf_uuid((ap_uuid_t *)uuid_binary_str,
2320 (thisFrame->ID3v2_Frame_Fields + 1)->field_string);
2321
2322 (thisFrame->ID3v2_Frame_Fields + 1)->field_length = 36;
2323 (thisFrame->ID3v2_Frame_Fields + 1)->alloc_length = 40;
2324 (thisFrame->ID3v2_Frame_Fields + 1)->ID3v2_Field_Type =
2325 ID3_BINARY_DATA_FIELD;
2326 thisFrame->ID3v2_Frame_Length += 36;
2327 } else {
2328 uint8_t uniqueIDlen = strlen(adjunct_payload->dataArg);
2329 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2330 thisFrame->ID3v2_Frame_Fields,
2331 0,
2332 adjunct_payload->dataArg,
2333 (uniqueIDlen > 64 ? 64 : uniqueIDlen)); // unique file ID
2334 }
2335
2336 modified_atoms = true;
2337 GlobalID3Tag->modified_tag = true;
2338 GlobalID3Tag->ID3v2_FrameCount++;
2339 break;
2340 }
2341 case ID3_CD_ID_FRAME: {
2342 thisFrame->ID3v2_Frame_Fields->field_length = GenerateMCDIfromCD(
2343 frame_payload, thisFrame->ID3v2_Frame_Fields->field_string);
2344 thisFrame->ID3v2_Frame_Length = thisFrame->ID3v2_Frame_Fields->field_length;
2345
2346 if (thisFrame->ID3v2_Frame_Length < 12) {
2347 free(thisFrame->ID3v2_Frame_Fields->field_string);
2348 thisFrame->ID3v2_Frame_Fields->field_string = NULL;
2349 thisFrame->ID3v2_Frame_Fields->alloc_length = 0;
2350 thisFrame->ID3v2_Frame_Length = 0;
2351 } else {
2352 modified_atoms = true;
2353 GlobalID3Tag->modified_tag = true;
2354 GlobalID3Tag->ID3v2_FrameCount++;
2355 }
2356 break;
2357 }
2358 case ID3_ATTACHED_PICTURE_FRAME: {
2359 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2360 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2361 thisFrame->ID3v2_Frame_Length +=
2362 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2363 adjunct_payload->mimeArg,
2364 TE_LATIN1); // mimetype
2365 thisFrame->ID3v2_Frame_Length +=
2366 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 2,
2367 adjunct_payload->pictype_uint8,
2368 NULL,
2369 1); // picturetype
2370 thisFrame->ID3v2_Frame_Length +=
2371 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3,
2372 adjunct_payload->descripArg,
2373 str_encoding); // description
2374 //(thisFrame->ID3v2_Frame_Fields+4)->ID3v2_Field_Type =
2375 // ID3_BINARY_DATA_FIELD; //because it wasn't malloced, this needs to be set
2376 // now
2377 thisFrame->ID3v2_Frame_Length +=
2378 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 4,
2379 0,
2380 frame_payload,
2381 0); // binary file (path)
2382 modified_atoms = true;
2383 GlobalID3Tag->modified_tag = true;
2384 GlobalID3Tag->ID3v2_FrameCount++;
2385 break;
2386 }
2387 case ID3_ATTACHED_OBJECT_FRAME: {
2388 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2389 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2390 thisFrame->ID3v2_Frame_Length +=
2391 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2392 adjunct_payload->mimeArg,
2393 TE_LATIN1); // mimetype
2394 if (memcmp(adjunct_payload->filenameArg, "FILENAMESTAMP", 13) == 0) {
2395 const char *derived_filename = NULL;
2396 #if defined(_WIN32)
2397 derived_filename = strrchr(frame_payload, '\\');
2398 #if defined(__CYGWIN__)
2399 const char *derived_filename2 = strrchr(frame_payload, '/');
2400 if (derived_filename2 > derived_filename) {
2401 derived_filename = derived_filename2;
2402 }
2403 #endif
2404 #else
2405 derived_filename = strrchr(frame_payload, '/');
2406 #endif
2407 if (derived_filename == NULL) {
2408 derived_filename = frame_payload;
2409 } else {
2410 derived_filename++; // get rid of the preceding slash
2411 }
2412 thisFrame->ID3v2_Frame_Length +=
2413 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2414 derived_filename,
2415 str_encoding); // filename
2416 } else {
2417 thisFrame->ID3v2_Frame_Length +=
2418 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2419 adjunct_payload->filenameArg,
2420 str_encoding); // filename
2421 }
2422 thisFrame->ID3v2_Frame_Length +=
2423 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3,
2424 adjunct_payload->descripArg,
2425 str_encoding); // description
2426 thisFrame->ID3v2_Frame_Length +=
2427 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 4,
2428 0,
2429 frame_payload,
2430 0); // binary file (path)
2431 modified_atoms = true;
2432 GlobalID3Tag->modified_tag = true;
2433 GlobalID3Tag->ID3v2_FrameCount++;
2434 break;
2435 }
2436 case ID3_GROUP_ID_FRAME: {
2437 uint32_t groupdatalen = strlen(adjunct_payload->dataArg);
2438 if (adjunct_payload->groupSymbol > 0) {
2439 thisFrame->ID3v2_Frame_Length +=
2440 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields,
2441 frame_payload,
2442 TE_LATIN1); // owner field
2443 thisFrame->ID3v2_Frame_Length +=
2444 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2445 adjunct_payload->groupSymbol,
2446 NULL,
2447 1); // group symbol
2448 if (groupdatalen > 0) { // not quite binary (unless it were entered as hex
2449 // & converted), but it will do
2450 thisFrame->ID3v2_Frame_Length +=
2451 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 2,
2452 0,
2453 adjunct_payload->dataArg,
2454 groupdatalen); // group symbol
2455 }
2456 modified_atoms = true;
2457 GlobalID3Tag->modified_tag = true;
2458 GlobalID3Tag->ID3v2_FrameCount++;
2459 }
2460 break;
2461 }
2462 case ID3_SIGNATURE_FRAME: {
2463 if (adjunct_payload->groupSymbol > 0) {
2464 thisFrame->ID3v2_Frame_Length +=
2465 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2466 adjunct_payload->groupSymbol,
2467 NULL,
2468 1); // group symbol
2469 thisFrame->ID3v2_Frame_Length +=
2470 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2471 0,
2472 frame_payload,
2473 strlen(frame_payload)); // signature
2474 modified_atoms = true;
2475 GlobalID3Tag->modified_tag = true;
2476 GlobalID3Tag->ID3v2_FrameCount++;
2477 }
2478 break;
2479 }
2480 case ID3_PRIVATE_FRAME: {
2481 uint32_t datalen =
2482 strlen(adjunct_payload->dataArg); // kinda precludes a true "binary"
2483 // sense, but whatever...
2484 if (datalen > 0) {
2485 thisFrame->ID3v2_Frame_Length +=
2486 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields,
2487 frame_payload,
2488 TE_LATIN1); // owner field
2489 thisFrame->ID3v2_Frame_Length +=
2490 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2491 0,
2492 adjunct_payload->dataArg,
2493 datalen); // data
2494 modified_atoms = true;
2495 GlobalID3Tag->modified_tag = true;
2496 GlobalID3Tag->ID3v2_FrameCount++;
2497 }
2498 break;
2499 }
2500 case ID3_PLAYCOUNTER_FRAME: {
2501 uint64_t playcount = 0;
2502 char play_count_syncsafe[16];
2503
2504 memset(play_count_syncsafe, 0, sizeof(play_count_syncsafe));
2505
2506 if (strcmp(frame_payload, "+1") == 0) {
2507 if (thisFrame->ID3v2_Frame_Length == 4) {
2508 playcount = (uint64_t)syncsafe32_to_UInt32(
2509 thisFrame->ID3v2_Frame_Fields->field_string) +
2510 1;
2511 } else if (thisFrame->ID3v2_Frame_Length > 4) {
2512 playcount =
2513 syncsafeXX_to_UInt64(thisFrame->ID3v2_Frame_Fields->field_string,
2514 thisFrame->ID3v2_Frame_Fields->field_length) +
2515 1;
2516 } else {
2517 playcount = 1;
2518 }
2519 } else {
2520 sscanf(frame_payload, "%" SCNu64, &playcount);
2521 }
2522
2523 if (playcount < 268435455) {
2524 convert_to_syncsafe32(playcount, play_count_syncsafe);
2525 thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(
2526 thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, 4);
2527 } else {
2528 uint8_t conversion_len =
2529 convert_to_syncsafeXX(playcount, play_count_syncsafe);
2530 thisFrame->ID3v2_Frame_Length =
2531 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2532 0,
2533 play_count_syncsafe,
2534 conversion_len);
2535 }
2536 modified_atoms = true;
2537 GlobalID3Tag->modified_tag = true;
2538 GlobalID3Tag->ID3v2_FrameCount++;
2539 break;
2540 }
2541 case ID3_POPULAR_FRAME: {
2542 unsigned char popm_rating = 0;
2543 uint64_t popm_playcount = 0;
2544 char popm_play_count_syncsafe[16];
2545
2546 memset(popm_play_count_syncsafe, 0, sizeof(popm_play_count_syncsafe));
2547
2548 if (adjunct_payload->ratingArg != NULL) {
2549 popm_rating = strtoul(adjunct_payload->ratingArg, NULL, 10);
2550 }
2551 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2552 thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // owner field
2553 thisFrame->ID3v2_Frame_Length +=
2554 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2555 0,
2556 (char *)&popm_rating,
2557 1); // rating
2558
2559 if (adjunct_payload->dataArg != NULL) {
2560 if (strlen(adjunct_payload->dataArg) > 0) {
2561 if (memcmp(adjunct_payload->dataArg, "+1", 3) == 0) {
2562 if ((thisFrame->ID3v2_Frame_Fields + 2)->field_length == 4) {
2563 popm_playcount =
2564 (uint64_t)syncsafe32_to_UInt32(
2565 (thisFrame->ID3v2_Frame_Fields + 2)->field_string) +
2566 1;
2567 } else if ((thisFrame->ID3v2_Frame_Fields + 2)->field_length > 4) {
2568 popm_playcount =
2569 syncsafeXX_to_UInt64(
2570 (thisFrame->ID3v2_Frame_Fields + 2)->field_string,
2571 (thisFrame->ID3v2_Frame_Fields + 2)->field_length) +
2572 1;
2573 } else {
2574 popm_playcount = 1;
2575 }
2576 } else {
2577 sscanf(adjunct_payload->dataArg, "%" SCNu64, &popm_playcount);
2578 }
2579 }
2580 }
2581 if (popm_playcount > 0) {
2582 if (popm_playcount < 268435455) {
2583 convert_to_syncsafe32(popm_playcount, popm_play_count_syncsafe);
2584 thisFrame->ID3v2_Frame_Length =
2585 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2586 0,
2587 popm_play_count_syncsafe,
2588 4); // syncsafe32 counter
2589 } else {
2590 uint8_t conversion_len =
2591 convert_to_syncsafeXX(popm_playcount, popm_play_count_syncsafe);
2592 thisFrame->ID3v2_Frame_Length =
2593 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2594 0,
2595 popm_play_count_syncsafe,
2596 conversion_len); // BIGsyncsafe counter
2597 }
2598 }
2599 modified_atoms = true;
2600 GlobalID3Tag->modified_tag = true;
2601 GlobalID3Tag->ID3v2_FrameCount++;
2602 break;
2603 }
2604 } // end switch
2605 return;
2606 }
2607
APar_EmbeddedFileTests(const char * filepath,int frameType,AdjunctArgs * adjunct_payloads)2608 void APar_EmbeddedFileTests(const char *filepath,
2609 int frameType,
2610 AdjunctArgs *adjunct_payloads) {
2611 if (frameType == ID3_ATTACHED_PICTURE_FRAME) {
2612
2613 // get cli imagetype
2614 uint8_t total_image_types =
2615 (uint8_t)(sizeof(ImageTypeList) / sizeof(*ImageTypeList));
2616 uint8_t img_typlen = strlen(adjunct_payloads->pictypeArg) + 1;
2617 const char *img_comparison_str = NULL;
2618
2619 for (uint8_t itest = 0; itest < total_image_types; itest++) {
2620 if (img_typlen == 5) {
2621 img_comparison_str = ImageTypeList[itest].hexstring;
2622 } else {
2623 img_comparison_str = ImageTypeList[itest].imagetype_str;
2624 }
2625 if (strcmp(adjunct_payloads->pictypeArg, img_comparison_str) == 0) {
2626 adjunct_payloads->pictype_uint8 = ImageTypeList[itest].hexcode;
2627 }
2628 }
2629
2630 if (strlen(filepath) > 0) {
2631 // see if file even exists
2632 TestFileExistence(filepath, true);
2633
2634 char *image_headerbytes = (char *)calloc(1, (sizeof(char) * 25));
2635 FILE *imagefile = APar_OpenFile(filepath, "rb");
2636 APar_ReadFile(image_headerbytes, imagefile, 24);
2637 fclose(imagefile);
2638 // test mimetype
2639 if (strlen(adjunct_payloads->mimeArg) == 0 ||
2640 memcmp(adjunct_payloads->mimeArg, "-->", 3) == 0) {
2641 uint8_t total_image_tests =
2642 (uint8_t)(sizeof(ImageList) / sizeof(*ImageList));
2643 for (uint8_t itest = 0; itest < total_image_tests; itest++) {
2644 if (ImageList[itest].image_testbytes == 0) {
2645 adjunct_payloads->mimeArg = ImageList[itest].image_mimetype;
2646 break;
2647 } else if (memcmp(image_headerbytes,
2648 ImageList[itest].image_binaryheader,
2649 ImageList[itest].image_testbytes) == 0) {
2650 adjunct_payloads->mimeArg = ImageList[itest].image_mimetype;
2651 if (adjunct_payloads->pictype_uint8 == 0x01) {
2652 if (memcmp(image_headerbytes + 16,
2653 "\x00\x00\x00\x20\x00\x00\x00\x20",
2654 8) != 0 &&
2655 itest != 2) {
2656 adjunct_payloads->pictype_uint8 = 0x02;
2657 }
2658 }
2659 break;
2660 }
2661 }
2662 }
2663 free(image_headerbytes);
2664 image_headerbytes = NULL;
2665 }
2666
2667 } else if (frameType == ID3_ATTACHED_OBJECT_FRAME) {
2668 if (strlen(filepath) > 0) {
2669 TestFileExistence(filepath, true);
2670 FILE *embedfile = APar_OpenFile(filepath, "rb");
2671 fclose(embedfile);
2672 }
2673 }
2674 return;
2675 }
2676
APar_ConvertField_to_UTF8(ID3v2Frame * targetframe,int fieldtype)2677 char *APar_ConvertField_to_UTF8(ID3v2Frame *targetframe, int fieldtype) {
2678 char *utf8str = NULL;
2679 uint8_t targetfield = 0xFF;
2680 uint8_t textencoding = 0;
2681
2682 for (uint8_t frm_field = 0; frm_field < targetframe->ID3v2_FieldCount;
2683 frm_field++) {
2684 if ((targetframe->ID3v2_Frame_Fields + frm_field)->ID3v2_Field_Type ==
2685 fieldtype) {
2686 targetfield = frm_field;
2687 break;
2688 }
2689 }
2690
2691 if (targetfield != 0xFF) {
2692 if (targetframe->ID3v2_Frame_Fields->ID3v2_Field_Type ==
2693 ID3_TEXT_ENCODING_FIELD) {
2694 textencoding = targetframe->ID3v2_Frame_Fields->field_string[0];
2695 }
2696
2697 if (textencoding == TE_LATIN1) {
2698 utf8str = (char *)calloc(
2699 1,
2700 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2701 ->field_length *
2702 2) +
2703 16);
2704 isolat1ToUTF8(
2705 (unsigned char *)utf8str,
2706 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2707 ->field_length *
2708 2) +
2709 16,
2710 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2711 ->field_string),
2712 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length);
2713
2714 } else if (textencoding == TE_UTF8) { // just so things can be free()'d with
2715 // testing; a small price to pay
2716 utf8str = (char *)calloc(
2717 1,
2718 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2719 ->field_length) +
2720 16);
2721 memcpy(utf8str,
2722 (targetframe->ID3v2_Frame_Fields + targetfield)->field_string,
2723 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length);
2724
2725 } else if (textencoding == TE_UTF16BE_NO_BOM) {
2726 utf8str = (char *)calloc(
2727 1,
2728 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2729 ->field_length *
2730 4) +
2731 16);
2732 UTF16BEToUTF8(
2733 (unsigned char *)utf8str,
2734 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2735 ->field_length *
2736 4) +
2737 16,
2738 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2739 ->field_string),
2740 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length);
2741
2742 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
2743 utf8str = (char *)calloc(
2744 1,
2745 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2746 ->field_length *
2747 4) +
2748 16);
2749 if (memcmp((targetframe->ID3v2_Frame_Fields + targetfield)->field_string,
2750 "\xFF\xFE",
2751 2) == 0) {
2752 UTF16LEToUTF8(
2753 (unsigned char *)utf8str,
2754 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2755 ->field_length *
2756 4) +
2757 16,
2758 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2759 ->field_string +
2760 2),
2761 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length - 2);
2762 } else {
2763 UTF16BEToUTF8(
2764 (unsigned char *)utf8str,
2765 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2766 ->field_length *
2767 4) +
2768 16,
2769 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2770 ->field_string +
2771 2),
2772 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length - 2);
2773 }
2774 }
2775 }
2776
2777 return utf8str;
2778 }
2779
2780 /*----------------------
2781 APar_FindFrame
2782 id3v2tag - an already initialized ID3 tag (contained by an ID32 atom)
2783 with 0 or more frames as a linked list frame_str - target frame string (like
2784 "TIT2") frameID - a known frame in listed in AP_ID3v2_FrameDefinitions &
2785 enumerated in AP_ID3v2_Definitions.h frametype - the type of frame (text,
2786 described text, picture, object...) to search for adjunct_payloads - holds
2787 optional/required args for supplementary matching; example: described text
2788 matching on frame name & description; TODO more criteria createframe - create
2789 the frame if not found to be existing; this initialzes the frame only - not its
2790 fields.
2791
2792 this provides 2 functions: actually searching while looping through the
2793 frames & creation of a frame at the end of the frame list.
2794 ----------------------*/
APar_FindFrame(ID3v2Tag * id3v2tag,const char * frame_str,int frameID,int frametype,AdjunctArgs * adjunct_payloads,bool createframe)2795 ID3v2Frame *APar_FindFrame(ID3v2Tag *id3v2tag,
2796 const char *frame_str,
2797 int frameID,
2798 int frametype,
2799 AdjunctArgs *adjunct_payloads,
2800 bool createframe) {
2801 ID3v2Frame *returnframe = NULL;
2802 ID3v2Frame *evalframe = id3v2tag->ID3v2_FirstFrame;
2803 uint8_t supplemental_matching = 0;
2804
2805 if (createframe) {
2806 ID3v2Frame *newframe = (ID3v2Frame *)calloc(1, sizeof(ID3v2Frame));
2807 newframe->ID3v2_NextFrame = NULL;
2808 if (id3v2tag->ID3v2_FirstFrame == NULL)
2809 id3v2tag->ID3v2_FirstFrame = newframe;
2810 if (id3v2tag->ID3v2_FrameList != NULL)
2811 id3v2tag->ID3v2_FrameList->ID3v2_NextFrame = newframe;
2812 id3v2tag->ID3v2_FrameList = newframe;
2813 return newframe;
2814 }
2815
2816 if (APar_EvalFrame_for_Field(frametype, ID3_DESCRIPTION_FIELD)) {
2817 supplemental_matching = 0x01;
2818 }
2819
2820 while (evalframe != NULL) {
2821 // if (trametype is a type containing a modifer like description or image
2822 // type or symbol or such things
2823 if (supplemental_matching != 0) {
2824
2825 // match on description + frame name
2826 if (supplemental_matching & 0x01 &&
2827 evalframe->ID3v2_Frame_ID == frameID) {
2828 char *utf8_descrip =
2829 APar_ConvertField_to_UTF8(evalframe, ID3_DESCRIPTION_FIELD);
2830 if (utf8_descrip != NULL) {
2831 if (strcmp(adjunct_payloads->descripArg, utf8_descrip) == 0) {
2832 returnframe = evalframe;
2833 free(utf8_descrip);
2834 break;
2835 }
2836 free(utf8_descrip);
2837 }
2838 }
2839
2840 } else if (evalframe->ID3v2_Frame_ID == ID3_UNKNOWN_FRAME) {
2841 if (memcmp(frame_str, evalframe->ID3v2_Frame_Namestr, 4) == 0) {
2842 returnframe = evalframe;
2843 break;
2844 }
2845
2846 } else {
2847 // fprintf(stdout, "frame is %s; eval frameID is %d ?= %d\n", frame_str,
2848 // evalframe->ID3v2_Frame_ID, frameID);
2849 if (evalframe->ID3v2_Frame_ID == frameID) {
2850 returnframe = evalframe;
2851 break;
2852 }
2853 }
2854 evalframe = evalframe->ID3v2_NextFrame;
2855 }
2856 return returnframe;
2857 }
2858
2859 /*----------------------
2860 APar_ID3FrameAmmend
2861 id32_atom - the ID32 atom targeted to this language; the ID32 atom is
2862 already created, the ID3 tag is either created or containing already parsed ID3
2863 frames frame_str - the string for the frame (like TCON) that is desired. This
2864 string must be a known frame string in AP_ID3v2_FrameDefinitions.h frame_payload
2865 - the major piece of metadata to be set (for APIC its the path, for MCDI its a
2866 device...), that can optionally be NULL (for removal of the frame)
2867 adjunct_payloads - a structure holding a number of optional/required
2868 parameters for the frame (compression...) str_encoding - the encoding to be used
2869 in the fields of the target frame when different encodings are allowed
2870
2871 lookup what frame_str is supposed to look like in the KnownFrames[] array in
2872 AP_ID3v2_FrameDefinitions.h. First see if this frame exists at all - if it does
2873 & the frame_str is NULL or blank (""), then mark this frame for elimination. if
2874 the frame is of a particular type (like TCON), run some tests on the
2875 frame_payload. If all is well after the tests, and the frame does not exists,
2876 create it via APar_FindFrame(... true) & initialize the frame to hold data. Send
2877 the frame, payload & adjunct payloads onto APar_FrameDataPut to actually place
2878 the data onto the frame
2879 ----------------------*/
APar_ID3FrameAmmend(AtomicInfo * id32_atom,const char * frame_str,const char * frame_payload,AdjunctArgs * adjunct_payloads,uint8_t str_encoding)2880 void APar_ID3FrameAmmend(AtomicInfo *id32_atom,
2881 const char *frame_str,
2882 const char *frame_payload,
2883 AdjunctArgs *adjunct_payloads,
2884 uint8_t str_encoding) {
2885 ID3v2Frame *targetFrame = NULL;
2886
2887 if (id32_atom == NULL)
2888 return;
2889 GlobalID3Tag = id32_atom->ID32_TagInfo;
2890 // fprintf(stdout, "frame is %s; payload is %s; %s %s\n", frame_str,
2891 // frame_payload, adjunct_payloads->descripArg, adjunct_payloads->targetLang);
2892
2893 int frameID =
2894 MatchID3FrameIDstr(frame_str, GlobalID3Tag->ID3v2Tag_MajorVersion);
2895 int frameType = KnownFrames[frameID + 1].ID3v2_FrameType;
2896 uint8_t frameCompositionList = GetFrameCompositionDescription(frameType);
2897
2898 if (frameType == ID3_ATTACHED_PICTURE_FRAME ||
2899 frameType == ID3_ATTACHED_OBJECT_FRAME) {
2900 APar_EmbeddedFileTests(frame_payload, frameType, adjunct_payloads);
2901 }
2902
2903 targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo,
2904 frame_str,
2905 frameID,
2906 frameType,
2907 adjunct_payloads,
2908 false);
2909
2910 if (frame_payload == NULL) {
2911 if (targetFrame != NULL) {
2912 targetFrame->eliminate_frame = true;
2913 modified_atoms = true;
2914 id32_atom->ID32_TagInfo->modified_tag = true;
2915 }
2916 return;
2917
2918 } else if (strlen(frame_payload) == 0) {
2919 if (targetFrame != NULL) {
2920 targetFrame->eliminate_frame =
2921 true; // thats right, frames of empty text are removed - so be a doll
2922 // and try to convey some info, eh?
2923 modified_atoms = true;
2924 id32_atom->ID32_TagInfo->modified_tag = true;
2925 }
2926 return;
2927
2928 } else {
2929 if (frameType == ID3_UNKNOWN_FRAME) {
2930 APar_assert(false, 10, frame_str);
2931 return;
2932 }
2933
2934 // check tags to be set so they conform to the id3v2 informal specification
2935 if (frameType == ID3_TEXT_FRAME) {
2936 if (targetFrame != NULL) {
2937 if (!targetFrame->eliminate_frame)
2938 adjunct_payloads->multistringtext =
2939 true; // if a frame already exists and isn't marked for
2940 // elimination, append a new string
2941 }
2942
2943 if (frameID == ID3v2_FRAME_COPYRIGHT ||
2944 frameID == ID3v2_FRAME_PRODNOTICE) {
2945 if ((TestCharInRange(frame_payload[0], '0', '9') +
2946 TestCharInRange(frame_payload[1], '0', '9') +
2947 TestCharInRange(frame_payload[2], '0', '9') +
2948 TestCharInRange(frame_payload[3], '0', '9') !=
2949 4) ||
2950 frame_payload[4] != ' ') {
2951 fprintf(stderr,
2952 "AtomicParsley warning: frame %s was skipped because it did "
2953 "not start with a year followed by a space\n",
2954 KnownFrames[frameID].ID3V2p4_FrameID);
2955 return;
2956 }
2957
2958 } else if (frameID == ID3v2_FRAME_PART_O_SET ||
2959 frameID == ID3v2_FRAME_TRACKNUM) {
2960 uint8_t pos_len = strlen(frame_payload);
2961 for (uint8_t letter_idx = 0; letter_idx < pos_len; letter_idx++) {
2962 if (frame_payload[letter_idx] == '/')
2963 continue;
2964 if (TestCharInRange(frame_payload[letter_idx], '0', '9') != 1) {
2965 if (frameID - 1 == ID3v2_FRAME_PART_O_SET) {
2966 fprintf(stderr,
2967 "AtomicParsley warning: frame %s was skipped because it "
2968 "had an extraneous character: %c\n",
2969 KnownFrames[frameID].ID3V2p4_FrameID,
2970 frame_payload[letter_idx]);
2971 return;
2972 } else { // okay this is to support the beloved vinyl
2973 if (!(TestCharInRange(frame_payload[letter_idx], 'A', 'F') &&
2974 TestCharInRange(frame_payload[letter_idx + 1], '0', '9'))) {
2975 fprintf(stderr,
2976 "AtomicParsley warning: frame %s was skipped because "
2977 "it had an extraneous character: %c\n",
2978 KnownFrames[frameID].ID3V2p4_FrameID,
2979 frame_payload[letter_idx]);
2980 return;
2981 }
2982 }
2983 }
2984 }
2985 } else if (frameID == ID3v2_FRAME_ISRC) {
2986 uint8_t isrc_len = strlen(frame_payload);
2987 if (isrc_len != 12) {
2988 fprintf(stderr,
2989 "AtomicParsley warning: setting ISRC frame was "
2990 "skipped because it was not 12 characters long\n");
2991 return;
2992 }
2993 for (uint8_t isrc_ltr_idx = 0; isrc_ltr_idx < isrc_len;
2994 isrc_ltr_idx++) {
2995 if (TestCharInRange(frame_payload[isrc_ltr_idx], '0', '9') +
2996 TestCharInRange(frame_payload[isrc_ltr_idx], 'A', 'Z') ==
2997 0) {
2998 fprintf(stderr,
2999 "AtomicParsley warning: ISRC can only consist of A-Z & "
3000 "0-9; letter %u was %c; skipping\n",
3001 isrc_ltr_idx + 1,
3002 frame_payload[isrc_ltr_idx]);
3003 return;
3004 }
3005 }
3006 }
3007 }
3008
3009 if (targetFrame == NULL) {
3010 targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo,
3011 frame_str,
3012 frameID,
3013 frameType,
3014 adjunct_payloads,
3015 true);
3016 if (targetFrame == NULL) {
3017 fprintf(stdout, "NULL frame\n");
3018 exit(0);
3019 } else {
3020 APar_FrameInit(targetFrame,
3021 frame_str,
3022 frameID,
3023 frameCompositionList,
3024 frame_payload);
3025 }
3026 }
3027 }
3028
3029 if (targetFrame != NULL) {
3030 if (adjunct_payloads->zlibCompressed) {
3031 targetFrame->ID3v2_Frame_Flags |=
3032 (ID32_FRAMEFLAG_COMPRESSED + ID32_FRAMEFLAG_LENINDICATED);
3033 }
3034
3035 if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_LANGUAGE) {
3036 APar_FrameDataPut(targetFrame,
3037 adjunct_payloads->targetLang,
3038 adjunct_payloads,
3039 str_encoding);
3040
3041 } else if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) {
3042 uint8_t genre_idx = ID3StringGenreToInt(frame_payload);
3043 if (genre_idx != 0xFF) {
3044 char genre_str_idx[2];
3045 genre_str_idx[0] = 0;
3046 genre_str_idx[1] = 0;
3047 genre_str_idx[1] = 0;
3048 sprintf(genre_str_idx, "%u", genre_idx);
3049 APar_FrameDataPut(
3050 targetFrame, genre_str_idx, adjunct_payloads, str_encoding);
3051 } else {
3052 APar_FrameDataPut(
3053 targetFrame, frame_payload, adjunct_payloads, str_encoding);
3054 }
3055
3056 } else {
3057 APar_FrameDataPut(
3058 targetFrame, frame_payload, adjunct_payloads, str_encoding);
3059 }
3060
3061 if (adjunct_payloads->zlibCompressed) {
3062 targetFrame->ID3v2_Frame_ExpandedLength = targetFrame->ID3v2_Frame_Length;
3063 }
3064 targetFrame->ID3v2_Frame_GroupingSymbol = adjunct_payloads->groupSymbol;
3065 }
3066 return;
3067 }
3068
3069 ///////////////////////////////////////////////////////////////////////////////////////
3070 // id3 cleanup function //
3071 ///////////////////////////////////////////////////////////////////////////////////////
3072
3073 /*----------------------
3074 APar_FreeID32Memory
3075
3076 free all the little bits of allocated memory. Follow the ID3v2Frame pointers
3077 by each frame's ID3v2_NextFrame. Each frame has ID3v2_FieldCount number of field
3078 strings (char*) that were malloced.
3079 ----------------------*/
APar_FreeID32Memory(ID3v2Tag * id32tag)3080 void APar_FreeID32Memory(ID3v2Tag *id32tag) {
3081 ID3v2Frame *aframe = id32tag->ID3v2_FirstFrame;
3082 while (aframe != NULL) {
3083
3084 #if defined(DEBUG_V)
3085 fprintf(stdout,
3086 "freeing frame %s of %u fields\n",
3087 aframe->ID3v2_Frame_Namestr,
3088 aframe->ID3v2_FieldCount);
3089 #endif
3090 for (uint8_t id3fld = 0; id3fld < aframe->ID3v2_FieldCount; id3fld++) {
3091 #if defined(DEBUG_V)
3092 fprintf(stdout,
3093 "freeing field %s ; %u of %u fields\n",
3094 (aframe->ID3v2_Frame_Fields + id3fld)->field_string,
3095 id3fld + 1,
3096 aframe->ID3v2_FieldCount);
3097 #endif
3098 ID3v2Fields *afield = aframe->ID3v2_Frame_Fields + id3fld;
3099 ID3v2Fields *freefield = NULL;
3100 while (true) {
3101 if (afield != NULL && afield->field_string != NULL) {
3102 free(afield->field_string);
3103 afield->field_string = NULL;
3104 }
3105 freefield = afield;
3106 afield = afield->next_field;
3107 if (afield == NULL)
3108 break;
3109 if (aframe->ID3v2_Frame_Fields + id3fld != freefield)
3110 free(freefield);
3111 }
3112 }
3113 free(aframe->ID3v2_Frame_Fields);
3114 aframe->ID3v2_Frame_Fields = NULL;
3115 free(aframe);
3116 aframe = aframe->ID3v2_NextFrame;
3117 }
3118 return;
3119 }
3120