view tools/data2inc.c @ 2576:812b16ee49db

I had been living under apparent false impression that "realfft.c" on which the FFT implementation in DMLIB was basically copied from was released in public domain at some point, but it could very well be that it never was. Correct license is (or seems to be) GNU GPL. Thus I removing the code from DMLIB, and profusely apologize to the author, Philip Van Baren. It was never my intention to distribute code based on his original work under a more liberal license than originally intended.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 11 Mar 2022 16:32:50 +0200
parents 3feca4682680
children 9807ae37ad69
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-2022 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "dmtool.h"
#include "dmlib.h"
#include "dmargs.h"
#include "dmmutex.h"


#define SET_DEF_LINELEN  16
#define SET_MAX_FEXTS    16


//
// Types etc
//
typedef struct
{
    char *name;
    char *desc;
    char *fexts[SET_MAX_FEXTS];

    char *defDataType;

    int  (*initContext) (void **pctx);
    int  (*closeContext) (void *pctx);

    int  (*writeHeader) (FILE *fh, void *ctx, const char *name);
    int  (*writeDecl) (FILE *fh, void *ctx, const size_t len, const char *name);
    int  (*writeData) (FILE *fh, void *ctx, const Uint8 *buf, const size_t len);
    int  (*writeFooter) (FILE *fh, void *ctx, const size_t len, const char *name);
} DMOutputFormat;


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

const DMOutputFormat *setFormat = NULL;
int     optIndentation = -1,
        optLineLen = SET_DEF_LINELEN;
BOOL    optHexMode = FALSE,
        optQuiet = FALSE,
        optExtraData = FALSE,
        optFormatting = TRUE;


static const DMOptArg optList[] =
{
    {  0, '?', "help"            , "Show this help", OPT_NONE },
    {  1,   0, "license"         , "Print out this program's license agreement", OPT_NONE },

    { 10, 'n', "name"            , "Set object name", OPT_ARGREQ },
    { 12, 't', "type"            , "Set datatype (unsigned char/byte)", OPT_ARGREQ },
    { 14, 'f', "format"          , "Set output format (see list below)", OPT_ARGREQ },
    { 16, 'a', "add-line"        , "Add this line to start of file", OPT_ARGREQ },
    { 18, 'l', "line-items"      , "Set number of items per line", OPT_ARGREQ },
    { 20, 'x', "hexadecimal"     , "Use hexadecimal output", OPT_NONE },
    { 22, 'q', "quiet"           , "Do not add comments", OPT_NONE },
    { 24, 'N', "no-formatting"   , "Disable additional output formatting", OPT_NONE },
    { 26, 'i', "indentation"     , "Set indentation (negative value = tab)", OPT_ARGREQ },
    { 28, 'e', "extra-data"      , "Add object end labels and size in asm output", OPT_NONE },
};

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


static void dmPrintIndentation(FILE *fh)
{
    int level;
    const char *str;

    if (optIndentation < 0)
    {
        level = - optIndentation;
        str = "\t";
    }
    else
    {
        level = optIndentation;
        str = " ";
    }

    for (int i = 0; i < level; i++)
        fputs(str, fh);
}


static int dmHandleError(const int res)
{
    return res >= 0 ? DMERR_OK : dmGetErrno();
}


off_t dmGetFileSize(FILE *fh)
{
    off_t len, pos = ftello(fh);
    fseeko(fh, 0, SEEK_END);
    len = ftello(fh);
    fseeko(fh, pos, SEEK_SET);
    return len;
}


/* Assembler include data output functions
 */
int writeHeader_ASM(FILE *fh, void *ctx, const char *name)
{
    int res = 0;
    (void) ctx;

    if (name)
        res = fprintf(fh, "; '%s'", name);
    else
        res = fprintf(fh, "; Generated");

    if (res >= 0)
        res = fprintf(fh, " by %s v%s\n",
        dmProgName, dmProgVersion);

    return dmHandleError(res);
}


int writeDecl_ASM(FILE *fh, void *ctx, const size_t len, const char *name)
{
    int res = 0;
    (void) ctx;

    if (optExtraData)
        res = fprintf(fh, "%s_size = %" DM_PRIu_SIZE_T "\n", name, len);

    if (res >= 0)
        res = fprintf(fh, "%s:\n", name);

    return dmHandleError(res);
}


int writeData_ASM(FILE *fh, void *ctx, const Uint8 * buf, const size_t len)
{
    int res;
    (void) ctx;

    dmPrintIndentation(fh);
    res = fprintf(fh, "%s ", optDataType);

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

        if (res >= 0 && i + 1 < len)
            res = fprintf(fh, optFormatting ? ", " : ",");
    }

    if (res >= 0)
        res = fprintf(fh, "\n");

    return dmHandleError(res);
}


