id3dev 26.01 An ID3 metadata library

Loading...
Searching...
No Matches
id3v2Frame.c
Go to the documentation of this file.
1
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <limits.h>
17#include "id3v2/id3v2Frame.h"
18#include "id3v2/id3v2Context.h"
19#include "id3dependencies/ByteStream/include/byteInt.h"
20#include "id3dependencies/ByteStream/include/byteUnicode.h"
21#include "id3dependencies/ByteStream/include/byteStream.h"
22
23 static char *internal_base64Encode(const unsigned char *input, size_t inputLength) {
24 static const unsigned char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
25 const size_t outputLength = 4 * ((inputLength + 2) / 3);
26 unsigned char *output = malloc(outputLength + 1);
27
28 if (output == NULL) {
29 return NULL;
30 }
31
32 size_t i = 0;
33 size_t j = 0;
34 for (i = 0, j = 0; i < inputLength; i += 3, j += 4) {
35 const unsigned int byte1 = input[i];
36 const unsigned int byte2 = (i + 1 < inputLength) ? input[i + 1] : 0;
37 const unsigned int byte3 = (i + 2 < inputLength) ? input[i + 2] : 0;
38
39 output[j] = base64Chars[byte1 >> 2];
40 output[j + 1] = base64Chars[((byte1 & 0x03) << 4) | (byte2 >> 4)];
41 output[j + 2] = (i + 1 < inputLength) ? base64Chars[((byte2 & 0x0F) << 2) | (byte3 >> 6)] : (unsigned char) '=';
42 output[j + 3] = (i + 2 < inputLength) ? base64Chars[byte3 & 0x3F] : (unsigned char) '=';
43 }
44
45 output[outputLength] = '\0';
46 return (char *) output;
47}
48
49
68 Id3v2FrameHeader *id3v2CreateFrameHeader(uint8_t id[ID3V2_FRAME_ID_MAX_SIZE], bool tagAlter, bool fileAlter,
69 bool readOnly, bool unsync, uint32_t decompressionSize,
70 uint8_t encryptionSymbol, uint8_t groupSymbol) {
71 Id3v2FrameHeader *h = malloc(sizeof(Id3v2FrameHeader));
72
73 for (int i = 0; i < ID3V2_FRAME_ID_MAX_SIZE; i++) {
74 h->id[i] = id[i];
75 }
76
77 h->tagAlterPreservation = tagAlter;
78 h->fileAlterPreservation = fileAlter;
79 h->readOnly = readOnly;
80 h->decompressionSize = decompressionSize;
81 h->encryptionSymbol = encryptionSymbol;
82 h->groupSymbol = groupSymbol;
83 h->unsynchronisation = unsync;
84
85 return h;
86}
87
96 void id3v2DestroyFrameHeader(Id3v2FrameHeader **toDelete) {
97 if (*toDelete) {
98 free(*toDelete);
99 *toDelete = NULL;
100 toDelete = NULL;
101 }
102}
103
115 Id3v2ContentEntry *id3v2CreateContentEntry(void *entry, size_t size) {
116 Id3v2ContentEntry *ce = malloc(sizeof(Id3v2ContentEntry));
117
118 if (!size) {
119 ce->entry = NULL;
120 ce->size = 0;
121 } else {
122 void *tmp = malloc(size);
123 memcpy(tmp, entry, size);
124
125 ce->entry = tmp;
126 ce->size = size;
127 }
128
129 return ce;
130}
131
145 int id3v2CompareContentEntry(const void *first, const void *second) {
146 const Id3v2ContentEntry *one = (Id3v2ContentEntry *) first;
147 const Id3v2ContentEntry *two = (Id3v2ContentEntry *) second;
148
149 if (one == NULL || two == NULL) {
150 return 1;
151 }
152
153 int diff = 0;
154 const size_t usableSize = (one->size <= two->size) ? one->size : two->size;
155 for (size_t i = 0; i < usableSize; i++) {
156 if (((unsigned char *) one->entry)[i] != ((unsigned char *) two->entry)[i]) {
157 diff = ((unsigned char *) one->entry)[i] - ((unsigned char *) two->entry)[i];
158 return diff;
159 }
160 }
161
162 return 0;
163}
164
175 char *id3v2PrintContentEntry(const void *toBePrinted) {
176 const Id3v2ContentEntry *e = (Id3v2ContentEntry *) toBePrinted;
177 const int memCount = snprintf(NULL, 0, "Size: %zu, data: %p\n", e->size, e->entry);
178
179 char *str = calloc(memCount + 1, sizeof(char));
180
181 (void) snprintf(str, memCount, "Size: %zu, data: %p\n", e->size, e->entry);
182
183 return str;
184}
185
196 void *id3v2CopyContentEntry(const void *toBeCopied) {
197 Id3v2ContentEntry *e = (Id3v2ContentEntry *) toBeCopied;
198
199 return id3v2CreateContentEntry(e->entry, e->size);
200}
201
210 void id3v2DeleteContentEntry(void *toBeDeleted) {
211 Id3v2ContentEntry *e = (Id3v2ContentEntry *) toBeDeleted;
212
213 if (e->entry != NULL) {
214 free(e->entry);
215 }
216 free(e);
217}
218
226 void id3v2DeleteFrame(void *toBeDeleted) {
227 Id3v2Frame *f = (Id3v2Frame *) toBeDeleted;
228 id3v2DestroyFrame(&f);
229}
230
241 int id3v2CompareFrame(const void *first, const void *second) {
242 const Id3v2Frame *f = (Id3v2Frame *) first;
243 const Id3v2Frame *s = (Id3v2Frame *) second;
244 ListIter i1, i2;
245 void *tmp1 = NULL;
246 void *tmp2 = NULL;
247 int diff = 0;
248
249 if (f == NULL) {
250 return -1;
251 }
252
253 if (s == NULL) {
254 return 1;
255 }
256
257 diff = memcmp(f->header->id, s->header->id, ID3V2_FRAME_ID_MAX_SIZE);
258 if (diff != 0) {
259 return diff;
260 }
261
262 diff = (int) f->header->decompressionSize - (int) s->header->decompressionSize;
263 if (diff != 0) {
264 return diff;
265 }
266
267 diff = f->header->encryptionSymbol - s->header->encryptionSymbol;
268 if (diff != 0) {
269 return diff;
270 }
271
272 if (f->header->fileAlterPreservation != s->header->fileAlterPreservation) {
273 return 1;
274 }
275
276 diff = f->header->groupSymbol - s->header->groupSymbol;
277 if (diff != 0) {
278 return diff;
279 }
280
281 if (f->header->readOnly != s->header->readOnly) {
282 return 1;
283 }
284
285 if (f->header->tagAlterPreservation != s->header->tagAlterPreservation) {
286 return 1;
287 }
288
289 if (f->header->unsynchronisation != s->header->unsynchronisation) {
290 return 1;
291 }
292
293 diff = (int) f->entries->length - (int) s->entries->length;
294 if (diff != 0) {
295 return diff;
296 }
297
298 i1 = listCreateIterator(f->entries);
299 i2 = listCreateIterator(s->entries);
300
301 while ((tmp1 = listIteratorNext(&i1)) != NULL) {
302 tmp2 = listIteratorNext(&i2);
303
304 diff = id3v2CompareContentEntry(tmp1, tmp2);
305
306 if (diff != 0) {
307 return diff;
308 }
309 }
310
311 diff = (int) f->contexts->length - (int) s->contexts->length;
312 if (diff != 0) {
313 return diff;
314 }
315
316 i1 = listCreateIterator(f->contexts);
317 i2 = listCreateIterator(s->contexts);
318
319 while ((tmp1 = listIteratorNext(&i1)) != NULL) {
320 tmp2 = listIteratorNext(&i2);
321
322 diff = id3v2CompareContentContext(tmp1, tmp2);
323 if (diff != 0) {
324 return diff;
325 }
326 }
327
328 return diff;
329}
330
341 char *id3v2PrintFrame(const void *toBePrinted) {
342 const Id3v2Frame *f = (Id3v2Frame *) toBePrinted;
343 char *s = NULL;
344
345 const int memCount = snprintf(NULL, 0, "header : %p, context : %p, entries : %p", f->header, f->contexts,
346 f->entries);
347
348 s = calloc(memCount + 1, sizeof(char));
349
350 (void) snprintf(s, memCount, "header : %p, context : %p, entries : %p", f->header, f->contexts, f->entries);
351
352 return s;
353}
354
364 void *id3v2CopyFrame(const void *toBeCopied) {
365 Id3v2Frame *f = (Id3v2Frame *) toBeCopied;
366
367 Id3v2FrameHeader *h = id3v2CreateFrameHeader(f->header->id,
368 f->header->tagAlterPreservation,
369 f->header->fileAlterPreservation,
370 f->header->readOnly,
371 f->header->unsynchronisation,
372 f->header->decompressionSize,
373 f->header->encryptionSymbol,
374 f->header->groupSymbol);
375
376 return id3v2CreateFrame(h, listDeepCopy(f->contexts), listDeepCopy(f->entries));
377}
378
379
391 Id3v2Frame *id3v2CreateFrame(Id3v2FrameHeader *header, List *context, List *entries) {
392 Id3v2Frame *frame = malloc(sizeof(Id3v2Frame));
393
394 frame->contexts = context;
395 frame->entries = entries;
396 frame->header = header;
397
398 return frame;
399}
400
409 void id3v2DestroyFrame(Id3v2Frame **toDelete) {
410 if (*toDelete) {
411 listFree((*toDelete)->contexts);
412 listFree((*toDelete)->entries);
413 id3v2DestroyFrameHeader(&(*toDelete)->header);
414 free(*toDelete);
415 *toDelete = NULL;
416 toDelete = NULL;
417 }
418}
419
420
435 Id3v2Frame *id3v2CreateEmptyFrame(const char id[ID3V2_FRAME_ID_MAX_SIZE], uint8_t version, HashTable *userPairs) {
436 if (id == NULL) {
437 return NULL;
438 }
439
440 HashTable *pairs = NULL;
441 List *context = NULL;
442 List *entries = NULL;
443 Id3v2ContentContext *cc = NULL;
444 Id3v2FrameHeader *header = NULL;
445 Id3v2Frame *f = NULL;
446
447 pairs = id3v2CreateDefaultIdentifierContextPairings(version);
448
449 // first pass
450 context = hashTableRetrieve(pairs, id);
451
452 // second pass
453 if (context == NULL && id[0] == 'T') {
454 context = hashTableRetrieve(pairs, "T");
455 }
456
457 // third pass
458 if (context == NULL && id[0] == 'W') {
459 context = hashTableRetrieve(pairs, "W");
460 }
461
462 // forth pass
463 if (context == NULL) {
464 context = hashTableRetrieve(userPairs, id);
465 }
466
467 // fifth pass generic
468 if (context == NULL) {
469 context = hashTableRetrieve(pairs, "?");
470 }
471
472
473 ListIter i = listCreateIterator(context);
474
475 entries = listCreate(id3v2PrintContentEntry, id3v2DeleteContentEntry, id3v2CompareContentEntry,
476 id3v2CopyContentEntry);
477 while ((cc = listIteratorNext(&i)) != NULL) {
478 if (cc->type != iter_context) {
479 Id3v2ContentEntry *ce = id3v2CreateContentEntry("\0", 1);
480 listInsertBack(entries, (void *) ce);
481 }
482 }
483
484 header = id3v2CreateFrameHeader((uint8_t *) id, false, false, false, false, 0, 0, 0);
485 f = id3v2CreateFrame(header, listDeepCopy(context), entries);
486
487 hashTableFree(pairs);
488 return f;
489}
490
501 bool id3v2CompareFrameId(const Id3v2Frame *frame, const char id[ID3V2_FRAME_ID_MAX_SIZE]) {
502 if (frame == NULL) {
503 return false;
504 }
505
506 if (frame->header == NULL) {
507 return false;
508 }
509
510 return memcmp(frame->header->id, id, ID3V2_FRAME_ID_MAX_SIZE) == 0 ? true : false;
511}
512
513
524ListIter id3v2CreateFrameTraverser(Id3v2Tag *tag) {
525 ListIter e;
526 e.current = NULL;
527
528 if (tag == NULL) {
529 return e;
530 }
531
532 if (tag->frames == NULL) {
533 return e;
534 }
535
536 return listCreateIterator(tag->frames);
537}
538
549 Id3v2Frame *id3v2FrameTraverse(ListIter *traverser) {
550 return (Id3v2Frame *) listIteratorNext(traverser);
551}
552
563ListIter id3v2CreateFrameEntryTraverser(Id3v2Frame *frame) {
564 ListIter e;
565 e.current = NULL;
566
567 if (frame == NULL) {
568 return e;
569 }
570
571 if (frame->entries == NULL) {
572 return e;
573 }
574
575 return listCreateIterator(frame->entries);
576}
577
578
591 void *id3v2ReadFrameEntry(ListIter *traverser, size_t *dataSize) {
592 if (traverser == NULL) {
593 *dataSize = 0;
594 return NULL;
595 }
596
597 Id3v2ContentEntry *data = (Id3v2ContentEntry *) listIteratorNext(traverser);
598 void *ret = NULL;
599
600
601 if (data == NULL) {
602 *dataSize = 0;
603 return NULL;
604 }
605
606 if (!data->size) {
607 *dataSize = 0;
608 return NULL;
609 }
610
611 ret = malloc(data->size);
612 memset(ret, 0, data->size);
613 memcpy(ret, data->entry, data->size);
614
615 *dataSize = data->size;
616 return ret;
617}
618
619
632 char *id3v2ReadFrameEntryAsChar(ListIter *traverser, size_t *dataSize) {
633 unsigned char *tmp = NULL;
634 unsigned char *outString = NULL;
635 unsigned char encoding = 0;
636 char *escapedStr = NULL;
637 bool convi = false;
638 int utf8BomOffset = 0;
639 size_t outLen = 0;
640 size_t j = 0;
641
642 tmp = (unsigned char *) id3v2ReadFrameEntry(traverser, dataSize);
643
644 // Failed
645 if (!tmp) {
646 return NULL;
647 }
648
649 // detect utf16
650 if (*dataSize > BYTE_BOM_SIZE) {
651 if (byteHasBOM(tmp)) {
652 if (tmp[0] == 0xff && tmp[1] == 0xfe) {
653 encoding = BYTE_UTF16LE;
654 } else {
655 encoding = BYTE_UTF16BE;
656 }
657 }
658 }
659
660 // add some padding to tmp
661 unsigned char *reallocPtr = realloc(tmp, *dataSize + (size_t) (BYTE_PADDING * 2));
662
663 if (reallocPtr == NULL) {
664 free(tmp);
665 *dataSize = 0;
666 return NULL;
667 }
668
669 tmp = reallocPtr;
670
671 memset(tmp + *dataSize, 0, (size_t) (BYTE_PADDING * 2));
672 outLen = *dataSize + (size_t) (BYTE_PADDING * 2);
673
674 // detect utf8/ascii
675 if (byteIsUtf8(tmp)) {
676 encoding = BYTE_UTF8;
677 }
678
679 // detect latin1
680 if (byteIsUtf8(tmp) && encoding == 0) {
681 encoding = BYTE_ISO_8859_1;
682 }
683
684 // convert to UTF8
685 convi = byteConvertTextFormat(tmp, encoding, *dataSize + (size_t) (BYTE_PADDING * 2), &outString, BYTE_UTF8,
686 &outLen);
687
688 if (!convi && outLen == 0) {
689 free(tmp);
690 *dataSize = 0;
691 return NULL;
692 }
693
694 // data is already in utf8
695 if (convi && outLen == 0) {
696 outString = tmp;
697 } else {
698 *dataSize = outLen;
699 free(tmp);
700 }
701
702 // check for UTF8 BOM
703 if (*dataSize >= 3 && outString[0] == 0xEF && outString[1] == 0xBB && outString[2] == 0xBF) {
704 utf8BomOffset = 3;
705 }
706
707 // escape quotes and backslashes
708 escapedStr = malloc((2 * (*dataSize - utf8BomOffset)) + 1);
709 j = 0;
710
711 for (size_t i = 0; i < *dataSize - utf8BomOffset; i++) {
712 if (outString[i + utf8BomOffset] == '"' || outString[i + utf8BomOffset] == '\\') {
713 escapedStr[j++] = '\\';
714 escapedStr[j++] = (char) outString[i + utf8BomOffset];
715 } else {
716 escapedStr[j++] = (char) outString[i + utf8BomOffset];
717 }
718 }
719
720 escapedStr[j] = '\0';
721 free(outString);
722
723 // truncate
724 size_t nullPos = 0;
725 while (escapedStr[nullPos] != '\0') {
726 nullPos++;
727 }
728 *dataSize = nullPos;
729 escapedStr[nullPos] = '\0';
730
731 if (*dataSize == 0) {
732 *dataSize = 1;
733 }
734
735 return escapedStr;
736}
737
748uint8_t id3v2ReadFrameEntryAsU8(ListIter *traverser) {
749 uint8_t *tmp = NULL;
750 uint8_t ret = 0;
751 size_t dataSize = 0;
752
753 tmp = (uint8_t *) id3v2ReadFrameEntry(traverser, &dataSize);
754
755 // Failed
756 if (!tmp) {
757 return 0;
758 }
759
760 ret = tmp[0];
761 free(tmp);
762
763 return ret;
764}
765
778uint16_t id3v2ReadFrameEntryAsU16(ListIter *traverser) {
779 unsigned char *tmp = NULL;
780 uint16_t ret = 0;
781
782 size_t dataSize = 0;
783
784 tmp = (unsigned char *) id3v2ReadFrameEntry(traverser, &dataSize);
785
786 // Failed
787 if (!tmp) {
788 return 0;
789 }
790
791 if (dataSize >= sizeof(uint16_t)) {
792 ret = (uint16_t) tmp[1] << 8 | (uint16_t) tmp[0];
793 } else if (dataSize == sizeof(uint8_t)) {
794 ret = (uint16_t) tmp[0];
795 }
796
797 free(tmp);
798 return ret;
799}
800
812uint32_t id3v2ReadFrameEntryAsU32(ListIter *traverser) {
813 unsigned char *tmp = NULL;
814 uint32_t ret = 0;
815 size_t dataSize = 0;
816
817 tmp = (unsigned char *) id3v2ReadFrameEntry(traverser, &dataSize);
818
819 if (!tmp) {
820 return 0;
821 }
822
823 // clamp to max size
824 if (dataSize > sizeof(uint32_t)) {
825 dataSize = sizeof(uint32_t);
826 }
827
828 switch (dataSize) {
829 case 1:
830 ret = tmp[0];
831 break;
832 case 2:
833 ret = tmp[1] | (tmp[0] << 8);
834 break;
835 case 3:
836 ret = tmp[2] | (tmp[1] << 8) | (tmp[0] << 16);
837 break;
838 case 4:
839 ret = tmp[3] | (tmp[2] << 8) | (tmp[1] << 16) | (tmp[0] << 24);
840 break;
841 default:
842 break;
843 }
844
845
846 free(tmp);
847 return ret;
848}
849
865 bool id3v2WriteFrameEntry(Id3v2Frame *frame, ListIter *entries, size_t entrySize, const void *entry) {
866 if (frame == NULL || entries == NULL || entrySize == 0 || entry == NULL) {
867 return false;
868 }
869
870 if (frame->contexts == NULL || frame->entries == NULL || entries->current == NULL) {
871 return false;
872 }
873
874
875 ListIter contextIter = listCreateIterator(frame->contexts);
876 ListIter entriesIter = listCreateIterator(frame->entries);
877 Id3v2ContentEntry *ce = NULL;
878 Id3v2ContentContext *cc = NULL;
879 size_t posce = 0;
880 size_t poscc = 0;
881 size_t newSize = 0;
882 void *newData = 0;
883
884 // locate the entries position in the frame
885 while ((ce = (Id3v2ContentEntry *) listIteratorNext(&entriesIter)) != NULL) {
886 const int comp = frame->entries->compareData((void *) ce, entries->current->data);
887
888 if (comp == 0) {
889 break;
890 }
891
892 posce++;
893 }
894
895 if (ce == NULL) {
896 return false;
897 }
898
899 // locate the context for the entry
900 while ((cc = (Id3v2ContentContext *) listIteratorNext(&contextIter)) != NULL) {
901 if (cc->type == iter_context) {
902 // in case an iter is the first context
903 poscc = (poscc == 0) ? 0 : poscc - 1;
904 }
905
906 if (poscc == posce) {
907 break;
908 }
909
910 poscc++;
911 }
912
913 if (cc == NULL) {
914 return false;
915 }
916
917 newSize = entrySize;
918
919 if (entrySize > cc->max) {
920 newSize = cc->max;
921 }
922
923 if (entrySize < cc->min) {
924 newSize = cc->min;
925 }
926
927
928 newData = malloc(newSize);
929 memset(newData, 0, newSize);
930 memcpy(newData, entry, newSize);
931
932 free(((Id3v2ContentEntry *) entries->current->data)->entry);
933 ((Id3v2ContentEntry *) entries->current->data)->entry = newData;
934 ((Id3v2ContentEntry *) entries->current->data)->size = newSize;
935
936 return true;
937}
938
939
953 bool id3v2AttachFrameToTag(Id3v2Tag *tag, Id3v2Frame *frame) {
954 if (tag == NULL || frame == NULL) {
955 return false;
956 }
957
958 if (tag->frames == NULL || frame->contexts == NULL || frame->entries == NULL || frame->header == NULL) {
959 return false;
960 }
961
962 return listInsertBack(tag->frames, (void *) frame) ? true : false;
963}
964
965
978 Id3v2Frame *id3v2DetachFrameFromTag(Id3v2Tag *tag, Id3v2Frame *frame) {
979 if (tag == NULL || frame == NULL) {
980 return NULL;
981 }
982
983 return listDeleteData(tag->frames, (void *) frame);
984}
985
986
1002uint8_t *id3v2FrameHeaderSerialize(Id3v2FrameHeader *header, uint8_t version, uint32_t frameSize, size_t *outl) {
1003 unsigned char *tmp = NULL;
1004 ByteStream *stream = NULL;
1005 uint8_t *out = NULL;
1006
1007 if (header == NULL || version > ID3V2_TAG_VERSION_4) {
1008 *outl = 0;
1009 return NULL;
1010 }
1011
1012 switch (version) {
1013 case ID3V2_TAG_VERSION_2:
1014
1015 // size of header for this version is always 6 bytes.
1016 stream = byteStreamCreate(NULL, 6);
1017
1018 byteStreamWrite(stream, header->id, ID3V2_FRAME_ID_MAX_SIZE - 1);
1019
1020 tmp = u32tob(frameSize);
1021 byteStreamWrite(stream, tmp + 1, 3);
1022 free(tmp);
1023
1024 break;
1025 case ID3V2_TAG_VERSION_3:
1026
1027 // base header is always 10 bytes.
1028 stream = byteStreamCreate(NULL, 10);
1029
1030 byteStreamWrite(stream, header->id, ID3V2_FRAME_ID_MAX_SIZE);
1031
1032 tmp = u32tob(frameSize);
1033 byteStreamWrite(stream, tmp, 4);
1034 free(tmp);
1035
1036 byteStreamWriteBit(stream, header->tagAlterPreservation, 7);
1037 byteStreamWriteBit(stream, header->fileAlterPreservation, 6);
1038 byteStreamWriteBit(stream, header->readOnly, 5);
1039 byteStreamSeek(stream, 1, SEEK_CUR);
1040
1041 byteStreamWriteBit(stream, (header->decompressionSize > 0) ? true : false, 7);
1042 byteStreamWriteBit(stream, (header->encryptionSymbol > 0) ? true : false, 6);
1043 byteStreamWriteBit(stream, (header->groupSymbol > 0) ? true : false, 5);
1044 byteStreamSeek(stream, 1, SEEK_CUR);
1045
1046 if (header->decompressionSize > 0) {
1047 byteStreamResize(stream, stream->bufferSize + 4);
1048 tmp = u32tob(header->decompressionSize);
1049 byteStreamWrite(stream, tmp, 4);
1050 free(tmp);
1051 }
1052
1053 if (header->encryptionSymbol > 0) {
1054 byteStreamResize(stream, stream->bufferSize + 1);
1055 byteStreamWrite(stream, &header->encryptionSymbol, 1);
1056 }
1057
1058 if (header->groupSymbol > 0) {
1059 byteStreamResize(stream, stream->bufferSize + 1);
1060 byteStreamWrite(stream, &header->groupSymbol, 1);
1061 }
1062
1063 break;
1064 case ID3V2_TAG_VERSION_4:
1065
1066 // base header is always 10 bytes.
1067 stream = byteStreamCreate(NULL, 10);
1068
1069 byteStreamWrite(stream, header->id, ID3V2_FRAME_ID_MAX_SIZE);
1070
1071 tmp = u32tob(byteSyncintEncode(frameSize));
1072 byteStreamWrite(stream, tmp, 4);
1073 free(tmp);
1074
1075 byteStreamWriteBit(stream, header->tagAlterPreservation, 6);
1076 byteStreamWriteBit(stream, header->fileAlterPreservation, 5);
1077 byteStreamWriteBit(stream, header->readOnly, 4);
1078 byteStreamSeek(stream, 1, SEEK_CUR);
1079
1080 byteStreamWriteBit(stream, (header->groupSymbol > 0) ? true : false, 6);
1081 byteStreamWriteBit(stream, (header->decompressionSize > 0) ? true : false, 3);
1082 byteStreamWriteBit(stream, (header->encryptionSymbol > 0) ? true : false, 2);
1083 byteStreamWriteBit(stream, (header->unsynchronisation > 0) ? true : false, 1);
1084 byteStreamWriteBit(stream, (header->decompressionSize > 0) ? true : false, 0);
1085 byteStreamSeek(stream, 1, SEEK_CUR);
1086
1087 if (header->groupSymbol > 0) {
1088 byteStreamResize(stream, stream->bufferSize + 1);
1089 byteStreamWrite(stream, &header->groupSymbol, 1);
1090 }
1091
1092 if (header->encryptionSymbol > 0) {
1093 byteStreamResize(stream, stream->bufferSize + 1);
1094 byteStreamWrite(stream, &header->encryptionSymbol, 1);
1095 }
1096
1097 if (header->decompressionSize > 0) {
1098 byteStreamResize(stream, stream->bufferSize + 4);
1099 tmp = u32tob(header->decompressionSize);
1100 byteStreamWrite(stream, tmp, 4);
1101 free(tmp);
1102 }
1103
1104 break;
1105 default:
1106 *outl = 0;
1107 return NULL;
1108 }
1109
1110
1111 byteStreamRewind(stream);
1112 out = calloc(stream->bufferSize, sizeof(uint8_t));
1113 *outl = stream->bufferSize;
1114 byteStreamRead(stream, out, stream->bufferSize);
1115 byteStreamDestroy(stream);
1116 return out;
1117}
1118
1136 char *id3v2FrameHeaderToJSON(const Id3v2FrameHeader *header, uint8_t version) {
1137 char *json = NULL;
1138 size_t memCount = 3;
1139
1140 if (header == NULL) {
1141 json = calloc(memCount, sizeof(char));
1142 memcpy(json, "{}\0", memCount);
1143 return json;
1144 }
1145
1146 switch (version) {
1147 case ID3V2_TAG_VERSION_2:
1148
1149 memCount += snprintf(NULL, 0,
1150 "{\"id\":\"%c%c%c\"}",
1151 header->id[0],
1152 header->id[1],
1153 header->id[2]);
1154
1155 json = calloc(memCount + 1, sizeof(char));
1156
1157 (void) snprintf(json, memCount,
1158 "{\"id\":\"%c%c%c\"}",
1159 header->id[0],
1160 header->id[1],
1161 header->id[2]);
1162
1163 break;
1164 case ID3V2_TAG_VERSION_3:
1165
1166 memCount += snprintf(NULL, 0,
1167 "{\"id\":\"%c%c%c%c\",\"tagAlterPreservation\":%s,\"fileAlterPreservation\":%s,\"readOnly\":%s,\"decompressionSize\":%"
1168 PRId32",\"encryptionSymbol\":%d,\"groupSymbol\":%d}",
1169 header->id[0],
1170 header->id[1],
1171 header->id[2],
1172 header->id[3],
1173 ((header->tagAlterPreservation == true) ? "true" : "false"),
1174 ((header->fileAlterPreservation == true) ? "true" : "false"),
1175 ((header->readOnly == true) ? "true" : "false"),
1176 header->decompressionSize,
1177 header->encryptionSymbol,
1178 header->groupSymbol);
1179
1180 json = calloc(memCount + 1, sizeof(char));
1181
1182 (void) snprintf(json, memCount,
1183 "{\"id\":\"%c%c%c%c\",\"tagAlterPreservation\":%s,\"fileAlterPreservation\":%s,\"readOnly\":%s,\"decompressionSize\":%"
1184 PRId32",\"encryptionSymbol\":%d,\"groupSymbol\":%d}",
1185 header->id[0],
1186 header->id[1],
1187 header->id[2],
1188 header->id[3],
1189 ((header->tagAlterPreservation == true) ? "true" : "false"),
1190 ((header->fileAlterPreservation == true) ? "true" : "false"),
1191 ((header->readOnly == true) ? "true" : "false"),
1192 header->decompressionSize,
1193 header->encryptionSymbol,
1194 header->groupSymbol);
1195
1196 break;
1197 case ID3V2_TAG_VERSION_4:
1198
1199 memCount += snprintf(NULL, 0,
1200 "{\"id\":\"%c%c%c%c\",\"tagAlterPreservation\":%s,\"fileAlterPreservation\":%s,\"readOnly\":%s,\"unsynchronisation\":%s,\"decompressionSize\":%"
1201 PRId32",\"encryptionSymbol\":%d,\"groupSymbol\":%d}",
1202 header->id[0],
1203 header->id[1],
1204 header->id[2],
1205 header->id[3],
1206 ((header->tagAlterPreservation == true) ? "true" : "false"),
1207 ((header->fileAlterPreservation == true) ? "true" : "false"),
1208 ((header->readOnly == true) ? "true" : "false"),
1209 ((header->unsynchronisation == true) ? "true" : "false"),
1210 header->decompressionSize,
1211 header->encryptionSymbol,
1212 header->groupSymbol);
1213
1214 json = calloc(memCount + 1, sizeof(char));
1215
1216 (void) snprintf(json, memCount,
1217 "{\"id\":\"%c%c%c%c\",\"tagAlterPreservation\":%s,\"fileAlterPreservation\":%s,\"readOnly\":%s,\"unsynchronisation\":%s,\"decompressionSize\":%"
1218 PRId32",\"encryptionSymbol\":%d,\"groupSymbol\":%d}",
1219 header->id[0],
1220 header->id[1],
1221 header->id[2],
1222 header->id[3],
1223 ((header->tagAlterPreservation == true) ? "true" : "false"),
1224 ((header->fileAlterPreservation == true) ? "true" : "false"),
1225 ((header->readOnly == true) ? "true" : "false"),
1226 ((header->unsynchronisation == true) ? "true" : "false"),
1227 header->decompressionSize,
1228 header->encryptionSymbol,
1229 header->groupSymbol);
1230
1231 break;
1232
1233 // no support
1234 default:
1235 json = calloc(memCount, sizeof(char));
1236 memcpy(json, "{}\0", memCount);
1237 break;
1238 }
1239
1240 return json;
1241}
1242
1260uint8_t *id3v2FrameSerialize(Id3v2Frame *frame, uint8_t version, size_t *outl) {
1261 ByteStream *stream = NULL;
1262 Id3v2ContentContext *cc = NULL;
1263
1264 if (frame == NULL || version > ID3V2_TAG_VERSION_4) {
1265 *outl = 0;
1266 return NULL;
1267 }
1268
1269 ListIter context = listCreateIterator(frame->contexts);
1270 ListIter trav = id3v2CreateFrameEntryTraverser(frame);
1271 ListIter iterStorage;
1272 size_t readSize = 0;
1273 size_t contentSize = 0;
1274 size_t currIterations = 0;
1275 size_t headerSize = 0;
1276 uint8_t *header = NULL;
1277 uint8_t *out = NULL;
1278 unsigned char *tmp = NULL;
1279 bool exit = false;
1280 bool bitFlag = false;
1281
1282 // the frame size will be updated later as it cannot be calculated
1283 // before processing frame entries
1284 header = id3v2FrameHeaderSerialize(frame->header, version, 0, &headerSize);
1285 stream = byteStreamCreate(header, headerSize);
1286 free(header);
1287
1288 byteStreamSeek(stream, 0, SEEK_END);
1289
1290 while ((cc = listIteratorNext(&context)) != NULL) {
1291 switch (cc->type) {
1292 // encoding will always be enforced
1293 case encodedString_context: {
1294 ListIter contentContextIter = listCreateIterator(frame->contexts);
1295 ListIter contentEntryIter = listCreateIterator(frame->entries);
1296 size_t poscc = 0;
1297 size_t posce = 0;
1298 size_t utf8Len = 0;
1299 uint8_t encoding = 0;
1300 void *iterNext = NULL;
1301
1302
1303 // hunt down "encoding" key
1304 while ((iterNext = listIteratorNext(&contentContextIter)) != NULL) {
1305 if (((Id3v2ContentContext *) iterNext)->type == iter_context) {
1306 poscc--;
1307 }
1308
1309 if (((Id3v2ContentContext *) iterNext)->key == id3v2djb2("encoding")) {
1310 break;
1311 }
1312
1313 poscc++;
1314 }
1315
1316 // hunt down encoding value
1317 while ((iterNext = listIteratorNext(&contentEntryIter)) != NULL) {
1318 if (poscc == posce) {
1319 encoding = ((uint8_t *) ((Id3v2ContentEntry *) iterNext)->entry)[0];
1320 break;
1321 }
1322
1323 posce++;
1324 }
1325
1326 // enforce encoding as utf8
1327 tmp = (unsigned char *) id3v2ReadFrameEntryAsChar(&trav, &utf8Len);
1328
1329 if (tmp == NULL || utf8Len == 0) {
1330 exit = true;
1331 break;
1332 }
1333
1334 unsigned char *outStr = NULL;
1335 size_t outLen = 0;
1336
1337 // non-empty strings
1338 if (utf8Len >= 1 && tmp[0] != 0) {
1339 bool convi = false;
1340 convi = byteConvertTextFormat(tmp, BYTE_UTF8, utf8Len, &outStr, encoding, &outLen);
1341
1342 if (convi == false && outLen == 0) {
1343 free(tmp);
1344 exit = true;
1345 break;
1346 }
1347
1348
1349 // data is already in utf8
1350 if (convi && outLen == 0) {
1351 outStr = tmp;
1352 outLen = utf8Len;
1353 } else {
1354 free(tmp);
1355 }
1356
1357 // prepend BOM
1358 if (encoding == BYTE_UTF16BE || encoding == BYTE_UTF16LE) {
1359 bytePrependBOM(encoding, &outStr, &outLen);
1360 }
1361 } else {
1362 free(tmp);
1363 }
1364
1365 // append null spacer if there are more entries in the list
1366 if (trav.current != NULL) {
1367 switch (encoding) {
1368 case BYTE_ISO_8859_1:
1369 case BYTE_ASCII:
1370 case BYTE_UTF8: {
1371 unsigned char *reallocPtr = realloc(outStr, outLen + 1);
1372 if (reallocPtr == NULL) {
1373 free(outStr);
1374 byteStreamDestroy(stream);
1375 *outl = 0;
1376 return NULL;
1377 }
1378 outStr = reallocPtr;
1379 memset(outStr + outLen, 0, 1);
1380 outLen++;
1381 break;
1382 }
1383 case BYTE_UTF16BE:
1384 case BYTE_UTF16LE: {
1385 unsigned char *reallocPtr = realloc(outStr, outLen + 2);
1386 if (reallocPtr == NULL) {
1387 free(outStr);
1388 byteStreamDestroy(stream);
1389 *outl = 0;
1390 return NULL;
1391 }
1392 outStr = reallocPtr;
1393 memset(outStr + outLen, 0, 2);
1394 outLen += 2;
1395 break;
1396 }
1397 default:
1398 break;
1399 }
1400 }
1401
1402 byteStreamResize(stream, stream->bufferSize + outLen);
1403 byteStreamWrite(stream, outStr, outLen);
1404 contentSize += outLen;
1405 free(outStr);
1406
1407 break;
1408 }
1409
1410 // written the same way with no spacer I repeat no spacer over
1411 case numeric_context:
1412 case noEncoding_context:
1413 case binary_context:
1414 case precision_context:
1415 tmp = id3v2ReadFrameEntry(&trav, &readSize);
1416
1417 if (tmp == NULL) {
1418 exit = true;
1419 break;
1420 }
1421
1422 byteStreamResize(stream, stream->bufferSize + readSize);
1423 byteStreamWrite(stream, tmp, readSize);
1424 free(tmp);
1425 contentSize += readSize;
1426 break;
1427
1428 // latin1 will be enforced
1429 case latin1Encoding_context: {
1430 bool convi = false;
1431 unsigned char *outStr = NULL;
1432 size_t outLen = 0;
1433 size_t utf8len = 0;
1434
1435 tmp = (unsigned char *) id3v2ReadFrameEntryAsChar(&trav, &utf8len);
1436
1437 if (tmp == NULL) {
1438 exit = true;
1439 break;
1440 }
1441
1442 // ensure latin1
1443 convi = byteConvertTextFormat(tmp, BYTE_UTF8, utf8len, &outStr, BYTE_ISO_8859_1, &outLen);
1444
1445 if (convi == false && outLen == 0) {
1446 free(tmp);
1447 break;
1448 }
1449
1450 // data is already in latin1
1451 if (convi && outLen == 0) {
1452 outStr = tmp;
1453 outLen = utf8len;
1454 } else {
1455 free(tmp);
1456 }
1457
1458 // add spacer
1459 if (trav.current != NULL) {
1460 unsigned char *reallocPtr = realloc(outStr, outLen + 1);
1461 if (reallocPtr == NULL) {
1462 free(outStr);
1463 byteStreamDestroy(stream);
1464 *outl = 0;
1465 return NULL;
1466 }
1467 outStr = reallocPtr;
1468 memset(outStr + outLen, 0, 1);
1469 outLen++;
1470 }
1471
1472 byteStreamResize(stream, stream->bufferSize + outLen);
1473 byteStreamWrite(stream, outStr, outLen);
1474
1475 free(outStr);
1476 contentSize += outLen;
1477 break;
1478 }
1479
1480 case iter_context: {
1481 // create a new iter
1482 if (currIterations == 0) {
1483 iterStorage = context;
1484
1485 context = listCreateIterator(frame->contexts);
1486
1487 for (size_t i = 0; i < cc->min; i++) {
1488 listIteratorNext(&context);
1489 }
1490 }
1491
1492 // iter
1493 if (currIterations != cc->max && currIterations != 0) {
1494 context = listCreateIterator(frame->contexts);
1495
1496 for (size_t i = 0; i < cc->min; i++) {
1497 listIteratorNext(&context);
1498 }
1499 }
1500
1501 // reset
1502 if (currIterations >= cc->max) {
1503 context = iterStorage;
1504
1505 for (size_t i = 0; i < currIterations; i++) {
1506 listIteratorNext(&context);
1507 }
1508
1509 currIterations = 0;
1510 }
1511
1512 currIterations++;
1513
1518 }
1519 break;
1520 // wildly long and probably inefficient, but it works for now and is the best I can do with my current knowledge
1521 case bit_context: {
1522 // there is another bit context next
1523 if (listIteratorHasNext(context)) {
1524 if (((Id3v2ContentContext *) context.current->data)->type != bit_context) {
1525 bitFlag = false;
1526 } else {
1527 bitFlag = true;
1528 }
1529 }
1530
1531
1532 // at least 1 or more bit contexts in a row
1533 if (bitFlag == true) {
1534 unsigned char *bitBuff = NULL;
1535 unsigned char **byteDataArr = NULL;
1536 size_t *byteDataSizeArr = NULL;
1537 size_t *nbits = NULL;
1538
1539 size_t arrSize = 0;
1540 size_t totalBits = 0;
1541 size_t totalBytes = 0;
1542 size_t bitBuffSize = 0;
1543
1544 // copy values
1545 while (true) {
1546 tmp = id3v2ReadFrameEntry(&trav, &readSize);
1547
1548 if (tmp == NULL) {
1549 exit = true;
1550 break;
1551 }
1552
1553 if (byteDataSizeArr == NULL) {
1554 byteDataSizeArr = malloc(sizeof(size_t));
1555 byteDataSizeArr[0] = readSize;
1556
1557 nbits = malloc(sizeof(size_t));
1558 nbits[0] = cc->max;
1559
1560 byteDataArr = (unsigned char **) malloc(sizeof(unsigned char *));
1561 byteDataArr[0] = malloc(readSize);
1562
1563 for (size_t i = 0; i < readSize; i++) {
1564 byteDataArr[0][i] = tmp[i];
1565 }
1566
1567 arrSize++;
1568 } else {
1569 arrSize++;
1570 byteDataSizeArr = realloc(byteDataSizeArr, arrSize * sizeof(size_t));
1571 byteDataSizeArr[arrSize - 1] = readSize;
1572
1573 nbits = realloc(nbits, arrSize * sizeof(size_t));
1574 nbits[arrSize - 1] = cc->max;
1575
1576 byteDataArr = realloc(byteDataArr, arrSize * sizeof(unsigned char *));
1577 byteDataArr[arrSize - 1] = malloc(readSize);
1578
1579 for (size_t i = 0; i < readSize; i++) {
1580 byteDataArr[arrSize - 1][i] = tmp[i];
1581 }
1582 }
1583
1584 totalBits += cc->max;
1585
1586 free(tmp);
1587
1588
1589 if (listIteratorHasNext(context)) {
1590 if (((Id3v2ContentContext *) context.current->data)->type != bit_context) {
1591 break;
1592
1593 // seek to the next context
1594 } else {
1595 cc = listIteratorNext(&context);
1596 }
1597 } else {
1598 break;
1599 }
1600 }
1601
1602 totalBytes = ((totalBits / CHAR_BIT) % 2) ? (totalBits / CHAR_BIT) + 1 : totalBits / CHAR_BIT;
1603 // ? odd : even
1604
1605 bitBuff = malloc(totalBytes);
1606 memset(bitBuff, 0, totalBytes);
1607 bitBuffSize = totalBytes;
1608
1609 // reverse the byte data
1610 for (size_t i = 0; i < arrSize; i++) {
1611 size_t dataSize = byteDataSizeArr[i];
1612 size_t halfSize = dataSize / 2;
1613 for (size_t j = 0; j < halfSize; j++) {
1614 unsigned char temp = byteDataArr[i][j];
1615 byteDataArr[i][j] = byteDataArr[i][dataSize - j - 1];
1616 byteDataArr[i][dataSize - j - 1] = temp;
1617 }
1618 }
1619
1620 int step = 0;
1621 int offset = 0;
1622 int bitIndex = 0;
1623 for (size_t i = totalBytes; i > 0; i--) {
1624 if (step >= arrSize || totalBytes == 0) {
1625 break;
1626 }
1627
1628 size_t nBit = nbits[step];
1629 size_t nBytes = byteDataSizeArr[step];
1630 unsigned char *data = byteDataArr[step];
1631 int counter = 0;
1632
1633 while (nBit > 0) {
1634 if (counter == nBytes) {
1635 break;
1636 }
1637
1638 int j = 0;
1639 for (j = 0; j < CHAR_BIT; j++) {
1640 if (nBit == 0) {
1641 break;
1642 }
1643
1644 // switch to the next byte
1645 if (j + offset >= CHAR_BIT) {
1646 offset = 0;
1647 totalBytes--;
1648 bitIndex = 0;
1649 }
1650
1651 bitBuff[totalBytes - 1] = setBit(bitBuff[totalBytes - 1], bitIndex,
1652 readBit(data[counter], j) > 0 ? true : false);
1653 bitIndex++;
1654
1655 nBit--;
1656 }
1657
1658 if (j < CHAR_BIT) {
1659 offset = j;
1660 } else {
1661 offset = 0;
1662 }
1663 counter++;
1664 }
1665
1666 step++;
1667 }
1668
1669 byteStreamResize(stream, stream->bufferSize + bitBuffSize);
1670 byteStreamWrite(stream, bitBuff, bitBuffSize);
1671 contentSize += bitBuffSize;
1672
1673 for (size_t i = 0; i < arrSize; i++) {
1674 free(byteDataArr[i]);
1675 }
1676
1677 free(bitBuff);
1678 free(nbits);
1679 free(byteDataArr);
1680 free(byteDataSizeArr);
1681
1682 bitFlag = false;
1683
1684 // read a single and only bit context
1685 } else {
1686 int totalBytesNeeded = (cc->max / CHAR_BIT) + 1;
1687 int nBit = CHAR_BIT - 1;
1688
1689 tmp = id3v2ReadFrameEntry(&trav, &readSize);
1690
1691 if (tmp == NULL) {
1692 exit = true;
1693 break;
1694 }
1695
1696 while (totalBytesNeeded > 0) {
1697 byteStreamResize(stream, stream->bufferSize + 1);
1698
1699 while (nBit >= 0) {
1700 byteStreamWriteBit(stream, (readBit(tmp[readSize - 1], nBit) > 0) ? true : false, nBit);
1701 nBit--;
1702 }
1703
1704 byteStreamSeek(stream, 1, SEEK_CUR);
1705 totalBytesNeeded--;
1706 }
1707
1708 contentSize += readSize;
1709 free(tmp);
1710 }
1711
1712 break;
1713 }
1714
1715 case adjustment_context: {
1716 ListIter contentContextIter = listCreateIterator(frame->contexts);
1717 ListIter contentEntryIter = listCreateIterator(frame->entries);
1718 size_t poscc = 0;
1719 size_t posce = 0;
1720 uint32_t rSize = 0;
1721 void *iterNext = NULL;
1722
1723
1724 // hunt down "adjustment" key
1725 while ((iterNext = listIteratorNext(&contentContextIter)) != NULL) {
1726 if (((Id3v2ContentContext *) iterNext)->type == iter_context) {
1727 poscc--;
1728 }
1729
1730 if (((Id3v2ContentContext *) iterNext)->key == id3v2djb2("adjustment")) {
1731 break;
1732 }
1733
1734 poscc++;
1735 }
1736
1737 // hunt down adjustment value
1738 while ((iterNext = listIteratorNext(&contentEntryIter)) != NULL) {
1739 if (poscc == posce) {
1740 rSize = btou32((uint8_t *) ((Id3v2ContentEntry *) iterNext)->entry,
1741 (int) ((Id3v2ContentEntry *) iterNext)->size);
1742 break;
1743 }
1744
1745 posce++;
1746 }
1747
1748 tmp = id3v2ReadFrameEntry(&trav, &readSize);
1749
1750 if (tmp == NULL) {
1751 exit = true;
1752 break;
1753 }
1754
1755 byteStreamResize(stream, stream->bufferSize + rSize);
1756 byteStreamWrite(stream, tmp, rSize);
1757
1758 contentSize += rSize;
1759 free(tmp);
1760 break;
1761 }
1762 case unknown_context:
1763 default:
1764 exit = true;
1765 break;
1766 }
1767
1768 if (exit == true) {
1769 break;
1770 }
1771 }
1772
1773
1774 // write in the frame size
1775 switch (version) {
1776 case ID3V2_TAG_VERSION_2:
1777 tmp = u32tob(contentSize);
1778 byteStreamSeek(stream, ID3V2_FRAME_ID_MAX_SIZE - 1, SEEK_SET);
1779 byteStreamWrite(stream, tmp + 1, ID3V2_FRAME_ID_MAX_SIZE - 1);
1780 free(tmp);
1781
1782 break;
1783 case ID3V2_TAG_VERSION_3:
1784 tmp = u32tob(contentSize);
1785 byteStreamSeek(stream, ID3V2_FRAME_ID_MAX_SIZE, SEEK_SET);
1786 byteStreamWrite(stream, tmp, ID3V2_FRAME_ID_MAX_SIZE);
1787 free(tmp);
1788
1789 break;
1790 case ID3V2_TAG_VERSION_4:
1791 tmp = u32tob(byteSyncintEncode(contentSize));
1792 byteStreamSeek(stream, ID3V2_FRAME_ID_MAX_SIZE, SEEK_SET);
1793 byteStreamWrite(stream, tmp, ID3V2_FRAME_ID_MAX_SIZE);
1794 free(tmp);
1795
1796 break;
1797 default:
1798 break;
1799 }
1800
1801 byteStreamRewind(stream);
1802 *outl = stream->bufferSize;
1803 out = calloc(stream->bufferSize, sizeof(uint8_t));
1804 byteStreamRead(stream, out, stream->bufferSize);
1805 byteStreamDestroy(stream);
1806
1807 return out;
1808}
1809
1843 char *id3v2FrameToJSON(Id3v2Frame *frame, uint8_t version) {
1844 char *json = NULL;
1845 char *headerJson = NULL;
1846 char **contentJson = NULL;
1847 char *concatenatedString = NULL;
1848 size_t contentJsonSize = 0;
1849 size_t memCount = 3;
1850 size_t concatenatedStringLength = 0;
1851 size_t currIterations = 0;
1852
1853 ListIter trav = id3v2CreateFrameEntryTraverser(frame);
1854 ListIter context = listCreateIterator(frame->contexts);
1855 ListIter iterStorage;
1856
1857
1858 Id3v2ContentContext *cc = NULL;
1859 bool exit = false;
1860
1861 unsigned char *tmp = NULL;
1862
1863 if (frame == NULL || version > ID3V2_TAG_VERSION_4) {
1864 json = calloc(memCount, sizeof(char));
1865 memcpy(json, "{}\0", memCount);
1866 return json;
1867 }
1868
1869 while ((cc = (Id3v2ContentContext *) listIteratorNext(&context)) != NULL) {
1870 switch (cc->type) {
1871 // treated as base64
1872 case noEncoding_context:
1873 case bit_context:
1874 case binary_context: {
1875 size_t readSize = 0;
1876 size_t contentMemCount = 3;
1877 char *b64 = NULL;
1878
1879 tmp = id3v2ReadFrameEntry(&trav, &readSize);
1880
1881 if (tmp == NULL || readSize == 0) {
1882 exit = true;
1883 break;
1884 }
1885
1886
1887 b64 = internal_base64Encode(tmp, readSize);
1888
1889 contentMemCount += snprintf(NULL, 0,
1890 "{\"value\":\"%s\",\"size\":%zu}",
1891 b64,
1892 strlen(b64));
1893
1894 contentJsonSize++;
1895 if (contentJson == NULL) {
1896 contentJson = (char **) calloc(contentJsonSize, sizeof(char *));
1897 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
1898
1899 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
1900 "{\"value\":\"%s\",\"size\":%zu}",
1901 b64,
1902 readSize);
1903 } else {
1904 char **reallocContentJson = (char **) realloc((void *) contentJson,
1905 (contentJsonSize) * sizeof(char *));
1906
1907 if (reallocContentJson == NULL) {
1908 for (size_t i = 0; i < contentJsonSize - 1; i++) {
1909 free(contentJson[i]);
1910 }
1911 free((void *) contentJson);
1912 free(b64);
1913 free(tmp);
1914 return NULL;
1915 }
1916
1917 contentJson = reallocContentJson;
1918 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
1919 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
1920 "{\"value\":\"%s\",\"size\":%zu}",
1921 b64,
1922 readSize);
1923 }
1924
1925 free(tmp);
1926 free(b64);
1927 }
1928 break;
1929
1930
1931 // will always be treated as utf8 when in json
1932 case encodedString_context:
1933 case latin1Encoding_context: {
1934 size_t readSize = 0;
1935 size_t contentMemCount = 3;
1936
1937 tmp = (unsigned char *) id3v2ReadFrameEntryAsChar(&trav, &readSize);
1938
1939 if (tmp == NULL || readSize == 0) {
1940 exit = true;
1941 break;
1942 }
1943
1944
1945 contentMemCount += snprintf(NULL, 0,
1946 "{\"value\":\"%s\",\"size\":%zu}",
1947 (char *) tmp,
1948 readSize);
1949
1950 contentJsonSize++;
1951 if (contentJson == NULL) {
1952 contentJson = (char **) calloc(contentJsonSize, sizeof(char *));
1953 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
1954
1955 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
1956 "{\"value\":\"%s\",\"size\":%zu}",
1957 (char *) tmp,
1958 readSize);
1959 } else {
1960 char **reallocContentJson = (char **) realloc((void *) contentJson,
1961 (contentJsonSize) * sizeof(char *));
1962
1963 if (reallocContentJson == NULL) {
1964 for (size_t i = 0; i < contentJsonSize - 1; i++) {
1965 free(contentJson[i]);
1966 }
1967 free((void *) contentJson);
1968 free(tmp);
1969 return NULL;
1970 }
1971
1972 contentJson = reallocContentJson;
1973 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
1974 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
1975 "{\"value\":\"%s\",\"size\":%zu}",
1976 (char *) tmp,
1977 readSize);
1978 }
1979
1980 free(tmp);
1981 }
1982 break;
1983
1984 case numeric_context: {
1985 size_t readSize = 0;
1986 size_t contentMemCount = 3;
1987 size_t num = 0;
1988
1989 tmp = id3v2ReadFrameEntry(&trav, &readSize);
1990
1991 if (tmp == NULL || readSize == 0) {
1992 exit = true;
1993 break;
1994 }
1995
1996 num = btost(tmp, (int) readSize);
1997 free(tmp);
1998
1999 contentMemCount += snprintf(NULL, 0,
2000 "{\"value\":\"%zu\",\"size\":%zu}",
2001 num,
2002 readSize);
2003
2004 contentJsonSize++;
2005 if (contentJson == NULL) {
2006 contentJson = (char **) calloc(contentJsonSize, sizeof(char *));
2007 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
2008
2009 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
2010 "{\"value\":\"%zu\",\"size\":%zu}",
2011 num,
2012 readSize);
2013 } else {
2014 char **reallocContentJson = (char **) realloc((void *) contentJson,
2015 (contentJsonSize) * sizeof(char *));
2016
2017 if (reallocContentJson == NULL) {
2018 for (size_t i = 0; i < contentJsonSize - 1; i++) {
2019 free(contentJson[i]);
2020 }
2021 free((void *) contentJson);
2022 return NULL;
2023 }
2024
2025 contentJson = reallocContentJson;
2026 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
2027 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
2028 "{\"value\":\"%zu\",\"size\":%zu}",
2029 num,
2030 readSize);
2031 }
2032 }
2033 break;
2034
2035 case precision_context: {
2036 size_t readSize = 0;
2037 size_t contentMemCount = 0;
2038 float value = 0;
2039
2040 tmp = id3v2ReadFrameEntry(&trav, &readSize);
2041
2042 if (tmp == NULL || readSize == 0) {
2043 exit = true;
2044 break;
2045 }
2046
2047
2048 memcpy(&value, tmp, sizeof(value));
2049 free(tmp);
2050
2051 contentMemCount = snprintf(NULL, 0,
2052 "{\"value\":\"%f\",\"size\":%zu}",
2053 value,
2054 readSize);
2055
2056 contentJsonSize++;
2057 if (contentJson == NULL) {
2058 contentJson = (char **) calloc(contentJsonSize, sizeof(char *));
2059 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
2060
2061 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
2062 "{\"value\":\"%f\",\"size\":%zu}",
2063 value,
2064 readSize);
2065 } else {
2066 char **reallocContentJson = (char **) realloc((void *) contentJson,
2067 (contentJsonSize) * sizeof(char *));
2068
2069 if (reallocContentJson == NULL) {
2070 for (size_t i = 0; i < contentJsonSize - 1; i++) {
2071 free(contentJson[i]);
2072 }
2073 free((void *) contentJson);
2074 return NULL;
2075 }
2076 contentJson = reallocContentJson;
2077 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
2078 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
2079 "{\"value\":\"%f\",\"size\":%zu}",
2080 value,
2081 readSize);
2082 }
2083 }
2084 break;
2085
2086 // produces no json
2087 case iter_context: {
2088 // create a new iter
2089 if (currIterations == 0) {
2090 iterStorage = context;
2091
2092 context = listCreateIterator(frame->contexts);
2093
2094 for (size_t i = 0; i < cc->min; i++) {
2095 listIteratorNext(&context);
2096 }
2097 }
2098
2099 // iter
2100 if (currIterations != cc->max && currIterations != 0) {
2101 context = listCreateIterator(frame->contexts);
2102
2103 for (size_t i = 0; i < cc->min; i++) {
2104 listIteratorNext(&context);
2105 }
2106 }
2107
2108 // reset
2109 if (currIterations >= cc->max) {
2110 context = iterStorage;
2111
2112 for (size_t i = 0; i < currIterations; i++) {
2113 listIteratorNext(&context);
2114 }
2115
2116 currIterations = 0;
2117 }
2118
2119 currIterations++;
2120
2121
2122 // This will go on forever until a failure condition is met by a previous context
2123 // for example, latin1_context detects null
2124 }
2125 break;
2126
2127 // base 64 again but concatenated
2128 case adjustment_context: {
2129 ListIter contentContextIter = listCreateIterator(frame->contexts);
2130 ListIter contentEntryIter = listCreateIterator(frame->entries);
2131 size_t poscc = 0;
2132 size_t posce = 0;
2133 size_t readSize = 0;
2134 size_t contentMemCount = 0;
2135 void *iterNext = NULL;
2136 char *b64 = NULL;
2137
2138
2139 // hunt down "adjustment" key
2140 while ((iterNext = listIteratorNext(&contentContextIter)) != NULL) {
2141 if (((Id3v2ContentContext *) iterNext)->type == iter_context) {
2142 poscc--;
2143 }
2144
2145 if (((Id3v2ContentContext *) iterNext)->key == id3v2djb2("adjustment")) {
2146 break;
2147 }
2148
2149 poscc++;
2150 }
2151
2152 // hunt down adjustment value
2153 while ((iterNext = listIteratorNext(&contentEntryIter)) != NULL) {
2154 if (poscc == posce) {
2155 readSize = btou32((uint8_t *) ((Id3v2ContentEntry *) iterNext)->entry,
2156 (int) ((Id3v2ContentEntry *) iterNext)->size);
2157 break;
2158 }
2159
2160 posce++;
2161 }
2162
2163
2164 tmp = id3v2ReadFrameEntry(&trav, &readSize);
2165
2166 if (tmp == NULL || readSize == 0) {
2167 exit = true;
2168 break;
2169 }
2170
2171
2172 b64 = internal_base64Encode(tmp, readSize);
2173 free(tmp);
2174
2175
2176 contentMemCount = snprintf(NULL, 0,
2177 "{\"value\":\"%s\",\"size\":%zu}",
2178 b64,
2179 strlen(b64));
2180
2181 contentJsonSize++;
2182 if (contentJson == NULL) {
2183 contentJson = (char **) calloc(contentJsonSize, sizeof(char *));
2184 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
2185
2186 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
2187 "{\"value\":\"%s\",\"size\":%zu}",
2188 b64,
2189 readSize);
2190 } else {
2191 char **reallocContentJson = (char **) realloc((void *) contentJson,
2192 (contentJsonSize) * sizeof(char *));
2193
2194 if (reallocContentJson == NULL) {
2195 for (size_t i = 0; i < contentJsonSize - 1; i++) {
2196 free(contentJson[i]);
2197 }
2198 free((void *) contentJson);
2199 free(b64);
2200 return NULL;
2201 }
2202 contentJson = reallocContentJson;
2203 contentJson[contentJsonSize - 1] = calloc(contentMemCount + 1, sizeof(char));
2204 (void) snprintf(contentJson[contentJsonSize - 1], contentMemCount,
2205 "{\"value\":\"%s\",\"size\":%zu}",
2206 b64,
2207 readSize);
2208 }
2209
2210 free(b64);
2211 }
2212 break;
2213
2214 case unknown_context:
2215 default:
2216 exit = true;
2217 break;
2218 }
2219
2220 if (exit == true) {
2221 break;
2222 }
2223 }
2224
2225 headerJson = id3v2FrameHeaderToJSON(frame->header, version);
2226
2227 // concatenate all JSON data stored in contentJson into a single string split by ","
2228 if (contentJson != NULL) {
2229 for (size_t i = 0; i < contentJsonSize; i++) {
2230 concatenatedStringLength += strlen(contentJson[i]) + 1;
2231 }
2232
2233 concatenatedString = calloc(concatenatedStringLength + 1, sizeof(char));
2234
2235 size_t offset = 0;
2236 for (size_t i = 0; i < contentJsonSize; i++) {
2237 if (i > 0) {
2238 concatenatedString[offset++] = ',';
2239 }
2240
2241 size_t currentLen = strlen(contentJson[i]);
2242 memcpy(concatenatedString + offset, contentJson[i], currentLen);
2243 offset += currentLen;
2244 }
2245 concatenatedString[offset] = '\0';
2246 }
2247
2248 memCount += snprintf(NULL, 0,
2249 "{\"header\":%s,\"content\":[%s]}",
2250 headerJson,
2251 concatenatedString);
2252
2253 json = calloc(memCount + 1, sizeof(char));
2254 (void) snprintf(json, memCount,
2255 "{\"header\":%s,\"content\":[%s]}",
2256 headerJson,
2257 concatenatedString);
2258
2259
2260 free(headerJson);
2261
2262 if (concatenatedString != NULL) {
2263 free(concatenatedString);
2264 }
2265
2266 if (contentJson != NULL) {
2267 for (size_t i = 0; i < contentJsonSize; i++) {
2268 free(contentJson[i]);
2269 }
2270 free((void *) contentJson);
2271 }
2272
2273 return json;
2274}
id3v2Context.h
Function definitions for ID3v2 frame context definitions and parsing configuration.
id3v2djb2
unsigned long id3v2djb2(const char *str)
Computes a DJB2 hash value for a null-terminated string.
Definition id3v2Context.c:32
id3v2CreateDefaultIdentifierContextPairings
HashTable * id3v2CreateDefaultIdentifierContextPairings(unsigned int version)
Creates a default mapping of frame identifiers to their corresponding parse contexts for all ID3v2 ve...
Definition id3v2Context.c:1416
id3v2CompareContentContext
int id3v2CompareContentContext(const void *first, const void *second)
Performs comparison of two content context structures.
Definition id3v2Context.c:115
id3v2FrameHeaderToJSON
char * id3v2FrameHeaderToJSON(const Id3v2FrameHeader *header, uint8_t version)
Converts a frame header structure to its JSON representation based on ID3v2 version.
Definition id3v2Frame.c:1136
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
id3v2ReadFrameEntryAsU32
uint32_t id3v2ReadFrameEntryAsU32(ListIter *traverser)
Reads the current entry as a 32-bit unsigned integer, advancing the iterator.
Definition id3v2Frame.c:812
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
id3v2FrameHeaderSerialize
uint8_t * id3v2FrameHeaderSerialize(Id3v2FrameHeader *header, uint8_t version, uint32_t frameSize, size_t *outl)
Serializes a frame header to binary format according to the specified ID3v2 version.
Definition id3v2Frame.c:1002
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
id3v2AttachFrameToTag
bool id3v2AttachFrameToTag(Id3v2Tag *tag, Id3v2Frame *frame)
Inserts a frame at the end of a tag's frames list.
Definition id3v2Frame.c:953
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
id3v2DestroyFrameHeader
void id3v2DestroyFrameHeader(Id3v2FrameHeader **toDelete)
Frees all memory allocated for a frame header and nullifies the pointer.
Definition id3v2Frame.c:96
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
id3v2PrintFrame
char * id3v2PrintFrame(const void *toBePrinted)
Generates a string representation of a frame for debugging.
Definition id3v2Frame.c:341
id3v2CompareFrameId
bool id3v2CompareFrameId(const Id3v2Frame *frame, const char id[ID3V2_FRAME_ID_MAX_SIZE])
Compares a frame's identifier with a provided ID string.
Definition id3v2Frame.c:501
id3v2FrameTraverse
Id3v2Frame * id3v2FrameTraverse(ListIter *traverser)
Advances the iterator and returns the next frame in the list.
Definition id3v2Frame.c:549
id3v2DeleteFrame
void id3v2DeleteFrame(void *toBeDeleted)
Frees all memory allocated for an ID3v2 frame structure.
Definition id3v2Frame.c:226
id3v2ReadFrameEntryAsU16
uint16_t id3v2ReadFrameEntryAsU16(ListIter *traverser)
Reads the current entry as a 16-bit unsigned integer, advancing the iterator.
Definition id3v2Frame.c:778
id3v2Frame.h
Function definitions for ID3v2 frame lifecycle, traversal, serialization, and content management.
id3v2CopyContentEntry
void * id3v2CopyContentEntry(const void *toBeCopied)
Creates a deep copy of a content entry.
Definition id3v2Frame.c:196
id3v2PrintContentEntry
char * id3v2PrintContentEntry(const void *toBePrinted)
Generates a string representation of a content entry for debugging.
Definition id3v2Frame.c:175
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
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
iter_context
@ iter_context
Iterator for repeating context sequences.
Definition id3v2Types.h:221
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
adjustment_context
@ adjustment_context
Dynamic upper bound adjustment based on prior context with key 'adjustment'.
Definition id3v2Types.h:227
binary_context
@ binary_context
Binary data block with no terminator.
Definition id3v2Types.h:179
bit_context
@ bit_context
Bit-level field (1-8 bits).
Definition id3v2Types.h:215
precision_context
@ precision_context
Floating-point values (float or double).
Definition id3v2Types.h:203
unknown_context
@ unknown_context
Error/invalid context state (-1).
Definition id3v2Types.h:167
Id3v2Frame
struct _Id3v2Frame Id3v2Frame
Complete ID3v2 frame structure with header, parsing contexts, and data.
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.
_Id3v2ContentContext::min
size_t min
Minimum size in bytes (or bits for bit_context, or start index for iter_context).
Definition id3v2Types.h:248
_Id3v2ContentContext::max
size_t max
Maximum size in bytes (or bits for bit_context, or iteration count for iter_context).
Definition id3v2Types.h:254
_Id3v2ContentContext::type
Id3v2ContextType type
Context type determining parsing behavior (string, binary, numeric, etc.)
Definition id3v2Types.h:239
_Id3v2ContentEntry::size
size_t size
Size in bytes of the data pointed to by entry.
Definition id3v2Types.h:268
_Id3v2ContentEntry::entry
void * entry
Pointer to extracted field data (string, binary, numeric, etc.). Type determined by corresponding con...
Definition id3v2Types.h:265
_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
_Id3v2FrameHeader::groupSymbol
uint8_t groupSymbol
Group identifier to associate related frames. 0 if frame is not grouped.
Definition id3v2Types.h:155
_Id3v2FrameHeader::fileAlterPreservation
bool fileAlterPreservation
Marks the frame as unknown if the file is altered.
Definition id3v2Types.h:140
_Id3v2FrameHeader::readOnly
bool readOnly
Marks the frame as read only. If true, frame content is read-only and should not be modified.
Definition id3v2Types.h:143
_Id3v2FrameHeader::encryptionSymbol
uint8_t encryptionSymbol
Encryption method identifier. 0 if frame is not encrypted.
Definition id3v2Types.h:152
_Id3v2FrameHeader::tagAlterPreservation
bool tagAlterPreservation
Marks the frame as unknown if the tag is altered.
Definition id3v2Types.h:137
_Id3v2FrameHeader::unsynchronisation
bool unsynchronisation
Marks a tag as unsynchronised.
Definition id3v2Types.h:146
_Id3v2FrameHeader::decompressionSize
uint32_t decompressionSize
Decompressed size in bytes if frame uses zlib compression. 0 if uncompressed.
Definition id3v2Types.h:149
_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
_Id3v2Tag::frames
List * frames
Linked list of Id3v2Frame structures containing all tag metadata.
Definition id3v2Types.h:299