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

/*------------------------------------------------------------------*/
/*                                                                  */
/*                         Psx Weave v1.02                          */
/*                                                                  */
/*                     by FrANK G. Barchard, EAC                    */
/*                                                                  */
/*                    Main program - Jun 17, 1997                   */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Version Date   SE History:                                       */
/* ------- ----   -- --------                                       */
/*    1.00 94xxxx FB EASM no frills psxweave                        */
/*    1.01 970501 JT Rewrite by Jan Tian                            */
/*    1.02 970617 FB Spit/SGI version                               */
/*                                                                  */
/*------------------------------------------------------------------*/

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

#include "spit.h"
#include "psxweavei.c"

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




typedef unsigned char  u_char;
typedef unsigned short u_short;
typedef unsigned int  u_long;

/* */
typedef struct
{
    char    filenumber;
    char    channelnumber;
    char    submode;
    char    condinginfo;
} MODE_2_SUBHEADER_DEF;

/* CD location defined in libcd.h */
typedef struct
{
    u_char minute;      /* minute (BCD) */
    u_char second;      /* second (BCD) */
    u_char sector;      /* sector (BCD) */
    u_char track;       /* track (void) */
} CdlLOC;

/* stream subheader defined in libcd.h */
typedef struct
{
    /* system data */
    u_short id;
    u_short type;
    u_short secCount;
    u_short nSectors;
    u_long  frameCount;
    u_long  frameSize;

    /* user defined data */
    u_short width;
    u_short height;
    u_long  numFrames;                      /* use this field for "numFrames" */
    u_long  dummy2;
    CdlLOC  loc;
} StHEADER;

typedef struct
{
    MODE_2_SUBHEADER_DEF    subheader[2];           /* 8 bytes in total */
    StHEADER                dataheader;             /* 32 bytes */
    char                    data[2016];             /* 2016=2048-sizeof(StHEADER) */
    char                    EDC[4];
    char                    ECC[276];
} STREAM_SECTOR_DEF;

#define STREAM_SECTOR_SIZE 2336         /* (subheader)+(data)+(EDC)+(ECC) = 8+2048+4+276 = 2336 */
#define STR_VIDEO_SECTOR_SIZE 2048
#define MAX_VIDEO_SECTORS_PER_FRAME 16

MODE_2_SUBHEADER_DEF VideoSubHeader={0x00,0x01,0x42,0x80};
MODE_2_SUBHEADER_DEF AudioSubHeader={0x00,0x00,0x00,0x00};
MODE_2_SUBHEADER_DEF DummySubHeader={0x00,0x01,0x42,0x80};
MODE_2_SUBHEADER_DEF PadSubHeader  ={0x00,0x01,0xC8,0x80};

STREAM_SECTOR_DEF VideoSector;
STREAM_SECTOR_DEF LastVideoFrameSectors[MAX_VIDEO_SECTORS_PER_FRAME+1];
STREAM_SECTOR_DEF AudioSector;
STREAM_SECTOR_DEF DummySector;
STREAM_SECTOR_DEF PadSector;

FILE *VideoFile, *AudioFile, *XAFile;
int AudioStartFrameIndex=2;
int AudioCompressionRatio=8;                        /* 8:1 for double seed, 4:1 for single speed */
int UseMinNumFrames = 0;
int UseMaxNumFrames = 0;
int SpecifiedNumFrames = 0;
int MatchVideoDuration=0;
int MatchAudioDuration=0;
int DropFrame=0;

#define DEFAULT_FRAME_RATE 15

#define DEFAULT_PAD_FRAMES 30
int NumPadFrames = DEFAULT_PAD_FRAMES;

/*---------------------------------------------------------------------------------------*/

