//-----------------------------------
//--- 010 Editor v2.0 Binary Template
//
// File:     WAVTemplate.bt
// Author:   SweetScape Software
// Revision: 1.0
// Purpose:  Defines a template for
//    parsing WAV audio files.
//-----------------------------------

// Typedefs for the wave file
typedef char ID[4];

// Record whether we have found a format chunk yet
local int haveValidFormat = false;

//-----------------------------------
// Define structures used in WAV files

// Stores the file header information
typedef struct 
{
    ID     groupID;
    long   size;
    ID     riffType;
} WAVRIFFHEADER;

// Stores the format information for the file
typedef struct {
    ID             chunkID;
    long           chunkSize;
    local int pos = FTell();
    short          wFormatTag;
    unsigned short wChannels;
    unsigned long  dwSamplesPerSec;
    unsigned long  dwAvgBytesPerSec;
    unsigned short wBlockAlign;
    unsigned short wBitsPerSample;

    // Mark that we have found a valid format chunk
    haveValidFormat = true;

    // Unknown data at the end of the chunk
    if( chunkSize > (FTell() - pos) )
        uchar unknown[ chunkSize - (FTell() - pos) ];

    // Padding so the next chunk starts on an even byte
    if( chunkSize & 1 )
        uchar padding;
} FORMATCHUNK;

// Stores the actual wave data
typedef struct 
{
    ID             chunkID;
    long           chunkSize;

    // Test if we have a valid format
    if( !haveValidFormat )
    {
        Warning( "File contains no valid WAVE format chunk." );
        return -1;
    }

    // Parse the samples of the data
    if( ((format.wBitsPerSample != 8) && (format.wBitsPerSample != 16) && (format.wBitsPerSample != 32)) 
        || (chunkSize % (int)format.wBlockAlign != 0) )
    {
        // Unsupported storage method used
        unsigned char  waveformData[chunkSize];
    }
    else if( (format.wChannels == 1) && (format.wBitsPerSample == 8) )
    {
        // Define an array of 8-bit samples - common case
        uchar samples[ chunkSize ];
    }
    else if( (format.wChannels == 1) && (format.wBitsPerSample == 16) )
    {
        // Define an array of 16-bit samples - common case
        short samples[ chunkSize/2 ];
    }
    else if( (format.wChannels == 1) && (format.wBitsPerSample == 32) )
    {
        // Define an array of 32-bit samples - common case
        int samples[ chunkSize/4 ];
    }
    else
    {
        // Define general case sample
        struct SAMPLES {
            if( format.wBitsPerSample == 8 )
                 uchar channels[ format.wChannels ];
            else if( format.wBitsPerSample == 16 )
                 short channels[ format.wChannels ];
            else if( format.wBitsPerSample == 32 )
                 int   channels[ format.wChannels ];
        } samples[ chunkSize / (int)format.wBlockAlign ];
    }

    // Padding so the next chunk starts on an even byte
    if( (chunkSize & 1) && (FTell() < FileSize()) )
        uchar padding;
} DATACHUNK;

// Stores the size of the wave after decompression
typedef struct
{
    ID             chunkID;
    long           chunkSize;
    unsigned long  uncompressedSize;
} FACTCHUNK;

// Stores a list of cue points or markers to points in the data
typedef struct {
    long    dwIdentifier;
    long    dwPosition;
    ID      fccChunk;
    long    dwChunkStart;
    long    dwBlockStart;
    long    dwSampleOffset;
} CUEPOINT;

typedef struct {
    ID        chunkID;
    long      chunkSize;
    local int pos = FTell();
    long      dwCuePoints;
    CUEPOINT  points[dwCuePoints];

    // Unknown data at the end of the chunk
    if( chunkSize > (FTell() - pos) )
        uchar unknown[ chunkSize - (FTell() - pos) ];
} CUECHUNK;

// Define a list chunk with a set of subchunks
typedef struct {
    ID        chunkID;
    long      chunkSize;
    char      listData[chunkSize];

    // Padding so the next chunk starts on an even byte
    if( (chunkSize & 1) && (FTell() < FileSize()) )
        uchar padding;
} LISTSUBCHUNK;

typedef struct {
    ID        chunkID;
    long      chunkSize;
    local quad pos = FTell();
    ID        chunkType;

    // Read the subchunks
    while( FTell() - pos < chunkSize )
        LISTSUBCHUNK subchunk;

    // Padding so the next chunk starts on an even byte
    if( (chunkSize & 1) && (FTell() < FileSize()) )
        uchar padding;
} LISTCHUNK;

// A chunk which could not be identified
typedef struct {
    ID        chunkID;
    long      chunkSize;
    uchar     unknownData[chunkSize];

    // Padding so the next chunk starts on an even byte
    if( (chunkSize & 1) && (FTell() < FileSize()) )
        uchar padding;
} UNKNOWNCHUNK;

//---------------------------------------------

// Define the headers
LittleEndian(); 
SetBackColor( cLtPurple );
WAVRIFFHEADER header;

// Check for valid header
if( header.groupID != "RIFF" || header.riffType != "WAVE" )
{
    Warning( "File is not a valid wave file. Template stopped." );
    return -1;
}

// Read the file as a set of chunks
local char tag[5];
local uint size;
while( !FEof() )
{
    // Read the chunk tag 
    ReadBytes( tag, FTell(), 4 );
    tag[4] = 0;

    // See which chunk this is
    switch( tag )
    {
        case "fmt ":
            SetBackColor( cLtGray );
            FORMATCHUNK format;
            break;
        case "data":
            SetBackColor( cNone );
            DATACHUNK   data;
            break;
        case "fact":
            SetBackColor( cLtBlue );
            FACTCHUNK   fact;
            break;
        case "cue ":
            SetBackColor( cLtGray );
            CUECHUNK    cue;
            break;
        case "LIST":
            SetBackColor( cLtYellow );
            LISTCHUNK   list;
            break;
        default:
            // Unknown chunk
            size = ReadUInt( FTell()+4 );
            Printf( "Encountered unknown chunk '%s' of size %d at position %Ld.\n",
                tag, size, FTell() );
            SetBackColor( cNone );
            UNKNOWNCHUNK unknown;
            break;
    }
}




 