/*
    FPSE main()
    ===========

    Written by BERO
    Modified by LDChen
*/

#include "fpse.h"

// FPSE can use this executables:
#define PSX_EXE     1
#define CPE_EXE     2
#define COFF_EXE    3
#define INVALID_EXE 4

// Actually these are unused, but in the future...
#if 0
// Section names of COFF format:
static char text_name[]  = { ".text"  };
static char rdata_name[] = { ".rdata" };
static char data_name[]  = { ".data"  };
static char sdata_name[] = { ".sdata" };
static char sbss_name[]  = { ".sbss"  };
static char bss_name[]   = { ".bss"   };

static char *COFF_Names[8] = {
    text_name,
    rdata_name,
    data_name,
    sdata_name,
    sbss_name,
    bss_name,
    NULL,
    NULL
};
#endif

#define SCOFF_TEXT      0
#define SCOFF_RDATA     1
#define SCOFF_DATA      2
#define SCOFF_SDATA     3
#define SCOFF_SBSS      4
#define SCOFF_BSS       5
#define SCOFF_UNKNOWN   6

unsigned char *rom,*ram,*scratchpad,*extrom;

int FPSE_Flags = EMULATE_BIOS | COMPILE;

extern int breakpoint;

static char ActionR[] = { "ar.rom" };

static UINT32 FSize(FILE *f)
{
    UINT32 curpos = ftell(f);
    UINT32 endpos;

    fseek(f,0L,SEEK_END);
    endpos = ftell(f);
    fseek(f,curpos,SEEK_SET);

    return endpos;
}

static int PSXGetFileType(FILE *f)
{
    UINT32      current;
    UINT8       mybuf[2048];
    EXE_HEADER *exe_hdr;
    FILHDR     *coff_hdr;

    current = ftell(f);
    fseek(f,0L,SEEK_SET);
    fread(mybuf,2048,1,f);
    fseek(f,current,SEEK_SET);

    exe_hdr = (EXE_HEADER *)mybuf;
    if (memcmp(exe_hdr->id,"PS-X EXE",8)==0)
        return PSX_EXE;

    if (mybuf[0]=='C' && mybuf[1]=='P' && mybuf[2]=='E')
        return CPE_EXE;

    coff_hdr = (FILHDR *)mybuf;
    if (coff_hdr->f_magic == 0x0162)
        return COFF_EXE;

    return INVALID_EXE;
}

#if 0
static int COFFSectionName(char *str)
{
    int x;

    COFF_Names[SCOFF_UNKNOWN] = str;
    for (x=SCOFF_TEXT; x<=SCOFF_UNKNOWN; x++)
        if (!strcmp(str,COFF_Names[x])) break;

    return x;
}
#endif

