diff --git a/tools/Makefile b/tools/Makefile index 0ad4bb5a..e024c483 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,9 +1,12 @@ -.PHONY: all gifanalyzer sdl_image_test +.PHONY: all gifanalyzer sdl_image_test test_iff -all: gifanalyzer sdl_image_test +all: gifanalyzer sdl_image_test test_iff gifanalyzer: $(MAKE) -C $@ sdl_image_test: $(MAKE) -C $@ + +test_iff: + $(MAKE) -C $@ diff --git a/tools/test_iff/.gitignore b/tools/test_iff/.gitignore new file mode 100644 index 00000000..3effc978 --- /dev/null +++ b/tools/test_iff/.gitignore @@ -0,0 +1,2 @@ +*.o +parseiff diff --git a/tools/test_iff/Makefile b/tools/test_iff/Makefile new file mode 100644 index 00000000..d1ffc306 --- /dev/null +++ b/tools/test_iff/Makefile @@ -0,0 +1,10 @@ +CFLAGS = -Wall + +.PHONY: all clean + +all: parseiff + +clean: + $(RM) *.o parseiff + +parseiff: parseiff.o diff --git a/tools/test_iff/checkiff.sh b/tools/test_iff/checkiff.sh new file mode 100755 index 00000000..65e97e15 --- /dev/null +++ b/tools/test_iff/checkiff.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# set executable +PARSEIFF=./parseiff +if [ ! -x ${PARSEIFF} ] ; then + PARSEIFF="$(dirname $0)/parseiff" +fi + +if [ "$1" = "" ] ; then + echo "Usage: $0 " + echo "" + echo "Just run the parser on files. For directories, list files with parsing errors." + exit 1 +fi + +if [ -d "$1" ] ; then + find "$1" | + while IFS= read line; do + if [ -f "$line" ] && file "$line" | grep -q "IFF data" ; then + ${PARSEIFF} "$line" > /dev/null + #if ! ${PARSEIFF} "$line" > /dev/null ; then + # echo "error in $line" + #fi + fi + done +else + ${PARSEIFF} $1 +fi diff --git a/tools/test_iff/parseiff.c b/tools/test_iff/parseiff.c new file mode 100644 index 00000000..d9ab2a5a --- /dev/null +++ b/tools/test_iff/parseiff.c @@ -0,0 +1,210 @@ +/* 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); + 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); + 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; +}