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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                       Dis Assembler v1.13                        ",
"                                                                  ",
"                     by Frank Barchard, EAC                       ",
"                                                                  ",
"                   Main program - Mar 28, 2007                    ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 981029 FB initial version                                ",
"    1.01 981102 FB pass thru all major options                    ",
"    1.02 981104 FB -disasm default, allow .exe, -? help           ",
"    1.03 981105 FB 'name' for spaces                              ",
"    1.04 981129 FB symbols and make options require - or /        ",
"    1.05 010427 FB dis ps2 elf object files                       ",
"    1.06 010505 FB dis gamecube metrowerks                        ",
"    1.07 010507 FB linux version                                  ",
"    1.08 010802 FB gc archives                                    ",
"    1.09 010803 FB osx gcc objects                                ",
"    1.10 011002 FB -v show command line done                      ",
"    1.11 011106 FB ildasm on net assembly                         ",
"    1.12 060425 FB ppu-lv2-objdump                                ",
"    1.13 070328 FB spu-lv2-objdump                                ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

/*
D:\real\lib\gc>xd realcodecz.a -h
XDump v1.66 (Vogue) 010530 by FrANK Barchard, EAC
00000: 213c6172 63683e0a 2f202020 20202020  !<arch>./
00010: 20202020 20202020 39393635 37343631          99657461
00020: 38202020 30202020 20203020 20202020  8   0     0"                                                                  ",
*/

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

#include <stdio.h>
#include <limits.h>
#include "spit.h"
#include "findfile.h"
#include "wildmatch.h"
#include "stdgimex.h"

const char TextVersion[]="1.12 (Samba) 2007.12.4";
#define TOOL_VERSION 113
#define TOOL_NAME "dis"

int OPTlist=0;

#define OBJTYPE_PC 0
#define OBJTYPE_PS2 1
#define OBJTYPE_PS3 2
#define OBJTYPE_SPU 3
#define OBJTYPE_GC 4
#define OBJTYPE_GCC 5
#define OBJTYPE_OSX 6
#define OBJTYPE_NET 7

/* any kind of option (-v or \DISASM) */
int DIS_isoption(char *argv)
{
    int isopt=0;

    if (argv[0]=='-'
#if !defined(__unix__)
      || argv[0]=='/'
#endif
    )
    {
        isopt = 1;
    }
    return(isopt);
}

int DIS_isdisoption(const char *optptr)
{
    int i;
    int ok=0;
    char *disopts[]=
    {
        "all",
        "arch",
        "archivemembers",
        "dependents",
        "directives",
        "disasm",
        "exports",
        "fpo",
        "headers",
        "imports",
        "linenumbers",
        "linkermember",
        "loadconfig",
        "out",
        "pdata",
        "rawdata",
        "relocations",
        "section",
        "summary",
        "symbols",
    };

    if (*optptr=='-' || *optptr=='/')
    {
        ++optptr;
        for (i=0; i<sizeof(disopts)/sizeof(disopts[0]); ++i)
        {
            if (!strnicmp(optptr,disopts[i],strlen(disopts[i])))
            {
                ok = strlen(disopts[i]);
                break;
            }
        }
    }
    return(ok);
}

char *DIS_extension(char *nameptr)
{
    char *s;
    char *ext=NULL;
    s = nameptr+strlen(nameptr)-1;
    while (s>=nameptr)
    {
        if (*s=='.')
        {
            ext = s;
            break;
        }
        if (*s=='/' || *s=='\\' || *s==':')
        {
            break;
        }
        --s;
    }
    return(ext);
}

int DIS_fileexist(char *nextname)
{
    int ok=0;
    GSTREAM *g;
    g = gopen(nextname);
    if (g)
    {
        ok = 1;
        gclose(g);
    }
    return(ok);
}


char *DIS_findfile(char *name)
{
    static char nextname[SPITMAXLINE];

    int x=0;
    char *extensions[]=
    {
        "",".obj",".o",".dll",".exe",".elf",
        "",0,
    };

    do
    {
        sprintf(nextname,"%s%s",name,extensions[x]);
        if (DIS_fileexist(nextname))
            break;
        ++x;
    } while (extensions[x]);

    return(nextname);
}


