/* $Id: dumpsout.c,v 1.4 2007-02-14 21:28:13 chris Exp $
 *
 * dump a WEGA s.out file
 * written by Christian Groessler <chris@groessler.org>
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/param.h>  /* MAXPATHLEN */
#include "cpgtypes.h"
#include "bitops.h"

#include "s.out.h"

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif


static char *progname;
static char *soutfile_name;
static FILE *soutfile;
static FILE *hdrdump;  /* dump file for header contents (ASCII) */

static int dump; /* if TRUE, -dump command line argument given */
static int silent; /* if TRUE, don't dump header to stdout */

static struct s_exec fhdr;
static struct segt *mysegt;

/* values from fhdr: */
static cpg_u16_t magic, flag;
static cpg_s32_t imsize;  /* Size of memory image section */
static cpg_s32_t bss;     /* Size of bss sections */
static cpg_u16_t segt;    /* Size of segment table section */
static cpg_u16_t syms;    /* Size of symbol table section */
static cpg_s32_t entry;   /* Entry point address */
static cpg_u16_t codesz;  /* 8-bit padded code size */
static cpg_u16_t lines;   /* Number of line table entries */


static void commandline(int argc, char **argv)
{
    int i = 0;
    char *err_arg;

    if (argc < 2) {
        fprintf(stderr, "$Id: dumpsout.c,v 1.4 2007-02-14 21:28:13 chris Exp $\n");
        fprintf(stderr, "(c) Copyright 2007 Christian Groessler <chris@groessler.org>\n");
        fprintf(stderr, "Compiled at " __DATE__ "\n");
        fprintf(stderr, "usage: %s [options] <s.out file>\n",
                progname, progname);
        fprintf(stderr, "\toptions are:\n");
        fprintf(stderr, "\t\t-dump\t\tdump section contents to file\n");
        fprintf(stderr, "\t\t-s\t\tbe silent\n");
        fprintf(stderr, "\n");
        exit(1);
    }

    argc--;
    while(i++, argc--) {
        if (! strcmp(*(argv+i),"-dump")) {
            dump = TRUE;
            continue;
        }
        if (! strcmp(*(argv+i),"-s")) {
            silent = TRUE;
            continue;
        }
        if (soutfile_name) {
            fprintf(stderr,"%s: too many input files\n", progname);
            exit(1);
        }
        soutfile_name = *(argv+i);
    }
}


