changeset 1737:811b20de954c

Merge.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 08 Jun 2018 22:32:30 +0300
parents 245f45073f4c (current diff) eeb6a82b4654 (diff)
children b4992d9f72fe
files
diffstat 7 files changed, 389 insertions(+), 218 deletions(-) [+]
line wrap: on
line diff
--- a/tools/64vw.c	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/64vw.c	Fri Jun 08 22:32:30 2018 +0300
@@ -15,13 +15,14 @@
 #define SET_SKIP_AMOUNT 10
 
 
-int    optVFlags = 0;
-int    optScrWidth, optScrHeight;
-int    optForcedFormat = -1;
-BOOL   optProbeOnly = FALSE;
-BOOL   optListOnly  = FALSE;
-size_t noptFilenames1 = 0, noptFilenames2 = 0;
-char   **optFilenames = NULL;
+int     optVFlags = 0;
+int     optScrWidth, optScrHeight;
+int     optForcedFormat = -1;
+BOOL    optInfoOnly  = FALSE,
+        optProbeOnly = FALSE,
+        optListOnly  = FALSE;
+size_t  noptFilenames1 = 0, noptFilenames2 = 0;
+char    **optFilenames = NULL;
 
 
 static const DMOptArg optList[] =
@@ -32,8 +33,9 @@
     { 3, 'S', "scale",      "Scale image by factor (1-10)", OPT_ARGREQ },
     { 4, 'f', "format",     "Force input format (see --formats)", OPT_ARGREQ },
     { 5, 'F', "formats",    "List supported input formats", OPT_NONE },
-    { 6, 'p', "probe",      "Probe only (no display)", OPT_NONE },
-    { 7, 'l', "list",       "Output list of files that were recognized (implies -p)", OPT_NONE },
+    { 6, 'i', "info",       "Print information only (no display)", OPT_NONE },
+    { 7, 'l', "list",       "Output list of files that were recognized (implies -i)", OPT_NONE },
+    { 8, 'p', "probe",      "Probe only (do not attempt to decode the image)", OPT_NONE },
 };
 
 const int optListN = sizeof(optList) / sizeof(optList[0]);
@@ -143,6 +145,12 @@
         case 6:
             if (dmVerbosity < 1)
                 dmVerbosity = 1;
+            optInfoOnly = TRUE;
+            break;
+
+        case 8:
+            if (dmVerbosity < 1)
+                dmVerbosity = 1;
             optProbeOnly = TRUE;
             break;
 
@@ -186,7 +194,10 @@
     if ((ret = dmReadDataFile(NULL, filename, &dataBuf, &dataSize)) != DMERR_OK)
         goto exit;
 
-    ret = dmC64DecodeBMP(cimage, dmGrowBufCreateFrom(&tmp, dataBuf, dataSize), 0, 2, fmt, forced);
+    if (optProbeOnly)
+        ret = dmC64ProbeBMP(dataBuf, dataSize, fmt) != DM_PROBE_SCORE_FALSE ? DMERR_OK : DMERR_NOT_SUPPORTED;
+    else
+        ret = dmC64DecodeBMP(cimage, dmGrowBufCreateFrom(&tmp, dataBuf, dataSize), 0, 2, fmt, forced);
 
 exit:
     dmFree(dataBuf);
@@ -199,17 +210,23 @@
     DMImage bmap;
     int ret;
 
-    bmap.size = surf->pitch * surf->h;
-    bmap.data = surf->pixels;
-    bmap.pitch = surf->pitch;
-    bmap.width = surf->w;
-    bmap.height = surf->h;
+    memset(&bmap, 0, sizeof(bmap));
+    bmap.size     = surf->pitch * surf->h;
+    bmap.data     = surf->pixels;
+    bmap.pitch    = surf->pitch;
+    bmap.width    = surf->w;
+    bmap.height   = surf->h;
+    bmap.ncolors  = C64_NCOLORS;
+    bmap.constpal = TRUE;
+    bmap.pal      = dmDefaultC64Palette;
 
     if (fmt->convertFrom != NULL)
         ret = fmt->convertFrom(&bmap, cimage, fmt);
     else
         ret = dmC64ConvertGenericBMP2Image(&bmap, cimage, fmt);
 
+    SDL_SetPaletteColors(surf->format->palette, (SDL_Color *) bmap.pal, 0, bmap.ncolors);
+
     return ret;
 }
 
@@ -262,8 +279,8 @@
     else
         forced = NULL;
 
-    // If we are simply probing, no need to initialize SDL etc
-    if (optProbeOnly)
+    // If we are simply displaying file information, no need to initialize SDL etc
+    if (optInfoOnly || optProbeOnly)
     {
         for (size_t n = 0; n < noptFilenames2; n++)
         {
@@ -474,7 +491,6 @@
                 goto exit;
             }
 
-            SDL_SetPaletteColors(surf->format->palette, (SDL_Color *)dmDefaultC64Palette, 0, C64_NCOLORS);
 
             if (texture != NULL)
                 SDL_DestroyTexture(texture);
--- a/tools/gfxconv.c	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/gfxconv.c	Fri Jun 08 22:32:30 2018 +0300
@@ -1791,7 +1791,6 @@
                             goto error;
                         }
 
