# HG changeset patch # User Matti Hamalainen # Date 1352004111 -7200 # Node ID f7c9d1619c74093fb88e7ebb1ac888aea514cb90 # Parent a67600e186d0a14b3143198160bbd68293f6ea9b Beginnings of IFF ILBM reader. Not functional, only chunk parsing, BMHD header chunk parsing and CMAP handling implemented. diff -r a67600e186d0 -r f7c9d1619c74 gfxconv.c --- a/gfxconv.c Sun Nov 04 06:41:10 2012 +0200 +++ b/gfxconv.c Sun Nov 04 06:41:51 2012 +0200 @@ -64,6 +64,11 @@ FFMT_IMAGE , IMGFMT_PCX, }, { + "IFF ILBM file", "lbm", TRUE, FALSE, + FFMT_IMAGE , IMGFMT_ILBM, + }, + + { "IFFMaster RAW image file", "araw", FALSE, TRUE, FFMT_IMAGE , IMGFMT_ARAW, }, diff -r a67600e186d0 -r f7c9d1619c74 libgfx.c --- 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, diff -r a67600e186d0 -r f7c9d1619c74 libgfx.h --- a/libgfx.h Sun Nov 04 06:41:10 2012 +0200 +++ b/libgfx.h Sun Nov 04 06:41:51 2012 +0200 @@ -21,6 +21,7 @@ IMGFMT_PNG, IMGFMT_PPM, IMGFMT_PCX, + IMGFMT_ILBM, IMGFMT_ARAW, IMGFMT_LAST