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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                         UnChunkify v1.00                         ",
"                                                                  ",
"                       by Frank Barchard, EAC                     ",
"                                                                  ",
"                    Main program - Oct 25, 2005                   ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 060508 FB initial version based on unchunkify             ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

/****************************************************************/
/*  Spit Setup                                                  */
/****************************************************************/

#if defined(_MSC_VER) && _MSC_VER<1400
#pragma optimize("g", off)
#endif
#if defined(_MSC_VER)
#pragma warning(push,1)
#include "windows.h"
#pragma warning(pop)
#endif

#include "spit.h"
#include "stdgimex.h"
#include "ranger.h"

const char TextVersion[]="1.00 (Samba) 2007.12.4";
#define TOOL_VERSION 100
#define TOOL_NAME "unchunkify"

char OPTreturn[SPITMAXLINE];
double OPTfins;

// get size field from chunk and return
int GetChunkSize(const unsigned char *buffer)
{
    int size = ggeti(buffer+4, 4);
    return size;
}

// read a chunk (frame) from file
unsigned char *GetChunk(GSTREAM *g)
{
    unsigned char *buffer = NULL;

    unsigned char chunkheaderbuf[8];
    int rsize = gread(g,chunkheaderbuf, 8);
    if (rsize==8)
    {
        int size = GetChunkSize(chunkheaderbuf);

        if (size<1000000)
        {
            int asize = (size+31)&~31;
            buffer = (unsigned char *) galloc(asize);
            memcpy(buffer,chunkheaderbuf,8);
            int rsize = gread(g, buffer+8, size-8);
            if (rsize!=(size-8))
            {
                gfree(buffer);
                buffer = NULL;
            }
        }
    }

    return buffer;
}


int unchunkify(const char *filein, const char *fileout)
{
    int errorCount=0;

    GSTREAM * fin = gopen(filein);
    GSTREAM * fout = NULL;
    if (!fin)
    {
        if (OPTverbose>=1)
            printf("error: Cannot open file: %s\n",filein);
        ++errorCount;
    }
    else
    {
        char tmpoutname[SPITMAXLINE];
        if (OPTwrite)
        {
            // if no name provided, default to vp6
            char outBuf[512];
            if (!strcmp(filein,fileout))
            {
                strcpy(outBuf,fileout);
                char *ext = strchr(outBuf,'.');
                if (ext)
                    *ext = '\0';
                strcat(outBuf,".m2v");
                fileout = outBuf;
            }

            // to allow in-place conversion, use a temporary name
            SPIT_tempname(tmpoutname,fileout);

            fout = gwopen(tmpoutname);
            if (!fout)
            {
                if (OPTverbose>=2)
                    printf("unchunkify - COULD NOT OPEN FILE FOR WRITE '%s'\n",tmpoutname);
                gclose(fin);
            }
        }

        if (!fout && OPTwrite)
        {
            if (OPTverbose>=1)
                printf("error: Cannot open output file: %s\n",fileout);
            ++errorCount;
        }
        else
        {
            bool done = false;

            // start the main read write loop. The handlers
            // will take care of when to fill header and write
            // out movie data.
            int TotalFrames=0;
            do
            {
                unsigned char *chunk = GetChunk(fin);
                if (chunk)
                {
                    if (*((char *)chunk)=='M' && strncmp((char *)chunk,"MVhd",4))   // skip sound and MVhd
                        gwrite(fout,chunk+8,GetChunkSize(chunk)-8);
                        ++TotalFrames;
                    gfree(chunk);
                }
                else
                    done = true;

            } while (!done);


            if (OPTverbose>=2)
            {
                printf("unchunkify converted: %s -> %s, %d chunks\n",filein,fileout, TotalFrames);
            }
        }

        gclose(fin);

        // cleanup

        if (fout)
        {
            gclose(fout);
            remove(fileout);
            rename(tmpoutname,fileout);
        }
    }
    return errorCount;

}



/*
;
; ABSTRACT
;
; unchunkify - flattens an AVI file into an EA chunk file
;
; SUMMARY
;
; unchunkify -option] <infilename.avi> =<outfilename.vp6>
;     @file          response file, containing a list of options and files
;     =file          specify a output file ie source=dest
;     -b0            disable banner
;     -v0            no console output
;     -v1            less output
;     -v2            normal output (default)
;     -v             more output
;     -fins<n>        specify expected fins for error checking (default 29.97)
;     -w0            dont output file
;     -r             recurse subdirectories to find files
;     -r%ENV%        traverse environment variable ENV to find files
;     -time          show execution time
;     -return<expr>  return expression (default errorcount).  See variables
;
; VARIABLES:
;     errorcount, TOOL_VERSION, SPIT_VERSION, time,
;     width, height, frames, fourcc, buffersize, fins
;
; DESCRIPTION
;
; unchunkify flattens an AVI file into an EA chunk file.
;
; AVI file format (aka RIFF) is a heirarchical chunk format.
; Rifft flattens out this heirarchy, pretty much ignoring all
; 'LIST' chunks.
; The chunks are very similar to EA's chunk format, but the
; size field does not include the chunk header.  Rifft adds 8
; to the size fields.  The video frame chunks are also renamed.
; For AVI video, a few special headers indicate the resolution
; and framerate of the video.  This information is gathered into
; one header chunk and output as the first chunk.
; Audio is not really supported.  The tool adapts audio, but the
; runtime libraries do not support the content of those chunks.
;
; avi is a chunk format
;
; RETURNS
;
; 0 for success (no return value)
;
; SEE ALSO
;
; chimp, gx, sx, glump
;
; SAMPLE OUTPUT
;
; >unchunkify 204_800x450_100K_1PBQ.avi -fins30
; unchunkify converted: 204_800x450_100K_1PBQ.avi -> 204_800x450_100K_1PBQ.vp6, 800x450, 758 chunks
;
; END ABSTRACT
;
*/

