// (c) 1990-2007 Electronic Arts Inc.

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                           Rifft v1.06                            ",
"                                                                  ",
"              by Ian Davids & FrANK G. Barchard, EAC              ",
"                                                                  ",
"                    Main program - Jun 19, 2006                   ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 030209 ID initial version                                ",
"    1.01 030509 FB 64 bit IO, Chunk, OpenDML                      ",
"    1.02 040821 FB madcow added                                   ",
"    1.03 040821 FB divx                                           ",
"    1.04 040821 FB vp6 and moved to util                          ",
"    1.05 050305 FB unsigned size fields, show fps                 ",
"    1.06 060619 FB zero length chunks                             ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

#include "ansi.h"
#include "rifft.h"
#include "stdgimex.h"
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#include <vfw.h>

const char TextVersion[]="1.06 (Samba) 2007.12.4";

/////////////////////////////////////////////////////////////
// Structures
/////////////////////////////////////////////////////////////
#if ! defined(GCALL)
#if defined(_MSC_VER)
#define GCALL __stdcall
#else
#define GCALL
#endif
#endif

//----------------------------
// Describes list chunks (RIFF/LIST)
struct RIFF_HEADERSIDS
{
    char * fourcc;
    char * description;
    char * colourFore;
    char * colourBack;
    int colourmodifier;
};

//----------------------------
// main program info struct
struct INFO_HEADER
{
    char fourcc[4];
    unsigned int size;
    char  firstFourBytes[4];
    int  listType;
    GPOS indentPosList[200];
    int  numIndents;
    GPOS filePos;
    char * blockData;
    int interactiveMode;
    int verboseMode;
    char filename[512];
    int sizeAdjust;
    int bigEndian;
    int chunkNum;
};


static int CHUNKFLAG_FIRST=1;
static int CHUNKFLAG_CHUNK=2;
static int CHUNKFLAG_BIGENDIAN=4; // size field is big endian

//----------------------------
// describes how to handle
// a known RIFF chunk
struct RIFF_INFO
{
    char * fourcc;            // has the fourCC of the chunk
    char * name;               // short descriptive name of the chunk
    char * description;        // longer more verbose name (possibly used in -v mode)
    int listType;              // some chunks are allowed to contain smaller chunks
    int hasHeader;             // some chunks have ID's that force the user to jump over 12 bytes
    char * colourFore;         // colour information for displaying this chunks info
    char * colourBack;
    int colourmodifier;
    int hasHandler;            // chunk type has a special handler for verbose mode
    void (GCALL * Handler)(INFO_HEADER * ih);
    int chunkFlags;            // 1=start chunk, 2=chunk vs riff, 4=big endian
};


struct INDX_BASEFORM
{
    WORD LongsPerEntry;        // size of each entry in the Index Array
    BYTE IndexSubtype;         // future use must be zero
    BYTE IndexType;            // one of the AVI_INDEX_* codes
    DWORD EntriesInUse;        // index of the first unused member in the index array
    DWORD ChunkID;             // fcc of what is indexed
    __int64 BaseOffset;          // all offsets in the index are relative to this
    DWORD Reserved;            // must be zero
};

struct VPRP_HEADER
{
    DWORD VideoFormatToken;    //
    DWORD VideoStandard;
    DWORD VerticalRefreshRate;
    DWORD HTotalInt;
    DWORD VTotalInLines;
    DWORD FrameAspectRatio;
    DWORD FrameWidthInPixels;
    DWORD FrameHeightInLines;
    DWORD FieldPerFrame;
};

// Block Handlers for verbose mode
void GCALL avih_handler(INFO_HEADER * ih);
void GCALL strh_handler(INFO_HEADER * ih);
void GCALL indx_handler(INFO_HEADER * ih);
void GCALL dmlh_handler(INFO_HEADER * ih);
void GCALL vprp_handler(INFO_HEADER * ih);
void GCALL info_handler(INFO_HEADER * ih);
void GCALL madk_handler(INFO_HEADER * ih);
void GCALL divx_handler(INFO_HEADER * ih);
void GCALL mvxf_handler(INFO_HEADER * ih);

RIFF_HEADERSIDS riff_headerIDs[] = {
    // AVI file types, identify different RIFF file types
    {"CPPO","APPS Software International C++ Object Format","white","blue",1},
    {"PAL ","Palette File Format","white","blue",1},
    {"RDIB","Device Independent Bitmap Format","white","blue",1},
    {"RMID","MIDI Audio Format","white","blue",1},
    {"RMMP","Multimedia Movie File Format","white","blue",1},
    {"WAVE","Waveform Audio Format","blue","black",1},
    {"AVI ","Audio Video Interleave Format","white","blue",1},
    {"AVIX","OpenDML AVI Extension RIFF chunk","white","blue",1},
    {"ILBM","Interleaved Bitmap (IFF)","white","blue",1},
    {"ON2f","ON2 format","blue","black",1},
    {"MVhd","EA Chunked AVI format","white","blue",1},

    // LIST chunk types, identify what type of chunk a LIST chunk is
    {"hdrl","{hrdl} Main AVI Header","blue","black",0},
    {"strl","{strl} Stream List Block","blue","black",0},
    {"odml","{odml} OpenDML List Block","blue","black",0},
    {"movi","{movi} Movie Data Segment","blue","black",0},
    {"rec ","{rec } Movie Data Subchunk (read block for performance reasons)","blue","black",0},
    {"INFO","{INFO} General file information chunk","blue","black",0},

    NULL
};


