view tools/lib64fmts.c @ 1545:3b613fcbf3ff

Improve how format read/write capabilities are marked and shown.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 12 May 2018 21:01:46 +0300
parents 69fa95707e65
children fa4fa9a6e78f
line wrap: on
line source

/*
 * 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;
    DMCompParams cfg;

    cfg.type = DM_COMP_RLE_MARKER;
    cfg.rleMarker = *(buf + 0x0d);

    if ((res = dmDecodeGenericRLEAlloc(&mem, buf + 0x0e, buf + len, &cfg)) != 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;
    DMCompParams cfg;
    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, &cfg.rleMarker, DM_COMP_RLE_MARKER);

    // Add the header bits
    if (!dmGrowBufPut(buf, magicID, strlen(magicID)) ||
        !dmGrowBufPutU8(buf, cfg.rleMarker))
    {
        res = DMERR_MALLOC;
        goto out;
    }

    // And now RLE compress the data to the existing buffer
    cfg.type = DM_COMP_RLE_MARKER;
    cfg.rleMinCount = 3;
    cfg.rleMaxCount = 255;
    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);

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 fmtDrazLaceGetLaceType(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 BOOL fmtDrazLaceSetLaceType(const DMC64EncDecOp *op, DMGrowBuf *buf,
    const DMC64Image *img, const DMC64ImageFormat *fmt)
{
    (void) fmt;
    buf->data[op->offs] = (img->laceType == D64_ILACE_RES) ? 1 : 0;
    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;
    DMCompParams cfg;

    // 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;

    memcpy(tmp.data, buf, len);
    tmp.len = len + 1;

    // Now do an RLE decode on the enlarged buffer
    cfg.type = DM_COMP_RLE_MARKER;
    cfg.rleMarker = 0xC2;
    if ((res = dmDecodeGenericRLEAlloc(&mem, tmp.data, tmp.data + tmp.len, &cfg)) != 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 fmtEncodeAmicaPaintPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
{
    int res;
    DMGrowBuf tmp;
    DMCompParams cfg;

    // Encode the data to temp buffer
    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
        goto out;

    // And now RLE compress the data to the existing buffer
    cfg.type = DM_COMP_RLE_MARKER;
    cfg.rleMarker = 0xC2;
    cfg.rleMinCount = 3;
    cfg.rleMaxCount = 255;
    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);

out:
    dmGrowBufFree(&tmp);
    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 fmtTruePaintGetLaceType(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;
    DMCompParams cfg;

    cfg.type = DM_COMP_RLE_MARKER;
    cfg.rleMarker = *(buf + 15);
    if ((res = dmDecodeGenericRLEAlloc(&mem, buf + FUNPAINT2_HEADER_SIZE, buf + len, &cfg)) != 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;
    DMCompParams cfg;

    // Encode the data to temp buffer
    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
        goto out;

    // Analyze the data ..
    dmGenericRLEAnalyze(&tmp, &cfg.rleMarker, DM_COMP_RLE_MARKER);

    // Add the header bits
    if (!dmGrowBufPut(buf, fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) ||
        !dmGrowBufPutU8(buf, cfg.rleMarker))
    {
        res = DMERR_MALLOC;
        goto out;
    }

    // And now RLE compress the data to the existing buffer
    cfg.type = DM_COMP_RLE_MARKER;
    cfg.rleMinCount = 3;
    cfg.rleMaxCount = 255;
    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);

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, DM_FMT_RDWR,
        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, DM_FMT_RDWR,
        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, DM_FMT_RDWR,
        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, fmtDrazLaceGetLaceType, NULL },
            { DT_ENC_FUNCTION, 0x2742, 0,  1, NULL, fmtDrazLaceSetLaceType },
            { DT_LAST,         0,      0,  0, NULL, NULL },
        }
    },

    {
        D64_FMT_MC | D64_FMT_ILACE, "drl", "DrazLace 1.0 (unpacked)", 0x5800, 18242, DM_FMT_RDWR,
        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, fmtDrazLaceGetLaceType, NULL },
            { DT_ENC_FUNCTION, 0x2742, 0,  1, NULL, fmtDrazLaceSetLaceType },
            { DT_LAST,         0,      0,  0, NULL, NULL },
        }
    },

    {
        D64_FMT_MC | D64_FMT_ILACE, "mci", "Truepaint (unpacked)", 0x9c00, 19434, DM_FMT_RD,
        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, fmtTruePaintGetLaceType, NULL },
            { DT_LAST,         0,      0,  0, NULL, NULL },
        }
    },

    {
        D64_FMT_MC, "kla", "Koala Paint (unpacked)", 0x6000, 10003, DM_FMT_RDWR,
        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, "aas", "Advanced Art Studio (unpacked)", 0x2000, 10018, DM_FMT_RDWR,
        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, DM_FMT_RDWR,
        C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
        C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
        fmtProbeAmicaPaintPacked,
        fmtDecodeAmicaPaintPacked, fmtEncodeAmicaPaintPacked,
        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, DM_FMT_RDWR,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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, DM_FMT_RDWR,
        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, DM_FMT_RDWR,
        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, DM_FMT_RDWR,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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, DM_FMT_RD,
        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]);