#if 0
/*
;
; ABSTRACT
;
; dis - tool to call disassembler
;
; SUMMARY
;
; dis [-options] [files]
;     @file          response file, containing a list of options and files
;     =file          specify a listing file ie source=dest
;     -l             output a listing file (.lst)
;     -v0            no console output
;     -v1            prints source file name and errors
;     -v2            status line for each file (default)
;     -v3            additional info
;     -all
;     -arch
;     -archivemembers
;     -dependents
;     -directives
;     -disasm
;     -exports
;     -fpo
;     -headers
;     -imports
;     -linenumbers
;     -linkermember[:{1|2}]
;     -loadconfig
;     -out:filename
;     -pdata
;     -rawdata[:{none|bytes|shorts|longs}[,#]]
;     -relocations
;     -section:name
;     -summary
;     -symbols
;
; DESCRIPTION
;
; Call VC dumpbin but allow spit options and piping output.
;
; RETURNS
;
; 0 if no errors else number of errors
;
; SEE ALSO
;
; wdis, dumpbin, dumpobj
;
; SAMPLE OUTPUT
;
; dis bob.obj
;
; END ABSTRACT
;
*/
#endif

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

char *optionhelp[]=
{
    "    @file          response file, containing a list of options and files\n",
    "    =file          specify a listing file ie source=dest\n",
    "    -l             output a listing file (.lst)\n",
    "    -v0            no console output\n",
    "    -v1            prints source file name and errors\n",
    "    -v2            status line for each file (default)\n",
    "    -v3            additional info\n",
    "    -all\n",
    "    -arch\n",
    "    -archivemembers\n",
    "    -dependents\n",
    "    -directives\n",
    "    -disasm\n",
    "    -exports\n",
    "    -fpo\n",
    "    -headers\n",
    "    -imports\n",
    "    -linenumbers\n",
    "    -linkermember[:{1|2}]\n",
    "    -loadconfig\n",
    "    -out:filename\n",
    "    -pdata\n",
    "    -rawdata[:{none|bytes|shorts|longs}[,#]]\n",
    "    -relocations\n",
    "    -section:name\n",
    "    -summary\n",
    "    -symbols\n",
    0,
};


void SPIT_reset(void)
{
    OPTlist = 0;
    OPTkeepopts = 1;
}

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

    printf("DIS v%s by Frank Barchard,EAC\n",TextVersion);
    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

    switch (utype)
    {
        case 2:
            puts("");
            puts("    -l    output to listing file");
            puts("    -=    specify list file name");
            puts("     @    response file       ");
          printf("%s -h    # for more help\n", prgname);
          printf("type '%s -help' for web page (%s)\n", prgname, urlname);
            puts("");
            break;


        case 3:
          puts("");
          printf("VERSION: %s v%s, Spit v%.2f\n",SPITmachine,TextVersion,(float)SPIT_VERSION/100.0f);
            printf("SUMMARY: %s is a disassembler tool\n", prgname);
          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=0;

    {
        int ok=DIS_isdisoption(optptr);

        if (ok)
        {
            char arg[SPITMAXLINE];

            d = SPIT_getfield( arg,optptr+ok, 0,1);
        }
    }

    if (!d)
    {
        switch (*optptr|(char)' ')
        {
            case 'l':
                d = SPIT_getopt( &OPTlist,optptr+1, "output list file", 0, 1);
                break;
            default:;
        }
    }
    return(d);
}

