Mercurial > hg > dmlib
view tools/lib64fmts.c @ 2501:62b7dfd5bfa5
Cosmetic.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 28 Apr 2020 22:31:45 +0300 |
parents | 09082816665d |
children | 4dbb6572622d |
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-2020 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include "lib64gfx.h" #define DM_MEMCMP_SIZE(mptr, mcmp) memcmp((mptr), (mcmp), sizeof(mcmp)) #define DM_MEMCMP_LEN(mptr, mcmp) memcmp((mptr), (mcmp), strlen(mcmp)) // Basic probe, but return MAX score for this format 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 fmtMicroIllustrator_MagicID_1[] = { 0xff, 0x80, 0x69, 0x67, 0x14, 0x00, }; static const Uint8 fmtMicroIllustrator_MagicID_2[] = { 0xe8, 0x03, 0xe8, 0x03, 0x40, 0x1f, }; static int fmtProbeMicroIllustrator(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len == fmt->size && DM_MEMCMP_SIZE(buf->data + 2, fmtMicroIllustrator_MagicID_1) == 0 && DM_MEMCMP_SIZE(buf->data + 9, fmtMicroIllustrator_MagicID_2) == 0 ) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtEncodeMicroIllustrator(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { (void) op; (void) img; (void) fmt; memcpy(buf->data + 2, fmtMicroIllustrator_MagicID_1, sizeof(fmtMicroIllustrator_MagicID_1)); memcpy(buf->data + 9, fmtMicroIllustrator_MagicID_2, sizeof(fmtMicroIllustrator_MagicID_2)); return DMERR_OK; } static const Uint8 fmtMarqPETSCII_ID1[] = { 0x01, 0x08, 0x0b, 0x08, 0xef, 0x00, 0x9e, 0x32, 0x30, 0x36, 0x31, 0x00, 0x00, 0x00, 0xa9, 0x0b, 0x8d, 0x11, 0xd0, }; static const Uint8 fmtMarqPETSCII_ID2[] = { 0xa9, 0x00, 0x8d, 0x18, 0xd0, // lda #? : sta $d018 0xa9, 0x00, 0x8d, 0x20, 0xd0, // lda #? : sta $d020 0xa9, 0x00, 0x8d, 0x21, 0xd0, // lda #? : sta $d021 }; static const Uint8 fmtMarqPETSCII_ID3[] = { 0xa2, 0x00, 0xa0, 0xfa, 0xbd, 0x61, 0x08, 0x9d, 0x00, 0x04, 0xbd, 0x5b, 0x09, 0x9d, 0xfa, 0x04, 0xbd, 0x55, 0x0a, 0x9d, 0xf4, 0x05, 0xbd, 0x4f, 0x0b, 0x9d, 0xee, 0x06, 0xbd, 0x49, 0x0c, 0x9d, 0x00, 0xd8, 0xbd, 0x43, 0x0d, 0x9d, 0xfa, 0xd8, 0xbd, 0x3d, 0x0e, 0x9d, 0xf4, 0xd9, 0xbd, 0x37, 0x0f, 0x9d, 0xee, 0xda, 0xe8, 0x88, 0xd0, 0xcc, 0xa9, 0x1b, 0x8d, 0x11, 0xd0, 0x4c, 0x5e, 0x08, }; static int fmtProbeMarqPETSCII(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { (void) fmt; if (buf->len == 2098 && DM_MEMCMP_SIZE(buf->data, fmtMarqPETSCII_ID1) == 0 && DM_MEMCMP_SIZE(buf->data + sizeof(fmtMarqPETSCII_ID1) + sizeof(fmtMarqPETSCII_ID2), fmtMarqPETSCII_ID3) == 0 ) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDecodeHiresPETSCIICharsetData(const DMC64EncDecOp *op, DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) fmt; Uint8 val; switch (buf->data[op->offs]) { case 0x14: val = 0; break; // upper case case 0x17: val = 1; break; // lower case default: return DMERR_INVALID_DATA; } img->extraInfo[D64_EI_CHAR_CASE] = val; img->extraInfo[D64_EI_MODE] = D64_FMT_HIRES | D64_FMT_CHAR; img->extraInfo[D64_EI_CHAR_CUSTOM] = 0; return DMERR_OK; } static int fmtEncodeHiresPETSCIICharsetData( const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { Uint8 val; (void) fmt; switch (img->extraInfo[D64_EI_CHAR_CASE]) { case 0: val = 0x14; break; case 1: val = 0x17; break; default: return DMERR_INVALID_DATA; } buf->data[op->offs + 2] = val; return DMERR_OK; } static int fmtEncodeMarqPETSCIIData(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { memcpy(buf->data, fmtMarqPETSCII_ID1, sizeof(fmtMarqPETSCII_ID1)); memcpy(buf->data + sizeof(fmtMarqPETSCII_ID1), fmtMarqPETSCII_ID2, sizeof(fmtMarqPETSCII_ID2)); memcpy(buf->data + sizeof(fmtMarqPETSCII_ID1) + sizeof(fmtMarqPETSCII_ID2), fmtMarqPETSCII_ID3, sizeof(fmtMarqPETSCII_ID3)); return fmtEncodeHiresPETSCIICharsetData(op, buf, img, fmt); } static const Uint8 fmtPetsciiKrisszHu_ID1[] = { 0x01, 0x08, 0x0b, 0x08, 0x0A, 0x00, 0x9E, 0x32, 0x30, 0x36, 0x31, 0x00, 0x00, 0x00, 0xA9, }; static const Uint8 fmtPetsciiKrisszHu_ID2[] = { 0x8d, 0x11, 0xd0, 0xa9, 0x80, 0x8d, 0x91, 0x02, 0xa9, 0x18, }; static int fmtProbePetsciiKrisszHu(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { (void) fmt; if (buf->len == 10193 && DM_MEMCMP_SIZE(buf->data, fmtPetsciiKrisszHu_ID1) == 0 && DM_MEMCMP_SIZE(buf->data + 0x10, fmtPetsciiKrisszHu_ID2) == 0 ) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDecodePetsciiKrisszHuData(const DMC64EncDecOp *op, DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) op; (void) buf; (void) fmt; const Uint8 *data = img->extraData[0].data; switch (data[0x0028 - 2]) { case 0x00: img->extraInfo[D64_EI_MODE] = D64_FMT_HIRES | D64_FMT_CHAR; img->d020 = data[0x001e - 2]; img->bgcolor = data[0x0023 - 2]; break; case 0xd8: img->extraInfo[D64_EI_MODE] = D64_FMT_MC | D64_FMT_CHAR; img->d020 = data[0x001e - 2]; img->bgcolor = data[0x0023 - 2]; img->d022 = data[0x002d - 2]; img->d023 = data[0x0032 - 2]; break; case 0x01: img->extraInfo[D64_EI_MODE] = D64_FMT_ECM | D64_FMT_CHAR; img->d020 = data[0x001e - 2]; img->bgcolor = data[0x0023 - 2]; img->d022 = data[0x0028 - 2]; img->d023 = data[0x002d - 2]; img->d024 = data[0x0032 - 2]; break; default: return DMERR_INVALID_DATA; } // XXX TODO this format saves the charset data (for 256 chars) // in the PRG and there is no direct indication whether it is // a customized one or copy of C64 ROM charset .. we could // implement a hash-based detection at some point. img->extraInfo[D64_EI_CHAR_CASE] = 0; img->extraInfo[D64_EI_CHAR_CUSTOM] = 1; return DMERR_OK; } static int fmtDecodeMarqOldData(const DMC64EncDecOp *op, DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) op; (void) buf; (void) fmt; img->extraInfo[D64_EI_MODE] = D64_FMT_HIRES | D64_FMT_CHAR; img->extraInfo[D64_EI_CHAR_CUSTOM] = 0; return DMERR_OK; } static const Uint8 fmtCocaPETSCII_ID1[] = { 0x01, 0x08, 0x0b, 0x08, 0x0a, 0x00, 0x9e, 0x32, 0x30, 0x36, 0x31, 0x00, 0x00, 0x00, 0x78, 0xa2, 0x00, 0xbd, 0x5a, 0x08, 0x9d, 0x00, 0x04, 0xbd, 0x5a, 0x09, 0x9d, 0x00, 0x05, 0xbd, 0x5a, 0x0a, 0x9d, 0x00, 0x06, 0xbd, 0x5a, 0x0b, 0x9d, 0x00, 0x07, 0xbd, 0x5a, 0x0c, 0x9d, 0x00, 0xd8, 0xbd, 0x5a, 0x0d, 0x9d, 0x00, 0xd9, 0xbd, 0x5a, 0x0e, 0x9d, 0x00, 0xda, 0xbd, 0x5a, 0x0f, 0x9d, 0x00, 0xdb, 0xe8, 0xd0, 0xcd, 0xad, 0x42, 0x0c, 0x8d, 0x20, 0xd0, 0xad, 0x43, 0x0c, 0x8d, 0x21, 0xd0, 0xad, 0x44, 0x0c, 0x8d, 0x18, 0xd0, 0x4c, 0x55, 0x08 }; static int fmtProbeCocaPETSCII(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { (void) fmt; if (buf->len == 2115 && DM_MEMCMP_SIZE(buf->data, fmtCocaPETSCII_ID1) == 0 && buf->data[0x0c44 - 0x0801 + 2] == 0x14) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtEncodeCocaPETSCIIData(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { memcpy(buf->data, fmtCocaPETSCII_ID1, sizeof(fmtCocaPETSCII_ID1)); return fmtEncodeHiresPETSCIICharsetData(op, buf, img, fmt); } static int fmtProbeKoalaPainter(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int score = DM_PROBE_SCORE_FALSE; if (buf->len == 10003 || buf->len == 10004) 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 < 10002 && 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 fmtProbeArtStudio(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if ((buf->len == fmt->size || buf->len == 9002) && dmCompareAddr16(buf, 0, 0x2000)) 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) && DM_MEMCMP_LEN(ident, "DRAZPAINT ") == 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->mode & 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; if ((res = dmGenericRLEAnalyze(&tmp, &cfg)) != DMERR_OK) goto out; // 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) && DM_MEMCMP_LEN(buf->data + 2, "DRAZLACE! 1.0") == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDrazLaceGetLaceType(const DMC64EncDecOp *op, DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { (void) fmt; img->extraInfo[D64_EI_ILACE_TYPE] = buf->data[op->offs] ? D64_ILACE_RES : D64_ILACE_COLOR; return DMERR_OK; } static int fmtDrazLaceSetLaceType(const DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageCommonFormat *fmt) { (void) fmt; buf->data[op->offs] = (img->extraInfo[D64_EI_ILACE_TYPE] == D64_ILACE_RES) ? 1 : 0; return DMERR_OK; } static int fmtGetPixelDrazLace(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) (void) vshift; return dmC64GetGenericMCPixel(scan->col, img, bmoffs, scroffs, 6 - (rasterX & 6), 0, rasterX & 1, 0, img->bgcolor); } static const char *fmtBDP5_MagicID = "BDP 5.00"; static int fmtProbeBDP5Packed(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 20 && dmCompareAddr16(buf, 0, fmt->addr) && DM_MEMCMP_LEN(buf->data + 2, fmtBDP5_MagicID) == 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; if ((res = dmGenericRLEAnalyze(&mem, &cfg)) != DMERR_OK) goto out; // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) fmtBDP5_MagicID, strlen(fmtBDP5_MagicID)) || !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 *fmtGunPaint_MagicID = "GUNPAINT (JZ) "; #define fmtGunPaint_MagicLen (16) #define fmtGunPaint_MagicOffs (0x03e8) static int fmtProbeGunPaint(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > fmtGunPaint_MagicOffs + fmtGunPaint_MagicLen && dmCompareAddr16(buf, 0, fmt->addr) && memcmp(buf->data + fmtGunPaint_MagicOffs + 2, fmtGunPaint_MagicID, fmtGunPaint_MagicLen) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int 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 + fmtGunPaint_MagicOffs + 2, fmtGunPaint_MagicID, fmtGunPaint_MagicLen); return DMERR_OK; } 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 int fmtGetPixelFLIDesigner(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_MC_PIXEL_DEFS(img) return dmC64GetGenericMCPixel(scan->col, img, bmoffs, scroffs, vshift, rasterY & 7, 0, 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 if ((res = dmGenericRLEAnalyze(&tmp1, &cfg)) != DMERR_OK) goto out; 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 int fmtGetPixelBlackMailFLI(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_MC_PIXEL_DEFS(img) Uint8 bgcol = (unsigned) rasterY < img->extraData[0].size ? img->extraData[0].data[rasterY] : img->bgcolor; return dmC64GetGenericMCPixel(scan->col, img, bmoffs, scroffs, vshift, rasterY & 7, 0, 0, bgcol); } static int fmtGetPixelTruePaint(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) (void) vshift; return dmC64GetGenericMCPixel(scan->col, img, bmoffs, scroffs, 6 - (rasterX & 6), 0, rasterX & 1, 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) && DM_MEMCMP_SIZE(buf->data + 2, 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; } static int fmtGetPixelFlinterlazer(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_MC_PIXEL_DEFS(img) const int vbank = rasterY & 7; Uint8 color1, color2, bgcol = img->bgcolor; int res; if ((res = dmC64GetGenericMCPixel(&color1, img, bmoffs, scroffs, vshift, vbank , 0, 0, bgcol)) != DMERR_OK || (res = dmC64GetGenericMCPixel(&color2, img, bmoffs, scroffs, vshift, vbank + 8, 1, 0, bgcol)) != DMERR_OK) return res; *(scan->col) = (color1 * D64_NCOLORS) + color2; return DMERR_OK; } enum { I_BRK = 0x00, I_RTS = 0x60, I_LDA_IMD = 0xa9, I_STA_ABS = 0x8d, }; static int fmtDecode6502SpeedCode(const DMGrowBuf *buf, DMC64Image *img, const int cbank) { size_t reg_pc = 0; Uint8 reg_accu = 0;//, reg_x = 0, reg_y = 0; Uint16 reg_ptr; while (reg_pc < buf->size) { Uint8 instr = buf->data[reg_pc++]; switch (instr) { case I_LDA_IMD: if (reg_pc >= buf->size) goto out; reg_accu = buf->data[reg_pc++]; break; case I_STA_ABS: if (reg_pc >= buf->size) goto out; reg_ptr = buf->data[reg_pc++]; if (reg_pc >= buf->size) goto out; reg_ptr |= buf->data[reg_pc++] << 8; if (reg_ptr >= 0xd800 && reg_ptr <= 0xdbff) { img->color[cbank].data[reg_ptr - 0xd800] = reg_accu; } break; case I_RTS: return DMERR_OK; default: return DMERR_INVALID_DATA; } } out: return DMERR_INVALID_DATA; } static int fmtDecodeFlinterlazer(const DMC64EncDecOp *op, DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageCommonFormat *fmt) { DMGrowBuf tmp; (void) op; (void) fmt; // Flinterlazer stores color RAMs as speedcode, so we need to // decode some 6510 instructions to get the data. return fmtDecode6502SpeedCode( dmGrowBufConstCopyOffsSize(&tmp, buf, 0, 0x17b2), img, 0); } #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) { // In the unpacked format the first 0x40 bytes should be 0xff for (size_t offs = 2; offs < 0x42; offs++) if (buf->data[offs] != 0xff) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_MAX; } } return DM_PROBE_SCORE_FALSE; } static int fmtDecodeCosmosDesignsHiresManagerPacked(DMC64Image *img, const DMGrowBuf *psrc, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp; Uint8 data, *dstBuf; const size_t baseAddr = 0x4000; const size_t dstSize = 0x8000 - baseAddr; size_t dstOffs, srcOffs, ncount; // Allocate output buffer if ((dstBuf = dmMalloc0(dstSize)) == NULL) { return dmError(DMERR_MALLOC, "Could not allocate memory for RLE decoding buffer.\n"); } // Setup input and output offsets srcOffs = psrc->len - 1; dstOffs = 0x7ff2 - baseAddr - 1; while (dstOffs > 0 && srcOffs > 0) { // Get one byte of data data = psrc->data[srcOffs]; // Current data byte tells us the mode if (data == 0) { // RLE run if (srcOffs < 3) { res = dmError(DMERR_INVALID_DATA, "RLE: Invalid data/out of data for run sequence.\n"); goto out; } ncount = psrc->data[--srcOffs]; data = psrc->data[--srcOffs]; if (dstOffs < ncount) goto finish; dstOffs -= ncount; for (size_t n = 0; n < ncount + 1; n++) dstBuf[dstOffs + n] = data; srcOffs--; } else { // Literal run of data bytes ncount = data; if (srcOffs < ncount) ncount = srcOffs; if (dstOffs < ncount) ncount = dstOffs; srcOffs -= ncount; dstOffs -= ncount - 1; for (size_t n = 0; n < ncount; n++) dstBuf[dstOffs + n] = psrc->data[srcOffs + n]; } } finish: // Fixups that the original decoder does, not necessary really dstBuf[0x7ff0 - baseAddr] = 0x03; dstBuf[0x7ffe - baseAddr] = dstBuf[4]; for (size_t n = 0; n < 0x40; n++) dstBuf[n] = 0xff; for (size_t n = 0; n < 0x100; n++) dstBuf[0x40 + n] = 0x00; res = dmC64DecodeGenericBMP(img, dmGrowBufConstCreateFrom(&tmp, dstBuf, dstSize), fmt); out: dmFree(dstBuf); return res; } static const char *fmtFunPaint2_MagicID = "FUNPAINT (MT) "; #define fmtFunPaint2_Header_Size (0x10) static int fmtProbeFunPaint2(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { if (buf->len > 30 && DM_MEMCMP_LEN(buf->data + 2, fmtFunPaint2_MagicID) == 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, fmtFunPaint2_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, fmtFunPaint2_Header_Size), fmt); } return res; } static int fmtEncodeFunPaint2Unpacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) fmtFunPaint2_MagicID, strlen(fmtFunPaint2_MagicID)) || !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; if ((res = dmGenericRLEAnalyze(&mem, &cfg)) != DMERR_OK) goto out; // Add the header bits if (!dmGrowBufPut(buf, (Uint8 *) fmtFunPaint2_MagicID, strlen(fmtFunPaint2_MagicID)) || !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 int fmtGetPixelFunPaint2(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) const int bitmap = rasterX & 1; Uint8 bgcol = (unsigned) rasterY < img->extraData[0].size ? img->extraData[0].data[rasterY] : img->bgcolor; (void) vshift; return dmC64GetGenericMCPixel(scan->col, img, bmoffs, scroffs, 6 - (rasterX & 6), yb + (bitmap * 8), bitmap, 0, bgcol); } static int fmtGetPixelBFLI(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_MC_PIXEL_DEFS(img) const int vbb = rasterY < 200 ? 0 : 1; const int vbank = (rasterY & 7) + (vbb * 8); return dmC64GetGenericMCPixel(scan->col, img, bmoffs & 0x1fff, scroffs & 0x3ff, vshift, vbank, vbb, 0, img->bgcolor); } static int fmtGetPixelPentelPaint(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) const int spr_y = rasterY / D64_SPR_HEIGHT_PX; const int spr_yd = rasterY % D64_SPR_HEIGHT_PX; const int spr_x = rasterX / D64_SPR_WIDTH_PX; const int spr_xd = (rasterX % D64_SPR_WIDTH_PX) / 8; const int offs = (spr_y * 8 + spr_x) * D64_SPR_SIZE + (spr_yd * D64_SPR_WIDTH_UT) + spr_xd; const int mask = 1 << (7 - (rasterX & 7)); int res; Uint8 color1, color2 = img->extraData[0].data[offs] & mask ? 0x0f : 0, color3 = img->extraData[0].data[offs + D64_SPR_SIZE * 155] & mask ? img->d022 : 0; if ((res = dmC64GetGenericSCPixel(&color1, img, bmoffs, scroffs, vshift, 0, 0)) != DMERR_OK) return res; *(scan->col) = color3 ? color3 : ( color2 ? color2 : color1 ); return DMERR_OK; } // Horizontal character X-offset and scanline Y-offset #define DM_CREST_SHFLI_IMG_XOFFS 14 #define DM_CREST_SHFLI_IMG_YOFFS 1 // True image width and height in character blocks #define DM_CREST_SHFLI_IMG_WIDTH (4 * D64_SPR_WIDTH_UT) #define DM_CREST_SHFLI_IMG_HEIGHT 21 #define DM_CREST_SHFLI_BANKS 8 #define DM_CREST_SHFLI_PTRS 8 static const Uint8 fmtCrestSHFLI_Sprite_pointers[DM_CREST_SHFLI_BANKS][DM_CREST_SHFLI_PTRS] = { { 0x80, 0x84, 0x85, 0x89, 0x8A, 0x8E, 0x8F, 0x93 }, { 0x94, 0x98, 0x99, 0x9D, 0x9E, 0xA2, 0xA3, 0xA7 }, { 0xA8, 0xAC, 0xAD, 0xB1, 0xB2, 0xB6, 0xB7, 0xBB }, { 0xBC, 0xC0, 0xC1, 0xC5, 0xC6, 0xCA, 0xCB, 0xCF }, { 0xD0, 0xD4, 0xD5, 0xD9, 0xDA, 0xDE, 0xDF, 0xE3 }, { 0xE4, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE }, { 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6 }, { 0xF7, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E }, }; static const Uint8 fmtCrestSHFLI_MagicID_Packed[] = { 0x83, 0x92, 0x85, 0x93, 0x94, }; static int fmtProbeCrestSHFLI(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { // Unpacked variant if (buf->len == fmt->size && dmCompareAddr16(buf, 0, fmt->addr)) { int score = DM_PROBE_SCORE_MAYBE; for (int nbank = 0; nbank < DM_CREST_SHFLI_BANKS; nbank++) { if (memcmp(buf->data + 2 + (nbank * 0x0400) + 0x03f8, fmtCrestSHFLI_Sprite_pointers[nbank], DM_CREST_SHFLI_PTRS) == 0) score += DM_PROBE_SCORE_GOOD; } return score; } // Packed variant if (dmCompareAddr16(buf, 0, fmt->addr) && buf->len > sizeof(fmtCrestSHFLI_MagicID_Packed) && DM_MEMCMP_SIZE(buf->data + buf->len - sizeof(fmtCrestSHFLI_MagicID_Packed), fmtCrestSHFLI_MagicID_Packed) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDecodeCrestSHFLIPacked(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int res; DMGrowBuf tmp, mem; DMCompParams cfg; // Compression is typical RLE, with first byte being the RLE marker byte etc. // However, as a difference to the uncompressed files, only the data of the // 96 pixels wide area (4 sprite widths) x 168 tall is saved. 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; cfg.rleMarkerB = buf->data[0]; if ((res = dmDecodeGenericRLEAlloc( &mem, dmGrowBufConstCopyOffs(&tmp, buf, 1), &cfg)) == DMERR_OK) { const size_t fmtUncompSize = 0x1ff0; const size_t dstSize = 16 * 1024; Uint8 *dstBuf, *sptr, *dptr; // Check uncompressed size? if (mem.len != fmtUncompSize) { res = dmError(DMERR_INVALID_DATA, "%s: Unexpected uncompressed data size %" DM_PRIu_SIZE_T " bytes (should be %" DM_PRIu_SIZE_T ").\n", cfg.func, mem.len, fmtUncompSize); goto out; } // Allocate output buffer if ((dstBuf = dmMalloc0(dstSize)) == NULL) { res = dmError(DMERR_MALLOC, "%s: Could not allocate temporary memory buffer of %" DM_PRIu_SIZE_T " bytes.\n", cfg.func, dstSize); goto out; } // Now that we have the uncompressed data (0x1ff0 bytes), we need to // re-arrange it. The data is as follows .. // // 0x0000 - sprite data for 64 sprites // 0x1000 - bitmap (12 * 21 bytes) // 0x1800 - screen RAMs (12 * 21 bytes) x 8 banks // 0x1fe8 - sprite color #1 // 0x1fe9 - sprite color #2 // // Copy sprite colors sptr = mem.data + 0x1fe8; dptr = dstBuf + 0x03e8; dptr[0] = sptr[0]; dptr[1] = sptr[1]; // First, clear and set some defaults that are not saved in the file for (int nbank = 0; nbank < DM_CREST_SHFLI_BANKS; nbank++) { dptr = dstBuf + nbank * 0x0400; // Set preset screen RAM for other area memset(dptr, 0xff, fmt->format->chWidth * fmt->format->chHeight); // Copy sprite data points memcpy(dptr + 0x03f8, &fmtCrestSHFLI_Sprite_pointers[nbank], DM_CREST_SHFLI_PTRS); } // Now we copy and transform the bitmap and screen RAM data. for (int yc = 0; yc < DM_CREST_SHFLI_IMG_HEIGHT * 8; yc++) { const int syy = yc / 8; const int syd = yc & 7; // In the image the first visible scanline is unused, but in // the compressed version data starts right away, so we offset // the destination Y coordinate by one. const int dyc = yc + DM_CREST_SHFLI_IMG_YOFFS; const int dyy = dyc / 8; const int dyd = dyc & 7; // Format of the bitmap data is one horizontal pixel row (12 bytes) // times 21*8 rows, e.g. the data is "linear" rows of bytes and not // arranged in usual c64 bitmap "char" order. Thus we reorder it. sptr = mem.data + 0x1000 + DM_CREST_SHFLI_IMG_WIDTH * (syd + 8 * syy); dptr = dstBuf + 0x2000 + DM_CREST_SHFLI_IMG_XOFFS * 8 + (fmt->format->chWidth * dyy * 8) + dyd; for (int xc = 0; xc < DM_CREST_SHFLI_IMG_WIDTH; xc++) { dptr[xc * 8] = sptr[xc]; sptr[xc] = 0xaa; } // A bit similar arrangement is used for the screen RAM data. // Each row of 12 bytes of data is for a bank. Next row is for // next bank, etc. sptr = mem.data + 0x1800 + DM_CREST_SHFLI_IMG_WIDTH * syd + DM_CREST_SHFLI_IMG_WIDTH * 8 * syy; dptr = dstBuf + DM_CREST_SHFLI_IMG_XOFFS + 0x0400 * dyd + fmt->format->chWidth * dyy; for (int xc = 0; xc < DM_CREST_SHFLI_IMG_WIDTH; xc++) { dptr[xc] = sptr[xc]; sptr[xc] = 0xaa; } } // The sprite data is also transformed similarly, data is // in same scanline format as the bitmap. Thus we need to // place it where it belongs based on the sprite pointers. for (int yc = 0; yc < DM_CREST_SHFLI_IMG_HEIGHT * 8; yc++) { const int yd = yc % D64_SPR_HEIGHT_PX; const Uint8 *sprPtrs = fmtCrestSHFLI_Sprite_pointers[yc % 8]; Uint8 *sp1, *sp2, *dp; dptr = dstBuf + D64_SPR_WIDTH_UT * yd; sp1 = mem.data + DM_CREST_SHFLI_IMG_WIDTH * yc; sp2 = sp1 + 0x0800; for (int xc = 0; xc < DM_CREST_SHFLI_IMG_WIDTH / D64_SPR_WIDTH_UT; xc++) { dp = dptr + D64_SPR_SIZE * sprPtrs[xc]; for (int xd = 0; xd < D64_SPR_WIDTH_UT; xd++) dp[xd] = *sp1++; dp = dptr + D64_SPR_SIZE * sprPtrs[xc + 4]; for (int xd = 0; xd < D64_SPR_WIDTH_UT; xd++) dp[xd] = *sp2++; } } res = dmC64DecodeGenericBMP(img, dmGrowBufConstCreateFrom(&tmp, dstBuf, dstSize), fmt); dmFree(dstBuf); } out: dmGrowBufFree(&mem); return res; } static int fmtGetSpritePixelCrestSHFLI(DMC64ScanLine *scan, const DMC64Image *img, const int sindex, const int cindex, const int spr_xd, const int spr_yd, const int mask) { const size_t offs = sindex * D64_SPR_SIZE + (D64_SPR_WIDTH_UT * spr_yd) + spr_xd; if (offs >= img->extraData[14].size) return DMERR_BOUNDS; if (img->extraData[14].data[offs] & mask) { *(scan->col) = img->extraData[15].data[cindex]; return DMERR_OK; } return -1; } static int fmtGetPixelCrestSHFLI(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) const int sprOffsetX = DM_CREST_SHFLI_IMG_XOFFS * 8, sprOffsetY = DM_CREST_SHFLI_IMG_YOFFS; const int nbank = rasterY & 7; int res; if (rasterY / 8 >= DM_CREST_SHFLI_IMG_HEIGHT) { *(scan->col) = 0x0f; return DMERR_OK; } if (rasterY >= sprOffsetY && rasterX >= sprOffsetX && rasterX < sprOffsetX + 4 * D64_SPR_WIDTH_PX) { const int localX = rasterX - sprOffsetX, localY = rasterY - sprOffsetY; const int sbank = localY & 7; const int spr_yd = localY % D64_SPR_HEIGHT_PX; const int spr_x = localX / D64_SPR_WIDTH_PX; const int spr_xd = (localX % D64_SPR_WIDTH_PX) / 8; const int mask = 1 << (7 - (localX & 7)); const int spr_offs = spr_x & 3; const int spr_index1 = img->extraData[sbank].data[spr_offs]; const int spr_index2 = img->extraData[sbank].data[spr_offs + 4]; if ((res = fmtGetSpritePixelCrestSHFLI(scan, img, spr_index1, 0, spr_xd, spr_yd, mask)) == DMERR_OK || res != -1) return res; if ((res = fmtGetSpritePixelCrestSHFLI(scan, img, spr_index2, 1, spr_xd, spr_yd, mask)) == DMERR_OK || res != -1) return res; } if ((res = dmC64GetGenericSCPixel(scan->col, img, bmoffs, scroffs, vshift, nbank, 0)) != DMERR_OK) return res; return DMERR_OK; } static int fmtGetPixelHCB(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_MC_PIXEL_DEFS(img) const int vbank = (rasterY / 4) & 1; const int ry = rasterY / 5; Uint8 bgcol = (unsigned) ry < img->extraData[0].size ? img->extraData[0].data[ry] : img->bgcolor; return dmC64GetGenericMCPixel(scan->col, img, bmoffs, scroffs, vshift, vbank, 0, vbank, bgcol); } static int fmtGetPixelCrestHIFLIorCDHM(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) return dmC64GetGenericSCPixel(scan->col, img, bmoffs, scroffs, vshift, rasterY & 7, 0); } static int fmtGetPixelECI(DMC64ScanLine *scan, const DMC64Image *img, const int rasterX, const int rasterY) { DM_C64_GENERIC_SC_PIXEL_DEFS(img) const int vbank = rasterY & 7; Uint8 color1, color2; int res; if ((res = dmC64GetGenericSCPixel(&color1, img, bmoffs, scroffs, vshift, vbank , 0)) != DMERR_OK || (res = dmC64GetGenericSCPixel(&color2, img, bmoffs, scroffs, vshift, vbank + 8, 1)) != DMERR_OK) return res; *(scan->col) = (color1 * D64_NCOLORS) + color2; return DMERR_OK; } static int fmtProbeECIPacked(const DMGrowBuf *buf, const DMC64ImageFormat *fmt) { int score = DM_PROBE_SCORE_FALSE; if (buf->len > 128 && dmCompareAddr16(buf, 0, fmt->addr)) { size_t i, n; // Count statistics about used byte values and compare to // value in buf[2] which is the RLE marker for (n = 0, i = 3; i < buf->len; i++) if (buf->data[i] == buf->data[2]) n++; if (n > 50) score = DM_PROBE_SCORE_GOOD; else if (n > 25) score = DM_PROBE_SCORE_AVG; else if (n > 10) score = DM_PROBE_SCORE_MAYBE; } if (// Try to avoid misprobe of Crest Hires FLI Designer and Cosmos Design format buf->len == 16386 || // Face Painter buf->len == 10004) score /= 3; return score; } static int fmtDecodeECIPacked(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[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; if ((res = dmGenericRLEAnalyze(&tmp, &cfg)) != DMERR_OK) goto out; // 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_REPEAT_BLOCK(dtype, start, oindex, bindex, osize, bsize, oflags) \ { DO_COPY, (dtype), (start) + ((osize) * (oindex)), (bindex), (bsize), 0, NULL, NULL, (oflags) } #define DEF_REPEAT_BLOCK_8(dtype, start, sindex, osize, bsize, oflags) \ DEF_REPEAT_BLOCK((dtype), (start), 0, ((sindex) + 0), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 1, ((sindex) + 1), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 2, ((sindex) + 2), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 3, ((sindex) + 3), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 4, ((sindex) + 4), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 5, ((sindex) + 5), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 6, ((sindex) + 6), (osize), (bsize), (oflags)), \ DEF_REPEAT_BLOCK((dtype), (start), 7, ((sindex) + 7), (osize), (bsize), (oflags)) // // 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, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2328, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2710, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #1: Black Mail FLI Graph D64_FMT_MC | D64_FMT_FLI, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, fmtGetPixelBlackMailFLI, { { DO_COPY , DS_EXTRA_DATA , 0x0000, 0, 200 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x0100, 0, 0 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x0500, 0, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x2500, 0, 0 , 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0 , 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, } }, { // #2: Art Studio etc. Hires D64_FMT_HIRES, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #3: FunPaint II D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, D64_SCR_WIDTH, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, fmtGetPixelFunPaint2, { DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x0000, 0, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x2000, 0, 0 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_EXTRA_DATA , 0x3f48, 0, 100 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x4000, 0, 0 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x43e8, 8, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x63e8, 1, 0 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_EXTRA_DATA , 0x8328, 0, 100 , 100, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_ILACE_RES, 0 , 0 , D64_EI_ILACE_TYPE, NULL, NULL, DF_DECODE }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0, 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, } }, { // #4: DrazPaint 1.x & 2 D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2740, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #5: DrazLace 1.0 D64_FMT_MC | D64_FMT_ILACE, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, fmtGetPixelDrazLace, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2740, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 0x2742, 0, 1, 0, fmtDrazLaceGetLaceType, fmtDrazLaceSetLaceType, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x2800, 1, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #6: TruePaint D64_FMT_MC | D64_FMT_ILACE, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, fmtGetPixelTruePaint, { { DO_COPY , DS_SCREEN_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x03e8, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x2400, 1, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x4400, 1, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x4800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_ILACE_RES, 0 , 0 , D64_EI_ILACE_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #7: ECI Graphic Editor Hires FLI D64_FMT_HIRES | D64_FMT_FLI | D64_FMT_ILACE, D64_SCR_WIDTH, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, fmtGetPixelECI, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x2000, 0, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x4000, 1, 0, 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x6000, 8, 0x400, 0, DF_NORMAL), { DO_SET_OP , DS_EXTRA_INFO , D64_ILACE_COLOR, 0 , 0 , D64_EI_ILACE_TYPE, NULL, NULL, DF_DECODE }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0, 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #8: Cosmos Designs Hires Manager D64_FMT_HIRES | D64_FMT_FLI, D64_SCR_WIDTH, 24*8, // Actually 296 x 192 (=24*8) D64_SCR_CH_WIDTH, 24, 1, 1, NULL, NULL, fmtGetPixelCrestHIFLIorCDHM, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x2000, 0, 0x400, 0, DF_NORMAL), { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0, 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #9: FBI Crew FLI Designer 1.x & 2.0 D64_FMT_MC | D64_FMT_FLI, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, fmtGetPixelFLIDesigner, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM, 0x0400, 0, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x2400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0, 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #10: Doodle D64_FMT_HIRES, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, { // #11: Crest Super Hires FLI Editor 1.0 (SHF) D64_FMT_HIRES | D64_FMT_FLI | D64_FMT_SPRITES, D64_SCR_WIDTH, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, 24, 1, 1, NULL, NULL, fmtGetPixelCrestSHFLI, { DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM, 0x0000, 0 , 0x0400, 0x0400, DF_NORMAL), DEF_REPEAT_BLOCK_8(DS_EXTRA_DATA, 0x03f8, 0 , 0x0400, 8, DF_DECODE), // Sprite pointers for each bank { DO_COPY , DS_EXTRA_DATA , 0x03e8, 15 , 2 , 0, NULL, NULL, DF_DECODE }, // 2 sprite colors { DO_COPY , DS_EXTRA_DATA , 0x0000, 14 , 0x3e00, 0, NULL, NULL, DF_DECODE }, // Lazily copy whole data for sprite data { DO_COPY , DS_BITMAP_RAM , 0x2000, 0 , 0 , 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0, 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0 , 0 , 0, NULL, NULL, DF_NORMAL }, } }, }; // // 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, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x07e8, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, }, }, NULL }, { "p64", "Picasso 64 (unpacked)", 0x1800, 10050, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x07fe, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, }, }, 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, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x2328, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2329, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2338, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, }, }, NULL }, { "ims", "Image System MC (unpacked)", 0x3c00, 10218, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x23ff, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x2400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "mil", "Micro Illustrator (unpacked)", 0x18dc, 10022, DM_FMT_RDWR | DM_FMT_BROKEN, fmtProbeMicroIllustrator, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 20 + 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 20 + 1000 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 20 + 2000 , 0, 0, 0, NULL, NULL, DF_NORMAL }, // XXX TODO: Unknown where the background color is set, so default to 0x00 { DO_SET_OP , DS_BGCOL , 0x00 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 20 + 0x3e8 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 20 + 0x3e9 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 20 + 0x3ea , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 20 + 0x3eb , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 0 , 0, 0, 0, NULL, fmtEncodeMicroIllustrator, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "cdu", "CDU-Paint (unpacked)", 0x7eef, 10277, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000 + 0x111, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40 + 0x111, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2328 + 0x111, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2710 + 0x111, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "rbp", "Rainbow Painter (unpacked)", 0x5c00, 10242, DM_FMT_RDWR | DM_FMT_BROKEN, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x0400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL, DF_NORMAL }, // XXX TODO: Not sure if the background color is hardcoded .. { DO_SET_OP , DS_BGCOL , 0x00 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "sar", "Saracen Paint (unpacked)", 0x7800, 10219, DM_FMT_RDWR, fmtProbeSaracenPaint, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x7800 - 0x7800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x7bf0 - 0x7800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x7c00 - 0x7800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x9c00 - 0x7800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "blp", "Blazing Paddles (unpacked)", 0xA000, 10242, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, // Both d020 and bgcolor confirmed by tests { DO_SET_MEM_LO , DS_D020 , 0x1f7f, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x1f80, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x2000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "pmg", "Paint Magic crippled MC (unpacked)", 0x3f8e, 9332, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x4000 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x6000 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x5f40 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_COLOR_RAM , 0x5f43 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x5f44 + 0x72 - 0x4000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "a64", "Wigmore Artist 64 (unpacked)", 0x4000, 10242, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x2000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x27fe, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x27ff, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, 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, fmtProbeArtStudio, 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, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_SCREEN_RAM , 0xCF , 0, 0, 0, NULL, NULL, DF_NORMAL }, // Default colors used by MM are --^^ { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "hir", "Plain hires (unpacked)", 0x2000, 8002, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_HIRES, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_SCREEN_RAM , 0xF0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "gih", "Gigapaint hires (unpacked)", 0x6000, 8002, DM_FMT_RDWR, fmtProbeGigapaintHires, NULL, NULL, { D64_FMT_HIRES, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_SCREEN_RAM , 0x0F , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "bfli", "Pu-239 Big FLI/BFLI (unpacked)", 0x3bff, 33795, DM_FMT_RD, NULL, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT * 2, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, fmtGetPixelBFLI, { { DO_COPY , DS_COLOR_RAM , 0x0001, 0, 0x400 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x0401, 0, 0x400 , 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x2401, 0, 0x2000, 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x4401, 8, 0x400 , 0x400, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x6401, 1, 0x2000, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8, 0, 0, D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, } }, 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] }, { "dfed", "Dolphins FLI Editor v3.2 (unpacked)", 0x3b00, 17665, DM_FMT_RDWR, NULL, NULL, NULL, { }, &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] }, { "flnt", "Flinterlazer 1.0 (unpacked)", 0x284e, 38812, DM_FMT_RD | DM_FMT_BROKEN, NULL, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, fmtGetPixelFlinterlazer, { { DO_COPY , DS_COLOR_RAM , 0x0000 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x0000 , 1, 0 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x4000 - 0x284e, 0, 0 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x6000 - 0x284e, 0, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x8000 - 0x284e, 1, 0 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0xA000 - 0x284e, 8, 0x400, 0, DF_NORMAL), { DO_SET_OP , DS_EXTRA_INFO , D64_ILACE_COLOR, 0 , 0 , D64_EI_ILACE_TYPE, NULL, NULL, DF_DECODE }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8 , 0, 0 , D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_FUNC , 0 , 0 , 0, 0, 0, fmtDecodeFlinterlazer, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "eci", "ECI Graphic Editor 1.0 (unpacked)", 0x4000, 32770, DM_FMT_RDWR, NULL, NULL, NULL, { }, &dmC64CommonFormats[7] }, { "ecip", "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, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x1f40, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2328, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x2710, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x2711, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "xx1", "Unknown $2000 format (unpacked)", 0x2000, 10242, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x2000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x2400, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_BGCOL , 0x00 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, 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, 2, 1, NULL, NULL, NULL, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, XX2_BSIZE, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , XX2_BSIZE, 0, XX2_SIZE, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , XX2_BSIZE + XX2_SIZE, 0, XX2_SIZE, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_BGCOL , 11 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, 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 1.1 (unpacked)", 0x4000, 33603, DM_FMT_RDWR, fmtProbeGunPaint, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI | D64_FMT_ILACE, D64_SCR_WIDTH, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH , D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, fmtGetPixelFunPaint2, // The format is essentially same as FP2 { DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x0000, 0, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x2000, 0, 0 , 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_EXTRA_DATA , 0x3f4f, 0, 177, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x4000, 0, 0 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x4400, 8, 0x400, 0, DF_NORMAL), { DO_COPY , DS_BITMAP_RAM , 0x6400, 1, 0 , 0, NULL, NULL, DF_NORMAL }, // 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, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_ILACE_RES , 0, 0 , D64_EI_ILACE_TYPE, NULL, NULL, DF_DECODE }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8 , 0, 0 , D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_FUNC , 0 , 0 , 0, 0 , 0, NULL, fmtEncodeGunPaint, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "hcb", "Half Char Bitmap (unpacked)", 0x5000, 12148, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_MC | D64_FMT_FLI, D64_SCR_WIDTH / 2, D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, fmtGetPixelHCB, { { DO_COPY , DS_COLOR_RAM , 0x0000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x0400, 1, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0c00, 1, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_BITMAP_RAM , 0x1000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_EXTRA_DATA , 0x2f40, 0, D64_SCR_HEIGHT / 4, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8 , 0, 0 , D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "pen", "Pentel Paint (unpacked)", 0x4800, 19845, DM_FMT_RD | DM_FMT_BROKEN, NULL, NULL, NULL, { D64_FMT_HIRES | D64_FMT_FLI | D64_FMT_SPRITES, 192, D64_SCR_HEIGHT, 24, D64_SCR_CH_HEIGHT, 2, 1, NULL, NULL, fmtGetPixelPentelPaint, { { DO_COPY , DS_BITMAP_RAM , 0x0000 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_OP , DS_SCREEN_RAM , 0x10 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x9580 - 0x4800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D022 , 0x9581 - 0x4800, 0, 0, 0, NULL, NULL, DF_NORMAL }, // Sprite color { DO_SET_MEM_LO , DS_COLOR_RAM , 0x9582 - 0x4800, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_EXTRA_DATA , 0x5ac0 - 0x4800, 0, D64_SPR_SIZE * 235, 0, NULL, NULL, DF_NORMAL }, // Sprite data { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8 , 0, 0 , D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "chfd", "Crest Hires FLI Designer (unpacked)", 0x4000, 16386, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_HIRES | D64_FMT_FLI, D64_SCR_WIDTH, D64_SCR_HEIGHT, // Actually 296 x 112 (=14*8) D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, fmtGetPixelCrestHIFLIorCDHM, { { DO_COPY , DS_BITMAP_RAM , 0x0000, 0, 0 , 0, NULL, NULL, DF_NORMAL }, DEF_REPEAT_BLOCK_8(DS_SCREEN_RAM , 0x2000, 0, 0x400, 0, DF_NORMAL), { DO_SET_OP , DS_EXTRA_INFO , D64_FLI_8 , 0, 0 , D64_EI_FLI_TYPE, NULL, NULL, DF_DECODE }, { DO_LAST , 0 , 0 , 0, 0 , 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "cshf", "Crest Super Hires FLI Editor v1.0 (unpacked)", 0x4000, 15874, DM_FMT_RDWR, fmtProbeCrestSHFLI, NULL, NULL, { }, &dmC64CommonFormats[11] }, { "cshfp", "Crest Super Hires FLI Editor v1.0 (packed)", 0xa000, 0, DM_FMT_RD, fmtProbeCrestSHFLI, fmtDecodeCrestSHFLIPacked, NULL, { }, &dmC64CommonFormats[11] }, { "cdhm", "Cosmos Designs Hires Manager (unpacked)", 0x4000, 16385, DM_FMT_RDWR, fmtProbeCosmosDesignsHiresManager, NULL, NULL, { }, &dmC64CommonFormats[8] }, { "cdhp", "Cosmos Designs Hires Manager (packed)", 0x4000, 0, DM_FMT_RD, fmtProbeCosmosDesignsHiresManager, fmtDecodeCosmosDesignsHiresManagerPacked, NULL, { }, &dmC64CommonFormats[8] }, { "mrqp", "Marq's PETSCII editor (new format) (unpacked)", 0x0801, 0, DM_FMT_RDWR, fmtProbeMarqPETSCII, NULL, NULL, { D64_FMT_HIRES | D64_FMT_CHAR, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { // For offset values see petscii/m_c64.pde :: save_prg() { DO_FUNC , 0 , 20 - 2, 0, 0, 0, NULL, fmtEncodeMarqPETSCIIData, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x60 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x60 + 1000,0,0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 25 - 2, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 30 - 2, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 20 - 2, 0, 0, 0, fmtDecodeHiresPETSCIICharsetData, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "mqpo", "Marq's PETSCII editor (old format) (unpacked)", 0x0801, 2499, DM_FMT_RD, NULL, NULL, NULL, { D64_FMT_HIRES | D64_FMT_CHAR, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x01ab, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x01ab + 1000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x01a9, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x01aa, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 0 , 0, 0, 0, fmtDecodeMarqOldData, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "pkhu", "petscii.krissz.hu editor (unpacked)", 0x0801, 0, DM_FMT_RD, fmtProbePetsciiKrisszHu, NULL, NULL, { D64_FMT_CHAR, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_COPY , DS_SCREEN_RAM , 0x2001 - 2, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x23e9 - 2, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_EXTRA_DATA , 0x0000 , 0, 0x0100, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_CHAR_DATA , 0x1801 - 2, 0, 0x0800, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 0 , 0, 0 , 0, fmtDecodePetsciiKrisszHuData, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "poca", "Petscii Coca editor (unpacked)", 0x0801, 0, DM_FMT_RDWR, fmtProbeCocaPETSCII, NULL, NULL, { D64_FMT_HIRES | D64_FMT_CHAR, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_FUNC , 0 , 0x0c44 - 0x0801, 0, 0, 0, NULL, fmtEncodeCocaPETSCIIData, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x085a - 0x0801, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x0c5a - 0x0801, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x0c42 - 0x0801, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x0c43 - 0x0801, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 0x0c44 - 0x0801, 0, 0, 0, fmtDecodeHiresPETSCIICharsetData, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, { "acpe", "Abyss Connection PETSCII-Editor 4.61 (unpacked)", 0x3000, 2026, DM_FMT_RDWR, NULL, NULL, NULL, { D64_FMT_HIRES | D64_FMT_CHAR, D64_SCR_WIDTH , D64_SCR_HEIGHT, D64_SCR_CH_WIDTH, D64_SCR_CH_HEIGHT, 1, 1, NULL, NULL, NULL, { { DO_FUNC , 0 , 0x33ea - 0x3000, 0, 0, 0, NULL, fmtEncodeHiresPETSCIICharsetData, DF_NORMAL }, { DO_COPY , DS_SCREEN_RAM , 0x0000 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_COPY , DS_COLOR_RAM , 0x0400 , 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_D020 , 0x33e8 - 0x3000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_SET_MEM_LO , DS_BGCOL , 0x33e9 - 0x3000, 0, 0, 0, NULL, NULL, DF_NORMAL }, { DO_FUNC , 0 , 0x33ea - 0x3000, 0, 0, 0, fmtDecodeHiresPETSCIICharsetData, NULL, DF_NORMAL }, { DO_LAST , 0 , 0 , 0, 0, 0, NULL, NULL, DF_NORMAL }, } }, NULL }, }; const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]);