sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

vadpcm_enc.c (16317B)


      1 #include <string.h>
      2 #include <assert.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <getopt.h>
      6 #include "vadpcm.h"
      7 
      8 static char usage[] = "[-t -l min_loop_length] -c codebook aifcfile compressedfile";
      9 
     10 int main(int argc, char **argv)
     11 {
     12     s32 c;
     13     char *progname = argv[0];
     14     s16 nloops = 0;
     15     s16 numMarkers;
     16     s16 *inBuffer;
     17     s16 ts;
     18     s32 minLoopLength = 800;
     19     s32 ***coefTable = NULL;
     20     s32 *state;
     21     s32 order;
     22     s32 npredictors;
     23     s32 done = 0;
     24     s32 truncate = 0;
     25     s32 num;
     26     s32 tableSize;
     27     s32 nsam;
     28     s32 left;
     29     u32 newEnd;
     30     s32 nRepeats;
     31     s32 i;
     32     s32 j;
     33     s32 k;
     34     s32 nFrames;
     35     s32 offset;
     36     s32 cChunkPos;
     37     s32 currentPos;
     38     s32 soundPointer = 0;
     39     s32 startPointer = 0;
     40     s32 startSoundPointer = 0;
     41     s32 cType;
     42     s32 nBytes = 0;
     43     u32 loopEnd;
     44     char *compName = "VADPCM ~4-1";
     45     char *appCodeName = "VADPCMCODES";
     46     char *appLoopName = "VADPCMLOOPS";
     47     u8 strnLen;
     48     Chunk AppChunk;
     49     Chunk FormChunk;
     50     ChunkHeader CSndChunk;
     51     ChunkHeader Header;
     52     CommonChunk CommChunk;
     53     SoundDataChunk SndDChunk;
     54     InstrumentChunk InstChunk;
     55     Loop *loops = NULL;
     56     ALADPCMloop *aloops;
     57     Marker *markers;
     58     CodeChunk cChunk;
     59 #ifdef __sgi
     60     char filename[1024];
     61 #else
     62     const char *filename;
     63 #endif
     64     FILE *fhandle;
     65     FILE *ifile;
     66     FILE *ofile;
     67 
     68     if (argc < 2)
     69     {
     70         fprintf(stderr, "%s %s\n", progname, usage);
     71         exit(1);
     72     }
     73 
     74     while ((c = getopt(argc, argv, "tc:l:")) != -1)
     75     {
     76         switch (c)
     77         {
     78         case 'c':
     79 #ifdef __sgi
     80             if (sscanf(optarg, "%s", filename) == 1)
     81 #else
     82             // Allow filenames with spaces
     83             filename = optarg;
     84 #endif
     85             {
     86                 if ((fhandle = fopen(filename, "r")) == NULL)
     87                 {
     88                     fprintf(stderr, "Codebook file %s could not be opened\n", filename);
     89                     exit(1);
     90                 }
     91                 if (readcodebook(fhandle, &coefTable, &order, &npredictors) != 0)
     92                 {
     93                     fprintf(stderr, "Error reading codebook\n");
     94                     exit(1);
     95                 }
     96             }
     97             break;
     98 
     99         case 't':
    100             truncate = 1;
    101             break;
    102 
    103         case 'l':
    104             sscanf(optarg, "%d", &minLoopLength);
    105             break;
    106 
    107         default:
    108             break;
    109         }
    110     }
    111 
    112     if (coefTable == 0)
    113     {
    114         fprintf(stderr, "You should specify a coefficient codebook with the [-c] option\n");
    115         exit(1);
    116     }
    117 
    118     argv += optind - 1;
    119     if ((ifile = fopen(argv[1], MODE_READ)) == NULL)
    120     {
    121         fprintf(stderr, "%s: input file [%s] could not be opened.\n", progname, argv[1]);
    122         exit(1);
    123     }
    124     if ((ofile = fopen(argv[2], MODE_WRITE)) == NULL)
    125     {
    126         fprintf(stderr, "%s: output file [%s] could not be opened.\n", progname, argv[2]);
    127         exit(1);
    128     }
    129 
    130     state = malloc(16 * sizeof(s32));
    131     for (i = 0; i < 16; i++)
    132     {
    133         state[i] = 0;
    134     }
    135 
    136 #ifndef __sgi
    137     // If there is no instrument chunk, make sure to output zeroes instead of
    138     // garbage. (This matches how the IRIX -g-compiled version behaves.)
    139     memset(&InstChunk, 0, sizeof(InstChunk));
    140 #endif
    141 
    142     inBuffer = malloc(16 * sizeof(s16));
    143 
    144     fread(&FormChunk, sizeof(Chunk), 1, ifile);
    145     BSWAP32(FormChunk.ckID)
    146     BSWAP32(FormChunk.ckSize)
    147     BSWAP32(FormChunk.formType)
    148 
    149     // @bug This doesn't check for FORM for AIFF files, probably due to mistaken operator precedence.
    150     if (!((FormChunk.ckID == 0x464f524d && // FORM
    151            FormChunk.formType == 0x41494643) || // AIFC
    152            FormChunk.formType == 0x41494646)) // AIFF
    153     {
    154         fprintf(stderr, "%s: [%s] is not an AIFF-C File\n", progname, argv[1]);
    155         exit(1);
    156     }
    157 
    158     while (!done)
    159     {
    160         num = fread(&Header, 8, 1, ifile);
    161         if (num <= 0)
    162         {
    163             done = 1;
    164             break;
    165         }
    166         BSWAP32(Header.ckID)
    167         BSWAP32(Header.ckSize)
    168 
    169         Header.ckSize++, Header.ckSize &= ~1;
    170         switch (Header.ckID)
    171         {
    172         case 0x434f4d4d: // COMM
    173             offset = ftell(ifile);
    174             num = fread(&CommChunk, sizeof(CommonChunk), 1, ifile);
    175             if (num <= 0)
    176             {
    177                 fprintf(stderr, "%s: error parsing file [%s]\n", progname, argv[1]);
    178                 done = 1;
    179             }
    180             BSWAP16(CommChunk.numChannels)
    181             BSWAP16(CommChunk.numFramesH)
    182             BSWAP16(CommChunk.numFramesL)
    183             BSWAP16(CommChunk.sampleSize)
    184             if (FormChunk.formType != 0x41494646) // AIFF
    185             {
    186                 BSWAP16(CommChunk.compressionTypeH)
    187                 BSWAP16(CommChunk.compressionTypeL)
    188                 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL;
    189                 if (cType != 0x4e4f4e45) // NONE
    190                 {
    191                     fprintf(stderr, "%s: file [%s] contains compressed data.\n", progname, argv[1]);
    192                     exit(1);
    193                 }
    194             }
    195             if (CommChunk.numChannels != 1)
    196             {
    197                 fprintf(stderr, "%s: file [%s] contains %ld channels, only 1 channel supported.\n", progname, argv[1], (long) CommChunk.numChannels);
    198                 exit(1);
    199             }
    200             if (CommChunk.sampleSize != 16)
    201             {
    202                 fprintf(stderr, "%s: file [%s] contains %ld bit samples, only 16 bit samples supported.\n", progname, argv[1], (long) CommChunk.sampleSize);
    203                 exit(1);
    204             }
    205             fseek(ifile, offset + Header.ckSize, SEEK_SET);
    206             break;
    207 
    208         case 0x53534e44: // SSND
    209             offset = ftell(ifile);
    210             fread(&SndDChunk, sizeof(SoundDataChunk), 1, ifile);
    211             BSWAP32(SndDChunk.offset)
    212             BSWAP32(SndDChunk.blockSize)
    213             // The assert error messages specify line numbers 219/220. Match
    214             // that using a #line directive.
    215 #ifdef __sgi
    216 #  line 218
    217 #endif
    218             assert(SndDChunk.offset == 0);
    219             assert(SndDChunk.blockSize == 0);
    220             soundPointer = ftell(ifile);
    221             fseek(ifile, offset + Header.ckSize, SEEK_SET);
    222             break;
    223 
    224         case 0x4d41524b: // MARK
    225             offset = ftell(ifile);
    226             fread(&numMarkers, sizeof(s16), 1, ifile);
    227             BSWAP16(numMarkers)
    228             markers = malloc(numMarkers * sizeof(Marker));
    229             for (i = 0; i < numMarkers; i++)
    230             {
    231                 fread(&markers[i], sizeof(Marker), 1, ifile);
    232                 BSWAP16(markers[i].MarkerID)
    233                 BSWAP16(markers[i].positionH)
    234                 BSWAP16(markers[i].positionL)
    235                 fread(&strnLen, 1, 1, ifile);
    236                 if ((strnLen & 1) != 0)
    237                 {
    238                     fseek(ifile, strnLen, SEEK_CUR);
    239                 }
    240                 else
    241                 {
    242                     fseek(ifile, strnLen + 1, SEEK_CUR);
    243                 }
    244             }
    245             fseek(ifile, offset + Header.ckSize, SEEK_SET);
    246             break;
    247 
    248         case 0x494e5354: // INST
    249             offset = ftell(ifile);
    250             fread(&InstChunk, sizeof(InstrumentChunk), 1, ifile);
    251             BSWAP16(InstChunk.sustainLoop.playMode)
    252             BSWAP16(InstChunk.sustainLoop.beginLoop)
    253             BSWAP16(InstChunk.sustainLoop.endLoop)
    254             BSWAP16(InstChunk.releaseLoop.playMode)
    255             BSWAP16(InstChunk.releaseLoop.beginLoop)
    256             BSWAP16(InstChunk.releaseLoop.endLoop)
    257             aloops = malloc(2 * sizeof(ALADPCMloop));
    258             loops = malloc(2 * sizeof(Loop));
    259             if (InstChunk.sustainLoop.playMode == 1)
    260             {
    261                 loops[nloops].beginLoop = InstChunk.sustainLoop.beginLoop;
    262                 loops[nloops].endLoop = InstChunk.sustainLoop.endLoop;
    263                 nloops++;
    264             }
    265             if (InstChunk.releaseLoop.playMode == 1)
    266             {
    267                 loops[nloops].beginLoop = InstChunk.releaseLoop.beginLoop;
    268                 loops[nloops].endLoop = InstChunk.releaseLoop.endLoop;
    269                 nloops++;
    270             }
    271             fseek(ifile, offset + Header.ckSize, SEEK_SET);
    272             break;
    273 
    274         default:
    275             fseek(ifile, Header.ckSize, SEEK_CUR);
    276             break;
    277         }
    278     }
    279 
    280     FormChunk.formType = 0x41494643; // AIFC
    281     BSWAP32(FormChunk.ckID)
    282     BSWAP32(FormChunk.ckSize)
    283     BSWAP32(FormChunk.formType)
    284     fwrite(&FormChunk, sizeof(Chunk), 1, ofile);
    285 
    286     Header.ckID = 0x434f4d4d; // COMM
    287     Header.ckSize = sizeof(CommonChunk) + 1 + 11;
    288     BSWAP32(Header.ckID)
    289     BSWAP32(Header.ckSize)
    290     fwrite(&Header, sizeof(ChunkHeader), 1, ofile);
    291     CommChunk.compressionTypeH = 0x5641; // VA
    292     CommChunk.compressionTypeL = 0x5043; // PC
    293     cChunkPos = ftell(ofile);
    294     // CommChunk written later
    295     fwrite(&CommChunk, sizeof(CommonChunk), 1, ofile);
    296     strnLen = sizeof("VADPCM ~4-1") - 1;
    297     fwrite(&strnLen, 1, 1, ofile);
    298     fwrite(compName, strnLen, 1, ofile);
    299 
    300     Header.ckID = 0x494e5354; // INST
    301     Header.ckSize = sizeof(InstrumentChunk);
    302     BSWAP32(Header.ckID)
    303     BSWAP32(Header.ckSize)
    304     fwrite(&Header, sizeof(ChunkHeader), 1, ofile);
    305     BSWAP16(InstChunk.sustainLoop.playMode)
    306     BSWAP16(InstChunk.sustainLoop.beginLoop)
    307     BSWAP16(InstChunk.sustainLoop.endLoop)
    308     BSWAP16(InstChunk.releaseLoop.playMode)
    309     BSWAP16(InstChunk.releaseLoop.beginLoop)
    310     BSWAP16(InstChunk.releaseLoop.endLoop)
    311     fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile);
    312 
    313     tableSize = order * 2 * npredictors * 8;
    314     strnLen = sizeof("VADPCMCODES") - 1;
    315     AppChunk.ckID = 0x4150504c; // APPL
    316     AppChunk.ckSize = 4 + tableSize + 1 + strnLen + sizeof(CodeChunk);
    317     AppChunk.formType = 0x73746f63; // stoc
    318     BSWAP32(AppChunk.ckID)
    319     BSWAP32(AppChunk.ckSize)
    320     BSWAP32(AppChunk.formType)
    321     fwrite(&AppChunk, sizeof(Chunk), 1, ofile);
    322     cChunk.version = 1;
    323     cChunk.order = order;
    324     cChunk.nEntries = npredictors;
    325     BSWAP16(cChunk.version)
    326     BSWAP16(cChunk.order)
    327     BSWAP16(cChunk.nEntries)
    328     fwrite(&strnLen, 1, 1, ofile);
    329     fwrite(appCodeName, strnLen, 1, ofile);
    330     fwrite(&cChunk, sizeof(CodeChunk), 1, ofile);
    331 
    332     for (i = 0; i < npredictors; i++)
    333     {
    334         for (j = 0; j < order; j++)
    335         {
    336             for (k = 0; k < 8; k++)
    337             {
    338                 ts = coefTable[i][k][j];
    339                 BSWAP16(ts)
    340                 fwrite(&ts, sizeof(s16), 1, ofile);
    341             }
    342         }
    343     }
    344 
    345     currentPos = 0;
    346     if (soundPointer > 0)
    347     {
    348         fseek(ifile, soundPointer, SEEK_SET);
    349     }
    350     else
    351     {
    352         fprintf(stderr, "%s: Error in sound chunk", progname);
    353         exit(1);
    354     }
    355 
    356     soundPointer = ftell(ofile);
    357     // CSndChunk written later
    358     fwrite(&CSndChunk, sizeof(ChunkHeader), 1, ofile);
    359     BSWAP32(SndDChunk.offset)
    360     BSWAP32(SndDChunk.blockSize)
    361     fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile);
    362     startSoundPointer = ftell(ifile);
    363     for (i = 0; i < nloops; i++)
    364     {
    365         if (lookupMarker(&aloops[i].start, loops[i].beginLoop, markers, numMarkers) != 0)
    366         {
    367             fprintf(stderr, "%s: Start loop marker not found\n", progname);
    368         }
    369         else if (lookupMarker(&aloops[i].end, loops[i].endLoop, markers, numMarkers) != 0)
    370         {
    371             fprintf(stderr, "%s: End loop marker not found\n", progname);
    372         }
    373         else
    374         {
    375             startPointer = startSoundPointer + aloops[i].start * 2;
    376             nRepeats = 0;
    377             newEnd = aloops[i].end;
    378             while (newEnd - aloops[i].start < minLoopLength)
    379             {
    380                 nRepeats++;
    381                 newEnd += aloops[i].end - aloops[i].start;
    382             }
    383 
    384             while (currentPos <= aloops[i].start)
    385             {
    386                 if (fread(inBuffer, sizeof(s16), 16, ifile) == 16)
    387                 {
    388                     BSWAP16_MANY(inBuffer, 16)
    389                     vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
    390                     currentPos += 16;
    391                     nBytes += 9;
    392                 }
    393                 else
    394                 {
    395                     fprintf(stderr, "%s: Not enough samples in file [%s]\n", progname, argv[1]);
    396                     exit(1);
    397                 }
    398             }
    399 
    400             for (j = 0; j < 16; j++)
    401             {
    402                 if (state[j] >= 0x8000)
    403                 {
    404                     state[j] = 0x7fff;
    405                 }
    406                 if (state[j] < -0x7fff)
    407                 {
    408                     state[j] = -0x7fff;
    409                 }
    410                 aloops[i].state[j] = state[j];
    411             }
    412 
    413             aloops[i].count = -1;
    414             while (nRepeats > 0)
    415             {
    416                 for (; currentPos + 16 < aloops[i].end; currentPos += 16)
    417                 {
    418                     if (fread(inBuffer, sizeof(s16), 16, ifile) == 16)
    419                     {
    420                         BSWAP16_MANY(inBuffer, 16)
    421                         vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
    422                         nBytes += 9;
    423                     }
    424                 }
    425                 left = aloops[i].end - currentPos;
    426                 fread(inBuffer, sizeof(s16), left, ifile);
    427                 BSWAP16_MANY(inBuffer, left)
    428                 fseek(ifile, startPointer, SEEK_SET);
    429                 fread(inBuffer + left, sizeof(s16), 16 - left, ifile);
    430                 BSWAP16_MANY(inBuffer + left, 16 - left)
    431                 vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
    432                 nBytes += 9;
    433                 currentPos = aloops[i].start - left + 16;
    434                 nRepeats--;
    435             }
    436             aloops[i].end = newEnd;
    437         }
    438     }
    439 
    440     nFrames = (CommChunk.numFramesH << 16) + CommChunk.numFramesL;
    441     if ((nloops > 0U) & truncate)
    442     {
    443         lookupMarker(&loopEnd, loops[nloops - 1].endLoop, markers, numMarkers);
    444         nFrames = (loopEnd + 16 < nFrames ? loopEnd + 16 : nFrames);
    445     }
    446 
    447     while (currentPos < nFrames)
    448     {
    449         if (nFrames - currentPos < 16)
    450         {
    451             nsam = nFrames - currentPos;
    452         }
    453         else
    454         {
    455             nsam = 16;
    456         }
    457 
    458         if (fread(inBuffer, 2, nsam, ifile) == nsam)
    459         {
    460             BSWAP16_MANY(inBuffer, nsam)
    461             vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, nsam);
    462             currentPos += nsam;
    463             nBytes += 9;
    464         }
    465         else
    466         {
    467             fprintf(stderr, "Missed a frame!\n");
    468             break;
    469         }
    470     }
    471 
    472     if (nBytes % 2)
    473     {
    474         nBytes++;
    475         ts = 0;
    476         fwrite(&ts, 1, 1, ofile);
    477     }
    478 
    479     if (nloops > 0)
    480     {
    481         strnLen = sizeof("VADPCMLOOPS") - 1;
    482         AppChunk.ckID = 0x4150504c; // APPL
    483         AppChunk.ckSize = nloops * sizeof(ALADPCMloop) + strnLen + 4 + 1 + 2 + 2;
    484         AppChunk.formType = 0x73746f63; // stoc
    485         BSWAP32(AppChunk.ckID)
    486         BSWAP32(AppChunk.ckSize)
    487         BSWAP32(AppChunk.formType)
    488         fwrite(&AppChunk, sizeof(Chunk), 1, ofile);
    489         fwrite(&strnLen, 1, 1, ofile);
    490         fwrite(appLoopName, strnLen, 1, ofile);
    491         ts = 1;
    492         BSWAP16(ts)
    493         fwrite(&ts, sizeof(s16), 1, ofile);
    494         BSWAP16(nloops)
    495         fwrite(&nloops, sizeof(s16), 1, ofile);
    496         BSWAP16(nloops)
    497         for (i = 0; i < nloops; i++)
    498         {
    499             BSWAP32(aloops[i].start)
    500             BSWAP32(aloops[i].end)
    501             BSWAP32(aloops[i].count)
    502             BSWAP16_MANY(aloops[i].state, 16)
    503             fwrite(&aloops[i], sizeof(ALADPCMloop), 1, ofile);
    504         }
    505     }
    506 
    507     fseek(ofile, soundPointer, SEEK_SET);
    508     CSndChunk.ckID = 0x53534e44; // SSND
    509     CSndChunk.ckSize = nBytes + 8;
    510     BSWAP32(CSndChunk.ckID)
    511     BSWAP32(CSndChunk.ckSize)
    512     fwrite(&CSndChunk, sizeof(ChunkHeader), 1, ofile);
    513     fseek(ofile, cChunkPos, SEEK_SET);
    514     nFrames = nBytes * 16 / 9;
    515     CommChunk.numFramesH = nFrames >> 16;
    516     CommChunk.numFramesL = nFrames & 0xffff;
    517     BSWAP16(CommChunk.numChannels)
    518     BSWAP16(CommChunk.numFramesH)
    519     BSWAP16(CommChunk.numFramesL)
    520     BSWAP16(CommChunk.sampleSize)
    521     BSWAP16(CommChunk.compressionTypeH)
    522     BSWAP16(CommChunk.compressionTypeL)
    523     fwrite(&CommChunk, sizeof(CommonChunk), 1, ofile);
    524     fclose(ifile);
    525     fclose(ofile);
    526     return 0;
    527 }