-                        dmSetDefaultC64Palette(outImage);
                         res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
                         break;
 
@@ -1814,7 +1813,6 @@
                             goto error;
                         }
 
-                        dmSetDefaultC64Palette(outImage);
                         res = dmWriteSpritesAndChars(optOutFilename, outImage, optOutFormat, optInMulticolor);
                         break;
 
--- a/tools/lib64fmts.c	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/lib64fmts.c	Fri Jun 08 22:32:30 2018 +0300
@@ -9,27 +9,6 @@
 #include "lib64gfx.h"
 
 
-// Based on Pepto's palette, stolen from VICE
-DMColor dmDefaultC64Palette[C64_NCOLORS] =
-{
-    { 0x00, 0x00, 0x00, 0xff },
-    { 0xFF, 0xFF, 0xFF, 0xff },
-    { 0x68, 0x37, 0x2B, 0xff },
-    { 0x70, 0xA4, 0xB2, 0xff },
-    { 0x6F, 0x3D, 0x86, 0xff },
-    { 0x58, 0x8D, 0x43, 0xff },
-    { 0x35, 0x28, 0x79, 0xff },
-    { 0xB8, 0xC7, 0x6F, 0xff },
-    { 0x6F, 0x4F, 0x25, 0xff },
-    { 0x43, 0x39, 0x00, 0xff },
-    { 0x9A, 0x67, 0x59, 0xff },
-    { 0x44, 0x44, 0x44, 0xff },
-    { 0x6C, 0x6C, 0x6C, 0xff },
-    { 0x9A, 0xD2, 0x84, 0xff },
-    { 0x6C, 0x5E, 0xB5, 0xff },
-    { 0x95, 0x95, 0x95, 0xff },
-};
-
 
 static int fmtProbeKoalaPaintPacked(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
 {
@@ -50,9 +29,10 @@
     DMGrowBuf mem;
     DMCompParams cfg;
 
-    cfg.type        = DM_COMP_RLE_MARKER;
-    cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_2;
-    cfg.rleMarkerB  = 0xfe;
+    cfg.func         = fmt->name;
+    cfg.type         = DM_COMP_RLE_MARKER;
+    cfg.flags        = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_2;
+    cfg.rleMarkerB   = 0xfe;
 
     if ((res = dmDecodeGenericRLEAlloc(&mem, buf, &cfg)) != DMERR_OK)
         goto out;
@@ -76,6 +56,7 @@
         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   = 0xfe;
@@ -111,9 +92,10 @@
     DMGrowBuf mem, tmp;
     DMCompParams cfg;
 
-    cfg.type        = DM_COMP_RLE_MARKER;
-    cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1;
-    cfg.rleMarkerB  = buf->data[0x0d];
+    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,
         dmGrowBufCreateFromOffs(&tmp, buf, 0x0e), &cfg)) != DMERR_OK)
@@ -139,6 +121,7 @@
         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 = 3;
@@ -211,10 +194,11 @@
     DMGrowBuf mem, tmp;
     DMCompParams cfg;
 
-    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];
+    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];
 
     if ((res = dmDecodeGenericRLEAlloc(&mem,
         dmGrowBufCreateFromOffs(&tmp, buf, 10), &cfg)) != DMERR_OK)
@@ -239,6 +223,7 @@
         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 = 3;
@@ -318,9 +303,10 @@
     tmp.len = buf->len + 1;
 
     // Now do an RLE decode on the enlarged buffer
-    cfg.type        = DM_COMP_RLE_MARKER;
-    cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1;
-    cfg.rleMarkerB  = 0xC2;
+    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;
@@ -346,6 +332,7 @@
         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;
@@ -412,58 +399,55 @@
 
 //
 // Based on disassembly of the depacker routine. Encoding seems to be
-// some kind of "improved RLE" variant.
+// some kind of "improved RLE" variant with different modes and a
+// simplistic "codebook".
 //
