Mercurial > hg > dmlib
diff lib64gfx.c @ 407:59244a7ae37f
Move c64 utilities to the engine lib, as we benefit from a common framework.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 03 Nov 2012 02:19:51 +0200 |
parents | |
children | e4b2f689aff6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib64gfx.c Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,727 @@ +/* + * 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" +#include <errno.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, +}; + + + +DMImage * dmImageAlloc(int width, int height) +{ + DMImage *img = dmCalloc(1, sizeof(DMImage)); + if (img == NULL) + return NULL; + + img->width = img->pitch = width; + img->height = height; + img->data = dmMalloc(width * height * sizeof(uint8_t)); + if (img->data == NULL) + { + dmFree(img); + return NULL; + } + + return img; +} + + +void dmImageFree(DMImage *img) +{ + if (img != NULL) + { + if (!img->constpal) + { + dmFree(img->pal); + } + dmFree(img->data); + dmFree(img); + } +} + + +int dmImageGetBytesPerPixel(int format) +{ + switch (format) + { + case DM_IFMT_PALETTE : return 1; + case DM_IFMT_RGB : return 3; + case DM_IFMT_RGBA : return 4; + case DM_IFMT_RGB_PLANE : return 3; + default: return 0; + } +} + + +int dmC64ConvertCSData(DMImage *img, + int xoffs, int yoffs, const uint8_t *buf, + int width, int height, BOOL multicolor, int *colors) +{ + int yc, widthpx = width * 8; + uint8_t *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_t *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_t 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_t *d = dp; + + for (xc = 0; xc < widthpx; xc++) + { + const int b = buf[offs + (xc / 8)]; + const int v = 7 - (xc & 7); + const uint8_t c = colors[(b >> v) & 1]; + + *d++ = c; + } + + dp += img->pitch; + } + } + + return 0; +} + + +static int fmtProbeDrazPaint(const uint8_t *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_t *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_t *buf, const size_t len, const DMC64ImageFormat *fmt) +{ + int res; + uint8_t rleMarker; + uint8_t *mem, *dst, *dstEnd; + const uint8_t *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_t *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_t *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_t *buf, const size_t 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_t *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_t *buf, const size_t len, const DMC64ImageFormat *fmt) +{ + int res; + uint8_t *mem, *dst, *dstEnd; + const uint8_t *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_t *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_t *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_t *buf, const size_t 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_t *buf, const size_t len, + DMC64ImageFormat **pfmt) +{ + int i, scoreMax = 0, 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 0; +} + + +int dmC64DecodeGenericBMP(DMC64Image *img, const uint8_t *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_t *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_t *dp = screen->data; + + for (yc = 0; yc < C64_SCR_HEIGHT; yc++) + { + uint8_t *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_t 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_t *dp = screen->data; + + for (yc = 0; yc < C64_SCR_HEIGHT; yc++) + { + uint8_t *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_t 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_t *dp = screen->data; + + for (yc = 0; yc < C64_SCR_HEIGHT; yc++) + { + uint8_t *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_t 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 (2*1024) + +int dmReadDataFile(const char *filename, uint8_t **pbuf, size_t *pbufSize) +{ + FILE *f; + uint8_t *dataBuf = NULL, *dataPtr; + size_t bufSize, readSize, dataSize; + + if (filename == NULL) + f = stdin; + else + if ((f = fopen(filename, "rb")) == NULL) + { + int err = errno; + dmError("Could not open input file '%s': %d, %s\n", + filename, err, strerror(err)); + return -1; + } + + readSize = bufSize = BUF_SIZE_INITIAL; + if ((dataBuf = dmMalloc(bufSize)) == NULL) + { + fclose(f); + dmError("Error allocating memory for data, %d bytes.\n", bufSize); + return -4; + } + + dataPtr = dataBuf; + dataSize = 0; + + while (!feof(f) && !ferror(f)) + { + size_t read = fread(dataPtr, 1, readSize, f); + + dataSize += read; + dataPtr += read; + + if (read == readSize && !feof(f)) + { + readSize = BUF_SIZE_GROW; + bufSize += BUF_SIZE_GROW; + if ((dataBuf = dmRealloc(dataBuf, bufSize)) == NULL) + { + dmError("Error reallocating memory for data, %d bytes.\n", bufSize); + return -4; + } + } + else + break; + } + + fclose(f); + + *pbuf = dataBuf; + *pbufSize = dataSize; + + return 0; +}