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