changeset 1707:a0986cfd6f9d

More consistently use DMGrowBuf in the lib64gfx APIs, and implement "backwards" RLE decoding and encoding (optionally regards input/output). Not tested very much yet, there may be bugs.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 05 Jun 2018 21:58:10 +0300
parents 311b14855a1e
children 55db8d6dffa5
files src/dmgrowbuf.c tools/64vw.c tools/gfxconv.c tools/lib64fmts.c tools/lib64gfx.c tools/lib64gfx.h
diffstat 6 files changed, 201 insertions(+), 170 deletions(-) [+]
line wrap: on
line diff
--- a/src/dmgrowbuf.c	Tue Jun 05 19:57:08 2018 +0300
+++ b/src/dmgrowbuf.c	Tue Jun 05 21:58:10 2018 +0300
@@ -218,7 +218,9 @@
             return FALSE;
     }
 
-    buf->len = nsize;
+    if (nsize > buf->len)
+        buf->len = nsize;
+
     return TRUE;
 }
 
--- a/tools/64vw.c	Tue Jun 05 19:57:08 2018 +0300
+++ b/tools/64vw.c	Tue Jun 05 21:58:10 2018 +0300
@@ -180,12 +180,13 @@
 {
     Uint8 *dataBuf = NULL;
     size_t dataSize;
+    DMGrowBuf tmp;
     int ret;
 
     if ((ret = dmReadDataFile(NULL, filename, &dataBuf, &dataSize)) != DMERR_OK)
         goto exit;
 
-    ret = dmC64DecodeBMP(cimage, dataBuf, dataSize, 0, 2, fmt, forced);
+    ret = dmC64DecodeBMP(cimage, dmGrowBufCreateFrom(&tmp, dataBuf, dataSize), 0, 2, fmt, forced);
 
 exit:
     dmFree(dataBuf);
--- a/tools/gfxconv.c	Tue Jun 05 19:57:08 2018 +0300
+++ b/tools/gfxconv.c	Tue Jun 05 21:58:10 2018 +0300
@@ -1686,7 +1686,7 @@
     // Check and compute the input skip
     if (optInSkip > dataSizeOrig)
     {
-        dmErrorMsg("Input skip value %d is larger than input size %d.\n",
+        dmErrorMsg("Input skip value %d is larger than input size %" DM_PRIu_SIZE_T ".\n",
             optInSkip, dataSizeOrig);
         goto error;
     }
@@ -1694,12 +1694,12 @@
     dataBuf = dataBufOrig + optInSkip;
     dataSize = dataSizeOrig - optInSkip;
 
-
     // Perform probing, if required
     if (optInFormat == FFMT_AUTO || optInFormat == FFMT_BITMAP)
     {
         // Probe for format
         const DMC64ImageFormat *forced = NULL;
+        DMGrowBuf tbuf;
         int res;
 
         if (optForcedFormat >= 0)
@@ -1709,7 +1709,7 @@
                 forced->name, forced->type, forced->fext);
         }
 
-        res = dmC64DecodeBMP(&inC64Image, dataBuf, dataSize, optInSkip, optInSkip + 2, &inC64Fmt, forced);
+        res = dmC64DecodeBMP(&inC64Image, dmGrowBufCreateFrom(&tbuf, dataBuf, dataSize), 0, 2, &inC64Fmt, forced);
         if (forced == NULL && inC64Fmt != NULL && res == DMERR_OK)
         {
             dmMsg(1, "Probed '%s' format image, type %d, %s\n",
--- a/tools/lib64fmts.c	Tue Jun 05 19:57:08 2018 +0300
+++ b/tools/lib64fmts.c	Tue Jun 05 21:58:10 2018 +0300
@@ -31,7 +31,6 @@
 };
 
 
-
 static int fmtProbeKoalaPaintPacked(const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
 {
     // Attempt to prevent misprobes of unpacked Koala and Run Paint
@@ -45,7 +44,7 @@
 }
 
 
-static int fmtDecodeKoalaPaintPacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+static int fmtDecodeKoalaPaintPacked(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     int res;
     DMGrowBuf mem;
@@ -54,10 +53,11 @@
     cfg.type        = DM_COMP_RLE_MARKER;
     cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_2;
     cfg.rleMarkerB  = 0xfe;
-    if ((res = dmDecodeGenericRLEAlloc(&mem, buf, buf + len, &cfg)) != DMERR_OK)
+
+    if ((res = dmDecodeGenericRLEAlloc(&mem, buf, &cfg)) != DMERR_OK)
         goto out;
 
-    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+    res = dmC64DecodeGenericBMP(img, &mem, fmt);
 
 out:
     dmGrowBufFree(&mem);
@@ -81,7 +81,7 @@
     cfg.rleMarkerB   = 0xfe;
     cfg.rleMinCountB = 3;
     cfg.rleMaxCountB = 255;
-    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);
+    res = dmEncodeGenericRLE(buf, &tmp, &cfg);
 
 out:
     dmGrowBufFree(&tmp);
@@ -105,20 +105,21 @@
 }
 
 
-static int fmtDecodeDrazPaintPacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+static int fmtDecodeDrazPaintPacked(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     int res;
-    DMGrowBuf mem;
+    DMGrowBuf mem, tmp;
     DMCompParams cfg;
 
     cfg.type        = DM_COMP_RLE_MARKER;
     cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1;
-    cfg.rleMarkerB  = buf[0x0d];
+    cfg.rleMarkerB  = buf->data[0x0d];
 
-    if ((res = dmDecodeGenericRLEAlloc(&mem, buf + 0x0e, buf + len, &cfg)) != DMERR_OK)
+    if ((res = dmDecodeGenericRLEAlloc(&mem,
+        dmGrowBufCreateFromOffs(&tmp, buf, 0x0e), &cfg)) != DMERR_OK)
         goto out;
 
-    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+    res = dmC64DecodeGenericBMP(img, &mem, fmt);
 
 out:
     dmGrowBufFree(&mem);
@@ -143,7 +144,7 @@
     cfg.rleMinCountB = 3;
     cfg.rleMaxCountB = 255;
 
-    dmGenericRLEAnalyze(tmp.data, tmp.len, &cfg);
+    dmGenericRLEAnalyze(&tmp, &cfg);
 
     // Add the header bits
     if (!dmGrowBufPut(buf, (Uint8 *) magicID, strlen(magicID)) ||
@@ -154,7 +155,7 @@
     }
 
     // And now RLE compress the data to the existing buffer
-    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);
+    res = dmEncodeGenericRLE(buf, &tmp, &cfg);
 
 out:
     dmGrowBufFree(&tmp);
@@ -174,11 +175,10 @@
 
 
 static BOOL fmtDrazLaceGetLaceType(DMC64Image *img, const DMC64EncDecOp *op,
-    const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+    const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
-    (void) len;
     (void) fmt;
-    img->laceType = buf[op->offs] ? D64_ILACE_RES : D64_ILACE_COLOR;
+    img->laceType = buf->data[op->offs] ? D64_ILACE_RES : D64_ILACE_COLOR;
     return TRUE;
 }
 
@@ -205,21 +205,22 @@
 }
 
 
-static int fmtDecodeBDP5Packed(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+static int fmtDecodeBDP5Packed(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     int res;
-    DMGrowBuf mem;
+    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[8];
-    cfg.rleMarkerW  = buf[9];
+    cfg.rleMarkerB  = buf->data[8];
+    cfg.rleMarkerW  = buf->data[9];
 
-    if ((res = dmDecodeGenericRLEAlloc(&mem, buf + 10, buf + len, &cfg)) != DMERR_OK)
+    if ((res = dmDecodeGenericRLEAlloc(&mem,
+        dmGrowBufCreateFromOffs(&tmp, buf, 10), &cfg)) != DMERR_OK)
         goto out;
 
-    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+    res = dmC64DecodeGenericBMP(img, &mem, fmt);
 
 out:
     dmGrowBufFree(&mem);
@@ -230,11 +231,11 @@
 static int fmtEncodeBDP5Packed(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
 {
     int res;
-    DMGrowBuf tmp;
+    DMGrowBuf mem;
     DMCompParams cfg;
 
     // Encode the data to temp buffer
-    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
+    if ((res = dmC64EncodeGenericBMP(TRUE, &mem, img, fmt)) != DMERR_OK)
         goto out;
 
     // Analyze and setup RLE
@@ -245,7 +246,7 @@
     cfg.rleMinCountW = 256;
     cfg.rleMaxCountW = 1024;
 
-    dmGenericRLEAnalyze(tmp.data, tmp.len, &cfg);
+    dmGenericRLEAnalyze(&mem, &cfg);
 
     // Add the header bits
     if (!dmGrowBufPut(buf, (Uint8 *) fmtBDP5MagicID, strlen(fmtBDP5MagicID)) ||
@@ -257,10 +258,10 @@
     }
 
     // And now RLE compress the data to the existing buffer
-    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);
+    res = dmEncodeGenericRLE(buf, &mem, &cfg);
 
 out:
-    dmGrowBufFree(&tmp);
+    dmGrowBufFree(&mem);
     return res;
 }
 
@@ -302,7 +303,7 @@
 }
 
 
-static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     int res;
     DMGrowBuf mem, tmp;
@@ -310,22 +311,22 @@
 
     // Amica Paint apparently is broken and stores one byte less than it should
     // so we need to do some crappy buffer expansion here ..
-    if ((res = dmGrowBufAlloc(&tmp, len + 4, 4)) != DMERR_OK)
+    if ((res = dmGrowBufAlloc(&tmp, buf->len + 4, 4)) != DMERR_OK)
         return res;
 
-    memcpy(tmp.data, buf, len);
-    tmp.len = len + 1;
+    memcpy(tmp.data, buf->data, buf->len);
+    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;
 
-    if ((res = dmDecodeGenericRLEAlloc(&mem, tmp.data, tmp.data + tmp.len, &cfg)) != DMERR_OK)
+    if ((res = dmDecodeGenericRLEAlloc(&mem, &tmp, &cfg)) != DMERR_OK)
         goto out;
 
     // And finally decode to bitmap struct
-    res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+    res = dmC64DecodeGenericBMP(img, &mem, fmt);
 
 out:
     dmGrowBufFree(&tmp);
@@ -337,11 +338,11 @@
 static int fmtEncodeAmicaPaintPacked(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
 {
     int res;
-    DMGrowBuf tmp;
+    DMGrowBuf mem;
     DMCompParams cfg;
 
     // Encode the data to temp buffer
-    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
+    if ((res = dmC64EncodeGenericBMP(TRUE, &mem, img, fmt)) != DMERR_OK)
         goto out;
 
     // And now RLE compress the data to the existing buffer
@@ -351,10 +352,10 @@
     cfg.rleMinCountB = 3;
     cfg.rleMaxCountB = 255;
 
-    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);
+    res = dmEncodeGenericRLE(buf, &mem, &cfg);
 
 out:
-    dmGrowBufFree(&tmp);
+    dmGrowBufFree(&mem);
     return res;
 }
 
@@ -370,11 +371,10 @@
 
 
 static BOOL fmtTruePaintGetLaceType(DMC64Image *img, const DMC64EncDecOp *op,
-    const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+    const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     (void) op;
     (void) buf;
-    (void) len;
     (void) fmt;
     img->laceType = D64_ILACE_RES;
     return TRUE;
@@ -427,10 +427,11 @@
 }
 
 
-static int fmtDecodeTruePaintPacked(DMC64Image *img, const Uint8 *src, const size_t srcLen, const DMC64ImageFormat *fmt)
+static int fmtDecodeTruePaintPacked(DMC64Image *img, const DMGrowBuf *src, const DMC64ImageFormat *fmt)
 {
     int res = DMERR_OK;
     Uint8 *dst = NULL;
+    DMGrowBuf dstTmp;
     const Uint8 *codeBook1, *codeBook2;
     size_t
         srcOffs, dstOffs,
@@ -439,8 +440,8 @@
         // 1c00-67e8 is the actual area used tho
 
     // Codebooks: #1 is trampoline table markers, #2 is RLE data table
-    codeBook1 = src + 0x81 - 2;
-    codeBook2 = src + 0x85 - 2;
+    codeBook1 = src->data + 0x81 - 2;
+    codeBook2 = src->data + 0x85 - 2;
 
     // Allocate output buffer
     if ((dst = dmMalloc0(dstLen)) == NULL)
@@ -451,7 +452,7 @@
     }
 
     // Begin decompression
-    srcOffs = srcLen;
+    srcOffs = src->len;
     dstOffs = dstLen;
 
     while (srcOffs > 0 && dstOffs > 0)
@@ -460,7 +461,7 @@
         int count = 1, scount;
         BOOL found = FALSE;
 
-        if (!fmtTruePaintGetByte(src, &srcOffs, &data, &res, -1))
+        if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, -1))
             goto out;
 
         for (int n = 0; n < 8; n++)
