/*----------------------------------------------------------------------------*
 | This file is part of DEU (Doom Editing Utilities), created by the DEU team:|
 | Raphael Quinet, Brendon Wyber, Ted Vessenes and others.  See README.1ST or |
 | the "about" dialog box for full credits.                                   |
 |                                                                            |
 | DEU is an open project: if you think that you can contribute, please join  |
 | the DEU team.  You will be credited for any code (or ideas) included in    |
 | the next version of the program.                                           |
 |                                                                            |
 | If you want to make any modifications and re-distribute them on your own,  |
 | you must follow the conditions of the DEU license.  Read the file LICENSE  |
 | in this directory or README.1ST in the top directory.  If do not have a    |
 | copy of these files, you can request them from any member of the DEU team, |
 | or by mail: Raphael Quinet, Rue des Martyrs 9, B-4550 Nandrin (Belgium).   |
 |                                                                            |
 | This program comes with absolutely no warranty.  Use it at your own risks! |
 *----------------------------------------------------------------------------*

 D_MAIN.C - The main program...

*/

/*! The instructions for compiling DEU should be moved to another file and
    should explain how to compile with GCC, TC and other compilers, under
    DOS, Windows or UNIX.  A sample Imakefile/Makefile/project file for
    each system should be distributd with the sources.
*/
/*
   Compile with command
      tcc -Z -mh -r -G -O -f -edeu *.c \tc\lib\graphics.lib
   Be sure to use the Huge memory model when you compile...
*/

/* Note from R.Q. (Nov-Dec 1994):
      NEW USER INTERFACE FOR FUTURE VERSIONS OF DEU:
      The new user interface should be graphical and the commands that
      are currently available from the text-based interface should be
      put in menus and dialog boxes.  Here are some of my ideas about
      what the user interface should look like:
      - The main window contains a menu and a list of levels.  This list
        can be expanded to show all entries from the IWAD, from a PWAD,
        or from the PWAD(s) + IWAD.  Several options would be available
        from the menu in order to specify what kind of entries should be
        displayed (by default, only the list of levels) and from what
        files (by default, from all files that are loaded).
      - The user can select entries in the list or double-click on one
        of them.  Any object or group of objects can be copied to another
        WAD file.  Any single object can be copied to a raw binary file
        or to another type of file using some built-in filters (e.g. music
        entries converted to MIDI, sounds to WAV or VOC, graphics to GIF
        or PPM, etc.).
      - If the user double-clicks on one entry, this means that he wants
        to edit it.  The default editing mode for unknown objects is a
        simple hex editor (improvement over the current Dump command).
        For maps, the editor is of course the usual DEU map editor.  For
        sprites, floors and wall patches, there will be a (very) simple
        graphics editor.  There should also be a texture editor, which
        allows the user to choose and move patches, etc.  Editing sounds
        and music could be a nightmare, but not if DEU calls an external
        program for this (i.e. save entry in a temporary file, lauch program,
        wait for termination, get modified file).  This could also be done
        with graphics, using the built-in GIF filter.
      - One option in the main menu pops up a file chooser from which the
        user can select a new file to load.  If this file is a PWAD, all
        entries are loaded and a new window for this WAD is opened.  If this
        is another type of file (WAV, GIF, MIDI, etc.), the contents are
        filtered and stored in a new WAD file (e.g. NONAME00.WAD).  If the
        type of the file is unknown, the contents are not filtered but are
        also stored in a new WAD file.  The file chooser should provide a
        filter for *.WAD, *.GIF, and other types of files.
      - If several windows showing the contents of WAD files are open, it
        would be nice to be able to move or copy entries from one file to
        another (using drag and drop directly).
      Some users like to be able to call DEU from batch files in order to
      do complex tasks several times without having to type all commands
      twice.  This would not be possible with a graphics-only interface.
      But maybe we should create a kind of scripting language to overcome
      this.  Then we would have the best of both worlds.  But this should
      be put at the bottom of the list of things to do...
*/


