id3dev 26.01 An ID3 metadata library

Loading...
Searching...
No Matches
id3v2.c
Go to the documentation of this file.
1
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include "id3dependencies/ByteStream/include/byteStream.h"
16#include "id3dependencies/ByteStream/include/byteUnicode.h"
17#include "id3dependencies/ByteStream/include/byteInt.h"
18#include "id3v2/id3v2.h"
19#include "id3v2/id3v2Frame.h"
20#include "id3v2/id3v2Parser.h"
21#include "id3v2/id3v2Context.h"
22#include "id3v2/id3v2TagIdentity.h"
23
31 Id3v2Tag *id3v2TagFromFile(const char *filename) {
32 ByteStream *stream = NULL;
33 Id3v2Tag *tag = NULL;
34
35 if (filename == NULL) {
36 return NULL;
37 }
38
39 stream = byteStreamFromFile(filename);
40 if (stream == NULL) {
41 return NULL;
42 }
43
44 tag = id3v2ParseTagFromBuffer(stream->buffer, stream->bufferSize, NULL);
45 byteStreamDestroy(stream);
46
47 return tag;
48}
49
57 Id3v2Tag *id3v2CopyTag(const Id3v2Tag *toCopy) {
58 Id3v2TagHeader *header = NULL;
59
60 if (toCopy == NULL) {
61 return NULL;
62 }
63
64 if (toCopy->header == NULL) {
65 return NULL;
66 }
67
68 header = id3v2CreateTagHeader(toCopy->header->majorVersion, toCopy->header->minorVersion, toCopy->header->flags,
69 NULL);
70 header->extendedHeader = (toCopy->header->extendedHeader != NULL)
71 ? id3v2CreateExtendedTagHeader(toCopy->header->extendedHeader->padding,
72 toCopy->header->extendedHeader->crc,
73 toCopy->header->extendedHeader->update,
74 toCopy->header->extendedHeader->tagRestrictions,
75 toCopy->header->extendedHeader->restrictions)
76 : NULL;
77
78 return id3v2CreateTag(header, listDeepCopy(toCopy->frames));
79}
80
89 bool id3v2CompareTag(Id3v2Tag *tag1, Id3v2Tag *tag2) {
90 if (tag1 == NULL || tag2 == NULL) {
91 return false;
92 }
93
94 if (tag1->header == NULL || tag2->header == NULL) {
95 return false;
96 }
97
98 // check header
99
100 if (tag1->header->majorVersion != tag2->header->majorVersion) {
101 return false;
102 }
103
104 if (tag1->header->minorVersion != tag2->header->minorVersion) {
105 return false;
106 }
107
108 if (tag1->header->flags != tag2->header->flags) {
109 return false;
110 }
111
112 // check extended header
113
114 if (tag1->header->extendedHeader != NULL && tag2->header->extendedHeader != NULL) {
115 if (tag1->header->extendedHeader->padding != tag2->header->extendedHeader->padding) {
116 return false;
117 }
118
119 if (tag1->header->extendedHeader->crc != tag2->header->extendedHeader->crc) {
120 return false;
121 }
122
123 if (tag1->header->extendedHeader->update != tag2->header->extendedHeader->update) {
124 return false;
125 }
126
127 if (tag1->header->extendedHeader->tagRestrictions != tag2->header->extendedHeader->tagRestrictions) {
128 return false;
129 }
130
131 if (tag1->header->extendedHeader->restrictions != tag2->header->extendedHeader->restrictions) {
132 return false;
133 }
134 }
135
136 // check frames
137
138 ListIter frames1 = id3v2CreateFrameTraverser(tag1);
139 ListIter frames2 = id3v2CreateFrameTraverser(tag2);
140
141 Id3v2Frame *f1 = NULL;
142 Id3v2Frame *f2 = NULL;
143
144
145 while ((f1 = id3v2FrameTraverse(&frames1)) != NULL) {
146 f2 = id3v2FrameTraverse(&frames2);
147
148 if (f2 == NULL) {
149 return false;
150 }
151
152 if (id3v2CompareFrame(f1, f2) != 0) {
153 return false;
154 }
155 }
156
157 f2 = id3v2FrameTraverse(&frames2);
158 if (f2 != NULL) {
159 return false;
160 }
161
162 return true;
163}
164
173 Id3v2Frame *id3v2ReadFrameByID(const char id[ID3V2_FRAME_ID_MAX_SIZE], Id3v2Tag *tag) {
174 if (id == NULL || tag == NULL) {
175 return NULL;
176 }
177
178 int i = 0;
179 ListIter frames = id3v2CreateFrameTraverser(tag);
180 Id3v2Frame *f = NULL;
181
182
183 // sanitize id
184 for (i = 0; i < ID3V2_FRAME_ID_MAX_SIZE; i++) {
185 if (id[i] == '\0') {
186 break;
187 }
188 }
189
190 // find frame
191 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
192 if (memcmp(id, f->header->id, i) == 0) {
193 return id3v2CopyFrame(f);
194 }
195 }
196
197 return NULL;
198}
199
208 int id3v2RemoveFrameByID(const char *id, Id3v2Tag *tag) {
209 Id3v2Frame *test = NULL;
210 Id3v2Frame *remove = NULL;
211
212 test = id3v2ReadFrameByID(id, tag);
213 remove = id3v2DetachFrameFromTag(tag, test);
214
215 id3v2DestroyFrame(&test);
216
217 if (remove != NULL) {
218 id3v2DestroyFrame(&remove);
219 return 1;
220 }
221
222 return 0;
223}
224
235 int id3v2InsertTextFrame(const char id[ID3V2_FRAME_ID_MAX_SIZE], const uint8_t encoding, const char *string,
236 Id3v2Tag *tag) {
237 if (id == NULL || string == NULL || tag == NULL || encoding > BYTE_UTF8) {
238 return false;
239 }
240
241 if (strlen(string) == 0) {
242 return false;
243 }
244
245 Id3v2FrameHeader *header = NULL;
246 Id3v2Frame *f = NULL;
247 Id3v2ContentEntry *entry = NULL;
248 uint8_t *usableString = NULL;
249 size_t outLen = 0;
250 bool convi = false;
251
252 // set up frame
253 header = id3v2CreateFrameHeader((uint8_t *) id, 0, 0, 0, 0, 0, 0, 0);
254 f = id3v2CreateFrame(header, id3v2CreateTextFrameContext(),
255 listCreate(id3v2PrintContentEntry, id3v2DeleteContentEntry, id3v2CompareContentEntry,
256 id3v2CopyContentEntry));
257
258 // add encoding
259 entry = id3v2CreateContentEntry((void *) &encoding, 1);
260 listInsertBack(f->entries, (void *) entry);
261
262 //add text
263 convi = byteConvertTextFormat((unsigned char *) string, BYTE_UTF8, strlen(string), &usableString, encoding,
264 &outLen);
265
266 // already in target encoding - use original string
267 if (convi == true && outLen == 0) {
268 usableString = (uint8_t *) strdup(string);
269
270 if (usableString == NULL) {
271 id3v2DestroyFrame(&f);
272 return false;
273 }
274
275 outLen = strlen(string);
276 } else if (convi == false || outLen == 0 || usableString == NULL) {
277 id3v2DestroyFrame(&f);
278 return false;
279 }
280
281 // re enable utf16 len support
282 bytePrependBOM(encoding, &usableString, &outLen);
283
284 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
285 // false positive memory leak from realloc
286 // NOLINTNEXTLINE
287 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
288 if (reallocPtr == NULL) {
289 free(usableString);
290 id3v2DestroyFrame(&f);
291 return false;
292 }
293 usableString = reallocPtr;
294 memset(usableString + outLen, 0, BYTE_PADDING);
295 }
296
297 // add encoded text
298 entry = id3v2CreateContentEntry((void *) usableString, outLen);
299 listInsertBack(f->entries, (void *) entry);
300 free(usableString);
301
302 listInsertBack(tag->frames, (void *) f);
303
304 // false positive memory leak for usableString due to realloc
305 // NOLINTNEXTLINE
306 return true;
307}
308
316 int id3v2ReadTagVersion(const Id3v2Tag *tag) {
317 if (tag == NULL) {
318 return -1;
319 }
320
321 if (tag->header == NULL) {
322 return -1;
323 }
324
325 return tag->header->majorVersion;
326}
327
337 char *id3v2ReadTextFrameContent(const char id[ID3V2_FRAME_ID_MAX_SIZE], Id3v2Tag *tag) {
338 Id3v2Frame *frame = id3v2ReadFrameByID(id, tag);
339 ListIter entries = {0};
340 ListIter context = {0};
341 Id3v2ContentContext *cc = NULL;
342 size_t dataSize = 0;
343 char *ret = NULL;
344
345 if (frame == NULL) {
346 return NULL;
347 }
348
349 // verify text frame via context
350 if (frame->contexts->length != 2 && frame->entries->length != 2) {
351 id3v2DestroyFrame(&frame);
352 return NULL;
353 }
354
355 context = listCreateIterator(frame->contexts);
356
357 for (int i = 0; i < 2; i++) {
358 cc = listIteratorNext(&context);
359
360 if (cc == NULL) {
361 id3v2DestroyFrame(&frame);
362 return NULL;
363 }
364
365 if (i == 0) {
366 if (cc->type != numeric_context) {
367 id3v2DestroyFrame(&frame);
368 return NULL;
369 }
370 } else if (i == 1) {
371 if (cc->type != encodedString_context) {
372 id3v2DestroyFrame(&frame);
373 return NULL;
374 }
375 }
376 }
377
378 // read text frame
379 entries = id3v2CreateFrameEntryTraverser(frame);
380 id3v2ReadFrameEntryAsU8(&entries);
381
382 ret = id3v2ReadFrameEntryAsChar(&entries, &dataSize);
383
384 id3v2DestroyFrame(&frame);
385
386 return ret;
387}
388
396 char *id3v2ReadTitle(Id3v2Tag *tag) {
397 if (tag == NULL) {
398 return NULL;
399 }
400
401 char *str = NULL;
402
403 switch (tag->header->majorVersion) {
404 case ID3V2_TAG_VERSION_2:
405 str = id3v2ReadTextFrameContent("TT2", tag);
406 break;
407 case ID3V2_TAG_VERSION_3:
408 case ID3V2_TAG_VERSION_4:
409 str = id3v2ReadTextFrameContent("TIT2", tag);
410 break;
411 default:
412 return NULL;
413 }
414
415 return str;
416}
417
425 char *id3v2ReadArtist(Id3v2Tag *tag) {
426 if (tag == NULL) {
427 return NULL;
428 }
429
430 char *str = NULL;
431
432 switch (tag->header->majorVersion) {
433 case ID3V2_TAG_VERSION_2:
434 str = id3v2ReadTextFrameContent("TP1", tag);
435 break;
436 case ID3V2_TAG_VERSION_3:
437 case ID3V2_TAG_VERSION_4:
438 str = id3v2ReadTextFrameContent("TPE1", tag);
439 break;
440 default:
441 return NULL;
442 }
443
444 return str;
445}
446
454 char *id3v2ReadAlbumArtist(Id3v2Tag *tag) {
455 if (tag == NULL) {
456 return NULL;
457 }
458
459 char *str = NULL;
460
461 switch (tag->header->majorVersion) {
462 case ID3V2_TAG_VERSION_2:
463 str = id3v2ReadTextFrameContent("TP2", tag);
464 break;
465 case ID3V2_TAG_VERSION_3:
466 case ID3V2_TAG_VERSION_4:
467 str = id3v2ReadTextFrameContent("TPE2", tag);
468 break;
469 default:
470 return NULL;
471 }
472
473 return str;
474}
475
483 char *id3v2ReadAlbum(Id3v2Tag *tag) {
484 if (tag == NULL) {
485 return NULL;
486 }
487
488 char *str = NULL;
489
490 switch (tag->header->majorVersion) {
491 case ID3V2_TAG_VERSION_2:
492 str = id3v2ReadTextFrameContent("TAL", tag);
493 break;
494 case ID3V2_TAG_VERSION_3:
495 case ID3V2_TAG_VERSION_4:
496 str = id3v2ReadTextFrameContent("TALB", tag);
497 break;
498 default:
499 return NULL;
500 }
501
502 return str;
503}
504
512 char *id3v2ReadYear(Id3v2Tag *tag) {
513 if (tag == NULL) {
514 return NULL;
515 }
516
517 char *str = NULL;
518
519 switch (tag->header->majorVersion) {
520 case ID3V2_TAG_VERSION_2:
521 str = id3v2ReadTextFrameContent("TYE", tag);
522 break;
523 case ID3V2_TAG_VERSION_3:
524 case ID3V2_TAG_VERSION_4:
525 str = id3v2ReadTextFrameContent("TYER", tag);
526 break;
527 default:
528 return NULL;
529 }
530
531 return str;
532}
533
541 char *id3v2ReadGenre(Id3v2Tag *tag) {
542 if (tag == NULL) {
543 return NULL;
544 }
545
546 char *str = NULL;
547
548 switch (tag->header->majorVersion) {
549 case ID3V2_TAG_VERSION_2:
550 str = id3v2ReadTextFrameContent("TCO", tag);
551 break;
552 case ID3V2_TAG_VERSION_3:
553 case ID3V2_TAG_VERSION_4:
554 str = id3v2ReadTextFrameContent("TCON", tag);
555 break;
556 default:
557 return NULL;
558 }
559
560 return str;
561}
562
570 char *id3v2ReadTrack(Id3v2Tag *tag) {
571 if (tag == NULL) {
572 return NULL;
573 }
574
575 char *str = NULL;
576
577 switch (tag->header->majorVersion) {
578 case ID3V2_TAG_VERSION_2:
579 str = id3v2ReadTextFrameContent("TRK", tag);
580 break;
581 case ID3V2_TAG_VERSION_3:
582 case ID3V2_TAG_VERSION_4:
583 str = id3v2ReadTextFrameContent("TRCK", tag);
584 break;
585 default:
586 return NULL;
587 }
588
589 return str;
590}
591
599 char *id3v2ReadComposer(Id3v2Tag *tag) {
600 if (tag == NULL) {
601 return NULL;
602 }
603
604 char *str = NULL;
605
606 switch (tag->header->majorVersion) {
607 case ID3V2_TAG_VERSION_2:
608 str = id3v2ReadTextFrameContent("TCM", tag);
609 break;
610 case ID3V2_TAG_VERSION_3:
611 case ID3V2_TAG_VERSION_4:
612 str = id3v2ReadTextFrameContent("TCOM", tag);
613 break;
614 default:
615 return NULL;
616 }
617
618 return str;
619}
620
628 char *id3v2ReadDisc(Id3v2Tag *tag) {
629 if (tag == NULL) {
630 return NULL;
631 }
632
633 char *str = NULL;
634
635 switch (tag->header->majorVersion) {
636 case ID3V2_TAG_VERSION_2:
637 str = id3v2ReadTextFrameContent("TPA", tag);
638 break;
639 case ID3V2_TAG_VERSION_3:
640 case ID3V2_TAG_VERSION_4:
641 str = id3v2ReadTextFrameContent("TPOS", tag);
642 break;
643 default:
644 return NULL;
645 }
646
647 return str;
648}
649
658 char *id3v2ReadLyrics(Id3v2Tag *tag) {
659 Id3v2Frame *frame = NULL;
660 ListIter entries = {0};
661 ListIter context = {0};
662 Id3v2ContentContext *cc = NULL;
663 size_t dataSize = 0;
664 char *ret = NULL;
665
666 if (tag == NULL) {
667 return NULL;
668 }
669
670
671 switch (tag->header->majorVersion) {
672 case ID3V2_TAG_VERSION_2:
673 frame = id3v2ReadFrameByID("ULT", tag);
674 break;
675 case ID3V2_TAG_VERSION_3:
676 case ID3V2_TAG_VERSION_4:
677 frame = id3v2ReadFrameByID("USLT", tag);
678 break;
679 default:
680 return NULL;
681 }
682
683 if (frame == NULL) {
684 return NULL;
685 }
686
687 // verify lyric frame via context
688 if (frame->contexts->length != 4 && frame->entries->length != 4) {
689 id3v2DestroyFrame(&frame);
690 return NULL;
691 }
692
693 context = listCreateIterator(frame->contexts);
694
695 for (int i = 0; i < 4; i++) {
696 cc = listIteratorNext(&context);
697
698 if (cc == NULL || (i == 0 && cc->type != numeric_context) ||
699 (i == 1 && cc->type != noEncoding_context) ||
700 (i == 2 && cc->type != encodedString_context) ||
701 (i == 3 && cc->type != encodedString_context)) {
702 id3v2DestroyFrame(&frame);
703 return NULL;
704 }
705 }
706
707 // read text frame
708 entries = id3v2CreateFrameEntryTraverser(frame);
709 id3v2ReadFrameEntryAsU8(&entries);
710 id3v2ReadFrameEntryAsU8(&entries);
711 id3v2ReadFrameEntryAsU8(&entries);
712
713 ret = id3v2ReadFrameEntryAsChar(&entries, &dataSize);
714 id3v2DestroyFrame(&frame);
715
716 return ret;
717}
718
727 char *id3v2ReadComment(Id3v2Tag *tag) {
728 Id3v2Frame *frame = NULL;
729 ListIter entries = {0};
730 ListIter context = {0};
731 Id3v2ContentContext *cc = NULL;
732 size_t dataSize = 0;
733 char *ret = NULL;
734
735 if (tag == NULL) {
736 return NULL;
737 }
738
739
740 switch (tag->header->majorVersion) {
741 case ID3V2_TAG_VERSION_2:
742 frame = id3v2ReadFrameByID("COM", tag);
743 break;
744 case ID3V2_TAG_VERSION_3:
745 case ID3V2_TAG_VERSION_4:
746 frame = id3v2ReadFrameByID("COMM", tag);
747 break;
748 default:
749 return NULL;
750 }
751
752 if (frame == NULL) {
753 return NULL;
754 }
755
756 // verify lyric frame via context
757 if (frame->contexts->length != 4 && frame->entries->length != 4) {
758 id3v2DestroyFrame(&frame);
759 return NULL;
760 }
761
762 context = listCreateIterator(frame->contexts);
763
764 for (int i = 0; i < 4; i++) {
765 cc = listIteratorNext(&context);
766
767 if (cc == NULL || (i == 0 && cc->type != numeric_context) ||
768 (i == 1 && cc->type != noEncoding_context) ||
769 (i == 2 && cc->type != encodedString_context) ||
770 (i == 3 && cc->type != encodedString_context)) {
771 id3v2DestroyFrame(&frame);
772 return NULL;
773 }
774 }
775
776 // read text frame
777 entries = id3v2CreateFrameEntryTraverser(frame);
778 id3v2ReadFrameEntryAsU8(&entries);
779 id3v2ReadFrameEntryAsU8(&entries);
780 id3v2ReadFrameEntryAsU8(&entries);
781
782 ret = id3v2ReadFrameEntryAsChar(&entries, &dataSize);
783 id3v2DestroyFrame(&frame);
784
785 return ret;
786}
787
797uint8_t *id3v2ReadPicture(uint8_t type, const Id3v2Tag *tag, size_t *dataSize) {
798 *dataSize = 0;
799
800 if (tag == NULL) {
801 return NULL;
802 }
803
804 Id3v2Frame *f = NULL;
805 ListIter frames = listCreateIterator(tag->frames);
806 uint8_t usableType = ((type > 0x14) ? 0x00 : type); // clamp type
807
808 while ((f = listIteratorNext(&frames)) != NULL) {
809 ListIter entries = id3v2CreateFrameEntryTraverser(f);
810
811 if (memcmp("PIC", f->header->id, ID3V2_FRAME_ID_MAX_SIZE - 1) == 0 ||
812 memcmp("APIC", f->header->id, ID3V2_FRAME_ID_MAX_SIZE) == 0) {
813 id3v2ReadFrameEntryAsU8(&entries); // encoding
814 id3v2ReadFrameEntryAsU8(&entries); // mime type
815
816 if (id3v2ReadFrameEntryAsU8(&entries) == usableType) {
817 id3v2ReadFrameEntryAsU8(&entries); // description
818 return id3v2ReadFrameEntry(&entries, dataSize);
819 }
820 }
821 }
822
823 return NULL;
824}
825
836 int id3v2WriteTextFrameContent(const char id[ID3V2_FRAME_ID_MAX_SIZE], const char *string, Id3v2Tag *tag) {
837 if (id == NULL || string == NULL || tag == NULL) {
838 return false;
839 }
840
841 // null is an invalid string
842 if (strlen(string) == 0) {
843 return false;
844 }
845
846 Id3v2Frame *f = NULL;
847 ListIter frames = id3v2CreateFrameTraverser(tag);
848 ListIter context = {0};
849 ListIter entries = {0};
850 Id3v2ContentContext *cc = NULL;
851 uint8_t encoding = 0;
852 uint8_t *usableString = NULL;
853 size_t outLen = 0;
854 int i = 0;
855 bool convi = false;
856
857
858 // sanitize id
859 for (i = 0; i < ID3V2_FRAME_ID_MAX_SIZE; i++) {
860 if (id[i] == '\0') {
861 break;
862 }
863 }
864
865 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
866 if (memcmp(id, f->header->id, i) == 0) {
867 break;
868 }
869 }
870
871 if (f == NULL) {
872 return id3v2InsertTextFrame(id, BYTE_UTF16LE, string, tag);
873 }
874
875 // verify text frame via context
876 if (f->contexts->length != 2 && f->entries->length != 2) {
877 return false;
878 }
879
880 context = listCreateIterator(f->contexts);
881
882 for (int j = 0; j < 2; j++) {
883 cc = listIteratorNext(&context);
884
885 if (cc == NULL || (j == 0 && cc->type != numeric_context) ||
886 (j == 1 && cc->type != encodedString_context)) {
887 return false;
888 }
889 }
890
891 entries = id3v2CreateFrameEntryTraverser(f);
892
893 encoding = id3v2ReadFrameEntryAsU8(&entries); // encoding
894
895 convi = byteConvertTextFormat((unsigned char *) string, BYTE_UTF8, strlen(string), &usableString, encoding,
896 &outLen);
897
898 if (!convi && outLen == 0 && usableString == NULL) {
899 return false;
900 }
901
902 // data is already in utf8
903 if (convi && outLen == 0) {
904 usableString = (uint8_t *) strdup(string);
905 outLen = strlen(string);
906 }
907
908 bytePrependBOM(encoding, &usableString, &outLen);
909
910 // re enable utf16 len support
911 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
912 // realloc false positive memory leak as usableString becomes the memory owner
913 // NOLINTNEXTLINE
914 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
915 if (reallocPtr == NULL) {
916 free(usableString);
917 return false;
918 }
919 usableString = reallocPtr;
920 memset(usableString + outLen, 0, BYTE_PADDING);
921 }
922
923 if (id3v2WriteFrameEntry(f, &entries, byteStrlen(encoding, usableString), (void *) usableString)) {
924 free(usableString);
925 // memory is now owned by frame
926 // NOLINTNEXTLINE
927 return true;
928 }
929
930 if (usableString != NULL) {
931 free(usableString);
932 }
933
934 // memory is owned by frame
935 // NOLINTNEXTLINE
936 return false;
937}
938
947 int id3v2WriteTitle(const char *title, Id3v2Tag *tag) {
948 if (title == NULL || tag == NULL) {
949 return false;
950 }
951
952 switch (tag->header->majorVersion) {
953 case ID3V2_TAG_VERSION_2:
954 return id3v2WriteTextFrameContent("TT2", title, tag);
955 case ID3V2_TAG_VERSION_3:
956 case ID3V2_TAG_VERSION_4:
957 return id3v2WriteTextFrameContent("TIT2", title, tag);
958 default:
959 break;
960 }
961
962 return false;
963}
964
973 int id3v2WriteArtist(const char *artist, Id3v2Tag *tag) {
974 if (artist == NULL || tag == NULL) {
975 return false;
976 }
977
978 switch (tag->header->majorVersion) {
979 case ID3V2_TAG_VERSION_2:
980 return id3v2WriteTextFrameContent("TP1", artist, tag);
981 case ID3V2_TAG_VERSION_3:
982 case ID3V2_TAG_VERSION_4:
983 return id3v2WriteTextFrameContent("TPE1", artist, tag);
984 default:
985 break;
986 }
987
988 return false;
989}
990
999 int id3v2WriteAlbumArtist(const char *albumArtist, Id3v2Tag *tag) {
1000 if (albumArtist == NULL || tag == NULL) {
1001 return false;
1002 }
1003
1004 switch (tag->header->majorVersion) {
1005 case ID3V2_TAG_VERSION_2:
1006 return id3v2WriteTextFrameContent("TP2", albumArtist, tag);
1007 case ID3V2_TAG_VERSION_3:
1008 case ID3V2_TAG_VERSION_4:
1009 return id3v2WriteTextFrameContent("TPE2", albumArtist, tag);
1010 default:
1011 break;
1012 }
1013
1014 return false;
1015}
1016
1025 int id3v2WriteAlbum(const char *album, Id3v2Tag *tag) {
1026 if (album == NULL || tag == NULL) {
1027 return false;
1028 }
1029
1030 switch (tag->header->majorVersion) {
1031 case ID3V2_TAG_VERSION_2:
1032 return id3v2WriteTextFrameContent("TAL", album, tag);
1033 case ID3V2_TAG_VERSION_3:
1034 case ID3V2_TAG_VERSION_4:
1035 return id3v2WriteTextFrameContent("TALB", album, tag);
1036 default:
1037 break;
1038 }
1039
1040 return false;
1041}
1042
1051 int id3v2WriteYear(const char *year, Id3v2Tag *tag) {
1052 if (year == NULL || tag == NULL) {
1053 return false;
1054 }
1055
1056 switch (tag->header->majorVersion) {
1057 case ID3V2_TAG_VERSION_2:
1058 return id3v2WriteTextFrameContent("TYE", year, tag);
1059 case ID3V2_TAG_VERSION_3:
1060 case ID3V2_TAG_VERSION_4:
1061 return id3v2WriteTextFrameContent("TYER", year, tag);
1062 default:
1063 break;
1064 }
1065
1066 return false;
1067}
1068
1077 int id3v2WriteGenre(const char *genre, Id3v2Tag *tag) {
1078 if (genre == NULL || tag == NULL) {
1079 return false;
1080 }
1081
1082 switch (tag->header->majorVersion) {
1083 case ID3V2_TAG_VERSION_2:
1084 return id3v2WriteTextFrameContent("TCO", genre, tag);
1085 case ID3V2_TAG_VERSION_3:
1086 case ID3V2_TAG_VERSION_4:
1087 return id3v2WriteTextFrameContent("TCON", genre, tag);
1088 default:
1089 break;
1090 }
1091
1092 return false;
1093}
1094
1103 int id3v2WriteTrack(const char *track, Id3v2Tag *tag) {
1104 if (track == NULL || tag == NULL) {
1105 return false;
1106 }
1107
1108 switch (tag->header->majorVersion) {
1109 case ID3V2_TAG_VERSION_2:
1110 return id3v2WriteTextFrameContent("TRK", track, tag);
1111 case ID3V2_TAG_VERSION_3:
1112 case ID3V2_TAG_VERSION_4:
1113 return id3v2WriteTextFrameContent("TRCK", track, tag);
1114 default:
1115 break;
1116 }
1117
1118 return false;
1119}
1120
1129 int id3v2WriteDisc(const char *disc, Id3v2Tag *tag) {
1130 if (disc == NULL || tag == NULL) {
1131 return false;
1132 }
1133
1134 switch (tag->header->majorVersion) {
1135 case ID3V2_TAG_VERSION_2:
1136 return id3v2WriteTextFrameContent("TPA", disc, tag);
1137 case ID3V2_TAG_VERSION_3:
1138 case ID3V2_TAG_VERSION_4:
1139 return id3v2WriteTextFrameContent("TPOS", disc, tag);
1140 default:
1141 break;
1142 }
1143
1144 return false;
1145}
1146
1155 int id3v2WriteComposer(const char *composer, Id3v2Tag *tag) {
1156 if (composer == NULL || tag == NULL) {
1157 return false;
1158 }
1159
1160 switch (tag->header->majorVersion) {
1161 case ID3V2_TAG_VERSION_2:
1162 return id3v2WriteTextFrameContent("TCM", composer, tag);
1163 case ID3V2_TAG_VERSION_3:
1164 case ID3V2_TAG_VERSION_4:
1165 return id3v2WriteTextFrameContent("TCOM", composer, tag);
1166 default:
1167 break;
1168 }
1169
1170 return false;
1171}
1172
1173// internal function ------------------------------------------------------------------------
1174 static int internal_id3v2CreateLyricFrameUTF16LE(const uint8_t v, const char *lyrics, Id3v2Tag *tag) {
1175 // check for legal args
1176 // NOLINTNEXTLINE
1177 if (lyrics == NULL) {
1178 // NOLINTNEXTLINE
1179 return false;
1180 }
1181
1182 if (strlen(lyrics) == 0) {
1183 return false;
1184 }
1185
1186 Id3v2Frame *f = NULL;
1187 ListIter entries = {0};
1188 uint8_t encoding = BYTE_UTF16LE;
1189 uint8_t *usableString = NULL;
1190 size_t outLen = 0;
1191 bool convi = false;
1192
1193 // create frame
1194 switch (v) {
1195 case ID3V2_TAG_VERSION_2:
1196 f = id3v2CreateEmptyFrame("ULT\0", v, NULL);
1197 break;
1198 case ID3V2_TAG_VERSION_3:
1199 case ID3V2_TAG_VERSION_4:
1200 f = id3v2CreateEmptyFrame("USLT", v, NULL);
1201 break;
1202 default:
1203 return false;
1204 }
1205
1206 if (f == NULL) {
1207 return false;
1208 }
1209
1210 // update entries
1211 entries = id3v2CreateFrameEntryTraverser(f);
1212
1213 // add encoding
1214 if (!id3v2WriteFrameEntry(f, &entries, 1, (void *) &encoding)) {
1215 id3v2DestroyFrame(&f);
1216 return false;
1217 }
1218 id3v2ReadFrameEntryAsU8(&entries);
1219
1220 // add language
1221 if (!id3v2WriteFrameEntry(f, &entries, 3, (void *) "zxx")) {
1222 // unknown language
1223 id3v2DestroyFrame(&f);
1224 return false;
1225 }
1226 id3v2ReadFrameEntryAsU8(&entries);
1227
1228 // ensure description is nothing
1229 if (!id3v2WriteFrameEntry(f, &entries, 1, (void *) "\0")) {
1230 id3v2DestroyFrame(&f);
1231 return false;
1232 }
1233 id3v2ReadFrameEntryAsU8(&entries);
1234
1235 // add lyrics
1236
1237 convi = byteConvertTextFormat((unsigned char *) lyrics, BYTE_UTF8, strlen(lyrics), &usableString, BYTE_UTF16LE,
1238 &outLen);
1239
1240 if (convi == false || outLen == 0 || usableString == NULL) {
1241 id3v2DestroyFrame(&f);
1242 return false;
1243 }
1244
1245 // re add bom and padding
1246 bytePrependBOM(encoding, &usableString, &outLen);
1247
1248 // realloc false positive memory leak as usableString becomes the memory owner
1249 // NOLINTNEXTLINE
1250 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
1251 if (reallocPtr == NULL) {
1252 free(usableString);
1253 id3v2DestroyFrame(&f);
1254 return false;
1255 }
1256 usableString = reallocPtr;
1257
1258 memset(usableString + outLen, 0, BYTE_PADDING);
1259
1260
1261 if (!id3v2WriteFrameEntry(f, &entries, outLen, (void *) usableString)) {
1262 id3v2DestroyFrame(&f);
1263 free(usableString);
1264
1265 // memory is owned by frame - false positive
1266 // NOLINTNEXTLINE
1267 return false;
1268 }
1269 id3v2ReadFrameEntryAsU8(&entries);
1270
1271
1272 listInsertBack(tag->frames, (void *) f);
1273 free(usableString);
1274
1275 // memory is owned by frame - false positive
1276 // NOLINTNEXTLINE
1277 return true;
1278}
1279
1290 int id3v2WriteLyrics(const char *lyrics, Id3v2Tag *tag) {
1291 if (lyrics == NULL || tag == NULL) {
1292 return false;
1293 }
1294
1295 // null is an invalid string
1296 if (strlen(lyrics) == 0) {
1297 return false;
1298 }
1299
1300 Id3v2Frame *f = NULL;
1301 ListIter frames = id3v2CreateFrameTraverser(tag);
1302 ListIter context = {0};
1303 ListIter entries = {0};
1304 Id3v2ContentContext *cc = NULL;
1305 uint8_t encoding = 0;
1306 uint8_t *usableString = NULL;
1307 size_t outLen = 0;
1308 char id[ID3V2_FRAME_ID_MAX_SIZE] = {0, 0, 0, 0};
1309 int i = 0;
1310 bool convi = false;
1311
1312 switch (tag->header->majorVersion) {
1313 case ID3V2_TAG_VERSION_2:
1314 i = ID3V2_FRAME_ID_MAX_SIZE - 1;
1315 memcpy(id, "ULT", i);
1316 break;
1317 case ID3V2_TAG_VERSION_3:
1318 case ID3V2_TAG_VERSION_4:
1319 i = ID3V2_FRAME_ID_MAX_SIZE;
1320 memcpy(id, "USLT", i);
1321 break;
1322 default:
1323 return false;
1324 }
1325
1326 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
1327 if (memcmp(id, f->header->id, i) == 0) {
1328 break;
1329 }
1330 }
1331
1332 if (f == NULL) {
1333 return internal_id3v2CreateLyricFrameUTF16LE(tag->header->majorVersion, lyrics, tag);
1334 }
1335
1336 // verify frame via context
1337 if (f->contexts->length != 4 && f->entries->length != 4) {
1338 return false;
1339 }
1340
1341 context = listCreateIterator(f->contexts);
1342
1343 for (int j = 0; j < 4; j++) {
1344 cc = listIteratorNext(&context);
1345
1346 if (cc == NULL || (j == 0 && cc->type != numeric_context) ||
1347 (j == 1 && cc->type != noEncoding_context) ||
1348 (j == 2 && cc->type != encodedString_context) ||
1349 (j == 3 && cc->type != encodedString_context)) {
1350 return false;
1351 }
1352 }
1353
1354 entries = id3v2CreateFrameEntryTraverser(f);
1355
1356 encoding = id3v2ReadFrameEntryAsU8(&entries); // encoding
1357
1358 convi = byteConvertTextFormat((unsigned char *) lyrics, BYTE_UTF8, strlen(lyrics), &usableString, encoding,
1359 &outLen);
1360
1361 if (!convi && outLen == 0 && usableString == NULL) {
1362 return false;
1363 }
1364
1365 // data is already in utf8
1366 if (convi && outLen == 0) {
1367 usableString = (uint8_t *) strdup(lyrics);
1368 outLen = strlen(lyrics);
1369 }
1370
1371 bytePrependBOM(encoding, &usableString, &outLen);
1372
1373 // re enable utf16 len support
1374 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
1375 // realloc false positive memory leak as usableString becomes the memory owner
1376 // NOLINTNEXTLINE
1377 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
1378 if (reallocPtr == NULL) {
1379 free(usableString);
1380 return false;
1381 }
1382 usableString = reallocPtr;
1383 memset(usableString + outLen, 0, BYTE_PADDING);
1384 }
1385
1386 id3v2ReadFrameEntryAsU8(&entries); // language
1387 id3v2ReadFrameEntryAsU8(&entries); // description
1388
1389 if (id3v2WriteFrameEntry(f, &entries, byteStrlen(encoding, usableString), (void *) usableString)) {
1390 free(usableString);
1391 // memory is now owned by frame
1392 // NOLINTNEXTLINE
1393 return true;
1394 }
1395
1396 if (usableString != NULL) {
1397 free(usableString);
1398 }
1399
1400 // memory is owned by frame
1401 // NOLINTNEXTLINE
1402 return false;
1403}
1404
1405
1406// internal function -----------------------------------------------------------------------------------------------------------
1407// NOLINTNEXTLINE
1408 static int internal_id3v2CreateCommentFrameUTF16LE(uint8_t v, const char lang[3], const char *desc, const char *comment,
1409 Id3v2Tag *tag) {
1410 // check for legal args
1411 // NOLINTNEXTLINE
1412 if (lang == NULL || desc == NULL || comment == NULL) {
1413 // NOLINTNEXTLINE
1414 return false;
1415 }
1416
1417 if (strlen(lang) != 3 || strlen(comment) == 0) {
1418 return false;
1419 }
1420
1421 Id3v2Frame *f = NULL;
1422 Id3v2FrameHeader *header = NULL;
1423 Id3v2ContentEntry *ce = NULL;
1424 uint8_t *usableString = NULL;
1425 uint8_t encoding = BYTE_UTF16LE;
1426 size_t outLen = 0;
1427 bool convi = false;
1428
1429 // get frame header
1430 switch (v) {
1431 case ID3V2_TAG_VERSION_2:
1432 header = id3v2CreateFrameHeader((uint8_t *) "COM\0", 0, 0, 0, 0, 0, 0, 0);
1433 break;
1434 case ID3V2_TAG_VERSION_3:
1435 case ID3V2_TAG_VERSION_4:
1436 header = id3v2CreateFrameHeader((uint8_t *) "COMM", 0, 0, 0, 0, 0, 0, 0);
1437 break;
1438 default:
1439 return false;
1440 }
1441
1442 // create frame
1443 f = id3v2CreateFrame(header, id3v2CreateCommentFrameContext(),
1444 listCreate(id3v2PrintContentEntry, id3v2DeleteContentEntry, id3v2CompareContentEntry,
1445 id3v2CopyContentEntry));
1446
1447 ce = id3v2CreateContentEntry((void *) &encoding, 1);
1448 listInsertBack(f->entries, (void *) ce);
1449
1450 ce = id3v2CreateContentEntry((void *) lang, 3);
1451 listInsertBack(f->entries, (void *) ce);
1452
1453 // make a description
1454 if (strlen(desc) != 0) {
1455 convi = byteConvertTextFormat((unsigned char *) desc, BYTE_UTF8, strlen(desc), &usableString, encoding,
1456 &outLen);
1457
1458 if (!convi && outLen == 0 && usableString == NULL) {
1459 id3v2DestroyFrame(&f);
1460 return false;
1461 }
1462
1463 // data is already in target encoding
1464 if (convi && outLen == 0) {
1465 usableString = (uint8_t *) strdup(desc);
1466 outLen = strlen(desc);
1467 }
1468
1469 bytePrependBOM(encoding, &usableString, &outLen);
1470
1471 // re enable utf16 len support
1472 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
1473 // realloc false positive memory leak as usableString becomes the memory owner
1474 // NOLINTNEXTLINE
1475 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
1476 if (reallocPtr == NULL) {
1477 free(usableString);
1478 id3v2DestroyFrame(&f);
1479 return false;
1480 }
1481 usableString = reallocPtr;
1482
1483 memset(usableString + outLen, 0, BYTE_PADDING);
1484 }
1485
1486 ce = id3v2CreateContentEntry((void *) usableString, outLen);
1487 listInsertBack(f->entries, (void *) ce);
1488
1489 free(usableString);
1490 usableString = NULL;
1491 outLen = 0;
1492 } else {
1493 ce = id3v2CreateContentEntry("\0", 1);
1494 listInsertBack(f->entries, (void *) ce);
1495 }
1496
1497 convi = byteConvertTextFormat((unsigned char *) comment, BYTE_UTF8, strlen(comment), &usableString, encoding,
1498 &outLen);
1499
1500 if (!convi && outLen == 0 && usableString == NULL) {
1501 id3v2DestroyFrame(&f);
1502 // memory is owned by frame which gets freed - false positive
1503 // NOLINTNEXTLINE
1504 return false;
1505 }
1506
1507 // data is already in target encoding
1508 if (convi && outLen == 0) {
1509 usableString = (uint8_t *) strdup(comment);
1510 outLen = strlen(comment);
1511 }
1512
1513 bytePrependBOM(encoding, &usableString, &outLen);
1514
1515 // re enable utf16 len support
1516 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
1517 // realloc false positive memory leak as usableString becomes the memory owner
1518 // NOLINTNEXTLINE
1519 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
1520 if (reallocPtr == NULL) {
1521 free(usableString);
1522 id3v2DestroyFrame(&f);
1523 // memory is owned by frame which gets freed - false positive
1524 // NOLINTNEXTLINE
1525 return false;
1526 }
1527 usableString = reallocPtr;
1528 memset(usableString + outLen, 0, BYTE_PADDING);
1529 }
1530
1531 // add comment
1532 ce = id3v2CreateContentEntry((void *) usableString, outLen);
1533 listInsertBack(f->entries, (void *) ce);
1534 free(usableString);
1535
1536 listInsertBack(tag->frames, (void *) f);
1537 // memory is owned by frame - false positive
1538 // NOLINTNEXTLINE
1539 return true;
1540}
1541
1553 int id3v2WriteComment(const char *comment, Id3v2Tag *tag) {
1554 if (comment == NULL || tag == NULL) {
1555 return false;
1556 }
1557
1558 // null is an invalid string
1559 if (strlen(comment) == 0) {
1560 return false;
1561 }
1562
1563 Id3v2Frame *f = NULL;
1564 ListIter frames = id3v2CreateFrameTraverser(tag);
1565 ListIter context = {0};
1566 ListIter entries = {0};
1567 Id3v2ContentContext *cc = NULL;
1568 uint8_t encoding = 0;
1569 uint8_t *usableString = NULL;
1570 size_t outLen = 0;
1571 char id[ID3V2_FRAME_ID_MAX_SIZE] = {0, 0, 0, 0};
1572 int i = 0;
1573 bool convi = false;
1574
1575
1576 switch (tag->header->majorVersion) {
1577 case ID3V2_TAG_VERSION_2:
1578 i = ID3V2_FRAME_ID_MAX_SIZE - 1;
1579 memcpy(id, "COM", i);
1580 break;
1581 case ID3V2_TAG_VERSION_3:
1582 case ID3V2_TAG_VERSION_4:
1583 i = ID3V2_FRAME_ID_MAX_SIZE;
1584 memcpy(id, "COMM", i);
1585 break;
1586 default:
1587 return false;
1588 }
1589
1590 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
1591 if (memcmp(id, f->header->id, i) == 0) {
1592 break;
1593 }
1594 }
1595
1596 if (f == NULL) {
1597 return internal_id3v2CreateCommentFrameUTF16LE(tag->header->majorVersion, "zxx", "", comment, tag);
1598 // zxx is no/unknown language
1599 }
1600
1601 // verify frame via context
1602 if (f->contexts->length != 4 && f->entries->length != 4) {
1603 return false;
1604 }
1605
1606 context = listCreateIterator(f->contexts);
1607
1608 for (int j = 0; j < 4; j++) {
1609 cc = listIteratorNext(&context);
1610
1611 if (cc == NULL || (j == 0 && cc->type != numeric_context) ||
1612 (j == 1 && cc->type != noEncoding_context) ||
1613 (j == 2 && cc->type != encodedString_context) ||
1614 (j == 3 && cc->type != encodedString_context)) {
1615 return false;
1616 }
1617 }
1618
1619 entries = id3v2CreateFrameEntryTraverser(f);
1620
1621 encoding = id3v2ReadFrameEntryAsU8(&entries); // encoding
1622
1623 convi = byteConvertTextFormat((unsigned char *) comment, BYTE_UTF8, strlen(comment), &usableString, encoding,
1624 &outLen);
1625
1626 if (!convi && outLen == 0 && usableString == NULL) {
1627 return false;
1628 }
1629
1630 // data is already in utf8
1631 if (convi && outLen == 0) {
1632 usableString = (uint8_t *) strdup(comment);
1633 outLen = strlen(comment);
1634 }
1635
1636 bytePrependBOM(encoding, &usableString, &outLen);
1637
1638 // re enable utf16 len support
1639 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
1640 // realloc false positive memory leak as usableString becomes the memory owner
1641 // NOLINTNEXTLINE
1642 uint8_t *reallocPtr = realloc(usableString, outLen + BYTE_PADDING);
1643 if (reallocPtr == NULL) {
1644 free(usableString);
1645 return false;
1646 }
1647 usableString = reallocPtr;
1648 memset(usableString + outLen, 0, BYTE_PADDING);
1649 }
1650
1651 id3v2ReadFrameEntryAsU8(&entries); // language
1652 id3v2ReadFrameEntryAsU8(&entries); // description
1653
1654 if (id3v2WriteFrameEntry(f, &entries, byteStrlen(encoding, usableString), (void *) usableString)) {
1655 free(usableString);
1656 // memory is now owned by frame
1657 // NOLINTNEXTLINE
1658 return true;
1659 }
1660
1661 if (usableString != NULL) {
1662 free(usableString);
1663 }
1664 // memory is owned by frame
1665 // NOLINTNEXTLINE
1666 return false;
1667}
1668
1669// internal function ---------------------------------------------------------------------------------------------------------
1670// NOLINTNEXTLINE
1671 static int internal_id3v2CreatePictureFrameUTF16LEtype0(uint8_t v, uint8_t *image, size_t imageSize, const char *kind,
1672 Id3v2Tag *tag) {
1673 // check for legal args
1674 // NOLINTNEXTLINE
1675 if (image == NULL || imageSize == 0 || kind == NULL || tag == NULL) {
1676 // NOLINTNEXTLINE
1677 return false;
1678 }
1679
1680 if (strlen(kind) == 0) {
1681 return false;
1682 }
1683
1684 Id3v2Frame *f = NULL;
1685 ListIter entries = {0};
1686 uint8_t encoding = BYTE_UTF16LE;
1687 size_t kindLen = 0;
1688
1689 // create frame
1690 switch (v) {
1691 case ID3V2_TAG_VERSION_2:
1692 f = id3v2CreateEmptyFrame("PIC\0", v, NULL);
1693 break;
1694 case ID3V2_TAG_VERSION_3:
1695 case ID3V2_TAG_VERSION_4:
1696 f = id3v2CreateEmptyFrame("APIC", v, NULL);
1697 break;
1698 default:
1699 return false;
1700 }
1701
1702 if (f == NULL) {
1703 return false;
1704 }
1705
1706 // // update entries
1707 entries = id3v2CreateFrameEntryTraverser(f);
1708
1709 // add encoding
1710 if (!id3v2WriteFrameEntry(f, &entries, 1, (void *) &encoding)) {
1711 id3v2DestroyFrame(&f);
1712 return false;
1713 }
1714 id3v2ReadFrameEntryAsU8(&entries);
1715
1716 switch (tag->header->majorVersion) {
1717 case ID3V2_TAG_VERSION_2:
1718 kindLen = strlen(kind);
1719
1720 if (kindLen > 3) {
1721 kindLen = 3;
1722 }
1723
1724 if (!id3v2WriteFrameEntry(f, &entries, kindLen, (void *) kind)) {
1725 return false;
1726 }
1727
1728 break;
1729 case ID3V2_TAG_VERSION_3:
1730 case ID3V2_TAG_VERSION_4: {
1731 char *mime = NULL;
1732
1733 mime = calloc(sizeof(char), strlen("image/") + strlen(kind) + 1);
1734
1735 memcpy(mime, "image/", strlen("image/"));
1736 // extra + 1 in calloc ensures null termination - false positive below
1737 // NOLINTNEXTLINE
1738 memcpy(mime + strlen("image/"), kind, strlen(kind));
1739
1740 if (!id3v2WriteFrameEntry(f, &entries, strlen(mime), (void *) mime)) {
1741 free(mime);
1742 return false;
1743 }
1744
1745 free(mime);
1746 }
1747 break;
1748 default:
1749 id3v2DestroyFrame(&f);
1750 return false;
1751 }
1752 id3v2ReadFrameEntryAsU8(&entries); // mime type skip
1753
1754 // add type
1755 if (!id3v2WriteFrameEntry(f, &entries, 1, (void *) "\0")) {
1756 id3v2DestroyFrame(&f);
1757 return false;
1758 }
1759 id3v2ReadFrameEntryAsU8(&entries);
1760
1761 // ensure description is nothing
1762 if (!id3v2WriteFrameEntry(f, &entries, 1, (void *) "\0")) {
1763 id3v2DestroyFrame(&f);
1764 return false;
1765 }
1766 id3v2ReadFrameEntryAsU8(&entries);
1767
1768 // write image
1769 if (!id3v2WriteFrameEntry(f, &entries, imageSize, (void *) image)) {
1770 id3v2DestroyFrame(&f);
1771 return false;
1772 }
1773 id3v2ReadFrameEntryAsU8(&entries);
1774
1775
1776 listInsertBack(tag->frames, (void *) f);
1777 return true;
1778}
1779
1794 int id3v2WritePicture(uint8_t *image, size_t imageSize, const char *kind, uint8_t type, Id3v2Tag *tag) {
1795 if (image == NULL || imageSize == 0 || kind == NULL || tag == NULL) {
1796 return false;
1797 }
1798
1799 if (strlen(kind) == 0) {
1800 return false;
1801 }
1802
1803 Id3v2Frame *f = NULL;
1804 ListIter frames = id3v2CreateFrameTraverser(tag);
1805 ListIter context = {0};
1806 ListIter entries = {0};
1807 Id3v2ContentContext *cc = NULL;
1808 uint8_t usableType = ((type > 0x14) ? 0x00 : type); // clamp type
1809 char id[ID3V2_FRAME_ID_MAX_SIZE] = {0, 0, 0, 0};
1810 int i = 0;
1811 int kindLen = 0;
1812
1813 // what id will be used
1814 switch (tag->header->majorVersion) {
1815 case ID3V2_TAG_VERSION_2:
1816 i = ID3V2_FRAME_ID_MAX_SIZE - 1;
1817 memcpy(id, "PIC", i);
1818 break;
1819 case ID3V2_TAG_VERSION_3:
1820 case ID3V2_TAG_VERSION_4:
1821 i = ID3V2_FRAME_ID_MAX_SIZE;
1822 memcpy(id, "APIC", i);
1823 break;
1824 default:
1825 return false;
1826 }
1827
1828 // find frame
1829 while (true) {
1830 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
1831 if (memcmp(id, f->header->id, i) == 0) {
1832 break;
1833 }
1834 }
1835
1836 if (f == NULL) {
1837 return internal_id3v2CreatePictureFrameUTF16LEtype0(tag->header->majorVersion, image, imageSize, kind, tag);
1838 }
1839
1840 // verify frame via context
1841 if (f->contexts->length != 5 && f->entries->length != 5) {
1842 return false;
1843 }
1844
1845 context = listCreateIterator(f->contexts);
1846
1847 for (int j = 0; j < 4; j++) {
1848 cc = listIteratorNext(&context);
1849
1850 if (cc == NULL) {
1851 return false;
1852 }
1853
1854 if ((j == 0 && cc->type != numeric_context) ||
1855 (j == 1 && (cc->type != noEncoding_context && cc->type != latin1Encoding_context)) ||
1856 (j == 2 && cc->type != numeric_context) ||
1857 (j == 3 && cc->type != encodedString_context) ||
1858 // can always be binary context - false positive
1859 // NOLINTNEXTLINE
1860 (j == 4 && cc->type != binary_context)) {
1861 return false;
1862 }
1863 }
1864
1865 entries = id3v2CreateFrameEntryTraverser(f);
1866
1867 id3v2ReadFrameEntryAsU8(&entries); // encoding
1868 id3v2ReadFrameEntryAsU8(&entries); // mime type
1869 if (id3v2ReadFrameEntryAsU8(&entries) == usableType) {
1870 break;
1871 }
1872 }
1873
1874 entries = id3v2CreateFrameEntryTraverser(f);
1875
1876 id3v2ReadFrameEntryAsU8(&entries); // encoding
1877
1878 switch (tag->header->majorVersion) {
1879 case ID3V2_TAG_VERSION_2:
1880 kindLen = (int) strlen(kind);
1881
1882 if (kindLen > 3) {
1883 kindLen = 3;
1884 }
1885
1886 if (!id3v2WriteFrameEntry(f, &entries, kindLen, (void *) kind)) {
1887 return false;
1888 }
1889
1890 break;
1891 case ID3V2_TAG_VERSION_3:
1892 case ID3V2_TAG_VERSION_4: {
1893 char *mime = NULL;
1894
1895 mime = calloc(sizeof(char), strlen("image/") + strlen(kind) + 1);
1896
1897 memcpy(mime, "image/", strlen("image/"));
1898 // extra + 1 in calloc ensures null termination - false positive below
1899 // NOLINTNEXTLINE
1900 memcpy(mime + strlen("image/"), kind, strlen(kind));
1901
1902 if (!id3v2WriteFrameEntry(f, &entries, strlen(mime), (void *) mime)) {
1903 free(mime);
1904 return false;
1905 }
1906
1907 free(mime);
1908 }
1909 break;
1910 default:
1911 return false;
1912 }
1913
1914 id3v2ReadFrameEntryAsU8(&entries); // mime type skip
1915 id3v2ReadFrameEntryAsU8(&entries); // picture type
1916 id3v2ReadFrameEntryAsU8(&entries); // description
1917
1918 return id3v2WriteFrameEntry(f, &entries, imageSize, (void *) image);
1919}
1920
1932 int id3v2WritePictureFromFile(const char *filename, const char *kind, uint8_t type, Id3v2Tag *tag) {
1933 if (filename == NULL || kind == NULL || tag == NULL) {
1934 return false;
1935 }
1936
1937 if (strlen(filename) == 0 || strlen(kind) == 0) {
1938 return false;
1939 }
1940
1941 FILE *f = NULL;
1942 uint8_t *data = NULL;
1943 size_t size = 0;
1944 int ret = 0;
1945
1946 f = fopen(filename, "rb");
1947
1948 if (f == NULL) {
1949 return false;
1950 }
1951
1952 (void) fseek(f, 0, SEEK_END);
1953 size = ftell(f);
1954 (void) fseek(f, 0, SEEK_SET);
1955
1956 data = calloc(sizeof(uint8_t), size);
1957
1958 if (fread(data, sizeof(uint8_t), size, f) != size) {
1959 (void) fclose(f);
1960 free(data);
1961 return false;
1962 }
1963
1964 (void) fclose(f);
1965
1966 ret = id3v2WritePicture(data, size, kind, type, tag);
1967
1968 free(data);
1969
1970 return ret;
1971}
1972
1984uint8_t *id3v2TagSerialize(Id3v2Tag *tag, size_t *outl) {
1985 if (tag == NULL) {
1986 *outl = 0;
1987 return NULL;
1988 }
1989
1990 if (tag->header == NULL || tag->frames == NULL) {
1991 *outl = 0;
1992 return NULL;
1993 }
1994
1995 ByteStream *stream = NULL;
1996 ByteStream *headerStream = NULL;
1997 ByteStream *frameStream = NULL;
1998 ByteStream *footerStream = NULL;
1999 Id3v2Frame *f = NULL;
2000 ListIter frames = id3v2CreateFrameTraverser(tag);
2001 uint32_t fsize = 0;
2002 uint32_t padding = 0;
2003 uint8_t *sizeBytes = NULL;
2004 uint8_t *headerOut = NULL;
2005 uint8_t *out = NULL;
2006 size_t headerOutl = 0;
2007
2008 // frame stream
2009 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
2010 ByteStream *tmp = NULL;
2011 size_t frameOutl = 0;
2012 uint8_t *frameOut = NULL;
2013
2014 frameOut = id3v2FrameSerialize(f, tag->header->majorVersion, &frameOutl);
2015
2016 if (frameOut == NULL && frameOutl == 0) {
2017 break;
2018 }
2019
2020 tmp = byteStreamCreate(frameOut, frameOutl);
2021 free(frameOut);
2022
2023 if (tmp == NULL) {
2024 break;
2025 }
2026
2027 if (frameStream == NULL) {
2028 frameStream = byteStreamCreate(byteStreamCursor(tmp), tmp->bufferSize);
2029 byteStreamSeek(frameStream, 0, SEEK_END);
2030 } else {
2031 byteStreamResize(frameStream, frameStream->bufferSize + tmp->bufferSize);
2032 byteStreamWrite(frameStream, tmp->buffer, tmp->bufferSize);
2033 }
2034 byteStreamDestroy(tmp);
2035 }
2036
2037 // check if above failed somehow
2038 if (frameStream == NULL) {
2039 *outl = 0;
2040 return NULL;
2041 }
2042
2043 byteStreamRewind(frameStream);
2044
2045 // unsync?
2046 if (id3v2ReadUnsynchronisationIndicator(tag->header)) {
2047 ByteStream *tmp = NULL;
2048
2049 tmp = byteStreamCreate(NULL, frameStream->bufferSize * 2);
2050
2051 for (size_t i = 0; i < frameStream->bufferSize; i++) {
2052 byteStreamWrite(tmp, byteStreamCursor(frameStream), 1);
2053
2054 byteStreamSeek(tmp, 1, SEEK_CUR);
2055 byteStreamSeek(frameStream, 1, SEEK_CUR);
2056 }
2057
2058 byteStreamDestroy(frameStream);
2059 frameStream = tmp;
2060 fsize = frameStream->bufferSize;
2061 byteStreamRewind(frameStream);
2062 } else {
2063 fsize = frameStream->bufferSize;
2064 }
2065
2066 // header stream
2067 headerOut = id3v2TagHeaderSerialize(tag->header, 0, &headerOutl);
2068
2069 if (headerOut == NULL && headerOutl == 0) {
2070 byteStreamDestroy(frameStream);
2071 *outl = 0;
2072 return NULL;
2073 }
2074
2075 headerStream = byteStreamCreate(headerOut, headerOutl);
2076 free(headerOut);
2077
2078 // unsync?
2079 if (id3v2ReadUnsynchronisationIndicator(tag->header) && headerStream->bufferSize > 10) {
2080 ByteStream *tmp = NULL;
2081 uint32_t size = 0;
2082
2083 // ext size
2084 size = (headerStream->bufferSize - 10) * 2;
2085
2086 tmp = byteStreamCreate(NULL, 10 + size);
2087
2088 // write header back
2089 byteStreamWrite(tmp, byteStreamCursor(headerStream), 10);
2090 byteStreamSeek(headerStream, 10, SEEK_SET);
2091
2092 for (uint32_t i = 0; i < size; i++) {
2093 byteStreamWrite(tmp, byteStreamCursor(headerStream), 1);
2094
2095 byteStreamSeek(tmp, 1, SEEK_CUR);
2096 byteStreamSeek(headerStream, 1, SEEK_CUR);
2097 }
2098
2099 byteStreamDestroy(headerStream);
2100 headerStream = tmp;
2101 fsize += size;
2102 } else {
2103 fsize += ((headerStream->bufferSize > 10) ? headerStream->bufferSize - 10 : 0);
2104 }
2105
2106 byteStreamRewind(headerStream);
2107
2108 // footer stream?
2109 if (id3v2ReadFooterIndicator(tag->header) && tag->header->majorVersion == ID3V2_TAG_VERSION_4) {
2110 footerStream = byteStreamCreate(byteStreamCursor(headerStream), 10);
2111 byteStreamWrite(footerStream, (unsigned char *) "3DI", ID3V2_TAG_ID_SIZE);
2112 byteStreamRewind(footerStream);
2113 }
2114
2115 if (tag->header->extendedHeader != NULL) {
2116 fsize += tag->header->extendedHeader->padding;
2117 padding = tag->header->extendedHeader->padding;
2118 }
2119
2120 // insert size
2121 sizeBytes = u32tob(byteSyncintEncode(fsize));
2122 byteStreamSeek(headerStream, 6, SEEK_SET);
2123 byteStreamWrite(headerStream, sizeBytes, 4);
2124 byteStreamRewind(headerStream);
2125
2126 if (footerStream != NULL) {
2127 byteStreamSeek(footerStream, 6, SEEK_SET);
2128 byteStreamWrite(footerStream, sizeBytes, 4);
2129 byteStreamRewind(footerStream);
2130 }
2131
2132 free(sizeBytes);
2133
2134 // create stream
2135 stream = byteStreamCreate(
2136 NULL, frameStream->bufferSize + headerStream->bufferSize + ((footerStream != NULL)
2137 ? footerStream->bufferSize
2138 : 0));
2139 byteStreamWrite(stream, byteStreamCursor(headerStream), headerStream->bufferSize);
2140 byteStreamWrite(stream, byteStreamCursor(frameStream), frameStream->bufferSize);
2141 byteStreamSeek(stream, padding, SEEK_CUR);
2142 if (footerStream != NULL) {
2143 byteStreamWrite(stream, byteStreamCursor(footerStream), footerStream->bufferSize);
2144 byteStreamDestroy(footerStream);
2145 }
2146
2147 byteStreamDestroy(headerStream);
2148 byteStreamDestroy(frameStream);
2149 byteStreamRewind(stream);
2150
2151 out = calloc(stream->bufferSize, sizeof(uint8_t));
2152 *outl = stream->bufferSize;
2153 byteStreamRead(stream, out, stream->bufferSize);
2154 byteStreamDestroy(stream);
2155
2156 return out;
2157}
2158
2184 char *id3v2TagToJSON(Id3v2Tag *tag) {
2185 char *json = NULL;
2186 char *headerJson = NULL;
2187 char **contentJson = NULL;
2188 char *concatenatedString = NULL;
2189 size_t contentJsonSize = 0;
2190 size_t memCount = 3;
2191
2192 if (tag == NULL) {
2193 json = calloc(memCount, sizeof(char));
2194 memcpy(json, "{}\0", memCount);
2195 return json;
2196 }
2197
2198 if (tag->frames == NULL || tag->header == NULL) {
2199 json = calloc(memCount, sizeof(char));
2200 memcpy(json, "{}\0", memCount);
2201 return json;
2202 }
2203
2204 if (tag->header->majorVersion > ID3V2_TAG_VERSION_4) {
2205 json = calloc(memCount, sizeof(char));
2206 memcpy(json, "{}\0", memCount);
2207 return json;
2208 }
2209
2210 // get frames
2211
2212 ListIter frames = id3v2CreateFrameTraverser(tag);
2213 Id3v2Frame *f = NULL;
2214
2215 while ((f = id3v2FrameTraverse(&frames)) != NULL) {
2216 char *tmp = NULL;
2217 size_t jsonSize = 0;
2218
2219 tmp = id3v2FrameToJSON(f, tag->header->majorVersion);
2220 jsonSize = strlen(tmp);
2221
2222 contentJsonSize++;
2223 if (contentJson == NULL) {
2224 // first allocation and returned by the function - false positive
2225 // NOLINTNEXTLINE
2226 contentJson = (char **) calloc(contentJsonSize, sizeof(char *));
2227 contentJson[contentJsonSize - 1] = calloc(jsonSize + 1, sizeof(char));
2228 } else {
2229 // realloc false positive memory leak - false positive below
2230 // NOLINTNEXTLINE
2231 char **reallocPtr = realloc(contentJson, (contentJsonSize) * sizeof(char *));
2232 if (reallocPtr == NULL) {
2233 for (size_t i = 0; i < contentJsonSize - 1; i++) {
2234 free(contentJson[i]);
2235 }
2236 free((void *) contentJson);
2237 free(tmp);
2238 // all memory freed above - false positive
2239 // NOLINTNEXTLINE
2240 return NULL;
2241 }
2242 contentJson = reallocPtr;
2243 contentJson[contentJsonSize - 1] = calloc(jsonSize + 1, sizeof(char));
2244 }
2245
2246 memcpy(contentJson[contentJsonSize - 1], tmp, jsonSize);
2247
2248 free(tmp);
2249 }
2250
2251 // get header
2252 headerJson = id3v2TagHeaderToJSON(tag->header);
2253
2254 // concatenate all JSON data stored in contentJson into a single string split by ","
2255 if (contentJson != NULL) {
2256 size_t concatenatedStringLength = 0;
2257 for (size_t i = 0; i < contentJsonSize; i++) {
2258 concatenatedStringLength += strlen(contentJson[i]) + 1;
2259 }
2260
2261 concatenatedString = calloc(concatenatedStringLength + 1, sizeof(char));
2262
2263 size_t offset = 0;
2264 for (size_t i = 0; i < contentJsonSize; i++) {
2265 if (i > 0) {
2266 concatenatedString[offset++] = ',';
2267 }
2268
2269 const size_t currentLen = strlen(contentJson[i]);
2270 memcpy(concatenatedString + offset, contentJson[i], currentLen);
2271 offset += currentLen;
2272 }
2273 concatenatedString[offset] = '\0';
2274 }
2275
2276 memCount += snprintf(NULL, 0,
2277 "{\"header\":%s,\"content\":[%s]}",
2278 headerJson,
2279 concatenatedString);
2280 json = calloc(memCount + 1, sizeof(char));
2281 (void) snprintf(json, memCount,
2282 "{\"header\":%s,\"content\":[%s]}",
2283 headerJson,
2284 concatenatedString);
2285
2286
2287 free(headerJson);
2288
2289 if (concatenatedString != NULL) {
2290 free(concatenatedString);
2291 }
2292
2293 if (contentJson != NULL) {
2294 for (size_t i = 0; i < contentJsonSize; i++) {
2295 free(contentJson[i]);
2296 }
2297 free((void *) contentJson);
2298 }
2299
2300 // all memory is freed of moved to contentJson - false positive
2301 // NOLINTNEXTLINE
2302 return json;
2303}
2304
2317 int id3v2WriteTagToFile(const char *filePath, Id3v2Tag *tag) {
2318 if (filePath == NULL || tag == NULL) {
2319 return false;
2320 }
2321
2322 FILE *fp = NULL;
2323 uint8_t *out = NULL;
2324 size_t outl = 0;
2325 ByteStream *stream = NULL;
2326
2327 out = id3v2TagSerialize(tag, &outl);
2328
2329 if (out == NULL && outl == 0) {
2330 return false;
2331 }
2332
2333 stream = byteStreamCreate(out, outl);
2334 free(out);
2335
2336 fp = fopen(filePath, "r+b");
2337
2338 // write to a new file
2339 if (fp == NULL) {
2340 // create a new file and write the bytes to it
2341 fp = fopen(filePath, "wb");
2342
2343 if (fp == NULL) {
2344 byteStreamDestroy(stream);
2345 return false;
2346 }
2347
2348 if ((fwrite(byteStreamCursor(stream), 1, stream->bufferSize, fp)) == 0) {
2349 byteStreamDestroy(stream);
2350 (void) fclose(fp);
2351 return false;
2352 }
2353
2354 // update file
2355 } else {
2356 char id[ID3V2_TAG_ID_SIZE] = {0};
2357 bool hasTag = false;
2358 long fileSize = 0;
2359 bool prepend = 0;
2360 uint8_t *tmp = NULL;
2361 uint8_t *upperTmp = NULL;
2362 size_t upperBytes = 0;
2363 uint32_t oldTagSize = 0;
2364
2365 // does the tag exist?
2366 while (hasTag == false && feof(fp) == 0) {
2367 const size_t bytesRead = fread(id, sizeof(char), ID3V2_TAG_ID_SIZE, fp);
2368 upperBytes += bytesRead;
2369 if (bytesRead != ID3V2_TAG_ID_SIZE) {
2370 if (ferror(fp)) {
2371 break;
2372 }
2373 break;
2374 }
2375
2376 hasTag = (memcmp(id, "ID3", ID3V2_TAG_ID_SIZE) == 0);
2377 }
2378
2379 // 1. update flag is set
2380 if (hasTag == true && tag->header->extendedHeader != NULL) {
2381 prepend = false;
2382
2383 if (tag->header->extendedHeader->update == true) {
2384 prepend = true;
2385 }
2386
2387 // 2. a tag exists
2388 } else if (hasTag == true) {
2389 prepend = false;
2390
2391 // get the tag size
2392 (void) fseek(fp, 3, SEEK_CUR); // skips version and flag
2393 tmp = malloc(4);
2394 (void) fread(tmp, 1, 4, fp);
2395 oldTagSize = byteSyncintDecode(btou32(tmp, 4));
2396 free(tmp);
2397
2398 // 3. no tag exists
2399 } else {
2400 prepend = true;
2401 }
2402
2403 // correct for ID3 tag
2404 upperBytes = (upperBytes > ID3V2_TAG_ID_SIZE) ? upperBytes - ID3V2_TAG_ID_SIZE : 0;
2405
2406 // get file size
2407 (void) fseek(fp, 0, SEEK_END);
2408 fileSize = ftell(fp);
2409 (void) fseek(fp, 0, SEEK_SET);
2410
2411 // read the file
2412 tmp = malloc(fileSize);
2413 if (fread(tmp, 1, fileSize, fp) != fileSize) {
2414 free(tmp);
2415 (void) fclose(fp);
2416 byteStreamDestroy(stream);
2417 return 0;
2418 }
2419
2420 (void) fseek(fp, 0, SEEK_SET);
2421
2422 if (prepend) {
2423 // write the stream to a file
2424 if (fwrite(byteStreamCursor(stream), 1, stream->bufferSize, fp) != stream->bufferSize) {
2425 free(tmp);
2426 (void) fclose(fp);
2427 byteStreamDestroy(stream);
2428 return 0;
2429 }
2430
2431 // write the existing file data back to the file
2432 if (fwrite(tmp, 1, fileSize, fp) != fileSize) {
2433 free(tmp);
2434 (void) fclose(fp);
2435 byteStreamDestroy(stream);
2436 return 0;
2437 }
2438
2439 free(tmp);
2440 } else {
2441 uint32_t offset = 10;
2442
2443 if (id3v2ReadFooterIndicator(tag->header) && tag->header->majorVersion == ID3V2_TAG_VERSION_4) {
2444 offset += 10;
2445 }
2446
2447 if (tag->header->extendedHeader != NULL) {
2448 if (tag->header->extendedHeader->padding > 0) {
2449 offset += tag->header->extendedHeader->padding;
2450 }
2451 }
2452
2453 // prepend data above the tag
2454 if (upperBytes > 0) {
2455 upperTmp = calloc(sizeof(uint8_t), upperBytes);
2456
2457 if (fread(upperTmp, sizeof(char), upperBytes, fp) != upperBytes) {
2458 free(upperTmp);
2459 free(tmp);
2460 (void) fclose(fp);
2461 byteStreamDestroy(stream);
2462 return false;
2463 }
2464 free(upperTmp);
2465
2466 (void) fseek(fp, 0, SEEK_SET);
2467 }
2468
2469 // write the stream to a file
2470 if (fwrite(byteStreamCursor(stream), 1, stream->bufferSize, fp) != stream->bufferSize) {
2471 free(tmp);
2472 (void) fclose(fp);
2473 byteStreamDestroy(stream);
2474 return 0;
2475 }
2476
2477 // no need to read the old tag
2478 fileSize = fileSize - ((long) (oldTagSize + offset)) - (long) upperBytes;
2479
2480 if (fwrite(tmp + upperBytes + oldTagSize + offset, 1, fileSize, fp)) {
2481 free(tmp);
2482 (void) fclose(fp);
2483 byteStreamDestroy(stream);
2484 return 0;
2485 }
2486
2487 free(tmp);
2488 }
2489 }
2490
2491 (void) fclose(fp);
2492 byteStreamDestroy(stream);
2493 return true;
2494}
id3v2WriteLyrics
int id3v2WriteLyrics(const char *lyrics, Id3v2Tag *tag)
Writes lyrics to the appropriate unsynchronized lyrics frame in a tag.
Definition id3v2.c:1290
id3v2ReadAlbum
char * id3v2ReadAlbum(Id3v2Tag *tag)
Extracts the album name from an ID3v2 tag.
Definition id3v2.c:483
id3v2TagSerialize
uint8_t * id3v2TagSerialize(Id3v2Tag *tag, size_t *outl)
Serializes an ID3v2 tag structure to its binary representation.
Definition id3v2.c:1984
id3v2WriteGenre
int id3v2WriteGenre(const char *genre, Id3v2Tag *tag)
Writes the genre/content type to an ID3v2 tag.
Definition id3v2.c:1077
id3v2CompareTag
bool id3v2CompareTag(Id3v2Tag *tag1, Id3v2Tag *tag2)
Performs deep structural comparison of two ID3v2 tags for equality.
Definition id3v2.c:89
id3v2WritePictureFromFile
int id3v2WritePictureFromFile(const char *filename, const char *kind, uint8_t type, Id3v2Tag *tag)
Reads a picture from a file and writes it to the appropriate attached picture frame in a tag.
Definition id3v2.c:1932
id3v2ReadGenre
char * id3v2ReadGenre(Id3v2Tag *tag)
Extracts the genre/content type from an ID3v2 tag.
Definition id3v2.c:541
id3v2WriteTagToFile
int id3v2WriteTagToFile(const char *filePath, Id3v2Tag *tag)
Writes an ID3v2 tag to a file, creating, prepending, or replacing as needed.
Definition id3v2.c:2317
id3v2WriteYear
int id3v2WriteYear(const char *year, Id3v2Tag *tag)
Writes the year to an ID3v2 tag.
Definition id3v2.c:1051
id3v2ReadTrack
char * id3v2ReadTrack(Id3v2Tag *tag)
Extracts the track number/position from an ID3v2 tag.
Definition id3v2.c:570
id3v2ReadYear
char * id3v2ReadYear(Id3v2Tag *tag)
Extracts the year from an ID3v2 tag.
Definition id3v2.c:512
id3v2ReadTitle
char * id3v2ReadTitle(Id3v2Tag *tag)
Extracts the title/song name from an ID3v2 tag.
Definition id3v2.c:396
id3v2ReadAlbumArtist
char * id3v2ReadAlbumArtist(Id3v2Tag *tag)
Extracts the album artist/band/orchestra from an ID3v2 tag.
Definition id3v2.c:454
id3v2WritePicture
int id3v2WritePicture(uint8_t *image, size_t imageSize, const char *kind, uint8_t type, Id3v2Tag *tag)
Writes a picture to the appropriate attached picture frame in a tag.
Definition id3v2.c:1794
id3v2ReadComment
char * id3v2ReadComment(Id3v2Tag *tag)
Extracts the comment text from an ID3v2 tag.
Definition id3v2.c:727
id3v2ReadPicture
uint8_t * id3v2ReadPicture(uint8_t type, const Id3v2Tag *tag, size_t *dataSize)
Extracts picture/artwork data of a specific type from an ID3v2 tag.
Definition id3v2.c:797
id3v2ReadTextFrameContent
char * id3v2ReadTextFrameContent(const char id[ID3V2_FRAME_ID_MAX_SIZE], Id3v2Tag *tag)
Extracts the text content from a text frame with the specified ID.
Definition id3v2.c:337
id3v2WriteTextFrameContent
int id3v2WriteTextFrameContent(const char id[ID3V2_FRAME_ID_MAX_SIZE], const char *string, Id3v2Tag *tag)
Writes UTF-8 text content to a text frame, creating it if necessary.
Definition id3v2.c:836
id3v2ReadComposer
char * id3v2ReadComposer(Id3v2Tag *tag)
Extracts the composer from an ID3v2 tag.
Definition id3v2.c:599
id3v2ReadTagVersion
int id3v2ReadTagVersion(const Id3v2Tag *tag)
Retrieves the major version number from an ID3v2 tag.
Definition id3v2.c:316
id3v2WriteAlbumArtist
int id3v2WriteAlbumArtist(const char *albumArtist, Id3v2Tag *tag)
Writes the album artist/band/orchestra to an ID3v2 tag.
Definition id3v2.c:999
id3v2ReadArtist
char * id3v2ReadArtist(Id3v2Tag *tag)
Extracts the lead artist/performer from an ID3v2 tag.
Definition id3v2.c:425
id3v2InsertTextFrame
int id3v2InsertTextFrame(const char id[ID3V2_FRAME_ID_MAX_SIZE], const uint8_t encoding, const char *string, Id3v2Tag *tag)
Creates and inserts a new text frame with the specified encoding into a tag.
Definition id3v2.c:235
id3v2WriteDisc
int id3v2WriteDisc(const char *disc, Id3v2Tag *tag)
Writes a disc number to the appropriate disc number frame in a tag.
Definition id3v2.c:1129
id3v2ReadDisc
char * id3v2ReadDisc(Id3v2Tag *tag)
Extracts the disc number/position from an ID3v2 tag.
Definition id3v2.c:628
id3v2WriteComposer
int id3v2WriteComposer(const char *composer, Id3v2Tag *tag)
Writes a composer name to the appropriate composer frame in a tag.
Definition id3v2.c:1155
id3v2TagFromFile
Id3v2Tag * id3v2TagFromFile(const char *filename)
Reads and parses an ID3v2 tag from a file.
Definition id3v2.c:31
id3v2CopyTag
Id3v2Tag * id3v2CopyTag(const Id3v2Tag *toCopy)
Creates a deep copy of an ID3v2 tag structure.
Definition id3v2.c:57
id3v2ReadFrameByID
Id3v2Frame * id3v2ReadFrameByID(const char id[ID3V2_FRAME_ID_MAX_SIZE], Id3v2Tag *tag)
Searches for and returns a copy of the first frame matching the given ID.
Definition id3v2.c:173
id3v2WriteTitle
int id3v2WriteTitle(const char *title, Id3v2Tag *tag)
Writes the title/song name to an ID3v2 tag.
Definition id3v2.c:947
id3v2TagToJSON
char * id3v2TagToJSON(Id3v2Tag *tag)
Serializes an ID3v2 tag structure to JSON format.
Definition id3v2.c:2184
id3v2WriteArtist
int id3v2WriteArtist(const char *artist, Id3v2Tag *tag)
Writes the lead artist/performer to an ID3v2 tag.
Definition id3v2.c:973
id3v2WriteTrack
int id3v2WriteTrack(const char *track, Id3v2Tag *tag)
Writes the track number/position to an ID3v2 tag.
Definition id3v2.c:1103
id3v2WriteAlbum
int id3v2WriteAlbum(const char *album, Id3v2Tag *tag)
Writes the album name to an ID3v2 tag.
Definition id3v2.c:1025
id3v2RemoveFrameByID
int id3v2RemoveFrameByID(const char *id, Id3v2Tag *tag)
Locates and removes the first frame matching the given ID from a tag.
Definition id3v2.c:208
id3v2ReadLyrics
char * id3v2ReadLyrics(Id3v2Tag *tag)
Extracts unsynchronised lyrics/text transcription from an ID3v2 tag.
Definition id3v2.c:658
id3v2WriteComment
int id3v2WriteComment(const char *comment, Id3v2Tag *tag)
Writes a comment to the appropriate comment frame in a tag.
Definition id3v2.c:1553
id3v2.h
Declarations of ID3v2 tag operations including reading/writing metadata frames, tag serialization,...
id3v2Context.h
Function definitions for ID3v2 frame context definitions and parsing configuration.
id3v2CreateCommentFrameContext
List * id3v2CreateCommentFrameContext(void)
Creates a context definition list for comment frames (COMM).
Definition id3v2Context.c:464
id3v2CreateTextFrameContext
List * id3v2CreateTextFrameContext(void)
Context list generation.
Definition id3v2Context.c:243
id3v2Frame.h
Function definitions for ID3v2 frame lifecycle, traversal, serialization, and content management.
id3v2CompareFrame
int id3v2CompareFrame(const void *first, const void *second)
Performs deep comparison of two ID3v2 frame structures.
Definition id3v2Frame.c:241
id3v2WriteFrameEntry
bool id3v2WriteFrameEntry(Id3v2Frame *frame, ListIter *entries, size_t entrySize, const void *entry)
Writes data to the entry at the iterator's current position, clamping size to context constraints.
Definition id3v2Frame.c:865
id3v2FrameSerialize
uint8_t * id3v2FrameSerialize(Id3v2Frame *frame, uint8_t version, size_t *outl)
Serializes a complete ID3v2 frame to binary format according to the specified version.
Definition id3v2Frame.c:1260
id3v2CopyContentEntry
void * id3v2CopyContentEntry(const void *toBeCopied)
Creates a deep copy of a content entry.
Definition id3v2Frame.c:196
id3v2CreateFrameTraverser
ListIter id3v2CreateFrameTraverser(Id3v2Tag *tag)
Creates a list iterator for traversing frames in an ID3v2 tag.
Definition id3v2Frame.c:524
id3v2FrameToJSON
char * id3v2FrameToJSON(Id3v2Frame *frame, uint8_t version)
Converts a complete ID3v2 frame structure to its JSON representation.
Definition id3v2Frame.c:1843
id3v2ReadFrameEntry
void * id3v2ReadFrameEntry(ListIter *traverser, size_t *dataSize)
Reads and returns a deep copy of the current entry's data, advancing the iterator.
Definition id3v2Frame.c:591
id3v2CreateEmptyFrame
Id3v2Frame * id3v2CreateEmptyFrame(const char id[ID3V2_FRAME_ID_MAX_SIZE], uint8_t version, HashTable *userPairs)
Creates an empty frame structure with zero-initialized entries based on context lookup.
Definition id3v2Frame.c:435
id3v2CreateFrameHeader
Id3v2FrameHeader * id3v2CreateFrameHeader(uint8_t id[ID3V2_FRAME_ID_MAX_SIZE], bool tagAlter, bool fileAlter, bool readOnly, bool unsync, uint32_t decompressionSize, uint8_t encryptionSymbol, uint8_t groupSymbol)
Creates an ID3v2 frame header structure with specified flags and metadata.
Definition id3v2Frame.c:68
id3v2ReadFrameEntryAsChar
char * id3v2ReadFrameEntryAsChar(ListIter *traverser, size_t *dataSize)
Reads a frame entry as a UTF-8 encoded string with escaped special characters, advancing the iterator...
Definition id3v2Frame.c:632
id3v2CreateFrame
Id3v2Frame * id3v2CreateFrame(Id3v2FrameHeader *header, List *context, List *entries)
Creates an ID3v2 frame structure from provided components.
Definition id3v2Frame.c:391
id3v2CreateFrameEntryTraverser
ListIter id3v2CreateFrameEntryTraverser(Id3v2Frame *frame)
Creates a list iterator for traversing content entries within a frame.
Definition id3v2Frame.c:563
id3v2CopyFrame
void * id3v2CopyFrame(const void *toBeCopied)
Creates a deep copy of an ID3v2 frame structure.
Definition id3v2Frame.c:364
id3v2CreateContentEntry
Id3v2ContentEntry * id3v2CreateContentEntry(void *entry, size_t size)
Creates a content entry structure with a deep copy of the provided data.
Definition id3v2Frame.c:115
id3v2PrintContentEntry
char * id3v2PrintContentEntry(const void *toBePrinted)
Generates a string representation of a content entry for debugging.
Definition id3v2Frame.c:175
id3v2DestroyFrame
void id3v2DestroyFrame(Id3v2Frame **toDelete)
Frees all memory allocated for an ID3v2 frame structure and nullifies the pointer.
Definition id3v2Frame.c:409
id3v2DeleteContentEntry
void id3v2DeleteContentEntry(void *toBeDeleted)
Frees all memory allocated for a content entry structure.
Definition id3v2Frame.c:210
id3v2CompareContentEntry
int id3v2CompareContentEntry(const void *first, const void *second)
Compares two content entries byte-by-byte and returns the difference.
Definition id3v2Frame.c:145
id3v2DetachFrameFromTag
Id3v2Frame * id3v2DetachFrameFromTag(Id3v2Tag *tag, Id3v2Frame *frame)
Removes a frame from a tag's frames list and returns it to the caller.
Definition id3v2Frame.c:978
id3v2ReadFrameEntryAsU8
uint8_t id3v2ReadFrameEntryAsU8(ListIter *traverser)
Reads the first byte of the current entry as an 8-bit unsigned integer, advancing the iterator.
Definition id3v2Frame.c:748
id3v2FrameTraverse
Id3v2Frame * id3v2FrameTraverse(ListIter *traverser)
Advances the iterator and returns the next frame in the list.
Definition id3v2Frame.c:549
id3v2Parser.h
Definitions for binary parsing of ID3v2 tags, headers, frames, and complete tag structures from byte ...
id3v2ParseTagFromBuffer
Id3v2Tag * id3v2ParseTagFromBuffer(uint8_t *in, size_t inl, HashTable *userPairs)
Parses a complete ID3v2 tag from a byte buffer, including header, optional extended header,...
Definition id3v2Parser.c:1001
id3v2TagIdentity.h
Function definitions of ID3v2 tag header and tag structure creation, manipulation,...
id3v2TagHeaderToJSON
char * id3v2TagHeaderToJSON(const Id3v2TagHeader *header)
Converts an ID3v2 tag header structure to JSON string representation.
Definition id3v2TagIdentity.c:980
id3v2CreateTag
Id3v2Tag * id3v2CreateTag(Id3v2TagHeader *header, List *frames)
Creates and allocates an ID3v2 tag structure.
Definition id3v2TagIdentity.c:630
id3v2ReadFooterIndicator
int id3v2ReadFooterIndicator(Id3v2TagHeader *header)
Reads the footer presence flag from an ID3v2.4 tag header.
Definition id3v2TagIdentity.c:256
id3v2CreateExtendedTagHeader
Id3v2ExtendedTagHeader * id3v2CreateExtendedTagHeader(uint32_t padding, uint32_t crc, bool update, bool tagRestrictions, uint8_t restrictions)
Creates and allocates an ID3v2 extended tag header structure.
Definition id3v2TagIdentity.c:280
id3v2ReadUnsynchronisationIndicator
int id3v2ReadUnsynchronisationIndicator(Id3v2TagHeader *header)
Reads the unsynchronisation flag from a tag header.
Definition id3v2TagIdentity.c:176
id3v2CreateTagHeader
Id3v2TagHeader * id3v2CreateTagHeader(uint8_t majorVersion, uint8_t minorVersion, uint8_t flags, Id3v2ExtendedTagHeader *extendedHeader)
Creates and allocates an ID3v2 tag header structure.
Definition id3v2TagIdentity.c:29
id3v2TagHeaderSerialize
uint8_t * id3v2TagHeaderSerialize(Id3v2TagHeader *header, uint32_t uintSize, size_t *outl)
Serializes an ID3v2 tag header into a byte stream.
Definition id3v2TagIdentity.c:883
ID3V2_TAG_VERSION_3
#define ID3V2_TAG_VERSION_3
ID3v2.3 major version number (3)
Definition id3v2Types.h:43
Id3v2ContentEntry
struct _Id3v2ContentEntry Id3v2ContentEntry
Parsed data field from an ID3v2 frame.
Id3v2ContentContext
struct _Id3v2ContentContext Id3v2ContentContext
Parsing instructions for a single field within an ID3v2 frame.
Id3v2Tag
struct _Id3v2Tag Id3v2Tag
Complete ID3v2 tag structure containing header and metadata frames.
ID3V2_FRAME_ID_MAX_SIZE
#define ID3V2_FRAME_ID_MAX_SIZE
Maximum size in bytes for a frame ID field (4 bytes).
Definition id3v2Types.h:70
ID3V2_TAG_VERSION_2
#define ID3V2_TAG_VERSION_2
ID3v2.2 major version number (2)
Definition id3v2Types.h:40
noEncoding_context
@ noEncoding_context
Raw character data with no encoding or null terminator.
Definition id3v2Types.h:173
numeric_context
@ numeric_context
Integer values of 8, 16, 32, or 64 bits.
Definition id3v2Types.h:197
encodedString_context
@ encodedString_context
Text string with encoding determined by prior context with the key 'encoding'.
Definition id3v2Types.h:185
latin1Encoding_context
@ latin1Encoding_context
Latin-1 (ISO-8859-1) null-terminated string.
Definition id3v2Types.h:191
binary_context
@ binary_context
Binary data block with no terminator.
Definition id3v2Types.h:179
Id3v2Frame
struct _Id3v2Frame Id3v2Frame
Complete ID3v2 frame structure with header, parsing contexts, and data.
ID3V2_TAG_ID_SIZE
#define ID3V2_TAG_ID_SIZE
Size in bytes of the ID3v2 tag identifier "ID3" or "3DI" (3 bytes)
Definition id3v2Types.h:31
ID3V2_TAG_VERSION_4
#define ID3V2_TAG_VERSION_4
ID3v2.4 major version number (4)
Definition id3v2Types.h:46
Id3v2FrameHeader
struct _Id3v2FrameHeader Id3v2FrameHeader
ID3v2 frame header containing identification and processing flags.
Id3v2TagHeader
struct _Id3v2TagHeader Id3v2TagHeader
ID3v2 tag header containing version and parsing information.
_Id3v2ContentContext::type
Id3v2ContextType type
Context type determining parsing behavior (string, binary, numeric, etc.)
Definition id3v2Types.h:239
_Id3v2ExtendedTagHeader::restrictions
uint8_t restrictions
Bitfield defining tag restrictions in format ppqrrstt (ID3v2.4 only).
Definition id3v2Types.h:102
_Id3v2ExtendedTagHeader::crc
uint32_t crc
CRC-32 checksum of the tag's audio data for integrity verification.
Definition id3v2Types.h:89
_Id3v2ExtendedTagHeader::padding
uint32_t padding
Size of the extended header as either a 32-bit integer or syncsafe integer depending on version.
Definition id3v2Types.h:86
_Id3v2ExtendedTagHeader::update
bool update
Indicates this tag is an update to a previous tag (ID3v2.4 only)
Definition id3v2Types.h:92
_Id3v2ExtendedTagHeader::tagRestrictions
bool tagRestrictions
Indicates whether tag restrictions are applied.
Definition id3v2Types.h:95
_Id3v2FrameHeader::id
uint8_t id[ID3V2_FRAME_ID_MAX_SIZE]
Frame identifier (e.g., "TIT2" for title, "TALB" for album). 3 bytes in v2.2, 4 bytes in v2....
Definition id3v2Types.h:134
_Id3v2Frame::header
Id3v2FrameHeader * header
Frame header containing ID, flags, and processing parameters.
Definition id3v2Types.h:279
_Id3v2Frame::contexts
List * contexts
Linked list of Id3v2ContentContext parsing instructions defining frame field structure.
Definition id3v2Types.h:282
_Id3v2Frame::entries
List * entries
Linked list of Id3v2ContentEntry parsed data fields corresponding to contexts.
Definition id3v2Types.h:285
_Id3v2TagHeader::minorVersion
uint8_t minorVersion
Minor version/revision number.
Definition id3v2Types.h:116
_Id3v2TagHeader::extendedHeader
Id3v2ExtendedTagHeader * extendedHeader
Pointer to optional extended header structure. NULL if extended header flag not set.
Definition id3v2Types.h:125
_Id3v2TagHeader::majorVersion
uint8_t majorVersion
Major version number (2, 3, or 4 for ID3v2.2, ID3v2.3, ID3v2.4)
Definition id3v2Types.h:113
_Id3v2TagHeader::flags
uint8_t flags
Bitfield in format abcd0000 defining extra format and feature options of a tag.
Definition id3v2Types.h:122
_Id3v2Tag::frames
List * frames
Linked list of Id3v2Frame structures containing all tag metadata.
Definition id3v2Types.h:299
_Id3v2Tag::header
Id3v2TagHeader * header
Tag header with version, flags, and optional extended header.
Definition id3v2Types.h:296