view gfxconv.c @ 435:e4a3f183e463

Modularize some more.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 03 Nov 2012 16:08:30 +0200
parents b583a682f12d
children 2a4de5fe4003
line wrap: on
line source

/*
 * gfxconv - Convert various graphics formats
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 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 "dmfile.h"
#include "dmmutex.h"
#include "libgfx.h"
#include "lib64gfx.h"

//#define UNFINISHED 1

enum
{
    FFMT_AUTO = 0,

    FFMT_ASCII,
    FFMT_ANSI,
    FFMT_IMAGE,

    FFMT_CHAR,
    FFMT_SPRITE,
    FFMT_BITMAP,

    FFMT_LAST
};


typedef struct
{
    char *name;
    char *fext;
    BOOL in, out;
    int format;
    int subformat;
} DMConvFormat;


static DMConvFormat convFormatList[] =
{
    {
        "ASCII text", "asc", FALSE, TRUE,
        FFMT_ASCII  , 0,
    },
    {
        "ANSI colored text", "ansi", FALSE, TRUE,
        FFMT_ANSI   , 0,
    },
    {
        "PNG image file", "png", FALSE, TRUE,
        FFMT_IMAGE  , IMGFMT_PNG,
    },
    {
        "PPM image file", "ppm", FALSE, TRUE,
        FFMT_IMAGE  , IMGFMT_PPM,
    },
    {
        "PCX image file", "pcx", TRUE, TRUE,
        FFMT_IMAGE  , IMGFMT_PCX,
    },
    {
        "IFFMaster RAW image file", "araw", FALSE, TRUE,
        FFMT_IMAGE  , IMGFMT_ARAW,
    },

    {
        "C64 bitmap image file", NULL, TRUE, FALSE,
        FFMT_BITMAP , 0,
    },

    {
        "C64 character/font data", "chr", TRUE, FALSE,
        FFMT_CHAR   , 0
    },
    {
        "C64 sprite data", "spr", TRUE, FALSE,
        FFMT_SPRITE , 0
    },
};

static const int nconvFormatList = sizeof(convFormatList) / sizeof(convFormatList[0]);


#define ASC_NBITS    8
#define ASC_NCOLORS  4
static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#";


char    *optInFilename = NULL,
        *optOutFilename = NULL;
int     optInFormat = FFMT_AUTO,
        optOutFormat = FFMT_ASCII,
        optInSubFormat = IMGFMT_PNG,
        optOutSubFormat = IMGFMT_PNG,
        optItemCount = -1,
        optPlanedWidth = 1,
        optForcedFormat = -1;
int     optInSkip = 0;
BOOL    optInMulticolor = FALSE,
        optSequential = FALSE;
int     optColors[C64_MAX_COLORS];

DMImageSpec optSpec =
{
    .scale = 2,
    .nplanes = 4,
    .interleave = FALSE,
    .paletted = FALSE,
    .format = 0,
};

static DMOptArg optList[] =
{
    {  0, '?', "help",         "Show this help", OPT_NONE },
    { 15, 'v', "verbose",      "Increase verbosity", OPT_NONE },
    {  3, 'o', "output",       "Output filename", OPT_ARGREQ },
    {  1, 'i', "informat",     "Set input format ([s]prite, [c]har, [b]itmap, [i]mage)", OPT_ARGREQ },
    {  2, 'm', "multicolor",   "Input is multicolor", OPT_NONE },
    {  4, 's', "skip",         "Skip bytes in input", OPT_ARGREQ },
    {  5, 'f', "format",       "Output format (see list below)", OPT_ARGREQ },
    {  8, 'q', "sequential",   "Output sequential files (image output only)", OPT_NONE },
    {  6, 'c', "colormap",     "Color mappings (see below for information)", OPT_ARGREQ },
    {  7, 'n', "numitems",     "How many 'items' to view (default: all)", OPT_ARGREQ },
    {  9, 'S', "scale",        "Scale output by x (image output only)", OPT_ARGREQ },
    { 10, 'b', "bformat",      "Force input bitmap format (see below)", OPT_ARGREQ },
    { 11, 'w', "width",        "Item width (number of items per row, min 1)", OPT_ARGREQ },
    { 12, 'P', "paletted",     "Use indexed/paletted output (png, pcx output only)", OPT_NONE },
    { 13, 'B', "bplanes",      "Bits per pixel OR # of bitplanes (certain output formats)", OPT_ARGREQ },
    { 14, 'I', "interleave",   "Interleave scanlines (default: output whole planes)", OPT_NONE },
};

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


void argShowHelp()
{
    int i;

    dmPrintBanner(stdout, dmProgName, "[options] <input file>");
    dmArgsPrintHelp(stdout, optList, optListN);

    printf("\n"
    "Available input/output formats:\n"
    "  EXT | I | O | Description\n"
    "------+---+---+--------------------------------\n"
    );
    
    for (i = 0; i < nconvFormatList; i++)
    {
        DMConvFormat *fmt = &convFormatList[i];
        printf("%-5s | %c | %c | %s\n",
            fmt->fext ? fmt->fext : "",
            fmt->in ? 'X' : ' ',
            fmt->out ? 'X' : ' ',
            fmt->name);
    }

    printf("\nAvailable bitmap formats:\n");
    for (i = 0; i < ndmC64ImageFormats; i++)
    {
        DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
        printf("%3d | %-5s | %-15s | %s\n",
            i, fmt->extension,
            dmC64ImageTypeNames[fmt->type],
            fmt->name);
    }

    printf(
    "\n"
    "Color map definitions are used for ANSI, PCX, PPM and PNG output, to declare what\n"
    "output colors of the C64 palette are used for each single color/multi color\n"
    "bit-combination. For example, if the input is multi color sprite or char,\n"
    "you can define colors like: -c 0,8,3,15 .. for single color: -c 0,1\n"
    "The numbers are palette indexes, and the order is for bit(pair)-values\n"
    "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n"
    "special color that can be used for transparency.\n"
    );
}


int dmGetConvFormat(int format, int subformat)
{
    int i;
    for (i = 0; i < nconvFormatList; i++)
    {
        DMConvFormat *fmt = &convFormatList[i];
        if (fmt->format == format &&
            fmt->subformat == subformat)
            return i;
    }
    return -1;
}


BOOL dmGetFormatByExt(const char *fext, int *format, int *subformat)
{
    int i;
    if (fext == NULL)
        return FALSE;

    for (i = 0; i < nconvFormatList; i++)
    {
        DMConvFormat *fmt = &convFormatList[i];
        if (fmt->fext != NULL &&
            strcasecmp(fext, fmt->fext) == 0)
        {
            *format = fmt->format;
            *subformat = fmt->subformat;
            return TRUE;
        }
    }
    return FALSE;
}


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

        case 15:
            dmVerbosity++;
            break;

        case 1:
            switch (tolower(optArg[0]))
            {
                case 's':
                    optInFormat = FFMT_SPRITE;
                    break;
                case 'c':
                    optInFormat = FFMT_CHAR;
                    break;
                case 'b':
                    optInFormat = FFMT_BITMAP;
                    break;
                case 'i':
                    optInFormat = FFMT_IMAGE;
                    break;
                default:
                    dmError("Invalid input format '%s'.\n", optArg);
                    return FALSE;
            }
            break;
        
        case 2:
            optInMulticolor = TRUE;
            break;

        case 3:
            optOutFilename = optArg;
            break;
        
        case 4:
            if (!dmGetIntVal(optArg, &optInSkip))
            {
                dmError("Invalid skip value argument '%s'.\n", optArg);
                return FALSE;
            }
            break;

        case 5:
            if (!dmGetFormatByExt(optArg, &optOutFormat, &optOutSubFormat))
            {
                dmError("Invalid output format '%s'.\n", optArg);
                return FALSE;
            }
            break;

        case 6:
            {
                int index = 0, tmp;
                char *s, *p = optArg;

                while (index < C64_MAX_COLORS && *p != 0 && (s = strchr(p, ':')) != NULL)
                {
                    *s = 0;
                    if (sscanf(p, "%d", &tmp) == 1)
                        optColors[index++] = tmp;
                    p = s + 1;
                }
                
                if (*p && index < C64_MAX_COLORS)
                {
                    if (sscanf(p, "%d", &tmp) == 1)
                        optColors[index++] = tmp;
                }
                
                dmMsg(1, "Set color table: ");
                for (tmp = 0; tmp < index; tmp++)
                {
                    dmPrint(1, "[%d:%d]%s",
                        tmp, optColors[tmp],
                        (tmp < index - 1) ? ", " : "");
                }
                dmPrint(1, "\n");
            }
            break;

        case 7:
            if (sscanf(optArg, "%d", &optItemCount) != 1)
            {
                dmError("Invalid count value argument '%s'.\n", optArg);
                return FALSE;
            }
            break;

        case 8:
            optSequential = TRUE;
            break;

        case 9:
            {
                int tmp = atoi(optArg);
                if (tmp < 1 || tmp > 50)
                {
                    dmError("Invalid scale value '%s'.\n", optArg);
                    return FALSE;
                }
                optSpec.scale = tmp;
            }
            break;

        case 11:
            {
                int tmp = atoi(optArg);
                if (tmp < 1 || tmp > 512)
                {
                    dmError("Invalid width value '%s'.\n", optArg);
                    return FALSE;
                }
                optPlanedWidth = tmp;
            }
            break;

        case 12:
            optSpec.paletted = TRUE;
            break;

        case 13:
            {
                int tmp = atoi(optArg);
                if (tmp < 1 || tmp > 8)
                {
                    dmError("Invalid bitplanes/bpp value '%s'.\n", optArg);
                    return FALSE;
                }
                optSpec.nplanes = tmp;
            }
            break;

        case 14:
            optSpec.interleave = TRUE;
            break;

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

    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    if (!optInFilename)
        optInFilename = currArg;
    else
    {
        dmError("Source filename already specified, extraneous argument '%s'.\n",
             currArg);
        return FALSE;
    }

    return TRUE;
}


void dmPrintByte(FILE *out, int byte, int format, BOOL multicolor)
{
    int i;
    
    if (multicolor)
    {
        for (i = ASC_NBITS; i; i -= 2)
        {
            int val = (byte & (3ULL << (i - 2))) >> (i - 2);
            char ch;
            switch (format)
            {
                case FFMT_ASCII:
                    ch = dmASCIIPalette[val];
                    fprintf(out, "%c%c", ch, ch);
                    break;
                case FFMT_ANSI:
                    fprintf(out, "%c[0;%d;%dm##%c[0m",
                        0x1b,
                        1,
                        31 + optColors[val],
                        0x1b);
                    break;
            }
        }
    }
    else
    {
        for (i = ASC_NBITS; i; i--)
        {
            int val = (byte & (1ULL << (i - 1))) >> (i - 1);
            char ch;
            switch (format)
            {
                case FFMT_ASCII:
                    ch = val ? '#' : '.';
                    fputc(ch, out);
                    break;
                case FFMT_ANSI:
                    fprintf(out, "%c[0;%d;%dm %c[0m",
                        0x1b,
                        1,
                        31 + optColors[val],
                        0x1b);
                    break;
            }
        }
    }
}


void dmDumpCharASCII(FILE *outFile, const Uint8 *buf, int *offs, int format, BOOL multicolor)
{
    int yc;

    for (yc = 0; yc < C64_CHR_HEIGHT; yc++)
    {
        fprintf(outFile, "%04x : ", *offs);
        dmPrintByte(outFile, buf[yc], format, multicolor);
        fprintf(outFile, "\n");
        (*offs)++;
    }
}


void dmDumpSpriteASCII(FILE *outFile, const Uint8 *buf, int *offs, int format, BOOL multicolor)
{
    int bufOffs, xc, yc;

    for (bufOffs = yc = 0; yc < C64_SPR_HEIGHT; yc++)
    {
        fprintf(outFile, "%04x : ", *offs);
        for (xc = 0; xc < C64_SPR_WIDTH; xc++)
        {
            dmPrintByte(outFile, buf[bufOffs], format, multicolor);
            fprintf(outFile, " ");
            bufOffs++;
            (*offs)++;
        }
        fprintf(outFile, "\n");
    }
    (*offs)++;
}


#ifdef UNFINISHED
int dmConvertBMP2(DMImage *screen, const DM64Image *img)
{
    int yc;
    Uint8 *dp = screen->data;
    
    for (yc = 0; yc < screen->height; yc++)
    {
        Uint8 *d = dp;
        const int y = yc / 8, yb = yc & 7;
        const int scroffsy = y * C64_SCR_CH_WIDTH;
        const int bmoffsy = y * C64_SCR_WIDTH;
        int xc;

        for (xc = 0; xc < screen->width / 2; xc++)
        {
            const int x = xc / 4;
            const int scroffs = scroffsy + x;
            const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
            const int v = 6 - ((xc * 2) & 6);
            Uint8 c;

            switch ((b >> v) & 3)
            {
                case 0: c = img->bgcolor; break;
                case 1: c = img->screen[0][scroffs] >> 4; break;
                case 2: c = img->screen[0][scroffs] & 15; break;
                case 3: c = img->color[0][scroffs] & 15; break;
            }
            
            *d++ = c;
            *d++ = c;
        }

        dp += screen->pitch;
    }
    
    return 0;
}
#endif


int dmWriteImage(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info)
{
    if (info)
    {
        dmMsg(1, "Outputting %s image %d x %d -> %d x %d [%d]\n",
            dmImageFormatList[iformat],
            image->width, image->height,
            image->width * spec->scale, image->height * spec->scale,
            spec->scale);
    }

    switch (iformat)
    {
#ifdef DM_USE_LIBPNG
        case IMGFMT_PNG:
            if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "32bit RGBA");
            spec->format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA;
            return dmWritePNGImage(filename, image, spec);
#endif

        case IMGFMT_PPM:
            if (info) dmMsg(2, "24bit RGB output.\n");
            spec->format = DM_IFMT_RGB;
            return dmWritePPMImage(filename, image, spec);

        case IMGFMT_PCX:
            if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "24bit RGB");
            return dmWritePCXImage(filename, image, spec);

        case IMGFMT_ARAW:
            {
                int res;
                char *palFilename = dm_strdup_printf("%s.pal", filename);
                res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << optSpec.nplanes);
                dmFree(palFilename);
                if (res != DMERR_OK)
                    return res;

                if (info) dmMsg(2, "%d bitplanes, %s interleave.\n", spec->nplanes, spec->interleave ? "with" : "without");
                return dmWriteIFFMasterRAWImage(filename, image, spec);
            }

        default:
            return FALSE;
    }
}


int dmDumpSpritesAndChars(FILE *inFile)
{
    int dataOffs, itemCount, outWidth, outWidthPX, outHeight;
    size_t bufSize;
    Uint8 *bufData;

    switch (optInFormat)
    {
        case FFMT_CHAR:
            bufSize = C64_CHR_SIZE;
            outWidth = C64_CHR_WIDTH;
            outWidthPX = C64_CHR_WIDTH_PX;
            outHeight = C64_CHR_HEIGHT;
            break;

        case FFMT_SPRITE:
            bufSize = C64_SPR_SIZE;
            outWidth = C64_SPR_WIDTH;
            outWidthPX = C64_SPR_WIDTH_PX;
            outHeight = C64_SPR_HEIGHT;
            break;

        default:
            dmError("Invalid input format %d, internal error.\n", optInFormat);
            return -1;
    }

    if ((bufData = dmMalloc(bufSize)) == NULL)
    {
        dmError("Could not allocate temporary buffer of %d bytes.\n", bufSize);
        return -2;
    }


    dataOffs = optInSkip;
    itemCount = 0;

    if (optOutFormat == FFMT_ANSI || optOutFormat == FFMT_ASCII)
    {
        BOOL error = FALSE;
        FILE *outFile;

        if (optOutFilename == NULL)
            outFile = stdout;
        else
        if ((outFile = fopen(optOutFilename, "w")) == NULL)
        {
            int res = errno;
            dmError("Error opening output file '%s'. (%s)\n",
                  optOutFilename, strerror(res));
            goto error;
        }

        while (!feof(inFile) && !error && (optItemCount < 0 || itemCount < optItemCount))
        {
            memset(bufData, 0, bufSize);

            if (fread(bufData, 1, bufSize, inFile) != bufSize)
            {
                dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n",
                    bufSize, dataOffs);
                error = TRUE;
            }
            
            fprintf(outFile, "---- : -------------- #%d\n", itemCount);

            switch (optInFormat)
            {
                case FFMT_CHAR:
                    dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor);
                    break;
                case FFMT_SPRITE:
                    dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor);
                    break;
            }
            itemCount++;
        }

        fclose(outFile);
    }
    else
    if (optOutFormat == FFMT_IMAGE)
    {
        DMImage *outImage = NULL;
        char *outFilename = NULL;
        int outX = 0, outY = 0, err;

        if (optSequential)
        {
            if (optOutFilename == NULL)
            {
                dmError("Sequential image output requires filename template.\n");
                goto error;
            }

            outImage = dmImageAlloc(outWidthPX, outHeight);
            dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n",
                optItemCount,
                outImage->width, outImage->height,
                outImage->width * optSpec.scale, outImage->height * optSpec.scale);
        }
        else
        {
            int outIWidth, outIHeight;
            if (optItemCount <= 0)
            {
                dmError("Single-image output requires count to be set (-n).\n");
                goto error;
            }
            
            outIWidth = optPlanedWidth;
            outIHeight = (optItemCount / optPlanedWidth);
            if (optItemCount % optPlanedWidth)
                outIHeight++;
            
            outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight);
        }

        outImage->constpal = TRUE;
        outImage->pal      = dmC64Palette;
        outImage->ncolors  = C64_NCOLORS;
        outImage->ctrans   = 255;
        
        while (!feof(inFile) && (optItemCount < 0 || itemCount < optItemCount))
        {
            memset(bufData, 0, bufSize);

            if (fread(bufData, 1, bufSize, inFile) != bufSize)
            {
                dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n",
                    bufSize, dataOffs);
                break;
            }

            if ((err = dmC64ConvertCSData(outImage, outX * outWidthPX, outY * outHeight,
                bufData, outWidth, outHeight, optInMulticolor, optColors)) != DMERR_OK)
            {
                dmError("Internal error in conversion of raw data to bitmap: %d.\n", err);
                break;
            }

            if (optSequential)
            {
                outFilename = dm_strdup_printf("%s%04d.%s", optOutFilename, itemCount, convFormatList[optOutFormat].fext);
                if (outFilename == NULL)
                {
                    dmError("Could not allocate memory for filename template?\n");
                    goto error;
                }
                
                dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
                dmFree(outFilename);
            }
            else
            {
                if (++outX >= optPlanedWidth)
                {
                    outX = 0;
                    outY++;
                }
            }
            
            itemCount++;
        }

        if (!optSequential)
        {
            dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
        }
        
        dmImageFree(outImage);
    }

    dmFree(bufData);
    return 0;

error:
    dmFree(bufData);
    return -1;
}


int main(int argc, char *argv[])
{
    FILE *inFile;
    DMC64ImageFormat *cfmt;
    DMC64Image cimage;
    Uint8 *dataBuf = NULL;
    size_t dataSize;
    int i;

    // Default colors
    for (i = 0; i < C64_MAX_COLORS; i++)
        optColors[i] = i + 1;

    // Initialize and parse commandline
    dmInitProg("gfxconv", "Simple c64 graphics converter", "0.4", NULL, NULL);

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

#ifndef DM_USE_LIBPNG
    if (optOutFormat == IMGFMT_PNG)
    {
        dmError("PNG output format support not compiled in, sorry.\n");
        goto error;
    }
#endif

    // Determine input format, if not specified'
    if (optInFormat == FFMT_AUTO && optInFilename != NULL)
    {
        char *dext = strrchr(optInFilename, '.');
        if (dext)
        {
            dmGetFormatByExt(dext + 1, &optInFormat, &optInSubFormat);
        }
    }

    if (optInFilename == NULL)
    {
        if (optInFormat == FFMT_AUTO)
        {
            dmError("Standard input cannot be used without specifying input format.\n");
            goto error;
        }
        inFile = stdin;
    }
    else
    if ((inFile = fopen(optInFilename, "rb")) == NULL)
    {
        int res = errno;
        dmError("Error opening input file '%s'. (%s)\n",
              optInFilename, strerror(res));
        goto error;
    }

    if (dmReadDataFile(inFile, NULL, &dataBuf, &dataSize) != 0)
        goto error;

    if (optInFormat == FFMT_AUTO || optInFormat == FFMT_BITMAP)
    {
        // Probe for format
        DMC64ImageFormat *forced = NULL;
        int res;

        if (optForcedFormat >= 0)
        {
            forced = &dmC64ImageFormats[optForcedFormat];
            dmMsg(0,"Forced %s format image, type %d, %s\n",
                forced->name, forced->type, forced->extension);
        }

        res = dmC64DecodeBMP(&cimage, dataBuf, dataSize, optInSkip, optInSkip + 2, &cfmt, forced);
        if (forced == NULL && cfmt != NULL)
        {
            dmMsg(1,"Probed %s format image, type %d, %s\n",
                cfmt->name, cfmt->type, cfmt->extension);
        }
        
        if (res == 0)
            optInFormat = FFMT_BITMAP;
    }

    if (optInFormat == FFMT_AUTO)
    {
        DMImageFormat *ifmt = NULL;
        int index;
        if (dmImageProbeGeneric(dataBuf + optInSkip, dataSize - optInSkip, &ifmt, &index) > 0)
        {
            optInFormat = FFMT_IMAGE;
            optInSubFormat = index;
        }
    }

    if (optInFormat == FFMT_AUTO)
    {
        dmError("No input format specified, and could not be determined automatically.\n");
        exit(1);
    }

    // Skip, if needed
    if (fseek(inFile, optInSkip, SEEK_SET) != 0)
    {
        int res = errno;
        dmError("Could not seek to file position %d (0x%x): %s\n",
            optInSkip, optInSkip, strerror(res));
        goto error;
    }
    
    int inFormat = dmGetConvFormat(optInFormat, optInSubFormat),
        outFormat = dmGetConvFormat(optOutFormat, optOutSubFormat);
    
    if (inFormat != -1 && outFormat != -1)
    {
        char *inFmtName = convFormatList[inFormat].name,
             *inFmtExt = convFormatList[inFormat].fext,
             *outFmtName = convFormatList[outFormat].name,
             *outFmtExt = convFormatList[outFormat].fext;

        if (optInFormat == FFMT_BITMAP)
            inFmtExt = cfmt->name;

        dmMsg(1, "Attempting conversion %s (%s) -> %s (%s)\n",
            inFmtName, inFmtExt, outFmtName, outFmtExt);
    }

    switch (optInFormat)
    {
        case FFMT_SPRITE:
        case FFMT_CHAR:
            dmDumpSpritesAndChars(inFile);
            break;
        
        case FFMT_BITMAP:
            {
                DMImage *outImage = NULL;
                int res = DMERR_OK;

                if (optOutFilename == NULL)
                {
                    dmError("Output filename not set, required for image formats.\n");
                    goto error;
                }

                switch (optOutFormat)
                {
                    case FFMT_IMAGE:
                        if ((outImage = dmImageAlloc(C64_SCR_WIDTH, C64_SCR_HEIGHT)) == NULL)
                        {
                            dmError("Could not allocate output image surface %d x %d.\n",
                                C64_SCR_WIDTH, C64_SCR_HEIGHT);
                            goto error;
                        }
                        
                        outImage->pal      = (DMColor *) &dmC64Palette;
                        outImage->ncolors  = C64_NCOLORS;
                        outImage->constpal = TRUE;
                        
                        if (cfmt->convert != NULL)
                            res = cfmt->convert(outImage, &cimage);
                        else
                            res = dmC64ConvertGenericBMP2Image(outImage, &cimage);

                        if (res != DMERR_OK || outImage == NULL)
                        {
                            dmError("Error in bitmap to image conversion.\n");
                            goto error;
                        }

                        res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
                        break;

                    default:
                        dmError("Unsupported output format for bitmap/image conversion.\n");
                        break;
                }
                
                dmImageFree(outImage);
            }
            break;

        case FFMT_IMAGE:
            {
                DMImage *outImage;
                int res = DMERR_OK;

                if (optOutFilename == NULL)
                {
                    dmError("Output filename not set, required for image formats.\n");
                    goto error;
                }

                // Read input
                switch (optInSubFormat)
                {
                    case IMGFMT_PCX: res = dmReadPCXImageFILE(inFile, &outImage); break;
//                    case IMGFMT_PNG: res = dmReadPNGImageFILE(inFile, &outImage); break;
//                    case IMGFMT_ARAW: res = dmReadARAWImageFILE(inFile, &outImage, optSpec.nplanes); break;
                    default:
                        dmError("Unsupported input image format for bitmap/image conversion.\n");
                        break;
                    
                }

                if (res != DMERR_OK || outImage == NULL)
                    break;
                
                switch (optOutFormat)
                {
                    case FFMT_IMAGE:
                        res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
                        break;

                    default:
                        dmError("Unsupported output format for bitmap/image conversion.\n");
                        break;
                }
                
                dmImageFree(outImage);
            }
            break;
    }

    fclose(inFile);

    exit(0);
    return 0;

error:
    return -3;
    exit(3);
}