id3dev 26.01 An ID3 metadata library

Loading...
Searching...
No Matches
id3v2Parser.c
Go to the documentation of this file.
1
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <limits.h>
16#include "id3v2/id3v2Context.h"
17#include "id3v2/id3v2Frame.h"
18#include "id3v2/id3v2Parser.h"
19#include "id3v2/id3v2TagIdentity.h"
20#include "id3dependencies/ByteStream/include/byteStream.h"
21#include "id3dependencies/ByteStream/include/byteInt.h"
22#include "id3dependencies/ByteStream/include/byteUnicode.h"
23
24 static void internal_copyNBits(const unsigned char *src, unsigned char *dest, int startBit, int nBits) {
25 int byteIndex = startBit / CHAR_BIT;
26 int bitIndex = startBit % CHAR_BIT;
27 int remainingBits = nBits;
28
29 if (dest == NULL) {
30 return;
31 }
32
33 while (remainingBits > 0) {
34 unsigned char mask = 0xFF >> (CHAR_BIT - remainingBits);
35 unsigned char shiftedMask = mask << bitIndex;
36
37 // clear the destination bits
38 dest[byteIndex] &= ~shiftedMask;
39
40 // copy the bits from source to destination
41 dest[byteIndex] |= (src[byteIndex] & shiftedMask) >> bitIndex;
42
43 // update the counters
44 remainingBits -= (CHAR_BIT - bitIndex);
45 byteIndex++;
46 bitIndex = 0;
47 }
48}
49
72uint32_t id3v2ParseExtendedTagHeader(uint8_t *in, size_t inl, uint8_t version,
73 Id3v2ExtendedTagHeader **extendedTagHeader) {
74 if (in == NULL || inl == 0) {
75 *extendedTagHeader = NULL;
76 return 0;
77 }
78
79 ByteStream *stream = byteStreamCreate(in, inl);
80
81 if (!stream) {
82 *extendedTagHeader = NULL;
83 return 0;
84 }
85
86 // values needed
87 uint32_t padding = 0;
88 uint32_t crc = 0;
89 bool update = false;
90 bool tagRestrictions = false;
91 uint8_t restrictions = 0;
92
93 // values for parsing
94 int8_t flags = 0;
95 size_t offset = 0;
96 unsigned char crcBytes[5] = {0, 0, 0, 0, 0};
97 bool hasCrc = false;
98 bool hasRestrictions = false;
99 ByteStream *innerStream = NULL;
100
101 // size return
102 uint32_t hSize = 0;
103 uint32_t walk = 0;
104
105 if (version == ID3V2_TAG_VERSION_3 || version == ID3V2_TAG_VERSION_4) {
106 hSize = byteStreamReturnInt(stream);
107 if (!hSize) {
108 *extendedTagHeader = NULL;
109 byteStreamDestroy(stream);
110 return 0;
111 }
112
113 if (hSize > (stream->bufferSize - stream->cursor)) {
114 offset = hSize - (stream->bufferSize - stream->cursor);
115 }
116 }
117
118 innerStream = byteStreamCreate(byteStreamCursor(stream), hSize - offset);
119
120 switch (version) {
121 case ID3V2_TAG_VERSION_3:
122 flags = (int8_t) byteStreamReadBit(innerStream, 7);
123
124 // read flags
125 if (flags == -1) {
126 break;
127 }
128
129 // skip over flags
130 if (!(byteStreamSeek(innerStream, 2, SEEK_CUR))) {
131 break;
132 }
133
134 // read padding
135 padding = byteStreamReturnInt(innerStream);
136
137 if (!padding) {
138 if (flags == 0) {
139 break;
140 }
141 }
142
143 // check to see if a crc is there
144 if (flags) {
145 crc = byteStreamReturnInt(innerStream);
146 if (!crc) {
147 break;
148 }
149 }
150
151 break;
152
153 case ID3V2_TAG_VERSION_4:
154
155 // number of flag bytes
156 if (!(byteStreamSeek(innerStream, 1, SEEK_CUR))) {
157 break;
158 }
159
160 // read flags
161 if (byteStreamReadBit(innerStream, 6)) {
162 update = true;
163 }
164
165 if (byteStreamReadBit(innerStream, 5)) {
166 hasCrc = true;
167 }
168
169 if (byteStreamReadBit(innerStream, 4)) {
170 hasRestrictions = true;
171 }
172
173 if (!(byteStreamSeek(innerStream, 1, SEEK_CUR))) {
174 break;
175 }
176
177 // read crc
178 if (hasCrc) {
179 if (!(byteStreamRead(innerStream, (uint8_t *) crcBytes, 5))) {
180 break;
181 }
182
183 crc = byteSyncintDecode(btoi(crcBytes, 5));
184 }
185
186 // read restrictions
187 if (hasRestrictions) {
188 for (uint8_t k = 8; k > 0; k--) {
189 int bit = byteStreamReadBit(innerStream, k - 1);
190
191 if (bit == -1) {
192 break;
193 }
194
195 restrictions = setBit(restrictions, k - 1, (bool) bit);
196 }
197
198 tagRestrictions = true;
199
200 if (!(byteStreamSeek(innerStream, 1, SEEK_CUR))) {
201 break;
202 }
203 }
204
205 break;
206
207 // no support
208 default:
209 byteStreamDestroy(innerStream);
210 *extendedTagHeader = NULL;
211 return 0;
212 }
213
214 // clean up
215 byteStreamDestroy(stream);
216 *extendedTagHeader = id3v2CreateExtendedTagHeader(padding, crc, update, tagRestrictions, restrictions);
217 walk = innerStream->cursor + 4;
218
219 if (innerStream != NULL) {
220 byteStreamDestroy(innerStream);
221 }
222
223 return walk;
224}
225
250uint32_t id3v2ParseTagHeader(uint8_t *in, size_t inl, Id3v2TagHeader **tagHeader, uint32_t *tagSize) {
251 if (in == NULL || inl == 0) {
252 *tagHeader = NULL;
253 return 0;
254 }
255
256 ByteStream *stream = byteStreamCreate(in, inl);
257
258
259 if (!stream) {
260 *tagHeader = NULL;
261 return 0;
262 }
263
264 // values needed
265 uint8_t major = 0;
266 uint8_t minor = 0;
267 uint8_t flags = 0;
268
269 // values for parsing
270 uint32_t hSize = 0;
271 uint32_t walk = 0;
272 uint8_t id[ID3V2_TAG_ID_SIZE];
273 uint32_t idAsInt = 0;
274
275 // check for magic number
276 if (!(byteStreamRead(stream, id, ID3V2_TAG_ID_SIZE))) {
277 *tagHeader = NULL;
278 return 0;
279 }
280
281 for (int i = 0; i < ID3V2_TAG_ID_SIZE; ++i) {
282 idAsInt = (idAsInt << 8) | id[i];
283 }
284
285 if (ID3V2_TAG_ID_MAGIC_NUMBER_H != idAsInt) {
286 *tagHeader = NULL;
287 return ID3V2_TAG_ID_SIZE;
288 }
289
290
291 while (true) {
292 // versions
293 if (!(byteStreamRead(stream, &major, 1))) {
294 break;
295 }
296
297 if (!(byteStreamRead(stream, &minor, 1))) {
298 break;
299 }
300
301 // flags
302 if (!(byteStreamRead(stream, &flags, 1))) {
303 break;
304 }
305
306 // size
307 hSize = byteStreamReturnSyncInt(stream);
308 if (!hSize) {
309 break;
310 }
311
312 break;
313 }
314
315 walk = stream->cursor;
316 *tagSize = hSize;
317 *tagHeader = id3v2CreateTagHeader(major, minor, flags, NULL);
318 byteStreamDestroy(stream);
319 return walk;
320}
321
351uint32_t id3v2ParseFrameHeader(uint8_t *in, size_t inl, uint8_t version, Id3v2FrameHeader **frameHeader,
352 uint32_t *frameSize) {
353 if (in == NULL || inl == 0) {
354 *frameHeader = NULL;
355 *frameSize = 0;
356 return 0;
357 }
358
359 ByteStream *stream = byteStreamCreate(in, inl);
360
361 if (!stream) {
362 *frameHeader = NULL;
363 return 0;
364 }
365
366 // values needed
367 uint8_t id[ID3V2_FRAME_ID_MAX_SIZE] = {0, 0, 0, 0};
368 uint32_t tSize = 0;
369 bool tagAlter = false;
370 bool fileAlter = false;
371 bool readOnly = false;
372 bool unsync = false;
373 uint32_t decompressionSize = 0;
374 uint8_t encryptionSymbol = 0;
375 uint8_t groupSymbol = 0;
376
377 // values for parsing
378 uint8_t sizeBytes[ID3V2_FRAME_ID_MAX_SIZE];
379 uint8_t flagBytes[ID3V2_FRAME_FLAG_SIZE];
380 uint32_t walk = 0;
381
382 switch (version) {
383 case ID3V2_TAG_VERSION_2:
384
385 if (!byteStreamRead(stream, id, ID3V2_FRAME_ID_MAX_SIZE - 1)) {
386 *frameHeader = NULL;
387 *frameSize = 0;
388 byteStreamDestroy(stream);
389 return 0;
390 }
391
392 if (!byteStreamRead(stream, sizeBytes, ID3V2_FRAME_ID_MAX_SIZE - 1)) {
393 *frameHeader = NULL;
394 *frameSize = 0;
395 byteStreamDestroy(stream);
396 return ID3V2_FRAME_ID_MAX_SIZE - 1;
397 }
398
399 tSize = btoi(sizeBytes, ID3V2_FRAME_ID_MAX_SIZE - 1);
400 if (!tSize) {
401 break;
402 }
403
404 break;
405 case ID3V2_TAG_VERSION_3:
406 if (!byteStreamRead(stream, id, ID3V2_FRAME_ID_MAX_SIZE)) {
407 *frameHeader = NULL;
408 *frameSize = 0;
409 byteStreamDestroy(stream);
410 return 0;
411 }
412
413 tSize = byteStreamReturnU32(stream);
414 if (!tSize) {
415 *frameHeader = NULL;
416 *frameSize = 0;
417 byteStreamDestroy(stream);
418 return ID3V2_FRAME_ID_MAX_SIZE;
419 }
420
421 if (!(byteStreamRead(stream, flagBytes, ID3V2_FRAME_FLAG_SIZE))) {
422 *frameHeader = NULL;
423 *frameSize = tSize;
424 byteStreamDestroy(stream);
425 return ID3V2_FRAME_ID_MAX_SIZE * 2;
426 }
427
428 if (readBit(flagBytes[0], 7)) {
429 tagAlter = true;
430 }
431
432 if (readBit(flagBytes[0], 6)) {
433 fileAlter = true;
434 }
435
436 if (readBit(flagBytes[0], 5)) {
437 readOnly = true;
438 }
439
440 if (readBit(flagBytes[1], 7)) {
441 decompressionSize = byteStreamReturnInt(stream);
442 if (!decompressionSize) {
443 break;
444 }
445 }
446
447 if (readBit(flagBytes[1], 6)) {
448 if (!(byteStreamRead(stream, &encryptionSymbol, 1))) {
449 break;
450 }
451 }
452
453 if (readBit(flagBytes[1], 5)) {
454 if (!(byteStreamRead(stream, &groupSymbol, 1))) {
455 break;
456 }
457 }
458
459 break;
460 case ID3V2_TAG_VERSION_4:
461
462 if (!byteStreamRead(stream, id, ID3V2_FRAME_ID_MAX_SIZE)) {
463 *frameHeader = NULL;
464 *frameSize = 0;
465 byteStreamDestroy(stream);
466 return 0;
467 }
468
469 tSize = byteStreamReturnSyncInt(stream);
470 if (!tSize) {
471 *frameHeader = NULL;
472 *frameSize = 0;
473 byteStreamDestroy(stream);
474 return ID3V2_FRAME_ID_MAX_SIZE;
475 }
476
477 if (!(byteStreamRead(stream, flagBytes, ID3V2_FRAME_FLAG_SIZE))) {
478 *frameHeader = NULL;
479 *frameSize = tSize;
480 byteStreamDestroy(stream);
481 return ID3V2_FRAME_ID_MAX_SIZE * 2;
482 }
483
484 if (readBit(flagBytes[0], 6)) {
485 tagAlter = true;
486 }
487
488 if (readBit(flagBytes[0], 5)) {
489 fileAlter = true;
490 }
491
492 if (readBit(flagBytes[0], 4)) {
493 readOnly = true;
494 }
495
496 if (readBit(flagBytes[1], 6)) {
497 if (!byteStreamRead(stream, &groupSymbol, 1)) {
498 break;
499 }
500 }
501
502 if (readBit(flagBytes[1], 2)) {
503 if (!byteStreamRead(stream, &encryptionSymbol, 1)) {
504 break;
505 }
506 }
507
508 if (readBit(flagBytes[1], 1)) {
509 unsync = true;
510 }
511
512 if (readBit(flagBytes[1], 3) || encryptionSymbol || readBit(flagBytes[1], 0)) {
513 decompressionSize = byteStreamReturnSyncInt(stream);
514 }
515
516 break;
517
518 // no support
519 default:
520 *frameHeader = NULL;
521 *frameSize = 0;
522 byteStreamDestroy(stream);
523 return 0;
524 }
525
526 *frameHeader = id3v2CreateFrameHeader(id, tagAlter, fileAlter, readOnly, unsync, decompressionSize,
527 encryptionSymbol, groupSymbol);
528 *frameSize = tSize;
529 walk = stream->cursor;
530 byteStreamDestroy(stream);
531 // printf("[*] frameSize = %zu, walk = %zu stream->cursor = %zu\n", *frameSize, walk, stream->cursor); // debug info i dont wanna rewrite
532 return walk;
533}
534
565uint32_t id3v2ParseFrame(uint8_t *in, size_t inl, List *context, uint8_t version, Id3v2Frame **frame) {
566 if (in == NULL || inl == 0) {
567 *frame = NULL;
568 return 0;
569 }
570
571 ByteStream *stream = byteStreamCreate(in, inl);
572
573 if (!stream || !context) {
574 *frame = NULL;
575 return 0;
576 }
577
578 // values needed
579 Id3v2FrameHeader *header = NULL;
580 List *entries = NULL;
581 *frame = NULL;
582
583 // needed for parsing
584 size_t walk = 0;
585 size_t currIterations = 0;
586 size_t concurrentBitCount = 0;
587 uint32_t expectedHeaderSize = 0;
588 uint32_t expectedContentSize = 0;
589 ByteStream *innerStream = NULL;
590 ListIter iter;
591 ListIter iterStorage;
592 void *contextData = NULL;
593
594 expectedHeaderSize = id3v2ParseFrameHeader(stream->buffer, stream->bufferSize, version, &header,
595 &expectedContentSize);
596 walk += expectedHeaderSize;
597 if (!expectedHeaderSize) {
598 if (header != NULL) {
599 id3v2DestroyFrameHeader(&header);
600 }
601
602 byteStreamDestroy(stream);
603 return 0;
604 }
605
606 if (header == NULL || !expectedContentSize) {
607 // just in case both args are not null
608 if (header != NULL) {
609 id3v2DestroyFrameHeader(&header);
610 }
611
612 byteStreamDestroy(stream);
613 return expectedHeaderSize;
614 }
615
616 byteStreamSeek(stream, expectedHeaderSize, SEEK_CUR);
617
618 innerStream = byteStreamCreate(byteStreamCursor(stream), expectedContentSize);
619 entries = listCreate(id3v2PrintContentEntry, id3v2DeleteContentEntry, id3v2CompareContentEntry,
620 id3v2CopyContentEntry);
621
622
623 // is a frame compressed or encrypted?
624 // if so a generic context will be used meaning it's not up to me to decompress or unencrypted + if it's a text frame none
625 // of the reads or writes will work until someone reparses the data after its changed.
626 if (header->encryptionSymbol > 0 || header->decompressionSize > 0) {
627 uint8_t *data = NULL;
628 size_t dataSize = 0;
629 List *gContext = id3v2CreateGenericFrameContext();
630 Id3v2ContentContext *cc = (Id3v2ContentContext *) gContext->head->data;
631
632 if (cc->min >= cc->max) {
633 // no trust
634 dataSize = cc->min;
635 } else {
636 dataSize = cc->max;
637 }
638
639 if (dataSize > expectedContentSize) {
640 dataSize = expectedContentSize;
641 }
642
643 data = malloc(dataSize);
644
645 if (!byteStreamRead(innerStream, data, dataSize)) {
646 memset(data, 0, dataSize);
647 }
648
649 listInsertBack(entries, id3v2CreateContentEntry(data, dataSize));
650 free(data);
651
652 walk += innerStream->cursor;
653 *frame = id3v2CreateFrame(header, gContext, entries);
654 byteStreamDestroy(innerStream);
655 byteStreamDestroy(stream);
656 return walk;
657 }
658
659 iter = listCreateIterator(context);
660
661 while ((contextData = listIteratorNext(&iter)) != NULL) {
662 Id3v2ContentContext *cc = (Id3v2ContentContext *) contextData;
663
664 switch (cc->type) {
665 // encoded strings
666 case encodedString_context: {
667 uint8_t *data = NULL;
668 size_t dataSize = 0;
669
670 size_t posce = 0;
671 size_t poscc = 0;
672 uint8_t encoding = 0;
673 ListIter contentContextIter = listCreateIterator(context);
674 ListIter contentEntryIter = listCreateIterator(entries);
675 void *tmp = NULL;
676
677 while ((tmp = listIteratorNext(&contentContextIter)) != NULL) {
678 if (((Id3v2ContentContext *) tmp)->type == iter_context) {
679 poscc--;
680 }
681
682 if (((Id3v2ContentContext *) tmp)->key == id3v2djb2("encoding")) {
683 break;
684 }
685
686 poscc++;
687 }
688
689 while ((tmp = listIteratorNext(&contentEntryIter)) != NULL) {
690 if (poscc == posce) {
691 encoding = ((uint8_t *) ((Id3v2ContentEntry *) tmp)->entry)[0];
692 }
693
694 posce++;
695 }
696
697 switch (encoding) {
698 case BYTE_ISO_8859_1:
699 case BYTE_ASCII:
700 case BYTE_UTF8:
701 data = byteStreamReturnUtf8(innerStream, &dataSize);
702
703 if (data == NULL && dataSize == 0) {
704 byteStreamSeek(innerStream, 1, SEEK_CUR);
705 data = calloc(sizeof(unsigned char), 1);
706 memset(data, 0, 1);
707 dataSize = 1;
708 }
709 break;
710 case BYTE_UTF16BE:
711 case BYTE_UTF16LE:
712 data = byteStreamReturnUtf16(innerStream, &dataSize);
713
714 if (data == NULL && dataSize == 0) {
715 byteStreamSeek(innerStream, 2, SEEK_CUR);
716 data = calloc(sizeof(unsigned char), 2);
717 memset(data, 0, 2);
718 dataSize = 2;
719 }
720 break;
721 default:
722 break;
723 }
724
725 if (dataSize > cc->max) {
726 uint8_t *reallocPtr = realloc(data, cc->max);
727 if (reallocPtr == NULL) {
728 free(data);
729 listFree(entries);
730 byteStreamDestroy(innerStream);
731 byteStreamDestroy(stream);
732 id3v2DestroyFrameHeader(&header);
733 *frame = NULL;
734 return 0;
735 }
736 dataSize = cc->max;
737 data = reallocPtr;
738 } else if (cc->min > dataSize) {
739 uint8_t *reallocPtr = realloc(data, cc->min);
740 if (reallocPtr == NULL) {
741 free(data);
742 listFree(entries);
743 byteStreamDestroy(innerStream);
744 byteStreamDestroy(stream);
745 id3v2DestroyFrameHeader(&header);
746 *frame = NULL;
747 return 0;
748 }
749 data = reallocPtr;
750 memset(data + dataSize, 0, cc->min - dataSize);
751 dataSize = cc->min;
752 }
753
754 listInsertBack(entries, id3v2CreateContentEntry(data, dataSize));
755 free(data);
756
757 expectedContentSize = ((expectedContentSize < dataSize) ? 0 : expectedContentSize - dataSize);
758 }
759 break;
760 // only characters found within the latin1 character set
761 case latin1Encoding_context: {
762 uint8_t *data = NULL;
763 size_t dataSize = 0;
764
765 data = byteStreamReturnLatin1(innerStream, &dataSize);
766
767 if (dataSize > cc->max) {
768 dataSize = cc->max;
769 uint8_t *reallocPtr = realloc(data, dataSize);
770 if (reallocPtr == NULL) {
771 free(data);
772 listFree(entries);
773 byteStreamDestroy(innerStream);
774 byteStreamDestroy(stream);
775 id3v2DestroyFrameHeader(&header);
776 *frame = NULL;
777 return 0;
778 }
779 data = reallocPtr;
780 } else if (cc->min > dataSize) {
781 uint8_t *reallocPtr = realloc(data, cc->min);
782 if (reallocPtr == NULL) {
783 free(data);
784 listFree(entries);
785 byteStreamDestroy(innerStream);
786 byteStreamDestroy(stream);
787 id3v2DestroyFrameHeader(&header);
788 *frame = NULL;
789 return 0;
790 }
791 data = reallocPtr;
792 memset(data + dataSize, 0, cc->min - dataSize);
793 dataSize = cc->min;
794 }
795
796 listInsertBack(entries, id3v2CreateContentEntry(data, dataSize));
797 free(data);
798
799 expectedContentSize = ((expectedContentSize < dataSize) ? 0 : expectedContentSize - dataSize);
800 }
801 break;
802 // numbers (handled the same way)
803 case binary_context:
804 case noEncoding_context:
805 case precision_context:
806 case numeric_context: {
807 uint8_t *data = NULL;
808 size_t dataSize = 0;
809
810 if (cc->min >= cc->max) {
811 // no trust
812 dataSize = cc->min;
813 } else {
814 dataSize = cc->max;
815 }
816
817 if (dataSize > expectedContentSize) {
818 dataSize = expectedContentSize;
819 }
820
821 data = calloc(sizeof(uint8_t), dataSize);
822
823 if (!byteStreamRead(innerStream, data, dataSize)) {
824 memset(data, 0, dataSize);
825 }
826
827 listInsertBack(entries, id3v2CreateContentEntry(data, dataSize));
828 free(data);
829
830 expectedContentSize = ((expectedContentSize < dataSize) ? 0 : expectedContentSize - dataSize);
831 }
832 break;
833 case bit_context: {
834 uint8_t *data = NULL;
835 size_t nBits = 0;
836 size_t dataSize = 0;
837 Id3v2ContextType isBitContext = 0;
838
839 if (cc->min >= cc->max) {
840 // no trust
841 nBits = cc->min;
842 } else {
843 nBits = cc->max;
844 }
845
846 dataSize = (nBits + (CHAR_BIT - 1)) / CHAR_BIT;
847
848 data = malloc(dataSize);
849 memset(data, 0, dataSize);
850 internal_copyNBits(byteStreamCursor(innerStream), data, (int) concurrentBitCount, (int) nBits);
851
852 if (iter.current->data != NULL) {
853 Id3v2ContentContext *nextContext = (Id3v2ContentContext *) iter.current->data;
854 isBitContext = nextContext->type;
855 }
856
857 concurrentBitCount += nBits;
858
859 if (isBitContext != bit_context) {
860 if (concurrentBitCount / CHAR_BIT) {
861 byteStreamSeek(innerStream, concurrentBitCount / CHAR_BIT, SEEK_CUR);
862 expectedContentSize = ((expectedContentSize < dataSize) ? 0 : expectedContentSize - dataSize);
863 }
864
865 concurrentBitCount = 0;
866 }
867
868 listInsertBack(entries, id3v2CreateContentEntry(data, dataSize));
869 free(data);
870 }
871 break;
872 case iter_context: {
873 if (!currIterations) {
874 iterStorage = iter;
875
876 iter = listCreateIterator(context);
877
878 for (size_t i = 0; i < cc->min; i++) {
879 listIteratorNext(&iter);
880 }
881 }
882
883 if (currIterations != cc->max && currIterations != 0) {
884 iter = listCreateIterator(context);
885
886 for (size_t i = 0; i < cc->min; i++) {
887 listIteratorNext(&iter);
888 }
889 }
890
891
892 if (currIterations >= cc->max) {
893 iter = iterStorage;
894
895 for (size_t i = 0; i < currIterations; i++) {
896 listIteratorNext(&iter);
897 }
898
899 currIterations = 0;
900 }
901
902 currIterations++;
903 }
904 break;
905 case adjustment_context: {
906 uint8_t *data = NULL;
907 size_t dataSize = 0;
908
909 size_t posce = 0;
910 size_t poscc = 0;
911
912 ListIter contentContextIter = listCreateIterator(context);
913 ListIter contentEntryIter = listCreateIterator(entries);
914 void *tmp = NULL;
915
916 while ((tmp = listIteratorNext(&contentContextIter)) != NULL) {
917 if (((Id3v2ContentContext *) tmp)->type == iter_context) {
918 poscc--;
919 }
920
921 if (((Id3v2ContentContext *) tmp)->key == id3v2djb2("adjustment")) {
922 break;
923 }
924
925 poscc++;
926 }
927
928 while ((tmp = listIteratorNext(&contentEntryIter)) != NULL) {
929 if (poscc == posce) {
930 dataSize = btoi((unsigned char *) ((Id3v2ContentEntry *) tmp)->entry,
931 (int) ((Id3v2ContentEntry *) tmp)->size);
932 }
933
934 posce++;
935 }
936
937 if (dataSize > expectedContentSize) {
938 dataSize = expectedContentSize;
939 }
940
941 data = malloc(dataSize);
942 memset(data, 0, dataSize); //ensure data exists
943 byteStreamRead(innerStream, data, dataSize); // no need to check error code do to above memset
944
945 listInsertBack(entries, id3v2CreateContentEntry(data, dataSize));
946 free(data);
947
948 expectedContentSize = ((expectedContentSize < dataSize) ? 0 : expectedContentSize - dataSize);
949 }
950 break;
951 // no support
952 case unknown_context:
953 default:
954 byteStreamSeek(innerStream, 0, SEEK_END);
955 break;
956 }
957
958 if (expectedContentSize == 0 || byteStreamGetCh(innerStream) == EOF) {
959 break;
960 }
961 }
962
963
964 // enforce frame size from header parsing
965 walk += innerStream->bufferSize;
966
967 *frame = id3v2CreateFrame(header, listDeepCopy(context), entries);
968 byteStreamDestroy(innerStream);
969 byteStreamDestroy(stream);
970 return walk;
971}
972
1001 Id3v2Tag *id3v2ParseTagFromBuffer(uint8_t *in, size_t inl, HashTable *userPairs) {
1002 if (in == NULL || inl == 0) {
1003 return NULL;
1004 }
1005
1006 ByteStream *stream = byteStreamCreate(in, inl);
1007
1008 if (!stream) {
1009 return NULL;
1010 }
1011
1012 bool exit = false;
1013 uint32_t read = 0;
1014 uint32_t tagSize = 0;
1015 uint8_t id[ID3V2_TAG_ID_SIZE];
1016 Id3v2TagHeader *header = NULL;
1017 Id3v2ExtendedTagHeader *ext = NULL;
1018 List *frames = NULL;
1019 HashTable *pairs = NULL;
1020
1021 frames = listCreate(id3v2PrintFrame, id3v2DeleteFrame, id3v2CompareFrame, id3v2CopyFrame);
1022
1023 // locate the start of the tag
1024 while (true) {
1025 if (!byteStreamRead(stream, id, ID3V2_TAG_ID_SIZE)) {
1026 listFree(frames);
1027 byteStreamDestroy(stream);
1028 return NULL;
1029 }
1030
1031 if (btoi(id, ID3V2_TAG_ID_SIZE) == ID3V2_TAG_ID_MAGIC_NUMBER_H) {
1032 stream->cursor = stream->cursor - ID3V2_TAG_ID_SIZE;
1033 break;
1034 }
1035 }
1036
1037 while (true) {
1038 read = id3v2ParseTagHeader(byteStreamCursor(stream), stream->bufferSize - stream->cursor, &header, &tagSize);
1039
1040 if (read == 0 || header == NULL || tagSize == 0) {
1041 break;
1042 }
1043
1044 if (!byteStreamSeek(stream, read, SEEK_CUR)) {
1045 break;
1046 }
1047
1048 // shorten to exclude none tag data
1049 byteStreamResize(stream, tagSize + read);
1050
1051 if (id3v2ReadUnsynchronisationIndicator(header) == 1) {
1052 size_t readCount = 0;
1053 tagSize = tagSize / 2; // format is $xx $00 ...
1054 while (readCount < tagSize) {
1055 byteStreamSeek(stream, 1, SEEK_CUR);
1056 if (!byteStreamDeleteCh(stream)) {
1057 break;
1058 }
1059
1060 readCount++;
1061 }
1062
1063 byteStreamSeek(stream, 10, SEEK_SET);
1064 }
1065
1066 if ((header->majorVersion == ID3V2_TAG_VERSION_3 || header->majorVersion == ID3V2_TAG_VERSION_4) &&
1067 id3v2ReadExtendedHeaderIndicator(header) == 1) {
1068 read = id3v2ParseExtendedTagHeader(byteStreamCursor(stream), stream->bufferSize - stream->cursor,
1069 header->majorVersion, &ext);
1070 header->extendedHeader = ext;
1071
1072 if (read == 0 || ext == NULL) {
1073 break;
1074 }
1075
1076 tagSize = ((tagSize < read) ? 0 : tagSize - read);
1077 byteStreamSeek(stream, read, SEEK_CUR);
1078 }
1079
1080 pairs = id3v2CreateDefaultIdentifierContextPairings(header->majorVersion);
1081
1082 while (tagSize) {
1083 Id3v2Frame *frame = NULL;
1084 List *context = NULL;
1085 uint8_t frameId[ID3V2_FRAME_ID_MAX_SIZE] = {0};
1086
1087 if (header->majorVersion == ID3V2_TAG_VERSION_3 || header->majorVersion == ID3V2_TAG_VERSION_4) {
1088 if (!byteStreamRead(stream, frameId, ID3V2_FRAME_ID_MAX_SIZE)) {
1089 exit = true;
1090 break;
1091 }
1092
1093 stream->cursor = stream->cursor - ID3V2_FRAME_ID_MAX_SIZE;
1094 } else if (header->majorVersion == ID3V2_TAG_VERSION_2) {
1095 if (!byteStreamRead(stream, frameId, ID3V2_FRAME_ID_MAX_SIZE - 1)) {
1096 exit = true;
1097 break;
1098 }
1099
1100 stream->cursor = stream->cursor - (ID3V2_FRAME_ID_MAX_SIZE - 1);
1101 }
1102
1103 // check default pairings (pass 1/4)
1104 context = hashTableRetrieve(pairs, (char *) frameId);
1105
1106 // check user supplied ones (pass 2/4)
1107 if (context == NULL) {
1108 context = hashTableRetrieve(userPairs, (char *) frameId);
1109 }
1110
1111 // special considerations (pass 3/4)
1112 if (context == NULL) {
1113 if (frameId[0] == 'T') {
1114 context = hashTableRetrieve(pairs, "T");
1115 }
1116
1117 if (frameId[0] == 'W') {
1118 context = hashTableRetrieve(pairs, "W");
1119 }
1120 }
1121
1122 // use generic (pass 4/4)
1123 if (context == NULL) {
1124 context = hashTableRetrieve(pairs, "?");
1125 }
1126
1127 read = id3v2ParseFrame(byteStreamCursor(stream), stream->bufferSize - stream->cursor, context,
1128 header->majorVersion, &frame);
1129
1130 if (read == 0 || frame == NULL) {
1131 exit = true;
1132 break;
1133 }
1134
1135 listInsertBack(frames, frame);
1136 tagSize = ((tagSize < read) ? 0 : tagSize - read);
1137 byteStreamSeek(stream, read, SEEK_CUR);
1138 }
1139
1140 if (pairs != NULL) {
1141 hashTableFree(pairs);
1142 }
1143
1144 // double loop break
1145 if (exit) {
1146 break;
1147 }
1148
1149 byteStreamDestroy(stream);
1150 return id3v2CreateTag(header, frames);
1151 }
1152
1153 byteStreamDestroy(stream);
1154 return id3v2CreateTag(header, frames);
1155}
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
id3v2CreateGenericFrameContext
List * id3v2CreateGenericFrameContext(void)
Creates a context definition list for generic frames with unknown or unrecognized structure.
Definition id3v2Context.c:1387
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
id3v2CopyContentEntry
void * id3v2CopyContentEntry(const void *toBeCopied)
Creates a deep copy of a content entry.
Definition id3v2Frame.c:196
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
id3v2CreateFrame
Id3v2Frame * id3v2CreateFrame(Id3v2FrameHeader *header, List *context, List *entries)
Creates an ID3v2 frame structure from provided components.
Definition id3v2Frame.c:391
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
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
id3v2PrintFrame
char * id3v2PrintFrame(const void *toBePrinted)
Generates a string representation of a frame for debugging.
Definition id3v2Frame.c:341
id3v2DeleteFrame
void id3v2DeleteFrame(void *toBeDeleted)
Frees all memory allocated for an ID3v2 frame structure.
Definition id3v2Frame.c:226
id3v2ParseTagHeader
uint32_t id3v2ParseTagHeader(uint8_t *in, size_t inl, Id3v2TagHeader **tagHeader, uint32_t *tagSize)
Parses an ID3v2 tag header from a byte buffer (excluding extended header).
Definition id3v2Parser.c:250
id3v2ParseExtendedTagHeader
uint32_t id3v2ParseExtendedTagHeader(uint8_t *in, size_t inl, uint8_t version, Id3v2ExtendedTagHeader **extendedTagHeader)
Parses an ID3v2 extended tag header from a byte buffer.
Definition id3v2Parser.c:72
id3v2ParseFrame
uint32_t id3v2ParseFrame(uint8_t *in, size_t inl, List *context, uint8_t version, Id3v2Frame **frame)
Parses an ID3v2 frame (header + content) from a byte buffer using context-driven interpretation.
Definition id3v2Parser.c:565
id3v2ParseFrameHeader
uint32_t id3v2ParseFrameHeader(uint8_t *in, size_t inl, uint8_t version, Id3v2FrameHeader **frameHeader, uint32_t *frameSize)
Parses an ID3v2 frame header from a byte buffer.
Definition id3v2Parser.c:351
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
id3v2Parser.h
Definitions for binary parsing of ID3v2 tags, headers, frames, and complete tag structures from byte ...
id3v2TagIdentity.h
Function definitions of ID3v2 tag header and tag structure creation, manipulation,...
id3v2CreateTag
Id3v2Tag * id3v2CreateTag(Id3v2TagHeader *header, List *frames)
Creates and allocates an ID3v2 tag structure.
Definition id3v2TagIdentity.c:630
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
id3v2ReadExtendedHeaderIndicator
int id3v2ReadExtendedHeaderIndicator(Id3v2TagHeader *header)
Reads the extended header flag from an ID3v2.3/ID3v2.4 tag header.
Definition id3v2TagIdentity.c:209
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
Id3v2ExtendedTagHeader
struct _Id3v2ExtendedTagHeader Id3v2ExtendedTagHeader
Optional ID3v2 extended header containing supplementary tag metadata.
Id3v2ContextType
enum _Id3v2ContextType Id3v2ContextType
Context types for parsing and writing ID3v2 frame content fields.
Id3v2Frame
struct _Id3v2Frame Id3v2Frame
Complete ID3v2 frame structure with header, parsing contexts, and data.
ID3V2_FRAME_FLAG_SIZE
#define ID3V2_FRAME_FLAG_SIZE
Size in bytes of a frame header's flag section (2 bytes).
Definition id3v2Types.h:76
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.
ID3V2_TAG_ID_MAGIC_NUMBER_H
#define ID3V2_TAG_ID_MAGIC_NUMBER_H
Hexadecimal magic number for ID3v2 header tag identifier "ID3" (0x494433)
Definition id3v2Types.h:34
Id3v2TagHeader
struct _Id3v2TagHeader Id3v2TagHeader
ID3v2 tag header containing version and parsing information.
_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
_Id3v2FrameHeader::encryptionSymbol
uint8_t encryptionSymbol
Encryption method identifier. 0 if frame is not encrypted.
Definition id3v2Types.h:152
_Id3v2FrameHeader::decompressionSize
uint32_t decompressionSize
Decompressed size in bytes if frame uses zlib compression. 0 if uncompressed.
Definition id3v2Types.h:149
_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