
#include <inttypes.h>
#include "../fizmo/iff.h"
#include "../fizmo/types.h"

int main(int argc, char *argv[])
{
  FILE *iff_file;
  char release_number[2];
  char serial_number[7];
  char checksum[2];
  uint8_t pc_on_restore_data[3];
  uint32_t pc_on_restore;
  int argindex = 1;
  int chunk_length;
  int bytes_read;
  int data;
  uint32_t stack_frame_return_pc;
  bool stack_frame_discard_result;
  uint8_t stack_frame_result_var;
  uint8_t stack_frame_argument_mask;
  uint8_t stack_frame_arguments_supplied;
  uint8_t current_stack_frame_nof_locals = 0;
  uint16_t current_stack_frame_nof_functions_stack_words = 0;
  int frameno;
  uint16_t stack_word;
  int i;

  if (argc < 2)
  {
    puts("\nqzinspect 0.0.1 - Inspect quetzal savegame files");
    puts("See http://www.gnelson.demon.co.uk/zspec/quetzal.html\n");
    puts("Syntax: qzinspect <qzfile-1> [qzfile-2] ... [qzfile-n]\n");
    return 1;
  }

  while (argindex < argc)
  {
    printf("\nInspecting file \"%s\":\n", argv[argindex]);
    if ((iff_file = fopen(argv[argindex], "r")) == NULL)
    {
      perror("Error opening file");
      return -1;
    }

    if (find_chunk("IFhd", iff_file) == -1)
    {
      puts("Could not find \"IFhd\" chunk.");
    }
    else
    {
      if (fseek(iff_file, 4, SEEK_CUR) != 0)
      {
        perror("Error reading file");
        return -1;
      }

      if (fread(release_number, 2, 1, iff_file) != 1)
      {
        perror("Error reading file");
        return -1;
      }

      if (fread(serial_number, 6, 1, iff_file) != 1)
      {
        perror("Error reading file");
        return -1;
      }
      serial_number[6] = '\0';

      if (fread(checksum, 2, 1, iff_file) != 1)
      {
        perror("Error reading file");
        return -1;
      }

      if (fread(pc_on_restore_data, 3, 1, iff_file) != 1)
      {
        perror("Error reading file");
        return -1;
      }

      pc_on_restore = 0;
      pc_on_restore |= pc_on_restore_data[0] << 16;
      pc_on_restore |= pc_on_restore_data[1] << 8;
      pc_on_restore |= pc_on_restore_data[2];

      printf("Release number: %d.\n",
          ((uint8_t)(release_number[0] << 8) + ((uint8_t)release_number[1])));

      printf("Serial number: %s.\n", serial_number);

      printf("Checksum: %d.\n",
          (((uint8_t)checksum[0] << 8) || ((uint8_t)checksum[1])));

      printf("PC on restore: $%x.\n", (unsigned)pc_on_restore);
    }

    if (find_chunk("CMem", iff_file) == -1)
    {
      puts("Could not find \"CMem\" chunk.");
    }
    else
    {
      read_chunk_length(iff_file);
      chunk_length = get_last_chunk_length();
      printf("CMem chunk has a length of %d bytes.\n", chunk_length);
    }

    if (find_chunk("UMem", iff_file) == -1)
    {
      puts("Could not find \"UMem\" chunk.");
    }
    else
    {
      read_chunk_length(iff_file);
      chunk_length = get_last_chunk_length();
      printf("UMem chunk has a length of %d bytes.\n", chunk_length);
    }

    if (find_chunk("Stks", iff_file) == -1)
    {
      puts("Could not find \"Stcks\" chunk.");
    }
    else
    {
      read_chunk_length(iff_file);
      chunk_length = get_last_chunk_length();
      frameno = 1;
      while (bytes_read < chunk_length)
      {
        printf("\nStack frame #%d:\n", frameno++);
        // PC Bits 16-23
        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        stack_frame_return_pc = (data & 0xff) << 16;

        // PC Bits 8-15
        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        stack_frame_return_pc |= (data & 0xff) << 8;

        // PC Bits 0-7
        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        stack_frame_return_pc |= (data & 0xff);

        printf("Stack frame return PC: %x.\n", stack_frame_return_pc);

        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        stack_frame_discard_result = ((data & 0x10) != 0 ? true : false);
        current_stack_frame_nof_locals = (data & 0xf);

        printf("Discard result: ");
        if (stack_frame_discard_result == true)
          puts("true");
        else
          puts("false");
        printf("Number of locales: %d\n", current_stack_frame_nof_locals);

        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        stack_frame_result_var = (data & 0xff);
        printf("Stack frame result variable code: %x.\n",
            stack_frame_result_var);

        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        stack_frame_argument_mask = (data & 0xff);
        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        current_stack_frame_nof_functions_stack_words = ((data & 0xff) << 8);

        data = fgetc(iff_file);
        if (data == EOF)
        {
          perror("Error reading file");
          return -1;
        }
        current_stack_frame_nof_functions_stack_words |= (data & 0xff);
        printf("Number of stack entries: %d.\n",
            current_stack_frame_nof_functions_stack_words);

        bytes_read += 8;

        stack_frame_arguments_supplied = 0;
        while (stack_frame_argument_mask != 0)
        {
          stack_frame_arguments_supplied++;
          stack_frame_argument_mask >>= 1;
        }
        printf("Number of variables supplied to function call: %d.\n",
            stack_frame_arguments_supplied);

        printf("Locals: ");
        i = 0;
        // write locals and stack
        while (i < current_stack_frame_nof_locals)
        {
          data = fgetc(iff_file);
          if (data == EOF)
          {
            perror("Error reading file");
            return -1;
          }
          stack_word = (data & 0xff) << 8;

          data = fgetc(iff_file);
          if (data == EOF)
          {
            perror("Error reading file");
            return -1;
          }
          stack_word |= (data & 0xff);

          //z_stack_push_word(stack_word);
          if (i != 0)
            printf(", ");
          printf("$%x", stack_word);

          i++;
        }
        bytes_read += i * 2;
        printf("\n");

        printf("Stack entries:");
        i = 0;
        while (i < current_stack_frame_nof_functions_stack_words)
        {
          data = fgetc(iff_file);
          if (data == EOF)
          {
            perror("Error reading file");
            return -1;
          }
          stack_word = (data & 0xff) << 8;

          data = fgetc(iff_file);
          if (data == EOF)
          {
            perror("Error reading file");
            return -1;
          }
          stack_word |= (data & 0xff);

          //z_stack_push_word(stack_word);
          if (i != 0)
            printf(", ");
          printf("$%x", stack_word);

          i++;
        }
        printf("\n");
        bytes_read += i * 2;

      }
      printf("\n");
    }

    if (find_chunk("ANNO", iff_file) == -1)
    {
      puts("Could not find \"ANNO\" chunk.\n");
    }
    else
    {
      read_chunk_length(iff_file);
      chunk_length = get_last_chunk_length();
      printf("\"ANNO\" chunk contains %d bytes.\n", chunk_length);
      while (chunk_length--)
        putchar(fgetc(iff_file));
      putchar('\n');
    }

    if (find_chunk("FILE", iff_file) == -1)
    {
      puts("Could not find \"FILE\" chunk.\n");
    }
    else
    {
      read_chunk_length(iff_file);
      chunk_length = get_last_chunk_length();
      printf("\"FILE\" chunk contains %d bytes.\n", chunk_length);
      while (chunk_length--)
        putchar(fgetc(iff_file));
      putchar('\n');
    }

    fclose(iff_file);

    argindex++;
  }

  return 0;
}