static void dprintf(char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    cpg_u8_t fn_buf[MAXPATHLEN];

    if (! silent) vprintf(format, ap);

    if (dump) {
        if (! hdrdump) {
            sprintf(fn_buf, "%s-segments.txt", soutfile_name);
            hdrdump = fopen(fn_buf, "w");
            if (! hdrdump) {
                fprintf(stderr, "%s: cannot create header dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return;
            }
        }
        vfprintf(hdrdump, format, ap);
    }

    va_end(ap);
}


static void dump_header(void)
{
    if (fread(&fhdr, sizeof(fhdr), 1, soutfile) != 1) {
        fprintf(stderr, "%s: cannot read from input file: %s\n", progname, strerror(errno));
        return;
    }

    magic = read_be16(&fhdr.s_magic);
    flag = read_be16(&fhdr.s_flag);
    imsize = read_be32(&fhdr.s_imsize);
    bss = read_be32(&fhdr.s_bss);
    segt = read_be16(&fhdr.s_segt);
    syms = read_be16(&fhdr.s_syms);
    entry = read_be32(&fhdr.s_entry);
    codesz = read_be16(&fhdr.s_codesz);
    lines = read_be16(&fhdr.s_lines);

    dprintf("DUMP of \"%s\" s.out header:\n", soutfile_name);
    dprintf("\tmagic:\t\t0x%04X\t", magic);
    switch (magic) {
        case S_MAGIC1:
            dprintf("(segmented executable)\n");
            break;
        case S_MAGIC3:
            dprintf("(segmented executable split I/D)\n");
            break;
        case S_MAGIC4:
            dprintf("(segmented executable overlay)\n");
            break;
        case N_MAGIC1:
            dprintf("(non-segmented executable)\n");
            break;
        case N_MAGIC3:
            dprintf("(non-segmented executable split I/D)\n");
            break;
        case N_MAGIC4:
            dprintf("(non-segmented executable overlay)\n");
            break;
        case X_MAGIC1:
            dprintf("(b-bit executable)\n");
            goto not_supp8;
        case X_MAGIC3:
            dprintf("(8-bit executable split I/D)\n");
            goto not_supp8;
        case X_MAGIC4:
            dprintf("(8-bit executable overlay)\n");
      not_supp8:
            dprintf("\t8 BIT EXECUTABLED NOT SUPPORTED -- ABORTING...\n");
            return;
        default:
            dprintf("\n\tFILE FORMAT NOT RECOGNIZED\n");
            return;
    }

    dprintf("\timage size:\t%6lu  (0x%lx)\n", (unsigned long)imsize, (unsigned long)imsize);
    dprintf("\tBSS size:\t%6lu  (0x%lx)\n", (unsigned long)bss, (unsigned long)bss);
    dprintf("\tsegtable size:\t%6u  (0x%x)\n", (unsigned int)segt, (unsigned int)segt);
    dprintf("\tsymtable size:\t%6u  (0x%x)\n", (unsigned int)syms, (unsigned int)syms);
    dprintf("\tentry point:\t0x%x\n", (unsigned long)entry);
    dprintf("\tflag:\t\t0x%04X", flag);
    if (flag) {
        dprintf("  ( ");
        if (flag & SF_STRIP) dprintf("stripped ");
        if (flag & SF_OPREP) dprintf("oprep ");
        if (flag & SF_Z8) dprintf("z8 ");
        if (flag & SF_Z80) dprintf("z80 ");
        if (flag & SF_SEND) dprintf("send ");
        if (flag & SF_7FSTK) dprintf("7fstack ");
        dprintf(")");
    }
    dprintf("\n");
    dprintf("\tcode size:\t%6u  (0x%x)\n", (unsigned int)codesz, (unsigned int)codesz);
    dprintf("\tlinetab #:\t%6u\n", (unsigned int)lines);
}


static void dump_segtable(void)
{
    unsigned int n, i = 0;
    struct segt struct_segt;
    unsigned short atr;

    if (segt) {  /* segment table is present */
        if (segt % sizeof(struct segt)) {
            fprintf(stderr, "%s: !!! ERROR: segment table size (%u) not a multiple of segment table struct size (%u)\n",
                   progname, (unsigned int)segt, (unsigned int)sizeof(struct segt));
            return;
        }

        /* allocate buffer for segtable */
        mysegt = malloc(segt);
        if (! mysegt) {
            fprintf(stderr, "%s: cannot allocate memory: %s\n", strerror(errno));
            return;
        }

        /* file pointer of soutfile is at first byte after the file header */
        n = segt / sizeof(struct segt);
        while (n--) {
            if (fread(&struct_segt, sizeof(struct_segt), 1, soutfile) != 1) {
                fprintf(stderr, "%s: cannot read from input file: %s\n", progname, strerror(errno));
                return;
            }
            /* *(mysegt + i) = struct_segt; */
            dprintf("\tsegment #%u\n", i /*++*/);
            dprintf("\t\tsegno:\t%u\n", struct_segt.sg_segno);
            dprintf("\t\tcode section offset / 256:\t%u\n", struct_segt.sg_coff);
            dprintf("\t\tdata section offset / 256:\t%u\n", struct_segt.sg_doff);
            dprintf("\t\tbss section offset / 256:\t%u\n", struct_segt.sg_boff);
            dprintf("\t\tcode size:\t%6u  (0x%x)\n",
                    (unsigned int)read_be16(&struct_segt.sg_code),
                    (unsigned int)read_be16(&struct_segt.sg_code));
            dprintf("\t\tdata size:\t%6u  (0x%x)\n",
                    (unsigned int)read_be16(&struct_segt.sg_data),
                    (unsigned int)read_be16(&struct_segt.sg_data));
            dprintf("\t\tbss size:\t%6u  (0x%x)\n",
                    (unsigned int)read_be16(&struct_segt.sg_bss),
                    (unsigned int)read_be16(&struct_segt.sg_bss));
            atr = read_be16(&struct_segt.sg_atr);
            dprintf("\t\tattribute: 0x%04X", atr);
            if (atr) {
                dprintf("  ( ");
                if (atr & SG_CODE) dprintf("code ");
                if (atr & SG_DATA) dprintf("data ");
                if (atr & SG_BSS) dprintf("bss ");
                if (atr & SG_STACK) dprintf("stack ");
                if (atr & SG_OCODE) dprintf("ocode ");
                if (atr & SG_ODATA) dprintf("odata ");
                if (atr & SG_OBSS) dprintf("obss ");
                if (atr & SG_BOUND) dprintf("bound ");
                dprintf(")");
            }
            dprintf("\n");

            /* copy to local cache */
            *(mysegt + i) = struct_segt;  /* fills in correct cpg_u8_t values */
            mysegt[i].sg_code = read_be16(&struct_segt.sg_code);
            mysegt[i].sg_data = read_be16(&struct_segt.sg_data);
            mysegt[i].sg_bss = read_be16(&struct_segt.sg_bss);
            mysegt[i].sg_atr = read_be16(&struct_segt.sg_atr);
            i++;
        }
    }
}


static void dump_segs(void)
{
    unsigned int n, i = 0;
    unsigned int buflen;
    cpg_u8_t *buffer;
    cpg_u8_t fn_buf[MAXPATHLEN];
    FILE *dumpfile;

    n = segt / sizeof(struct segt); /* # of segments */

    while (n--) {
        if (mysegt[i].sg_code) {   /* text */
            buflen = mysegt[i].sg_code;
            buffer = malloc(buflen);
            if (! buffer) {
                abort();
            }

            /* read section data */
            if (fread(buffer, buflen, 1, soutfile) != 1) {
                fprintf(stderr, "%s: cannot read from input file: %s\n", progname, strerror(errno));
                return; /* @@@ */
            }

            /* create dump file */
            sprintf(fn_buf, "%s-segment%u-code-dump.bin", soutfile_name, mysegt[i].sg_segno);
            dumpfile = fopen(fn_buf, "wb");
            if (! dumpfile) {
                fprintf(stderr, "%s: cannot create dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return;
            }

            /* write section data */
            if (fwrite(buffer, buflen, 1, dumpfile) != 1) {
                fprintf(stderr, "%s: cannot write to dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return; /* @@@ */
            }

            /* close dump file */
            if (fclose(dumpfile)) {
                fprintf(stderr, "%s: cannot write to dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return; /* @@@ */
            }
        }
        if (mysegt[i].sg_data) {   /* data */
            buflen = mysegt[i].sg_data;
            buffer = malloc(buflen);
            if (! buffer) {
                abort();
            }

            /* read section data */
            if (fread(buffer, buflen, 1, soutfile) != 1) {
                fprintf(stderr, "%s: cannot read from input file: %s\n", progname, strerror(errno));
                return; /* @@@ */
            }

            /* create dump file */
            sprintf(fn_buf, "%s-segment%u-data-dump.bin", soutfile_name, mysegt[i].sg_segno);
            dumpfile = fopen(fn_buf, "wb");
            if (! dumpfile) {
                fprintf(stderr, "%s: cannot create dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return;
            }

            /* write section data */
            if (fwrite(buffer, buflen, 1, dumpfile) != 1) {
                fprintf(stderr, "%s: cannot write to dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return; /* @@@ */
            }

            /* close dump file */
            if (fclose(dumpfile)) {
                fprintf(stderr, "%s: cannot write to dump file '%s': %s\n", progname, fn_buf, strerror(errno));
                return; /* @@@ */
            }
        }
        i++;
    }
}


int main(int argc, char **argv)
{

    progname = *argv;

    commandline(argc, argv);

    soutfile = fopen(soutfile_name, "rb");
    if (! soutfile) {
        fprintf(stderr, "%s: cannot open %s: %s\n", progname, soutfile_name, strerror(errno));
        return 1;
    }

    dump_header();
    dump_segtable();
    if (dump) dump_segs();

    fclose(soutfile);
    if (hdrdump) fclose(hdrdump);

    return 0;
}

/**************************************************** end of file ********/
/* Local Variables: */
/* c-file-style: "cpg" */
/* c-basic-offset: 4 */
/* End: */
