diff libgfx.c @ 443:f7c9d1619c74

Beginnings of IFF ILBM reader. Not functional, only chunk parsing, BMHD header chunk parsing and CMAP handling implemented.
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 04 Nov 2012 06:41:51 +0200
parents a67600e186d0
children 7d588807f91d
line wrap: on
line diff
--- a/libgfx.c	Sun Nov 04 06:41:10 2012 +0200
+++ b/libgfx.c	Sun Nov 04 06:41:51 2012 +0200
@@ -920,12 +920,276 @@
 }
 
 
+#define IFF_ID_FORM        0x464F524D // "FORM"
+#define IFF_ID_ILBM        0x494C424D // "ILBM"
+#define IFF_ID_BMHD        0x424D4844 // "BMHD"
+#define IFF_ID_CMAP        0x434D4150 // "CMAP"
+#define IFF_ID_BODY        0x424F4459 // "BODY"
+
+#define IFF_MASK_NONE      0
+#define IFF_MASK_HAS_MASK  1
+#define IFF_MASK_TRANSP    2
+#define IFF_MASK_LASSO     3
+
+#define IFF_COMP_NONE      0
+#define IFF_COMP_BYTERUN1  1
+
+
+typedef struct
+{
+    Uint32 id;
+    Uint32 size;
+    int count;
+} DMIFFChunk;
+
+
+typedef struct
+{
+    Uint16 w, h;
+    Sint16 x, y;
+    Uint8  nplanes;
+    Uint8  masking;
+    Uint8  compression;
+    Uint8  pad1;
+    Uint16 transp;
+    Uint8  xasp, yasp;
+    Sint16 pagew, pageh;
+} DMIFFBMHD;
+
+
+static BOOL dmReadIFFChunk(FILE *fp, DMIFFChunk *chunk)
+{
+    if (!dm_fread_be32(fp, &chunk->id) ||
+        !dm_fread_be32(fp, &chunk->size))
+    {
+        dmError("ILBM: Could not read IFF chunk header.\n");
+        return FALSE;
+    }
+    else
+        return TRUE;
+}
+
+static char * dmGetIFFChunkID(DMIFFChunk *chunk, char *buf)
+{
+    buf[0] = (chunk->id >> 24) & 0xff;
+    buf[1] = (chunk->id >> 16) & 0xff;
+    buf[2] = (chunk->id >> 8) & 0xff;
+    buf[3] = (chunk->id) & 0xff;
+    buf[4] = 0;
+    return buf;
+}
+
+static BOOL dmCheckIFFChunk(DMIFFChunk *dest, DMIFFChunk *chunk, BOOL multi, Uint32 minSize)
+{
+    if (dest->count > 0 && !multi)
+    {
+        char buf[6];
+        dmError("ILBM: Multiple instances of chunk %s found.\n",
+            dmGetIFFChunkID(chunk, buf));
+        return FALSE;
+    }
+    
+    dest->count++;
+
+    if (chunk->size < minSize)
+        return FALSE;
+    
+    return TRUE;
+}
+
+
+int dmReadILBMImageFILE(FILE *fp, DMImage **pimg)
+{
+    Uint32 idILBM;
+    DMIFFChunk chunk, chBMHD, chCMAP, chBODY;
+    DMIFFBMHD bmhd;
+    int ncolors, i;
+    DMColor *pal = NULL;
+    DMImage *img = NULL;
+    BOOL paletted = FALSE, parsed = FALSE;
+    int res = DMERR_OK;
+
+    memset(&chBMHD, 0, sizeof(DMIFFChunk));
+    memset(&chCMAP, 0, sizeof(DMIFFChunk));
+    memset(&chBODY, 0, sizeof(DMIFFChunk));
+    memset(&bmhd, 0, sizeof(bmhd));
+
+    // Read IFF FORM header
+    if (!dmReadIFFChunk(fp, &chunk) ||
+        chunk.id != IFF_ID_FORM ||
+        chunk.size < 32)
+    {
+        dmError("ILBM: Not a IFF file.\n");
+        return DMERR_FREAD;
+    }
+    
+    // Check IFF ILBM signature
+    if (!dm_fread_be32(fp, &idILBM) ||
+        idILBM != IFF_ID_ILBM)
+    {
+        dmError("ILBM: Not a ILBM file.\n");
+        return DMERR_INVALID_DATA;
+    }
+
+    while (!parsed && !feof(fp))
+    {
+        if (!dmReadIFFChunk(fp, &chunk))
+        {
+            dmError("ILBM: Error reading IFF ILBM data.\n");
+            return DMERR_FREAD;
+        }
+        
+        switch (chunk.id)
+        {
+            case IFF_ID_BMHD:
+                if (!dmCheckIFFChunk(&chBMHD, &chunk, FALSE, sizeof(bmhd)))
+                    return DMERR_FREAD;
+
+                if (!dm_fread_be16(fp, &bmhd.w) ||
+                    !dm_fread_be16(fp, &bmhd.h) ||
+                    !dm_fread_be16(fp, (Uint16 *) &bmhd.x) ||
+                    !dm_fread_be16(fp, (Uint16 *) &bmhd.y) ||
+                    !dm_fread_byte(fp, &bmhd.nplanes) ||
+                    !dm_fread_byte(fp, &bmhd.masking) ||
+                    !dm_fread_byte(fp, &bmhd.compression) ||
+                    !dm_fread_byte(fp, &bmhd.pad1) ||
+                    !dm_fread_be16(fp, &bmhd.transp) ||
+                    !dm_fread_byte(fp, &bmhd.xasp) ||
+                    !dm_fread_byte(fp, &bmhd.yasp) ||
+                    !dm_fread_be16(fp, (Uint16 *) &bmhd.pagew) ||
+                    !dm_fread_be16(fp, (Uint16 *) &bmhd.pageh))
+                {
+                    dmError("ILBM: Error reading BMHD chunk.\n");
+                    return DMERR_FREAD;
+                }
+                dmMsg(2, "ILBM: BMHD %d x %d @ %d, %d : nplanes=%d, comp=%d, mask=%d\n",
+                    bmhd.w, bmhd.h, bmhd.x, bmhd.y, bmhd.nplanes, bmhd.compression, bmhd.masking);
+                break;
+            
+
+            case IFF_ID_CMAP:
+                // Check for multiple occurences of CMAP
+                if (!dmCheckIFFChunk(&chCMAP, &chunk, FALSE, 3))
+                    return DMERR_FREAD;
+
+                // Check for sanity
+                if (chunk.size % 3 != 0)
+                    dmError("ILBM: CMAP chunk size not divisible by 3, possibly broken file.\n");
+
+                ncolors = chunk.size / 3;
+                dmMsg(2, "ILBM: CMAP %d entries (%d bytes)\n",
+                    ncolors, chunk.size, 1 << bmhd.nplanes);
+
+                if (bmhd.nplanes > 0 && ncolors != 1 << bmhd.nplanes)
+                    dmMsg(2, "ILBM: Expected %d entries in CMAP.\n", 1 << bmhd.nplanes);
+                
+                if (ncolors == 0)
+                    break;
+                
+                // Allocate palette
+                if ((pal = dmMalloc(sizeof(DMColor) * ncolors)) == NULL)
+                {
+                    dmError("ILBM: Could not allocate memory for palette.\n");
+                    return DMERR_MALLOC;
+                }
+                
+                // Read palette
+                for (i = 0; i < ncolors; i++)
+                {
+                    Uint8 colR, colG, colB;
+                    if (!dm_fread_byte(fp, &colR) ||
+                        !dm_fread_byte(fp, &colG) ||
+                        !dm_fread_byte(fp, &colB))
+                    {
+                        dmError("ILBM: Error reading CMAP entry #%d, broken file.\n", i);
+                        return DMERR_FREAD;
+                    }
+                    
+                    pal[i].r = colR;
+                    pal[i].g = colG;
+                    pal[i].b = colB;
+                }
+
+                if (chBMHD.count && chBODY.count)
+                    parsed = TRUE;
+                break;
+            
+            case IFF_ID_BODY:
+                if (!dmCheckIFFChunk(&chBODY, &chunk, FALSE, 1))
+                    return DMERR_FREAD;
+
+                if (!chBMHD.count)
+                {
+                    dmError("ILBM: BODY chunk before BMHD?\n");
+                    return DMERR_INVALID_DATA;
+                }
+
+                dmMsg(2, "ILBM: BODY chunk size %d bytes\n", chunk.size);
+                if (fseeko(fp, chunk.size, SEEK_CUR) != 0)
+                {
+                    dmError("ILBM: Error skipping in file.");
+                    return DMERR_FREAD;
+                }
+                 
+                if (chCMAP.count)
+                    parsed = TRUE;
+                break;
+            
+            default:
+                {
+                    char buf[6];
+                    dmMsg(3, "Unknown chunk ID '%s', size %d\n",
+                        dmGetIFFChunkID(&chunk, buf), chunk.size);
+                    
+                    if (fseeko(fp, chunk.size, SEEK_CUR) != 0)
+                    {
+                        dmError("ILBM: Error skipping in file.");
+                        return DMERR_FREAD;
+                    }
+                }
+                break;
+        }
+        
+        if (chunk.size & 1)
+            fgetc(fp);
+    }
+
+    return res;
+}
+
+
+int dmReadILBMImage(const char *filename, DMImage **pimg)
+{
+    FILE *fp;
+    int res;
+
+    if ((fp = fopen(filename, "rb")) == NULL)
+    {
+        dmError("ILBM: Could not open file '%s' for reading.\n", filename);
+        return DMERR_FOPEN;
+    }
+    
+    res = dmReadILBMImageFILE(fp, pimg);
+
+    fclose(fp);
+    return res;
+}
+
+
+
+
 static int fmtProbePNG(const Uint8 *buf, const size_t len)
 {
     if (len > 64 && buf[0] == 0x89 &&
         buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G' &&
         buf[4] == 0x0d && buf[5] == 0x0a)
-        return DM_PROBE_SCORE_GOOD;
+    {
+        if (buf[12] == 'I' && buf[13] == 'H' &&
+            buf[14] == 'D' && buf[15] == 'R')
+            return DM_PROBE_SCORE_MAX;
+        else
+            return DM_PROBE_SCORE_GOOD;
+    }
 
     return DM_PROBE_SCORE_FALSE;
 }
