view lib64gfx.c @ 435:e4a3f183e463

Modularize some more.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 03 Nov 2012 16:08:30 +0200
parents ba6ec5ee5929
children afa7f8dd4a8f
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 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "lib64gfx.h"


const char *dmC64ImageTypeNames[DM_C64IFMT_LAST_TYPE] =
{
    "hires",
    "multicolor",
    "hires interlace",
    "mc interlace",
    "hires fli",
    "mc fli",
};


// Based on Pepto's palette, stolen from VICE
DMColor dmC64Palette[C64_NCOLORS] =
{
    { 0x00, 0x00, 0x00, 0 },
    { 0xFF, 0xFF, 0xFF, 0 },
    { 0x68, 0x37, 0x2B, 0 },
    { 0x70, 0xA4, 0xB2, 0 },
    { 0x6F, 0x3D, 0x86, 0 },
    { 0x58, 0x8D, 0x43, 0 },
    { 0x35, 0x28, 0x79, 0 },
    { 0xB8, 0xC7, 0x6F, 0 },
    { 0x6F, 0x4F, 0x25, 0 },
    { 0x43, 0x39, 0x00, 0 },
    { 0x9A, 0x67, 0x59, 0 },
    { 0x44, 0x44, 0x44, 0 },
    { 0x6C, 0x6C, 0x6C, 0 },
    { 0x9A, 0xD2, 0x84, 0 },
    { 0x6C, 0x5E, 0xB5, 0 },
    { 0x95, 0x95, 0x95, 0 },
};


const size_t dmC64DefaultSizes[DT_LAST] =
{
    C64_SCR_COLOR_SIZE,
    C64_SCR_BITMAP_SIZE,
    C64_SCR_SCREEN_SIZE,
    1,
    C64_SCR_EXTRADATA,
};



int dmC64ConvertCSData(DMImage *img,
    int xoffs, int yoffs, const Uint8 *buf,
    int width, int height, BOOL multicolor, int *colors)
{
    int yc, widthpx = width * 8;
    Uint8 *dp;

    if (img == NULL)
        return -1;
    if (xoffs < 0 || yoffs < 0)
        return -2;
    if (xoffs > img->width - widthpx ||
        yoffs > img->height - height)
        return -3;

    dp = img->data + (yoffs * img->pitch) + xoffs;

    if (multicolor)
    {
        for (yc = 0; yc < height; yc++)
        {
            const int offs = yc * width;
            int xc;
            Uint8 *d = dp;

            for (xc = 0; xc < widthpx / 2; xc++)
            {
                const int b = buf[offs + (xc / 4)];
                const int v = 6 - ((xc * 2) & 6);
                const Uint8 c = colors[(b >> v) & 3];
                
                *d++ = c;
                *d++ = c;
            }

            dp += img->pitch;
        }
    }
    else
    {
        for (yc = 0; yc < height; yc++)
        {
            const int offs = yc * width;
            int xc;
            Uint8 *d = dp;

            for (xc = 0; xc < widthpx; xc++)
            {
                const int b = buf[offs + (xc / 8)];
                const int v = 7 - (xc & 7);
                const Uint8 c = colors[(b >> v) & 1];
                
                *d++ = c;
            }

            dp += img->pitch;
        }
    }
    
    return 0;
}


static int fmtProbeDrazPaint(const Uint8 *buf, const size_t len)
{
    if (len == 10051 && buf[0] == 0x00 && buf[1] == 0x58)
        return DM_PROBE_SCORE_GOOD;

    return DM_PROBE_SCORE_FALSE;
}


static int fmtProbeDrazPaint20Packed(const Uint8 *buf, const size_t len)
{
    const char *ident = (const char *) buf + 2;
    if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 &&
            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;
    Uint8 rleMarker;
    Uint8 *mem, *dst, *dstEnd;
    const Uint8 *src, *srcEnd;

    if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL)
        return -1;
    
    rleMarker = *(buf + 0x0d);
    src       = buf + 0x0e;
    srcEnd    = buf + len;
    dst       = mem;
    dstEnd    = mem + C64_RAM_SIZE;

    while (src <= srcEnd && dst <= dstEnd)
    {
        int c = *src++;
        if (c == rleMarker && src + 2 <= srcEnd)
        {
            int cnt = *src++;
            c = *src++;
            while (cnt-- && dst <= dstEnd)
                *dst++ = c;
        }
        else
            *dst++ = c;
    }
    
    res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt);
    
    dmFree(mem);

    return res;
}


static int fmtProbeDrazLace10(const Uint8 *buf, const size_t len)
{
    if (len == 18242 && buf[0] == 0x00 && buf[1] == 0x58)
        return DM_PROBE_SCORE_GOOD;
    return DM_PROBE_SCORE_FALSE;
}