-static BOOL fmtTruePaintGetByte(const Uint8 *src, size_t *srcOffs, Uint8 *data, int *res, const int mode)
+static int fmtTruePaintGetByte(DMGrowBuf *src, Uint8 *data, const int mode)
 {
-    if (!dmReverseGetByte(src, srcOffs, data))
+    if (!dmGrowBufGetU8(src, data))
     {
-        *res = dmError(DMERR_INVALID_DATA,
+        return dmError(DMERR_INVALID_DATA,
             "TruePaintRLE: Out of input data (N=%d)\n", mode);
-        return FALSE;
     }
     else
-        return TRUE;
+        return DMERR_OK;
 }
 
 
-static int fmtDecodeTruePaintPacked(DMC64Image *img, const DMGrowBuf *src, const DMC64ImageFormat *fmt)
+static int fmtDecodeTruePaintPacked(DMC64Image *img, const DMGrowBuf *psrc, const DMC64ImageFormat *fmt)
 {
     int res = DMERR_OK;
-    Uint8 *dst = NULL;
-    DMGrowBuf dstTmp;
     const Uint8 *codeBook1, *codeBook2;
-    size_t
-        srcOffs, dstOffs,
-        dstLen  = 0x4be8;
-        // 1b7e-67e8 decoded by original depacker
-        // 1c00-67e8 is the actual area used tho
+    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 = src->data + 0x81 - 2;
-    codeBook2 = src->data + 0x85 - 2;
+    codeBook1 = psrc->data + 0x81 - 2;
+    codeBook2 = psrc->data + 0x85 - 2;
 
     // Allocate output buffer
-    if ((dst = dmMalloc0(dstLen)) == NULL)
-    {
-        res = dmError(DMERR_MALLOC,
-            "Could not allocate memory for temporary decompression buffer.\n");
+    if ((res = dmGrowBufAlloc(&dst, 64*1024, 4*1024)) != DMERR_OK)
         goto out;
-    }
 
-    // Begin decompression
-    srcOffs = src->len;
-    dstOffs = dstLen;
+    // 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 (srcOffs > 0 && dstOffs > 0)
+    while ((res = fmtTruePaintGetByte(&src, &data, -1)) == DMERR_OK)
     {
-        Uint8 data;
-        int count = 1, scount;
+        unsigned int count = 1;
         BOOL found = FALSE;
 
-        if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, -1))
-            goto out;
-
         for (int n = 0; n < 8; n++)
         if (codeBook1[n] == data && !found)
         {
@@ -471,7 +455,7 @@
             switch (n)
             {
                 case 4: // Y = 4, JTO = $0B
-                    if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, n))
+                    if ((res = fmtTruePaintGetByte(&src, &data, n)) != DMERR_OK)
                         goto out;
 
                     count = data;
@@ -485,12 +469,12 @@
                     // fallthrough
 
                 case 0: // Y = 0, JTO = $19
-                    if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, n))
+                    if ((res = fmtTruePaintGetByte(&src, &data, n)) != DMERR_OK)
                         goto out;
                     break;
 
                 case 2: // Y = 2, JTO = $07
-                    if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, n))
+                    if ((res = fmtTruePaintGetByte(&src, &data, n)) != DMERR_OK)
                         goto out;
 
                     count = data;
@@ -508,23 +492,16 @@
             }
         }
 
-        for (scount = count; count; count--)
-        {
-            if (!dmReversePutByte(dst, &dstOffs, data))
-            {
-                res = dmError(DMERR_INVALID_DATA,
-                    "TruePaintRLE: Out of output space for run: %d x $%02x!\n",
-                    scount, data);
-                goto out;
-            }
-        }
+        if ((res = dmGenericRLEOutputRun(&dst, &cfg, data, count)) != DMERR_OK)
+            goto out;
     }
 
 finish:
-    res = dmC64DecodeGenericBMP(img, dmGrowBufCreateFrom(&dstTmp, dst, dstLen), fmt);
+    dmFinishRLEBuffers(&dst, &src, &cfg);
+    res = dmC64DecodeGenericBMP(img, &dst, fmt);
 
 out:
-    dmFree(dst);
+    dmGrowBufFree(&dst);
     return res;
 }
 
@@ -593,9 +570,10 @@
         DMGrowBuf mem;
         DMCompParams cfg;
 
-        cfg.type        = DM_COMP_RLE_MARKER;
-        cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1;
-        cfg.rleMarkerB  = buf->data[15];
+        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];
 
         if ((res = dmDecodeGenericRLEAlloc(
             &mem, dmGrowBufCreateFromOffs(&tmp, buf, FUNPAINT2_HEADER_SIZE), &cfg)) == DMERR_OK)
@@ -634,6 +612,7 @@
         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 = 3;
@@ -757,6 +736,40 @@
 }
 
 
+static BOOL fmtECIGetLaceType(DMC64Image *img, const DMC64EncDecOp *op,
+    const DMGrowBuf *buf, const DMC64ImageFormat *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 raster)
+{
+    const int vbank = raster & 7;
+    Uint8
+        c1 = dmC64GetGenericSCPixel(img, bmoffs, scroffs, shift, vbank    , 0, 0),
+        c2 = dmC64GetGenericSCPixel(img, bmoffs, scroffs, shift, vbank + 8, 1, 0);
+
+    (void) bitmap;
+    return (c1 * C64_NCOLORS) + c2;
+}
+
+
+static int fmtECIConvertBMP2Image(DMImage *dst, const DMC64Image *src, const DMC64ImageFormat *fmt)
+{
+    if (!dmSetMixedColorC64Palette(dst))
+        return DMERR_MALLOC;
+
+    return dmC64ConvertGenericBMP2Image(dst, src, fmt);
+}
+
+
 //
 // Helper macros for defining screen ram layouts
 // common for FLI type foramts
