Mercurial > hg > dmlib
view tools/lib64gfx.c @ 1463:bde6a66bc2f6
Change dmDecodeGenericRLE() to use DMGrowBuf as output. Also add support
for two different common types of RLE encoding. Add stub function of
dmEncodeGenericRLE(), not implemented yet. Change functions using
dmDecodeGenericRLE to use the new API. Also fix a problem in Amica Paint
RLE decoding as the format seems to save one byte less than is necessary.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 10 May 2018 21:27:24 +0300 |
parents | 9cb6dd1046bf |
children | 88845f95e791 |
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" #define BUF_SIZE_INITIAL (16*1024) #define BUF_SIZE_GROW (4*1024) // 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 }, }; #define DM_RLE_MARKER 1 #define DM_RLE_MASK 2 #define DM_GET_ADDR_LO(addr) ((addr) & 0xff) #define DM_GET_ADDR_HI(addr) (((addr) >> 8) & 0xff) void dmSetDefaultC64Palette(DMImage *img) { img->constpal = TRUE; img->pal = dmDefaultC64Palette; img->ncolors = C64_NCOLORS; img->ctransp = 255; } static BOOL dmCompareAddr16(const Uint8 *buf, const size_t offs, const Uint16 addr) { return buf[offs ] == DM_GET_ADDR_LO(addr) && buf[offs + 1] == DM_GET_ADDR_HI(addr); } int dmC64ImageGetNumBanks(const DMC64ImageFormat *fmt) { int nbanks = 0; for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; if (op->type == DT_LAST) break; if (op->bank > nbanks) nbanks = op->bank; } return nbanks + 1; } DMC64Image *dmC64ImageAlloc(const DMC64ImageFormat *fmt) { DMC64Image *img = dmMalloc0(sizeof(DMC64Image)); if (img == NULL) return NULL; // Initialize image information img->width = fmt->width; img->height = fmt->height; img->ch_width = fmt->ch_width; img->ch_height = fmt->ch_height; img->nbanks = dmC64ImageGetNumBanks(fmt); // Allocate banks if ((img->color = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL || (img->bitmap = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL || (img->screen = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL || (img->charmem = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL) goto err; for (int i = 0; i < img->nbanks; i++) { if ((img->color[i] = dmMalloc0(img->ch_width * img->ch_height)) == NULL) goto err; if ((img->bitmap[i] = dmMalloc0(img->ch_width * img->ch_height * 8)) == NULL) goto err; if ((img->screen[i] = dmMalloc0(img->ch_width * img->ch_height)) == NULL) goto err; if ((img->charmem[i] = dmMalloc0(C64_MAX_CHARS * C64_CHR_SIZE)) == NULL) goto err; } return img; err: dmC64ImageFree(img); return NULL; } void dmC64ImageFree(DMC64Image *img) { if (img != NULL) { // Free the allocated areas for (int i = 0; i < img->nbanks; i++) { dmFree(img->color[i]); dmFree(img->bitmap[i]); dmFree(img->screen[i]); dmFree(img->charmem[i]); } // Free the pointers to the areas dmFree(img->color); dmFree(img->bitmap); dmFree(img->screen); dmFree(img->charmem); // Extra data .. for (int i = 0; i < C64_MAX_EXTRA_DATA; i++) dmFree(img->extraData[i]); dmMemset(img, 0, sizeof(DMC64Image)); dmFree(img); } } char * dmC64GetImageTypeString(char *buf, const size_t len, const int type) { snprintf(buf, len, "%s%s%s%s", (type & D64_FMT_FLI) ? "FLI " : "", (type & D64_FMT_MC) ? "MCol " : "HiRes ", (type & D64_FMT_ILACE) ? "Ilace " : "", (type & D64_FMT_CHAR) ? "CharMap" : "" ); return buf; } int dmC64ConvertCSDataToImage(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 DMERR_NULLPTR; if (xoffs < 0 || yoffs < 0 || xoffs > img->width - widthpx || yoffs > img->height - height) return DMERR_INVALID_ARGS; 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 DMERR_OK; } static int dmDecodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const Uint8 rleMarker, const Uint8 rleMask, const int rleType) { int res; if ((res = dmGrowBufAlloc(dst, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK) goto err; // Perform RLE decode while (src < srcEnd) { Uint8 c = *src++; int cnt = 1; switch (rleType) { case DM_RLE_MARKER: if (c == rleMarker) { if (srcEnd - src < 2) { res = dmError(DMERR_INVALID_DATA, "Foobar: %d\n", srcEnd - src); goto err; } cnt = *src++; c = *src++; } break; case DM_RLE_MASK: if ((c & rleMask) == rleMarker) { if (srcEnd - src < 1) { res = dmError(DMERR_INVALID_DATA, "foobar2\n"); goto err; } // XXX TODO actually we probably want another mask here cnt = c & (0xff ^ rleMask); c = *src++; } break; } while (cnt--) { if (!dmGrowBufPutU8(dst, c)) { res = dmError(DMERR_MALLOC, "bazzooo\n"); goto err; } } } // Reallocate the memory if ((res = dmGrowBufResize(dst)) != DMERR_OK) goto err; res = DMERR_OK; err: return res; } static int dmEncodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const Uint8 rleMarker) { (void) dst; (void) src; (void) srcEnd; (void) rleMarker; return DMERR_OK; } static inline Uint8 dmC64GetGenericSCPixel( const DMC64Image *img, const int bmoffs, const int scroffs, const int vshift, const int vbank, const int vbitmap, const int cbank) { (void) cbank; if ((img->bitmap[vbitmap][bmoffs] >> vshift) & 1) return img->screen[vbank][scroffs] >> 4; else return img->screen[vbank][scroffs] & 15; } static inline Uint8 dmC64GetGenericMCPixel( const DMC64Image *img, const int bmoffs, const int scroffs, const int vshift, const int vbank, const int vbitmap, const int cbank) { switch ((img->bitmap[vbitmap][bmoffs] >> vshift) & 3) { case 0: return img->bgcolor; break; case 1: return img->screen[vbank][scroffs] >> 4; break; case 2: return img->screen[vbank][scroffs] & 15; break; default: return img->color[cbank][scroffs] & 15; break; } } static inline Uint8 fmtGetGenericSCPixel( const DMC64Image *img, const int bmoffs, const int scroffs, const int vshift, const int vbitmap, const int raster) { (void) raster; return dmC64GetGenericSCPixel(img, bmoffs, scroffs, vshift, 0, vbitmap, 0); } static inline Uint8 fmtGetGenericMCPixel( 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); } 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 = dmDecodeGenericRLE(&mem, buf + 0x0e, buf + len, *(buf + 0x0d), 0, DM_RLE_MARKER)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt); out: dmGrowBufFree(&mem); 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 struct _DMC64EncDecOp *op, const Uint8 *buf, const size_t len) { (void) len; 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, 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 struct _DMC64EncDecOp *op, const Uint8 *buf, const size_t len) { (void) op; (void) buf; (void) len; 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 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), "FUNPAINT (MT) ", 14) == 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, DM_RLE_MARKER)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt); out: dmGrowBufFree(&mem); 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; } // // 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 2.0 (packed)", 0x5800, 0, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, 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, 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, "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, "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, 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_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, NULL, 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, NULL, 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]); // Perform probing of the given data buffer, trying to determine // if it contains a supported "C64" image format. Returns the // "probe score", see libgfx.h for list of values. If a match // is found, pointer to format description is set to *pfmt. int dmC64ProbeBMP(const Uint8 *buf, const size_t len, const DMC64ImageFormat **pfmt) { int scoreMax = DM_PROBE_SCORE_FALSE, scoreIndex = -1; for (int i = 0; i < ndmC64ImageFormats; i++) { const DMC64ImageFormat *fmt = &dmC64ImageFormats[i]; int score = DM_PROBE_SCORE_FALSE; if (fmt->probe == NULL && fmt->size > 0 && fmt->addr > 0) { // Generic probe just checks matching size and load address if (len == fmt->size && dmCompareAddr16(buf, 0, fmt->addr)) score = DM_PROBE_SCORE_GOOD; } else if (fmt->probe != NULL) score = fmt->probe(buf, len, fmt); if (score > scoreMax) { scoreMax = score; scoreIndex = i; } } if (scoreIndex >= 0) { *pfmt = &dmC64ImageFormats[scoreIndex]; return scoreMax; } else return DM_PROBE_SCORE_FALSE; } static int dmC64SanityCheckEncDecOp(const int i, const DMC64EncDecOp *op, const DMC64Image *img) { switch (op->type) { case DT_COLOR_RAM: case DT_BITMAP: case DT_SCREEN_RAM: case DT_CHAR_DATA: if (op->bank < 0 || op->bank > img->nbanks) { return dmError(DMERR_INTERNAL, "Invalid bank %d / %d definition in generic encode/decode operator %d @ #%d.\n", op->bank, img->nbanks, op->type, i); } break; case DT_EXTRA_DATA: if (op->bank < 0 || op->bank >= C64_MAX_EXTRA_DATA) { return dmError(DMERR_INTERNAL, "Invalid bank %d definition in generic encode/decode operator %d @ #%d.\n", op->bank, op->type, i); } break; } if (op->type < 0 || op->type >= DT_LAST) { return dmError(DMERR_INTERNAL, "Invalid encode/decode operator type %d @ #%d.\n", op->type, i); } return DMERR_OK; } static BOOL dmC64GetOpSize(const DMC64EncDecOp *op, const DMC64ImageFormat *fmt, size_t *size) { BOOL check; switch (op->type) { case DT_SCREEN_RAM: case DT_COLOR_RAM: *size = fmt->ch_height * fmt->ch_width; check = TRUE; break; case DT_BITMAP: *size = fmt->ch_height * fmt->ch_width * 8; check = TRUE; break; case DT_EXTRA_DATA: *size = op->size; check = TRUE; break; case DT_CHAR_DATA: *size = C64_MAX_CHARS * C64_CHR_SIZE; check = TRUE; break; case DT_COLOR_REG: *size = 1; check = FALSE; break; default: *size = 0; check = FALSE; } if (op->size != 0) { if (check && op->size > *size) return FALSE; *size = op->size; } return TRUE; } int dmC64DecodeGenericBMP(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt) { int res = DMERR_OK; if (buf == NULL || img == NULL || fmt == NULL) return DMERR_NULLPTR; // Clear the image structure, set basics img->type = fmt->type; img->width = fmt->width; img->height = fmt->height; img->ch_width = fmt->ch_width; img->ch_height = fmt->ch_height; img->nbanks = dmC64ImageGetNumBanks(fmt); // Perform decoding for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; const Uint8 *src; size_t size; // Check for last operator if (op->type == DT_LAST) break; // Check operation validity if ((res = dmC64SanityCheckEncDecOp(i, op, img)) != DMERR_OK) return res; // Check size if (!dmC64GetOpSize(op, fmt, &size)) { return dmError(DMERR_INVALID_DATA, "Decode op SIZE out of bounds, op #%d type=%d, offs=%d ($%04x), " "bank=%d, size=%d ($%04x) vs. allocated %d ($%04x)\n", i, op->type, op->offs, op->offs, op->bank, size, size, op->size, op->size); } // Do we need to reallocate some more space? if (op->offs + size > len) { return dmError(DMERR_INVALID_DATA, "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); } src = buf + op->offs; // Perform operation 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_CHAR_DATA: memcpy(img->charmem[op->bank], src, size); break; case DT_EXTRA_DATA: if (img->extraData[op->bank] != NULL) { return dmError(DMERR_INTERNAL, "Extra data block already allocated and used! " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", i, op->offs, op->offs, op->bank, size, size, len, len); } if ((img->extraData[op->bank] = dmMalloc0(size)) == NULL) { return dmError(DMERR_MALLOC, "Could not allocate extradata block! " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", i, op->offs, op->offs, op->bank, size, size, len, len); } img->extraDataSizes[op->bank] = size; memcpy(img->extraData[op->bank], src, size); break; case DT_COLOR_REG: switch (op->size) { case DC_D020: img->d020 = *src; break; case DC_BGCOL: case DC_D021: img->bgcolor = *src; break; case DC_D022: img->d022 = *src; break; case DC_D023: img->d023 = *src; break; case DC_D024: img->d024 = *src; break; default: return dmError(DMERR_INTERNAL, "Unhandled DT_COLOR_REG mode %d in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, i, op->offs, op->offs, op->bank, size, size, len, len); } break; case DT_COLOR_SET: switch (op->size) { case DC_D020: img->d020 = op->offs; break; case DC_BGCOL: case DC_D021: img->bgcolor = op->offs; break; case DC_D022: img->d022 = op->offs; break; case DC_D023: img->d023 = op->offs; break; case DC_D024: img->d024 = op->offs; break; default: return dmError(DMERR_INTERNAL, "Unhandled DT_COLOR_SET mode %d in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, i, op->offs, op->offs, op->bank, size, size, len, len); } break; case DT_CHAR_CONFIG: switch (op->offs) { case D64_CHCFG_SCREEN: break; case D64_CHCFG_LINEAR: { for (int bank = 0; bank < img->nbanks; bank++) for (int offs = 0; offs < fmt->ch_height * fmt->ch_width; offs++) img->screen[bank][offs] = offs & 0xff; } break; default: return dmError(DMERR_INTERNAL, "Unhandled DT_CHAR_CONFIG mode %d in ", "op #%d, bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->offs, i, op->bank, size, size, len, len); } break; case DT_DEC_FUNCTION: if (op->decfunction == NULL) { return dmError(DMERR_INTERNAL, "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); } if (!op->decfunction(img, op, buf, len)) { return dmError(DMERR_INTERNAL, "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); } break; } } return DMERR_OK; } int dmC64EncodeGenericBMP(Uint8 **pbuf, size_t *plen, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res = DMERR_OK; Uint8 *buf; size_t allocated; if (pbuf == NULL || plen == NULL || img == NULL || fmt == NULL) return DMERR_NULLPTR; // Allocate the output buffer *plen = 0; if (fmt->size > 0) *plen = allocated = fmt->size; else allocated = 16 * 1024; if ((buf = dmMalloc(allocated)) == NULL) { return dmError(DMERR_MALLOC, "Could not allocate %d bytes of memory for C64 image encoding buffer.\n", allocated); goto err; } // Perform encoding for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; Uint8 *dst = buf + op->offs; size_t size, chksize; // Check for last operator if (op->type == DT_LAST) break; // Check operation validity if ((res = dmC64SanityCheckEncDecOp(i, op, img)) != DMERR_OK) goto err; // Check size if (!dmC64GetOpSize(op, fmt, &size)) { res = dmError(DMERR_INVALID_DATA, "Decode op SIZE out of bounds, op #%d type=%d, offs=%d ($%04x), " "bank=%d, size=%d ($%04x) vs. allocated %d ($%04x)\n", i, op->type, op->offs, op->offs, op->bank, size, size, op->size, op->size); goto err; } // Do we need to reallocate some more space? chksize = op->offs + size; if (chksize > allocated) { size_t diff = allocated - chksize, grow = (diff / (BUF_SIZE_GROW - 1)) * BUF_SIZE_GROW; allocated += grow; if ((buf = dmRealloc(buf, allocated)) == NULL) { res = dmError(DMERR_MALLOC, "Could not re-allocate %d bytes of memory for C64 image encoding buffer.\n", allocated); goto err; } } if (fmt->size == 0 && chksize > *plen) *plen = chksize; // Perform operation switch (op->type) { case DT_COLOR_RAM: memcpy(dst, img->color[op->bank], size); break; case DT_BITMAP: memcpy(dst, img->bitmap[op->bank], size); break; case DT_SCREEN_RAM: memcpy(dst, img->screen[op->bank], size); break; case DT_CHAR_DATA: memcpy(dst, img->charmem[op->bank], size); break; case DT_EXTRA_DATA: if (img->extraData[op->bank] == NULL) { res = dmError(DMERR_NULLPTR, "DT_EXTRA_DATA block is NULL in ", "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", i, op->offs, op->offs, op->bank, size, size, *plen, *plen); goto err; } if (size > img->extraDataSizes[op->bank]) { res = dmError(DMERR_INTERNAL, "DT_EXTRA_DATA size mismatch %d <> %d in ", "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, img->extraDataSizes[op->bank], i, op->offs, op->offs, op->bank, size, size, *plen, *plen); goto err; } memcpy(dst, img->extraData[op->bank], size); break; case DT_COLOR_REG: switch (op->size) { case DC_D020: *dst = img->d020; break; case DC_BGCOL: case DC_D021: *dst = img->bgcolor; break; case DC_D022: *dst = img->d022; break; case DC_D023: *dst = img->d023; break; case DC_D024: *dst = img->d024; break; default: res = dmError(DMERR_INTERNAL, "Unhandled DT_COLOR_REG mode %d in ", "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, i, op->offs, op->offs, op->bank, size, size, *plen, *plen); goto err; } break; case DT_ENC_FUNCTION: if (op->encfunction == NULL) { res = dmError(DMERR_INTERNAL, "Encode 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, *plen, *plen); goto err; } /* if (!op->encfunction(op, buf, len)) { res = dmError(DMERR_INTERNAL, "Encode 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); goto out; } */ break; } } res = DMERR_OK; err: *pbuf = buf; return res; } // Convert a generic "C64" format bitmap in DMC64Image struct to // a indexed/paletted bitmap image. int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src, const DMC64ImageFormat *fmt) { DMC64GetPixelFunc getPixel; // Sanity check arguments if (dst == NULL || src == NULL) return DMERR_NULLPTR; if (dst->width < src->width || dst->height < src->height) { return dmError(DMERR_INVALID_DATA, "Invalid src vs. dst width/height %d x %d <-> %d x %d\n", src->width, src->height, dst->width, dst->height); } dmMemset(dst->data, 0, dst->size); // Check pixel getter function if (fmt->getPixel != NULL) getPixel = fmt->getPixel; else getPixel = (fmt->type & D64_FMT_MC) ? fmtGetGenericMCPixel : fmtGetGenericSCPixel; // Resolution interlaced pics need to halve the source width int rwidth = src->width; if ((src->type & D64_FMT_ILACE) && src->laceType == D64_ILACE_RES) rwidth /= 2; // Perform conversion Uint8 *dp = dst->data; for (int yc = 0; yc < src->height; yc++) { Uint8 *d = dp; const int y = yc / 8, yb = yc & 7; const int scroffsy = y * src->ch_width; int xc; if (src->type & D64_FMT_CHAR) { // Charmode conversion if ((src->type & D64_FMT_MC) == D64_FMT_HIRES) // Hi-res charmap for (xc = 0; xc < rwidth; xc++) { const int x = xc / 8; const int scroffs = scroffsy + x; const int chr = src->screen[0][scroffs]; const int v = 7 - (xc & 7); if ((src->charmem[0][chr * C64_CHR_SIZE + yb] >> v) & 1) *d++ = src->color[0][scroffs]; else *d++ = src->bgcolor; } else // Multicolor variants for (xc = 0; xc < rwidth; xc++) { const int x = xc / 4; const int scroffs = scroffsy + x; const int chr = src->screen[0][scroffs]; const int col = src->color[0][scroffs] & 15; if (col & 8) { const int v = 6 - ((xc * 2) & 6); switch ((src->charmem[0][chr * C64_CHR_SIZE + yb] >> v) & 3) { case 0: *d++ = src->bgcolor; break; case 1: *d++ = src->d022; break; case 2: *d++ = src->d023; break; case 3: *d++ = col; } } else { const int v = 7 - (xc & 7); if ((src->charmem[0][chr * C64_CHR_SIZE + yb] >> v) & 1) *d++ = src->color[0][scroffs]; else *d++ = src->bgcolor; } } } else { // Perform generic BITMAP conversion const int bmoffsy = y * src->ch_width * 8 + yb; if ((src->type & D64_FMT_MC) == D64_FMT_HIRES) // Hi-res bitmap for (xc = 0; xc < rwidth; xc++) { const int x = xc / 8; const int scroffs = scroffsy + x; const int bmoffs = bmoffsy + (x * 8); const int vshift = 7 - (xc & 7); *d++ = getPixel(src, bmoffs, scroffs, vshift, 0, yc); } else // Multicolor bitmap and variants for (xc = 0; xc < rwidth; xc++) { const int x = xc / 4; const int scroffs = scroffsy + x; const int bmoffs = bmoffsy + (x * 8); const int vshift = 6 - ((xc * 2) & 6); if (src->type & D64_FMT_ILACE) { switch (src->laceType) { case D64_ILACE_RES: *d++ = getPixel(src, bmoffs, scroffs, vshift, 0, yc); *d++ = getPixel(src, bmoffs, scroffs, vshift, 1, yc); break; default: return DMERR_NOT_SUPPORTED; } } else { *d++ = getPixel(src, bmoffs, scroffs, vshift, 0, yc); } } } dp += dst->pitch; } return DMERR_OK; } int dmC64ConvertBMP2Image(DMImage **pdst, const DMC64Image *src, const DMC64ImageFormat *fmt) { int res; DMImage *dst; if (pdst == NULL || src == NULL) return DMERR_NULLPTR; // Allocate image structure if ((*pdst = dst = dmImageAlloc(src->width, src->height, DM_IFMT_PALETTE, -1)) == NULL) return DMERR_MALLOC; // Set partial palette information dst->ncolors = C64_NCOLORS; dst->constpal = TRUE; // Convert if (fmt->convertFrom != NULL) res = fmt->convertFrom(dst, src, fmt); else res = dmC64ConvertGenericBMP2Image(dst, src, fmt); return res; } int dmC64DecodeBMP(DMC64Image **img, const Uint8 *buf, const size_t len, const size_t probeOffs, const size_t loadOffs, const DMC64ImageFormat **fmt, const DMC64ImageFormat *forced) { if (img == NULL) return DMERR_NULLPTR; // Check for forced format if (forced != NULL) *fmt = forced; else { // Nope, perform a generic probe if (probeOffs >= len) return DMERR_OUT_OF_DATA; if (dmC64ProbeBMP(buf + probeOffs, len - probeOffs, fmt) == DM_PROBE_SCORE_FALSE) return DMERR_NOT_SUPPORTED; } if (loadOffs >= len) return DMERR_INVALID_ARGS; if (*fmt == NULL) return DMERR_INVALID_DATA; // Allocate memory if ((*img = dmC64ImageAlloc(*fmt)) == NULL) return DMERR_MALLOC; // 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); }