RIFF_INFO riff_info[] = {
    {"RIFF","RIFF File Header","Master file chunk, contains all other chunks",1,1,"white","black",1,0,0,CHUNKFLAG_FIRST},
    {"LIST","List container chunk","Contains multiple arbitrary subchunks",1,1,"green","black",2,0,0,0},
    {"avih","Bitmap info for the entire file","Essentially a BITMAPINFO structure for the file",0,0,"white","black",0,1,avih_handler,0},
    {"strh","Stream Header","Contains the basic stream description",0,0,"white","black",0,1,strh_handler,0},
    {"strf","Stream Format","Contains specific stream information",0,0,"white","black",0,0,0,0},
    {"strd","Optional Stream Codec Data","Contains Codec State Data",0,0,"white","black",0,0,0,0},
    {"strn","Stream Name","Application specific information naming the current stream",0,0,"white","black",0,0,0,0},
    {"00dc","Compressed DIB","Contains video data relevant to stream 0",0,0,"yellow","black",0,0,0,0},
    {"01dc","Compressed DIB","Contains video data relevant to stream 1",0,0,"yellow","black",0,0,0,0},
    {"02dc","Compressed DIB","Contains video data relevant to stream 2",0,0,"yellow","black",0,0,0,0},
    {"03dc","Compressed DIB","Contains video data relevant to stream 3",0,0,"yellow","black",0,0,0,0},
    {"04dc","Compressed DIB","Contains video data relevant to stream 4",0,0,"yellow","black",0,0,0,0},
    {"05dc","Compressed DIB","Contains video data relevant to stream 5",0,0,"yellow","black",0,0,0,0},
    {"00iv","Indeo Compressed DIB","Contains video data relevant to stream 0",0,0,"yellow","black",0,0,0,0},
    {"00db","Uncompressed DIB","Contains uncompressed video data relevant to stream 0",0,0,"white","black",0,0,0,0},
    {"01db","Uncompressed DIB","Contains uncompressed video data relevant to stream 1",0,0,"white","black",0,0,0,0},
    {"02db","Uncompressed DIB","Contains uncompressed video data relevant to stream 2",0,0,"white","black",0,0,0,0},
    {"03db","Uncompressed DIB","Contains uncompressed video data relevant to stream 3",0,0,"white","black",0,0,0,0},
    {"04db","Uncompressed DIB","Contains uncompressed video data relevant to stream 4",0,0,"white","black",0,0,0,0},
    {"05db","Uncompressed DIB","Contains uncompressed video data relevant to stream 5",0,0,"white","black",0,0,0,0},
    {"00wb","Audio Data","Contains audio data relevant to stream 0",0,0,"cyan","black",0,0,0,0},
    {"01wb","Audio Data","Contains audio data relevant to stream 1",0,0,"cyan","black",0,0,0,0},
    {"02wb","Audio Data","Contains audio data relevant to stream 2",0,0,"cyan","black",0,0,0,0},
    {"03wb","Audio Data","Contains audio data relevant to stream 3",0,0,"cyan","black",0,0,0,0},
    {"04wb","Audio Data","Contains audio data relevant to stream 4",0,0,"cyan","black",0,0,0,0},
    {"05wb","Audio Data","Contains audio data relevant to stream 5",0,0,"cyan","black",0,0,0,0},
    {"00pc","Palette Change","Contains palette data relevant to stream 0",0,0,"magenta","black",1,0,0,0},
    {"01pc","Palette Change","Contains palette data relevant to stream 1",0,0,"magenta","black",1,0,0,0},
    {"02pc","Palette Change","Contains palette data relevant to stream 2",0,0,"magenta","black",1,0,0,0},
    {"03pc","Palette Change","Contains palette data relevant to stream 3",0,0,"magenta","black",1,0,0,0},
    {"04pc","Palette Change","Contains palette data relevant to stream 4",0,0,"magenta","black",1,0,0,0},
    {"05pc","Palette Change","Contains palette data relevant to stream 5",0,0,"magenta","black",1,0,0,0},
    {"JUNK","Program Specific Meta Data","Often contains meta data like the creating program name",0,0,"white","black",0,0,0,0},
    {"idx1","AVI file index","Contains information about each chunk in the file",0,0,"white","black",0,0,0,0},
    {"indx","OpenDML AVI Extension Index Chunk","Contains information about each chunk in the file, or pointers to other index chunks",0,0,"white","black",0,1,indx_handler,0},
    {"ix00","OpenDML Interleaved Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"ix01","OpenDML Interleaved Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"ix02","OpenDML Interleaved Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"ix03","OpenDML Interleaved Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"ix04","OpenDML Interleaved Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"ix05","OpenDML Interleaved Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"00ix","OpenDML Interleaved Data Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"01ix","OpenDML Interleaved Data Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"02ix","OpenDML Interleaved Data Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"03ix","OpenDML Interleaved Data Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"04ix","OpenDML Interleaved Data Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"05ix","OpenDML Interleaved Data Index Chunk","Contains information about chunks in a specific stream in the file",0,0,"white","black",0,1,indx_handler,0},
    {"dmlh","Extended AVI header","Contains the real frame count of the AVI file even if there is an AVIX chunk",0,0,"white","black",0,1,dmlh_handler,0},
    {"vprp","Video properties header","Identifies video signal properties: (type of signal and framing of compression)",0,0,"white","black",0,1,vprp_handler,0},
    {"tcdl","Timecode Discontinuity Table","",0,0,"white","black",0,0,0,0},
    {"time","Timecode Stream","Defines a timecode value for every tick of the timecode stream",0,0,"white","black",0,0,0,0},
    {"ISMP","Timecode contains SMPTE timecode type","Indicates chunk contains the SMPTE timecode of the digitization starting point. The time is expressed as a zero-terminated text string of the form HH:MM:SS:FF.",0,0,"white","black",0,0,0,0},
    {"IDIT","ASCII string timecode","chunk specifies the time and date digitizing commenced. This time is contained in an ASCII string (Wed Jan 02 02:03:55 1990",0,0,"white","black",0,0,0,0},
    {"INAM","Name chunk","Program name chunk",0,0,"white","black",0,1,info_handler,0},
    {"IARL","Archival Location","Indicates where the subject of the file is archived",0,0,"white","black",0,1,info_handler,0},
    {"IART","Artist","Lists the artist of the original subject of the file",0,0,"white","black",0,1,info_handler,0},
    {"ICMS","Commissioned","Lists the name of the person or org. that commissioned the subject of the file",0,0,"white","black",0,1,info_handler,0},
    {"ICMT","Comments","Provides general comments about the file or the subject of the file, end each sentence with a period NO NEWLINES!",0,0,"white","black",0,1,info_handler,0},
    {"ICOP","Copyright","Records the copyright information for the file",0,0,"white","black",0,1,info_handler,0},
    {"ICRD","Creation Date","Specifies the date the subject of the file was created y-m-d format",0,0,"white","black",0,1,info_handler,0},
    {"ICRP","Cropped","Describes if an image has been cropped and how: ie lower-right corner",0,0,"white","black",0,1,info_handler,0},
    {"IDIM","Dimensions","Specifies the dimensions of the original subject of the file",0,0,"white","black",0,1,info_handler,0},
    {"IDPI","Dots Per Inch","Stores the dots per inch setting of the digitizer used to produce the file.",0,0,"white","black",0,1,info_handler,0},
    {"IENG","Engineer","Stores the name of the engineer who worked on the file,semicolon and blank are the seperators",0,0,"white","black",0,1,info_handler,0},
    {"IGNR","Genre","Describes the original work such as Landscape or Still Life",0,0,"white","black",0,1,info_handler,0},
    {"IKEY","Keywords","Provides a list of keywords that refer to the file. Semicolon blank is the seperator",0,0,"white","black",0,1,info_handler,0},
    {"ILGT","Lightness","Describes the changes in lightness settings on the digitizer required to produce the file",0,0,"white","black",0,1,info_handler,0},
    {"IMED","Medium","Describes the original subject of the file, such as computer image, drawing, lithograph",0,0,"white","black",0,1,info_handler,0},
    {"IPLT","Palette Setting","Specifies the number of colours requested when digitizing the image",0,0,"white","black",0,1,info_handler,0},
    {"IPRD","Product","Specifies the name of the title the file was originally intended for.",0,0,"white","black",0,1,info_handler,0},
    {"ISBJ","Subject","Describes the contents of the file",0,0,"white","black",0,1,info_handler,0},
    {"ISFT","Software","Identifies the name of the software package used to make the file",0,0,"white","black",0,1,info_handler,0},
    {"ISHP","Sharpness","Identifies the changes in sharpness for the digitizer required to produce the file",0,0,"white","black",0,1,info_handler,0},
    {"ISRC","Source","Identifies the name of the person who supplied the original subject of the file",0,0,"white","black",0,1,info_handler,0},
    {"ISRF","Source Form","Idnetifies the original form of the material that was digitized ie slide, paperm map",0,0,"white","black",0,1,info_handler,0},
    {"ITCH","Technician","Identifies the technician who digitized the subject file",0,0,"white","black",0,1,info_handler,0},
    // WAVE File Chunks
    {"fmt ","Wave File Data Format Chunk","Specifies the format of the WAVE data",0,0,"white","black",0,0,0,0},
    {"data","Wave File Data Chunk","Actual PCM encoded WAVE data",0,0,"white","black",0,0,0,0},

    // EA Chunk formats
    {"DIV1","DivX Header","Overall File Description",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"HEAD","DivX Media Descriptor","width height",0,0,"white","black",0,1,divx_handler,CHUNKFLAG_CHUNK},
    {"FRAM","DivX Frame","Compressed frame",0,0,"white","black",0,0,0,CHUNKFLAG_CHUNK},
    {"MPCh","MPeg2 Chunk","MPC wrapped slices of frame into chunk",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"MADk","Madcow Keyframe Chunk","Madcow keyframes contain the size information",0,0,"white","black",0,1,madk_handler,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"MADe","Madcow Extra Frame Chunk","Madcow extra frames are predicted from the keyframes but can be dropped",0,0,"white","black",0,0,0,CHUNKFLAG_CHUNK},
    {"MADm","Madcow Motion Frame Chunk","Madcow motion frames are predicted from the keyframes",0,0,"white","black",0,0,0,CHUNKFLAG_CHUNK},
    {"SCHl","Audio Header Chunk","Audio Header chunk appears once per file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SCCl","Audio Header Chunk 2","Audio Header chunk 2 appears once per file",0,0,"white","black",0,0,0,CHUNKFLAG_CHUNK},
    {"SCDl","Audio Data Chunk","Audio Data chunk",0,0,"white","black",0,0,0,CHUNKFLAG_CHUNK},
    {"SCEl","Audio End Chunk","Audio End chunk",0,0,"white","black",0,0,0,CHUNKFLAG_CHUNK},
    {"GMXh","GMX Header","Contains information about the GMX file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"GMXp","GMX Frame","Contains compressed pixels of an image",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SsfF","Streaming Shape Frame PC (FSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SsfX","Streaming Shape Frame XBOX (XSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SsfS","Streaming Shape Frame PS2 (SSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SsfG","Streaming Shape Frame Gamecube (GSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"ShpF","Shape File PC (FSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"ShpX","Shape File XBOX (XSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"ShpS","Shape File PS2 (SSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"ShpG","Shape File Gamecube (GSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FntF","Font File PC (FFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FntX","Font File XBOX (XFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FntS","Font File PS2 (SFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FntG","Font File Gamecube (GFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SSFF","Streaming Shape Frame PC (FSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SSFX","Streaming Shape Frame XBOX (XSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SSFS","Streaming Shape Frame PS2 (SSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SSFG","Streaming Shape Frame Gamecube (GSS)","Contains a shape",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SHPI","Shape File PC (FSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SHPX","Shape File XBOX (XSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SHPS","Shape File PS2 (SSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"SHPG","Shape File Gamecube (GSH)","Contains a shape file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FNTF","Font File PC (FFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FNTX","Font File XBOX (XFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FNTS","Font File PS2 (SFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"FNTG","Font File Gamecube (GFN)","Contains a font file",0,0,"white","black",0,0,0,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"MVhd","EA Chunked AVI Header","Contains AVI meta data",0,0,"white","blue",1,1,mvxf_handler,CHUNKFLAG_FIRST+CHUNKFLAG_CHUNK},
    {"MV0F","Chunked AVI Movie Frame","Contains a compressed movie frame",0,0,"yellow","black",0,0,0,CHUNKFLAG_CHUNK},
    {"MV0K","Chunked AVI Key Frame","Contains a compressed movie key frame",0,0,"yellow","black",0,0,0,CHUNKFLAG_CHUNK},

    // ON2 (vp3/vp5 etc) use AVI but replace RIFF with 'ON2 '
    {"ON2 ","ON2 RIFF File Header","Master file chunk, contains all other chunks",1,1,"white","black",1,0,0,CHUNKFLAG_FIRST},

    // FORM (iff) iff files use FORM
    {"FORM","IFF File Header","Master file chunk, contains all other chunks",1,1,"white","black",1,0,0,CHUNKFLAG_FIRST|CHUNKFLAG_BIGENDIAN},
    {"FOR4","Maya IFF File Header","Master file chunk, contains all other chunks",1,1,"white","black",1,0,0,CHUNKFLAG_FIRST|CHUNKFLAG_BIGENDIAN},
    {"BMHD","Bitmap Header","Contains header information for the bitmap",0,0,"white","black",0,0,0,CHUNKFLAG_BIGENDIAN},
    {"CMAP","Color Map","Color palette for indexed images",0,0,"white","black",0,0,0,CHUNKFLAG_BIGENDIAN},
    {"BODY","Body of image","Contains the actual pixels of an image",0,0,"white","black",0,0,0,CHUNKFLAG_BIGENDIAN},


    NULL};

// **********************************************************
// Functions
// **********************************************************



// ----------------------------------------------------------
static void PrintBanner()
{
    printf("Rifft v%s By Ian Davids and Frank Barchard\n",TextVersion);
}

// ----------------------------------------------------------
static void PrintUsage()
{
    PrintBanner();
    printf("Syntax: rifft <riff filename>\n");
    printf("    Purpose: dumps information about RIFF chunks in a file\n");
    printf("    -i             interactive mode (waits for enter before each step)\n");
    printf("    -h             this help information\n");
    printf("    -v             verbose data about the file\n");
    printf("    -fourCC<text>  print out a description of the chunk specified\n");
    printf("    -list          lists all known chunk types\n");
}

// ----------------------------------------------------------
static void PrintIndent(INFO_HEADER * info_header)
{
    char curIndent[512];
    int nInd = info_header->numIndents;
    int i = 0;

    if (info_header->listType)         // don't indent on LIST chunk header
        nInd--;

    for(i = 0; i < nInd; i++)
    {
        curIndent[i] = '\t';
    }
    curIndent[i] = '\0';
    ConColour("white","black",0);
    printf(curIndent);
}

// ----------------------------------------------------------
static int match_fourcc(const char * a, const char * b)
{
    if (a[0]==b[0]
     && a[1]==b[1]
     && a[2]==b[2]
     && a[3]==b[3])
        return 1;
    return 0;
}


// ----------------------------------------------------------
static int FindChunkType(INFO_HEADER * info_header)
{
    for (int i=0; riff_info[i].fourcc; ++i)
    {
        if (match_fourcc(riff_info[i].fourcc,info_header->fourcc))
            return i;
    }
    return -1;
}

// ----------------------------------------------------------
static int FindListType(INFO_HEADER * info_header)
{
    int i = 0;
    RIFF_HEADERSIDS * ri = riff_headerIDs;
    while(ri->description)
    {
        if (match_fourcc(ri->fourcc,info_header->firstFourBytes))
            return i;
        i++;
        ri++;
    }
    return -1;
}

// ----------------------------------------------------------
void GCALL avih_handler(INFO_HEADER * ih)
{
    MainAVIHeader * ma = (MainAVIHeader *)ih->blockData;
    PrintIndent(ih);
    printf("Total Frames:  %d\n",ma->dwTotalFrames);
    PrintIndent(ih);
    printf("Initial Frame: %d\n",ma->dwInitialFrames);
    PrintIndent(ih);
    printf("Streams:       %d\n",ma->dwStreams);
    PrintIndent(ih);
    printf("Width:         %d\n",ma->dwWidth);
    PrintIndent(ih);
    printf("Height:        %d\n",ma->dwHeight);
    PrintIndent(ih);
    printf("FLAGS:         %s\n",ma->dwFlags&AVIF_HASINDEX?"Has idx1 Chunk":"No idx1 Chunk");
    PrintIndent(ih);
    printf("FLAGS:         %s\n",ma->dwFlags&AVIF_MUSTUSEINDEX?"Must use the idx1 chunk":"physical order is frame order");
    PrintIndent(ih);
    printf("FLAGS:         %s\n",ma->dwFlags&AVIF_ISINTERLEAVED?"Data is interleaved":"Data is not interleaved");
    PrintIndent(ih);
    printf("FLAGS:         %s\n",ma->dwFlags&AVIF_WASCAPTUREFILE?"Capture file for RTV":"not a capture file");
    PrintIndent(ih);
    printf("FLAGS:         %s\n",ma->dwFlags&AVIF_COPYRIGHTED?"Data is copyrighted":"Not copyrighted");
    PrintIndent(ih);
    printf("MicroSec Between Frames: %d (%.2f fps)\n",ma->dwMicroSecPerFrame,1000000.0f/ma->dwMicroSecPerFrame);
    PrintIndent(ih);
    printf("Aprox Max Data Rate:     %d\n",ma->dwMaxBytesPerSec);
    PrintIndent(ih);
    printf("Suggested Buffer Size    %d\n",ma->dwSuggestedBufferSize);
}

#define VIDS 1935960438
#define AUDS 1935963489
#define TXTS 1937012852
// ----------------------------------------------------------
void GCALL strh_handler(INFO_HEADER * ih)
{
    AVIStreamHeader * ma = (AVIStreamHeader *)ih->blockData;
    PrintIndent(ih);
    printf("Data Type:       %s\n",ma->fccType==VIDS?"Video Data Stream":(ma->fccType==AUDS?"Audio Data Stream":(ma->fccType==TXTS?"Text Data Stream":"Unknown Data Stream")));
    PrintIndent(ih);
    printf("Compressor:      [%c%c%c%c]\n",ma->fccHandler&0xFF,(ma->fccHandler>>8)&0xFF,(ma->fccHandler>>16)&0xFF,(ma->fccHandler>>24)&0xFF);
    if (ma->dwFlags)
    {
        PrintIndent(ih);
        printf("Flags:       %s%s\n",ma->dwFlags&AVISF_DISABLED?"Default Disabled":"",ma->dwFlags&AVISF_VIDEO_PALCHANGES?",Palette Changes":"");
    }
    PrintIndent(ih);
    printf("Audio Skew Time: %d\n",ma->dwInitialFrames);
    PrintIndent(ih);
    printf("Time Scale:      %d\n",ma->dwScale);
    PrintIndent(ih);
    printf("Time Rate:       %d\n",ma->dwRate);
    PrintIndent(ih);
    printf("Starting Time:   %d\n",ma->dwStart);
    PrintIndent(ih);
    printf("Length of Str:   %d\n",ma->dwLength);
    PrintIndent(ih);
    printf("SuggestBufSize:  %d\n",ma->dwSuggestedBufferSize);
    PrintIndent(ih);
    printf("Quality:         %d\n",ma->dwQuality);
    PrintIndent(ih);
    if (ma->dwSampleSize)
        printf("Sample Size: %d\n",ma->dwSampleSize);
    else
        printf("Sample Size:     Varies\n");
    PrintIndent(ih);
    printf("Frame:           <%d,%d> , <%d,%d>\n",ma->rcFrame.left,ma->rcFrame.top,ma->rcFrame.right,ma->rcFrame.bottom);
}

#define AVI_INDEX_OF_INDEXES 0x00
#define AVI_INDEX_OF_CHUNKS  0x01
#define AVI_INDEX_IS_DATA    0x80
// IndexSubtype for INDEX_OF_CHUNKS type
#define AVI_INDEX_2FIELD     0x01
// ----------------------------------------------------------
void GCALL indx_handler(INFO_HEADER * ih)
{
    INDX_BASEFORM * id = (INDX_BASEFORM *)ih->blockData;
    PrintIndent(ih);
    printf("Size of each index entry (in longs):    %d\n",id->LongsPerEntry);
    PrintIndent(ih);
    printf("Index Type:              %s\n",
           id->IndexType==AVI_INDEX_OF_CHUNKS?"Index of stream chunks":
           id->IndexType==AVI_INDEX_OF_INDEXES?"Super Index":
           id->IndexType==AVI_INDEX_IS_DATA?"Index is data":"Unknown index type");
    if (id->IndexType == AVI_INDEX_OF_CHUNKS && id->IndexSubtype == AVI_INDEX_2FIELD)
    {
        PrintIndent(ih);
        printf("Index Subtype:           Indexes fields\n");
    }
    PrintIndent(ih);
    printf("Number of index entries: %d\n",id->EntriesInUse);
    PrintIndent(ih);
    printf("Indexes Data:            %c%c%c%c\n",id->ChunkID&0xFF,(id->ChunkID>>8)&0xFF,(id->ChunkID>>16)&0xFF,(id->ChunkID>>24)&0xFF);
    PrintIndent(ih);
    printf("BaseOffset:              %I64x\n",id->BaseOffset);
}

// ----------------------------------------------------------
void GCALL dmlh_handler(INFO_HEADER * ih)
{
    DWORD totalFrames = *((DWORD *)(ih->blockData));
    PrintIndent(ih);
    printf("Total File Framecount:    %d\n",totalFrames);
}

enum {FORMAT_UNKNOWN,FORMAT_PAL_SQUARE,FORMAT_PAL_CCIR_601,FORMAT_NTSC_SQUARE,FORMAT_NTSC_CCIR_601}VIDEO_FORMAT;
enum {STANDARD_UNKNOWN,STANDARD_PAL,STANDARD_NTSC,STANDARD_SECAM}VIDEO_STANDARD;
// ----------------------------------------------------------
void GCALL vprp_handler(INFO_HEADER * ih)
{
    VPRP_HEADER * vprp = (VPRP_HEADER *)ih->blockData;
    PrintIndent(ih);
    printf("Video Format Token:    %s\n",vprp->VideoFormatToken==FORMAT_UNKNOWN?"Format Unknown":
                                     vprp->VideoFormatToken==FORMAT_PAL_SQUARE?"Format Pal Square":
                                     vprp->VideoFormatToken==FORMAT_PAL_CCIR_601?"Format Pal CCIR 601":
                                     vprp->VideoFormatToken==FORMAT_NTSC_SQUARE?"Format NTSC Square":
                                     vprp->VideoFormatToken==FORMAT_NTSC_CCIR_601?"Format NTSC CCIR 601":"Format Unknown");
    PrintIndent(ih);
    printf("Video Standard:        %s\n",vprp->VideoStandard==STANDARD_UNKNOWN?"Standard Unknown":
                                       vprp->VideoStandard==STANDARD_PAL?"Standard Pal":
                                       vprp->VideoStandard==STANDARD_NTSC?"Standard NTSC":
                                       vprp->VideoStandard==STANDARD_SECAM?"Standard SECAM":"Standard Unknown");
    PrintIndent(ih);
    printf("Vertical Refresh Rate: %d\n",vprp->VerticalRefreshRate);
    printf("H Total in T:          %d\n",vprp->HTotalInt);
    printf("V Total in lines:      %d\n",vprp->VTotalInLines);
    printf("Active AspectRatio:    %d:%d\n",(vprp->FrameAspectRatio>>16)&0xFFFF,vprp->FrameAspectRatio&0xFFFF);
    printf("Active Frm W. in Pix:  %d\n",vprp->FrameWidthInPixels);
    printf("Active Frm H. in Lines:%d\n",vprp->FrameHeightInLines);
    printf("# fields per frame     %d\n",vprp->FieldPerFrame);
}

typedef struct
{
    char    id[4];
    char    size[4];
    char    reserved[4];    // used to be CRC
    char    time[4];        // fps = (1000.0 * 65536.0)/time;
    char    width[2];
    char    height[2];
    char    ver[1];         // 0 = original , 1 = 601 style YUV coeficients . 2 = flags present
    char    quality[1];
    char    flags[1];       // 1 = keyframe , 2 = extra frame
    char    reserved2[1];
} MADCHUNKHDR;

#define MADGETFIELD(field) ggeti((void *)&field, sizeof(field))

// ----------------------------------------------------------
void GCALL madk_handler(INFO_HEADER * ih)
{
    MADCHUNKHDR * ma = (MADCHUNKHDR *)ih->blockData;
    int period = MADGETFIELD(ma->time);
    double fps = (1000.0 * 65536.0)/period;

    PrintIndent(ih);
    printf("Time:          %d (%.2f fps)\n",period,fps);
    PrintIndent(ih);
    printf("Width:         %d\n",MADGETFIELD(ma->width));
    PrintIndent(ih);
    printf("Height:        %d\n",MADGETFIELD(ma->height));
    PrintIndent(ih);
    printf("Ver:           %d\n",MADGETFIELD(ma->ver));
    PrintIndent(ih);
    printf("Quality:       %d\n",MADGETFIELD(ma->quality));
    PrintIndent(ih);
    printf("Flags:         %d\n",MADGETFIELD(ma->flags));
}




typedef char u8;
typedef short u16;
typedef int u32;

//! VID Chunk - first chunk of every .vid file
typedef struct VidVID {
    u8      versionMajor;                       //!< major VID file version
    u8      versionMinor;                       //!< major VID file version

    u8      vidConvMajor;                       //!< major converter version
    u8      vidConvMinor;                       //!< major converter version
    u16     vidConvBuild;                       //!< converter build
    u8      _unknown[14];                       //!< padding to guarantee at least 32 bytes of total length
} VidVID;

//! VIDH Chunk - video header information
typedef struct VidVIDH {
    u16     width;                              //!< width of video
    u16     height;                             //!< height of video
    u32     maxFrameCount;                      //!< number of frames
    u32     maxBufferSize;                      //!< maximum chunk size in bytes
    u32     frameRate;                          //!< fps = frameRate / frameRateScale
    u16     frameRateScale;
    u8      flags;
} VidVIDH;

typedef struct VidAUDHVAUD {
    u32     frq;                                //!< audio playback frequency in Hz
    u8      numChannels;                        //!< number of channels
    u8      _reserved[3];
    u32     maxHeap;
    u32     preAlloc;
    u32     reserved2;
} VidAUDHVAUD;

#define DIVGETFIELD(field) ggetm((void *)&field, sizeof(field))
// ----------------------------------------------------------
void GCALL divx_handler(INFO_HEADER * ih)
{
    VidVIDH * ma = (VidVIDH *)((char *)ih->blockData + 24);
    double fps = (double) DIVGETFIELD(ma->frameRate) / (double) DIVGETFIELD(ma->frameRateScale);

    PrintIndent(ih);
    printf("Time:          %d/%d (%.2f fps)\n",DIVGETFIELD(ma->frameRate), DIVGETFIELD(ma->frameRateScale), fps);
    PrintIndent(ih);
    printf("Width:         %d\n",DIVGETFIELD(ma->width));
    PrintIndent(ih);
    printf("Height:        %d\n",DIVGETFIELD(ma->height));
    PrintIndent(ih);
    printf("maxFrameCount: %d\n",DIVGETFIELD(ma->maxFrameCount));
    PrintIndent(ih);
    printf("maxBufferSize: %u\n",DIVGETFIELD(ma->maxBufferSize));
    PrintIndent(ih);
    printf("flags:         %d\n",DIVGETFIELD(ma->flags));

    VidAUDHVAUD * aa = (VidAUDHVAUD *)((char *)ih->blockData + 24+sizeof(VidVIDH)+16);

    if (DIVGETFIELD(aa->numChannels)>=1 && DIVGETFIELD(aa->numChannels)<=10)
    {
        PrintIndent(ih);
        printf("frq:           %d\n",DIVGETFIELD(aa->frq));
        PrintIndent(ih);
        printf("numChannels:   %d\n",DIVGETFIELD(aa->numChannels));
        PrintIndent(ih);
        printf("maxHeap:       %d\n",DIVGETFIELD(aa->maxHeap));
        PrintIndent(ih);
        printf("preAlloc:      %d\n",DIVGETFIELD(aa->preAlloc));
    }
}

struct ChunkedAviHeader
{
  char   mId[4];                    // 'AVhd' FourCC
  char   mLength[4];                // Length of 'AVhd' chunk
  char   mCompressor[4];            // Compressor FourCC
  char   mWidth[2];
  char   mHeight[2];
  char   mTotalFrames[4];           // Total number of frames in the file. This should always be accurate
  char   mSuggestedBufferSize[4];   // Recommended decode buffer size
  char   mFpsNumerator[4];          // FPS = Numerator/Denominator
  char   mFpsDenominator[2];        // Eg. 30000/1001=29.97
  char   mFlags[2];                 // Eg. uses B-frames
};
#define CHK_AVI_GETFIELD(field) ggeti((void *)&field, sizeof(field))
// EA Chunked AVI format has a header and
// movie data in some sort of fram block
// ----------------------------------------------------------
void GCALL mvxf_handler(INFO_HEADER * ih)
{
    ChunkedAviHeader * ma = (ChunkedAviHeader *)(ih->blockData);
    double fps = (double) CHK_AVI_GETFIELD(ma->mFpsNumerator) / (double) CHK_AVI_GETFIELD(ma->mFpsDenominator);

    PrintIndent(ih);
    printf("Compressor:          [%c%c%c%c]\n",ma->mCompressor[0],ma->mCompressor[1],ma->mCompressor[2],ma->mCompressor[3]);
    PrintIndent(ih);
    printf("Time:                %d/%d (%.2f fps)\n",CHK_AVI_GETFIELD(ma->mFpsNumerator), CHK_AVI_GETFIELD(ma->mFpsDenominator), fps);
    PrintIndent(ih);
    printf("Width:               %d\n",CHK_AVI_GETFIELD(ma->mWidth));
    PrintIndent(ih);
    printf("Height:              %d\n",CHK_AVI_GETFIELD(ma->mHeight));
    PrintIndent(ih);
    printf("TotalFrames:         %d\n",CHK_AVI_GETFIELD(ma->mTotalFrames));
    PrintIndent(ih);
    printf("SuggestedBufferSize: %u\n",CHK_AVI_GETFIELD(ma->mSuggestedBufferSize));
    PrintIndent(ih);
    printf("Flags:               %d\n",CHK_AVI_GETFIELD(ma->mFlags));
}

// ----------------------------------------------------------
void GCALL info_handler(INFO_HEADER * ih)
{
    char * str = (char *)ih->blockData;
    if (strlen(str) > 0)
    {
        PrintIndent(ih);
        printf("%s\n",str);
    }
    else
    {
        PrintIndent(ih);
        printf(" - Blank -\n");
    }
}

// ----------------------------------------------------------
void pushback(GPOS * list, int pos, int end)
{
    int e = end-1;
    for(int i = pos; i < e; i++)
    {
        list[i] = list[i+1];
    }
}

// ----------------------------------------------------------
int GetNextRiffChunk(GSTREAM * fp,INFO_HEADER * info_header)
{
    int index = 0;
    int rc = gread(fp,info_header,12);
    unsigned int seekval = 0;
    GPOS filePos = 0;
    // clean up from last pass.
    // if we read in some data then cleanup
    if (info_header->blockData)
    {
        free(info_header->blockData);
        info_header->blockData = 0;
    }
    if (!rc)
        return 0;
    info_header->listType = 0;                   // clear the list chunk identifier
    filePos = gtell(fp);

    info_header->filePos = filePos - 12I64;
    index = FindChunkType(info_header);

    if (info_header->chunkNum==0 && index>=0)
    {
        int chunkFlags = riff_info[index].chunkFlags;
        if (chunkFlags&CHUNKFLAG_FIRST)
        {
            info_header->sizeAdjust = (chunkFlags&CHUNKFLAG_CHUNK)?0:8;  // Chunk vs RIFF sizes include header
            info_header->bigEndian  = (chunkFlags&CHUNKFLAG_BIGENDIAN)?1:0;  // RIFF vs IFF
        }
    }

    if (info_header->bigEndian)
        gputi(&info_header->size,ggetm(&info_header->size,4),4);

    if (index != -1 && riff_info[index].hasHandler && !(riff_info[index].listType))
    {   // only do anything different if we are NOT a LIST type!
        // read in data for the handler.
        if (index == -1 || !(riff_info[index].listType))
        { // if not a list type then seek over chunk
            seekval = info_header->size;
            seekval = (seekval+1)&~1;                  // word align
            GPOS t = gtell(fp);
            gseek(fp,t+info_header->sizeAdjust-rc);                     // seek back the first four bytes
            info_header->blockData = (char *)malloc(seekval);  // get the data itself.
            gread(fp,info_header->blockData,seekval);
        }
    }
    else
    { // skip over the data we don't need it.
        if (index == -1 || !(riff_info[index].listType))
        { // if not a list type then seek over chunk
            seekval = info_header->size;
            GPOS t = gtell(fp);

            gseek(fp,t+(((GPOS)seekval+info_header->sizeAdjust-rc+1)&~1));  // note seeking word aligned
        }
        else if (!(riff_info[index].hasHeader))
        { // if not master RIFF chunk or LIST chunk then I have to seek back extra 4 bytes
            GPOS t = gtell(fp);
            gseek(fp,t+info_header->sizeAdjust-rc);
            filePos = gtell(fp);

            info_header->indentPosList[info_header->numIndents] = filePos + (GPOS) info_header->size;
            info_header->numIndents++;
            info_header->listType = 1;
        }
        else
        { // just setup the indent information.
            filePos = gtell(fp);
            info_header->indentPosList[info_header->numIndents] = filePos + (GPOS) info_header->size;
            info_header->numIndents++;
            info_header->listType = 1;
        }
    }
    // get rid of any indents from the indent list if they have been exceeded.

    filePos = gtell(fp);
    for(int i = info_header->numIndents-1; i >= 0; i--)
    {
        if (filePos > info_header->indentPosList[i])
        {
            pushback(info_header->indentPosList,i,info_header->numIndents);
            info_header->numIndents--;
        }
    }
    ++info_header->chunkNum;
    return 1;

}

// ----------------------------------------------------------
void ShowRIFFListType(INFO_HEADER * info_header)
{
    int h_index = FindListType(info_header);
    if (h_index == -1)
    {
        ConColour("red","black",1);
        printf("Unknown LIST Chunk Type [%-4.4s]\n",info_header->firstFourBytes);
        return;
    }
    ConColour(riff_headerIDs[h_index].colourFore,riff_headerIDs[h_index].colourBack,riff_headerIDs[h_index].colourmodifier);
    printf("%s\n",riff_headerIDs[h_index].description);
}


// ----------------------------------------------------------
void DumpRiffChunk(INFO_HEADER * info_header)
{
    int index = FindChunkType(info_header);
    int i = 0;

    PrintIndent(info_header);

    if (index == -1)
    {
        ConColour("red","black",0);
        printf("[%-4.4s] - Unknown Chunk (0x%I64x)\n",info_header->fourcc,info_header->filePos);
    }
    else
    {
        ConColour(riff_info[index].colourFore,riff_info[index].colourBack,riff_info[index].colourmodifier);
        printf("[%-4.4s] - %s (0x%I64x, %u)\n",riff_info[index].fourcc,riff_info[index].name,info_header->filePos, info_header->size);
    }
    if (index != -1 && riff_info[index].hasHeader)
    {
        PrintIndent(info_header);
        ShowRIFFListType(info_header);
    }
    // if we have verbose mode and we have a handler for this chunk type then
    // fire off the stuff to the chunk handler.
    if (info_header->verboseMode && riff_info[index].hasHandler && riff_info[index].Handler)
    {
        riff_info[index].Handler(info_header);
    }

    ConColour("white","black",0);
}

// ----------------------------------------------------------
void ListAllChunkTypes(INFO_HEADER * ih)
{
    RIFF_INFO * ri = riff_info;
    while(ri->name)
    {
        printf("[%s] - %s\n\t%s\n",ri->fourcc,ri->name,ri->description);
        ri++;
    }
    printf("---- LIST TYPES ----\n");
    RIFF_HEADERSIDS * rhi = riff_headerIDs;
    while(rhi->description)
    {
        printf("\t[%s] - %s\n",rhi->fourcc,rhi->description);
        rhi++;
    }
}

// ----------------------------------------------------------
int ParseCommandLine(int argc,char ** argv,INFO_HEADER * ih)
{
    int haveFileName = 0;
    if (argc < 2)
    {
        PrintUsage();
        return 0;
    }
    for(int i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            if (strlen(argv[i]) == 2 && argv[i][1] == 'h')
            {
                PrintUsage();
                return 0;
            }
            if (strlen(argv[i]) == 2 && argv[i][1] == 'i')
            {
                ih->interactiveMode = 1;
                continue;
            }
            if (strlen(argv[i]) == 2 && argv[i][1] == 'v')
            {
                ih->verboseMode = 1;
                continue;
            }
            if (strlen(argv[i]) == 11 && !strnicmp(&(argv[i][1]),"fourCC",6))
            {
                int index = 0;
                ih->fourcc[0] = argv[i][7];
                ih->fourcc[1] = argv[i][8];
                ih->fourcc[2] = argv[i][9];
                ih->fourcc[3] = argv[i][10];
                index = FindChunkType(ih);
                if (!index)
                {
                    printf("Could Not Find Chunk Type: %s\n",&(argv[i][7]));
                    return 0;
                }
                else
                {
                    printf("[%s] - %s %s\n%s\n",riff_info[index].fourcc,riff_info[index].name,riff_info[index].listType?"(LIST TYPE)":"",riff_info[index].description);
                    return 0;
                }
                continue;
            }
            if (strlen(argv[i]) == 5 && !strnicmp(&(argv[i][1]),"list",4))
            {
                ListAllChunkTypes(ih);
                return 0;
            }
            else
            {
                PrintUsage();
                printf("Unknown Option: %s\n",argv[i]);
                return 0;
            }
        }
        else
        {
            if (!haveFileName)
            {
                strcpy(ih->filename,argv[i]);
                haveFileName = 1;
                continue;
            }
            else
            {
                PrintUsage();
                printf("Error: To many filenames\n");
                return 0;
            }
        }
    }
    if (!haveFileName)
    {
        PrintUsage();
        return 0;
    }
    return 1;
}

// ----------------------------------------------------------
int main(int argc, char ** argv)
{
    GSTREAM * fp;
    bool do_once = false;
    INFO_HEADER info_header;
    memset(&info_header,0,sizeof(INFO_HEADER));

    if (!ParseCommandLine(argc,argv,&info_header))
        return 0;

    PrintBanner();
    fp = gopen(info_header.filename);
    if (!fp)
    {
        ConColour("red","black",0);
        printf("error: Cannot open file: %s\n",argv[1]);
        ConColour("white","black",0);
        return 0;
    }
    while (GetNextRiffChunk(fp,&info_header))
    {
        if (!do_once)
        {
            int ind = FindChunkType(&info_header);
            printf("riff %s\n",info_header.filename);
            printf("riff %p\n",fp);
            printf("riff ind %d\n",ind);
            int chunkFlags = 0;
            if (ind>=0)
            {
                chunkFlags = riff_info[ind].chunkFlags;
                printf("chunkFlags %x\n",chunkFlags);
            }

            if (!(chunkFlags&CHUNKFLAG_FIRST))
            {
                printf("Not a RIFF file, aborting\n");
                return 0;
            }
            do_once = true;
        }
        DumpRiffChunk(&info_header);
#ifndef __PPU__
        if (info_header.interactiveMode)
        {   char c = _getch();
            if ( c == 'q' || c == 'Q')
            {
                if (info_header.blockData)
                    free(info_header.blockData);
                ConColour("white","black",0);
                gclose(fp);
                return 0;
            }
        }
#endif
    }
    ConColour("white","black",0);
    gclose(fp);
    return 0;
}
