view data2inc.c @ 510:43ea59887c69

Start work on making C64 formats encoding possible by changing DMDecodeOps to DMEncDecOps and adding fields and op enums for custom encode functions, renaming, etc. Split generic op sanity checking into a separate function in preparation for its use in generic encoding function.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 19 Nov 2012 15:06:01 +0200
parents 59244a7ae37f
children 9d668e48961c
line wrap: on
line source

/*
 * data2inc - Convert binary data to "C"-source or XA-compatible include file
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2003,2009-2012 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include <errno.h>
#include "dmlib.h"
#include "dmargs.h"
#include "dmmutex.h"

#define RA_LINEBUF    (16)

enum
{
    FMT_AUTO = 0,
    FMT_C,
    FMT_ASM
};

char    *optInFilename = NULL,
        *optOutFilename = NULL,
        *optObjName = "default_object",
        *optDataType = NULL,
        *optAddLine = NULL;

int     optIndentation = -1;
int     optFormat = FMT_AUTO;
BOOL    optHexMode = FALSE,
        optQuiet = FALSE,
        optExtraData = FALSE,
        optFormatting = TRUE;


void (*writeHeader) (FILE *, char *) = NULL;
void (*writeDecl) (FILE *, size_t, char *) = NULL;
void (*writeData) (FILE *, Uint8 *, size_t) = NULL;
void (*writeFooter) (FILE *, size_t, char *) = NULL;


static DMOptArg optList[] =
{
    {  0, '?', "help",           "Show this help", OPT_NONE },
    {  4, 'A', "format-asm",     "Output in XA-compatible asm", OPT_NONE },
    {  5, 'C', "format-c",       "Output in ANSI C", OPT_NONE },
    {  1, 'n', "name",           "Set object name", OPT_ARGREQ },
    {  2, 't', "type",           "Set datatype (unsigned char/byte)", OPT_ARGREQ },
    {  3, 'a', "add-line",       "Add this line to start of file", OPT_ARGREQ },
    {  6, 'x', "hexadecimal",    "Use hexadecimal output", OPT_NONE },
    {  7, 'q', "quiet",          "Do not add comments", OPT_NONE },
    {  8, 'f', "no-formatting",  "Disable additional output formatting", OPT_NONE },
    {  9, 'i', "indentation",    "Set indentation (negative value = tab)", OPT_ARGREQ },
    { 10, 'e', "extra-data",     "Add object end labels and size in asm output", OPT_NONE },
};

static const int optListN = sizeof(optList) / sizeof(optList[0]);


void argShowHelp()
{
    dmPrintBanner(stdout, dmProgName,
        "[options] [sourcefile] [destfile]");

    dmArgsPrintHelp(stdout, optList, optListN);
    
    printf(
    "\n"
    "To convert a data file to a C structure using 'Uint8' as type:\n"
    "$ data2inc -C -n variable_name -t Uint8 input.bin output.h\n"
    "\n"
    );
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    switch (optN)
    {
        case 0:
            argShowHelp();
            exit(0);
            break;

        case 1:
            optObjName = optArg;
            break;

        case 2:
            optDataType = optArg;
            break;

        case 3:
            optAddLine = optArg;
            break;

        case 4:
            optFormat = FMT_ASM;
            break;
        case 5:
            optFormat = FMT_C;
            break;
        case 6:
            optHexMode = TRUE;
            break;
        case 7:
            optQuiet = TRUE;
            break;
        case 8:
            optFormatting = FALSE;
            break;

        case 9:
            optIndentation = atoi(optArg);
            break;

        case 10:
            optExtraData = TRUE;
            break;

        default:
            dmError("Unknown option '%s'.\n", currArg);
            return FALSE;
    }

    return TRUE;
}


BOOL argHandleFile(char * currArg)
{
    if (!optInFilename)
        optInFilename = currArg;
    else
    if (!optOutFilename)
        optOutFilename = currArg;
    else
        dmError("Source and destination filenames already specified, extraneous argument '%s'.\n", currArg);

    return TRUE;
}


/* Assembler include data output functions
 */
void writeHeader_ASM(FILE * f, char *name)
{
    if (name)
        fprintf(f, "; '%s'", name);
    else
        fprintf(f, "; Generated");
    fprintf(f, " by %s v%s\n",
        dmProgName, dmProgVersion);
}

void writeDecl_ASM(FILE * f, size_t len, char *name)
{
    if (optExtraData)
        fprintf(f, "%s_size = %u\n", name, len);
    fprintf(f, "%s:\n", name);
}

void writeData_ASM(FILE * f, Uint8 * buf, size_t len)
{
    size_t i;

    fprintf(f, "%s ", optDataType);
    for (i = 0; i < len; i++)
    {
        if (optFormatting)
        {
            if (optHexMode)
                fprintf(f, "$%.2x", buf[i]);
            else
                fprintf(f, "%3d", buf[i]);
        }
        else
        {
            if (optHexMode)
                fprintf(f, "$%x", buf[i]);
            else
                fprintf(f, "%d", buf[i]);
        }

        if (i < (len - 1))
            fprintf(f, ",");
    }
}

