Mercurial > hg > dmlib
view tools/lib64fmts.c @ 2043:cbb3463fea2a
Initial dabbling for SDL2 migration of the SW rendering / dmsimple.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 30 Nov 2018 06:56:38 +0200 |
parents | cf966e66c9af |
children | c27ed6465022 |
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" static int fmtProbeGigapaintHires(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len == fmt->size && dmCompareAddr16(buf, 0, fmt->addr)) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } // XXX TODO: Research what these values actually mean. It would seem probable // that these may not be static values at all, as there are 8 more that change // before actual image data, but do not seem to be used in the image itself. static const Uint8 fmtMicroIllustratorMagicID_1[] = { 0xff, 0x80, 0x69, 0x67, 0x14, 0x00, }; static const Uint8 fmtMicroIllustratorMagicID_2[] = { 0xe8, 0x03, 0xe8, 0x03, 0x40, 0x1f, }; static int fmtProbeMicroIllustrator(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len == fmt->size && memcmp(buf->data + 2, fmtMicroIllustratorMagicID_1, sizeof(fmtMicroIllustratorMagicID_1)) == 0 && memcmp(buf->data + 9, fmtMicroIllustratorMagicID_2, sizeof(fmtMicroIllustratorMagicID_2)) == 0 ) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static BOOL fmtEncodeMicroIllustrator(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { (void) op; (void) img; (void) fmt; memcpy(buf->data + 2, fmtMicroIllustratorMagicID_1, sizeof(fmtMicroIllustratorMagicID_1)); memcpy(buf->data + 9, fmtMicroIllustratorMagicID_2, sizeof(fmtMicroIllustratorMagicID_2)); return TRUE; } static int fmtProbeKoalaPainter(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int score = DM_PROBE_SCORE_FALSE; if (buf->len == 10003) score += DM_PROBE_SCORE_MAYBE; if (dmCompareAddr16(buf, 0, fmt->addr)) score += DM_PROBE_SCORE_MAYBE; return score; } static int fmtProbeKoalaPainterPacked(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { // Attempt to prevent misprobes of unpacked Koala and Run Paint if (buf->len > 30 && buf->len != 10006 && buf->len != 10003 && dmCompareAddr16(buf, 0, fmt->addr)) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } static int fmtProbeDoodle(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 32 && (dmCompareAddr16(buf, 0, 0x1c00) || dmCompareAddr16(buf, 0, 0x5c00))) { // Packed variant if (fmt->size == 0xfe && buf->len != fmt->size && buf->len != 10242) // Attempt to avoid misprobes of "Rainbow Painter (unpacked)" return DM_PROBE_SCORE_MAX; // Unpacked variant if (fmt->size != 0xfe && buf->len == fmt->size) return DM_PROBE_SCORE_MAX; } return DM_PROBE_SCORE_FALSE; } static int fmtDecodeStaticRLEMarkerMode2(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem; DMCompParams cfg; cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_2; cfg.rleMarkerB = fmt->size; if ((res = dmDecodeGenericRLEAlloc(&mem, buf, &cfg)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, &mem, fmt); out: dmGrowBufFree(&mem); return res; } static int fmtEncodeStaticRLEMarkerMode2(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; DMCompParams cfg; // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK) goto out; // And now RLE compress the data to the existing buffer cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_2; cfg.rleMarkerB = fmt->size; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 255; res = dmEncodeGenericRLE(buf, &tmp, &cfg); out: dmGrowBufFree(&tmp); return res; } static int fmtProbeDrazPaint20Packed(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { const Uint8 *ident = buf->data + 2; if (buf->len > 22 && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(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 DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem, tmp; DMCompParams cfg; cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMarkerB = buf->data[0x0d]; if ((res = dmDecodeGenericRLEAlloc(&mem, dmGrowBufConstCopyOffs(&tmp, buf, 0x0e), &cfg)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, &mem, fmt); out: dmGrowBufFree(&mem); return res; } static int fmtEncodeDrazPaintPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; DMCompParams cfg; const char *magicID = (fmt->format->type & D64_FMT_ILACE) ? "DRAZLACE! 1.0" : "DRAZPAINT 2.0"; // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK) goto out; // Analyze and setup RLE cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 255; dmGenericRLEAnalyze(&tmp, &cfg); // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) magicID, strlen(magicID)) || !dmGrowBufPutU8(buf, cfg.rleMarkerB)) { res = DMERR_MALLOC; goto out; } // And now RLE compress the data to the existing buffer res = dmEncodeGenericRLE(buf, &tmp, &cfg); out: dmGrowBufFree(&tmp); return res; } static int fmtProbeDrazLace10Packed(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 22 && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(buf->data + 2, "DRAZLACE! 1.0", 13) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static BOOL fmtDrazLaceGetLaceType(DMC64Image *img, const DMC64EncDecOp *op, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) fmt; if (buf != NULL) img->laceType = buf->data[op->offs] ? D64_ILACE_RES : D64_ILACE_COLOR; else img->laceType = D64_ILACE_RES; return TRUE; } static BOOL fmtDrazLaceSetLaceType(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { (void) fmt; buf->data[op->offs] = (img->laceType == D64_ILACE_RES) ? 1 : 0; return TRUE; } static const char *fmtBDP5MagicID = "BDP 5.00"; static int fmtProbeBDP5Packed(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 20 && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(buf->data + 2, fmtBDP5MagicID, strlen(fmtBDP5MagicID)) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDecodeBDP5Packed(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem, tmp; DMCompParams cfg; cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_WORD_RUNS | DM_RLE_ORDER_1; cfg.rleMarkerB = buf->data[8]; cfg.rleMarkerW = buf->data[9]; // Boogie Down Paint apparently is broken and stores one byte less // than it should in some cases so we need to do some crappy buffer // expansion here .. if (dmGrowBufCopyOffs(&tmp, buf, 10, 1) == NULL) return DMERR_MALLOC; tmp.len = tmp.size; if ((res = dmDecodeGenericRLEAlloc(&mem, &tmp, &cfg)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, &mem, fmt); out: dmGrowBufFree(&tmp); dmGrowBufFree(&mem); return res; } static int fmtEncodeBDP5Packed(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem; DMCompParams cfg; // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &mem, img, fmt)) != DMERR_OK) goto out; // Analyze and setup RLE cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_WORD_RUNS | DM_RLE_ORDER_1; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 255; cfg.rleMinCountW = 256; cfg.rleMaxCountW = 1024; dmGenericRLEAnalyze(&mem, &cfg); // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) fmtBDP5MagicID, strlen(fmtBDP5MagicID)) || !dmGrowBufPutU8(buf, cfg.rleMarkerB) || !dmGrowBufPutU8(buf, cfg.rleMarkerW)) { res = DMERR_MALLOC; goto out; } // And now RLE compress the data to the existing buffer res = dmEncodeGenericRLE(buf, &mem, &cfg); out: dmGrowBufFree(&mem); return res; } static const char *fmtGunPaintMagicID = "GUNPAINT (JZ) "; #define fmtGunPaintMagicLen (14) #define fmtGunPaintMagicOffs (0x3e8) static int fmtProbeGunPaint(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > fmtGunPaintMagicOffs + fmtGunPaintMagicLen && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(buf->data + fmtGunPaintMagicOffs + 2, fmtGunPaintMagicID, fmtGunPaintMagicLen) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static BOOL fmtEncodeGunPaint(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { (void) op; (void) img; (void) fmt; // Here we assume that the op triggering this function is // at the end of the oplist, so the memory is allocated, memcpy(buf->data + fmtGunPaintMagicOffs + 2, fmtGunPaintMagicID, fmtGunPaintMagicLen); return TRUE; } static int fmtProbeAmicaPaintPacked(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { size_t i, n; if (buf->len < 256 || !dmCompareAddr16(buf, 0, fmt->addr)) return DM_PROBE_SCORE_FALSE; // Interpaint Hi-Res gives a false positive // as do some GunPaint images .. if (buf->len == 9002 || fmtProbeGunPaint(buf, fmt) > DM_PROBE_SCORE_GOOD) return DM_PROBE_SCORE_FALSE; for (n = 0, i = 2; i < buf->len; i++) if (buf->data[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 DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem, tmp; DMCompParams cfg; // Amica Paint apparently is broken and stores one byte less than it should // so we need to do some crappy buffer expansion here .. if (dmGrowBufCopy(&tmp, buf, 1) == NULL) return DMERR_MALLOC; tmp.len = tmp.size; // Now do an RLE decode on the enlarged buffer cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMarkerB = 0xC2; if ((res = dmDecodeGenericRLEAlloc(&mem, &tmp, &cfg)) != DMERR_OK) goto out; // And finally decode to bitmap struct res = dmC64DecodeGenericBMP(img, &mem, fmt); out: dmGrowBufFree(&tmp); dmGrowBufFree(&mem); return res; } static int fmtEncodeAmicaPaintPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem; DMCompParams cfg; // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &mem, img, fmt)) != DMERR_OK) goto out; // And now RLE compress the data to the existing buffer cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMarkerB = 0xC2; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 255; res = dmEncodeGenericRLE(buf, &mem, &cfg); out: dmGrowBufFree(&mem); return res; } static int fmtProbeSaracenPaint(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if ((buf->len == 10219 || buf->len == 10220) && dmCompareAddr16(buf, 0, fmt->addr)) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } static Uint8 fmtGetPixelFLIDesigner( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { (void) rasterX; return dmC64GetGenericMCPixel(img, bmoffs, scroffs, shift, rasterY & 7, bitmap, 0, img->bgcolor); } static int fmtProbeBlackMailFLIPacked(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 16 && dmCompareAddr16(buf, 0, fmt->addr) && dmCompareAddr16(buf, 2 + 1, fmt->addr + buf->len - 3) && dmCompareAddr16(buf, 2 + 3, 0x7f3f)) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDecodeBlackMailFLIPacked(DMC64Image *img, const DMGrowBuf *psrc, const DMC64ImageFormat *fmt) { int res; DMGrowBuf dst, src; DMCompParams cfg; cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1 | DM_RLE_ZERO_COUNT_MAX | DM_RLE_BACKWARDS_INPUT | DM_RLE_BACKWARDS_OUTPUT | DM_OUT_CROP_END; cfg.rleMarkerB = psrc->data[0]; cfg.cropOutLen = 0x4442 - 2; // Crop to unpacked size - load address // Skip the RLE marker byte, packed data end address and unpacked data end address dmGrowBufConstCopyOffs(&src, psrc, 1 + 2 + 2); if ((res = dmDecodeGenericRLEAlloc(&dst, &src, &cfg)) != DMERR_OK) goto out; res = dmC64DecodeGenericBMP(img, &dst, fmt); out: dmGrowBufFree(&dst); return res; } static int fmtEncodeBlackMailFLIPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp1, tmp2; DMCompParams cfg; dmGrowBufInit(&tmp1); dmGrowBufInit(&tmp2); // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &tmp1, img, fmt)) != DMERR_OK) goto out; // And now RLE compress the data to the existing buffer cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1 | DM_RLE_ZERO_COUNT_MAX | DM_RLE_BACKWARDS_INPUT | DM_RLE_BACKWARDS_OUTPUT; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 256; // this format allows 256 byte runs with ZERO_COUNT_MAX dmGenericRLEAnalyze(&tmp1, &cfg); if ((res = dmEncodeGenericRLEAlloc(&tmp2, &tmp1, &cfg)) != DMERR_OK) goto out; // Now, finally we must put in the header etc. if (!dmGrowBufPutU8(buf, cfg.rleMarkerB) || !dmGrowBufPutU16LE(buf, fmt->addr + tmp2.len + 4) || !dmGrowBufPutU16LE(buf, 0x7f3f) || !dmGrowBufPut(buf, tmp2.data, tmp2.len)) { res = DMERR_MALLOC; goto out; } out: dmGrowBufFree(&tmp1); dmGrowBufFree(&tmp2); return res; } static Uint8 fmtGetPixelBlackMailFLI( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { const int vbank = rasterY & 7; (void) rasterX; return dmC64GetGenericMCPixel( img, bmoffs, scroffs, shift, vbank, bitmap, 0, img->extraData[0].data[rasterY] & 15); } static BOOL fmtTruePaintGetLaceType(DMC64Image *img, const DMC64EncDecOp *op, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) op; (void) buf; (void) fmt; img->laceType = D64_ILACE_RES; return TRUE; } static Uint8 fmtGetPixelTruePaint( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { (void) rasterX; (void) rasterY; return dmC64GetGenericMCPixel(img, bmoffs, scroffs, shift, 0, bitmap, 0, img->bgcolor); } static int fmtProbeTruePaintPacked(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { // The beginning/un-changing part of the BASIC bootstrap and // relocation of decompression code static const Uint8 magicID[] = { 0x0b, 0x08, 0x09, 0x00, 0x9e, 0x32, 0x30, 0x35, 0x39, 0x00, 0xa2, 0x00, 0x78, 0xbd, 0x1c, 0x08, 0x9d, 0xf5, 0x00, 0xe8, 0xd0, 0xf7, 0xe6, 0x01, 0x4c, 0x01, 0x01, 0xa5, 0xfe, 0xd0, 0x02, 0xc6, 0xff, 0xc6, 0xfe }; if (buf->len >= 320 && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(buf->data + 2, magicID, sizeof(magicID)) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } // // Based on disassembly of the depacker routine. Encoding seems to be // some kind of "improved RLE" variant with different modes and a // simplistic "codebook". // static int fmtTruePaintGetByte(DMGrowBuf *src, Uint8 *data, const int mode) { if (!dmGrowBufGetU8(src, data)) { return dmError(DMERR_INVALID_DATA, "TruePaintRLE: Out of input data (N=%d)\n", mode); } else return DMERR_OK; } static int fmtDecodeTruePaintPacked(DMC64Image *img, const DMGrowBuf *psrc, const DMC64ImageFormat *fmt) { int res = DMERR_OK; const Uint8 *codeBook1, *codeBook2; DMGrowBuf dst, src; DMCompParams cfg; Uint8 data; // 1b7e-67e8 decoded by original depacker // 1c00-67e8 is the actual area used tho cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BACKWARDS_OUTPUT | DM_RLE_BACKWARDS_INPUT | DM_OUT_CROP_END; cfg.rleMarkerB = 0xfe; cfg.cropOutLen = 0x67e8 - 0x1c00; // Codebooks: #1 is trampoline table markers, #2 is RLE data table codeBook1 = psrc->data + 0x81 - 2; codeBook2 = psrc->data + 0x85 - 2; // Allocate output buffer if ((res = dmGrowBufAlloc(&dst, 64*1024, 4*1024)) != DMERR_OK) goto out; // As we need to modify the offs, etc. but not the data, // we will just make a shallow copy of the DMGrowBuf struct dmGrowBufConstCopy(&src, psrc); dmSetupRLEBuffers(&dst, &src, &cfg); while ((res = fmtTruePaintGetByte(&src, &data, -1)) == DMERR_OK) { unsigned int count = 1; BOOL found = FALSE; for (int n = 0; n < 8; n++) if (codeBook1[n] == data && !found) { found = TRUE; switch (n) { case 4: // Y = 4, JTO = $0B if ((res = fmtTruePaintGetByte(&src, &data, n)) != DMERR_OK) goto out; count = data; if (data == 0) goto finish; // fallthrough case 1: // Y = 1, JTO = $17 count += 2; // fallthrough case 0: // Y = 0, JTO = $19 if ((res = fmtTruePaintGetByte(&src, &data, n)) != DMERR_OK) goto out; break; case 2: // Y = 2, JTO = $07 if ((res = fmtTruePaintGetByte(&src, &data, n)) != DMERR_OK) goto out; count = data; // fallthrough case 3: // Y = 3, JTO = $0B count += 2; data = 0; break; default: // Y = [5..8], JTO = $00 count++; data = codeBook2[n]; break; } } if ((res = dmGenericRLEOutputRun(&dst, &cfg, data, count)) != DMERR_OK) goto out; } finish: dmFinishRLEBuffers(&dst, &src, &cfg); res = dmC64DecodeGenericBMP(img, &dst, fmt); out: dmGrowBufFree(&dst); return res; } #define XX2_MIN_SIZE 4000 #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) static int fmtProbeFormatXX2(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len >= XX2_MIN_SIZE && buf->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 DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; // 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. if (dmGrowBufCopy(&tmp, buf, buf->len < XX2_MIN_SIZE ? XX2_MIN_SIZE - buf->len : 0) == NULL) return DMERR_MALLOC; tmp.len = tmp.size; res = dmC64DecodeGenericBMP(img, &tmp, fmt); dmGrowBufFree(&tmp); return res; } static int fmtProbeCosmosDesignsHiresManager(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 32 && dmCompareAddr16(buf, 0, fmt->addr)) { // Packed variant if (fmt->size == 0 && dmCompareAddr16(buf, 2, fmt->addr + buf->len - 3) && dmCompareAddr16(buf, 4, 0x7ff2)) return DM_PROBE_SCORE_MAX; // Unpacked variant if (fmt->size != 0 && fmt->size == buf->len) return DM_PROBE_SCORE_GOOD; } return DM_PROBE_SCORE_FALSE; } #define FUNPAINT2_HEADER_SIZE (0x10) static const char *fmtFunPaint2MagicID = "FUNPAINT (MT) "; static int fmtProbeFunPaint2(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 30 && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(buf->data + 2, fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) == 0) { // Unpacked variant if (fmt->size != 0 && buf->data[14 + 2] == 0) return DM_PROBE_SCORE_MAX; // Packed variant if (fmt->size == 0 && buf->data[14 + 2] != 0) return DM_PROBE_SCORE_MAX; } return DM_PROBE_SCORE_FALSE; } static int fmtDecodeFunPaint2(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; // Check if the data is compressed if (buf->data[14]) { DMGrowBuf mem; DMCompParams cfg; cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMarkerB = buf->data[15]; dmGrowBufCopyOffs(&tmp, buf, FUNPAINT2_HEADER_SIZE, 1); tmp.len = tmp.size; if ((res = dmDecodeGenericRLEAlloc( &mem, &tmp, &cfg)) == DMERR_OK) res = dmC64DecodeGenericBMP(img, &mem, fmt); dmGrowBufFree(&mem); dmGrowBufFree(&tmp); } else { res = dmC64DecodeGenericBMP(img, dmGrowBufConstCopyOffs(&tmp, buf, FUNPAINT2_HEADER_SIZE), fmt); } return res; } static int fmtEncodeFunPaint2Unpacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) || !dmGrowBufPutU8(buf, 0) || // 0 == unpacked variant !dmGrowBufPutU8(buf, 0)) // RLE marker byte (not used in unpacked) return DMERR_MALLOC; return dmC64EncodeGenericBMP(FALSE, buf, img, fmt); } static int fmtEncodeFunPaint2Packed(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf mem; DMCompParams cfg; // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &mem, img, fmt)) != DMERR_OK) goto out; // Analyze and setup RLE cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 255; dmGenericRLEAnalyze(&mem, &cfg); // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) || !dmGrowBufPutU8(buf, 1) || // non-zero == packed variant !dmGrowBufPutU8(buf, cfg.rleMarkerB)) { res = DMERR_MALLOC; goto out; } // And now RLE compress the data to the existing buffer res = dmEncodeGenericRLE(buf, &mem, &cfg); out: dmGrowBufFree(&mem); return res; } static Uint8 fmtGetPixelFunPaint2( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { const int vbank = (rasterY & 7) + (bitmap * 8); (void) rasterX; return dmC64GetGenericMCPixel( img, bmoffs, scroffs, shift, vbank, bitmap, 0, img->extraData[0].data[rasterY] & 15); } static Uint8 fmtGetPixelBFLI( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { const int vbb = rasterY < 200 ? 0 : 1; const int vbank = (rasterY & 7) + (vbb * 8); (void) bitmap; (void) rasterX; return dmC64GetGenericMCPixel( img, bmoffs & 0x1fff, scroffs & 0x3ff, shift, vbank, vbb, 0, img->bgcolor); } static Uint8 fmtGetPixelPentelPaint( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { const int ry = rasterY / C64_SPR_HEIGHT_PX; const int yd = rasterY % C64_SPR_HEIGHT_PX; const int rx = rasterX / C64_SPR_WIDTH_PX; const int xd = rasterX % C64_SPR_WIDTH_PX; const int offs = (ry * 8 + rx) * C64_SPR_SIZE + (yd * C64_SPR_WIDTH_UT) + (xd / 8); const int mask = 1 << (7 - (rasterX & 7)); Uint8 color1 = dmC64GetGenericSCPixel(img, bmoffs, scroffs, shift, 0, bitmap, 0); Uint8 color2 = img->extraData[0].data[offs] & mask ? 0x0f : 0; Uint8 color3 = img->extraData[0].data[offs + C64_SPR_SIZE * 155] & mask ? img->d022 : 0; return color3 ? color3 : ( color2 ? color2 : color1 ); } static Uint8 fmtGetPixelHCB( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { const int vbank = (rasterY / 4) & 1; (void) bitmap; (void) rasterX; return dmC64GetGenericMCPixel( img, bmoffs, scroffs, shift, vbank, 0, vbank, img->extraData[0].data[rasterY / 5] & 15); } static Uint8 fmtGetPixelCrestHIFLIorCDHM( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { (void) rasterX; return dmC64GetGenericSCPixel(img, bmoffs, scroffs, shift, rasterY & 7, bitmap, 0); } static BOOL fmtECIGetLaceType(DMC64Image *img, const DMC64EncDecOp *op, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) op; (void) buf; (void) fmt; img->laceType = D64_ILACE_COLOR; return TRUE; } static Uint8 fmtGetPixelECI( const DMC64Image *img, const int bmoffs, const int scroffs, const int shift, const int bitmap, const int rasterX, const int rasterY) { const int vbank = rasterY & 7; Uint8 c1 = dmC64GetGenericSCPixel(img, bmoffs, scroffs, shift, vbank , 0, 0), c2 = dmC64GetGenericSCPixel(img, bmoffs, scroffs, shift, vbank + 8, 1, 0); (void) bitmap; (void) rasterX; return (c1 * C64_NCOLORS) + c2; } static int fmtConvertECIBMP2Image(DMImage *dst, const DMC64Image *src, const DMC64ImageFormat *fmt, const DMC64ImageConvSpec *spec) { if (!dmSetMixedColorC64Palette(dst)) return DMERR_MALLOC; return dmC64ConvertGenericBMP2Image(dst, src, fmt, spec); } static int fmtProbeECIPacked(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { size_t i, n; // XXX TODO: Perhaps count statistics about used byte values // and compare to value in buf[2] which is the RLE marker if (buf->len < 128 || !dmCompareAddr16(buf, 0, fmt->addr) || // Try to avoid misprobe of Crest Hires FLI Designer and Cosmos Design format buf->len == 16386 || buf->len == 16385 || // Face Painter buf->len == 10004) return DM_PROBE_SCORE_FALSE; for (n = 0, i = 3; i < buf->len; i++) if (buf->data[i] == buf->data[2]) 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 fmtDecodeECIPacked(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; DMGrowBuf mem; DMCompParams cfg; cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMarkerB = buf->data[0]; if ((res = dmDecodeGenericRLEAlloc( &mem, dmGrowBufConstCopyOffs(&tmp, buf, 1), &cfg)) == DMERR_OK) res = dmC64DecodeGenericBMP(img, &mem, fmt); dmGrowBufFree(&mem); return res; } static int fmtEncodeECIPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; DMCompParams cfg; // Encode the data to temp buffer if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK) goto out; // Analyze and setup RLE cfg.func = fmt->name; cfg.type = DM_COMP_RLE_MARKER; cfg.flags = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1; cfg.rleMinCountB = 4; cfg.rleMaxCountB = 255; dmGenericRLEAnalyze(&tmp, &cfg); // Add the header bits if (!dmGrowBufPutU8(buf, cfg.rleMarkerB)) { res = DMERR_MALLOC; goto out; } // And now RLE compress the data to the existing buffer res = dmEncodeGenericRLE(buf, &tmp, &cfg); out: dmGrowBufFree(&tmp); return res; } // // Helper macros for defining screen memory layouts // common for several FLI type image formats // #define DEF_SCREEN_RAM(start, oindex, bindex, osize, bsize) \ { DO_COPY, DS_SCREEN_RAM, (start) + ((osize) * (oindex)), (bindex), (bsize), 0, NULL, NULL } #define DEF_SCREEN_RAMS_8(start, sindex, osize, bsize) \ DEF_SCREEN_RAM((start), 0, (sindex + 0), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 1, (sindex + 1), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 2, (sindex + 2), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 3, (sindex + 3), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 4, (sindex + 4), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 5, (sindex + 5), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 6, (sindex + 6), (osize), (bsize)), \ DEF_SCREEN_RAM((start), 7, (sindex + 7), (osize), (bsize)) // // Many formats actually share memory layout and other specs, and there are // packed and unpacked versions of several formats. We'll reuse these here // through this common formats data array, referred from dmC64ImageFormats[] // DMC64ImageCommonFormat dmC64CommonFormats[] = { { // #0: Koala Painter type memory layout D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2328, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2710, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #1: Black Mail FLI Graph D64_FMT_MC | D64_FMT_FLI, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelBlackMailFLI, { { DO_COPY , DS_EXTRA_DATA , 0x0000, 0, 200, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x0100, 0, 0, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x0500, 0, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x2500, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #2: Art Studio etc. Hires D64_FMT_HIRES, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #3: FunPaint II D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, C64_SCR_WIDTH, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelFunPaint2, { DEF_SCREEN_RAMS_8(0x0000, 0, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x2000, 0, 0 , 0, NULL, NULL }, { DO_COPY , DS_EXTRA_DATA , 0x3f48, 0, 100, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x4000, 0, 0 , 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x43e8, 8, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x63e8, 1, 0 , 0, NULL, NULL }, { DO_COPY , DS_EXTRA_DATA , 0x8328, 0, 100, 100, NULL, NULL }, { DO_FUNC , 0 , 0 , 0, 1 , 0, fmtTruePaintGetLaceType, NULL }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL }, } }, { // #4: DrazPaint 1.x & 2 D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2740, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #5: DrazLace 1.0 D64_FMT_MC | D64_FMT_ILACE, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2740, 0, 0, 0, NULL, NULL }, { DO_FUNC , 0 , 0x2742, 0, 1, 0, fmtDrazLaceGetLaceType, fmtDrazLaceSetLaceType }, { DO_COPY , DS_BITMAP_RAM , 0x2800, 1, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #6: TruePaint D64_FMT_MC | D64_FMT_ILACE, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelTruePaint, { { DO_COPY , DS_SCREEN_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x03e8, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x2400, 1, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x4400, 1, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x4800, 0, 0, 0, NULL, NULL }, { DO_FUNC , 0 , 0x0000, 0, 0, 0, fmtTruePaintGetLaceType, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #7: ECI Graphic Editor Hires FLI D64_FMT_HIRES | D64_FMT_FLI, C64_SCR_WIDTH, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, fmtConvertECIBMP2Image, NULL, fmtGetPixelECI, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x2000, 0, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x4000, 1, 0, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x6000, 8, 0x400, 0), { DO_FUNC , 0 , 0 , 0, 0, 0, fmtECIGetLaceType, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #8: Cosmos Designs Hires Manager D64_FMT_HIRES | D64_FMT_FLI, C64_SCR_WIDTH, 24*8, // Actually 296 x 192 (=24*8) C64_SCR_CH_WIDTH, 24, NULL, NULL, fmtGetPixelCrestHIFLIorCDHM, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x2000, 0, 0x400, 0), { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #9: FBI Crew FLI Designer 1.x & 2.0 D64_FMT_MC | D64_FMT_FLI, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelFLIDesigner, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x0400, 0, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x2400, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, { // #10: Doodle D64_FMT_HIRES, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, }; // // Array with data for supported formats // DMC64ImageFormat dmC64ImageFormats[] = { { "d2p", "DrazPaint 1.4/2.0 (packed)", 0x5800, 0, DM_FMT_RDWR, fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, fmtEncodeDrazPaintPacked, { }, &dmC64CommonFormats[4] }, { "drp", "DrazPaint (unpacked)", 0x5800, 10051, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[4] }, { "dlp", "DrazLace 1.0 (packed)", 0x5800, 0, DM_FMT_RDWR, fmtProbeDrazLace10Packed, fmtDecodeDrazPaintPacked, fmtEncodeDrazPaintPacked, { }, &dmC64CommonFormats[5] }, { "drl", "DrazLace 1.0 (unpacked)", 0x5800, 18242, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[5] }, { "bdp5", "Boogie Down Paint 5 (packed)", 0x5000, 0, DM_FMT_RDWR, fmtProbeBDP5Packed, fmtDecodeBDP5Packed, fmtEncodeBDP5Packed, { }, &dmC64CommonFormats[0] // Memory format is same as Koala }, { "vid", "Vidcom 64 (unpacked)", 0x5800, 10050, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x07e8, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, }, }, NULL }, { "p64", "Picasso 64 (unpacked)", 0x1800, 10050, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x07fe, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, }, }, NULL }, { "mci", "Truepaint (unpacked)", 0x9c00, 19434, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[6] }, { "mcip", "Truepaint (packed)", 0x0801, 0, DM_FMT_RD, fmtProbeTruePaintPacked, fmtDecodeTruePaintPacked, NULL, { }, &dmC64CommonFormats[6] }, { "kla", "Koala Painter (unpacked)", 0x6000, 10003, DM_FMT_RDWR, fmtProbeKoalaPainter, NULL, NULL, { }, &dmC64CommonFormats[0] }, { "klp", "Koala Painter (packed)", 0x6000, 0xfe, DM_FMT_RDWR, // size is abused for RLE marker byte fmtProbeKoalaPainterPacked, fmtDecodeStaticRLEMarkerMode2, fmtEncodeStaticRLEMarkerMode2, { }, &dmC64CommonFormats[0] }, { "aas", "Advanced Art Studio (unpacked)", 0x2000, 10018, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_D020 , 0x2328, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2329, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2338, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, }, }, NULL }, { "ims", "Image System MC (unpacked)", 0x3c00, 10218, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x23ff, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x2400, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "mil", "Micro Illustrator (unpacked)", 0x18dc, 10022, DM_FMT_RDWR | DM_FMT_BROKEN, fmtProbeMicroIllustrator, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 20 + 0 , 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 20 + 1000 , 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 20 + 2000 , 0, 0, 0, NULL, NULL }, // XXX TODO: Unknown where the background color is set, so default to 0x00 { DO_SET_OP , DS_BGCOL , 0x00 , 0, 0, 0, NULL, NULL }, { DO_FUNC , 0 , 0 , 0, 0, 0, NULL, fmtEncodeMicroIllustrator }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "cdu", "CDU-Paint (unpacked)", 0x7eef, 10277, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000 + 0x111, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40 + 0x111, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2328 + 0x111, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2710 + 0x111, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "rbp", "Rainbow Painter (unpacked)", 0x5c00, 10242, DM_FMT_RDWR | DM_FMT_BROKEN, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL }, // XXX TODO: Not sure if the background color is hardcoded .. { DO_SET_OP , DS_BGCOL , 0x00 , 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "sar", "Saracen Paint (unpacked)", 0x7800, 10219, DM_FMT_RDWR, fmtProbeSaracenPaint, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x7800 - 0x7800, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x7bf0 - 0x7800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x7c00 - 0x7800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x9c00 - 0x7800, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "blp", "Blazing Paddles (unpacked)", 0xA000, 10242, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, // Both d020 and bgcolor confirmed by tests { DO_SET_MEM_LO , DS_D020 , 0x1f7f, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x1f80, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x2000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "pmg", "Paint Magic crippled MC (unpacked)", 0x3f8e, 9332, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x4000 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x6000 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_D020 , 0x5f40 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_COLOR_RAM , 0x5f43 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x5f44 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "a64", "Wigmore Artist 64 (unpacked)", 0x4000, 10242, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x2000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_D020 , 0x27fe, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x27ff, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "ami", "Amica Paint (packed)", 0x4000, 0, DM_FMT_RDWR, fmtProbeAmicaPaintPacked, fmtDecodeAmicaPaintPacked, fmtEncodeAmicaPaintPacked, { }, &dmC64CommonFormats[0] }, { "rpm", "Run Paint (unpacked)", 0x6000, 10006, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[0] }, { "ipc", "Interpaint MC (unpacked)", 0x4000, 10003, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[0] }, { "art", "Art Studio (unpacked)", 0x2000, 9009, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[2] }, { "iph", "Interpaint (unpacked)", 0x4000, 9002, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[2] }, { "dd", "Doodle (unpacked)", 0x1c00, 9218, DM_FMT_RDWR, fmtProbeDoodle, NULL, NULL, { }, &dmC64CommonFormats[10] }, { "jj", "Doodle (packed)", 0x5c00, 0xfe, DM_FMT_RDWR, // size is abused for RLE marker byte fmtProbeDoodle, fmtDecodeStaticRLEMarkerMode2, fmtEncodeStaticRLEMarkerMode2, { }, &dmC64CommonFormats[10] }, { "mon", "Monomagic (unpacked)", 0x2000, 8194, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_HIRES, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_OP , DS_SCREEN_RAM , 0xCF , 0, 0, 0, NULL, NULL }, // Default colors used by MM are --^^ { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "hir", "Plain hires (unpacked)", 0x2000, 8002, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_HIRES, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_OP , DS_SCREEN_RAM , 0xF0 , 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "gih", "Gigapaint hires (unpacked)", 0x6000, 8002, DM_FMT_RDWR, fmtProbeGigapaintHires, NULL, NULL, { D64_FMT_HIRES, C64_SCR_WIDTH , C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_OP , DS_SCREEN_RAM , 0x0F , 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "bfli", "Pu-239 Big FLI/BFLI (unpacked)", 0x3bff, 33795, DM_FMT_RD, NULL, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT * 2, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelBFLI, { { DO_COPY , DS_COLOR_RAM , 0x0001, 0, 0x400 , 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x0401, 0, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x2401, 0, 0x2000, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x4401, 8, 0x400, 0x400), { DO_COPY , DS_BITMAP_RAM , 0x6401, 1, 0x2000, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL }, } }, NULL }, { "bml", "Black Mail FLI (unpacked)", 0x3b00, 17474, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[1] }, { "bmlp", "Black Mail FLI (packed)", 0x38f0, 0, DM_FMT_RDWR, fmtProbeBlackMailFLIPacked, fmtDecodeBlackMailFLIPacked, fmtEncodeBlackMailFLIPacked, { }, &dmC64CommonFormats[1] }, { "fd1", "FBI Crew FLI Designer 1.1 (unpacked)", 0x3c00, 17409, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[9] }, { "fd2", "FLI Designer 2 (unpacked)", 0x3ff0, 17409, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[9] }, { "eci", "ECI Graphic Editor 1.0 (unpacked)", 0x4000, 32770, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[7] }, { "ecp", "ECI Graphic Editor 1.0 (packed)", 0x4000, 0, DM_FMT_RDWR, fmtProbeECIPacked, fmtDecodeECIPacked, fmtEncodeECIPacked, { }, &dmC64CommonFormats[7] }, { "fpt", "Face Painter (unpacked)", 0x4000, 10004, DM_FMT_RDWR, NULL, NULL, NULL, { // Almost same layout as Koala Painter, but FPT has D020 D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2328, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2710, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_D020 , 0x2711, 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "xx1", "Unknown $2000 format (unpacked)", 0x2000, 10242, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x2000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL }, { DO_SET_OP , DS_BGCOL , 0x00 , 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "xx2", "Unknown $2000 format (unpacked)", 0x2000, 0, DM_FMT_RDWR, fmtProbeFormatXX2, fmtDecodeFormatXX2, NULL, { D64_FMT_MC, XX2_WIDTH_CH * 4, XX2_HEIGHT_CH * 8, XX2_WIDTH_CH , XX2_HEIGHT_CH, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, XX2_BSIZE, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , XX2_BSIZE, 0, XX2_SIZE, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , XX2_BSIZE + XX2_SIZE, 0, XX2_SIZE, 0, NULL, NULL }, { DO_SET_OP , DS_BGCOL , 11 , 0, 0, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "fp2", "FunPaint II (unpacked)", 0x3ff0, 33694, DM_FMT_RDWR, fmtProbeFunPaint2, fmtDecodeFunPaint2, fmtEncodeFunPaint2Unpacked, { }, &dmC64CommonFormats[3] }, { "fp2p", "FunPaint II (packed)", 0x3ff0, 0, DM_FMT_RDWR, fmtProbeFunPaint2, fmtDecodeFunPaint2, fmtEncodeFunPaint2Packed, { }, &dmC64CommonFormats[3] }, { "gun", "GunPaint (unpacked)", 0x4000, 0, DM_FMT_RDWR, fmtProbeGunPaint, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, C64_SCR_WIDTH, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelFunPaint2, // The format is essentially same as FP2 { DEF_SCREEN_RAMS_8(0x0000, 0, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x2000, 0, 0 , 0, NULL, NULL }, { DO_COPY , DS_EXTRA_DATA , 0x3f4f, 0, 177, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x4000, 0, 0 , 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x4400, 8, 0x400, 0), { DO_COPY , DS_BITMAP_RAM , 0x6400, 1, 0 , 0, NULL, NULL }, // GunPaint does not store the last 3 d021 values .. so set them to black // XXX TODO: According to some, the last 4 should be same .. { DO_SET_MEM , DS_EXTRA_DATA , 0 , 0, 3 , 20+177, NULL, NULL }, { DO_FUNC , 0 , 0x2742, 0, 1 , 0, fmtTruePaintGetLaceType, NULL }, { DO_FUNC , 0 , fmtGunPaintMagicOffs, 0, fmtGunPaintMagicLen, 0, NULL, fmtEncodeGunPaint }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL }, } }, NULL }, { "hcb", "Half Char Bitmap (unpacked)", 0x5000, 12148, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI, C64_SCR_WIDTH / 2, C64_SCR_HEIGHT, C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelHCB, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_COLOR_RAM , 0x0400, 1, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x0800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_SCREEN_RAM , 0x0c00, 1, 0, 0, NULL, NULL }, { DO_COPY , DS_BITMAP_RAM , 0x1000, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_EXTRA_DATA , 0x2f40, 0, C64_SCR_HEIGHT / 4, 0, NULL, NULL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "pen", "Pentel Paint (unpacked)", 0x4800, 19845, DM_FMT_RD | DM_FMT_BROKEN, NULL, NULL, NULL, { D64_FMT_HIRES | D64_FMT_FLI, 192, C64_SCR_HEIGHT, 24, C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelPentelPaint, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, { DO_SET_OP , DS_SCREEN_RAM , 0x10 , 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_BGCOL , 0x9580 - 0x4800, 0, 0, 0, NULL, NULL }, { DO_SET_MEM_LO , DS_D022 , 0x9581 - 0x4800, 0, 0, 0, NULL, NULL }, // Sprite color { DO_SET_MEM_LO , DS_COLOR_RAM , 0x9582 - 0x4800, 0, 0, 0, NULL, NULL }, { DO_COPY , DS_EXTRA_DATA , 0x5ac0 - 0x4800, 0, C64_SPR_SIZE * 235, 0, NULL, NULL }, // Sprite data { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "chid", "Crest Hires FLI Designer (unpacked)", 0x4000, 16386, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_HIRES | D64_FMT_FLI, C64_SCR_WIDTH, C64_SCR_HEIGHT, // Actually 296 x 112 (=14*8) C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT, NULL, NULL, fmtGetPixelCrestHIFLIorCDHM, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL }, DEF_SCREEN_RAMS_8(0x2000, 0, 0x400, 0), { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL }, } }, NULL }, { "cdhm", "Cosmos Designs Hires Manager (unpacked)", 0x4000, 16385, DM_FMT_RDWR, fmtProbeCosmosDesignsHiresManager, NULL, NULL, { }, &dmC64CommonFormats[8] }, }; const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]);