/*
 *  main.c - main program
 *
 *  Written by:
 *   Andreas Dehmel
 *
 *  This file is part of armDeu, the portable WAD utility (the name derives
 *  from its initial port to the ARM-based RISC OS). armDeu is released under
 *  the GNU Public License (GPL) in the hope that it proves useful. Please
 *  note there is NO WARRANTY. For more information read the file License
 *  included in this release.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


#include "deu.h"
#include "main.h"




#define STRINGSIZE	256


typedef struct lump_desc_s {
  char *file;
  char *name;
} lump_desc_t;



Bool Registered = TRUE;




#ifdef __BIG_ENDIAN__
unsigned short	SwapSHORT(unsigned short x)
{
  return ((x << 8) | (x >> 8));
}

unsigned long	SwapLONG(unsigned long x)
{
  return ((x << 24) | ((x & 0xff00) << 8) | ((x &0xff0000) >> 8) | (x >> 24));
}
#endif



#ifdef __riscos__
static unsigned char *lower_case_table = NULL;

static void BuildLowerCaseTable(void)
{
  int i;

  lower_case_table = (unsigned char*)safe_malloc(256*sizeof(char));
  for (i=0; i<256; i++)
  {
    lower_case_table[i] = tolower(i);
  }
}


int strncasecmp (const char *str1, const char *str2, unsigned int size)
{
  unsigned char *a, *b;
  int i = size;

  a = (unsigned char *)str1; b = (unsigned char *)str2;

  while ((*a != 0) && (i > 0))
  {
    if (lower_case_table[*a] != lower_case_table[*b]) break;
    a++; b++; i--;
  }
  if ((lower_case_table[*a] == lower_case_table[*b]) || (i <= 0)) return 0;
  if (lower_case_table[*a] < lower_case_table[*b]) return -1;
  else return 1;
}
#endif



void *safe_malloc(unsigned int size)
{
   void *mem;

   if ((mem = malloc(size)) == NULL)
   {
      fprintf(stderr, "Unable to claim memory.\n");
      exit(-1);
   }
   return mem;
}



static void PrintUsage(const char *myname, int level)
{
  printf("armDeu 1.18b by Andreas Dehmel, GPL\n");
  if (level > 0)
  {
    printf("Based on Deu 5.21 by Brendon Wyber and Raphael Quinet\n\n");
    printf("Usage: %s [<mainWAD>] [OPTIONS]\n", myname);
    printf("OPTIONS:\n");
    printf("-d <file> dumps the main WAD's directory to file. If no file is given use stdout\n");
    printf("-f <file> the same for the first patch WAD\n");
    printf("-m writes out the names of all maps in the WAD(s)\n");
    printf("-b # sets the size in kB of the buffer used when building a new WAD. Default 1MB.\n");
    printf("-e eliminates lumps with identical names except for the last one.\n");
    printf("-o <file> writes merged WADs to <file>\n");
    printf("-or <file> writes lump contents to <file> (raw binary output mode)\n");
    printf("-c # select compressed WAD output; # is between 1 and 9, the higher the slower\n");
    printf("-p <patchWAD1> ... <patchWADn> reads in patch WADs\n");
    printf("-l <lump1> ... <lumpn> reads in lumps\n");
    printf("-lf <lumpfile> reads lumps from <lumpfile> (each line: filename lumpname)\n");
    printf("-v verbose output\n");
    printf("-r <pattern> only output lumps matching the pattern\n");
    printf("-a # suppresses the first # bytes of each lump (raw binary output mode only)\n");
    printf("-x expand multiple lumps (different dir entries for same lump)\n");
    printf("-n # maximum number of lines to output before waiting for a key\n");
    printf("Patch WADs are read to the end of the argument list or an arg beginning with '-'\n");
  }
  exit(0);
}


static void ListDirectory(const char *file, int mode)
{
  FILE *fp;

  fp = stdout;
  if (file != NULL)
  {
    if ((fp = fopen(file, "w")) == NULL)
    {
      fprintf(stderr, "Unable to open file <%s>. Using stdout instead.\n", file);
      fp = stdout;
    }
  }

  if (mode == 0)
    ListMasterDirectory(fp);
  else
    ListFileDirectory(fp, WadFileList->next);

  if (fp != stdout) fclose(fp);
}


int main(int argc, char *argv[])
{
  char *mainwad = NULL;
  char *outwad = NULL;
  char *maindirfile = NULL;
  char *patchdirfile = NULL;
  char *patchwads[64];
  lump_desc_t *lumpfiles = NULL;
  int patchnumber = 0;
  int lumpnumber = 0;
  int lumpmax;
  int i, listdir;
  int listmaps;

#ifdef __riscos__
  BuildLowerCaseTable();
#endif

  if (argc == 1)
    PrintUsage(argv[0], 0);

  lumpmax = 16;
  lumpfiles = (lump_desc_t*)safe_malloc(lumpmax*sizeof(lump_desc_t));

  i=1; listdir = 0; listmaps = 0;
  while (i<argc)
  {
    if (argv[i][0] == '-')
    {
      switch (argv[i][1])
      {
        case 'o':
	  if (argv[i][2] == 'r')
	  {
	    outputMode = OUTPUT_RAW_DATA;
	  }
	  else
	  {
	    outputMode = OUTPUT_WAD_DATA;
	  }
          if (outwad == NULL)
          {
            outwad = argv[++i];
          }
          else
          {
            fprintf(stderr, "You can't specify more than one output WAD.\n");
            exit(-1);
          }
          break;
        case 'p':
          i++;
          while (i<argc)
          {
            if (argv[i][0] == '-')
            {
              i--; break;
            }
            if (patchnumber >= 64)
            {
              fprintf(stderr, "Are you absolutely sure you need more than 64 patch WADs???\n");
              exit(-1);
            }
            patchwads[patchnumber++] = argv[i++];
          }
          break;
        case 'l':
          /* read lumps from file? Or are lumps given directly? */
          if (argv[i][2] == 'f')
          {
            FILE *fp;
            char buffer[STRINGSIZE];
            unsigned char *b, *d;
            unsigned int length;
            unsigned int lfline;

            if ((fp = fopen(argv[++i], "r")) == NULL)
            {
              fprintf(stderr, "Unable to open lump descriptor file <%s>\n", argv[i]);
              exit(-1);
            }
            lfline = 0;
            while (!feof(fp))
            {
              if (fgets(buffer, STRINGSIZE, fp) == NULL) break;
              lfline++;
              if (lumpnumber >= lumpmax)
              {
                lumpmax += 16;
                if ((lumpfiles = (lump_desc_t*)realloc(lumpfiles, lumpmax*sizeof(lump_desc_t))) == NULL)
                {
                  fprintf(stderr, "Out of memory!\n");
                  exit(-1);
                }
              }
              /* remove linefeed */
              b = (unsigned char*)buffer;
              while (*b != '\0')
              {
                if ((*b == '\n') || (*b == '\r')) *b = '\0';
                b++;
              }
              b = (unsigned char*)buffer;
              /* filename and lumpname are separated by space or tab */
              while (*b > ' ') b++;
              length = (int)((char*)b - buffer);
              lumpfiles[lumpnumber].file = safe_malloc(length+1);
              strncpy(lumpfiles[lumpnumber].file, buffer, length);
              lumpfiles[lumpnumber].file[length] = '\0';
              while ((*b <= ' ') && (*b != '\0')) b++;
              if (*b == '\0')
              {
                lumpfiles[lumpnumber].name = NULL;
              }
              else
              {
                d = b;
                while (*d > ' ') d++;
                length = (int)(d - b);
                lumpfiles[lumpnumber].name = safe_malloc(length+1);
                d = (unsigned char*)(lumpfiles[lumpnumber].name);
                while (length > 0)
                {
                  *d = toupper(*b);
                  b++; d++; length--;
                }
                *d = '\0';
              }
              /*printf("%s, %s\n", lumpfiles[lumpnumber].file, (lumpfiles[lumpnumber].name == NULL) ? "NULL" : lumpfiles[lumpnumber].name);*/
              lumpnumber++;
            }
            fclose(fp);
          }
          else
          {
            i++;
            while (i<argc)
            {
              if (argv[i][0] == '-')
              {
                i--; break;
              }
              if (lumpnumber >= lumpmax)
              {
                lumpmax += 16;
                if ((lumpfiles = (lump_desc_t*)realloc(lumpfiles, lumpmax*sizeof(lump_desc_t))) == NULL)
                {
                  fprintf(stderr, "Out of memory!\n");
                  exit(-1);
                }
              }
              lumpfiles[lumpnumber].file = argv[i++];
              lumpfiles[lumpnumber++].name = NULL;
            }
          }
          break;
        case 'h':
          PrintUsage(argv[0], 1);
          break;
        case 'v':
          verbose = 1;
          break;
        case 'd':
          if ((i < argc-1) && (argv[i+1][0] != '-'))
          {
            maindirfile = argv[++i];
          }
          else
          {
            maindirfile = NULL;
          }
          listdir |= 1;
          break;
        case 'f':
          if ((i < argc-1) && (argv[i+1][0] != '-'))
          {
            patchdirfile = argv[++i];
          }
          else
          {
            patchdirfile = NULL;
          }
          listdir |= 2;
          break;
        case 'm':
          listmaps = 1;
          break;
        case 'b':
          if (i < argc-1)
          {
            buffersize = 1024*atoi(argv[++i]);
          }
          else
          {
            fprintf(stderr, "Missing buffer size!"); exit(-1);
          }
          break;
        case 'e':
          eliminate = 1;
          break;
        case 'c':
          if (i < argc-1)
          {
            compressOut = atoi(argv[++i]);
            if ((compressOut < 1) || (compressOut > 9))
            {
              fprintf(stderr, "Bad compression value, must be a number between 1 and 9\n");
              exit(-1);
            }
          }
          break;
        case 'r':
          if (i < argc-1)
          {
            lumpPattern = argv[++i];
          }
          break;
	case 'a':
	  if (i < argc-1)
	  {
	    ignoreFirstBytes = atoi(argv[++i]);
	  }
	  break;
	case 'x':
	  expandMultipleLumps = 1;
	  break;
	case 'n':
	  if (i < argc-1)
	  {
	    maxOutputLines = atoi(argv[++i]);
	  }
	  break;
        default:
          fprintf(stderr, "Bad switch -%c\n", argv[i][1]);
          break;
      }
    }
    else
    {
      if (mainwad == NULL)
      {
        mainwad = argv[i];
      }
    }
    i++;
  }

  if (mainwad != NULL)
  {
    OpenMainWad(mainwad);

    if ((listdir & 1) != 0) ListDirectory(maindirfile, 0);
  }

  if ((patchnumber != 0) || (lumpnumber != 0))
  {
    if (MasterDir == NULL)
    {
      /* Build a dummy main WAD structure */
      WadFileList = (WadFileInfo*)safe_malloc(sizeof(WadFileInfo));
      WadFileList->next = NULL;
      WadFileList->filename = "?";
      WadFileList->fileinfo = NULL;
      memcpy(WadFileList->type, "PWAD",4);
      WadFileList->dirsize = 0;
      WadFileList->dirstart = 0;
      WadFileList->directory = (Directory*)safe_malloc(sizeof(Directory));
      WadFileList->directory->name[0] = 0;
      MasterDir = (MasterDirectory*)safe_malloc(sizeof(MasterDirectory));
      MasterDir->next = NULL;
      MasterDir->wadfile = WadFileList;
      MasterDir->dir.name[0] = 0;
    }
    for (i=0; i<patchnumber; i++)
    {
      OpenPatchWad(patchwads[i]);
    }
    for (i=0; i<lumpnumber; i++)
    {
      OpenLumpFile(lumpfiles[i].file, lumpfiles[i].name);
    }
    if ((listdir & 2) != 0) ListDirectory(patchdirfile, 1);
  }

  if (listmaps != 0)
  {
    ListWadMaps();
  }

  if (outwad != NULL)
  {
    BuildNewMainWad(outwad, (mainwad == NULL));
  }

  return 0;
}
