Mercurial > hg > dmlib
view tools/lib64gfx.c @ 1401:aaed8fa9a11f
Fix jssASCIItoStr() bounds check.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 30 Oct 2017 18:01:25 +0200 |
parents | 7acadc08bc72 |
children | a9afb2ad39cb |
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-2017 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 dmC64Palette[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_GET_ADDR_LO(addr) ((addr) & 0xff) #define DM_GET_ADDR_HI(addr) (((addr) >> 8) & 0xff) 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) { for (int i = 0; i < img->nbanks; i++) { dmFree(img->color[i]); dmFree(img->bitmap[i]); dmFree(img->screen[i]); dmFree(img->charmem[i]); } 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) { *buf = 0; if (type & D64_FMT_FLI) strncat(buf, "FLI ", len - strlen(buf)); strncat(buf, (type & D64_FMT_MC) ? "MCol " : "HiRes ", len - strlen(buf)); if (type & D64_FMT_ILACE) strncat(buf, "Ilace ", len - strlen(buf)); if (type & D64_FMT_CHAR) strncat(buf, "CharMap", len - strlen(buf)); 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(Uint8 **mem, Uint8 **pdstEnd, const Uint8 *src, const Uint8 *srcEnd, const Uint8 rleMarker) { Uint8 *dst, *dstEnd; if ((*mem = dmMalloc(C64_RAM_SIZE)) == NULL) return DMERR_MALLOC; 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; } *pdstEnd = dst; 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; Uint8 *mem = NULL, *end; if ((res = dmDecodeGenericRLE(&mem, &end, buf + 0x0e, buf + len, *(buf + 0x0d))) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, mem, end - mem + 1, fmt); out: dmFree(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; Uint8 *mem = NULL, *end; if ((res = dmDecodeGenericRLE(&mem, &end, buf, buf + len, 0xC2)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, mem, end - mem + 1, fmt); out: dmFree(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; Uint8 *mem = NULL, *end; if ((res = dmDecodeGenericRLE(&mem, &end, buf + FUNPAINT2_HEADER_SIZE, buf + len, *(buf + 15))) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, mem, end - mem + 1, fmt); out: dmFree(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) { 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; int res; // 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 error; } // Perform encoding for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; Uint8 *dst = 2 + 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 error; // 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 error; } // Do we need to reallocate some more space? chksize = op->offs + size + 2; 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 error; } } 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 error; } 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 error; } 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 error; } 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 error; } /* 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; } } buf[0] = DM_GET_ADDR_LO(fmt->addr); buf[1] = DM_GET_ADDR_HI(fmt->addr); *pbuf = buf; return DMERR_OK; error: dmFree(buf); *pbuf = NULL; *plen = 0; 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) { Uint8 *dp = dst->data; int yc; DMC64GetPixelFunc getPixel; // Sanity check arguments if (dst == NULL || src == NULL) return DMERR_NULLPTR; if (dst->width < src->width || dst->height < src->height) return DMERR_INVALID_DATA; 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 for (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 palette dst->pal = (DMColor *) &dmC64Palette; 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_INVALID_DATA; if (dmC64ProbeBMP(buf + probeOffs, len - probeOffs, fmt) == DM_PROBE_SCORE_FALSE) return DMERR_INVALID_DATA; } 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); }