@@ -470,7 +471,7 @@
             switch (n)
             {
                 case 4: // Y = 4, JTO = $0B
-                    if (!fmtTruePaintGetByte(src, &srcOffs, &data, &res, n))
+                    if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, n))
                         goto out;
 
                     count = data;
@@ -484,12 +485,12 @@
                     // fallthrough
 
                 case 0: // Y = 0, JTO = $19
-                    if (!fmtTruePaintGetByte(src, &srcOffs, &data, &res, n))
+                    if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, n))
                         goto out;
                     break;
 
                 case 2: // Y = 2, JTO = $07
-                    if (!fmtTruePaintGetByte(src, &srcOffs, &data, &res, n))
+                    if (!fmtTruePaintGetByte(src->data, &srcOffs, &data, &res, n))
                         goto out;
 
                     count = data;
@@ -520,7 +521,7 @@
     }
 
 finish:
-    res = dmC64DecodeGenericBMP(img, dst, dstLen, fmt);
+    res = dmC64DecodeGenericBMP(img, dmGrowBufCreateFrom(&dstTmp, dst, dstLen), fmt);
 
 out:
     dmFree(dst);
@@ -545,20 +546,21 @@
 }
 
 
-static int fmtDecodeFormatXX2(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+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.
-    size_t nlen = len < XX2_MIN_SIZE ? XX2_MIN_SIZE : len;
+    size_t nlen = buf->len < XX2_MIN_SIZE ? XX2_MIN_SIZE : buf->len;
     Uint8 *mem = dmMalloc0(nlen);
     if (mem == NULL)
         return DMERR_MALLOC;
 
-    memcpy(mem, buf, len);
-    res = dmC64DecodeGenericBMP(img, mem, nlen, fmt);
+    memcpy(mem, buf->data, buf->len);
+    res = dmC64DecodeGenericBMP(img, dmGrowBufCreateFrom(&tmp, mem, nlen), fmt);
 
     dmFree(mem);
     return res;
@@ -580,29 +582,30 @@
 }
 
 