@@ -1164,7 +1177,27 @@
     },
 
     {
-        D64_FMT_MC, "xx1", "Unknown $2000 format (unpacked)", 0x2000, 10242, DM_FMT_RD,
+        D64_FMT_HIRES | D64_FMT_FLI,
+        "eci", "ECI Graphic Editor 1.0 (unpacked)", 0x4000, 32770, DM_FMT_RD,
+        C64_SCR_WIDTH, C64_SCR_HEIGHT,
+        C64_SCR_CH_WIDTH, C64_SCR_CH_HEIGHT,
+        NULL,
+        NULL, NULL,
+        fmtECIConvertBMP2Image, NULL,
+        fmtGetPixelECI,
+        {
+            { DO_COPY     , DS_BITMAP_RAM  , 0x0000, 0,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8(0x2000, 0, 0x400),
+            { DO_COPY     , DS_BITMAP_RAM  , 0x4000, 1,  0, NULL, NULL },
+            DEF_SCREEN_RAMS_8(0x6000, 8, 0x400),
+            { DO_DEC_FUNC , 0              , 0     , 0,  0, fmtECIGetLaceType, NULL },
+            { DO_LAST     , 0              , 0     , 0,  0, NULL, NULL },
+        },
+        NULL
+    },
+
+    {
+        D64_FMT_MC, "xx1", "Unknown $2000 format (unpacked)", 0x2000, 10242, DM_FMT_RDWR,
         C64_SCR_WIDTH / 2, C64_SCR_HEIGHT,
         C64_SCR_CH_WIDTH , C64_SCR_CH_HEIGHT,
         NULL,
@@ -1183,7 +1216,7 @@
     },
 
     {
-        D64_FMT_MC, "xx2", "Unknown $2000 format (unpacked)", 0x2000, 0, DM_FMT_RD,
+        D64_FMT_MC, "xx2", "Unknown $2000 format (unpacked)", 0x2000, 0, DM_FMT_RDWR,
         XX2_WIDTH_CH * 4, XX2_HEIGHT_CH * 8,
         XX2_WIDTH_CH    , XX2_HEIGHT_CH,
         fmtProbeFormatXX2,
--- a/tools/lib64gfx.c	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/lib64gfx.c	Fri Jun 08 22:32:30 2018 +0300
@@ -12,6 +12,28 @@
 #define BUF_SIZE_GROW      (4*1024)
 
 
+// Based on Pepto's palette, stolen from VICE
+DMColor dmDefaultC64Palette[C64_NCOLORS] =
+{
+    { 0x00, 0x00, 0x00, 0xff },
+    { 0xFF, 0xFF, 0xFF, 0xff },
+    { 0x68, 0x37, 0x2B, 0xff },
+    { 0x70, 0xA4, 0xB2, 0xff },
+    { 0x6F, 0x3D, 0x86, 0xff },
+    { 0x58, 0x8D, 0x43, 0xff },
+    { 0x35, 0x28, 0x79, 0xff },
+    { 0xB8, 0xC7, 0x6F, 0xff },
+    { 0x6F, 0x4F, 0x25, 0xff },
+    { 0x43, 0x39, 0x00, 0xff },
+    { 0x9A, 0x67, 0x59, 0xff },
+    { 0x44, 0x44, 0x44, 0xff },
+    { 0x6C, 0x6C, 0x6C, 0xff },
+    { 0x9A, 0xD2, 0x84, 0xff },
+    { 0x6C, 0x5E, 0xB5, 0xff },
+    { 0x95, 0x95, 0x95, 0xff },
+};
+
+
 char * dmC64GetImageTypeString(char *buf, const size_t len, const int type, const BOOL lng)
 {
     snprintf(buf, len,
@@ -28,9 +50,9 @@
 
 void dmC64ImageDump(FILE *fh, const DMC64Image *img, const DMC64ImageFormat *fmt)
 {
-    char typeStr[64];
+    char typeStr[64], typeStr2[64];
 
-    dmC64GetImageTypeString(typeStr, sizeof(typeStr), img->type, TRUE);
+    dmC64GetImageTypeString(typeStr, sizeof(typeStr), fmt->type, TRUE);
 
     if (fmt != NULL)
     {
@@ -39,31 +61,48 @@
             fmt->name, fmt->fext);
     }
 
-    fprintf(fh,
-        "Type                : %s\n"
-        "Banks               : %d\n",
-        typeStr,
-        img->nbanks);
+    if (img != NULL)
+    {
+        dmC64GetImageTypeString(typeStr2, sizeof(typeStr2), img->type, TRUE);
 
-    if (img->type & D64_FMT_ILACE)
-    {
-        char *tmps;
-        switch (img->laceType)
+        fprintf(fh,
+            "Type                : %s [%s]\n"
+            "Banks               : %d\n",
+            typeStr, typeStr2,
+            img->nbanks);
+
+        if (img->type & D64_FMT_ILACE)
         {
-            case D64_ILACE_COLOR: tmps = "color"; break;
-            case D64_ILACE_RES: tmps = "resolution"; break;
-            default: tmps = "ERROR"; break;
+            char *tmps;
+            switch (img->laceType)
+            {
+                case D64_ILACE_COLOR: tmps = "color"; break;
+                case D64_ILACE_RES: tmps = "resolution"; break;
+                default: tmps = "ERROR"; break;
+            }
+            fprintf(fh,
+                "Interlace type      : %s\n",
+                tmps);
         }
+
         fprintf(fh,
-            "Interlace type      : %s\n",
-            tmps);
+            "Width x Height      : %d x %d [%d x %d]\n"
+            "CHwidth x CHheight  : %d x %d [%d x %d]\n",
+            img->width, img->height,
+            fmt->width, fmt->height,
+            img->chWidth, img->chHeight,
+            fmt->chWidth, fmt->chHeight);
     }
-
-    fprintf(fh,
-        "Width x Height      : %d x %d\n"
-        "CHwidth x CHheight  : %d x %d\n",
-        img->width, img->height,
-        img->chWidth, img->chHeight);
+    else
+    {
+        fprintf(fh,
+            "Type                : %s\n"
+            "Width x Height      : %d x %d\n"
+            "CHwidth x CHheight  : %d x %d\n",
+            typeStr,
+            fmt->width, fmt->height,
+            fmt->chWidth, fmt->chHeight);
+    }
 }
 
 
@@ -76,6 +115,29 @@
 }
 
 
+BOOL dmSetMixedColorC64Palette(DMImage *img)
+{
+    if (!dmImagePaletteAlloc(img, C64_NCOLORS * C64_NCOLORS, -1))
+        return FALSE;
+
+    int n = 0;
+    for (int n1 = 0; n1 < C64_NCOLORS; n1++)
+    {
+        const DMColor *col1 = &dmDefaultC64Palette[n1];
+        for (int n2 = 0; n2 < C64_NCOLORS; n2++)
+        {
+            const DMColor *col2 = &dmDefaultC64Palette[n2];
+            img->pal[n].r = (col1->r + col2->r) / 2;
+            img->pal[n].g = (col1->g + col2->g) / 2;
+            img->pal[n].b = (col1->b + col2->b) / 2;
+            n++;
+        }
+    }
+
+    return TRUE;
+}
+
+
 BOOL dmCompareAddr16(const Uint8 *buf, const size_t offs, const Uint16 addr)
 {
     return buf[offs    ] == DM_GET_ADDR_LO(addr) &&
@@ -100,30 +162,6 @@
 }
 
 
-BOOL dmReverseGetByte(const Uint8 *buf, size_t *offs, Uint8 *data)
-{
-    if (*offs > 0)
-    {
-        *data = buf[--(*offs)];
-        return TRUE;
-    }
-    else
-        return FALSE;
-}
-
-
-BOOL dmReversePutByte(Uint8 *buf, size_t *offs, const Uint8 data)
-{
-    if (*offs > 0)
-    {
-        buf[--(*offs)] = data;
-        return TRUE;
-    }
-    else
-        return FALSE;
-}
-
-
 int dmC64MemBlockAlloc(DMC64MemBlock *blk, const size_t size)
 {
     if ((blk->data = dmMalloc0(size)) == NULL)
@@ -327,7 +365,7 @@
 }
 
 
-static void dmSetupRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg)
+void dmSetupRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg)
 {
     if (cfg->flags & DM_RLE_BACKWARDS_INPUT)
     {
@@ -343,6 +381,49 @@
 }
 
 
+void dmFinishRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg)
+{
+    if (cfg->flags & DM_RLE_BACKWARDS_OUTPUT)
+    {
+        memmove(dst->data, dst->data + dst->offs, dst->len);
+        dst->offs = 0;
+    }
+
+    switch (cfg->flags & DM_OUT_CROP_MASK)
+    {
+        case DM_OUT_CROP_END:
+            if (cfg->cropOutLen < dst->len)
+            {
+                memmove(dst->data, dst->data + dst->len - cfg->cropOutLen + 1, cfg->cropOutLen);
+                dst->len = cfg->cropOutLen;
+            }
+            break;
+
+        case DM_OUT_CROP_START:
+            if (cfg->cropOutLen <= dst->len)
+                dst->len = cfg->cropOutLen;
+            break;
+    }
+}
+
+
+int dmGenericRLEOutputRun(DMGrowBuf *dst, const DMCompParams *cfg, const Uint8 data, const unsigned int count)
+{
+    unsigned int scount;
+    for (scount = count; scount; scount--)
+    {
+        if (!dmGrowBufPutU8(dst, data))
+        {
+            return dmError(DMERR_MALLOC,
+                "%s: RLE: Could not output RLE run %d x 0x%02x @ "
+                "offs=0x%" DM_PRIx_SIZE_T ", size=0x%" DM_PRIx_SIZE_T ".\n",
+                cfg->func, count, data, dst->offs, dst->size);
+        }
+    }
+    return DMERR_OK;
+}
+
+
 int dmDecodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *psrc, const DMCompParams *cfg)
 {
     int res;
@@ -366,7 +447,9 @@
                 if (!dmGrowBufGetU8(&src, &tmp1) ||
                     !dmGrowBufGetU8(&src, &tmp2))
                 {
-                    res = DMERR_INVALID_DATA;
+                    res = dmError(DMERR_INVALID_DATA,
+                        "%s: RLE: Invalid data/out of data for byte length run sequence.\n",
+                        cfg->func);
                     goto out;
                 }
                 switch (cfg->flags & DM_RLE_ORDER_MASK)
@@ -389,7 +472,9 @@
                     !dmGrowBufGetU8(&src, &tmp2) ||
                     !dmGrowBufGetU8(&src, &tmp3))
                 {
-                    res = DMERR_INVALID_DATA;
+                    res = dmError(DMERR_INVALID_DATA,
+                        "%s: RLE: Invalid data/out of data for word length run sequence.\n",
+                        cfg->func);
                     goto out;
                 }
                 switch (cfg->flags & DM_RLE_ORDER_MASK)
