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


const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                          MovieCat v1.00                          ",
"                                                                  ",
"                       by Frank Barchard, EAC                     ",
"                                                                  ",
"                    Main program - Jul 27, 2006                   ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 060727 FB initial version based on avi2chunk             ",
"                                                                  ",
"------------------------------------------------------------------",
"\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"
//#include <vfw.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 "moviecat"

char OPTreturn[SPITMAXLINE];
double OPTfps;


/*
;
; ABSTRACT
;
; moviecat - concatenates chunked AVI files (vp6/mpv)
;
; SUMMARY
;
; moviecat -option] output.vp6 in1.vp6 in2.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
;     -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, fps
;
; DESCRIPTION
;
; moviecat concatenates several vp6 or mpv files into one.
;
; RETURNS
;
; 0 for success (no return value)
;
; SEE ALSO
;
; chimp, gx, sx, glump
;
; SAMPLE OUTPUT
;
; >moviecat 204_800x450_100K_1PBQ.avi -fps30
; MoviEcat 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;
    OPTfps = 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",
    "    -fps<n>        specify expected fps 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, fps\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("Moviecat 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("  -fps 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,"fps",3))
            {
                char OPTstr[SPITMAXLINE];
                d = SPIT_getfield( OPTstr,optptr+3,1,0);
                OPTfps = (float) atof(OPTstr);
            }
            break;

        default:
            break;
    }
    return(d);
}

struct ChunkedAviHeader
{
  char   mId[4];                    // 'MVhd' FourCC
  char   mLength[4];                // Length of 'MVhd' 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
};

bool gcopy(GSTREAM *gin,GSTREAM *gout,GPOS fsizeleft)
{
    int blockchop = 32768;
    void *inbuf = galloc(blockchop);
    bool ok = true;

    while (fsizeleft && ok)
    {
        int nextsize=(int) gmin(fsizeleft,blockchop);
        ok = gread(gin,inbuf,nextsize)==nextsize;
        if (ok)
        {
            ok = gwrite(gout,inbuf,nextsize)==nextsize;
        }
        if (ok)
            fsizeleft -= nextsize;
    }

    gfree(inbuf);
    return fsizeleft==0;
}


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)
        {
            bool isFirst=true;
            int frameCount=0;
            GSTREAM *gout=0;
            char MVHD[32];
            memset(MVHD,0,sizeof(MVHD));

            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)
                {
                    if (isFirst)
                    {
                        gout = gwopen(SPITsargv[i]);
                        isFirst = false;
                    }
                    else
                    {
                        GSTREAM *g = gopen(SPITsargv[i]);
                        if (g)
                        {
                            GPOS len = glen(g);
                            gread(g,MVHD,32);
                            if (ggetm(MVHD+0,4)==0x4d566864)
                            {
                                if (frameCount==0)
                                    gseek(g,0);
                                else
                                    len -= 32;
                                frameCount += ggeti(MVHD+16,4);

                                gcopy(g,gout,len);
                            }
                            else
                            {
                                if (OPTverbose)
                                    printf("moviecat - ERROR %s is not a chunked AVI movie. skipping\n",SPITsargv[i]);
                                ++errorcount;
                            }
                            gclose(g);
                        }
                        else
                        {
                            if (OPTverbose)
                                printf("moviecat - ERROR %s not found. skipping\n",SPITsargv[i]);
                            ++errorcount;
                        }
                    }

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

            gseek(gout,0);
            gputi(MVHD+16,frameCount,4);
            gwrite(gout,MVHD,32);
            gclose(gout);
        }
    } 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);

#if 0
        // extracted from the avih block
        EXPR_set(expr,"width",      ggetfield(info_header.chunkHeader.mWidth));
        EXPR_set(expr,"height",     ggetfield(info_header.chunkHeader.mHeight));
        EXPR_set(expr,"frames",     ggetfield(info_header.chunkHeader.mTotalFrames));
        EXPR_set(expr,"fourcc",     ggetm(&info_header.chunkHeader.mCompressor,4));
        EXPR_set(expr,"buffersize", ggetfield(info_header.chunkHeader.mSuggestedBufferSize));
        EXPR_set(expr,"fps",        ggetfield(info_header.chunkHeader.mFpsNumerator) / ggetfield(info_header.chunkHeader.mFpsDenominator) );
#endif
        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;
}