void writeFooter_ASM(FILE * f, size_t len, char *name)
{
    (void) len;
    if (optExtraData)
        fprintf(f, "%s_end: \n", name);
    else
        fprintf(f, "\n");
}


/* ANSI-C include data output functions
 */
void writeHeader_C(FILE * f, char *name)
{
    if (name)
        fprintf(f, "/* '%s' generated", name);
    else
        fprintf(f, "/* Generated");

    fprintf(f, " by %s v%s\n" " */\n",
        dmProgName, dmProgVersion);
}

void writeDecl_C(FILE * f, size_t len, char *name)
{
    fprintf(f, "const %s %s[%u] = {\n",
        optDataType, name, len);

    printf("extern const %s %s[%u];\n",
        optDataType, name, len);
}

void writeData_C(FILE * f, Uint8 * buf, size_t len)
{
    size_t i;

    for (i = 0; i < len; i++)
    {
        if (optFormatting)
        {
            if (optHexMode)
                fprintf(f, "0x%.2x", buf[i]);
            else
                fprintf(f, "%3d", buf[i]);
        }
        else
        {
            if (optHexMode)
                fprintf(f, "0x%x", buf[i]);
            else
                fprintf(f, "%d", buf[i]);
        }

        fprintf(f, ",");
    }
}

void writeFooter_C(FILE * f, size_t len, char *name)
{
    (void) len;
    (void) name;
    fprintf(f, "};\n");
}


off_t dmGetFileSize(FILE *f)
{
    off_t len, pos = ftell(f);
    fseeko(f, 0, SEEK_END);
    len = ftell(f);
    fseek(f, pos, SEEK_SET);
    return len;
}


int main(int argc, char *argv[])
{
    FILE *sfile = NULL, *dfile = NULL;
    off_t inSize;
    Uint8 inBuf[RA_LINEBUF];
    int tmpRes;

    /* Initialize */
    dmInitProg("data2inc", "Data to include converter", "0.6", NULL, NULL);
    dmVerbosity = 0;

    /* Parse arguments */
    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, TRUE))
        exit(1);

    /* Determine output type, if not specified */
    if (optFormat == FMT_AUTO)
    {
        char *dext;

        if (optOutFilename == NULL)
        {
            dmError("Output format not specified and no output filename given (try --help)\n");
            exit(1);
        }

        /* Check filename extension */
        dext = strrchr(optOutFilename, '.');
        if (dext)
        {
            dext++;
            if (!strcasecmp(dext, "c") || !strcasecmp(dext, "h") ||
                !strcasecmp(dext, "cc") || !strcasecmp(dext, "cpp") ||
                !strcasecmp(dext, "hpp") || !strcasecmp(dext, "c++"))
                optFormat = FMT_C;
            else
                optFormat = FMT_ASM;
        } else
            optFormat = FMT_ASM;
    }

    /* Set functions */
    switch (optFormat)
    {
        case FMT_ASM:
            if (!optDataType)
                optDataType = ".byte";

            writeHeader = writeHeader_ASM;
            writeDecl = writeDecl_ASM;
            writeData = writeData_ASM;
            writeFooter = writeFooter_ASM;
            break;

        case FMT_C:
            if (!optDataType)
                optDataType = "unsigned char";

            writeHeader = writeHeader_C;
            writeDecl = writeDecl_C;
            writeData = writeData_C;
            writeFooter = writeFooter_C;
            break;

        case FMT_AUTO:
        default:
            dmError("Internal error, FMT_AUTO at output function init.\n");
            exit(2);
    }

    /* Open the files */
    if (optInFilename == NULL)
        sfile = stdin;
    else
    if ((sfile = fopen(optInFilename, "rb")) == NULL)
    {
        tmpRes = errno;
        dmError("Error opening input file '%s'. (%s)\n",
            optInFilename, strerror(tmpRes));
        exit(3);
    }

    if (optOutFilename == NULL)
        dfile = stdout;
    else
    if ((dfile = fopen(optOutFilename, "wa")) == NULL)
    {
        tmpRes = errno;
        dmError("Error creating output file '%s'. (%s)\n",
            optOutFilename, strerror(tmpRes));
        exit(4);
    }

    /* Get sourcefile size */
    inSize = dmGetFileSize(sfile);

    /* Output header */
    if (!optQuiet)
        writeHeader(dfile, optOutFilename);

    if (optAddLine)
        fprintf(dfile, "%s\n", optAddLine);

    /* Output declaration */
    writeDecl(dfile, inSize, optObjName);

    /* Output data */
    while (!feof(sfile))
    {
        tmpRes = fread(inBuf, sizeof(Uint8), RA_LINEBUF, sfile);
        if (tmpRes > 0)
        {
            if (optIndentation < 0)
                fprintf(dfile, "\t");
            else
            if (optIndentation > 0)
            {
                int i;
                for (i = 0; i < optIndentation; i++)
                    fputs(" ", dfile);
            }

            writeData(dfile, inBuf, tmpRes);

            fprintf(dfile, "\n");
        }
    }


    /* Output footer */
    writeFooter(dfile, inSize, optObjName);

    /* Exit */
    fclose(sfile);
    fclose(dfile);

    exit(0);
    return 0;
}