-static int fmtDecodeFunPaint2(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt)
+static int fmtDecodeFunPaint2(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     int res;
+    DMGrowBuf tmp;
 
     // Check if the data is compressed
-    if (buf[14])
+    if (buf->data[14])
     {
         DMGrowBuf mem;
         DMCompParams cfg;
 
         cfg.type        = DM_COMP_RLE_MARKER;
         cfg.flags       = DM_RLE_BYTE_RUNS | DM_RLE_ORDER_1;
-        cfg.rleMarkerB  = buf[15];
+        cfg.rleMarkerB  = buf->data[15];
 
         if ((res = dmDecodeGenericRLEAlloc(
-            &mem, buf + FUNPAINT2_HEADER_SIZE, buf + len, &cfg)) == DMERR_OK)
-            res = dmC64DecodeGenericBMP(img, mem.data, mem.len, fmt);
+            &mem, dmGrowBufCreateFromOffs(&tmp, buf, FUNPAINT2_HEADER_SIZE), &cfg)) == DMERR_OK)
+            res = dmC64DecodeGenericBMP(img, &mem, fmt);
 
         dmGrowBufFree(&mem);
     }
     else
     {
-        res = dmC64DecodeGenericBMP(img, buf + FUNPAINT2_HEADER_SIZE, len - FUNPAINT2_HEADER_SIZE, fmt);
+        res = dmC64DecodeGenericBMP(img, dmGrowBufCreateFromOffs(&tmp, buf, FUNPAINT2_HEADER_SIZE), fmt);
     }
 
     return res;