/* the includes */
#include "deu.h"
#ifdef DEU_UNIX
#include <sys/types.h>
#include <sys/time.h>
#else
#include <time.h>
#endif
#include "d_misc.h"
#include "d_config.h"
#include "d_wads.h"
#include "g_mouse.h"  /* Hide/ShowMousePointer */
#include "g_gfx.h"    /* InitGfx, TermGfx */
#include "i_dialog.h" /* ViewPalette */
#include "i_textur.h" /* ChooseSprite */
#include "m_edit.h"   /* OLD_EditLevel */
#include "d_main.h"
#include "f_extins.h"

int DoomVersion = 0; /* which version of Doom or Heretic are we using? */

/* WARNING to owners of the SHAREWARE version of Doom or Heretic: Id Software
   has requested that no editor works with the shareware game.  DEU will not
   allow you to save your changes if you do not have the registered or
   commercial version of the game.  REGISTER your copy if you want to be able
   to use all features of DEU.  It's well worth it!  But never attempt to
   modify the DoomVersion variable and recompile DEU without the version
   checking.  Not only is this forbidden, but it could also have some very
   annoying side effects on your files.  You have been warned...
*/


/*
   Output the credits of the program to the specified file.
*/

void Credits(FILE *where)
{
  /*! The full credits will be in README.1ST or some other file, and the
      list of contributors should be displayed in an "About" box in the
      editor.  If you have any suggestions, send them to me (Raphael).
   */
  fprintf(where, "DEU: Doom Editing Utilities, ver %s.\n", DEU_VERSION);
/*!  fprintf(where, "DEU: Doom Editing Utilities, ver 5.3-SAMS\n"); */
  fprintf(where, "By the DEU Team: Raphael Quinet, Brendon Wyber, Ted Vessenes and others\n");
  fprintf(where, "(see the file README.1ST or the About box for full credits)\n");
  fprintf(where, "*** Any ideas about what should be printed here? - RQ ***\n");
}


/*
   Output the program usage to the specified file.
*/

void Usage(FILE *where)
{
  fprintf(where, "Usage: DEU [options...]\n");
  fprintf(where, "Some options are:\n");
  fprintf(where, "   -h        Print this help message (also -help)\n");
  fprintf(where, "   -w <wad>  Gives the name of the main wad file (also -main).\n");
  fprintf(where, "   -pw <wad> To add one patch wad file to be loaded; may be repeated.\n");
  fprintf(where, "   -file <wad...> To add a list of patch wad files to be loaded.\n");
  fprintf(where, "   -config <cfg> Gives the name of the config file.\n");
  fprintf(where, "   -d        Enter debug mode (also -debug).\n");
  fprintf(where, "   -q        Suppresses some sounds (also -quiet).\n");
  fprintf(where, "   -qq       Suppresses all sounds (also -quieter).\n");
  fprintf(where, "   -e        Stops prompts for confirmation (also -expert).\n");
  fprintf(where, "   -v        Set the default video mode number (also -video).\n");
#ifdef __TURBOC__
  fprintf(where, "   -bgi <dr> Set the default video driver (*.BGI file).\n");
  fprintf(where, "   -fw       Force color #15 to be white (also -forcewhite).\n");
  fprintf(where, "   -fc       Use a \"fake\" mouse cursor (also -fakecursor).\n");
#endif
  fprintf(where, "   -sb       Swaps the mouse buttons (also -swapbuttons).\n");
  fprintf(where, "   -i        Show the info bar in the editors (also -infobar).\n");
  fprintf(where, "   -z <zoom> Set the initial zoom factor for the editors (also -zoom).\n");
  fprintf(where, "   -edit <map> Start the editor directly on a given map name.\n");
  fprintf(where, "   -quit     Quit DEU after editing (if used with -edit).\n");
  fprintf(where, "Put a '+' instead of a '-' before boolean options to reverse their effect.\n");
  fprintf(where, "Other options are described in the documentation (DEU.TXT).  Try also the\n");
  fprintf(where, "\"HELP\" and \"SET\" commands at the DEU prompt.\n");
}


/*
   Display a funny message on the screen.
*/