int writeFooter_ASM(FILE *fh, void *ctx, const size_t len, const char *name)
{
    int res;
    (void) len;
    (void) ctx;

    if (optExtraData)
        res = fprintf(fh, "%s_end: \n", name);
    else
        res = fprintf(fh, "\n");

    return dmHandleError(res);
}


/* ANSI-C include data output functions
 */
int writeHeader_C(FILE *fh, void *ctx, const char *name)
{
    int res;
    (void) ctx;

    if (name)
        res = fprintf(fh, "/* '%s' generated", name);
    else
        res = fprintf(fh, "/* Generated");

    if (res >= 0)
        res = fprintf(fh, " by %s v%s\n" " */\n",
        dmProgName, dmProgVersion);

    return dmHandleError(res);
}


int writeDecl_C(FILE *fh, void *ctx, const size_t len, const char *name)
{
    int res;
    (void) ctx;

    res = fprintf(fh, "%s %s[%" DM_PRIu_SIZE_T "] = {\n",
        optDataType, name, len);

    if (res >= 0)
        res = printf("extern %s %s[%" DM_PRIu_SIZE_T "];\n",
        optDataType, name, len);

    return dmHandleError(res);
}


int writeData_C(FILE *fh, void *ctx, const Uint8 *buf, const size_t len)
{
    int res = 0;
    (void) ctx;

    dmPrintIndentation(fh);

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

        if (res >= 0)
            res = fprintf(fh, optFormatting ? ", " : ",");
    }

    if (res >= 0)
        res = fprintf(fh, "\n");

    return dmHandleError(res);
}


int writeFooter_C(FILE *fh, void *ctx, const size_t len, const char *name)
{
    int res;
    (void) len;
    (void) name;
    (void) ctx;

    res = fprintf(fh, "};\n");
    return dmHandleError(res);
}


/*
 * List of formats
 */
static const DMOutputFormat dmFormatList[] =
{
    {
        "asm",
        "XA65 compatible assembler",
        { "s", "asm", NULL },
        ".byte",

        NULL,
        NULL,

        writeHeader_ASM,
        writeDecl_ASM,
        writeData_ASM,
        writeFooter_ASM,
    },

    {
        "c",
        "ANSI C array",
        { "c", "h", "cc", "cpp", "hpp", "c++", NULL },
        "unsigned char",

        NULL,
        NULL,

        writeHeader_C,
        writeDecl_C,
        writeData_C,
        writeFooter_C,
    },
};

static const int ndmFormatList = sizeof(dmFormatList) / sizeof(dmFormatList[0]);




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

    dmArgsPrintHelp(stdout, optList, optListN, 0, 80 - 2);

    fprintf(stdout,
    "\n"
    "Available output formats (-f <frmt>):\n"
    " frmt | Description (filename extensions)\n"
    "------+------------------------------------------\n");

    for (int i = 0; i < ndmFormatList; i++)
    {
        const DMOutputFormat *fmt = &dmFormatList[i];
        fprintf(stdout, "%-5s | %s (",
            fmt->name, fmt->desc);

        for (int n = 0; n < SET_MAX_FEXTS && fmt->fexts[n] != NULL; n++)
        {
            fprintf(stdout, ".%s%s",
                fmt->fexts[n],
                (n + 1 < SET_MAX_FEXTS &&
                fmt->fexts[n + 1] != NULL) ? " " : "");
        }
        fprintf(stdout, ")\n");
    }

    fprintf(stdout,
    "\n"
    "To convert a data file to a C structure using 'Uint8' as type:\n"
    "$ data2inc -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:
            dmPrintLicense(stdout);
            exit(0);
            break;

        case 10:
            optObjName = optArg;
            break;

        case 12:
            optDataType = optArg;
            break;

        case 14:
            for (int i = 0; i < ndmFormatList; i++)
            {
                const DMOutputFormat *fmt = &dmFormatList[i];
                if (strcasecmp(fmt->name, optArg) == 0)
                {
                    setFormat = fmt;
                    return TRUE;
                }
            }
            dmErrorMsg("Invalid format name '%s'.\n",
                optArg);
            return FALSE;

        case 16:
            optAddLine = optArg;
            break;

        case 18:
            optLineLen = atoi(optArg);
            if (optLineLen < 1)
            {
                dmErrorMsg("Invalid line length / number of items per line '%s'.\n",
                    optArg);
                return FALSE;
            }
            break;

        case 20:
            optHexMode = TRUE;
            break;

        case 22:
            optQuiet = TRUE;
            break;

        case 24:
            optFormatting = FALSE;
            break;

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

        case 28:
            optExtraData = TRUE;
            break;

        default:
            dmErrorMsg("Unimplemented option argument '%s'.\n", currArg);
            return FALSE;
    }

    return TRUE;
}


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

    return TRUE;
}


