/*
 *  MapToSpr
 *  Reconstructs an image from a mosaic of smaller images
 *  Copyright (C) 2006  Chris Bazley
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public Licence as published by
 *  the Free Software Foundation; either version 2 of the Licence, or
 *  (at your option) any later version.
 *
 *  This program 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 Licence for more details.
 *
 *  You should have received a copy of the GNU General Public Licence
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* ISO headers */
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#include <stdint.h>

/* RISC OS headers */
#ifdef RISC_OS
#include "kernel.h"
#include "swis.h"
#endif

/* Local headers */
#include "SprFormats.h"

static bool verbose = false, order = false, vflip = false;
static unsigned int width;
static char *input_map = NULL, *output_sprite = NULL, *input_tiles = NULL;

static void *safe_calloc(size_t nmemb, size_t size)
{
  void *mem = calloc(nmemb, size);
  if (mem == NULL) {
    fprintf(stderr, "Memory allocation of %u bytes failed\n", nmemb * size);
    exit(EXIT_FAILURE);
  }
  return mem;
}

static void file_to_sprite_name(spriteheader *sprite_header, const char *file_path)
{
  const char *ptr = file_path + strlen(file_path); /* terminator */
  const char *leaf_name;
  unsigned int dotcount = 0, c;

  while(ptr > file_path && dotcount < 1) {
    ptr--; /* scan string backwards from terminator */
    if (*ptr == '.')
      dotcount++;
  }
  leaf_name = (dotcount >= 1 ? ptr + 1 : file_path);

  memset(sprite_header->name, '\0', sizeof(sprite_header->name));
  for(c = 0; c < sizeof(sprite_header->name) && leaf_name[c] != '\0'; c++)
    sprite_header->name[c] = tolower(leaf_name[c]);
}

static spriteheader *find_tile_sprite(spriteareaheader *sprite_area, unsigned int tile_no)
{
  /* Returns a pointer to a sprite of the given name */
  spriteheader *sph;
  char sprite_name[sizeof(sph->name) + 1];
  unsigned int sprite_no;

#ifndef NDEBUG
  printf("Searching for tile sprite %u\n", tile_no);
#endif

  if (order) {
    if (tile_no >= sprite_area->sprite_count) {
      fprintf(stderr, "Tile sprite reference %u out of range (%u in file)\n",
              tile_no, sprite_area->sprite_count);
      exit(EXIT_FAILURE);
    }
  } else {
#ifdef RISC_OS
    const _kernel_oserror *e;
#endif

    sprintf(sprite_name, "tile_%u", tile_no);

#ifdef RISC_OS
    e = _swix(OS_SpriteOp, _INR(0,2)|_OUT(2), SPRITEOP_SELECT_SPRITE +
        SPRITEOP_USERAREA_SPRNAME, sprite_area, sprite_name, &sph);
    if (e == NULL) {
#ifndef NDEBUG
      printf("Address of sprite is %p\n", sph);
#endif
      return sph;
    }
    if (e->errnum != SPRITE_ERR_DOESNTEXIST)
      fprintf(stderr, "Could not select sprite: %s\n", e->errmess);
    else
      fprintf(stderr, "Sprite '%s' not found\n", sprite_name);
    exit(EXIT_FAILURE);
#endif
  }

  /* Scan the sprite area, which is a bit like a linked list
     (the header of each sprite contains a byte offset to the next) */
  sph = (spriteheader *)((char *)sprite_area + sprite_area->first);
  for (sprite_no = 0; sprite_no < sprite_area->sprite_count; sprite_no++)
  {
    assert((char *)sph < (char *)sprite_area + sprite_area->used);

    if (order && sprite_no == tile_no || !order && strncmp(sph->name,
    sprite_name, sizeof(sph->name))) {
#ifndef NDEBUG
      printf("Address of sprite is %p\n", sph);
#endif
      return sph;
    }

    sph = (spriteheader *)((char *)sph + sph->size);
  }

  fprintf(stderr, "Tile sprite %u not found\n", tile_no);
  exit(EXIT_FAILURE);
  return NULL; /* suppress compiler warning */
}

#ifdef RISC_OS