@@ -415,7 +500,9 @@
             {
                 if (!dmGrowBufGetU8(&src, &tmp1))
                 {
-                    res = DMERR_INVALID_DATA;
+                    res = dmError(DMERR_INVALID_DATA,
+                        "%s: RLE: Invalid data/out of data for byte length mask/run sequence.\n",
+                        cfg->func);
                     goto out;
                 }
 
@@ -424,16 +511,11 @@
             }
         }
 
-        while (count--)
-        {
-            if (!dmGrowBufPutU8(dst, data))
-            {
-                res = DMERR_MALLOC;
-                goto out;
-            }
-        }
+        if ((res = dmGenericRLEOutputRun(dst, cfg, data, count)) != DMERR_OK)
+            goto out;
     }
 
+    dmFinishRLEBuffers(dst, &src, cfg);
     res = DMERR_OK;
 
 out:
@@ -451,9 +533,10 @@
 }
 
 
-static BOOL dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, unsigned int count, const DMCompParams *cfg)
+int dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, const unsigned int count, const DMCompParams *cfg)
 {
     BOOL copyOnly = FALSE;
+    int res;
 
     switch (cfg->type)
     {
@@ -463,20 +546,20 @@
             {
                 // A simple marker byte RLE variant: [Marker] [count] [data]
                 if (!dmGrowBufPutU8(dst, cfg->rleMarkerW))
-                    return FALSE;
+                    goto err;
 
                 switch (cfg->flags & DM_RLE_ORDER_MASK)
                 {
                     case DM_RLE_ORDER_1:
                         if (!dmGrowBufPutU16LE(dst, count) ||
                             !dmGrowBufPutU8(dst, data))
-                            return FALSE;
+                            goto err;
                         break;
 
                     case DM_RLE_ORDER_2:
                         if (!dmGrowBufPutU8(dst, data) ||
                             !dmGrowBufPutU16LE(dst, count))
-                            return FALSE;
+                            goto err;
                         break;
                 }
             }
@@ -486,20 +569,20 @@
             {
                 // A simple marker byte RLE variant: [Marker] [count] [data]
                 if (!dmGrowBufPutU8(dst, cfg->rleMarkerB))
-                    return FALSE;
+                    goto err;
 
                 switch (cfg->flags & DM_RLE_ORDER_MASK)
                 {
                     case DM_RLE_ORDER_1:
                         if (!dmGrowBufPutU8(dst, count) ||
                             !dmGrowBufPutU8(dst, data))
-                            return FALSE;
+                            goto err;
                         break;
 
                     case DM_RLE_ORDER_2:
                         if (!dmGrowBufPutU8(dst, data) ||
                             !dmGrowBufPutU8(dst, count))
-                            return FALSE;
+                            goto err;
                         break;
                 }
             }
@@ -514,23 +597,22 @@
                 // and the lower bits contain the count: [Mask + count] [data]
                 if (!dmGrowBufPutU8(dst, cfg->rleMarkerBits | count) ||
                     !dmGrowBufPutU8(dst, data))
-                    return FALSE;
+                    goto err;
             }
             else
                 copyOnly = TRUE;
             break;
     }
 