/****************************************************************/
/*  SPIT Routines                                               */
/****************************************************************/

void SPIT_reset(void)
{
    OPTkeepopts = 1;
    OPTtime = 0;
    OPTfins = 0;
    strcpy(OPTreturn,"");

    return;
}

char *optionhelp[]=
{
    "    @file          response file, containing a list of options and files\n",
    "    =file          specify a output file ie source=dest\n",
    "    -b0            disable banner\n",
    "    -v0            no console output\n"
    "    -v1            less output\n"
    "    -v2            normal output (default)\n"
    "    -v             more output\n",
    "    -fins<n>        specify expected fins for error checking (default 29.97)\n",
    "    -w0            dont output file\n",
    "    -r             recurse subdirectories to find files\n"
    "    -r%ENV%        traverse environment variable ENV to find files\n",
    "    -time          show execution time\n",
    "    -return<expr>  return expression (default errorcount).  See variables\n"
    "\n",
    "VARIABLES:\n"
    "    errorcount, TOOL_VERSION, SPIT_VERSION, time,\n"
    "    width, height, frames, fourcc, buffersize, fins\n",
    0,
};

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    char **hlp;

    if (((OPTverbose>=2 && utype==2) || OPTverbose>=4))
        printf("Mpv2chunk v%s by FrANK G. Barchard\n",TextVersion);


    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

    switch (utype)
    {
        case 0:
        case 1:
            break;

        case 2:
            puts(" -time time execution       -return value");
            puts("    -v verbose                   -w write file");
            puts("  -fins framerate");
            puts("     @ response file              = output file");
          printf("type '%s -h' for more help\n", prgname);
          printf("type '%s -help' for web page (%s)\n", prgname, urlname);
            break;

        case 3:
            puts("");
          printf("VERSION: %s v%s, Spit v%.2f\n",SPIT_MACHINE,TextVersion,(float)SPIT_VERSION/100.0f);
            puts("SUMMARY: Flattens an AVI file into an EA chunk file");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");

            hlp = optionhelp;
            while (*hlp)
            {
                printf("%s",*hlp);
                ++hlp;
            }
            break;

         case 4:
            puts("");
            {
                hlp = optionhelp;
                while (*hlp)
                {
                    if (SPIT_strstr(*hlp,envname))
                        printf("%s",*hlp);
                    ++hlp;
                }
                puts("");
            }
            break;

        default:
            break;
    }
}

char *SPIT_opt(const char* optptr)
{
    char *d=NULL;
    switch (tolower(*optptr))
    {
        case 'r':
            if (!strnicmp(optptr,"return",6))
            {
                d = SPIT_getfield( OPTreturn,optptr+6,1,0);
            }
            break;

        case 'f':
            if (!strnicmp(optptr,"fins",3))
            {
                char OPTstr[SPITMAXLINE];
                d = SPIT_getfield( OPTstr,optptr+3,1,0);
                OPTfins = (float) atof(OPTstr);
            }
            break;

        default:
            break;
    }
    return(d);
}

int main(int argc, char* argv[])
{
    int more;
    int i;
    int errorcount=0;
    double totaltime;

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);

    EXPR *expr = EXPR_open();

    totaltime = SPIT_gettime();

    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

        if (!SPIThelpgiven)
        {
            for (i=0; i<SPITargnum; ++i)
            {
                bool isopt = false;
                if (SPITsargv[i][0]=='-' || SPITsargv[i][0]=='+' || (SPITsargv[i][0]==OPTdot && SPITsargv[i][1]!='.' && SPITsargv[i][1]!='/' && SPITsargv[i][1]!='\\'))
                    isopt = Zop(SPITsargv[i]+1)?true:false;
                else if (SPITsargv[i][0]=='=')
                    isopt = Zop(SPITsargv[i])?true:false;

                if (!isopt)
                {
                    errorcount += unchunkify(SPITsargv[i],SPITdargv[i]);

                    if (OPTverbose>=3)
                    {
                        printf("%s -> %s\n",SPITsargv[i],SPITdargv[i]);
                    }
                }
            }
        }
    } while (more>0);

    totaltime = SPIT_gettime()-totaltime;

    int returncode = errorcount;
    if (OPTreturn[0])
    {
        RANGER *r = RANGER_open(expr,OPTreturn,0.0,1000000.0);  // min/max mostly ignored

        EXPR_set(expr,"errorcount",   errorcount);
        EXPR_set(expr,"TOOL_VERSION", TOOL_VERSION);
        EXPR_set(expr,"SPIT_VERSION", SPIT_VERSION);

        // extracted from the avih block
        EXPR_set(expr,"time",       totaltime);

        returncode = (int) RANGER_closest(r, (double) returncode, RANGER_ROUND_CLOSEST);

        RANGER_close(r);
    }
    EXPR_close(expr);

    if (OPTverbose==1)
        puts("");

    if (OPTtime)
    {
        if (totaltime>=3600000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds) (%.2f minutes) (%.2f hours)",totaltime,totaltime/1000.0,totaltime/60000.0,totaltime/3600000.0);
        else if (totaltime>=60000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds) (%.2f minutes)",totaltime,totaltime/1000.0,totaltime/60000.0);
        else if (totaltime>=1000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds)",totaltime,totaltime/1000.0);
        else
            printf("TIME " TOOL_NAME " %.2f ms",totaltime);
    }

    SPIT_restore();

    if (galloccount)
    {
        if (OPTverbose>=2)
        {
            printf(TOOL_NAME " - WARNING MEMORY NOT FREED TO SYSTEM %d\n",galloccount);
            returncode = galloccount;
        }
    }

    return returncode;
}