void get_input_parameters(int argc, char **argv)
{
    int i,videodone,audiodone,xadone;
    char *videofilename, *audiofilename, *xafilename;
    int promptOverwrite;
    char answer[256];
    struct _find_t fileinfo;

    if ( argc < 4 )
    {
        printf("This program combines video and audio files into PSX XA file.\n");
        printf("Usage:  janweave videofile audiofile xafile [-options]\n");
        printf("e.g.:   janweave test.str null test.xa\n");
        printf("Options:\n");
        printf("   -nfXX       number of frames XX\n");
        printf("   -max        use the maximum length of the two input files\n");
        printf("   -min        use the minimum length of the two input files\n");
        printf("   -ma         match audio duration\n");
        printf("   -asfXX      audio start frame index XX, default is 2.\n");
        printf("   -y          disable overwrite prompt.\n");
        printf("   -pfXX       pad XX frames at the end, default=30(for one second)\n");
        printf("               in general: XX=150/(FrameRate*SectorsPerFrame)\n");
        printf("   -drop       drop every other frame\n");
        exit(10);
    }

    videodone = audiodone = xadone = 0;
    videofilename = audiofilename= xafilename = NULL;
    VideoFile = AudioFile = XAFile = NULL;
    UseMinNumFrames = 0;
    UseMaxNumFrames = 0;
    SpecifiedNumFrames = 0;
    MatchVideoDuration = 0;
    MatchAudioDuration = 0;
    promptOverwrite = 1;
    NumPadFrames = DEFAULT_PAD_FRAMES;
    DropFrame = 0;

    for (i=1; i<argc; i++)
    {
        _strupr(argv[i]);

        if ( argv[i][0] == '-' )
        {
            if ( strncmp(argv[i], "-DROP", 5) == 0 )
                DropFrame = 1;
            else if ( strncmp(argv[i], "-ASF", 4) == 0 )
                AudioStartFrameIndex = atoi(&argv[i][4]);
            else if ( strncmp(argv[i], "-MIN", 4) == 0 )
                UseMinNumFrames = 1;
            else if ( strncmp(argv[i], "-MAX", 4) == 0 )
                UseMaxNumFrames = 1;
            else if ( strncmp(argv[i], "-NF", 3) == 0 )
                SpecifiedNumFrames = atoi(&argv[i][3]);
            else if ( strncmp(argv[i], "-MV", 3) == 0 )
                MatchVideoDuration = 1;
            else if ( strncmp(argv[i], "-MA", 3) == 0 )
                MatchAudioDuration = 1;
            else if ( strncmp(argv[i], "-PF", 3) == 0 )
                NumPadFrames = atoi(&argv[i][3]);
            else if ( strncmp(argv[i], "-Y", 2) == 0 )
                promptOverwrite = 0;
        }
        else if ( !videodone )
        {
            if ( strncmp(argv[i],"NULL",4) != 0 )
                videofilename = argv[i];
            videodone = 1;
        }
        else if ( !audiodone )
        {
            if ( strncmp(argv[i],"NULL",4) != 0 )
                audiofilename = argv[i];
            audiodone = 1;
        }
        else if ( !xadone )
        {
            if ( strncmp(argv[i],"NULL",4) != 0 )
                xafilename = argv[i];
            xadone = 1;
        }
    }

    printf("janweave %s %s %s\n",videofilename,audiofilename,xafilename);

    if ( !videofilename && !audiofilename )
    {
        printf("I need at least ONE input file !\n");
        exit(10);
    }

    if ( videofilename )
    {
        VideoFile = fopen(videofilename,"rb");
        if ( !VideoFile )
        {
            printf("Can't open video file %s for input!\n", videofilename);
            exit(10);
        }
    }

    if ( audiofilename )
    {
        AudioFile = fopen(audiofilename,"rb");
        if ( !AudioFile )
        {
            printf("Can't open audio file %s for input!\n", audiofilename);
            exit(10);
        }
    }

    if ( !xafilename )
    {
        printf("No output file name specified!\n");
        exit(10);
    }
    else
    {
        if ( _dos_findfirst(xafilename,_A_NORMAL,&fileinfo)==0 && promptOverwrite )
        {
            printf("Overwrite %s ?(y/n)\n",xafilename);
            gets(&answer);
            if ( answer[0] != 'y' && answer[0] != 'Y' )
                exit(10);
        }

        if ( (XAFile = fopen(xafilename, "wb")) == 0 )
        {
            printf("Can't open file %s for output!\n", xafilename);
            exit(10);
        }
    }

    printf("Video input: %s\n", videofilename);
    printf("Audio input: %s\n", audiofilename);
    printf("XA output:   %s\n", xafilename);
}
/*---------------------------------------------------------------------------------------*/
void init_sector_buffer(STREAM_SECTOR_DEF *sector, MODE_2_SUBHEADER_DEF *subheader)
{
    memset(sector, 0, sizeof(STREAM_SECTOR_DEF));
    sector->subheader[0] = sector->subheader[1] = *subheader;
}
/*---------------------------------------------------------------------------------------*/
int read_VideoSector(FILE *infile)
{
    if ( infile && fread(&VideoSector.dataheader, STR_VIDEO_SECTOR_SIZE, 1, infile) )
    {
        /* save LastVideoFrameSectors */
        if ( VideoSector.dataheader.secCount >= MAX_VIDEO_SECTORS_PER_FRAME )
            printf("SecCount error: %d\n", VideoSector.dataheader.secCount);
        else
            LastVideoFrameSectors[VideoSector.dataheader.secCount] = VideoSector;

        return(1);
    }
    else
        return(0);
}
/*---------------------------------------------------------------------------------------*/
int read_AudioSector(FILE *infile)
{
    if ( infile && fread(&AudioSector, STREAM_SECTOR_SIZE, 1, infile) )
        return(1);
    else
        return(0);
}
/*---------------------------------------------------------------------------------------*/
void write_VideoSector(FILE *outfile, int numframes, int frameindex)
{
    printf("V");

    VideoSector.dataheader.numFrames = numframes;
    VideoSector.dataheader.frameCount = frameindex;

    fwrite(&VideoSector,STREAM_SECTOR_SIZE,1,outfile);
}
/*---------------------------------------------------------------------------------------*/
void write_LastVideoFrameSectors_sector(FILE *outfile, int frameindex, int sectorindex )
{
    STREAM_SECTOR_DEF *vs;

    if ( sectorindex < 0 || sectorindex > (MAX_VIDEO_SECTORS_PER_FRAME-1) )
    {
        printf("LastVideoFrameSectors sectorindex error: %d\n",sectorindex);
        return;
    }

    printf("v");

    vs = &LastVideoFrameSectors[sectorindex];
    vs->dataheader.frameCount = frameindex;
    fwrite(vs,STREAM_SECTOR_SIZE,1,outfile);
}
/*---------------------------------------------------------------------------------------*/
void write_AudioSector(FILE *outfile)
{
    printf("A");
    fwrite(&AudioSector,STREAM_SECTOR_SIZE,1,outfile);
}
/*---------------------------------------------------------------------------------------*/
void write_DummySector(FILE *outfile, int numframes, int frameindex)
{
    printf(".");

    DummySector.dataheader.numFrames = numframes;
    DummySector.dataheader.frameCount = frameindex;

    fwrite(&DummySector,STREAM_SECTOR_SIZE,1,outfile);
}
/*---------------------------------------------------------------------------------------*/
void write_PadSector(FILE *outfile, int numframes, int frameindex, int sectorindex)
{
    printf("P");

    /* use the last video frame as pad */
    PadSector = LastVideoFrameSectors[sectorindex];

    /* set the propper header info */
    PadSector.dataheader.secCount = sectorindex;
    PadSector.dataheader.frameCount = frameindex;
    PadSector.dataheader.numFrames = numframes;         /* dummy1 */

    fwrite(&PadSector,STREAM_SECTOR_SIZE,1,outfile);
}

