changeset 2246:d76b0c92769d

Refactor data2inc to be more flexible.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 15 Jun 2019 20:14:14 +0300
parents 95f669692b01
children bb7255792dd6
files tools/data2inc.c
diffstat 1 files changed, 260 insertions(+), 157 deletions(-) [+]
line wrap: on
line diff
--- a/tools/data2inc.c	Sat Jun 15 14:14:40 2019 +0300
+++ b/tools/data2inc.c	Sat Jun 15 20:14:14 2019 +0300
@@ -1,7 +1,7 @@
 /*
  * 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)
+ * (C) Copyright 2003,2009-2019 Tecnic Software productions (TNSP)
  *
  * Please read file 'COPYING' for information on license and distribution.
  */
@@ -11,64 +11,99 @@
 #include "dmmutex.h"
 
 
-#define RA_LINEBUF    (16)
+#define SET_DEF_LINELEN  16
+#define SET_MAX_FEXTS    16
+
 
-enum
+//
+// Types etc
+//
+typedef struct
 {
-    FMT_AUTO = 0,
-    FMT_C,
-    FMT_ASM
-};
+    char *name;
+    char *desc;
+    char *fexts[SET_MAX_FEXTS];
+
+    char *type;
 
+    void (*writeHeader) (FILE *fh, const char *name);
+    void (*writeDecl) (FILE *fh, const size_t len, const char *name);
+    void (*writeData) (FILE *fh, const Uint8 *buf, const size_t len);
+    void (*writeFooter) (FILE *fh, const size_t len, const char *name);
+} DMOutputFormat;
+
+
+//
+// Options
+//
 char    *optInFilename = NULL,
         *optOutFilename = NULL,
         *optObjName = "default_object",
         *optDataType = NULL,
         *optAddLine = NULL;
 
-int     optIndentation = -1;
-int     optFormat = FMT_AUTO;
+const DMOutputFormat *setFormat = NULL;
+int     optIndentation = -1,
+        optLineLen = SET_DEF_LINELEN;
 BOOL    optHexMode = FALSE,
         optQuiet = FALSE,
         optExtraData = FALSE,
         optFormatting = TRUE;
 
 
-void (*writeHeader) (FILE *, char *) = NULL;
-void (*writeDecl) (FILE *, unsigned int, char *) = NULL;
-void (*writeData) (FILE *, Uint8 *, unsigned int) = NULL;
-void (*writeFooter) (FILE *, unsigned int, char *) = NULL;
-
-
 static const 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 },
+    {  3, 'f', "format",         "Set output format (see list below)", OPT_ARGREQ },
+    {  4, 'a', "add-line",       "Add this line to start of file", OPT_ARGREQ },
+    {  5, 'n', "nitems",         "Set number of items per line", 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 },
+    {  8, 'N', "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]);
+static const DMOutputFormat dmFormatList[];
+static const int ndmFormatList;
 
 
 void argShowHelp()
 {
     dmPrintBanner(stdout, dmProgName,
-        "[options] [sourcefile] [destfile]");
+    "[options] [sourcefile] [destfile]");
 
     dmArgsPrintHelp(stdout, optList, optListN, 0);
 
-    printf(
+    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 -C -n variable_name -t Uint8 input.bin output.h\n"
+    "$ data2inc -n variable_name -t Uint8 input.bin output.h\n"
     "\n"
     );
 }
@@ -92,21 +127,42 @@
             break;
 
         case 3:
+            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 4:
             optAddLine = optArg;
             break;
 
-        case 4:
-            optFormat = FMT_ASM;
+        case 5:
+            optLineLen = atoi(optArg);
+            if (optLineLen < 1)
+            {
+                dmErrorMsg("Invalid line length / number of items per line '%s'.\n",
+                    optArg);
+                return FALSE;
+            }
             break;
-        case 5:
-            optFormat = FMT_C;
-            break;
+
+
         case 6:
             optHexMode = TRUE;
             break;
+
         case 7:
             optQuiet = TRUE;
             break;
+
         case 8:
             optFormatting = FALSE;
             break;
