useful to see if an IFF file is malformed, but also to see how many frames there is in an animation, etc.
		
			
				
	
	
		
			211 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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 <http://www.gnu.org/licenses/>
 | 
						|
*/
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#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 <file.iff>\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;
 | 
						|
}
 |