//////////////////////////////////////////////////////////////// // simple IFF header display program based on tools by A'rpi/ESP-team // .aiff/.8svx fileformat docs from Apple headers and wotsit.org // Updated, cleaned up and added big/little endian detection // itemBase (chunks): 4 bytes itemType (IFF 4 character type) // 4 bytes itemLen (data to follow itemType) // ... // Format uses EA's IFF structure with audio stream descriptions for // compression based around the Apple QuickTime sound description // of it's sample data making it only usable for audio on storage media. // Therefore it has a fixed order of header followed by sample data. //////////////////////////////////////////////////////////////// #include #include #define itemParsing #if defined (__i386__) #define bytes2short(a,b) (b << 8) | (a) #define bytes2long(a,b,c,d) (d << 24) | (c << 16) | (b << 8) | (a) #define longs2dlong(a,b) (b << 32) | (a) #else #define bytes2short(a,b) (a << 8) | (b) #define bytes2long(a,b,c,d) (a << 24) | (b << 16) | (c << 8) | (d) #define longs2dlong(a,b) (a << 32) | (b) #endif #define bytes2type(a,b,c,d) (a << 24) | (b << 16) | (c << 8) | (d) #define isAIFF 0 #define isAIFC 1 int8_t commChunk = isAIFF; u_int32_t smpRate; u_int16_t readShort(FILE *fileInput) { u_int8_t bytes4short[2]; // Read in bytes4short and ignore if empty if (fread(&bytes4short,2,1,fileInput) <= 0) return -1; return bytes2short(bytes4short[0],bytes4short[1]); } u_int32_t readLong(FILE *fileInput) { u_int8_t bytes4long[4]; // Read in bytes4long and ignore if empty if (fread(&bytes4long,4,1,fileInput) <= 0) return -1; return bytes2long(bytes4long[0],bytes4long[1],bytes4long[2],bytes4long[3]); } u_int64_t readDlong(FILE *fileInput) { u_int8_t bytes4dlong[8]; u_int64_t long1; u_int64_t long2; // Read in bytes4dlong and ignore if empty if (fread(&bytes4dlong,8,1,fileInput) <= 0) return -1; long1 = bytes2long(bytes4dlong[0],bytes4dlong[1],bytes4dlong[2],bytes4dlong[3]); long2 = bytes2long(bytes4dlong[4],bytes4dlong[5],bytes4dlong[6],bytes4dlong[7]); return longs2dlong(long1,long2); } u_int8_t *mapType(u_int32_t type) { switch (type) { case bytes2type('F','O','R','M'): return ("IFF Format outer"); case bytes2type('A','I','F','F'): return ("Big Endian PCM Audio"); case bytes2type('A','I','F','C'): return ("Other Encoded Audio"); case bytes2type('8','S','V','X'): return ("Amiga Audio"); case bytes2type('C','O','M','M'): return ("Common header"); case bytes2type('S','S','N','D'): return ("Sound samples"); case bytes2type('F','V','E','R'): return ("Format Version Date"); case bytes2type('w','a','v','e'): return ("RIFF Waveform description"); case bytes2type('M','A','R','K'): return ("Markers"); case bytes2type('C','O','M','T'): return ("Marker comments"); case bytes2type('I','N','S','T'): return ("Instrument"); case bytes2type('M','I','D','I'): return ("MIDI"); case bytes2type('A','E','S','D'): return ("Audio Equipment Status"); case bytes2type('A','P','P','L'): return ("Application/Program Specific"); case bytes2type('N','A','M','E'): return ("Name (title)"); case bytes2type('(','C',')',' '): return ("Copyright"); case bytes2type('A','N','N','O'): return ("Annotation (comment)"); case bytes2type('A','U','T','H'): return ("Author"); case bytes2type('n','o','n','e'): return ("Big Endian Two's Complement"); case bytes2type('t','w','o','s'): return ("Big Endian Two's Complement"); case bytes2type('s','o','w','t'): return ("Little Endian Two's Complement"); case bytes2type('r','a','w',' '): return ("Offset Binary"); case bytes2type('i','m','a','4'): return ("Apple ADPCM - fixed block"); case bytes2type('a','l','a','w'): return ("A-law"); case bytes2type('u','l','a','w'): return ("ITU G.711 mu-law"); case bytes2type('a','g','s','m'): return ("ETSI GSM 6.10 - fixed block"); case bytes2type('.','m','p','3'): return ("MPEG layer III - VBR"); case bytes2type('m','s',0,2): return ("MS ADPCM - variable block"); case bytes2type('m','s',0,17): return ("Intel DVI/ADPCM - variable block"); case bytes2type('m','s',0,48): return ("Dolby v2 (AC2)"); case bytes2type('m','s',0,49): return ("MS GSM 6.10 - variable block"); case bytes2type('m','s',0,69): return ("ITU G.726 ADPCM"); case bytes2type('m','s',0,80): return ("MPEG layer I or II"); case bytes2type('m','s',0,85): return ("MPEG layer III - fixed block"); case bytes2type('m','s',17,17): return ("ITU G.721 ADPCM"); case bytes2type('m','s',17,18): return ("ITU G.723 ADPCM"); case bytes2type('m','s',32,0): return ("Dolby v3 (AC3)"); case bytes2type('m','s',255,254): return ("Vorbis"); } return("Unknown"); } void findItems(FILE *fileInput,int32_t itemLevel,u_int32_t endPos) { u_int32_t itemPos,itemLen,itemType; u_int8_t itemChars[4]; while (endPos == 0 || ftell(fileInput) < endPos) { itemPos = ftell(fileInput); // Read in itemChars and ignore if empty if (fread(&itemChars,4,1,fileInput) <= 0) break; itemType = bytes2type(itemChars[0],itemChars[1],itemChars[2],itemChars[3]); // Read in itemLen itemLen = readLong(fileInput); // Items are 4 bytes itemType + 4 bytes itemLen printf("0x%08X: %*s %.4s (0x%08X) [%s chunk] (%u bytes)\n0x%08X:", itemPos,itemLevel*2,"",&itemType,itemType, mapType(itemType),itemLen,itemPos+8); #ifdef itemParsing if (itemType == (u_int32_t)(bytes2type('F','O','R','M'))) { // header u_int32_t formType; u_int8_t formChars[4]; if (fread(&formChars,4,1,fileInput) <= 0) break; formType = bytes2type(formChars[0],formChars[1],formChars[2],formChars[3]); printf(" Type of IFF: '%.4s' [%s]",&formType,mapType(formType)); if (formType == (u_int32_t)(bytes2type('A','I','F','C'))) commChunk = isAIFC; } if (itemType == (u_int32_t)(bytes2type('C','O','M','M'))) { // AIFF header u_int16_t smpChnl,smpSize,smpRate1,smpRate2; u_int32_t numDataBlcks,encodeType; u_int8_t n,encodeChars[4],pasLen,pad; smpChnl = readShort(fileInput); numDataBlcks = readLong(fileInput); smpSize = readShort(fileInput); readShort(fileInput); smpRate1 = readShort(fileInput); fread(&pad,1,1,fileInput); readShort(fileInput); smpRate2 = readShort(fileInput); fread(&pad,1,1,fileInput); smpRate = smpRate1+smpRate2/65536*100; printf(" [%u-bit", smpSize); if (smpChnl == 1) printf(" monaural"); else if (smpChnl == 2) printf(" interleaved stereo"); else printf(" %u interleaved channels",smpChnl); printf(" %u.%u samples/sec]",smpRate1,smpRate2/65536*100); printf("\n [for %u blocks",numDataBlcks); if (commChunk == isAIFF) printf(" (%02u:%02u:%02u.%u)]", (numDataBlcks/smpRate/60/60),(numDataBlcks/smpRate/60%60), (numDataBlcks/smpRate%60),(numDataBlcks%smpRate)); else { if (fread(&encodeChars,4,1,fileInput) <= 0) break; encodeType = bytes2type(encodeChars[0],encodeChars[1],encodeChars[2],encodeChars[3]); if (encodeType == (u_int32_t)(bytes2type('s','o','w','t'))) { printf(" (%02u:%02u:%02u.%u)]", (numDataBlcks/smpRate/60/60),(numDataBlcks/smpRate/60%60), (numDataBlcks/smpRate%60),(numDataBlcks%smpRate)); } else printf("]"); printf("\n [%s encoding with type '%.4s']",mapType(encodeType),&encodeType); fread(&pasLen,1,1,fileInput); u_int8_t string[pasLen+1]; if (pasLen) { for (n=0; n < pasLen; n++) fread(&string[n],1,1,fileInput); string[pasLen] = 0; printf("\n Encoding text: %s",&string); } if (!(pasLen%2)) fread(&pad,1,1,fileInput); } } if (itemType == (u_int32_t)(bytes2type('S','S','N','D'))) { // audio data u_int32_t dataOffset,blckSize,padUnit,flags,numFrms, frmsStart,numStrms,buffer,dspWidth,dspHeight,reserve[4]; dataOffset = readLong(fileInput); blckSize = readLong(fileInput); if (dataOffset) printf(" Sample data begins @ 0x%08X",dataOffset); if (blckSize) printf(" with %u bytes per block",blckSize); // Check and adapt to odd byte alignment if (itemLen%2) fseek(fileInput,itemPos+1+itemLen,SEEK_SET); else fseek(fileInput,itemPos+itemLen,SEEK_SET); } if (itemType == (u_int32_t)(bytes2type('M','A','R','K'))) { // sample markers u_int16_t numMarks,markID; u_int32_t dataBlck; u_int8_t n,m,pasLen,pad; numMarks = readShort(fileInput); for (n=0; n < numMarks; n++) { markID = readShort(fileInput); dataBlck = readLong(fileInput); fread(&pasLen,1,1,fileInput); u_int8_t string[pasLen+1]; if (pasLen) { for (m=0; m < pasLen; n++) fread(&string[m],1,1,fileInput); string[pasLen] = 0; printf(" Marker %u: %s @ %u (%02u:%02u:%02u.%u)\n",markID,&string,dataBlck, (dataBlck/smpRate/60/60),(dataBlck/smpRate/60%60), (dataBlck/smpRate%60),(dataBlck%smpRate)); } if (!(pasLen%2)) fread(&pad,1,1,fileInput); } } if (itemType == (u_int32_t)(bytes2type('C','O','M','T'))) { // marker comments u_int16_t numComments,markID,n,m,textLen,pad; numComments = readShort(fileInput); for (n=0; n < numComments; n++) { readLong(fileInput); // date markID = readShort(fileInput); textLen = readShort(fileInput); if (textLen%2) fread(&pad,1,1,fileInput); u_int8_t string[textLen+1]; if (textLen != 0) { for (m=0; m < textLen; m++) fread(&string[m],1,1,fileInput); string[textLen] = 0; printf(" Marker %u text: %s\n",markID,&string); } } } if (itemType == (u_int32_t)(bytes2type('N','A','M','E')) || itemType == (u_int32_t)(bytes2type('(','C',')',' ')) || itemType == (u_int32_t)(bytes2type('A','N','N','O')) || itemType == (u_int32_t)(bytes2type('A','U','T','H'))) { u_int8_t n,string[itemLen+1]; if (itemLen != 0) { for (n=0; n < itemLen; n++) fread(&string[n],1,1,fileInput); string[itemLen] = 0; printf(" Text: %s",&string); } } #endif printf("\n"); #if 1 switch(itemType) { case bytes2type('I','N','S','T'): // instrument case bytes2type('M','I','D','I'): // MIDI case bytes2type('A','E','S','D'): // audio equip status data case bytes2type('A','P','P','L'): // application/program info case bytes2type('F','V','E','R'): // format version date case bytes2type('w','a','v','e'): // waveform chunk case bytes2type('N','A','M','E'): // title info case bytes2type('(','C',')',' '): // copyright info case bytes2type('A','N','N','O'): // comment info case bytes2type('A','U','T','H'): // author info break; default: findItems(fileInput,itemLevel+1,itemPos+itemLen); } #else switch(itemType) { case bytes2type('F','O','R','M'): // header findItems(fileInput,itemLevel+1,itemPos+itemLen); } #endif // Check and adapt to odd byte alignment if (itemLen%2) fseek(fileInput,itemPos+9+itemLen,SEEK_SET); else fseek(fileInput,itemPos+8+itemLen,SEEK_SET); } } int32_t main(u_int32_t cmdLen,char* cmds[]) { FILE *fileInput; if ((fileInput = fopen(cmdLen > 1 ? cmds[1]:"TEST.MOV","rb")) == NULL) return 1; printf("File offset Type (Type - Hex) [Descriptive name]\n"); printf("============================================================\n"); findItems(fileInput,0,0); }