static int fmtProbeDrazLace10Packed(const Uint8 *buf, const size_t len)
{
    const char *ident = (const char *) buf + 2;
    if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 &&
            strncmp(ident, "DRAZLACE! 1.0", 13) == 0)
        return DM_PROBE_SCORE_MAX;
    
    return DM_PROBE_SCORE_FALSE;
}


static BOOL fmtDrazLaceSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const Uint8 *buf, const size_t len)
{
    (void) len;

    img->laceType = buf[op->offs] ? DM_C64ILACE_RES : DM_C64ILACE_COLOR;
    img->laceBank2 = 0;
    return TRUE;
}


#define AMICA_DM_PROBE_SIZE 1024
static int fmtProbeAmicaPaintPacked(const Uint8 *buf, const size_t len)
{
    int i, n;
    if (len < AMICA_DM_PROBE_SIZE || buf[0] != 0x00 || buf[1] != 0x40)
        return DM_PROBE_SCORE_FALSE;
    
    for (n = 0, i = 2; i < AMICA_DM_PROBE_SIZE; i++)
        if (buf[i] == 0xC2) n++;
    
    if (n > 5)
        return DM_PROBE_SCORE_GOOD;
    if (n > 3)
        return DM_PROBE_SCORE_AVG;

    return DM_PROBE_SCORE_MAYBE;
}


static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
{
    int res;
    Uint8 *mem, *dst, *dstEnd;
    const Uint8 *src, *srcEnd;

    if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL)
        return -1;
    
    src    = buf;
    srcEnd = buf + len;
    dst    = mem;
    dstEnd = mem + C64_RAM_SIZE;

    while (src <= srcEnd && dst <= dstEnd)
    {
        int c = *src++;
        if (c == 0xC2 && src + 2 <= srcEnd)
        {
            int cnt = *src++;
            c = *src++;
            while (cnt-- && dst <= dstEnd)
                *dst++ = c;
        }
        else
            *dst++ = c;
    }
    
    res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt);
    
    dmFree(mem);

    return res;
}


static int fmtProbeKoalaPaint(const Uint8 *buf, const size_t len)
{
    if (len == 10003 && buf[0] == 0x00 && buf[1] == 0x60)
        return DM_PROBE_SCORE_AVG;
    return DM_PROBE_SCORE_FALSE;
}


static int fmtProbeTruePaint(const Uint8 *buf, const size_t len)
{
    if (len == 19434 && buf[0] == 0x00 && buf[1] == 0x9c)
        return DM_PROBE_SCORE_GOOD;
    return DM_PROBE_SCORE_FALSE;
}


static BOOL fmtTruePaintSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const Uint8 *buf, const size_t len)
{
    (void) op;
    (void) buf;
    (void) len;
    img->laceType = DM_C64ILACE_RES;
    img->laceBank2 = 1;
    return TRUE;
}


DMC64ImageFormat dmC64ImageFormats[] =
{
    {
        DM_C64IFMT_MC, ".drp", "DrazPaint 2.0 (packed)",
        fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, NULL,
        4,
        {
            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
            { DT_BITMAP,     0x0800, 0,  0, NULL },
            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
        }
    },

    {
        DM_C64IFMT_MC_ILACE, ".dlp", "DrazLace 1.0 (packed)",
        fmtProbeDrazLace10Packed, fmtDecodeDrazPaintPacked, NULL,
        6,
        {
            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
            { DT_BITMAP,     0x0800, 0,  0, NULL },
            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
            { DT_BITMAP,     0x2800, 1,  0, NULL },
            { DT_FUNCTION,   0x2742, 0,  1, fmtDrazLaceSetLaceType },
        }
    },
    
    {
        DM_C64IFMT_MC, ".drp", "DrazPaint (unpacked)",
        fmtProbeDrazPaint, NULL, NULL,
        4,
        {
            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
            { DT_BITMAP,     0x0800, 0,  0, NULL },
            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
        }
    },

    {
        DM_C64IFMT_MC_ILACE, ".drl", "DrazLace 1.0 (unpacked)",
        fmtProbeDrazLace10, NULL, NULL,
        6,
        {
            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
            { DT_BITMAP,     0x0800, 0,  0, NULL },
            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
            { DT_BITMAP,     0x2800, 1,  0, NULL },
            { DT_FUNCTION,   0x2742, 0,  1, fmtDrazLaceSetLaceType },
        }
    },
    
    {
        DM_C64IFMT_MC_ILACE, ".mci", "Truepaint (unpacked)",
        fmtProbeTruePaint, NULL, NULL,
        6,
        {
            { DT_SCREEN_RAM, 0x0000, 0,  0, NULL },
            { DT_BGCOLOR,    0x03e8, 0,  0, NULL },
            { DT_BITMAP,     0x0400, 0,  0, NULL },
            { DT_BITMAP,     0x2400, 1,  0, NULL },
            { DT_SCREEN_RAM, 0x4400, 1,  0, NULL },
            { DT_COLOR_RAM,  0x4800, 0,  0, NULL },
            { DT_FUNCTION,   0x0000, 0,  0, fmtTruePaintSetLaceType },
        }
    },
    
    {
        DM_C64IFMT_MC, ".kla", "Koala Paint (unpacked)",
        fmtProbeKoalaPaint, NULL, NULL,
        4,
        {
            { DT_COLOR_RAM,  0x2328, 0,  0, NULL },
            { DT_BITMAP,     0x0000, 0,  0, NULL },
            { DT_SCREEN_RAM, 0x1f40, 0,  0, NULL },
            { DT_BGCOLOR,    0x2710, 0,  0, NULL },
        }
    },

    {
        DM_C64IFMT_MC, ".ami", "Amica Paint (packed)",
        fmtProbeAmicaPaintPacked, fmtDecodeAmicaPaintPacked, NULL,
        4,
        {
            { DT_COLOR_RAM,  0x2328, 0,  0, NULL },
            { DT_BITMAP,     0x0000, 0,  0, NULL },
            { DT_SCREEN_RAM, 0x1f40, 0,  0, NULL },
            { DT_BGCOLOR,    0x2710, 0,  0, NULL },
        }
    },
    
};