@@ -623,11 +626,11 @@
 static int fmtEncodeFunPaint2Packed(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt)
 {
     int res;
-    DMGrowBuf tmp;
+    DMGrowBuf mem;
     DMCompParams cfg;
 
     // Encode the data to temp buffer
-    if ((res = dmC64EncodeGenericBMP(TRUE, &tmp, img, fmt)) != DMERR_OK)
+    if ((res = dmC64EncodeGenericBMP(TRUE, &mem, img, fmt)) != DMERR_OK)
         goto out;
 
     // Analyze and setup RLE
@@ -636,7 +639,7 @@
     cfg.rleMinCountB = 3;
     cfg.rleMaxCountB = 255;
 
-    dmGenericRLEAnalyze(tmp.data, tmp.len, &cfg);
+    dmGenericRLEAnalyze(&mem, &cfg);
 
     // Add the header bits
     if (!dmGrowBufPut(buf, (Uint8 *) fmtFunPaint2MagicID, strlen(fmtFunPaint2MagicID)) ||
@@ -647,10 +650,10 @@
     }
 
     // And now RLE compress the data to the existing buffer
-    res = dmEncodeGenericRLE(buf, tmp.data, tmp.data + tmp.len, &cfg);
+    res = dmEncodeGenericRLE(buf, &mem, &cfg);
 
 out:
-    dmGrowBufFree(&tmp);
+    dmGrowBufFree(&mem);
     return res;
 }
 
@@ -1173,6 +1176,7 @@
             { DO_COPY     , DS_SCREEN_RAM  , 0x2000, 0,  0, NULL, NULL },
             { DO_COPY     , DS_COLOR_RAM   , 0x2400, 0,  0, NULL, NULL },
             { DO_SET_OP   , DS_BGCOL       , 0x00  , 0,  0, NULL, NULL },
+            { DO_SET_OP   , DS_EXTRA_DATA  , 10240 , 0,  0, NULL, NULL },
             { DO_LAST     , 0              , 0     , 0,  0, NULL, NULL },
         },
         NULL