#if 0
/*
;
; ABSTRACT
;
; psxweave - combines video and audio files into PSX XA file.
;
; SUMMARY
;
; janweave videofile audiofile xafile [-options]
; janweave test.str null test.xa
;     -nfXX     number of frames XX
;     -max      use the maximum length of the two input files
;     -min      use the minimum length of the two input files
;     -ma       match audio duration
;     -asfXX    audio start frame index XX, default is 2.
;     -y        disable overwrite prompt.
;     -pfXX     pad XX frames at the end, default=30(for one second)
;               in general: XX=150/(FrameRate*SectorsPerFrame)
;     -drop     drop every other frame
;     -nologo   disable banner
;     -v1       prints source file name and errors
;     -v0       no console output
;     -v2       status line for each file (default)
;     -v3       additional info
;     -w0       don't output file
;     -w1       output to standard out
;     -w2       output to file (default)
;     -w3       output to file and standard out
;     -w4       don't output file
;     -w5       hex output to standard out
;     -w6       hex output to file
;     -w7       hex output to file and standard out
;     @file     response file, containing a list of options and files
;     =file     specify a output file ie psxweave source=dest
;
; DESCRIPTION
;
; Psxweave weaves an MDEC str file with XA audio to produce an
; XA video.
;
; RETURNS
;
; no return code
;
; SEE ALSO
;
; gx, dctweave, Spit
;
; SAMPLE OUTPUT
;
; >psxweave *.ssh
;
;   Psx Weave v1.26 (eye) (c) 1992 FrANK Barchard, EAC
;   JET06.SSH: Reading, Weaveing, Writing, 26574->19382 (27.1%)
;
; END ABSTRACT
;
*/
#endif

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

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    printf("Psx Weave v%s by FrANK Barchard, EAC\n",TextVersion);
    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

    switch (utype)
    {
        case 2:
            puts("    -i item size                 -v verbose");
            puts("    -r minimum psx to weave       -w write file");
            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: Psx Weave weaves files with psx length weaveing");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");
            puts("    @file     response file, containing a list of options and files");
          printf("    =file     specify a output file ie %s source=dest\n", prgname);
            puts("    -c1       byte sized clues (default)");
            puts("    -c2       word sized clues");
            puts("    -i1       psxweave bytes (default)");
            puts("    -i2       psxweave words");
            puts("    -r<n>     minimum psx length to weave (3 default)");
            puts("    -t0       Bruce's amiga data format output");
            puts("    -t1       Frank's tg16 data format output (default)");
            puts("    -nologo   disable banner");
            puts("    -v0       no console output");
            puts("    -v1       prints source file name and errors");
            puts("    -v2       status line for each file (default)");
            puts("    -v3       additional info");
            puts("    -w0       don't output file");
            puts("    -w1       output to standard out");
            puts("    -w2       output to file (default)");
            puts("    -w3       output to file and standard out");
            puts("    -w4       don't output file");
            puts("    -w5       hex output to standard out");
            puts("    -w6       hex output to file");
            puts("    -w7       hex output to file and standard out");

        default:
            break;
    }
}