static void verify_sprite_area(spriteareaheader *sprite_area)
{
  /* Verify a sprite area (requires SpriteExtend 0.99 or later) */
  _kernel_oserror *e;
  e = _swix(OS_SpriteOp, _INR(0,1), SPRITEOP_USERAREA_SPRNAME +
            SPRITEOP_VERIFY_AREA, sprite_area);
  if (e != NULL) {
    fprintf(stderr, "Verification failed: %s\n", e->errmess);
    exit(EXIT_FAILURE);
  }
}

static void set_file_type(const char *file, int file_type)
{
  _kernel_osfile_block params;

  if(verbose) {
    char sys_var_name[32], file_type_string[16];
    const char *var;

    sprintf(sys_var_name, "File$Type_%X", file_type);
    var = getenv(sys_var_name);
    if(var == NULL)
      sprintf(file_type_string, "&%X", file_type);

    printf("Setting type of output file '%s' to %s.\n", file, var != NULL ?
           var : file_type_string);
  }

  /* Set type of named object */
  params.load = file_type;
  if (_kernel_osfile(18, file, &params) == _kernel_ERROR) {
    fprintf(stderr, "Failed to set file type: %s\n",
            _kernel_last_oserror()->errmess);
    exit(EXIT_FAILURE);
  }
}

static void check_file_type(const char *file, int req_file_type)
{
  _kernel_osfile_block params;
  int object_type, file_type;

  if(verbose)
    printf("Checking type of input file '%s'.\n", file);

  /* Read catalogue info for object without path */
  object_type = _kernel_osfile(17, file, &params);
  if(object_type == _kernel_ERROR) {
    fprintf(stderr, "Failed to read catalogue info: %s\n",
            _kernel_last_oserror()->errmess);
    exit(EXIT_FAILURE);
  }

  /* Generate a standard format error message if input file not found,
     or it is a directory */
  if(!object_type || object_type == 2) {
    params.load = object_type;
    if(_kernel_osfile(19, file, &params) == _kernel_ERROR)
      fprintf(stderr, "%s\n", _kernel_last_oserror()->errmess);
    exit(EXIT_FAILURE);
  }

  if((params.load & 0xfff00000) != 0xfff00000) {
    fprintf(stderr, "Input file is untyped (should be &%X)\n", req_file_type);
    exit(EXIT_FAILURE);
  }

  /* Extract file type (0-4095) from file's load address */
  file_type = params.load >> 8 & 0xfff;

  if(req_file_type != file_type) {
    char sys_var_name[32], req_type_string[16], file_type_string[16];
    const char *var;

    sprintf(sys_var_name, "File$Type_%X", req_file_type);
    var = getenv(sys_var_name);
    if(var != NULL) {
      strncpy(req_type_string, var, sizeof(req_type_string));
      req_type_string[sizeof(req_type_string) - 1] = '\0';
    } else {
      sprintf(req_type_string, "&%X", req_file_type);
    }

    sprintf(sys_var_name, "File$Type_%X", file_type);
    var = getenv(sys_var_name);
    if(var != NULL) {
      strncpy(file_type_string, var, sizeof(file_type_string));
      file_type_string[sizeof(file_type_string) - 1] = '\0';
    } else {
      sprintf(file_type_string, "&%X", file_type);
    }

    fprintf(stderr, "Input file has type %s (should be %s)\n",
            file_type_string, req_type_string);
    exit(EXIT_FAILURE);
  }
}
#endif

static void syntax_msg(void)
{
  puts("Syntax: *MapToSpr [-verbose] [-order] [-vflip] [-width 1..N] [-output <outputfile>] <mapfile> <tilesfile>");
}