--- a/tools/lib64gfx.c	Tue Jun 05 19:57:08 2018 +0300
+++ b/tools/lib64gfx.c	Tue Jun 05 21:58:10 2018 +0300
@@ -282,7 +282,7 @@
 }
 
 
-void dmGenericRLEAnalyze(const Uint8 *buf, const size_t len, DMCompParams *cfg)
+void dmGenericRLEAnalyze(const DMGrowBuf *buf, DMCompParams *cfg)
 {
 #define DM_STAT_MAX 256
     size_t *stats;
@@ -292,8 +292,8 @@
         return;
 
     // Get statistics on the data
-    for (size_t offs = 0; offs < len; offs++)
-        stats[buf[offs]]++;
+    for (size_t offs = 0; offs < buf->len; offs++)
+        stats[buf->data[offs]]++;
 
     // According to compression type ..
     switch (cfg->type)
@@ -301,7 +301,7 @@
         case DM_COMP_RLE_MARKER:
             {
                 size_t selected = 0,
-                    smallest = len;
+                    smallest = buf->len;
 
                 // Find least used byte value
                 for (size_t n = 0; n < DM_STAT_MAX; n++)
@@ -327,63 +327,83 @@
 }
 
 
-int dmDecodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg)
+static void dmSetupRLEBuffers(DMGrowBuf *dst, DMGrowBuf *src, const DMCompParams *cfg)
+{
+    if (cfg->flags & DM_RLE_BACKWARDS_INPUT)
+    {
+        src->offs = src->len - 1;
+        src->backwards = TRUE;
+    }
+
+    if (cfg->flags & DM_RLE_BACKWARDS_OUTPUT)
+    {
+        dst->backwards = TRUE;
+        dst->offs = dst->size - 1;
+    }
+}
+
+
+int dmDecodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *psrc, const DMCompParams *cfg)
 {
     int res;
+    Uint8 tmp1, tmp2, tmp3, data;
+    DMGrowBuf src;
 
-    // Perform RLE decode
-    while (src < srcEnd)
+    // 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 (dmGrowBufGetU8(&src, &data))
     {
-        Uint8 data = *src++;
-        int count = 1;
+        unsigned int count = 1;
 
         if (cfg->type == DM_COMP_RLE_MARKER)
         {
             // A simple marker byte RLE variant: [Marker] [count] [data]
             if (data == cfg->rleMarkerB && (cfg->flags & DM_RLE_BYTE_RUNS))
             {
-                if (srcEnd - src + 1 < 2)
+                if (!dmGrowBufGetU8(&src, &tmp1) ||
+                    !dmGrowBufGetU8(&src, &tmp2))
                 {
                     res = DMERR_INVALID_DATA;
-                    goto err;
+                    goto out;
                 }
-
                 switch (cfg->flags & DM_RLE_ORDER_MASK)
                 {
                     case DM_RLE_ORDER_1:
-                        count = src[0];
-                        data = src[1];
+                        count = tmp1;
+                        data  = tmp2;
                         break;
 
                     case DM_RLE_ORDER_2:
-                        data = src[0];
-                        count = src[1];
+                        data  = tmp1;
+                        count = tmp2;
                         break;
                 }
-                src += 2;
             }
             else
             if (data == cfg->rleMarkerW && (cfg->flags & DM_RLE_WORD_RUNS))
             {
-                if (srcEnd - src + 1 < 3)
+                if (!dmGrowBufGetU8(&src, &tmp1) ||
+                    !dmGrowBufGetU8(&src, &tmp2) ||
+                    !dmGrowBufGetU8(&src, &tmp3))
                 {
                     res = DMERR_INVALID_DATA;
-                    goto err;
+                    goto out;
                 }
-
                 switch (cfg->flags & DM_RLE_ORDER_MASK)
                 {
                     case DM_RLE_ORDER_1:
-                        count = (src[1] << 8) | src[0];
-                        data = src[2];
+                        count = (tmp2 << 8) | tmp1;
+                        data = tmp3;
                         break;
 
                     case DM_RLE_ORDER_2:
-                        data = src[0];
-                        count = (src[2] << 8) | src[1];
+                        data = tmp1;
+                        count = (tmp3 << 8) | tmp2;
                         break;
                 }
-                src += 3;
             }
         }
         else
@@ -393,14 +413,14 @@
             // and the lower bits contain the count: [Mask + count] [data]
             if ((data & cfg->rleMarkerMask) == cfg->rleMarkerBits)
             {
-                if (srcEnd - src + 1 < 1)
+                if (!dmGrowBufGetU8(&src, &tmp1))
                 {
                     res = DMERR_INVALID_DATA;
-                    goto err;
+                    goto out;
                 }
 
                 count = data & cfg->rleCountMask;
-                data = *src++;
+                data = tmp1;
             }
         }
 
@@ -409,29 +429,29 @@
             if (!dmGrowBufPutU8(dst, data))
             {
                 res = DMERR_MALLOC;
-                goto err;
+                goto out;
             }
         }
     }
 
     res = DMERR_OK;
 