int main(int argc, char* argv[])
{
    int more;
    int i;
    int j;
    static char commandline[65536];
    static char listfile[SPITMAXLINE];

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);
    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

        /* spawn once per object file */
        for (i=0; i<SPITargnum; ++i)
        {
            if (!DIS_isoption(SPITsargv[i]))
            {
                int hasdisoptions=0;
                char *nextname;
#if defined(_MSC_VER)
                int typeofobj = OBJTYPE_PC;
#elif defined(__APPLE__)
                int typeofobj = OBJTYPE_OSX;
#else
                int typeofobj = OBJTYPE_GCC;
#endif

                nextname = DIS_findfile(SPITsargv[i]);

                {
                    GSTREAM *g;
                    unsigned char buf[32];
                    g = gopen(nextname);
                    gread(g,buf,32);
                    if ((ggetm(buf,4)==GMAKEID(0x7f,'E','L','F'))
                     && (ggetm(buf+4,4)==0x01010100))
#if defined(_MSC_VER)
                        typeofobj = OBJTYPE_PS2;
#else
                        typeofobj = OBJTYPE_GCC;
#endif
                    else if ((ggetm(buf,4)==GMAKEID(0x7f,'E','L','F'))
                     && (ggetm(buf+4,4)==0x02020166))
                        typeofobj = OBJTYPE_PS3;
                    else if ((ggetm(buf,4)==GMAKEID(0x7f,'E','L','F'))
                     && (ggetm(buf+4,4)==0x01020100)
                     && (ggetm(buf+16,4)==0x00010017))
                        typeofobj = OBJTYPE_SPU;
                    else if ((ggetm(buf,4)==GMAKEID(0x7f,'E','L','F'))
                     && (ggetm(buf+4,4)==0x01020100))
                        typeofobj = OBJTYPE_GC;
                    else if (ggetm(buf,4)==GMAKEID('!','<','a','r'))
                        typeofobj = OBJTYPE_GC; // archive
                    else if (ggetm(buf,4)==0xfeed && ggetm(buf+4,4)==0xface)
                        typeofobj = OBJTYPE_OSX; // osx gcc object
                    else if (ggetm(buf,2)==GMAKEID(0,0,'M','Z'))
                    {
                        typeofobj = OBJTYPE_PC; // exe
                        gseek(g,0xe0);
                        gread(g,buf,8);
                        if (buf[0]==0x0a)
                            typeofobj = OBJTYPE_NET;
                    }


                    gclose(g);
#ifndef DEBUG
                    if (OPTverbose>=4)
#endif
                    {
                        printf("%s ",nextname);
                        switch (typeofobj)
                        {
                            default:
                            case OBJTYPE_PC:  printf("PC");  break;
                            case OBJTYPE_PS2: printf("PS2"); break;
                            case OBJTYPE_PS3: printf("PS3"); break;
                            case OBJTYPE_SPU: printf("SPU"); break;
                            case OBJTYPE_GC:  printf("GC");  break;
                            case OBJTYPE_GCC: printf("GCC"); break;
                            case OBJTYPE_OSX: printf("OSX"); break;
                            case OBJTYPE_NET: printf("NET"); break;
                        }
                        printf(", Disassembling\n");
                    }

                }

                if (typeofobj==OBJTYPE_PC)
                    strcpy(commandline,"dumpbin ");
                else if (typeofobj==OBJTYPE_PS2)
                    strcpy(commandline,"ee-objdump ");
                else if (typeofobj==OBJTYPE_PS3)
                    strcpy(commandline,"ppu-lv2-objdump ");
                else if (typeofobj==OBJTYPE_SPU)
                    strcpy(commandline,"spu-lv2-objdump ");
                else if (typeofobj==OBJTYPE_GC)
                    strcpy(commandline,"mwldeppc ");
                else if (typeofobj==OBJTYPE_GCC)
                    strcpy(commandline,"objdump ");
                else if (typeofobj==OBJTYPE_NET)
                    strcpy(commandline,"ildasm ");
                else //if (typeofobj==OBJTYPE_OSX)
                    strcpy(commandline,"objdump ");

                /* get all disassembler options */
                for (j=0; j<SPITargnum; ++j)
                {
                    if (DIS_isdisoption(SPITsargv[j]))
                    {
                        strcat(commandline,SPITsargv[j]);
                        strcat(commandline," ");
                        hasdisoptions = 1;
                        if (OPTverbose>=6)
                            printf("option %s\n",SPITsargv[j]);
                    }
                }
                if (!hasdisoptions)
                {
                    if (typeofobj==OBJTYPE_PC)
                        strcat(commandline,"-disasm -symbols ");
                    else if (typeofobj==OBJTYPE_PS2)
                        strcat(commandline,"--disassemble ");
                    else if (typeofobj==OBJTYPE_PS3)
                        strcat(commandline,"--disassemble ");
                    else if (typeofobj==OBJTYPE_SPU)
                        strcat(commandline,"--disassemble ");
                    else if (typeofobj==OBJTYPE_GC)
                        strcat(commandline,"-dis ");
                    else if (typeofobj==OBJTYPE_OSX)
                        strcat(commandline,"-d ");
                    else if (typeofobj==OBJTYPE_NET)
                        strcat(commandline," ");
                    else //if (typeofobj==OBJTYPE_GCC)
                        strcat(commandline,"-d ");
                }

                if (strchr(nextname,' '))
                    strcat(commandline,"\"");
                strcat(commandline,nextname);
                if (strchr(nextname,' '))
                    strcat(commandline,"\"");
                strcat(commandline," ");

                if (OPTlist || strcmp(SPITsargv[i],SPITdargv[i]))
                {
                    char *ext;
                    int isobj=0;
                    strcpy(listfile,SPITdargv[i]);
                    ext = DIS_extension(SPITdargv[i]);
                    if (ext)
                        if (strstr(".obj.o.dll.exe.elf",ext))
                            isobj = 1;

                    if (isobj || !strcmp(nextname,SPITdargv[i]) || (!ext && OPTlist))
                        SPIT_changeext( listfile,listfile, "lst");
                    strcat(commandline,">");
                    strcat(commandline,listfile);
                }
                if (OPTverbose>=4)
                    puts(commandline);
                if (OPTwrite) /* -w0 to disable */
                    system(commandline);
            }
        }
    } while (more > 0);
    SPIT_restore();

    return(0);
}