static void process_arguments(int argc, char *argv[])
{
  int arg;

  if (argc < 1) {
    fprintf(stderr, "Too few arguments\n");
    syntax_msg();
    exit(EXIT_FAILURE);
  }

  for(arg = 1; arg < argc && *argv[arg] == '-'; arg++) {
    if(!strcmp(argv[arg], "-width")) {
      arg++;
      if(arg >= argc) {
        syntax_msg();
        exit(EXIT_FAILURE);
      }
      for(int c = 0; c < strlen(argv[arg]); c++) {
        if(!isdigit(argv[arg][c])) {
          fprintf(stderr, "Width of input map must be numeric\n");
          exit(EXIT_FAILURE);
        }
      }
      sscanf(argv[arg], "%u", &width);
      if (width < 1 || width > UINT_MAX) {
        fprintf(stderr, "Width of input map must be in the range 1-%u\n",
                UINT_MAX);
        exit(EXIT_FAILURE);
      }
      continue;
    }

    if(!strcmp(argv[arg], "-vflip")) {
      vflip = true;
      continue;
    }

    if(!strcmp(argv[arg], "-verbose")) {
      verbose = true;
      continue;
    }

    if(!strcmp(argv[arg], "-order")) {
      order = true;
      continue;
    }

    if(!strcmp(argv[arg], "-output")) {
      arg++;
      if(arg >= argc) {
        syntax_msg();
        exit(EXIT_FAILURE);
      }
      output_sprite = argv[arg];
      continue;
    }

    fprintf(stderr, "Unknown switch '%s'\n", argv[arg]);
    syntax_msg();
    exit(EXIT_FAILURE);
  }

  if (arg >= argc) {
    fprintf(stderr, "No map file specified\n");
    syntax_msg();
    exit(EXIT_FAILURE);
  }
  input_map = argv[arg++];

  if (arg >= argc) {
    fprintf(stderr, "No tiles file specified\n");
    syntax_msg();
    exit(EXIT_FAILURE);
  }
  input_tiles = argv[arg++];

  if (arg < argc) {
    syntax_msg();
    exit(EXIT_FAILURE);
  }

  if(verbose) {
    printf("Input map file is '%s'.\n", input_map);
    printf("Input tiles file is '%s'.\n", input_tiles);
    if (width)
      printf("Using %u as width of input map.\n", width);
    else
      puts("Width of input map not specified.");
    if (vflip)
      puts("Will flip input map vertically.");

    if(output_sprite != NULL)
      printf("Output sprite file is '%s'.\n", output_sprite);
    else
      puts("No output sprite file.");
  }
}

static spriteareaheader *load_tile_sprites(void)
{
  spriteareaheader *input_buffer;
  size_t input_buffer_size;
  FILE *input;
  long int end_of_input;

#ifdef RISC_OS
  check_file_type(input_tiles, FILETYPE_SPRITE);
#endif

  if(verbose)
    printf("Opening tile sprites file '%s'.\n", input_tiles);
  input = fopen(input_tiles, "rb");
  if(input == NULL) {
    fprintf(stderr, "Could not open tile sprites file '%s'\n", input_tiles);
    exit(EXIT_FAILURE);
  }

  fseek(input, 0, SEEK_END);
  end_of_input = ftell(input);
  input_buffer_size = offsetof(spriteareaheader, sprite_count) +
                      (size_t)end_of_input;
  if(verbose)
    printf("Allocating input buffer of %u bytes.\n", input_buffer_size);
  input_buffer = (spriteareaheader *)malloc(input_buffer_size);
  if (input_buffer == NULL) {
    fprintf(stderr, "Memory allocation of %u bytes failed\n",
            input_buffer_size);
    fclose(input);
    exit(EXIT_FAILURE);
  }
  input_buffer->size = input_buffer_size;

  if(verbose)
    printf("About to read tile sprites into buffer.\n");

  fseek(input, 0, SEEK_SET);
  if(!fread(&input_buffer->sprite_count, (size_t)end_of_input, 1, input)) {
    fprintf(stderr, "Failed to read %ld bytes from tile sprites file\n",
            end_of_input);
    fclose(input);
    exit(EXIT_FAILURE);
  }

  if(verbose)
    printf("Closing tile sprites file.\n");
  fclose(input);

  if(verbose)
    printf("Tiles file contains %u sprites\n", input_buffer->sprite_count);

#ifdef RISC_OS
  verify_sprite_area(input_buffer);
#endif

  return input_buffer;
}