char *SPIT_opt(const char *optptr)
{
    switch (*optptr|(char)' ')
    {
        case 'r':
            return(SPIT_getopt( &OPTRLminlen,optptr+1, "RLminlen", 0, 256));
            break;

        case 'i':
            return(SPIT_getopt( &OPTRLitemsize,optptr+1, "RLitemsize", 1, 256));
            break;

        case 't':
            return(SPIT_getopt( &OPTRLweavetype,optptr+1, "RLweavetype", 0, 1));
            break;

        case 'c':
            return(SPIT_getopt( &OPTRLcluesize,optptr+1, "RLcluesize", 1, 256));
            break;

        default: ;
    }
    return((char *) 0);
}



/*---------------------------------------------------------------------------------------*/

void main(int argc, char **argv)
{
    int sectors_per_second = 150;               /* single speed = 75, 2k/sector */
    int total_output_sectors;
    int sectors_per_frame;
    int sectorCounter;
    int videoSectorCounter;
    int audioSectorCounter;
    int haveVideo, videoOver;
    int haveAudio;
    int frameindex,numframes;
    int lastframeindex;                         /* last Video\Audio frame index */
    int LastVideoFrameSectorsindex, lastaudioframeindex;
    int i,j;
    int videoSectorsPerFrame;
    int numvideoframes,numaudioframes,numaudiosectors;

    printf("PSX XA video/audio weaver, programmed by Jan Tian, May 1997.\n");

    get_input_parameters(argc, argv);

    init_sector_buffer(&VideoSector, &VideoSubHeader);
    init_sector_buffer(&AudioSector, &AudioSubHeader);
    init_sector_buffer(&DummySector, &DummySubHeader);
    init_sector_buffer(&PadSector,   &PadSubHeader);

    /* CD speed: single=75sectors/second, double=150sectors/second */
    LastVideoFrameSectorsindex = 0;
    lastaudioframeindex = 0;

    /* default settings */
    videoOver = 1;
    haveVideo = 0;
    videoSectorsPerFrame = 8;               /* default at 15fps ? */
    numvideoframes = 0;
    haveAudio = 0;
    numaudioframes = 0;

    if ( VideoFile )
    {
        rewind(VideoFile);
        videoOver = 0;
        haveVideo = read_VideoSector(VideoFile);
        videoSectorsPerFrame = VideoSector.dataheader.nSectors;
        if ( videoSectorsPerFrame )
            numvideoframes = filelength(_fileno(VideoFile)) / (videoSectorsPerFrame*STR_VIDEO_SECTOR_SIZE);

        if ( DropFrame )
            numvideoframes /= 2;
    }

    /* calc sectors_per_frame */
    sectors_per_frame = videoSectorsPerFrame;
    while ( (sectors_per_second % sectors_per_frame) != 0 )
        sectors_per_frame++;

    total_output_sectors = 0;
    frameindex = 0;
    sectorCounter = 0;
    videoSectorCounter = 0;
    audioSectorCounter = AudioStartFrameIndex * sectors_per_frame;

    if ( AudioFile )
    {
        numaudiosectors = filelength(_fileno(AudioFile)) / STREAM_SECTOR_SIZE;

        printf("numaudiosectors: %d\n",numaudiosectors);

        if ( sectors_per_frame )
            numaudioframes = (numaudiosectors * 8 + audioSectorCounter) / sectors_per_frame;

        rewind(AudioFile);
        haveAudio = read_AudioSector(AudioFile);
    }

    if ( SpecifiedNumFrames )
        numframes = SpecifiedNumFrames;
    else if ( MatchVideoDuration )
        numframes = numvideoframes;
    else if ( MatchAudioDuration )
        numframes = numaudioframes;
    else if ( UseMinNumFrames )
        numframes = min(numvideoframes, numaudioframes);
    else if ( UseMaxNumFrames )
        numframes = max(numvideoframes, numaudioframes);
    else
        numframes = numvideoframes;

    printf("numvideoframes=%d numaudioframes=%d numframes=%d\n",numvideoframes,numaudioframes,numframes);
    printf("Weaving %d frames of %d sectors(%d video) each; %d audio sectors ......\n\n", numframes,sectors_per_frame,videoSectorsPerFrame,numaudiosectors);

    for (;;)
    {
        if ( audioSectorCounter <= 0 )
        {
            if ( haveAudio )
            {
                write_AudioSector(XAFile);

                lastaudioframeindex = frameindex;
                haveAudio = read_AudioSector(AudioFile);
            }
            else
            {
                write_DummySector(XAFile, numframes, frameindex);
            }

            audioSectorCounter = AudioCompressionRatio;
        }
        else if ( videoSectorCounter < videoSectorsPerFrame )
        {
            if ( haveVideo )
            {
                write_VideoSector(XAFile, numframes, frameindex);

                haveVideo = read_VideoSector(VideoFile);

                if ( !haveVideo )
                {
                    if ( !videoOver )
                    {
                        videoOver = 1;
                        LastVideoFrameSectorsindex = frameindex;
                    }

                    if ( haveAudio )
                    {
                        rewind(VideoFile);
                        haveVideo = read_VideoSector(VideoFile);
                    }
                }
            }
            else
            {
                write_DummySector(XAFile, numframes, frameindex);
            }

            videoSectorCounter++;
        }
        else /* add dummy sectors */
        {
            write_DummySector(XAFile, numframes, frameindex);
        }

        sectorCounter++;
        audioSectorCounter--;
        total_output_sectors++;

        if ( sectorCounter >= sectors_per_frame )
        {
            /* end of frame */
            frameindex++;
            sectorCounter = 0;
            videoSectorCounter = 0;

            /* end of stream reached ? */
            if ( frameindex >= (numframes-1) )
                break;

            if ( DropFrame )
            {
                /* skip_frame */
                for (i=0; i<videoSectorsPerFrame; i++)
                    if ( haveVideo )
                        haveVideo = read_VideoSector(VideoFile);
                    else
                        break;
            }
        }
    }

    /* add pad frames ? */
    while ( NumPadFrames-- )
    {
        for ( i=0; i<sectors_per_frame; i++ )
            write_PadSector(XAFile, numframes, frameindex, i);

        frameindex++;
    }

    /* done */
    printf("\n\n");
    printf("Frameindex:    %d\n", frameindex);
    printf("Total frames:  %d\n", numframes);
    printf("Total sectors: %d\n", total_output_sectors);

    fcloseall();

    exit(0);
}
/*---------------------------------------------------------------------------------------*/