-    if (copyOnly)
-    {
-        while (count--)
-        {
-            if (!dmGrowBufPutU8(dst, data))
-                return FALSE;
-        }
-    }
+    if (copyOnly && (res = dmGenericRLEOutputRun(dst, cfg, data, count)) != DMERR_OK)
+        return res;
+
+    return DMERR_OK;
 
-    return TRUE;
+err:
+    return dmError(DMERR_MALLOC,
+        "%s: RLE: Could not output RLE sequence %d x 0x%02x.\n",
+        cfg->func, count, data);
 }
 
 
@@ -538,7 +620,7 @@
 {
     DMGrowBuf src;
     unsigned int count = 0;
-    int prev = -1;
+    int prev = -1, res = DMERR_OK;
     Uint8 data;
 
     // As we need to modify the offs, etc. but not the data,
@@ -554,7 +636,7 @@
             ((cfg->flags & DM_RLE_WORD_RUNS) && count >= cfg->rleMaxCountW) ||
             (((cfg->flags & DM_RLE_RUNS_MASK) == DM_RLE_BYTE_RUNS) && count >= cfg->rleMaxCountB))
         {
-            if (!dmEncodeGenericRLESequence(dst, prev, count, cfg))
+            if ((res = dmEncodeGenericRLESequence(dst, prev, count, cfg)) != DMERR_OK)
                 goto err;
 
             count = 1;
@@ -565,14 +647,14 @@
         prev = data;
     }
 