static uint8_t *load_map_file(bool wide_map, size_t *return_map_height)
{
  FILE *input;
  long int end_of_input;
  uint8_t *input_buffer;
  size_t map_height, grid_size;

  if (verbose)
    printf("Opening map file '%s'.\n", input_map);
  input = fopen(input_map, "rb");
  if (input == NULL) {
    fprintf(stderr, "Could not open map file '%s'\n", input_map);
    exit(EXIT_FAILURE);
  }

  fseek(input, 0, SEEK_END);
  end_of_input = ftell(input);
  grid_size = (size_t)(wide_map ? end_of_input / sizeof(uint16_t) :
              end_of_input / sizeof(uint8_t));

  if (!width) {
    /* If no width was specified by the user then we attempt to guess it from
       the map size. We assume that the map is probably wider than it is tall.
       Our first guess is the square root of the map area. We then try
       progressively smaller heights until we find one that fits into the area
       an exact no. of times. No more than 256 iterations should be required
       before reaching a height of 1 (which always fits). */
    unsigned int height = (unsigned int)sqrt(grid_size);

    while (height > 0 && grid_size % height) {
#ifndef NDEBUG
      printf("Height %u didn't fit\n", height);
#endif
      height--;
    }
    width = grid_size / height;
    if (verbose)
      printf("Guess width of map is %u (none specified)\n", width);
  }

  map_height = grid_size / width;
  if (!grid_size || grid_size % width) {
    fprintf(stderr, "Size of map (%ld bytes) is not a multiple of the "
            "specified width\n", end_of_input);
    fclose(input);
    exit(EXIT_FAILURE);
  }

  if (verbose)
    printf("Allocating input buffer of %u bytes.\n", (size_t)end_of_input);
  input_buffer = (uint8_t *)malloc((size_t)end_of_input);
  if (input_buffer == NULL) {
    fprintf(stderr, "Memory allocation of %u bytes failed\n",
            (size_t)end_of_input);
    fclose(input);
    exit(EXIT_FAILURE);
  }

  if (verbose)
    printf("About to read map into buffer.\n");

  fseek(input, 0, SEEK_SET);
  if (vflip) {
    /* Read one line at a time from the map file */
    if (wide_map) {
      uint16_t *map_16_bit = (uint16_t *)input_buffer + (map_height - 1) *
                             width;
      for (unsigned int row = 0; row < map_height; row ++) {
        if (fread(map_16_bit, sizeof(uint16_t), width, input) != width) {
          fprintf(stderr, "Failed reading a line from the map file\n");
          fclose(input);
          exit(EXIT_FAILURE);
        }
        map_16_bit -= width;
      } /* next row */
    } else {
      uint8_t *map_8_bit = (uint8_t *)input_buffer + (map_height - 1) * width;
      for (unsigned int row = 0; row < map_height; row ++) {
        if (fread(map_8_bit, sizeof(uint8_t), width, input) != width) {
          fprintf(stderr, "Failed reading a line from the map file\n");
          fclose(input);
          exit(EXIT_FAILURE);
        }
        map_8_bit -= width;
      } /* next row */
    }
  } else {
    /* Read the whole map at once */
    if (!fread(input_buffer, (size_t)end_of_input, 1, input)) {
      fprintf(stderr, "Failed to read %ld bytes from the map file\n",
              end_of_input);
      fclose(input);
      exit(EXIT_FAILURE);
    }
  }

  if(verbose)
    printf("Closing map file.\n");
  fclose(input);

  if (return_map_height != NULL)
    *return_map_height = map_height;

  return input_buffer;
}