void FunnyMessage(FILE *where)
{
/*
  fprintf(where, "\n");
  fprintf(where, "*----------------------------------------------------------------------------*\n");
  fprintf(where, "| Welcome to DEU!  This is a powerful utility and, like all good tools, it   |\n");
  fprintf(where, "| comes with its user's manual.  Please print and read DEU.TXT if you want   |\n");
  fprintf(where, "| to discover all the features of this program.  If you are new to DEU, the  |\n");
  fprintf(where, "| tutorial will show you how to build your first level.                      |\n");
  fprintf(where, "|                                                                            |\n");
  fprintf(where, "| Several things have changed since the last version.  If you don't want to  |\n");
  fprintf(where, "| miss an important new feature, read the revision history in README.1ST.    |\n");
  fprintf(where, "|                                                                            |\n");
  fprintf(where, "| DEU is an open project.  If you want to contribute or submit bug reports,  |\n");
  fprintf(where, "| please read README.1ST.  Contributions are always welcome.  If you have an |\n");
  fprintf(where, "| Internet e-mail address, you can use the DEU Help Service for more info,   |\n");
  fprintf(where, "| but please read the documentation first.  We are not paid for this...      |\n");
  fprintf(where, "| Hint: you can easily disable this message.  Read the docs carefully...     |\n");
  fprintf(where, "*----------------------------------------------------------------------------*\n");
*/
  fprintf(where, "*---------------------------------------------------------------------------*\n");
  fprintf(where, "| WARNING: this is a beta version of DEU 5.3.  Since we are still writing   |\n");
  fprintf(where, "| or modifying some parts of the program, we made no efforts to ensure that |\n");
  fprintf(where, "| this version is stable.  You should read README.1ST for a list of known   |\n");
  fprintf(where, "| bugs.  Expect some crashes from time to time...  Usually just before you  |\n");
  fprintf(where, "| save your level!  Do not use this beta version for any serious editing.   |\n");
  fprintf(where, "|                                                                           |\n");
  fprintf(where, "| Several new features have been added, but they are not documented yet.    |\n");
  fprintf(where, "| take a look at the source code if you want to know what's new.            |\n");
  fprintf(where, "*---------------------------------------------------------------------------*\n");
}



/*
   The main program menu loop.
*/