-err:
+out:
     return res;
 }
 
 
-int dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg)
+int dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg)
 {
     int res;
     if ((res = dmGrowBufAlloc(dst, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK)
         return res;
 
-    return dmDecodeGenericRLE(dst, src, srcEnd, cfg);
+    return dmDecodeGenericRLE(dst, src, cfg);
 }
 
 
-static BOOL dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, int count, const DMCompParams *cfg)
+static BOOL dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, unsigned int count, const DMCompParams *cfg)
 {
     BOOL copyOnly = FALSE;
 
@@ -514,14 +534,20 @@
 }
 
 
-int dmEncodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg)
+int dmEncodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *psrc, const DMCompParams *cfg)
 {
-    // Perform RLE encoding
-    int count = 0, prev = -1;
-    while (src < srcEnd)
+    DMGrowBuf src;
+    unsigned int count = 0;
+    int prev = -1;
+    Uint8 data;
+
+    // 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 (dmGrowBufGetU8(&src, &data))
     {
-        Uint8 data = *src++;
-
         // If new data byte is different, or we exceed the rleMaxCount
         // for the active runs mode(s) .. then encode the run.
         if (data != prev ||
@@ -546,17 +572,17 @@
 
 err:
      return dmError(DMERR_MALLOC,
-        "Could reallocate memory for RLE encoding buffer.\n");
+        "Could not reallocate memory for RLE encoding buffer.\n");
 }
 
 
-int dmEncodeGenericRLEAlloc(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg)
+int dmEncodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg)
 {
     int res;
     if ((res = dmGrowBufAlloc(dst, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK)
         return res;
 
-    return dmEncodeGenericRLE(dst, src, srcEnd, cfg);
+    return dmEncodeGenericRLE(dst, src, cfg);
 }
 
 
@@ -707,12 +733,11 @@
 }
 
 
-int dmC64DecodeGenericBMP(DMC64Image *img, const Uint8 *buf,
-    const size_t len, const DMC64ImageFormat *fmt)
+int dmC64DecodeGenericBMP(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt)
 {
     int res = DMERR_OK;
 
-    if (buf == NULL || img == NULL || fmt == NULL)
+    if (buf == NULL || buf->data == NULL || img == NULL || fmt == NULL)
         return DMERR_NULLPTR;
 
     // Clear the image structure, set basics
@@ -751,15 +776,16 @@
         }
 
         // Is the operation inside the bounds?
-        if (op->offs + size > len + 1)
+        if (op->offs + size > buf->len + 1)
         {
             return dmError(DMERR_INVALID_DATA,
                 "Decode DATA out of bounds, op #%d type=%d, subj=%d, offs=%d ($%04x), "
                 "bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                i, op->type, op->subject, op->offs, op->offs, op->bank, size, size, len, len);
+                i, op->type, op->subject, op->offs, op->offs, op->bank,
+                size, size, buf->len, buf->len);
         }
 
-        src = buf + op->offs;
+        src = buf->data + op->offs;
 
         // Perform operation
         switch (op->type)
@@ -781,7 +807,7 @@
                             return dmError(DMERR_MALLOC,
                                 "Could not allocate '%s' block! "
                                 "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                                blkname, i, op->offs, op->offs, op->bank, size, size, len, len);
+                                blkname, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
                         }
                         switch (op->type)
                         {
@@ -832,7 +858,7 @@
                         return dmError(DMERR_INTERNAL,
                             "Unhandled subject %d in "
                             "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                            op->subject, i, op->offs, op->offs, op->bank, size, size, len, len);
+                            op->subject, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
                 }
                 break;
 
@@ -854,7 +880,7 @@
                         return dmError(DMERR_INTERNAL,
                             "Unhandled DO_CHAR_CFG mode %d in ",
                             "op #%d, bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                            op->subject, i, op->bank, size, size, len, len);
+                            op->subject, i, op->bank, size, size, buf->len, buf->len);
                 }
                 break;
 
@@ -864,14 +890,14 @@
                     return dmError(DMERR_INTERNAL,
                         "Decode op is a function, but function ptr is NULL: "
                         "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                        i, op->offs, op->offs, op->bank, size, size, len, len);
+                        i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
                 }
-                if (!op->decFunction(img, op, buf, len, fmt))
+                if (!op->decFunction(img, op, buf, fmt))
                 {
                     return dmError(DMERR_INTERNAL,
                         "Decode op custom function failed: op #%d, "
                         "offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                        i, op->offs, op->offs, op->bank, size, size, len, len);
+                        i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
                 }
                 break;
         }
@@ -935,7 +961,7 @@
             res = dmError(DMERR_INVALID_DATA,
                 "Encode op SIZE out of bounds, op #%d type=%d, offs=%d ($%04x), "
                 "bank=%d, size=%d ($%04x) vs. allocated %d ($%04x)\n",
-                i, op->type, op->offs, op->offs, op->bank, size, size, op->size, op->size);
+                i, op->type, op->offs, op->offs, op->bank, size, size, buf->size, buf->size);
             goto err;
         }
 