@@ -130,10 +186,10 @@
 
 BOOL argHandleFile(char * currArg)
 {
-    if (!optInFilename)
+    if (optInFilename == NULL)
         optInFilename = currArg;
     else
-    if (!optOutFilename)
+    if (optOutFilename == NULL)
         optOutFilename = currArg;
     else
         dmErrorMsg("Source and destination filenames already specified, extraneous argument '%s'.\n", currArg);
@@ -144,259 +200,306 @@
 
 /* Assembler include data output functions
  */
-void writeHeader_ASM(FILE * f, char *name)
+void writeHeader_ASM(FILE *fh, const char *name)
 {
     if (name)
-        fprintf(f, "; '%s'", name);
+        fprintf(fh, "; '%s'", name);
     else
-        fprintf(f, "; Generated");
-    fprintf(f, " by %s v%s\n",
+        fprintf(fh, "; Generated");
+
+    fprintf(fh, " by %s v%s\n",
         dmProgName, dmProgVersion);
 }
 
-void writeDecl_ASM(FILE * f, unsigned int len, char *name)
+
+void writeDecl_ASM(FILE *fh, const size_t len, const char *name)
 {
     if (optExtraData)
-        fprintf(f, "%s_size = %u\n", name, len);
-    fprintf(f, "%s:\n", name);
+        fprintf(fh, "%s_size = %" DM_PRIu_SIZE_T "\n", name, len);
+
+    fprintf(fh, "%s:\n", name);
 }
 
-void writeData_ASM(FILE * f, Uint8 * buf, unsigned int len)
+
+void writeData_ASM(FILE *fh, const Uint8 * buf, const size_t len)
 {
-    fprintf(f, "%s ", optDataType);
-    for (unsigned int i = 0; i < len; i++)
+    fprintf(fh, "%s ", optDataType);
+
+    for (size_t i = 0; i < len; i++)
     {
         if (optFormatting)
         {
             if (optHexMode)
-                fprintf(f, "$%.2x", buf[i]);
+                fprintf(fh, "$%.2x", buf[i]);
             else
-                fprintf(f, "%3d", buf[i]);
+                fprintf(fh, "%3d", buf[i]);
         }
         else
         {
             if (optHexMode)
-                fprintf(f, "$%x", buf[i]);
+                fprintf(fh, "$%x", buf[i]);
             else
-                fprintf(f, "%d", buf[i]);
+                fprintf(fh, "%d", buf[i]);
         }
 
-        if (i < len - 1)
-            fprintf(f, ",");
+        if (i + 1 < len)
+            fprintf(fh, ",");
     }
 }
 
-void writeFooter_ASM(FILE * f, unsigned int len, char *name)
+
+void writeFooter_ASM(FILE *fh, const size_t len, const char *name)
 {
     (void) len;
     if (optExtraData)
-        fprintf(f, "%s_end: \n", name);
+        fprintf(fh, "%s_end: \n", name);
     else
-        fprintf(f, "\n");
+        fprintf(fh, "\n");
 }
 
 
 /* ANSI-C include data output functions
  */
-void writeHeader_C(FILE * f, char *name)
+void writeHeader_C(FILE *fh, const char *name)
 {
     if (name)
-        fprintf(f, "/* '%s' generated", name);
+        fprintf(fh, "/* '%s' generated", name);
     else
-        fprintf(f, "/* Generated");
+        fprintf(fh, "/* Generated");
 
-    fprintf(f, " by %s v%s\n" " */\n",
+    fprintf(fh, " by %s v%s\n" " */\n",
         dmProgName, dmProgVersion);
 }
 
-void writeDecl_C(FILE * f, unsigned int len, char *name)
+
+void writeDecl_C(FILE *fh, const size_t len, const char *name)
 {
-    fprintf(f, "%s %s[%u] = {\n",
+    fprintf(fh, "%s %s[%" DM_PRIu_SIZE_T "] = {\n",
         optDataType, name, len);
 
-    printf("extern %s %s[%u];\n",
+    printf("extern %s %s[%" DM_PRIu_SIZE_T "];\n",
         optDataType, name, len);
 }
 
-void writeData_C(FILE * f, Uint8 * buf, unsigned int len)
+
+void writeData_C(FILE *fh, const Uint8 *buf, const size_t len)
 {
-    unsigned int i;
-
-    for (i = 0; i < len; i++)
+    for (size_t i = 0; i < len; i++)
     {
         if (optFormatting)
         {
             if (optHexMode)
-                fprintf(f, "0x%.2x", buf[i]);
+                fprintf(fh, "0x%.2x", buf[i]);
             else
-                fprintf(f, "%3d", buf[i]);
+                fprintf(fh, "%3d", buf[i]);
         }
         else
         {
             if (optHexMode)
-                fprintf(f, "0x%x", buf[i]);
+                fprintf(fh, "0x%x", buf[i]);
             else
-                fprintf(f, "%d", buf[i]);
+                fprintf(fh, "%d", buf[i]);
         }
 
-        fprintf(f, ",");
+        fprintf(fh, ",");
     }
 }
 
-void writeFooter_C(FILE * f, unsigned int len, char *name)
+
+void writeFooter_C(FILE *fh, const size_t len, const char *name)
 {
     (void) len;
     (void) name;
-    fprintf(f, "};\n");
+
+    fprintf(fh, "};\n");
 }
 
 
-off_t dmGetFileSize(FILE *f)
+static const DMOutputFormat dmFormatList[] =
 {
-    off_t len, pos = ftello(f);
-    fseeko(f, 0, SEEK_END);
-    len = ftello(f);
-    fseeko(f, pos, SEEK_SET);
+    {
+        "asm",
+        "XA65 compatible assembler",
+        { "s", "asm", NULL },
+        ".byte",
+
+        writeHeader_ASM,
+        writeDecl_ASM,
+        writeData_ASM,
+        writeFooter_ASM,
+    },
+
+    {
+        "c",
+        "ANSI C array",
+        { "c", "h", "cc", "cpp", "hpp", "c++", NULL },
+
+        "unsigned char",
+
+        writeHeader_C,
+        writeDecl_C,
+        writeData_C,
+        writeFooter_C,
+    },
+};
+
+static const int ndmFormatList = sizeof(dmFormatList) / sizeof(dmFormatList[0]);
+
+
+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;
 }
 
 
+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 *sfile = NULL, *dfile = NULL;
-    off_t inSize;
-    Uint8 inBuf[RA_LINEBUF];
-    int tmpRes;
+    FILE *inFile = NULL, *outFile = NULL;
+    Uint8 *dataBuf = NULL;
+    size_t dataSize;
+    off_t totalSize;
+    int res;
 
-    /* Initialize */
-    dmInitProg("data2inc", "Data to include converter", "0.6", NULL, NULL);
+    // Initialize
+    dmInitProg("data2inc", "Data to include converter", "0.7", NULL, NULL);
     dmVerbosity = 0;
 
-    /* Parse arguments */
+    // Parse arguments
     if (!dmArgsProcess(argc, argv, optList, optListN,
         argHandleOpt, argHandleFile, OPTH_BAILOUT))
         exit(1);
 
-    /* Determine output type, if not specified */
-    if (optFormat == FMT_AUTO)
+    // Determine output type, if not specified
+    if (setFormat == NULL)
     {
-        char *dext;
-
         if (optOutFilename == NULL)
         {
             dmErrorMsg("Output format not specified and no output filename given (try --help)\n");
-            exit(1);
+            goto exit;
         }
 
-        /* Check filename extension */
-        dext = strrchr(optOutFilename, '.');
-        if (dext)
+        if ((setFormat = dmGuessFormatFromName(optOutFilename)) == NULL)
         {
-            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;
+            dmErrorMsg("Could not guess output format from filename '%s'.\n",
+                optOutFilename);
+            goto exit;
+        }
+
+        dmMsg(0, "Guessed output format: %s (%s)\n",
+            setFormat->desc, setFormat->name);
     }
 
-    /* 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";
+    // Set some option defaults
+    if (optDataType == NULL)
+        optDataType = setFormat->type;
 
-            writeHeader = writeHeader_C;
-            writeDecl = writeDecl_C;
-            writeData = writeData_C;
-            writeFooter = writeFooter_C;
-            break;
-
-        case FMT_AUTO:
-        default:
-            dmErrorMsg("Internal error, FMT_AUTO at output function init.\n");
-            exit(2);
-    }
-
-    /* Open the files */
+    // Open the files
     if (optInFilename == NULL)
-        sfile = stdin;
+        inFile = stdin;
     else
-    if ((sfile = fopen(optInFilename, "rb")) == NULL)
+    if ((inFile = fopen(optInFilename, "rb")) == NULL)
     {
-        tmpRes = dmGetErrno();
+        res = dmGetErrno();
         dmErrorMsg("Error opening input file '%s'. (%s)\n",
-            optInFilename, dmErrorStr(tmpRes));
-        exit(3);
+            optInFilename, dmErrorStr(res));
+        goto exit;
     }
 
     if (optOutFilename == NULL)
-        dfile = stdout;
+        outFile = stdout;
     else
-    if ((dfile = fopen(optOutFilename, "wa")) == NULL)
+    if ((outFile = fopen(optOutFilename, "wa")) == NULL)
     {
-        tmpRes = dmGetErrno();
+        res = dmGetErrno();
         dmErrorMsg("Error creating output file '%s'. (%s)\n",
-            optOutFilename, dmErrorStr(tmpRes));
-        exit(4);
+            optOutFilename, dmErrorStr(res));
+        goto exit;
+    }
+
+    // Allocate linebuffer
+    dataSize = optLineLen * sizeof(Uint8);
+    if ((dataBuf = dmMalloc(dataSize)) == NULL)
+    {
+        dmErrorMsg("Could not allocate %" DM_PRIu_SIZE_T " byte buffer.\n",
+            dataSize);
+        goto exit;
     }
 
-    /* Get sourcefile size */
-    inSize = dmGetFileSize(sfile);
+    // Get sourcefile size
+    totalSize = dmGetFileSize(inFile);
 
-    /* Output header */
+    // Output header
     if (!optQuiet)
-        writeHeader(dfile, optOutFilename);
+        setFormat->writeHeader(outFile, optOutFilename);
 
     if (optAddLine)
-        fprintf(dfile, "%s\n", optAddLine);
+        fprintf(outFile, "%s\n", optAddLine);
 
-    /* Output declaration */
-    writeDecl(dfile, inSize, optObjName);
+    // Output declaration
+    setFormat->writeDecl(outFile, totalSize, optObjName);
 
-    /* Output data */
-    while (!feof(sfile))
+    // Output data
+    while (!feof(inFile))
     {
-        tmpRes = fread(inBuf, sizeof(Uint8), RA_LINEBUF, sfile);
-        if (tmpRes > 0)
+        res = fread(dataBuf, 1, dataSize, inFile);
+        if (res > 0)
         {
             if (optIndentation < 0)
-                fprintf(dfile, "\t");
+            {
+                for (int i = 0; i < -optIndentation; i++)
+                    fputs("\t", outFile);
+            }
             else
             if (optIndentation > 0)
             {
-                int i;
-                for (i = 0; i < optIndentation; i++)
-                    fputs(" ", dfile);
+                for (int i = 0; i < optIndentation; i++)
+                    fputs(" ", outFile);
             }
 
-            writeData(dfile, inBuf, tmpRes);
+            setFormat->writeData(outFile, dataBuf, res);
 
-            fprintf(dfile, "\n");
+            fprintf(outFile, "\n");
         }
     }
 
-
-    /* Output footer */
-    writeFooter(dfile, inSize, optObjName);
+    // Output footer
+    setFormat->writeFooter(outFile, totalSize, optObjName);
 
-    /* Exit */
-    fclose(sfile);
-    fclose(dfile);
+exit:
+    // Cleanup
+    if (inFile != NULL)
+        fclose(inFile);
+
+    if (outFile != NULL)
+        fclose(outFile);
+
+    dmFree(dataBuf);
 
     return 0;
 }