-    if (!dmEncodeGenericRLESequence(dst, prev, count, cfg))
+    // If there is anything left in the output queue ..
+    if ((res = dmEncodeGenericRLESequence(dst, prev, count, cfg)) != DMERR_OK)
         goto err;
 
-    return DMERR_OK;
+    dmFinishRLEBuffers(dst, &src, cfg);
 
 err:
-     return dmError(DMERR_MALLOC,
-        "Could not reallocate memory for RLE encoding buffer.\n");
+    return res;
 }
 
 
@@ -631,6 +713,8 @@
     {
         case DO_COPY:
         case DO_SET_MEM:
+        case DO_SET_MEM_HI:
+        case DO_SET_MEM_LO:
         case DO_SET_OP:
             switch (op->subject)
             {
@@ -677,38 +761,34 @@
 
 BOOL dmC64GetOpSize(const DMC64EncDecOp *op, const DMC64ImageFormat *fmt, size_t *size)
 {
-    // Default to size of 0
-    *size = 0;
-
-    switch (op->type)
+    switch (op->subject)
     {
-        case DO_COPY:
-        case DO_SET_MEM:
-        case DO_SET_OP:
-            switch (op->subject)
-            {
-                case DS_SCREEN_RAM:
-                case DS_COLOR_RAM:
-                    *size = fmt->chHeight * fmt->chWidth;
-                    break;
+        case DS_SCREEN_RAM:
+        case DS_COLOR_RAM:
+            *size = fmt->chHeight * fmt->chWidth;
+            break;
+
+        case DS_BITMAP_RAM:
+            *size = fmt->chHeight * fmt->chWidth * 8;
+            break;
 
-                case DS_BITMAP_RAM:
-                    *size = fmt->chHeight * fmt->chWidth * 8;
-                    break;
-
-                case DS_CHAR_DATA:
-                    *size = C64_MAX_CHARS * C64_CHR_SIZE;
-                    break;
+        case DS_CHAR_DATA:
+            *size = C64_MAX_CHARS * C64_CHR_SIZE;
+            break;
 
-                case DS_D020:
-                case DS_BGCOL:
-                case DS_D021:
-                case DS_D022:
-                case DS_D023:
-                case DS_D024:
-                    *size = 1;
-                    break;
-            }
+        case DS_D020:
+        case DS_BGCOL:
+        case DS_D021:
+        case DS_D022:
+        case DS_D023:
+        case DS_D024:
+            *size = 1;
+            break;
+
+        default:
+            // Default to size of 0
+            *size = 0;
+            break;
     }
 
     // If the operator specified size is larger, use it.
@@ -792,6 +872,8 @@
         {
             case DO_COPY:
             case DO_SET_MEM:
+            case DO_SET_MEM_HI:
+            case DO_SET_MEM_LO:
             case DO_SET_OP:
                 switch (op->subject)
                 {
@@ -822,6 +904,12 @@
                             case DO_SET_OP:
                                 memset(blk->data, op->offs, size);
                                 break;
+
+                            default:
+                                return dmError(DMERR_INTERNAL,
+                                    "Unhandled op type %d in "
+                                    "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
+                                    op->type, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
                         }
                         break;
 
@@ -842,6 +930,13 @@
                                 value = op->offs;
                                 break;
 
+                            case DO_SET_MEM_HI:
+                                value = (*src >> 4) & 0x0f;
+                                break;
+
+                            case DO_SET_MEM_LO:
+                                value = *src & 0x0f;
+                                break;
                         }
                         switch (op->subject)
                         {
@@ -1044,6 +1139,14 @@
                                 *dst = value;
                                 break;
 
+                            case DO_SET_MEM_HI:
+                                *dst |= (value & 0x0f) << 4;
+                                break;
+
+                            case DO_SET_MEM_LO:
+                                *dst |= value & 0x0f;
+                                break;
+
                             case DO_SET_OP:
                                 // Do nothing in this case
                                 break;
@@ -1238,6 +1341,7 @@
     // Set partial palette information
     dst->ncolors  = C64_NCOLORS;
     dst->constpal = TRUE;
+    dst->pal      = dmDefaultC64Palette;
 
     // Convert
     if (fmt->convertFrom != NULL)
--- a/tools/lib64gfx.h	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/lib64gfx.h	Fri Jun 08 22:32:30 2018 +0300
@@ -86,6 +86,9 @@
     DO_SET_MEM,            // Set subject value or fill subject memory area to byte value from offset
     DO_SET_OP,             // Like above but value from op->offs field instead
 
+    DO_SET_MEM_HI,         // Copy one byte value like DO_SET_MEM, but high nibble only
+    DO_SET_MEM_LO,         // Like above, but low nibble
+
     DO_DEC_FUNC,           // Execute specified decoding function (only for decoding operations)
     DO_ENC_FUNC,           // Same, but for encoding
 
@@ -230,15 +233,23 @@
     DM_RLE_ORDER_2          = 0x0010, // Order: <marker>, <data>, <count/run length>
     DM_RLE_ORDER_MASK       = 0x00f0,
 
-    DM_RLE_BACKWARDS_INPUT  = 0x1000,
-    DM_RLE_BACKWARDS_OUTPUT = 0x2000,
+    DM_RLE_BACKWARDS_INPUT  = 0x0100,
+    DM_RLE_BACKWARDS_OUTPUT = 0x0200,
+
+    DM_OUT_CROP_NONE        = 0x0000,
+    DM_OUT_CROP_START       = 0x1000,
+    DM_OUT_CROP_END         = 0x2000,
+    DM_OUT_CROP_MASK        = 0xf000,
 };
 
 
 typedef struct
 {
+    char *func;
     int type;
     int flags;
+    size_t cropOutLen;
+
     Uint8
         // DM_COMP_RLE_MARKER mode
         rleMarkerB,          // Marker byte for byte length runs (if DM_RLE_BYTE_RUNS used)
@@ -273,10 +284,10 @@
 void      dmC64ImageDump(FILE *fh, const DMC64Image *img, const DMC64ImageFormat *fmt);
 
 void      dmSetDefaultC64Palette(DMImage *img);
+BOOL      dmSetMixedColorC64Palette(DMImage *img);
+
 BOOL      dmCompareAddr16(const Uint8 *buf, const size_t offs, const Uint16 addr);
 int       dmC64ImageGetNumBanks(const DMC64ImageFormat *fmt);
-BOOL      dmReverseGetByte(const Uint8 *buf, size_t *offs, Uint8 *data);
-BOOL      dmReversePutByte(Uint8 *buf, size_t *offs, const Uint8 data);
 
 // C64 bitmap image allocation/freeing
 int       dmC64MemBlockAlloc(DMC64MemBlock *blk, const size_t size);
@@ -309,6 +320,11 @@
 
 void      dmGenericRLEAnalyze(const DMGrowBuf *buf, DMCompParams *cfg);
 
+void      dmSetupRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg);
+void      dmFinishRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg);
+int       dmGenericRLEOutputRun(DMGrowBuf *dst, const DMCompParams *cfg, const Uint8 data, const unsigned int count);
+int       dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, const unsigned int count, const DMCompParams *cfg);
+
 int       dmDecodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
 int       dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
 
--- a/tools/libgfx.c	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/libgfx.c	Fri Jun 08 22:32:30 2018 +0300
@@ -172,6 +172,8 @@
 
     img->ncolors = ncolors;
     img->ctransp = ctransp;
+    img->constpal = FALSE;
+
     return dmPaletteAlloc(&(img->pal), ncolors, ctransp);
 }
 
--- a/tools/libgfx.h	Fri Jun 08 22:32:11 2018 +0300
+++ b/tools/libgfx.h	Fri Jun 08 22:32:30 2018 +0300
@@ -119,6 +119,8 @@
 int       dmImageProbeGeneric(const Uint8 *buf, const size_t len, const DMImageFormat **fmt, int *index);
 
 BOOL dmCompareColor(const DMColor *c1, const DMColor *c2, BOOL alpha);
+BOOL dmPaletteAlloc(DMColor **ppal, int ncolors, int ctransp);
+BOOL dmImagePaletteAlloc(DMImage *img, int ncolors, int ctransp);
 
 
 int dmWriteImageData(const DMImage *img, void *cbdata, int (*writeRowCB)(void *, const Uint8 *, const size_t), const DMImageConvSpec *spec);