@@ -949,9 +975,6 @@
             goto err;
         }
 
-        if (chksize > buf->len)
-            buf->len = chksize;
-
         // Perform operation
         Uint8 *dst = buf->data + buf->offs + op->offs;
         switch (op->type)
@@ -967,35 +990,31 @@
                     case DS_CHAR_DATA:
                     case DS_EXTRA_DATA:
                         dmC64GetOpMemBlockAndName(img, op->subject, op->bank, &blk, &blkname);
-                        if (blk->data == NULL)
-                        {
-                            res = dmError(DMERR_NULLPTR,
-                                "'%s' block is NULL in "
-                                "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                                blkname, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
-                            goto err;
-                        }
-                        if (size > blk->size)
-                        {
-                            res = dmError(DMERR_INTERNAL,
-                                "'%s' size mismatch %d <> %d in "
-                                "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                                blkname, op->size, blk->size, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
-                            goto err;
-                        }
                         switch (op->type)
                         {
                             case DO_COPY:
+                                if (blk->data == NULL)
+                                {
+                                    res = dmError(DMERR_NULLPTR,
+                                        "'%s' block is NULL in "
+                                        "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
+                                        blkname, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
+                                    goto err;
+                                }
+                                if (size > blk->size)
+                                {
+                                    res = dmError(DMERR_INTERNAL,
+                                        "'%s' size mismatch %d <> %d in "
+                                        "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
+                                        blkname, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
+                                    goto err;
+                                }
                                 memcpy(dst, blk->data, size);
                                 break;
 
                             case DO_SET_MEM:
-                                // This operation makes no sense
-                                res = dmError(DMERR_INTERNAL,
-                                    "'%s' block DO_SET_MEM (which makes no sense) in "
-                                    "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                                    blkname, op->size, blk->size, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
-                                goto err;
+                                // This operation makes no sense, so do nothing
+                                break;
 
                             case DO_SET_OP:
                                 memset(dst, op->offs, size);
@@ -1026,12 +1045,8 @@
                                 break;
 
                             case DO_SET_OP:
-                                // This operation makes no sense
-                                res = dmError(DMERR_INTERNAL,
-                                    "'%s' block DO_SET_OP (which makes no sense) in "
-                                    "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n",
-                                    blkname, op->size, blk->size, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len);
-                                goto err;
+                                // Do nothing in this case
+                                break;
                         }
                         break;
 
@@ -1234,11 +1249,13 @@
 }
 
 
-int dmC64DecodeBMP(DMC64Image **img, const Uint8 *buf, const size_t len,
+int dmC64DecodeBMP(DMC64Image **img, const DMGrowBuf *buf,
     const size_t probeOffs, const size_t loadOffs,
     const DMC64ImageFormat **fmt, const DMC64ImageFormat *forced)
 {
-    if (img == NULL)
+    DMGrowBuf tmp;
+
+    if (img == NULL || buf == NULL)
         return DMERR_NULLPTR;
 
     // Check for forced format
@@ -1247,19 +1264,21 @@
     else
     {
         // Nope, perform a generic probe
-        if (probeOffs >= len)
+        if (probeOffs >= buf->len)
             return DMERR_OUT_OF_DATA;
 
-        if (dmC64ProbeBMP(buf + probeOffs, len - probeOffs, fmt) == DM_PROBE_SCORE_FALSE)
+        dmGrowBufCreateFromOffs(&tmp, buf, probeOffs);
+        if (dmC64ProbeBMP(tmp.data, tmp.len, fmt) == DM_PROBE_SCORE_FALSE)
             return DMERR_NOT_SUPPORTED;
     }
 
-    if (loadOffs >= len)
+    if (loadOffs >= tmp.len)
         return DMERR_INVALID_ARGS;
 
     if (*fmt == NULL)
         return DMERR_NOT_SUPPORTED;
 
+    // Format supports only reading?
     if (((*fmt)->flags & DM_FMT_RD) == 0)
         return DMERR_NOT_SUPPORTED;
 
@@ -1267,11 +1286,13 @@
     if ((*img = dmC64ImageAlloc(*fmt)) == NULL)
         return DMERR_MALLOC;
 
+    dmGrowBufCreateFromOffs(&tmp, buf, loadOffs);
+
     // Decode the bitmap to memory layout
     if ((*fmt)->decode != NULL)
-        return (*fmt)->decode(*img, buf + loadOffs, len - loadOffs, *fmt);
+        return (*fmt)->decode(*img, &tmp, *fmt);
     else
-        return dmC64DecodeGenericBMP(*img, buf + loadOffs, len - loadOffs, *fmt);
+        return dmC64DecodeGenericBMP(*img, &tmp, *fmt);
 }
 
 
--- a/tools/lib64gfx.h	Tue Jun 05 19:57:08 2018 +0300
+++ b/tools/lib64gfx.h	Tue Jun 05 21:58:10 2018 +0300
@@ -175,7 +175,7 @@
     int    bank;
     size_t size;
 
-    BOOL   (*decFunction)(DMC64Image *img, const struct _DMC64EncDecOp *op, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt);
+    BOOL   (*decFunction)(DMC64Image *img, const struct _DMC64EncDecOp *op, const DMGrowBuf *buf, const DMC64ImageFormat *fmt);
     BOOL   (*encFunction)(const struct _DMC64EncDecOp *op, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt);
 } DMC64EncDecOp;
 
@@ -199,7 +199,7 @@
 
     int  (*probe)(const Uint8 *buf, const size_t len, const struct _DMC64ImageFormat *fmt);
 
-    int  (*decode)(DMC64Image *img, const Uint8 *buf, const size_t len, const struct _DMC64ImageFormat *fmt);
+    int  (*decode)(DMC64Image *img, const DMGrowBuf *buf, const struct _DMC64ImageFormat *fmt);
     int  (*encode)(DMGrowBuf *buf, const DMC64Image *img, const struct _DMC64ImageFormat *fmt);
 
     int  (*convertFrom)(DMImage *, const DMC64Image *, const struct _DMC64ImageFormat *fmt);
@@ -229,6 +229,9 @@
     DM_RLE_ORDER_1          = 0x0000, // Order: <marker>, <count/run length>, <data>
     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,
 };
 
 
@@ -246,7 +249,7 @@
         rleMarkerMask,       // Mask bits for marker: data & rleMarkerMask == rleMarkerBits
         rleCountMask;        // Mask bits for length: count = data & rleCountMask
 
-    int
+    unsigned int
         // Minimum and maximum run lengths
         rleMinCountB,
         rleMinCountW,
@@ -292,25 +295,25 @@
 
 int       dmC64ProbeBMP(const Uint8 *buf, const size_t len, const DMC64ImageFormat **fmt);
 
-int       dmC64DecodeGenericBMP(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt);
+int       dmC64DecodeGenericBMP(DMC64Image *img, const DMGrowBuf *buf, const DMC64ImageFormat *fmt);
 int       dmC64EncodeGenericBMP(const BOOL allocate, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt);
 
 int       dmC64ConvertBMP2Image(DMImage **pdst, const DMC64Image *src, const DMC64ImageFormat *fmt);
 int       dmC64ConvertImage2BMP(DMC64Image **pdst, const DMImage *src, const DMC64ImageFormat *fmt);
 
-int       dmC64DecodeBMP(DMC64Image **img, const Uint8 *buf, const size_t len, const size_t probeOffs, const size_t loadOffs, const DMC64ImageFormat **fmt, const DMC64ImageFormat *forced);
+int       dmC64DecodeBMP(DMC64Image **img, const DMGrowBuf *buf, const size_t probeOffs, const size_t loadOffs, const DMC64ImageFormat **fmt, const DMC64ImageFormat *forced);
 int       dmC64EncodeBMP(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt);
 
 int       dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src, const DMC64ImageFormat *fmt);
 int       dmC64ConvertGenericImage2BMP(DMC64Image *dst, const DMImage *src, const DMC64ImageFormat *fmt);
 
-void      dmGenericRLEAnalyze(const Uint8 *buf, const size_t len, DMCompParams *cfg);
+void      dmGenericRLEAnalyze(const DMGrowBuf *buf, DMCompParams *cfg);
 
-int       dmDecodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg);
-int       dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg);
+int       dmDecodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
+int       dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
 
-int       dmEncodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg);
-int       dmEncodeGenericRLEAlloc(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg);
+int       dmEncodeGenericRLE(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
+int       dmEncodeGenericRLEAlloc(DMGrowBuf *dst, const DMGrowBuf *src, const DMCompParams *cfg);
 
 
 //