@@ -933,17 +1197,37 @@
 
 static int fmtProbePCX(const Uint8 *buf, const size_t len)
 {
-    if (len > 128 + 64 &&
+    if (len > 128 + 32 &&
         buf[0] == 10 &&
-        buf[1] == 5 &&
+        (buf[1] == 5 || buf[1] == 2 || buf[1] == 3) &&
         buf[2] == 1 &&
-        buf[3] == 8)
+        (buf[3] == 8 || buf[3] == 4 || buf[3] == 3 || buf[3] == 1) &&
+        buf[65] >= 1 && buf[65] <= 4)
         return DM_PROBE_SCORE_GOOD;
 
     return DM_PROBE_SCORE_FALSE;
 }
 
 
+static int fmtProbeILBM(const Uint8 *buf, const size_t len)
+{
+    if (len > 32 &&
+        buf[ 0] == 'F' && buf[ 1] == 'O' &&
+        buf[ 2] == 'R' && buf[ 3] == 'M' &&
+        buf[ 8] == 'I' && buf[ 9] == 'L' &&
+        buf[10] == 'B' && buf[11] == 'M')
+    {
+        if (buf[12] == 'B' && buf[13] == 'M' &&
+            buf[14] == 'H' && buf[15] == 'D')
+            return DM_PROBE_SCORE_MAX;
+        else
+            return DM_PROBE_SCORE_GOOD;
+    }
+
+    return DM_PROBE_SCORE_FALSE;
+}
+
+
 DMImageFormat dmImageFormatList[IMGFMT_LAST] =
 {
     {
@@ -969,6 +1253,12 @@
         dmWritePCXImage, dmWritePCXImageFILE,
     },
     {
+        "ILBM", "IFF ILBM",
+        fmtProbeILBM,
+        dmReadILBMImage, dmReadILBMImageFILE,
+        NULL, NULL,
+    },
+    {
         "ARAW", "IFFMaster Amiga RAW",
         NULL,
         NULL, NULL,