const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]);


int dmC64ProbeGeneric(const Uint8 *buf, const size_t len, DMC64ImageFormat **pfmt)
{
    int i, scoreMax = DM_PROBE_SCORE_FALSE, scoreIndex = -1;

    for (i = 0; i < ndmC64ImageFormats; i++)
    {
        DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
        int score = fmt->probe(buf, len);
        if (score > scoreMax)
        {
            scoreMax = score;
            scoreIndex = i;
        }
    }

    if (scoreIndex >= 0)
    {
        *pfmt = &dmC64ImageFormats[scoreIndex];
        return scoreMax;
    }
    else
        return DM_PROBE_SCORE_FALSE;
}


int dmC64DecodeGenericBMP(DMC64Image *img, const Uint8 *buf,
    const size_t len, const DMC64ImageFormat *fmt)
{
    int i;

    memset(img, 0, sizeof(*img));
    img->type = fmt->type;

    for (i = 0; i < fmt->ndecodeOps; i++)
    {
        const DMDecodeOp *op = &fmt->decodeOps[i];
        const Uint8 *src;
        size_t size;

        if (op->bank < 0 || op->bank >= C64_SCR_MAX_BANK)
        {
            dmError("Invalid bank %d definition in generic decode operator %d @ #%d.\n",
                op->bank, op->type, i);
            return -1;
        }
        
        if (op->type < 0 || op->type >= DT_LAST)
        {
            dmError("Invalid decode operator type %d @ #%d.\n",
                op->type, i);
            return -1;
        }
        
        size = (op->size == 0) ? dmC64DefaultSizes[op->type] : op->size;
        
        if (op->offs + size > len)
        {
            dmError("Decode out of bounds, op #%d type=%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
                i, op->type, op->offs, op->offs, op->bank, size, size, len, len);
            return -2;
        }
        
        src = buf + op->offs;
        
        switch (op->type)
        {
            case DT_COLOR_RAM:   memcpy(img->color[op->bank], src, size); break;
            case DT_BITMAP:      memcpy(img->bitmap[op->bank], src, size); break;
            case DT_SCREEN_RAM:  memcpy(img->screen[op->bank], src, size); break;
            case DT_BGCOLOR:     img->bgcolor = *src; break;
            case DT_EXTRADATA:   memcpy(img->extradata, src, size); break;
            case DT_FUNCTION:
                if (op->function == NULL)
                {
                    dmError("Decode op is a function, but function ptr is NULL: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
                        i, op->offs, op->offs, op->bank, size, size, len, len);
                    return -6;
                }
                if (!op->function(img, op, buf, len))
                {
                    dmError("Decode op custom function failed: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
                        i, op->offs, op->offs, op->bank, size, size, len, len);
                    return -5;
                }
                break;
        }
    }
    
    return 0;
}


static int dmC64ConvertHiResBMP(DMImage *screen, const DMC64Image *img)
{
    int yc;
    Uint8 *dp = screen->data;
    
    for (yc = 0; yc < C64_SCR_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 < C64_SCR_WIDTH; xc++)
        {
            const int x = xc / 8;
            const int scroffs = scroffsy + x;
            const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
            const int v = 7 - (xc & 7);
            Uint8 c;

            if ((b >> v) & 1)
                c = img->screen[0][scroffs] & 15;
            else
                c = img->screen[0][scroffs] >> 4;
            
            *d++ = c;
        }

        dp += screen->pitch;
    }

    return 0;
}


