diff tools/lib64fmts.c @ 1503:c7b9ef56319b

Factor all the c64 file format specific things into lib64fmt.c
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 11 May 2018 07:41:55 +0300
parents
children 3265175b24d2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/lib64fmts.c	Fri May 11 07:41:55 2018 +0300
@@ -0,0 +1,838 @@
+/*
+ * Functions for reading and converting various restricted
+ * C64/etc and/or indexed/paletted graphics formats.
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2012-2018 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "lib64gfx.h"
+
+
+// Based on Pepto's palette, stolen from VICE
+DMColor dmDefaultC64Palette[C64_NCOLORS] =
+{
+    { 0x00, 0x00, 0x00, 0xff },
+    { 0xFF, 0xFF, 0xFF, 0xff },
+    { 0x68, 0x37, 0x2B, 0xff },
+    { 0x70, 0xA4, 0xB2, 0xff },
+    { 0x6F, 0x3D, 0x86, 0xff },
+    { 0x58, 0x8D, 0x43, 0xff },
+    { 0x35, 0x28, 0x79, 0xff },
+    { 0xB8, 0xC7, 0x6F, 0xff },
+    { 0x6F, 0x4F, 0x25, 0xff },
+    { 0x43, 0x39, 0x00, 0xff },
+    { 0x9A, 0x67, 0x59, 0xff },
+    { 0x44, 0x44, 0x44, 0xff },
+    { 0x6C, 0x6C, 0x6C, 0xff },
+    { 0x9A, 0xD2, 0x84, 0xff },
+    { 0x6C, 0x5E, 0xB5, 0xff },
+    { 0x95, 0x95, 0x95, 0xff },
+};
+
+
+static int fmtProbeDrazPaint20Packed(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    const char *ident = (const char *) buf + 2;
+
+    if (len > 22 &&
+        dmCompareAddr16(buf, 0, fmt->addr) &&
+        strncmp(ident, "DRAZPAINT ", 10) == 0 &&
+        ident[11] == '.' && (
+        (ident[10] == '1' && ident[12] == '4') ||
+        (ident[10] == '2' && ident[12] == '0')
+        ))
+        return DM_PROBE_SCORE_MAX;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtDecodeDrazPaintPacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    int res;
+    DMGrowBuf mem;
+
+    if ((res = dmDecodeGenericRLEAlloc(&mem, buf + 0x0e, buf + len, *(buf + 0x0d), 0, 0, DM_RLE_MARKER)) != DMERR_OK)
+        goto out;
+
+    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+
+out:
+    dmGrowBufFree(&mem);
+    return res;
+}
+
+
+static int fmtEncodeDrazPaintPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
+{
+    int res;
+    DMGrowBuf tmp;
+    Uint8 rleMarker;
+    const char *magicID = (fmt->type & D64_FMT_ILACE) ? "DRAZLACE! 1.0" : "DRAZPAINT 2.0";
+
+    // Encode the data to temp buffer
+    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
+        goto out;
+
+    // Analyze the data ..
+    dmGenericRLEAnalyze(&tmp, &rleMarker, DM_RLE_MARKER);
+    rleMarker = 0xff;
+
+    // Add the header bits
+    if (!dmGrowBufPut(buf, magicID, strlen(magicID)) ||
+        !dmGrowBufPutU8(buf, rleMarker))
+    {
+        res = DMERR_MALLOC;
+        goto out;
+    }
+
+    // And now RLE compress the data to the existing buffer
+    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len,
+        rleMarker, 3, 255, DM_RLE_MARKER);
+
+out:
+    dmGrowBufFree(&tmp);
+    return res;
+}
+
+
+static int fmtProbeDrazLace10Packed(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    if (len > 22 &&
+        dmCompareAddr16(buf, 0, fmt->addr) &&
+        strncmp((const char *) (buf + 2), "DRAZLACE! 1.0", 13) == 0)
+        return DM_PROBE_SCORE_MAX;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static BOOL fmtDrazLaceSetLaceType(DMC64Image *img, const DMC64EncDecOp *op,
+    const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    (void) len;
+    (void) fmt;
+    img->laceType = buf[op->offs] ? D64_ILACE_RES : D64_ILACE_COLOR;
+    return TRUE;
+}
+
+
+static int fmtProbeGunPaint(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    if (len > 0x400 &&
+        dmCompareAddr16(buf, 0, fmt->addr) &&
+        strncmp((const char *) (buf + 0x3ea), "GUNPAINT (JZ) ", 14) == 0)
+        return DM_PROBE_SCORE_MAX;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtProbeAmicaPaintPacked(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    size_t i, n;
+
+    if (len < 2048 || !dmCompareAddr16(buf, 0, fmt->addr))
+        return DM_PROBE_SCORE_FALSE;
+
+    // Interpaint Hi-Res gives a false positive
+    // as do some GunPaint images ..
+    if (len == 9002 || fmtProbeGunPaint(buf, len, fmt) > DM_PROBE_SCORE_GOOD)
+        return DM_PROBE_SCORE_FALSE;
+
+    for (n = 0, i = 2; i < len; i++)
+        if (buf[i] == 0xC2) n++;
+
+    if (n > 50)
+        return DM_PROBE_SCORE_GOOD;
+    if (n > 25)
+        return DM_PROBE_SCORE_AVG;
+    if (n > 10)
+        return DM_PROBE_SCORE_MAYBE;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    int res;
+    DMGrowBuf mem, tmp;
+
+    // Amica Paint apparently is broken and stores one byte less than it should
+    // so we need to do some crappy buffer expansion here ..
+    if ((res = dmGrowBufAlloc(&tmp, len + 4, 4)) != DMERR_OK)
+        return res;
+
+    tmp.len = len;
+    memcpy(tmp.data, buf, len);
+    tmp.data[tmp.len++] = 0;
+
+    // Now do an RLE decode on the enlarged buffer
+    if ((res = dmDecodeGenericRLE(&mem, tmp.data, tmp.data + tmp.len, 0xC2, 0, 0, DM_RLE_MARKER)) != DMERR_OK)
+        goto out;
+
+    // And finally decode to bitmap struct
+    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+
+out:
+    dmGrowBufFree(&tmp);
+    dmGrowBufFree(&mem);
+    return res;
+}
+
+
+static int fmtProbeFLIDesigner(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    if (len == fmt->size &&
+        (dmCompareAddr16(buf, 0, 0x3c00) || dmCompareAddr16(buf, 0, 0x3ff0)))
+        return DM_PROBE_SCORE_MAX;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static BOOL fmtTruePaintSetLaceType(DMC64Image *img, const DMC64EncDecOp *op,
+    const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    (void) op;
+    (void) buf;
+    (void) len;
+    (void) fmt;
+    img->laceType = D64_ILACE_RES;
+    return TRUE;
+}
+
+
+static Uint8 fmtGetPixelTruePaint(
+    const DMC64Image *img, const int bmoffs, const int scroffs,
+    const int vshift, const int vbitmap, const int raster)
+{
+    (void) raster;
+    return dmC64GetGenericMCPixel(img, bmoffs, scroffs, vshift, 0, vbitmap, 0);
+}
+
+
+#define XX2_MIN_SIZE 4000
+
+static int fmtProbeFormatXX2(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    if (len >= XX2_MIN_SIZE && len <= XX2_MIN_SIZE + 8 &&
+        dmCompareAddr16(buf, 0, fmt->addr))
+        return DM_PROBE_SCORE_MAYBE;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtDecodeFormatXX2(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    int res;
+
+    // If there is only data for less than XX2_MIN_SIZE bytes,
+    // allocate a buffer of that size and copy data there.
+    // Otherwise allocate len bytes.
+    size_t nlen = len < XX2_MIN_SIZE ? XX2_MIN_SIZE : len;
+    Uint8 *mem = dmMalloc0(nlen);
+    if (mem == NULL)
+        return DMERR_MALLOC;
+
+    memcpy(mem, buf, len);
+    res = dmC64DecodeGenericBMP(img, mem, nlen, fmt);
+
+    dmFree(mem);
+    return res;
+}
+
+
+#define FUNPAINT2_HEADER_SIZE (0x10)
+static const char *fmtFunPaint2MagicID = "FUNPAINT (MT) ";
+
+
+static BOOL fmtProbeFunPaint2Header(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    return
+        len > 30 &&
+        dmCompareAddr16(buf, 0, fmt->addr) &&
+        strncmp((const char *) (buf + 2), fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) == 0;
+}
+
+
+static int fmtProbeFunPaint2Unpacked(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    if (fmtProbeFunPaint2Header(buf, len, fmt) &&
+        buf[2 + 14] == 0)
+        return DM_PROBE_SCORE_MAX;
+    else
+        return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtProbeFunPaint2Packed(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    if (fmtProbeFunPaint2Header(buf, len, fmt) &&
+        buf[2 + 14] != 0)
+        return DM_PROBE_SCORE_MAX;
+    else
+        return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtDecodeFunPaint2Unpacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    return dmC64DecodeGenericBMP(img, buf + FUNPAINT2_HEADER_SIZE, len - FUNPAINT2_HEADER_SIZE, fmt);
+}
+
+
+static int fmtDecodeFunPaint2Packed(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    int res;
+    DMGrowBuf mem;
+    dmGrowBufInit(&mem);
+
+    if ((res = dmDecodeGenericRLE(&mem, buf + FUNPAINT2_HEADER_SIZE, buf + len, *(buf + 15), 0, 0, DM_RLE_MARKER)) != DMERR_OK)
+        goto out;
+
+    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+
+out:
+    dmGrowBufFree(&mem);
+    return res;
+}
+
+
+static int fmtEncodeFunPaint2Unpacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
+{
+    // Add the header bits
+    if (!dmGrowBufPut(buf, fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) ||
+        !dmGrowBufPutU8(buf, 0))
+        return DMERR_MALLOC;
+
+    return dmC64EncodeGenericBMP(FALSE, buf, img, fmt);
+}
+
+
+static int fmtEncodeFunPaint2Packed(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
+{
+    int res;
+    DMGrowBuf tmp;
+    Uint8 rleMarker;
+
+    // Encode the data to temp buffer
+    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
+        goto out;
+
+    // Analyze the data ..
+    dmGenericRLEAnalyze(&tmp, &rleMarker, DM_RLE_MARKER);
+    rleMarker = 0xff;
+
+    // Add the header bits
+    if (!dmGrowBufPut(buf, fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) ||
+        !dmGrowBufPutU8(buf, rleMarker))
+    {
+        res = DMERR_MALLOC;
+        goto out;
+    }
+
+    // And now RLE compress the data to the existing buffer
+    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len,
+        rleMarker, 3, 255, DM_RLE_MARKER);
+
+out:
+    dmGrowBufFree(&tmp);
+    return res;
+}
+
+
+static Uint8 fmtGetPixelFunPaint2(
+    const DMC64Image *img, const int bmoffs, const int scroffs,
+    const int vshift, const int vbitmap, const int raster)
+{
+    const int vbank = (raster & 7) + (vbitmap * 8);
+    int vr, vb;
+    if (raster < 100)
+    {
+        vb = 0;
+        vr = raster;
+    }
+    else
+    {
+        vb = 0;
+        vr = raster - 100;
+    }
+
+    switch ((img->bitmap[vbitmap][bmoffs] >> vshift) & 3)
+    {
+        case  0: return img->extraData[vb][vr] & 15; break;
+        case  1: return img->screen[vbank][scroffs] >> 4; break;
+        case  2: return img->screen[vbank][scroffs] & 15; break;
+        default: return img->color[0][scroffs] & 15; break;
+    }
+}
+
+
+static Uint8 fmtGetPixelGunPaint(
+    const DMC64Image *img, const int bmoffs, const int scroffs,
+    const int vshift, const int vbitmap, const int raster)
+{
+    const int vbank = (raster & 7);// + (vbitmap * 8);
+    int vr, vb;
+    if (raster < 177)
+    {
+        vb = 0;
+        vr = raster;
+    }
+    else
+    {
+        vb = 0;
+        vr = raster - 177;
+    }
+
+    switch ((img->bitmap[vbitmap][bmoffs] >> vshift) & 3)
+    {
+        case  0: return img->extraData[vb][vr] & 15; break;
+        case  1: return img->screen[vbank][scroffs] >> 4; break;
+        case  2: return img->screen[vbank][scroffs] & 15; break;
+        default: return img->color[0][scroffs] & 15; break;
+    }
+}
+
+
+static Uint8 fmtGetPixelBMFLI(
+    const DMC64Image *img, const int bmoffs, const int scroffs,
+    const int vshift, const int vbitmap, const int raster)
+{
+    const int vbank = raster & 7;
+    switch ((img->bitmap[vbitmap][bmoffs] >> vshift) & 3)
+    {
+        case  0: return img->extraData[0][raster]; break;
+        case  1: return img->screen[vbank][scroffs] >> 4; break;
+        case  2: return img->screen[vbank][scroffs] & 15; break;
+        default: return img->color[0][scroffs] & 15; break;
+    }
+}
+
+
+static Uint8 fmtGetPixelFLIDesigner(
+    const DMC64Image *img, const int bmoffs, const int scroffs,
+    const int vshift, const int vbitmap, const int raster)
+{
+    return dmC64GetGenericMCPixel(img, bmoffs, scroffs, vshift, raster & 7, vbitmap, 0);
+}
+
+
+static Uint8 fmtGetPixelCHFLI(
+    const DMC64Image *img, const int bmoffs, const int scroffs,
+    const int vshift, const int vbitmap, const int raster)
+{
+    const int vbank = raster & 7;
+
+    if ((img->bitmap[vbitmap][bmoffs] >> vshift) & 1)
+        return img->screen[vbank][scroffs] >> 4;
+    else
+        return img->screen[vbank][scroffs] & 15;
+}
+
+
+static int fmtEncodeStub(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
+{
+    (void) buf;
+    (void) img;
+    (void) fmt;
+
+    return dmError(DMERR_NOT_SUPPORTED,
+        "Encoding of '%s' format not supported.\n",
+        fmt->name);
+}
+
+
+//
+// Array with data for supported formats
+//
+#define DEF_SCREEN_RAM(start, oindex, bindex, osize) { DT_SCREEN_RAM, (start) + ((osize) * (oindex)), (bindex), 0, NULL, NULL }
+#define DEF_SCREEN_RAMS_8(start, sindex, osize) \
+    DEF_SCREEN_RAM((start), 0, (sindex + 0), (osize)), \
+    DEF_SCREEN_RAM((start), 1, (sindex + 1), (osize)), \
+    DEF_SCREEN_RAM((start), 2, (sindex + 2), (osize)), \
+    DEF_SCREEN_RAM((start), 3, (sindex + 3), (osize)), \
+    DEF_SCREEN_RAM((start), 4, (sindex + 4), (osize)), \
+    DEF_SCREEN_RAM((start), 5, (sindex + 5), (osize)), \
+    DEF_SCREEN_RAM((start), 6, (sindex + 6), (osize)), \
+    DEF_SCREEN_RAM((start), 7, (sindex + 7), (osize)),
+
+
+const DMC64ImageFormat dmC64ImageFormats[] =
+{
+    {
+        D64_FMT_MC, "d2p", "DrazPaint 1.4/2.0 (packed)", 0x5800, 0,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        fmtProbeDrazPaint20Packed,
+        fmtDecodeDrazPaintPacked, fmtEncodeDrazPaintPacked,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_COLOR_RAM,    0x0000, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0800, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x0400, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2740, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "drp", "DrazPaint (unpacked)", 0x5800, 10051,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_COLOR_RAM,    0x0000, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0800, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x0400, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2740, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_ILACE, "dlp", "DrazLace 1.0 (packed)", 0x5800, 0,
+        C64_SCR_WIDTH   , C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        fmtProbeDrazLace10Packed,
+        fmtDecodeDrazPaintPacked, fmtEncodeDrazPaintPacked,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_COLOR_RAM,    0x0000, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0800, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x0400, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2740, 0,  DC_BGCOL, NULL, NULL },
+            { DT_BITMAP,       0x2800, 1,  0, NULL, NULL },
+            { DT_DEC_FUNCTION, 0x2742, 0,  1, fmtDrazLaceSetLaceType, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_ILACE, "drl", "DrazLace 1.0 (unpacked)", 0x5800, 18242,
+        C64_SCR_WIDTH   , C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_COLOR_RAM,    0x0000, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0800, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x0400, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2740, 0,  DC_BGCOL, NULL, NULL },
+            { DT_BITMAP,       0x2800, 1,  0, NULL, NULL },
+            { DT_DEC_FUNCTION, 0x2742, 0,  1, fmtDrazLaceSetLaceType, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_ILACE, "mci", "Truepaint (unpacked)", 0x9c00, 19434,
+        C64_SCR_WIDTH   , C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        fmtGetPixelTruePaint,
+        {
+            { DT_SCREEN_RAM,   0x0000, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x03e8, 0,  DC_BGCOL, NULL, NULL },
+            { DT_BITMAP,       0x0400, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x2400, 1,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x4400, 1,  0, NULL, NULL },
+            { DT_COLOR_RAM,    0x4800, 0,  0, NULL, NULL },
+            { DT_DEC_FUNCTION, 0x0000, 0,  0, fmtTruePaintSetLaceType, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "kla", "Koala Paint (unpacked)", 0x6000, 10003,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_COLOR_RAM,    0x2328, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2710, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "ocp", "Advanced Art Studio (unpacked)", 0x2000, 10018,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_COLOR_RAM,    0x2338, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2329, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "ami", "Amica Paint (packed)", 0x4000, 0,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        fmtProbeAmicaPaintPacked,
+        fmtDecodeAmicaPaintPacked, fmtEncodeStub,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_COLOR_RAM,    0x2328, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2710, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "rpm", "Run Paint (unpacked)", 0x6000, 10006,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_COLOR_RAM,    0x2328, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2710, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_HIRES, "art", "Art Studio (unpacked)", 0x2000, 9009,
+        C64_SCR_WIDTH    , C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_HIRES, "iph", "Interpaint (unpacked)", 0x4000, 9002,
+        C64_SCR_WIDTH   , C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "ipc", "Interpaint MC (unpacked)", 0x4000, 10003,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x1f40, 0,  0, NULL, NULL },
+            { DT_COLOR_RAM,    0x2328, 0,  0, NULL, NULL },
+            { DT_COLOR_REG,    0x2710, 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_HIRES, "dd", "Doodle (unpacked)", 0x1c00, 9218,
+        C64_SCR_WIDTH   , C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_SCREEN_RAM,   0x0000, 0,  0, NULL, NULL },
+            { DT_BITMAP,       0x0400, 0,  0, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_FLI, "bml", "Blackmail FLI (unpacked)", 0x3b00, 17474,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        fmtGetPixelBMFLI,
+        {
+            { DT_EXTRA_DATA,   0x0000, 0,  200, NULL, NULL },
+            { DT_COLOR_RAM,    0x0100, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8( 0x0500, 0,  0x400)
+            { DT_BITMAP,       0x2500, 0,  0, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_FLI, "fli", "FLI Designer (unpacked)", 0, 17409,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        fmtProbeFLIDesigner,
+        NULL, NULL,
+        NULL, NULL,
+        fmtGetPixelFLIDesigner,
+        {
+            { DT_COLOR_RAM,    0x0000, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8( 0x0400, 0,  0x400)
+            { DT_BITMAP,       0x2400, 0,  0, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC, "xx1", "Unknown $2000 format (unpacked)", 0x2000, 10242,
+        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            { DT_SCREEN_RAM,   0x2000, 0,  0, NULL, NULL },
+            { DT_COLOR_RAM,    0x2400, 0,  0, NULL, NULL },
+            { DT_COLOR_SET,    0x00  , 0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+#define XX2_WIDTH_CH   40
+#define XX2_HEIGHT_CH  10
+#define XX2_SIZE       (XX2_WIDTH_CH * XX2_HEIGHT_CH)
+#define XX2_BSIZE      (XX2_SIZE * 8)
+
+    {
+        D64_FMT_MC, "xx2", "Unknown $2000 format (unpacked)", 0x2000, 0,
+        XX2_WIDTH_CH * 4, XX2_HEIGHT_CH * 8,
+        XX2_WIDTH_CH    , XX2_HEIGHT_CH,
+        fmtProbeFormatXX2,
+        fmtDecodeFormatXX2, NULL,
+        NULL, NULL,
+        NULL,
+        {
+            { DT_BITMAP,       0x0000, 0,  XX2_BSIZE, NULL, NULL },
+            { DT_COLOR_RAM,    XX2_BSIZE + XX2_SIZE, 0,  XX2_SIZE, NULL, NULL },
+            { DT_SCREEN_RAM,   XX2_BSIZE, 0,  XX2_SIZE, NULL, NULL },
+            { DT_COLOR_SET,    11,     0,  DC_BGCOL, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, "fp2", "FunPaint II (unpacked)", 0x3ff0, 33694,
+        C64_SCR_WIDTH, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        fmtProbeFunPaint2Unpacked,
+        fmtDecodeFunPaint2Unpacked, fmtEncodeFunPaint2Unpacked,
+        NULL, NULL,
+        fmtGetPixelFunPaint2,
+        {
+            DEF_SCREEN_RAMS_8( 0x0000, 0,  0x400)
+            { DT_BITMAP,       0x2000, 0,  0, NULL, NULL },
+            { DT_EXTRA_DATA,   0x3f40, 0,  100, NULL, NULL },
+            { DT_COLOR_RAM,    0x4000, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8( 0x43e8, 8,  0x400)
+            { DT_BITMAP,       0x63e8, 1,  0, NULL, NULL },
+            { DT_EXTRA_DATA,   0x8328, 1,  100, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, "fp2p", "FunPaint II (packed)", 0x3ff0, 0,
+        C64_SCR_WIDTH, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        fmtProbeFunPaint2Packed,
+        fmtDecodeFunPaint2Packed, fmtEncodeFunPaint2Packed,
+        NULL, NULL,
+        fmtGetPixelFunPaint2,
+        {
+            DEF_SCREEN_RAMS_8( 0x0000, 0,  0x400)
+            { DT_BITMAP,       0x2000, 0,  0, NULL, NULL },
+            { DT_EXTRA_DATA,   0x3f40, 0,  100, NULL, NULL },
+            { DT_COLOR_RAM,    0x4000, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8( 0x43e8, 8,  0x400)
+            { DT_BITMAP,       0x63e8, 1,  0, NULL, NULL },
+            { DT_EXTRA_DATA,   0x8328, 1,  100, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, "gun", "GunPaint (unpacked)", 0x4000, 0,
+        C64_SCR_WIDTH, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
+        fmtProbeGunPaint,
+        NULL, NULL,
+        NULL, NULL,
+        fmtGetPixelGunPaint,
+        {
+            DEF_SCREEN_RAMS_8( 0x0000, 0,  0x400)
+            { DT_BITMAP,       0x2000, 0,  0, NULL, NULL },
+            { DT_EXTRA_DATA,   0x3f4f, 0,  177, NULL, NULL },
+            { DT_COLOR_RAM,    0x4000, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8( 0x4400, 8,  0x400)
+            { DT_BITMAP,       0x6400, 1,  0, NULL, NULL },
+            { DT_EXTRA_DATA,   0x47e8, 1,  20, NULL, NULL },
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+
+    {
+        D64_FMT_HIRES | D64_FMT_FLI, "chi", "Crest Hires FLI Designer (unpacked)", 0x4000, 16386,
+        C64_SCR_WIDTH, 14 * 8,
+        C64_SCR_CH_WIDTH , 14,
+        NULL,
+        NULL, NULL,
+        NULL, NULL,
+        fmtGetPixelCHFLI,
+        {
+            { DT_BITMAP,       0x0000, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8( 0x2000, 0,  0x400)
+            { DT_LAST,         0,      0,  0, NULL, NULL },
+        }
+    },
+};
+
+const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]);
+