static void save_sprite(void *map, bool wide_map, size_t map_width, size_t map_height, spriteareaheader *tile_sprites)
{
  spriteareaheader sprite_area_header;
  spriteheader sprite_header;
  spriteheader *first_tile_spr;
  clock_t start_time;
  FILE *output;
  unsigned int grid_loc_count, bpp, first_tile_spr_type,
               tile_width, tile_height,
               output_width, output_height;
  size_t output_buf_size, output_image_size, map_size, output_row_length,
         tile_row_length;
  void *output_buffer;
  unsigned long next_perc, one_perc;

  assert(tile_sprites != NULL);
  first_tile_spr = (spriteheader *)((char *)tile_sprites + tile_sprites->first);

  first_tile_spr_type = (first_tile_spr->type & SPRITE_INFO_TYPE_MASK) >>
                        SPRITE_INFO_TYPE_SHIFT;

  if(!(first_tile_spr->type & SPRITE_INFO_NOT_MODE_SEL) ||
  (first_tile_spr_type != SPRITE_TYPE_32BPP && first_tile_spr_type !=
  SPRITE_TYPE_16BPP && first_tile_spr_type != SPRITE_TYPE_8BPP))
  {
    fprintf(stderr, "Tile sprites must be new format with 8, 16 or 32 bpp\n");
    exit(EXIT_FAILURE);
  }
  switch (first_tile_spr_type) {
    case SPRITE_TYPE_8BPP:
      bpp = 8;
      break;

    case SPRITE_TYPE_16BPP:
      bpp = 16;
      break;

    default:
      bpp = 32;
      break;
  }
  if(verbose)
    printf("First tile sprite has %u bits per pixel.\n", bpp);

  tile_row_length = (first_tile_spr->width + 1) * 4;
  tile_width = tile_row_length / (bpp / 8);
  if (first_tile_spr->right_bit < 31) {
    unsigned int wasted_bits = 31 - first_tile_spr->right_bit;
#ifndef NDEBUG
    printf("Righthand wastage is %u bits\n", wasted_bits);
#endif
    tile_width -= wasted_bits / bpp;
  }
  tile_height = first_tile_spr->height + 1;
  if(verbose)
    printf("First tile sprite is %u by %u pixels.\n", tile_width, tile_height);

  output_width = tile_width * map_width;
  output_height = tile_height * map_height;
  output_row_length = (output_width * bpp / 8 + 3 & ~3);
  output_image_size = output_row_length * output_height;
  if(verbose)
    printf("Output sprite will be %u by %u pixels (%u bytes).\n", output_width,
           output_height, output_image_size);

  /* Allocate memory for one row of tiles
     (a horizontal strip of the output image) */
  output_buf_size = output_row_length * tile_height;
  if(verbose)
    printf("Allocating %u bytes for horizontal strip of output sprite.\n",
           output_buf_size);
  output_buffer = safe_calloc(1, output_buf_size);

  /* Initialise the header of the sprite area. We don't bother setting the
     area size, because this is not included in the output file anyway. */
  sprite_area_header.sprite_count = 1;
  sprite_area_header.first = sizeof(spriteareaheader);
  sprite_area_header.used = sizeof(spriteareaheader) + sizeof(spriteheader) +
                            output_image_size;

  /* Create the output sprite file */
  if (verbose)
    printf("Opening output file '%s'.\n", output_sprite);
  output = fopen(output_sprite, "wb");
  if (output == NULL) {
    fprintf(stderr, "Could not open output file '%s'\n", output_sprite);
    exit(EXIT_FAILURE);
  }

  /* Write the area header to the output sprite file. */
#ifndef NDEBUG
  puts("Writing sprite area header to file");
#endif
  if (fwrite(&sprite_area_header.sprite_count, sizeof(spriteareaheader) -
  offsetof(spriteareaheader, sprite_count), 1, output) < 1)
  {
    fprintf(stderr, "Failed to write %u bytes to output file\n",
            sizeof(spriteareaheader) - offsetof(spriteareaheader,
            sprite_count));
    fclose(output);
    exit(EXIT_FAILURE);
  }

  /* Initialise the header of the lone output sprite. */
  sprite_header.size = sizeof(spriteheader) + output_image_size;
  file_to_sprite_name(&sprite_header, output_sprite);
  sprite_header.width = output_row_length / 4 - 1;
  sprite_header.height = output_height - 1;
  sprite_header.left_bit = 0;
  sprite_header.right_bit = SPRITE_RIGHT_BIT(output_width, bpp);
  sprite_header.image = sizeof(spriteheader);
  sprite_header.mask = sizeof(spriteheader);
  sprite_header.type = first_tile_spr->type;

  /* Write the sprite header to the output file. */
#ifndef NDEBUG
  puts("Writing sprite header to file");
#endif
  if (fwrite(&sprite_header, sizeof(spriteheader), 1, output) < 1) {
    fprintf(stderr, "Failed to write %u bytes to output file\n",
            sizeof(spriteheader));
    fclose(output);
    exit(EXIT_FAILURE);
  }

  next_perc = 0;
  grid_loc_count = 0;
  map_size = map_width * map_height;

  if(verbose) {
    puts("Constructing output image...");
    one_perc = ((unsigned long)map_size << 14) / 100;
    start_time = clock();
  } else {
    /* These initialisations are just to suppress compiler warnings */
    start_time = 0;
    one_perc = 0;
  }

  for (unsigned int map_row = 0; map_row < map_height; map_row++) {
    char *lhs_of_tile = (char *)output_buffer;
    size_t bytes_to_copy = tile_width * bpp / 8;

    for (unsigned int map_col = 0; map_col < map_width; map_col++) {
      spriteheader *tile_sprite;
      char *dest_pixel_row, *src_pixel_row;
      uint8_t *map_8_bit;
      uint16_t *map_16_bit;

      if(verbose && (unsigned long)grid_loc_count << 14 >= next_perc) {
        printf("%u%% done\n", (grid_loc_count * 100) / map_size);
        next_perc += one_perc;
      }

      if (wide_map) {
         map_16_bit = (uint16_t *)map;
         tile_sprite = find_tile_sprite(tile_sprites, map_16_bit[map_row *
                       map_width + map_col]);
      } else {
         map_8_bit = (uint8_t *)map;
         tile_sprite = find_tile_sprite(tile_sprites, map_8_bit[map_row *
                       map_width + map_col]);
      }
      if (tile_sprite != first_tile_spr && memcmp(&tile_sprite->width,
      &first_tile_spr->width, sizeof(spriteheader) - offsetof(spriteheader,
      width))) {
        fprintf(stderr, "Mismatch between tile sprites (must all have the same"
                "dimensions and format)\n");
        fclose(output);
        exit(EXIT_FAILURE);
      }
      src_pixel_row = (char *)tile_sprite + tile_sprite->image;
      dest_pixel_row = lhs_of_tile;

      for (unsigned int pix_row = 0; pix_row < tile_height; pix_row++) {
        memcpy(dest_pixel_row, src_pixel_row, bytes_to_copy);
        dest_pixel_row += output_row_length;
        src_pixel_row += tile_row_length;
      }

      grid_loc_count++;
      lhs_of_tile += bytes_to_copy;
    } /* next col */

    /* Write a horizontal strip of the output image to file */
#ifndef NDEBUG
    printf("Writing strip of output image to file\n");
#endif
    if (fwrite(output_buffer, output_buf_size, 1, output) < 1) {
      fprintf(stderr, "Failed to write %u bytes to output file\n",
              output_buf_size);
      fclose(output);
      exit(EXIT_FAILURE);
    }
  } /* next row */

  if(verbose) {
    double elapsed = (double)(clock() - start_time) / CLOCKS_PER_SEC;
    printf("Finished in %.2f seconds", elapsed);
    if (elapsed)
      printf(" (%.2f pixels per second)\n", ((double)tile_height * tile_width *
             grid_loc_count) / elapsed);
    else
      putchar('\n');
  }

  if (verbose)
    printf("Closing output file.\n");
  fclose(output);

#ifdef RISC_OS
  set_file_type(output_sprite, FILETYPE_SPRITE);
#endif
}

