diff lib64gfx.c @ 407:59244a7ae37f

Move c64 utilities to the engine lib, as we benefit from a common framework.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 03 Nov 2012 02:19:51 +0200
parents
children e4b2f689aff6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib64gfx.c	Sat Nov 03 02:19:51 2012 +0200
@@ -0,0 +1,727 @@
+/*
+ * 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 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "lib64gfx.h"
+#include <errno.h>
+
+const char *dmC64ImageTypeNames[DM_C64IFMT_LAST_TYPE] =
+{
+    "hires",
+    "multicolor",
+    "hires interlace",
+    "mc interlace",
+    "hires fli",
+    "mc fli",
+};
+
+
+// Based on Pepto's palette, stolen from VICE
+DMColor dmC64Palette[C64_NCOLORS] =
+{
+    { 0x00, 0x00, 0x00, 0 },
+    { 0xFF, 0xFF, 0xFF, 0 },
+    { 0x68, 0x37, 0x2B, 0 },
+    { 0x70, 0xA4, 0xB2, 0 },
+    { 0x6F, 0x3D, 0x86, 0 },
+    { 0x58, 0x8D, 0x43, 0 },
+    { 0x35, 0x28, 0x79, 0 },
+    { 0xB8, 0xC7, 0x6F, 0 },
+    { 0x6F, 0x4F, 0x25, 0 },
+    { 0x43, 0x39, 0x00, 0 },
+    { 0x9A, 0x67, 0x59, 0 },
+    { 0x44, 0x44, 0x44, 0 },
+    { 0x6C, 0x6C, 0x6C, 0 },
+    { 0x9A, 0xD2, 0x84, 0 },
+    { 0x6C, 0x5E, 0xB5, 0 },
+    { 0x95, 0x95, 0x95, 0 },
+};
+
+
+const size_t dmC64DefaultSizes[DT_LAST] =
+{
+    C64_SCR_COLOR_SIZE,
+    C64_SCR_BITMAP_SIZE,
+    C64_SCR_SCREEN_SIZE,
+    1,
+    C64_SCR_EXTRADATA,
+};
+
+
+
+DMImage * dmImageAlloc(int width, int height)
+{
+    DMImage *img = dmCalloc(1, sizeof(DMImage));
+    if (img == NULL)
+        return NULL;
+    
+    img->width = img->pitch = width;
+    img->height = height;
+    img->data = dmMalloc(width * height * sizeof(uint8_t));
+    if (img->data == NULL)
+    {
+        dmFree(img);
+        return NULL;
+    }
+    
+    return img;
+}
+
+
+void dmImageFree(DMImage *img)
+{
+    if (img != NULL)
+    {
+        if (!img->constpal)
+        {
+            dmFree(img->pal);
+        }
+        dmFree(img->data);
+        dmFree(img);
+    }
+}
+
+
+int dmImageGetBytesPerPixel(int format)
+{
+    switch (format)
+    {
+        case DM_IFMT_PALETTE   : return 1;
+        case DM_IFMT_RGB       : return 3;
+        case DM_IFMT_RGBA      : return 4;
+        case DM_IFMT_RGB_PLANE : return 3;
+        default:                 return 0;
+    }
+}
+
+
+int dmC64ConvertCSData(DMImage *img,
+    int xoffs, int yoffs, const uint8_t *buf,
+    int width, int height, BOOL multicolor, int *colors)
+{
+    int yc, widthpx = width * 8;
+    uint8_t *dp;
+
+    if (img == NULL)
+        return -1;
+    if (xoffs < 0 || yoffs < 0)
+        return -2;
+    if (xoffs > img->width - widthpx ||
+        yoffs > img->height - height)
+        return -3;
+
+    dp = img->data + (yoffs * img->pitch) + xoffs;
+
+    if (multicolor)
+    {
+        for (yc = 0; yc < height; yc++)
+        {
+            const int offs = yc * width;
+            int xc;
+            uint8_t *d = dp;
+
+            for (xc = 0; xc < widthpx / 2; xc++)
+            {
+                const int b = buf[offs + (xc / 4)];
+                const int v = 6 - ((xc * 2) & 6);
+                const uint8_t c = colors[(b >> v) & 3];
+                
+                *d++ = c;
+                *d++ = c;
+            }
+
+            dp += img->pitch;
+        }
+    }
+    else
+    {
+        for (yc = 0; yc < height; yc++)
+        {
+            const int offs = yc * width;
+            int xc;
+            uint8_t *d = dp;
+
+            for (xc = 0; xc < widthpx; xc++)
+            {
+                const int b = buf[offs + (xc / 8)];
+                const int v = 7 - (xc & 7);
+                const uint8_t c = colors[(b >> v) & 1];
+                
+                *d++ = c;
+            }
+
+            dp += img->pitch;
+        }
+    }
+    
+    return 0;
+}
+
+
+static int fmtProbeDrazPaint(const uint8_t *buf, const size_t len)
+{
+    if (len == 10051 && buf[0] == 0x00 && buf[1] == 0x58)
+        return DM_PROBE_SCORE_GOOD;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtProbeDrazPaint20Packed(const uint8_t *buf, const size_t len)
+{
+    const char *ident = (const char *) buf + 2;
+    if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 &&
+            strncmp(ident, "DRAZPAINT ", 10) == 0 &&
+            ident[11] == '.' && (
+            (ident[10] == '1' && ident[12] == '4') ||
+            (ident[10] == '2' && ident[12] == '0')
+            ))
+        return DM_PROBE_SCORE_MAX;
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtDecodeDrazPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    int res;
+    uint8_t rleMarker;
+    uint8_t *mem, *dst, *dstEnd;
+    const uint8_t *src, *srcEnd;
+
+    if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL)
+        return -1;
+    
+    rleMarker = *(buf + 0x0d);
+    src       = buf + 0x0e;
+    srcEnd    = buf + len;
+    dst       = mem;
+    dstEnd    = mem + C64_RAM_SIZE;
+
+    while (src <= srcEnd && dst <= dstEnd)
+    {
+        int c = *src++;
+        if (c == rleMarker && src + 2 <= srcEnd)
+        {
+            int cnt = *src++;
+            c = *src++;
+            while (cnt-- && dst <= dstEnd)
+                *dst++ = c;
+        }
+        else
+            *dst++ = c;
+    }
+    
+    res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt);
+    
+    dmFree(mem);
+
+    return res;
+}
+
+
+static int fmtProbeDrazLace10(const uint8_t *buf, const size_t len)
+{
+    if (len == 18242 && buf[0] == 0x00 && buf[1] == 0x58)
+        return DM_PROBE_SCORE_GOOD;
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtProbeDrazLace10Packed(const uint8_t *buf, const size_t len)
+{
+    const char *ident = (const char *) buf + 2;
+    if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 &&
+            strncmp(ident, "DRAZLACE! 1.0", 13) == 0)
+        return DM_PROBE_SCORE_MAX;
+    
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static BOOL fmtDrazLaceSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len)
+{
+    img->laceType = buf[op->offs] ? DM_C64ILACE_RES : DM_C64ILACE_COLOR;
+    img->laceBank2 = 0;
+    return TRUE;
+}
+
+
+#define AMICA_DM_PROBE_SIZE 1024
+static int fmtProbeAmicaPaintPacked(const uint8_t *buf, const size_t len)
+{
+    int i, n;
+    if (len < AMICA_DM_PROBE_SIZE || buf[0] != 0x00 || buf[1] != 0x40)
+        return DM_PROBE_SCORE_FALSE;
+    
+    for (n = 0, i = 2; i < AMICA_DM_PROBE_SIZE; i++)
+        if (buf[i] == 0xC2) n++;
+    
+    if (n > 5)
+        return DM_PROBE_SCORE_GOOD;
+    if (n > 3)
+        return DM_PROBE_SCORE_AVG;
+
+    return DM_PROBE_SCORE_MAYBE;
+}
+
+
+static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt)
+{
+    int res;
+    uint8_t *mem, *dst, *dstEnd;
+    const uint8_t *src, *srcEnd;
+
+    if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL)
+        return -1;
+    
+    src    = buf;
+    srcEnd = buf + len;
+    dst    = mem;
+    dstEnd = mem + C64_RAM_SIZE;
+
+    while (src <= srcEnd && dst <= dstEnd)
+    {
+        int c = *src++;
+        if (c == 0xC2 && src + 2 <= srcEnd)
+        {
+            int cnt = *src++;
+            c = *src++;
+            while (cnt-- && dst <= dstEnd)
+                *dst++ = c;
+        }
+        else
+            *dst++ = c;
+    }
+    
+    res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt);
+    
+    dmFree(mem);
+
+    return res;
+}
+
+
+static int fmtProbeKoalaPaint(const uint8_t *buf, const size_t len)
+{
+    if (len == 10003 && buf[0] == 0x00 && buf[1] == 0x60)
+        return DM_PROBE_SCORE_AVG;
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static int fmtProbeTruePaint(const uint8_t *buf, const size_t len)
+{
+    if (len == 19434 && buf[0] == 0x00 && buf[1] == 0x9c)
+        return DM_PROBE_SCORE_GOOD;
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
+static BOOL fmtTruePaintSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len)
+{
+    img->laceType = DM_C64ILACE_RES;
+    img->laceBank2 = 1;
+    return TRUE;
+}
+
+
+DMC64ImageFormat dmC64ImageFormats[] =
+{
+    {
+        DM_C64IFMT_MC, ".drp", "DrazPaint 2.0 (packed)",
+        fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, NULL,
+        4,
+        {
+            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
+            { DT_BITMAP,     0x0800, 0,  0, NULL },
+            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
+            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
+        }
+    },
+
+    {
+        DM_C64IFMT_MC_ILACE, ".dlp", "DrazLace 1.0 (packed)",
+        fmtProbeDrazLace10Packed, fmtDecodeDrazPaintPacked, NULL,
+        6,
+        {
+            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
+            { DT_BITMAP,     0x0800, 0,  0, NULL },
+            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
+            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
+            { DT_BITMAP,     0x2800, 1,  0, NULL },
+            { DT_FUNCTION,   0x2742, 0,  1, fmtDrazLaceSetLaceType },
+        }
+    },
+    
+    {
+        DM_C64IFMT_MC, ".drp", "DrazPaint (unpacked)",
+        fmtProbeDrazPaint, NULL, NULL,
+        4,
+        {
+            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
+            { DT_BITMAP,     0x0800, 0,  0, NULL },
+            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
+            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
+        }
+    },
+
+    {
+        DM_C64IFMT_MC_ILACE, ".drl", "DrazLace 1.0 (unpacked)",
+        fmtProbeDrazLace10, NULL, NULL,
+        6,
+        {
+            { DT_COLOR_RAM,  0x0000, 0,  0, NULL },
+            { DT_BITMAP,     0x0800, 0,  0, NULL },
+            { DT_SCREEN_RAM, 0x0400, 0,  0, NULL },
+            { DT_BGCOLOR,    0x2740, 0,  0, NULL },
+            { DT_BITMAP,     0x2800, 1,  0, NULL },
+            { DT_FUNCTION,   0x2742, 0,  1, fmtDrazLaceSetLaceType },
+        }
+    },
+    
+    {
+        DM_C64IFMT_MC_ILACE, ".mci", "Truepaint (unpacked)",
+        fmtProbeTruePaint, NULL, NULL,
+        6,
+        {
+            { DT_SCREEN_RAM, 0x0000, 0,  0, NULL },
+            { DT_BGCOLOR,    0x03e8, 0,  0, NULL },
+            { DT_BITMAP,     0x0400, 0,  0, NULL },
+            { DT_BITMAP,     0x2400, 1,  0, NULL },
+            { DT_SCREEN_RAM, 0x4400, 1,  0, NULL },
+            { DT_COLOR_RAM,  0x4800, 0,  0, NULL },
+            { DT_FUNCTION,   0x0000, 0,  0, fmtTruePaintSetLaceType },
+        }
+    },
+    
+    {
+        DM_C64IFMT_MC, ".kla", "Koala Paint (unpacked)",
+        fmtProbeKoalaPaint, NULL, NULL,
+        4,
+        {
+            { DT_COLOR_RAM,  0x2328, 0,  0, NULL },
+            { DT_BITMAP,     0x0000, 0,  0, NULL },
+            { DT_SCREEN_RAM, 0x1f40, 0,  0, NULL },
+            { DT_BGCOLOR,    0x2710, 0,  0, NULL },
+        }
+    },
+
+    {
+        DM_C64IFMT_MC, ".ami", "Amica Paint (packed)",
+        fmtProbeAmicaPaintPacked, fmtDecodeAmicaPaintPacked, NULL,
+        4,
+        {
+            { DT_COLOR_RAM,  0x2328, 0,  0, NULL },
+            { DT_BITMAP,     0x0000, 0,  0, NULL },
+            { DT_SCREEN_RAM, 0x1f40, 0,  0, NULL },
+            { DT_BGCOLOR,    0x2710, 0,  0, NULL },
+        }
+    },
+    
+};
+
+const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]);
+
+
+int dmC64ProbeGeneric(const uint8_t *buf, const size_t len,
+    DMC64ImageFormat **pfmt)
+{
+    int i, scoreMax = 0, scoreIndex = -1;
+
+    for (i = 0; i < ndmC64ImageFormats; i++)
+    {
+        DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
+        int score = fmt->probe(buf, len);
+        if (score > scoreMax)
+        {
+            scoreMax = score;
+            scoreIndex = i;
+        }
+    }
+
+    if (scoreIndex >= 0)
+    {
+        *pfmt = &dmC64ImageFormats[scoreIndex];
+        return scoreMax;
+    }
+    else
+        return 0;
+}
+
+
+int dmC64DecodeGenericBMP(DMC64Image *img, const uint8_t *buf,
+    const size_t len, const DMC64ImageFormat *fmt)
+{
+    int i;
+
+    memset(img, 0, sizeof(*img));
+    img->type = fmt->type;
+
+    for (i = 0; i < fmt->ndecodeOps; i++)
+    {
+        const DMDecodeOp *op = &fmt->decodeOps[i];
+        const uint8_t *src;
+        size_t size;
+
+        if (op->bank < 0 || op->bank >= C64_SCR_MAX_BANK)
+        {
+            dmError("Invalid bank %d definition in generic decode operator %d @ #%d.\n",
+                op->bank, op->type, i);
+            return -1;
+        }
+        
+        if (op->type < 0 || op->type >= DT_LAST)
+        {
+            dmError("Invalid decode operator type %d @ #%d.\n",
+                op->type, i);
+            return -1;
+        }
+        
+        size = (op->size == 0) ? dmC64DefaultSizes[op->type] : op->size;
+        
+        if (op->offs + size > len)
+        {
+            dmError("Decode out of bounds, op #%d type=%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
+                i, op->type, op->offs, op->offs, op->bank, size, size, len, len);
+            return -2;
+        }
+        
+        src = buf + op->offs;
+        
+        switch (op->type)
+        {
+            case DT_COLOR_RAM:   memcpy(img->color[op->bank], src, size); break;
+            case DT_BITMAP:      memcpy(img->bitmap[op->bank], src, size); break;
+            case DT_SCREEN_RAM:  memcpy(img->screen[op->bank], src, size); break;
+            case DT_BGCOLOR:     img->bgcolor = *src; break;
+            case DT_EXTRADATA:   memcpy(img->extradata, src, size); break;
+            case DT_FUNCTION:
+                if (op->function == NULL)
+                {
+                    dmError("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);
+                    return -6;
+                }
+                if (!op->function(img, op, buf, len))
+                {
+                    dmError("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);
+                    return -5;
+                }
+                break;
+        }
+    }
+    
+    return 0;
+}
+
+
+static int dmC64ConvertHiResBMP(DMImage *screen, const DMC64Image *img)
+{
+    int yc;
+    uint8_t *dp = screen->data;
+    
+    for (yc = 0; yc < C64_SCR_HEIGHT; yc++)
+    {
+        uint8_t *d = dp;
+        const int y = yc / 8, yb = yc & 7;
+        const int scroffsy = y * C64_SCR_CH_WIDTH;
+        const int bmoffsy = y * C64_SCR_WIDTH;
+        int xc;
+
+        for (xc = 0; xc < C64_SCR_WIDTH; xc++)
+        {
+            const int x = xc / 8;
+            const int scroffs = scroffsy + x;
+            const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
+            const int v = 7 - (xc & 7);
+            uint8_t c;
+
+            if ((b >> v) & 1)
+                c = img->screen[0][scroffs] & 15;
+            else
+                c = img->screen[0][scroffs] >> 4;
+            
+            *d++ = c;
+        }
+
+        dp += screen->pitch;
+    }
+
+    return 0;
+}
+
+
+static int dmC64ConvertMultiColorBMP(DMImage *screen, const DMC64Image *img)
+{
+    int yc;
+    uint8_t *dp = screen->data;
+    
+    for (yc = 0; yc < C64_SCR_HEIGHT; yc++)
+    {
+        uint8_t *d = dp;
+        const int y = yc / 8, yb = yc & 7;
+        const int scroffsy = y * C64_SCR_CH_WIDTH;
+        const int bmoffsy = y * C64_SCR_WIDTH;
+        int xc;
+
+        for (xc = 0; xc < C64_SCR_WIDTH / 2; xc++)
+        {
+            const int x = xc / 4;
+            const int scroffs = scroffsy + x;
+            const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
+            const int v = 6 - ((xc * 2) & 6);
+            uint8_t c;
+
+            switch ((b >> v) & 3)
+            {
+                case 0: c = img->bgcolor; break;
+                case 1: c = img->screen[0][scroffs] >> 4; break;
+                case 2: c = img->screen[0][scroffs] & 15; break;
+                case 3: c = img->color[0][scroffs] & 15; break;
+            }
+            
+            *d++ = c;
+            *d++ = c;
+        }
+
+        dp += screen->pitch;
+    }
+
+    return 0;
+}
+
+
+static int dmC64ConvertLaceMultiColorBMP(DMImage *screen, const DMC64Image *img)
+{
+    int yc;
+    uint8_t *dp = screen->data;
+    
+    for (yc = 0; yc < C64_SCR_HEIGHT; yc++)
+    {
+        uint8_t *d = dp;
+        const int y = yc / 8, yb = yc & 7;
+        const int scroffsy = y * C64_SCR_CH_WIDTH;
+        const int bmoffsy = y * C64_SCR_WIDTH;
+        int xc;
+
+        for (xc = 0; xc < C64_SCR_WIDTH / 2; xc++)
+        {
+            const int x = xc / 4;
+            const int scroffs = scroffsy + x;
+            const int bmoffs = bmoffsy + (x * 8) + yb;
+            const int v = 6 - ((xc * 2) & 6);
+            const int b1 = (img->bitmap[0][bmoffs] >> v) & 3;
+            const int b2 = (img->bitmap[1][bmoffs] >> v) & 3;
+            uint8_t c1, c2;
+
+            switch (b1)
+            {
+                case 0: c1 = img->bgcolor; break;
+                case 1: c1 = img->screen[0][scroffs] >> 4; break;
+                case 2: c1 = img->screen[0][scroffs] & 15; break;
+                case 3: c1 = img->color[0][scroffs] & 15; break;
+            }
+
+            switch (b2)
+            {
+                case 0: c2 = img->bgcolor; break;
+                case 1: c2 = img->screen[img->laceBank2][scroffs] >> 4; break;
+                case 2: c2 = img->screen[img->laceBank2][scroffs] & 15; break;
+                case 3: c2 = img->color[img->laceBank2][scroffs] & 15; break;
+            }
+            
+            *d++ = c1;
+            *d++ = c2;
+        }
+
+        dp += screen->pitch;
+    }
+
+    return 0;
+}
+
+
+int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src)
+{
+    switch (src->type)
+    {
+        case DM_C64IFMT_HIRES:
+            return dmC64ConvertHiResBMP(dst, src);
+        
+        case DM_C64IFMT_MC:
+            return dmC64ConvertMultiColorBMP(dst, src);
+
+        case DM_C64IFMT_MC_ILACE:
+            return dmC64ConvertLaceMultiColorBMP(dst, src);
+
+        default:
+            return -1;
+    }
+}
+
+
+#define BUF_SIZE_INITIAL   (16*1024)
+#define BUF_SIZE_GROW      (2*1024)
+
+int dmReadDataFile(const char *filename, uint8_t **pbuf, size_t *pbufSize)
+{
+    FILE *f;
+    uint8_t *dataBuf = NULL, *dataPtr;
+    size_t bufSize, readSize, dataSize;
+    
+    if (filename == NULL)
+        f = stdin;
+    else
+    if ((f = fopen(filename, "rb")) == NULL)
+    {
+        int err = errno;
+        dmError("Could not open input file '%s': %d, %s\n",
+            filename, err, strerror(err));
+        return -1;
+    }
+
+    readSize = bufSize = BUF_SIZE_INITIAL;
+    if ((dataBuf = dmMalloc(bufSize)) == NULL)
+    {
+        fclose(f);
+        dmError("Error allocating memory for data, %d bytes.\n", bufSize);
+        return -4;
+    }
+
+    dataPtr = dataBuf;
+    dataSize = 0;
+
+    while (!feof(f) && !ferror(f))
+    {
+        size_t read = fread(dataPtr, 1, readSize, f);
+
+        dataSize += read;
+        dataPtr += read;
+
+        if (read == readSize && !feof(f))
+        {
+            readSize = BUF_SIZE_GROW;
+            bufSize += BUF_SIZE_GROW;
+            if ((dataBuf = dmRealloc(dataBuf, bufSize)) == NULL)
+            {
+                dmError("Error reallocating memory for data, %d bytes.\n", bufSize);
+                return -4;
+            }
+        }
+        else
+            break;
+    }
+
+    fclose(f);
+
+    *pbuf = dataBuf;
+    *pbufSize = dataSize;
+    
+    return 0;
+}