static int dmC64ConvertMultiColorBMP(DMImage *screen, const DMC64Image *img)
{
    int yc;
    Uint8 *dp = screen->data;
    
    for (yc = 0; yc < C64_SCR_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 < C64_SCR_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;
}


static int dmC64ConvertLaceMultiColorBMP(DMImage *screen, const DMC64Image *img)
{
    int yc;
    Uint8 *dp = screen->data;
    
    for (yc = 0; yc < C64_SCR_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 < C64_SCR_WIDTH / 2; xc++)
        {
            const int x = xc / 4;
            const int scroffs = scroffsy + x;
            const int bmoffs = bmoffsy + (x * 8) + yb;
            const int v = 6 - ((xc * 2) & 6);
            const int b1 = (img->bitmap[0][bmoffs] >> v) & 3;
            const int b2 = (img->bitmap[1][bmoffs] >> v) & 3;
            Uint8 c1, c2;

            switch (b1)
            {
                case 0: c1 = img->bgcolor; break;
                case 1: c1 = img->screen[0][scroffs] >> 4; break;
                case 2: c1 = img->screen[0][scroffs] & 15; break;
                case 3: c1 = img->color[0][scroffs] & 15; break;
            }

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

        dp += screen->pitch;
    }

    return 0;
}


int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src)
{
    switch (src->type)
    {
        case DM_C64IFMT_HIRES:
            return dmC64ConvertHiResBMP(dst, src);
        
        case DM_C64IFMT_MC:
            return dmC64ConvertMultiColorBMP(dst, src);

        case DM_C64IFMT_MC_ILACE:
            return dmC64ConvertLaceMultiColorBMP(dst, src);

        default:
            return -1;
    }
}


#define BUF_SIZE_INITIAL   (16*1024)
#define BUF_SIZE_GROW      (8*1024)

int dmReadDataFile(FILE *inFile, const char *filename, Uint8 **pbuf, size_t *pbufSize)
{
    FILE *f;
    int res = DMERR_OK;
    Uint8 *dataBuf = NULL, *dataPtr;
    size_t readSize, dataSize, dataRead;
    
    if (inFile != NULL)
        f = inFile;
    else
    if (filename != NULL)
    {
        if ((f = fopen(filename, "rb")) == NULL)
        {
            dmError("Could not open '%s' for reading.\n", filename);
            return DMERR_FOPEN;
        }
    }
    else
    {
        dmError("NULL filename and stream pointers.\n");
        return DMERR_NULLPTR;
    }

    // Allocate initial data buffer
    readSize = dataSize = BUF_SIZE_INITIAL;
    if ((dataBuf = dmMalloc(dataSize)) == NULL)
    {
        dmError("Error allocating memory for data, %d bytes.\n", dataSize);
        res = DMERR_MALLOC;
        goto error;
    }

    dataPtr = dataBuf;
    dataRead = 0;

    while (!feof(f) && !ferror(f))
    {
        size_t read = fread(dataPtr, 1, readSize, f);
        dataPtr += read;
        dataRead += read;

        if (dataRead >= dataSize)
        {
            readSize = BUF_SIZE_GROW;
            dataSize += BUF_SIZE_GROW;
            if ((dataBuf = dmRealloc(dataBuf, dataSize)) == NULL)
            {
                dmError("Error reallocating memory for data, %d bytes.\n", dataSize);
                res = DMERR_MALLOC;
                goto error;
            }
        }
        else
            break;
    }

    *pbufSize = dataRead;
    *pbuf = dataBuf;

error:
    if (f != inFile)
        fclose(f);
    
    return res;
}


int dmC64DecodeBMP(DMC64Image *img, const Uint8 *buf, const size_t len,
    const size_t probeOffs, const size_t loadOffs,
    DMC64ImageFormat **fmt, DMC64ImageFormat *forced)
{
    // Check for forced format
    if (forced != NULL)
        *fmt = forced;
    else
    {
        // Nope, perform a generic probe
        if (probeOffs >= len)
            return -200;

        if (dmC64ProbeGeneric(buf + probeOffs, len - probeOffs, fmt) == DM_PROBE_SCORE_FALSE)
            return -201;
    }

    if (loadOffs >= len)
        return -203;

    // Decode the bitmap to memory layout
    if ((*fmt)->decode != NULL)
        return (*fmt)->decode(img, buf + loadOffs, len - loadOffs, *fmt);
    else
        return dmC64DecodeGenericBMP(img, buf + loadOffs, len - loadOffs, *fmt);
}