static int PSXLoadFile(char *nf, UINT32 addr, int flags)
{
    FILE      *fp;
    EXE_HEADER head;
    int        type;
    int        size;
    UINT8     *mybuf;
    UINT8     *buf;
    FILHDR    *coff_hdr;
    AOUTHDR   *opt_hdr;
    SCNHDR    *section;

    fp = fopen(nf,"rb");
    if (fp==NULL) return FPSE_ERR;

    if (addr)
    {
        printf("Loading %s at %08x\n",nf,(int)addr);
        size = FSize(fp);
        fread(ram + (addr & 0x1FFFFF),1,size,fp);
    } else {
        type = PSXGetFileType(fp);
        switch (type) {
        case PSX_EXE:
            fread(&head,1,sizeof(head),fp);
            head.t_addr = SWAP32(head.t_addr);
            head.t_size = SWAP32(head.t_size);
            head.pc0 = SWAP32(head.pc0);
            head.s_addr = SWAP32(head.s_addr);
            printf("Loading %s (PS_EXE) at %08x-%08x\n",
                   nf,(int)head.t_addr,(int)head.t_addr+(int)head.t_size);
            fread(&ram[head.t_addr&0x1fffff],1,head.t_size,fp);
            if (flags)
            {
                printf("Setting up PC=%08x\n",(int)head.pc0);
                setpc(head.pc0);
                reg.r[30] = reg.r[29] = head.s_addr;
                reg.r[31] = 0;
            }
            break;
        case CPE_EXE:
            printf("Loading %s (CPE_EXE)\n",nf);
            size = FSize(fp);
            mybuf = (UINT8*)malloc(size);
            fread(mybuf,1,size,fp);
            buf = mybuf + 6;
            while (*buf)
            {
                switch (*buf) {
                case 3:
                    if (flags)
                    {
                        UINT32 myPc = *(UINT32 *)(buf + 3);

                        printf("Setting up PC=%08x\n",(int)myPc);
                        setpc(myPc);
                        reg.r[30] = reg.r[29] = 0x801FFFF0;
                        reg.r[31] = 0;
                        buf += 7;
                    }
                    break;
                case 1:
                    {
                    UINT32 base,size;

                    base = *(UINT32 *)(buf + 1);
                    size = *(UINT32 *)(buf + 5);

                    PRINTF("Base Address = 0x%08x Size = 0x%08x(%d)\n" ,
                            (int)base , (int)size , (int)size);
                    memcpy(&ram[base & 0x1FFFFF], buf + 9, size);
                    buf += 9+size;
                    break;
                    }
                default:
                    printf("Unknown id field 0x%02x at offset 0x%08x" ,
                            *buf, (int)(buf - mybuf));
                    free(mybuf);
                    fclose(fp);
                    return FPSE_ERR;
          }
            }
            free(mybuf);
            break;
        case COFF_EXE:
            printf("Loading %s (COFF_EXE)\n",nf);
            size = FSize(fp);
            mybuf = (UINT8*)malloc(size);
            fread(mybuf,1,size,fp);
            coff_hdr = (FILHDR *)mybuf;
            opt_hdr  = (AOUTHDR*)(coff_hdr+1);
            section = (SCNHDR *)(mybuf + FILHSZ + coff_hdr->f_opthdr);

            if (flags)
            {
                printf("Setting up PC=%08x\n",(int)opt_hdr->entry);
                setpc(0x80010030);
                reg.r[30] = reg.r[29] = 0x801fff00;
                reg.r[31] = opt_hdr->entry;
            }

            for (size = 0; size < coff_hdr->f_nscns ; size++)
            {
/*
                if (flags)
                {
                    switch (COFFSectionName(section->s_name)) {
                    case SCOFF_BSS:
                        break;
                    }
                }
*/
                PRINTF("%8s at %08x size %08x\n",
                       section->s_name,
                       (int)section->s_paddr,
                       (int)section->s_size);

                if (section->s_scnptr)
                    memcpy(&ram[(section->s_paddr) & 0x1FFFFF],
                       mybuf+section->s_scnptr,section->s_size);
                else memset(&ram[(section->s_paddr) & 0x1FFFFF],
                            0,section->s_size);

                section++;
            }
            free(mybuf);
            break;
        case INVALID_EXE:
            printf("ERROR: %s not a valid EXE file.\n",nf);
            break;
        }
    }

    fclose(fp);

    return FPSE_OK;
}

static char helpmsg[] = 
     "\nSyntax: fpse [-option] [file]\n\n"
     " file         PSX-EXE file\n"
     " -c           compiler(default)\n"
     " -i           interpriter\n"
     " -b<biosfile> bios filename\n"
     " -a<romfile>  load A/R Flash\n"
     " -v           verbose\n"
     " -d           disassemble\n"
     " -e           run debugger\n"
     " -s<script>   script file name\n"
#ifdef AMIGA
     " -u#          cd unit (def: 2)\n"
     " -x<device>   cd device (def: atapi.device)\n"
     " -f           use screen (def: window)\n"
#endif
     " -?           display this help screen\n"
     "\nPlease read 'readme.txt' and 'terms.txt' before start.\n";

#ifdef AMIGA
     extern int amiga_cd_unit;
     extern char *amiga_cd_device;
     extern int amiga_use_screen;
#endif

