/* vim:expandtab:ts=2 sw=2:
*/
/* Grafx2 - The Ultimate 256-color bitmap paint program
* Gif Analyzer tool
Copyright 2018 Thomas Bernard
Grafx2 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
Grafx2 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grafx2; if not, see
*/
#include
#include
#include
#include
#define IFF_EOF -1
#define IFF_FILE_TOO_LONG -2
#define IFF_UNRECOGNIZED_CONTID -3
#define IFF_SIZE_MISMATCH -4
const char * parseiff_errorstr(int err)
{
switch (err)
{
case IFF_EOF:
return "prematurate End OF File";
case IFF_FILE_TOO_LONG:
return "Extra bytes in file after end of IFF";
case IFF_UNRECOGNIZED_CONTID:
return "Unrecognized IFF container ID (should probably be FORM)";
case IFF_SIZE_MISMATCH:
return "Size Mismatch";
default:
return "Unknown error";
}
}
static int read_long_be(FILE * f, uint32_t * l)
{
int i, b;
for (i = 0; i < 4; i++)
{
b = getc(f);
if (b == EOF)
return IFF_EOF;
*l = (*l << 8) | b;
}
return 0;
}
static const char * id_chunks[] = {
"LIST", "FORM", "PROP", "CAT ", " ", NULL
};
static int iscontainer(const char * chunkid)
{
int i;
for (i = 0; id_chunks[i]; i++)
{
if (memcmp(id_chunks[i], chunkid, 4) == 0)
return 1;
}
return 0;
}
static int parseiff_chunks(FILE * f, uint32_t size, int level)
{
int i, index;
size_t n;
char section[4];
uint32_t section_size;
//if (size&1)
//{
// fprintf(stderr, "WARNING: odd size of Container chunk, adjusting\n");
// size++;
//}
index = 0;
while (size >= 8)
{
printf("%06lX: ", ftell(f));
for (i = 0; i < level; i++)
printf(" ");
printf("#%02d ", index++);
n = fread(section, 1, 4, f);
if (n != 4)
return IFF_EOF;
if (read_long_be(f, §ion_size) < 0)
return IFF_EOF;
size -= 8;
if (iscontainer(section))
{
int r;
char format[4];
n = fread(format, 1, 4, f);
if (n != 4)
return IFF_EOF;
printf("%.4s %u %.4s\n", section, section_size, format);
r = parseiff_chunks(f, section_size - 4, level+1);
if (r < 0)
return r;
}
else
{
printf("%.4s %u\n", section, section_size);
if ((size & 1) == 0) // if container has EVEN size
section_size = (section_size+1)&~1; // round to WORD boundary
fseek(f, section_size, SEEK_CUR);
}
if (section_size > size)
{
fprintf(stderr, "remaining size in chunk : %u, section size is %u\n", size, section_size);
return IFF_SIZE_MISMATCH;
}
size -= section_size;
}
if (size != 0)
{
fprintf(stderr, "level=%d size=%u\n", level, size);
return IFF_SIZE_MISMATCH;
}
return 0;
}
static int parseiff_container(FILE * f, int level)
{
int i;
size_t n;
char contid[4];
char format[4];
uint32_t size;
printf("%06lX: ", ftell(f));
for (i = 0; i < level; i++)
printf(" ");
n = fread(contid, 1, 4, f);
if (n != 4)
return IFF_EOF;
if (read_long_be(f, &size) < 0)
return IFF_EOF;
printf("%.4s %u ", contid, size);
if (iscontainer(contid))
{
n = fread(format, 1, 4, f);
if (n != 4)
return IFF_EOF;
printf("%.4s\n", format);
return parseiff_chunks(f, size - 4, level+1);
}
printf("\n");
return IFF_UNRECOGNIZED_CONTID;
}
int parseiff(FILE * f)
{
int r;
long offset, file_size;
r = parseiff_container(f, 0);
if (r < 0)
return r;
// check we are at end of file
offset = ftell(f);
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fseek(f, offset, SEEK_SET);
if (file_size > offset + 8)
{
fprintf(stderr, "Tying to parse the %ld extra bytes.\n", file_size - offset);
r = parseiff_chunks(f, file_size - offset, 0);
if (r < 0)
return r;
}
offset = ftell(f);
fseek(f, 0, SEEK_END);
file_size = ftell(f);
if (offset != file_size)
{
fprintf(stderr, "parsed %ld bytes, but file is %ld bytes long.\n", offset, file_size);
return IFF_FILE_TOO_LONG;
}
return 0;
}
int main(int argc, char * * argv)
{
const char * filename;
FILE * f;
int r;
printf("IFF file parser. Displays structure of IFF files.\n");
if (argc < 2)
{
printf("Usage: %s \n", argv[0]);
return 1;
}
filename = argv[1];
f = fopen(filename, "rb");
if (f == NULL)
{
fprintf(stderr, "Can't open file %s for reading.\n", filename);
return 2;
}
r = parseiff(f);
if (r < 0)
{
putchar('\n');
fprintf(stderr, "%s: ERROR %s (%d)\n", filename, parseiff_errorstr(r), r);
}
return r;
}