void MainLoop(void)
{
  char    input[120];
  char   *com, *out;
  FILE   *file, *raw;
  WadPtr  wad;

  /* start the editor directly if the "edit" option was used */
  if (Config.autoEdit != NULL)
    {
      OLD_EditLevel(Config.autoEdit, FALSE);
      /* quit the program if the "quit" option was used */
      if (Config.autoQuit)
        return;
    }

  /*! This mess should be cleaned!  Any volunteers? */

  for (;;)
    {
      /* get the input */
      printf("\n[? for help]> ");
      gets(input);
      printf("\n");
      strupr(input);

      /* eat the white space and get the first command word */
      com = strtok(input, " ");

      /* user just hit return */
      if (com == NULL)
        printf("[Please enter a command or ? for help.]\n");

      /* user inputting for help */
      else if (!strcmp(com, "?") || !strcmp(com, "HELP"))
        {
          printf("? (or HELP)                       -- to display this text\n");
          printf("B[uild] <WadFile>                 -- to build a new main WAD file\n");
          printf("C[reate] [DirEntry]               -- to create and edit a new directory entry\n");
          /*! Dumping an entry in hex should be a special case of "Edit" */
          printf("D[ump] <DirEntry> [outfile]       -- to dump a directory entry in hex\n");
          printf("E[dit] [DirEntry]                 -- to edit a directory entry (map, etc.)\n");
          printf("G[roup] <WadFile>                 -- to group all patch wads in a file\n");
          printf("I[nsert] <RawFile> <DirEntry>     -- to insert a raw file in a patch wad file\n");
          printf("L[ist] <WadFile> [outfile]        -- to list the directory of a wadfile\n");
          printf("M[aster] [outfile]                -- to list the master directory\n");
          /*! The palette editing functions should be moved under "Edit" */
          printf("P[alette] [Palette Number]        -- to view the color palettes\n");
          printf("Q[uit]                            -- to quit\n");
          printf("R[ead] <WadFile>                  -- to read a new wad patch file\n");
          printf("S[ave] <DirEntry> <WadFile>       -- to save one object to a separate file\n");
          printf("SET [Option] [Value]              -- to change or print the value of an option\n");
          /*! The sprite editing functions should be moved under "Edit" */
          printf("V[iew] [SpriteName]               -- to display the sprites\n");
          printf("W[ads]                            -- to display the open wads\n");
          printf("X[tract] <DirEntry> <RawFile>     -- to save (extract) one object to a raw file\n");
        }

      /* user asked for list of open WAD files */
      else if (!strcmp(com, "WADS") || !strcmp(com, "W"))
        {
          printf("%-20s  IWAD  (Main wad file)\n", WadFileList->filename);
          for (wad = WadFileList->next; wad; wad = wad->next)
            {
              if (wad->directory[0].name[0] == 'E' && wad->directory[0].name[2] == 'M' && wad->directory[0].name[4] == '\0')
                printf("%-20s  PWAD  (Patch wad file for episode %c level %c)\n", wad->filename, wad->directory[0].name[1], wad->directory[0].name[3]);
              else if (wad->directory[0].name[0] == 'M' && wad->directory[0].name[1] == 'A' && wad->directory[0].name[2] == 'P' && wad->directory[0].name[5] == '\0')
                printf("%-20s  PWAD  (Patch wad file for map %c%c)\n", wad->filename, wad->directory[0].name[3], wad->directory[0].name[4]);
              else
                {
                  /* kluge */
                  strncpy(input, wad->directory[0].name, 8);
                  input[8] = '\0';
                  printf("%-20s  PWAD  (Patch wad file for %s)\n", wad->filename, input);
                }
            }
        }

      /* user asked to quit */
      else if (!strcmp(com, "QUIT") || !strcmp(com, "Q"))
        {
          if (DoomVersion == 0)
            printf("Remember to register your copy of DOOM!\n");
          else if (DoomVersion == 16)
            printf("Remember to register your copy of HERETIC!\n");
          printf("Goodbye...\n");
          break;
        }

      /* user asked to edit a level */
      else if (!strcmp(com, "EDIT") || !strcmp(com, "E")
            || !strcmp(com, "CREATE") || !strcmp(com, "C"))
        {
          out = strtok(NULL, " ");
          OLD_EditLevel(out, !strcmp(com, "CREATE") || !strcmp(com, "C"));
        }

      /* user asked to build a new main WAD file */
      else if (!strcmp(com, "BUILD") || !strcmp(com, "B"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Wad file name argument missing.]\n");
              continue;
            }
          for (wad = WadFileList; wad; wad = wad->next)
            if (!stricmp(com, wad->filename))
              break;
          if (wad)
            {
              printf("[File \"%s\" is opened and cannot be overwritten.]\n", com);
              continue;
            }
          BuildNewMainWad(com, FALSE);
        }

      /* user asked to build a compound patch WAD file */
      else if (!strcmp(com, "GROUP") || !strcmp(com, "G"))
        {
          if (WadFileList->next == NULL || WadFileList->next->next == NULL)
            {
              printf("[You need at least two open wad files if you want to group them.]\n");
              continue;
            }
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Wad file name argument missing.]\n");
              continue;
            }
          for (wad = WadFileList; wad; wad = wad->next)
            if (!stricmp(com, wad->filename))
              break;
          if (wad)
            {
              printf("[File \"%s\" is opened and cannot be overwritten.]\n", com);
              continue;
            }
          BuildNewMainWad(com, TRUE);
        }

      /* user ask for a listing of a WAD file */
      else if (!strcmp(com, "LIST") || !strcmp(com, "L"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Wad file name argument missing.]\n");
              continue;
            }
          for (wad = WadFileList; wad; wad = wad->next)
            if (!stricmp(com, wad->filename))
              break;
          if (wad == NULL)
            {
              printf("[Wad file \"%s\" is not open.]\n", com);
              continue;
            }
          out = strtok(NULL, " ");
          if (out)
            {
              printf("Outputting directory of \"%s\" to \"%s\".\n", wad->filename, out);
              if ((file = fopen(out, "wt")) == NULL)
                ProgError("error opening output file \"%s\"", com);
              Credits(file);
              ListFileDirectory(file, wad);
              fprintf(file, "\nEnd of file.\n");
              fclose(file);
            }
          else
            ListFileDirectory(stdout, wad);
        }

      /* user asked for the list of the master directory */
      else if (!strcmp(com, "MASTER") || !strcmp(com, "M"))
        {
          out = strtok(NULL, " ");
          if (out)
            {
              printf("Outputting master directory to \"%s\".\n", out);
              if ((file = fopen(out, "wt")) == NULL)
                ProgError("error opening output file \"%s\"", com);
              Credits(file);
              ListMasterDirectory(file);
              fprintf(file, "\nEnd of file.\n");
              fclose(file);
            }
          else
            ListMasterDirectory(stdout);
        }

      /* user asked to read a new patch WAD file */
      else if (!strcmp(com, "READ") || !strcmp(com, "R"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Wad file name argument missing.]\n");
              continue;
            }
          out = strtok(NULL, " ");
          if (out)
            *out = '\0';
          out = (char*)GetMemory((strlen(com) + 1) * sizeof(char));
          strcpy(out, com);
          OpenPatchWad(out);
          CloseUnusedWadFiles();
        }

      /*! Dumping an entry in hex should be a special case of "Edit" */
      /* user asked to dump the contents of a WAD file */
      else if (!strcmp(com, "DUMP") || !strcmp(com, "D"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Object name argument missing.]\n");
              continue;
            }
          out = strtok(NULL, " ");
          if (out)
            {
              printf("Outputting directory entry data to \"%s\".\n", out);
              if ((file = fopen(out, "wt")) == NULL)
                ProgError("error opening output file \"%s\"", com);
              Credits(file);
              DumpDirectoryEntry(file, com);
              fprintf(file, "\nEnd of file.\n");
              fclose(file);
            }
          else
            DumpDirectoryEntry(stdout, com);
        }

      /*! The sprite viewer should be a special case of "Edit" */
      /* user asked to view the sprites */
      else if (!strcmp(com, "VIEW") || !strcmp(com, "V"))
        {
          com = strtok(NULL, " ");
          InitGfx();
          if (UseMouse)
            HideMousePointer();
          ChooseSprite(-1, -1, "Sprite viewer", com);
          if (UseMouse)
            ShowMousePointer();
          TermGfx();
        }

      /*! The palette viewer should be a special case of "Edit" */
      /* user asked to view the palette */
      else if (!strcmp(com, "PALETTE") || !strcmp(com, "P"))
        {
          InitGfx();
          com = strtok(NULL, " ");
          ViewPalette(com == NULL ? 0 : atoi(com));
          TermGfx();
        }

      /* user asked to save an object to a separate PWAD file */
      else if (!strcmp(com, "SAVE") || !strcmp(com, "S"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Object name argument missing.]\n");
              continue;
            }
          if (strlen(com) > 8 || strchr(com, '.') != NULL)
            {
              printf("[Invalid object name.]\n");
              continue;
            }
          out = strtok(NULL, " ");
          if (out == NULL)
            {
              printf("[Wad file name argument missing.]\n");
              continue;
            }
          for (wad = WadFileList; wad; wad = wad->next)
            if (!stricmp(out, wad->filename))
              break;
          if (wad)
            {
              printf("[This Wad file is already in use.  You may not overwrite it.]\n");
              continue;
            }
          printf("Saving directory entry data to \"%s\".\n", out);
          if ((file = fopen(out, "wb")) == NULL)
            ProgError("error opening output file \"%s\"", out);
          SaveDirectoryEntry(file, com);
          fclose(file);
        }

      /* user asked to encapsulate a raw file in a PWAD file */
      else if (!strcmp(com, "INSERT") || !strcmp(com, "I"))
        {
          char filename[15];
          char entryname[10];

          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Raw file name argument missing.]\n");
              continue;
            }

          out = strtok(NULL, " ");
          if (out == NULL)
            {
              printf("[Object name argument missing.]\n");
              continue;
            }
          if (strlen(out) > 8 || strchr(out, '.') != NULL)
            {
              printf("[Invalid object name.]\n");
              continue;
            }
          if ((raw = fopen(com, "rb")) == NULL)
            {
              printf("[Error opening input file \"%s\".]\n", com);
              continue;
            }
          strcpy(filename, com);
          strcpy(entryname, out);
          /* kluge */
          strcpy(input, out);
          strcat(input, ".WAD");
          for (wad = WadFileList; wad; wad = wad->next)
            if (!stricmp(input, wad->filename))
              break;
          if (wad)
            {
              printf("[This Wad file is already in use (%s).  You may not overwrite it.]\n", input);
              continue;
            }
          printf("Including new object %s in \"%s\".\n", filename, input);
          if ((file = fopen(input, "wb")) == NULL)
            {
              printf("[Error opening output file \"%s\".]\n", input);
              fclose(raw);
              continue;
            }
          fclose(raw);
          fclose(file);
          /*

            I(SERT) SKY2.GIF SKY2

            filename  - <SKY2.GIF>    take rawfile and
            input     - SKY2.WAD      insert to wad file
            entryname - SKY2          directory entry


          */
          InsertResource( filename, input, entryname );
        }

      /* user asked to extract an object to a raw binary file */
      else if (!strcmp(com, "XTRACT") || !strcmp(com, "EXTRACT") || !strcmp(com, "X"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              printf("[Object name argument missing.]\n");
              continue;
            }
          if (strlen(com) > 8 || strchr(com, '.') != NULL)
            {
              printf("[Invalid object name.]\n");
              continue;
            }
          out = strtok(NULL, " ");
          if (out == NULL)
            {
              printf("[Raw file name argument missing.]\n");
              continue;
            }
          for (wad = WadFileList; wad; wad = wad->next)
            if (!stricmp(out, wad->filename))
              break;
          if (wad)
            {
              printf("[You may not overwrite an opened Wad file with raw data.]\n");
              continue;
            }
          printf("Saving directory entry data \"%s\" to \"%s\".\n", com, out);
          if ((file = fopen(out, "wb")) == NULL)
            {
              printf("[Error opening output file \"%s\".]\n", out);
            }
          fclose(file);
          /*

          E(XTRACT) D_E2M1 D_E2M1.MUS

          COM  -  D_E2M1        extract Directory entry from current wad and
          OUT  -  D_E2M1.MUS    save to rawfile

          */

          ExtractResource(com, out);
        }


      /* user asked to change (temporarily) a config option */
      else if (!strcmp(com, "SET"))
        {
          com = strtok(NULL, " ");
          if (com == NULL)
            {
              PrintOptionValues(stdout);
              continue;
            }
          out = strtok(NULL, " ");
          if (out == NULL)
            {
              out = GetOptionValue(com);
              if (out != NULL)
                printf("Current value: %s = %s\n", com, out);
              continue;
            }
          ChangeOption(com, out);
          LogMessage("Changing option \"%s\" to \"%s\"\n", com, out);
        }

      /* unknown command */
      else
        printf("[Unknown command \"%s\"!]\n", com);
    }
}



/*
   the main program
*/

int main(int argc, char *argv[])
{
  Credits(stdout);
  RANDOMIZE();
  argv++;
  argc--;
  /* read config file and command line options */
  InitDefaultOptions(argc, argv);
  ParseConfigFileOptions(Config.cfgFile);
  ParseCommandLineOptions(argc, argv);
  if (Config.quieter == TRUE)
    Config.quiet = TRUE;
  if (Config.reminder == TRUE)
    FunnyMessage(stdout);
  OpenLogFile(DEU_LOG_FILE);
  /* load the wad files */
  OpenMainWad(Config.mainWad);
  if (Config.patchWads != NULL)
    while (Config.patchWads[0] != NULL)
      {
        OpenPatchWad(Config.patchWads[0]);
        Config.patchWads++;
      }
  /* sanity check */
  CloseUnusedWadFiles();
  /* all systems go! */
  MainLoop();
  /* that's all, folks! */
  CloseWadFiles();
  CloseLogFile();
  return 0;
}


/* Does anybody want to port DEU to VMS + DECwindows?  It could be fun... */

/* end of file */