int main(int argc,char *argv[])
{
    FILE  *fp         = NULL;
    char  *file       = NULL;
    char  *scriptfile = NULL;
    char  *flashfile  = NULL;
    char   buffer[80];
    char   cmd[8];
    char   filename[80];
    UINT32 address;
    int    i,line;

//   breakpoint = 0x8003ace8;

    printf("FPSE - *FREE* PlayStation Emulator\n"
           "Win32 Version %2x.%02x\n\n"
           "Written by BERO and LDCHEN\n",
           FPSE_VERSION>>8, FPSE_VERSION & 0xFF);

    LoadCFG();

    for(i=1;i<argc;i++) {
        char *s = argv[i];

        if (s[0]=='-') {
            switch(s[1]) {
            case 'a':
                if (*(s+2) == 0) flashfile = ActionR;
                else {
                    flashfile = s+2;
                    FPSE_Flags |= ACTIONREPLAY;
                } 
                break;
            case 'c': FPSE_Flags |= COMPILE;   break;
            case 'i': FPSE_Flags &= ~COMPILE;  break;
            case 'v': FPSE_Flags |= VERBOSE;   break;
            case 'd': FPSE_Flags |= DISASMFLG; break;
            case 'b': FPSEIni.BiosName = s+2;  break;
            case 'e': FPSE_Flags |= DEBUG;     break;
            case 's': scriptfile = s+2;        break;
#ifdef AMIGA
            case 'u': amiga_cd_unit = atoi(s+2); break;
            case 'x': amiga_cd_device = strdup(s+2); break;
            case 'f': amiga_use_screen = 1; break;
#endif
            case '?':
            default:
                INI_Free();
                printf(helpmsg);
                return -1;
            }
        } else file = s;
    }

#ifdef AMIGA
    if (amiga_cd_device == NULL) {
        amiga_cd_device = strdup("atapi.device");
    }
#endif

    memalloc();
    bios_init();

    if (hw_init() != FPSE_OK)
    {
        printf("Couldn't initialize the hardware!\n");
        memfree();
        hw_close();
        INI_Free();
        return -1;
    }

    compile_init();
    Reset();

    if (FPSEIni.BiosName) {
        fp = fopen(FPSEIni.BiosName,"rb");
        if (fp==NULL) {
            strcpy(buffer,"bios\\");
            strcat(buffer,FPSEIni.BiosName);
            fp = fopen(buffer,"rb");
        }
        if (fp==NULL) {
            printf("can't load biosfile\n");
        } else {
            FPSE_Flags &= ~EMULATE_BIOS;
            fread(rom,1,0x80000,fp);
            fclose(fp);
// Now run the BIOS
            i = FPSE_Flags;
            FPSE_Flags &= ~(VERBOSE | DISASMFLG);
//            FPSE_Flags |= VERBOSE | DISASMFLG;
            while(PC != 0x80030000) doInst();
            FPSE_Flags = i;
        }
    }

    if (flashfile)
    {
        fp = fopen(flashfile,"rb");
        if (fp==NULL) {
            strcpy(buffer,"actionR\\");
            strcat(buffer,flashfile);
            fp = fopen(buffer,"rb");
        }
        if (fp==NULL) {
            printf("can't load A/R %s, using defaults\n",flashfile);
            flashfile = NULL;
        } else {
            printf("loading %s...\n",flashfile);
            fread(extrom,1,0x20000,fp);
            fclose(fp);
            Reset();
            if (emulate_bios)
            {
                setpc(*(UINT32 *)(extrom));
                reg.r[30] = reg.r[29] = 0x801fff00;
                reg.r[31] = 0;
            }
        }
    }

    PSXLoadFile("LibPS.exe",0,0);

    if (scriptfile)
    {
        fp = fopen(scriptfile,"rt");
        if (fp != NULL)
        {
            line = 0;
            while (fgets(buffer,sizeof(buffer),fp))
            {
                line++;
                i = sscanf(buffer,"%s %s %x",cmd,filename,(int *)&address);
                if (i != 2 && i != 3)
                {
                    printf("Syntax error in '%s' line %d.\n",scriptfile,line);
                    break;
                } else {
                    if (!stricmp(cmd,"run")) {
                      PSXLoadFile(filename,0,1);
                    } else if (!stricmp(cmd,"load")) {
                        if (i==2) PSXLoadFile(filename,0,0);
                             else PSXLoadFile(filename,address,0);
                    }
                }
            }
            fclose(fp);
        } else printf("Script '%s' not found.\n",scriptfile);
    }

        
    if (file) PSXLoadFile(file,0,1);

    if (debug) run_debug();
          else
    if (compile) compile_go();
            else go();

    INI_Free();

    memfree();

    hw_close();

    return 0;
}

#if 1
int printlog(char *fmt, ...)
{
     int ret;

     va_list argp;
     va_start(argp,fmt);
     ret = vprintf(fmt,argp);
     va_end(argp);

     return ret;
}

#else
int printlog(char *fmt, ...)
{
     int ret;

     va_list argp;
     static char buf[256],prevbuf[256];
     static char *bufp=buf;
     static int count;

     va_start(argp,fmt);
     ret = vsprintf(bufp,fmt,argp);
     va_end(argp);

     bufp += strlen(bufp);
     if (bufp[-1] != '\n') {
          return ret;
     }

     if (strcmp(buf,prevbuf)==0) {
          count++;
     } else {
          if (count) { printf(" %d times\n",count+1); count=0; }
          printf("%s",buf);
          strcpy(prevbuf,buf);
     }
     bufp=buf;

     return ret;
}

#endif