static bool wide_map_required(spriteareaheader *tile_sprites)
{
  spriteheader *input_sprite;
  char sprite_name[sizeof(input_sprite->name) + 1];
  unsigned int tilenum, sprite;

  if (order)
    return (tile_sprites->sprite_count > UINT8_MAX + 1);

  input_sprite = (spriteheader *)((char *)tile_sprites + tile_sprites->first);

  for (sprite = 0; sprite < tile_sprites->sprite_count; sprite++)
  {
    /* The sprite name embedded in the header may be unterminated,
     so we make a usable copy before passing it to sscanf */
    strncpy(sprite_name, input_sprite->name, sizeof(sprite_name) - 1);
    sprite_name[sizeof(sprite_name) - 1] = '\0';

    if(sscanf(sprite_name, "tile_%u", &tilenum) == 1 && tilenum > UINT8_MAX)
      return true;

    /* Calculate address of next sprite */
    input_sprite = (spriteheader *)((char *)input_sprite + input_sprite->size);
  } /* next sprite */

  return false;
}

int main(int argc, char *argv[])
{
  spriteareaheader *tile_sprites;
  uint8_t *map;
  size_t map_height;
  bool wide_map;

  process_arguments(argc, argv);

  tile_sprites = load_tile_sprites();
  wide_map = wide_map_required(tile_sprites);
  if (verbose)
    printf("Given no. of tile sprites, assuming %s map.\n", wide_map ?
           "wide" : "byte");

  map = load_map_file(wide_map, &map_height);

  if(output_sprite != NULL)
    save_sprite(map, wide_map, width, map_height, tile_sprites);

  free(map);
  free(tile_sprites);

  return EXIT_SUCCESS;
}