const DMOutputFormat *dmGuessFormatFromName(const char *filename)
{
    const char *fext;

    if ((fext = strrchr(filename, '.')) == NULL)
        return NULL;

    for (int i = 0; i < ndmFormatList; i++)
    {
        const DMOutputFormat *fmt = &dmFormatList[i];

        for (int n = 0; n < SET_MAX_FEXTS && fmt->fexts[n] != NULL; n++)
        {
            if (strcasecmp(fext + 1, fmt->fexts[n]) == 0)
                return fmt;
        }
    }

    return NULL;
}


int main(int argc, char *argv[])
{
    FILE *inFile = NULL, *outFile = NULL;
    Uint8 *dataBuf = NULL;
    void *ctx = NULL;
    size_t dataSize;
    off_t totalSize;
    int res = DMERR_OK;

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

    // Parse arguments
    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, OPTH_BAILOUT))
        goto out;

    // Determine output type, if not specified
    if (setFormat == NULL)
    {
        if (optOutFilename == NULL)
        {
            argShowHelp();
            res = dmError(DMERR_INVALID_ARGS,
                "Output format not specified and no output filename given.\n");
            goto out;
        }

        if ((setFormat = dmGuessFormatFromName(optOutFilename)) == NULL)
        {
            dmErrorMsg("Could not guess output format from filename '%s'.\n",
                optOutFilename);
            goto out;
        }

        dmMsg(0, "Guessed output format: %s (%s)\n",
            setFormat->desc, setFormat->name);
    }

    // Set some option defaults
    if (optDataType == NULL)
        optDataType = setFormat->defDataType;

    // Open the files
    if (optInFilename == NULL)
        inFile = stdin;
    else
    if ((inFile = fopen(optInFilename, "rb")) == NULL)
    {
        res = dmGetErrno();
        dmErrorMsg("Error opening input file '%s'. (%s)\n",
            optInFilename, dmErrorStr(res));
        goto out;
    }

    if (optOutFilename == NULL)
        outFile = stdout;
    else
    if ((outFile = fopen(optOutFilename, "wa")) == NULL)
    {
        res = dmGetErrno();
        dmErrorMsg("Error creating output file '%s'. (%s)\n",
            optOutFilename, dmErrorStr(res));
        goto out;
    }

    // Allocate linebuffer
    dataSize = optLineLen * sizeof(Uint8);
    if ((dataBuf = dmMalloc(dataSize)) == NULL)
    {
        dmErrorMsg("Could not allocate %" DM_PRIu_SIZE_T " byte buffer.\n",
            dataSize);
        goto out;
    }

    // Get sourcefile size
    totalSize = dmGetFileSize(inFile);

    // Call context init
    if (setFormat->initContext != NULL &&
        (res = setFormat->initContext(&ctx)) != DMERR_OK)
    {
        dmErrorMsg("Error initializing format %s (%s) context: %s\n",
            setFormat->name, setFormat->desc,
            dmErrorStr(res));
        goto out;
    }

    // Output header
    if (!optQuiet &&
        (res = setFormat->writeHeader(outFile, ctx, optOutFilename)) != DMERR_OK)
    {
        dmErrorMsg("Error writing output header: %s\n",
            dmErrorStr(res));
        goto out;
    }

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

    // Output declaration
    if (setFormat->writeDecl != NULL &&
        (res = setFormat->writeDecl(outFile, ctx, totalSize, optObjName)) != DMERR_OK)
    {
        dmErrorMsg("Error writing output declaration: %s\n",
            dmErrorStr(res));
        goto out;
    }

    // Output data
    while (!feof(inFile))
    {
        res = fread(dataBuf, 1, dataSize, inFile);
        if (res > 0 &&
            (res = setFormat->writeData(outFile, ctx, dataBuf, res)) != DMERR_OK)
        {
            dmErrorMsg("Error writing output data: %s\n",
                dmErrorStr(res));
            goto out;
        }
    }

    // Output footer
    if (setFormat->writeFooter != NULL &&
        (res = setFormat->writeFooter(outFile, ctx, totalSize, optObjName)) != DMERR_OK)
    {
        dmErrorMsg("Error writing output footer: %s\n",
            dmErrorStr(res));
        goto out;
    }

out:
    // Cleanup
    if (inFile != NULL)
        fclose(inFile);

    if (outFile != NULL)
        fclose(outFile);

    dmFree(dataBuf);

    if (setFormat != NULL &&
        setFormat->closeContext != NULL &&
        (res = setFormat->closeContext(ctx)) != DMERR_OK)
    {
        dmErrorMsg("Error closing format %s (%s) context: %s\n",
            setFormat->name, setFormat->desc,
            dmErrorStr(res));
    }

    return res;
}