/*----------------------------------------------------------------------------*
 | 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! |
 *----------------------------------------------------------------------------*

 W_LEVELS.C - Level loading and saving

*/

/* the includes */
#include "deu.h"
#include "d_main.h"
#include "d_misc.h"
#include "d_wads.h"
#include "w_levels.h"

/*! some of the b_*.h files are probably not needed */
#include "b_bsp.h"
#include "b_ui.h"
#include "b_reject.h"
#include "b_block.h"

/*! if SaveLevelData is moved to a M_*.C file, the next 2 lines should be deleted */
#include "g_gfx.h"
#include "g_mouse.h"
#include "i_menus.h"


/*! temp. global vars */
UInt16  num_wtexture;    /* number of wall textures */
char  **wtexture;        /* array of wall texture names */
UInt16  num_ftexture;    /* number of floor/ceiling textures */
char  **ftexture;        /* array of texture names */


/*! Note:
  What happens if there is no reference file (new level from scratch) and
  the user saves without rebuilding?
  Problem with references to dir entries and WAD file.
  Problem with "unused" vertices.
  Find a solution...
*/


/*
   Allocate and initialize the structure for a new level.
*/

LevelPtr CreateNewLevel(char *mapname)
{
  MDirPtr  ref_dir;
  LevelPtr newlevel;

  if (mapname != NULL)
    {
      ref_dir = FindMasterDir(MasterDir, mapname);
      /* if mapname was not NULL, then a valid level was expected */
      if (ref_dir == NULL)
        return NULL;
    }
  else
    ref_dir = NULL;

  newlevel = (LevelPtr) GetMemory(sizeof(struct LevelInfo));
  newlevel->dir = ref_dir;

  newlevel->num_things = 0;
  newlevel->things = NULL;
  newlevel->num_linedefs = 0;
  newlevel->linedefs = NULL;
  newlevel->num_sidedefs = 0;
  newlevel->sidedefs = NULL;
  newlevel->num_vertexes = 0;
  newlevel->vertexes = NULL;
  newlevel->num_sectors = 0;
  newlevel->sectors = NULL;

  newlevel->map_maxX = 4000;
  newlevel->map_maxY = 4000;
  newlevel->map_minX = -4000;
  newlevel->map_minY = -4000;

  newlevel->made_changes = FALSE;
  newlevel->made_map_changes = FALSE;
  newlevel->undo_operation = NULL;
  newlevel->undo_buffer = NULL;

  return newlevel;
}


/*
   Forget the level data.
*/

void ForgetLevelData(LevelPtr level)
{
  if (level->things != NULL)
    FreeFarMemory(level->things);
  if (level->vertexes != NULL)
    FreeFarMemory(level->vertexes);
  if (level->linedefs != NULL)
    FreeFarMemory(level->linedefs);
  if (level->sidedefs != NULL)
    FreeFarMemory(level->sidedefs);
  if (level->sectors != NULL)
    FreeFarMemory(level->sectors);
  if (level->undo_buffer != NULL)
    ForgetLevelData(level->undo_buffer);
  FreeMemory(level);
}


/*
   Get the map name (ExMy or MAPnn) for one level.
   Level->dir should never be accessed directly from other
   functions, since this structure is likely to change.
*/

char *GetLevelMapName(LevelPtr level)
{
  if (level != NULL && level->dir != NULL)
    return level->dir->dir.name;
  else
    return NULL;
}


/*
   Get the name of the file which contains the level data, if any.
   Level->dir should never be accessed directly from other
   functions, since this structure is likely to change.
*/

char *GetLevelFileName(LevelPtr level)
{
  if (level != NULL && level->dir != NULL && level->dir->wadfile != NULL)
    return level->dir->wadfile->filename;
  else
    return NULL;
}


/*
   Append data for one level to a structure already in memory.
*/
/*! The prototype of this function will have to be changed when MDirPtr is
    removed.  A direct pointer to a WAD file will be given instead. */
static void AppendLevelData(LevelPtr level, MDirPtr ref_dir)
{
  MDirPtr dir;
  Int16   n;
  UInt16  num_inc;
  UInt16  tag_inc;
  UInt16  old_num_vertexes;
  UInt16 *vertex_used;

  /* Get the number of vertices.  This will be used later to remove the     */
  /* extra vertices that are not used by any LineDef (added for the Segs). */
  dir = FindMasterDir(ref_dir, "VERTEXES");
  if (dir != NULL)
    old_num_vertexes = (UInt16) (dir->dir.size / 4L);
  else
    old_num_vertexes = 0;
  if (old_num_vertexes > 0)
    {
      vertex_used = (UInt16 *) GetMemory(old_num_vertexes * sizeof(UInt16));
      for (n = 0; n < old_num_vertexes; n++)
        vertex_used[n] = 0;
    }
  else
    vertex_used = NULL;

  /* find the highest tag number in the existing level */
  tag_inc = 0;
  for (n = 0; n < level->num_linedefs; n++)
    if (level->linedefs[n].tag > tag_inc)
      tag_inc = level->linedefs[n].tag;
  for (n = 0; n < level->num_sectors; n++)
    if (level->sectors[n].tag > tag_inc)
      tag_inc = level->sectors[n].tag;

  /* read in the Things data */
  dir = FindMasterDir(ref_dir, "THINGS");
  if (dir != NULL)
    {
      TPtr cur;

      num_inc = (UInt16) (dir->dir.size / 10L);
      if (level->num_things > 0)
        {
          level->things = (TPtr) ResizeFarMemory(level->things,
                                                  (UInt32) (level->num_things + num_inc) * sizeof(struct Thing));
          cur = level->things + level->num_things;
          level->num_things += num_inc;
        }
      else
        {
          level->things = (TPtr) GetFarMemory((UInt32) num_inc * sizeof(struct Thing));
          cur = level->things;
          level->num_things = num_inc;
        }
      BasicWadSeek(dir->wadfile, dir->dir.start);
      for (n = 0; n < num_inc; n++)
        {
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->xpos));
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->ypos));
          WadReadInt16(dir->wadfile, &(cur->angle));
          WadReadInt16(dir->wadfile, &(cur->type));
          WadReadInt16(dir->wadfile, &(cur->when));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
    }

  /* read in the LineDef information */
  dir = FindMasterDir(ref_dir, "LINEDEFS");
  if (dir != NULL)
    {
      LDPtr cur;

      num_inc = (UInt16) (dir->dir.size / 14L);
      if (level->num_linedefs > 0)
        {
          level->linedefs = (LDPtr) ResizeFarMemory(level->linedefs,
                                                    (UInt32) (level->num_linedefs + num_inc) * sizeof(struct LineDef));
          cur = level->linedefs + level->num_linedefs;
          level->num_linedefs += num_inc;
        }
      else
        {
          level->linedefs = (LDPtr) GetFarMemory((UInt32) num_inc * sizeof(struct LineDef));
          cur = level->linedefs;
          level->num_linedefs = num_inc;
        }
      BasicWadSeek(dir->wadfile, dir->dir.start);
      for (n = 0; n < num_inc; n++)
        {
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->start));
          vertex_used[cur->start] = 1;
          cur->start += level->num_vertexes;
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->end));
          vertex_used[cur->end] = 1;
          cur->end += level->num_vertexes;
          WadReadInt16(dir->wadfile, &(cur->flags));
          WadReadInt16(dir->wadfile, &(cur->type));
          WadReadInt16(dir->wadfile, &(cur->tag));
          if (cur->tag > 0)
            cur->tag += tag_inc;
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->sidedef1));
          cur->sidedef1 += level->num_sidedefs;
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->sidedef2));
          cur->sidedef2 += level->num_sidedefs;
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
    }

  /* read in the SideDef information */
  dir = FindMasterDir(ref_dir, "SIDEDEFS");
  if (dir != NULL)
    {
      SDPtr cur;

      num_inc = (UInt16) (dir->dir.size / 30L);
      if (level->num_sidedefs > 0)
        {
          level->sidedefs = (SDPtr) ResizeFarMemory(level->sidedefs,
                                                    (UInt32) (level->num_sidedefs + num_inc) * sizeof(struct SideDef));
          cur = level->sidedefs + level->num_sidedefs;
          level->num_sidedefs += num_inc;
        }
      else
        {
          level->sidedefs = (SDPtr) GetFarMemory((UInt32) num_inc * sizeof(struct SideDef));
          cur = level->sidedefs;
          level->num_sidedefs = num_inc;
        }
      BasicWadSeek(dir->wadfile, dir->dir.start);
      for (n = 0; n < num_inc; n++)
        {
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->xoff));
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->yoff));
          BasicWadRead(dir->wadfile, &(cur->tex1), 8);
          BasicWadRead(dir->wadfile, &(cur->tex2), 8);
          BasicWadRead(dir->wadfile, &(cur->tex3), 8);
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->sector));
          cur->sector += level->num_sectors;
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
    }

  /* read in the Vertices which are all the corners of the level, but ignore the */
  /* Vertices not used in any LineDef (they usually are at the end of the list). */
  num_inc = 0;
  for (n = 0; n < old_num_vertexes; n++)
    if (vertex_used[n])
      num_inc++;
  if (num_inc > 0)
    {
      VPtr  cur;
      Int16 xval, yval;

      if (level->num_vertexes > 0)
        {
          level->vertexes = (VPtr) ResizeFarMemory(level->vertexes,
                                                    (UInt32) (level->num_vertexes + num_inc) * sizeof(struct Vertex));
          cur = level->vertexes + level->num_vertexes;
          level->num_vertexes += num_inc;
        }
      else
        {
          level->vertexes = (VPtr) GetFarMemory((UInt32) num_inc * sizeof(struct Vertex));
          cur = level->vertexes;
          level->num_vertexes = num_inc;
        }
      dir = FindMasterDir(ref_dir, "VERTEXES");
      BasicWadSeek(dir->wadfile, dir->dir.start);
      for (n = 0; n < num_inc; n++)
        {
          WadReadInt16(dir->wadfile, (UInt16 *)&xval);
          WadReadInt16(dir->wadfile, (UInt16 *)&yval);
          if (vertex_used[n] > 0)
            {
              if (xval < level->map_minX)
                level->map_minX = xval;
              if (xval > level->map_maxX)
                level->map_maxX = xval;
              if (yval < level->map_minY)
                level->map_minY = yval;
              if (yval > level->map_maxY)
                level->map_maxY = yval;
              cur->x = xval;
              cur->y = yval;
#ifdef BUGGY_TURBOC_3
              cur = cur + 1;
#else
              cur++;
#endif
            }
        }
    }

  if (old_num_vertexes > 0)
  {
    LDPtr cur;

    /* update the Vertex numbers in the LineDefs (not really necessary, but...) */
    num_inc = 0;
    for (n = 0; n < old_num_vertexes; n++)
      if (vertex_used[n])
        vertex_used[n] = num_inc++;
    num_inc = level->num_vertexes - num_inc;
    cur = level->linedefs;
    for (n = 0; n < level->num_linedefs; n++)
      {
        if (cur->start >= num_inc)
          cur->start = vertex_used[cur->start - num_inc] + num_inc;
        if (cur->end >= num_inc)
          cur->end = vertex_used[cur->end - num_inc] + num_inc;
#ifdef BUGGY_TURBOC_3
        cur = cur + 1;
#else
        cur++;
#endif
      }
    FreeMemory(vertex_used);
  }

  /* ignore the Segs, SSectors and Nodes */

  /* read in the Sectors information */
  dir = FindMasterDir(ref_dir, "SECTORS");
  if (dir != NULL)
    {
      SPtr cur;

      num_inc = (UInt16) (dir->dir.size / 26L);
      if (level->num_sectors > 0)
        {
          level->sectors = (SPtr) ResizeFarMemory(level->sectors,
                                                    (UInt32) (level->num_sectors + num_inc) * sizeof(struct Sector));
          cur = level->sectors + level->num_sectors;
          level->num_sectors += num_inc;
        }
      else
        {
          level->sectors = (SPtr) GetFarMemory((UInt32) num_inc * sizeof(struct Sector));
          cur = level->sectors;
          level->num_sectors = num_inc;
        }
      BasicWadSeek(dir->wadfile, dir->dir.start);
      for (n = 0; n < num_inc; n++)
        {
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->floorh));
          WadReadInt16(dir->wadfile, (UInt16 *)&(cur->ceilh));
          BasicWadRead(dir->wadfile, &(cur->floort), 8);
          BasicWadRead(dir->wadfile, &(cur->ceilt), 8);
          WadReadInt16(dir->wadfile, &(cur->light));
          WadReadInt16(dir->wadfile, &(cur->type));
          WadReadInt16(dir->wadfile, &(cur->tag));
          cur->tag += tag_inc;
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
    }

  /* ignore the last entries (Reject & BlockMap) */
}


/*
   Load a level from a WAD file (return NULL if not found).
*/

/*! This function should open the WAD file, load the data, then close it. */
LevelPtr LoadLevelData(char *mapname, char *wadfile)
{
  MDirPtr  ref_dir;
  LevelPtr newlevel;

  /*! Is this message necessary ?  Do not mix user interface and file I/O. */
  DisplayMessage(-1, -1, "Reading data for level %s...", mapname);

  newlevel = CreateNewLevel(mapname);

  /*! Currently, the WAD file name is ignored.  When the main directory is
      removed, the file name will be used to load data from this file.
      Then it will be possible, for example, to edit the same level (ExMy or
      MAPnn) from two different files.  This is not possible now.  */
  ref_dir = FindMasterDir(MasterDir, mapname);
  if (ref_dir == NULL)
    return NULL;

  /* make sure that the outer bounds will be updated */
  newlevel->map_maxX = -32767;
  newlevel->map_maxY = -32767;
  newlevel->map_minX = 32767;
  newlevel->map_minY = 32767;
  /* load the level data */
  AppendLevelData(newlevel, ref_dir);
  return newlevel;
}


/*
   Load a level from a WAD file and merge the data with the current level.
*/

/*! This function should open the WAD file, load the data, then close it. */
Bool MergeLevelData(LevelPtr level, char *mapname, char *wadfile)
{
  MDirPtr  ref_dir;
  UInt16   old_num_vertexes;

  /*! Currently, the WAD file name is ignored.  When the main directory is
      removed, the file name will be used to load data from this file.
      Then it will be possible, for example, to edit the same level (ExMy or
      MAPnn) from two different files.  This is not possible now.  */
  ref_dir = FindMasterDir(MasterDir, mapname);
  if (ref_dir == NULL)
    return FALSE;

  old_num_vertexes = level->num_vertexes;
  AppendLevelData(level, ref_dir);
  level->made_changes = TRUE;
  if (level->num_vertexes != old_num_vertexes)
    level->made_map_changes = TRUE;
  return TRUE;
}



/*
   Save the minimum level data to a temporary PWAD file (autosave, etc.).
*/

void QuickSaveLevelData(LevelPtr level, char *tmpfile)
{
  FILE   *file;
  UInt16  n;
  UInt32  dirstart;
  UInt32  size, counter;
  char    mapname[8];

  LogMessage(": Saving temporary data to \"%s\"...\n", tmpfile);
  if ((file = fopen(tmpfile, "wb")) == NULL)
    ProgError("Unable to open file \"%s\"", tmpfile);

  WriteBytes(file, "PWAD", 4L);     /* PWAD file marker */
  counter = 11;
  WriteInt32(file, &counter);       /* 11 entries */
  WriteInt32(file, &counter);       /* fix this up later */
  counter = 12L;

  /* output the things data */
  if (level->num_things > 0)
    {
      TPtr cur;

      cur = level->things;
      for (n = 0; n < level->num_things; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->xpos));
          WriteInt16(file, (UInt16 *)&(cur->ypos));
          WriteInt16(file, &(cur->angle));
          WriteInt16(file, &(cur->type));
          WriteInt16(file, &(cur->when));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 10L;
        }
    }

  /* output the LineDefs */
  if (level->num_linedefs > 0)
    {
      LDPtr cur;

      cur = level->linedefs;
      for (n = 0; n < level->num_linedefs; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->start));
          WriteInt16(file, (UInt16 *)&(cur->end));
          WriteInt16(file, &(cur->flags));
          WriteInt16(file, &(cur->type));
          WriteInt16(file, &(cur->tag));
          WriteInt16(file, (UInt16 *)&(cur->sidedef1));
          WriteInt16(file, (UInt16 *)&(cur->sidedef2));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 14L;
        }
    }

  /* output the SideDefs */
  if (level->num_sidedefs > 0)
    {
      SDPtr cur;

      cur = level->sidedefs;
      for (n = 0; n < level->num_sidedefs; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->xoff));
          WriteInt16(file, (UInt16 *)&(cur->yoff));
          WriteBytes(file, &(cur->tex1), 8L);
          WriteBytes(file, &(cur->tex2), 8L);
          WriteBytes(file, &(cur->tex3), 8L);
          WriteInt16(file, (UInt16 *)&(cur->sector));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 30L;
        }
    }

  /* output the Vertices */
  if (level->num_vertexes > 0)
    {
      VPtr cur;

      cur = level->vertexes;
      for (n = 0; n < level->num_vertexes; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->x));
          WriteInt16(file, (UInt16 *)&(cur->y));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 4L;
        }
    }

  /* output the Sectors */
  if (level->num_sectors > 0)
    {
      SPtr cur;

      cur = level->sectors;
      for (n = 0; n < level->num_sectors; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->floorh));
          WriteInt16(file, (UInt16 *)&(cur->ceilh));
          WriteBytes(file, &(cur->floort), 8L);
          WriteBytes(file, &(cur->ceilt), 8L);
          WriteInt16(file, &(cur->light));
          WriteInt16(file, &(cur->type));
          WriteInt16(file, &(cur->tag));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 26L;
        }
    }

  /* output the actual directory */
  dirstart = counter;
  counter = 12L;
  size = 0L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  if (GetLevelMapName(level) == NULL)
    strcpy(mapname, "E1M1\0\0\0\0");
  else
    strncpy(mapname, GetLevelMapName(level), 8);
  WriteBytes(file, &mapname, 8L);

  size = (UInt32) level->num_things * 10L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "THINGS\0\0", 8L);
  counter += size;

  size = (UInt32) level->num_linedefs * 14L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "LINEDEFS", 8L);
  counter += size;

  size = (UInt32) level->num_sidedefs * 30L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SIDEDEFS", 8L);
  counter += size;

  size = (UInt32) level->num_vertexes * 4L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "VERTEXES", 8L);
  counter += size;

  /* no data for the Segs, SSectors and Nodes */
  size = 0L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SEGS\0\0\0\0", 8L);
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SSECTORS", 8L);
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "NODES\0\0\0", 8L);

  size = (UInt32) level->num_sectors * 26L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SECTORS\0", 8L);
  counter += size;

  /* no data for Reject and Blockmap */
  size = 0L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "REJECT\0\0", 8L);
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "BLOCKMAP", 8L);

  /* fix up the directory start information */
  if (fseek(file, 8L, SEEK_SET) != 0)
    ProgError("error writing to file");
  WriteInt32(file, &dirstart);

  /* close the file */
  fclose(file);
}


/*
   recursively save the Nodes data to a PWAD file
   RP: This function was taken from WinDEU 5.24 (DEU 5.21 code slightly
       modified)
*/

void SaveNodes(FILE *file, NPtr node, UInt16 *pnumnodes)
{
  if (node == NULL)
    return;

  /* Nodes tree walk: save child1, save child2, save parent */
  if ((node->child1 & 0x8000) == 0)
    {
      SaveNodes(file, node->node1, pnumnodes);
      node->child1 = node->node1->num;
    }
  if ((node->child2 & 0x8000) == 0)
    {
      SaveNodes(file, node->node2, pnumnodes);
      node->child2 = node->node2->num;
    }
  WriteInt16(file, &(node->x));
  WriteInt16(file, &(node->y));
  WriteInt16(file, &(node->dx));
  WriteInt16(file, &(node->dy));
  WriteInt16(file, &(node->maxy1));
  WriteInt16(file, &(node->miny1));
  WriteInt16(file, &(node->minx1));
  WriteInt16(file, &(node->maxx1));
  WriteInt16(file, &(node->maxy2));
  WriteInt16(file, &(node->miny2));
  WriteInt16(file, &(node->minx2));
  WriteInt16(file, &(node->maxx2));
  WriteInt16(file, &(node->child1));
  WriteInt16(file, &(node->child2));
  node->num = (*pnumnodes)++;
}


/*
   forget the Nodes
   RP: This function was taken from WinDEU 5.24 (DEU 5.21 code slightly
       modified)
*/

void ForgetNodes(NPtr node)
{
  if (node != NULL)
    {
      if ((node->child1 & 0x8000) == 0)
        ForgetNodes(node->node1);
      if ((node->child2 & 0x8000) == 0)
        ForgetNodes(node->node2);
      FreeMemory (node);
    }
}


/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  ! SaveLevelData will not work until the Nodes builder is fully working.   !
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/

/*
   Save the level data to a PWAD file.
*/

void SaveLevelData(LevelPtr level, char *origfile, char *outfile)
{
  FILE   *file;
  MDirPtr dir;
  UInt16  n;
  Int16   saved_num_vertexes;
  UInt32  dirstart;
  UInt16  NumSegs;      /* number of segments */
  SEPtr   Segs;         /* list of segments */
  UInt16  NumSSectors;  /* number of subsectors */
  SSPtr   SSectors;     /* list of subsectors */
  UInt16  NumNodes;     /* number of nodes */
  NPtr    Nodes;        /* nodes tree */
  Int16  *blockptr, *rejectptr;
  UInt32  size, counter;
  UInt32  blocksize, rejectsize;
  Bool    newnodes;
  int     rebuild = 0; /*! change this */

  /*! The argument "origfile" is not used yet.  It should contain the name
      of the original file from which this level was edited.  If this file
      is not the main wad file, this routine should copy in the new file all
      entries from the old file that were not edited: other maps, graphics,
      sounds, music, etc.
      Maybe a WadFileInfo structure (WadPtr) should be given, instead of
      only the file name.
  */
  /* Note from R.Q.:
      In a future version (or if there are volunteers for this version...),
      this function should be re-written (and maybe moved to a M_*.C file).

      Instead of saving everything to a single file, it should do this:
      - Call the QuickSaveLevelData function to save the level data in a
        temporary file.  The QuickSave function should itself be changed
        to return a WadPtr structure with the new directory pointers.
      - Call the Nodes builder which should save the Nodes, Reject and
        BlockMap in one or several new temporary files.  If these structures
        should not be rebuilt, then no new file is created.
      - Rename the original WAD file (if any) with a .BAK extension.
      - Call the BuildCompoundWad function that should be added to D_WADS.C.
        This function takes as input a list of WadPtr structures and builds
        a new WAD by copying the data from the various WAD files given in the
        list.  See the comment in D_WADS.C for more info.
        The list of WADs given to this function will contain all temporary
        files and also the original file, if any.
      - Delete the temporary files.

      Using this method, it will be possible to keep all entries from the
      old WAD file if it contains other maps, sounds, etc.  It will also be
      safer, because each temporary file is a WAD that can be reloaded if
      something goes wrong when building the Nodes or other structures.

      For a new level created from scratch, there will be no "original" file:
      if the user does not rebuild the Nodes, the empty entries created by
      the QuickSave function will be copied to the new file.

      Note: the WadPtr for the IWAD is never given in the list for
      BuildCompoundWad, or else the whole file would be copied.  If some
      entries from the IWAD should be copied (e.g. Nodes), then a temporary
      WadPtr structure is allocated, with only the pointers to the required
      entries in the IWAD.  This WadPtr will be freed after the call to
      BuildCompoundWad.
  */

  if (UseMouse)
    HideMousePointer();

  /*! Is this message necessary ?  Do not mix user interface and file I/O. */
  DisplayMessage(-1,-1,"Saving data to \"%s\"...", outfile);

  LogMessage(": Saving data to \"%s\"...\n", outfile);
  if ((file = fopen(outfile, "wb")) == NULL)
    ProgError("Unable to open file \"%s\"", outfile);

  WriteBytes(file, "PWAD", 4L);     /* PWAD file marker */
  counter = 11L;
  WriteInt32(file, &counter);       /* 11 entries */
  WriteInt32(file, &counter);       /* fix this up later */
  counter = 12L;
  dir = level->dir->next;

  /* output the things data */
  if (level->num_things > 0)
    {
      TPtr cur;

      cur = level->things;
      for (n = 0; n < level->num_things; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->xpos));
          WriteInt16(file, (UInt16 *)&(cur->ypos));
          WriteInt16(file, &(cur->angle));
          WriteInt16(file, &(cur->type));
          WriteInt16(file, &(cur->when));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 10L;
        }
    }
  dir = dir->next;

  /* update level->map_minX, map_minY, map_maxX and map_maxY */
  level->map_maxX = -32767;
  level->map_maxY = -32767;
  level->map_minX = 32767;
  level->map_minY = 32767;
  for (n = 0; n < level->num_vertexes; n++)
    {
      if (level->vertexes[n].x < level->map_minX)
        level->map_minX = level->vertexes[n].x;
      if (level->vertexes[n].x > level->map_maxX)
        level->map_maxX = level->vertexes[n].x;
      if (level->vertexes[n].y < level->map_minY)
        level->map_minY = level->vertexes[n].y;
      if (level->vertexes[n].y > level->map_maxY)
        level->map_maxY = level->vertexes[n].y;
    }

  /* save the number of vertices */
  saved_num_vertexes = level->num_vertexes;

  /*! change this... */
  if (level->made_map_changes)
    rebuild = HandleBuilderMenu(outfile);
  else
    rebuild = 0;

  /*! This should be done AFTER everything else has been saved!  Just in case...*/
  if (rebuild & 1)
    {                         /* build the BSP */
      if (UseMouse)
        HideMousePointer();
      DrawScreenBox3D(218, 0, ScrMaxX, 55);
      SetColor(WHITE);
      DrawScreenText(225, 10, "Rebuilding the NODES...");
      DrawScreenBoxHollow(225, 28, ScrMaxX - 10, 48);
      DrawScreenMeter(225, 28, ScrMaxX - 10, 48, 0.0);
      if (UseMouse)
        ShowMousePointer();
      /*! RP: 1. Should pass SSectors, NumSSEctors, Segs and NumSeg
                 to the BSP node builder. I think they are only needed
                 there (since they aren't in the level struct)
                 Is it TRUE?
              2. 'n' is not used in the first call to CreateNodes. It's now
                 internal to the B_BSP.C functions.
                 We can remove it from here.
                 Is it TRUE?
        Old call format:
          if (CreateNodes(&(Nodes), &n, &seglist) == FALSE)
      */
      if (CreateNodes (level,
                         &Nodes,
                         &Segs,
                         &NumSegs,
                         &SSectors,
                         &NumSSectors) == FALSE )
        {
          Beep();
          Beep();
          Beep();
          LogMessage("\nError: CreateNodes failed!\n\n");
          Beep();
          Beep();
          Beep();
        }
      else
        LogMessage(": Nodes created OK.\n");
      LogMessage("\tNumber of Vertices: %d\n", level->num_vertexes);
      LogMessage("\tNumber of SideDefs: %d\n", level->num_sidedefs);
      LogMessage("\tNumber of Segs:     %d\n", NumSegs);
      LogMessage("\tNumber of SSectors: %d\n", NumSSectors);
      if (UseMouse)
        HideMousePointer();
      DrawScreenMeter(225, 28, ScrMaxX - 10, 48, 1.0);
      if (UseMouse)
        ShowMousePointer();
      newnodes = TRUE;
    }
  else
    newnodes = FALSE;

  /* output the LineDefs */
  if (level->num_linedefs > 0)
    {
      LDPtr cur;

      cur = level->linedefs;
      for (n = 0; n < level->num_linedefs; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->start));
          WriteInt16(file, (UInt16 *)&(cur->end));
          WriteInt16(file, &(cur->flags));
          WriteInt16(file, &(cur->type));
          WriteInt16(file, &(cur->tag));
          WriteInt16(file, (UInt16 *)&(cur->sidedef1));
          WriteInt16(file, (UInt16 *)&(cur->sidedef2));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 14L;
        }
    }
  dir = dir->next;

  /* output the SideDefs */
  if (level->num_sidedefs > 0)
    {
      SDPtr cur;

      cur = level->sidedefs;
      for (n = 0; n < level->num_sidedefs; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->xoff));
          WriteInt16(file, (UInt16 *)&(cur->yoff));
          WriteBytes(file, &(cur->tex1), 8L);
          WriteBytes(file, &(cur->tex2), 8L);
          WriteBytes(file, &(cur->tex3), 8L);
          WriteInt16(file, (UInt16 *)&(cur->sector));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 30L;
        }
    }
  dir = dir->next;

  if (level->made_map_changes)
    {
      VPtr cur;

      /* output the Vertices */
      cur = level->vertexes;
      for (n = 0; n < level->num_vertexes; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->x));
          WriteInt16(file, (UInt16 *)&(cur->y));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 4L;
        }
    }
  else
    {
      /* copy the Vertices */
      size = dir->dir.size; counter += size;
      BasicWadSeek(dir->wadfile, dir->dir.start);
      CopyBytes(file, dir->wadfile->fileinfo, size);
    }
  dir = dir->next;

  if (newnodes)
    {
      SEPtr curse, oldse;
      SSPtr curss, oldss;

      /* output and forget the Segments */
      curse = Segs;
      while (curse)
        {
          WriteInt16(file, (UInt16 *)&(curse->start));
          WriteInt16(file, (UInt16 *)&(curse->end));
          WriteInt16(file, &(curse->angle));
          WriteInt16(file, (UInt16 *)&(curse->linedef));
          /* the node builder uses the '2' bit of the flip data */
          curse->flip = (curse->flip) & 1;
          WriteInt16(file, &(curse->flip));
          WriteInt16(file, &(curse->dist));
          oldse = curse;
          curse = curse->next;
          FreeFarMemory(oldse);
          counter += 12L;
        }
      dir = dir->next;

      /* output and forget the SSectors */
      curss = SSectors;
      while (curss)
        {
          WriteInt16(file, (UInt16 *)&(curss->num));
          WriteInt16(file, (UInt16 *)&(curss->first));
          oldss = curss;
          curss = curss->next;
          FreeFarMemory(oldss);
          counter += 4L;
        }
      dir = dir->next;

      /* output the Nodes */
      /*! RP NumNodes was previously (DEU 5.21) a global var.
             The BSP builder has nothing to do with it.
             Isn't it better to pass it a as parameter to SaveNodes. */
      NumNodes = 0;
      SaveNodes(file, Nodes, &NumNodes);
      counter += (UInt32) NumNodes * 28L;
      dir = dir->next;

      /* forget the Nodes */
      ForgetNodes(Nodes);
    }
  else
    {
      /* copy the Segs, SSectors and Nodes */
      for (n = 0; n < 3; n++)
        {
          size = dir->dir.size;
          counter += size;
          BasicWadSeek(dir->wadfile, dir->dir.start);
          CopyBytes(file, dir->wadfile->fileinfo, size);
          dir = dir->next;
        }
    }

  /* output the Sectors */
  if (level->num_sectors > 0)
    {
      SPtr cur;

      cur = level->sectors;
      for (n = 0; n < level->num_sectors; n++)
        {
          WriteInt16(file, (UInt16 *)&(cur->floorh));
          WriteInt16(file, (UInt16 *)&(cur->ceilh));
          WriteBytes(file, &(cur->floort), 8L);
          WriteBytes(file, &(cur->ceilt), 8L);
          WriteInt16(file, &(cur->light));
          WriteInt16(file, &(cur->type));
          WriteInt16(file, &(cur->tag));
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
          counter += 26L;
        }
    }
  dir = dir->next;

  if (rebuild & 2)
    {
      /* create and output the reject data */
      if (UseMouse)
        HideMousePointer();
      DrawScreenBox3D(218, 80, ScrMaxX, 135);
      SetColor(WHITE);
      DrawScreenText(225, 90, "Rebuilding the REJECT data...");
      DrawScreenBoxHollow(225, 108, ScrMaxX - 10, 128);
      DrawScreenMeter(225, 108, ScrMaxX - 10, 128, 0.0);
      if (UseMouse)
        ShowMousePointer();
      rejectptr = (Int16 *)CreateRejectData(level, &rejectsize);
      if (UseMouse)
        HideMousePointer();
      DrawScreenMeter(225, 108, ScrMaxX - 10, 128, 1.0);
      if (UseMouse)
        ShowMousePointer();
      WriteBytes(file, rejectptr, rejectsize); /*! byte order */
      FreeMemory(rejectptr);
    }
  else
    {
      /* copy the Reject data */
      rejectsize = dir->dir.size;
      BasicWadSeek(dir->wadfile, dir->dir.start);
      CopyBytes(file, dir->wadfile->fileinfo, rejectsize);
    }
  dir = dir->next;
  counter += rejectsize;

  if (rebuild & 4)
    {
      /* create and output the blockmap */
      if (UseMouse)
        HideMousePointer();
      DrawScreenBox3D(218, 160, ScrMaxX, 215);
      SetColor(WHITE);
      DrawScreenText(225, 170, "Rebuilding the BLOCKMAP...");
      DrawScreenBoxHollow(225, 188, ScrMaxX - 10, 208);
      DrawScreenMeter(225, 188, ScrMaxX - 10, 208, 0.0);
      if (UseMouse)
        ShowMousePointer();
      blockptr = (Int16 *)MakeBlockmap(level, &blocksize);
      WriteBytes(file, blockptr, blocksize); /*! byte order */
      if (UseMouse)
        HideMousePointer();
      DrawScreenMeter(225, 188, ScrMaxX - 10, 208, 1.0);
      if (UseMouse)
        ShowMousePointer();

      /*! RP Temporary removed this protection
             WARNING: This caused some problems in WinDEU
                      because this assumes P2_END is always
                      after the current 'dir' entry (we should
                      use MasterDir instead) */
      /*
      if (FindMasterDir(dir, "P2_END"))
        counter--;
      */
      FreeMemory(blockptr);
    }
  else
    {
      /* copy the blockmap data */
      blocksize = dir->dir.size;
      BasicWadSeek(dir->wadfile, dir->dir.start);
      CopyBytes(file, dir->wadfile->fileinfo, blocksize);
    }
  dir = dir->next;
  counter += blocksize;

  /* output the actual directory */
  dirstart = counter;
  counter = 12L;
  size = 0L;
  dir = level->dir;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, &(dir->dir.name), 8L);
  /*RP added this to skip level name entry! */
  dir = dir->next;

  size = (UInt32) level->num_things * 10L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "THINGS\0\0", 8L);
  counter += size;
  dir = dir->next;

  size = (UInt32) level->num_linedefs * 14L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "LINEDEFS", 8L);
  counter += size;
  dir = dir->next;

  size = (UInt32) level->num_sidedefs * 30L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SIDEDEFS", 8L);
  counter += size;
  dir = dir->next;

  if (level->made_map_changes)
    size = (UInt32) level->num_vertexes * 4L;
  else
    size = dir->dir.size;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "VERTEXES", 8L);
  counter += size;
  dir = dir->next;

  if (newnodes)
    size = (UInt32) NumSegs * 12L;
  else
    size = dir->dir.size;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SEGS\0\0\0\0", 8L);
  counter += size;
  dir = dir->next;

  if (newnodes)
    size = (UInt32) NumSSectors * 4L;
  else
    size = dir->dir.size;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SSECTORS", 8L);
  counter += size;
  dir = dir->next;

  if (newnodes)
    size = (UInt32) NumNodes * 28L;
  else
    size = dir->dir.size;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "NODES\0\0\0", 8L);
  counter += size;
  dir = dir->next;

  size = (UInt32) level->num_sectors * 26L;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "SECTORS\0", 8L);
  counter += size;
  dir = dir->next;

  size = rejectsize;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "REJECT\0\0", 8L);
  counter += size;
  dir = dir->next;

  size = blocksize;
  WriteInt32(file, &counter);
  WriteInt32(file, &size);
  WriteBytes(file, "BLOCKMAP", 8L);
  counter += size;
  dir = dir->next;

  /* fix up the directory start information */
  if (fseek(file, 8L, SEEK_SET) != 0)
    ProgError("error writing to file");
  WriteInt32(file, &dirstart);

  /* close the file */
  fclose(file);

  /* delete the vertices added by the Nodes builder */
  if (level->num_vertexes != saved_num_vertexes)
  {
     level->num_vertexes = saved_num_vertexes;
     level->vertexes = (VPtr) ResizeFarMemory(level->vertexes, level->num_vertexes * sizeof(struct Vertex));
  }

  /* the file is now up to date */
  level->made_changes = FALSE;
  if (newnodes)
    level->made_map_changes = FALSE;

  /*! change this... */
  if (rebuild)
    ExitBuilderMenu();

  /* update pointers in Master Directory */
  OpenPatchWad(outfile);

  /* this should free the old "*.BAK" file */
  CloseUnusedWadFiles();

  if (UseMouse)
    ShowMousePointer();
}


/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  ! The following functions have to be rewritten - use a cache for textures !
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/

/*
   function used by qsort to sort the texture names
*/
int SortTextures(const void *a, const void *b)
{
  return strcmp(*((char **)a), *((char **)b));
}


/*
   read in the wall texture names
*/

void ReadWTextureNames(void)
{
  MDirPtr  dir;
  UInt32  *offsets;
  Int16    n;
  UInt32   val;

  printf("Reading wall texture names\n");
  dir = FindMasterDir(MasterDir, "TEXTURE1");
  BasicWadSeek(dir->wadfile, dir->dir.start);
  WadReadInt32(dir->wadfile, &val);
  num_wtexture = (UInt16) val + 1;
  /* read in the offsets for texture1 names */
  offsets = (UInt32 *) GetMemory(num_wtexture * sizeof(UInt32));
  for (n = 1; n < num_wtexture; n++)
    WadReadInt32(dir->wadfile, &(offsets[n]));
  /* read in the actual names */
  wtexture = (char **) GetMemory(num_wtexture * sizeof(char *));
  wtexture[0] = (char *) GetMemory(9 * sizeof(char));
  strcpy(wtexture[0], "-");
  for (n = 1; n < num_wtexture; n++)
    {
      wtexture[n] = (char*) GetMemory(9 * sizeof(char));
      BasicWadSeek(dir->wadfile, dir->dir.start + offsets[n]);
      BasicWadRead(dir->wadfile, wtexture[n], 8);
      wtexture[n][8] = '\0';
    }
  FreeMemory(offsets);
  if (DoomVersion == 1)
    {
       dir = FindMasterDir(MasterDir, "TEXTURE2");
       BasicWadSeek(dir->wadfile, dir->dir.start);
       WadReadInt32(dir->wadfile, &val);
       /* read in the offsets for texture2 names */
       offsets = (UInt32 *) GetMemory(val * sizeof(UInt32));
       for (n = 0; n < val; n++)
         WadReadInt32(dir->wadfile, &(offsets[n]));
       /* read in the actual names */
       wtexture = (char **) ResizeMemory(wtexture, (num_wtexture + val) * sizeof(char *));
       for (n = 0; n < val; n++)
         {
           wtexture[num_wtexture + n] = (char*) GetMemory(9 * sizeof(char));
           BasicWadSeek(dir->wadfile, dir->dir.start + offsets[n]);
           BasicWadRead(dir->wadfile, wtexture[num_wtexture + n], 8);
           wtexture[num_wtexture + n][8] = '\0';
         }
       num_wtexture += val;
       FreeMemory(offsets);
    }
  /* sort the names */
  qsort(wtexture, num_wtexture, sizeof(char *), SortTextures);
}


/*
   forget the wall texture names
*/

void ForgetWTextureNames(void)
{
  UInt16 n;

  /* forget all names */
  for (n = 0; n < num_wtexture; n++)
     FreeMemory(wtexture[n]);

  /* forget the array */
  num_wtexture = 0;
  FreeMemory(wtexture);
}


/*
   read in the floor/ceiling texture names
*/

void ReadFTextureNames(void)
{
  MDirPtr dir;
  Int16   n, m;

  printf("Reading floor/ceiling texture names\n");
  /* count the names */
  dir = FindMasterDir(MasterDir, "F1_START");
  dir = dir->next;
  for (n = 0; dir && strcmp(dir->dir.name, "F1_END"); n++)
     dir = dir->next;
  num_ftexture = n;
  /* get the actual names from master dir. */
  dir = FindMasterDir(MasterDir, "F1_START");
  dir = dir->next;
  ftexture = (char **)GetMemory(num_ftexture * sizeof(char *));
  for (n = 0; n < num_ftexture; n++)
    {
      ftexture[n] = (char *)GetMemory(9 * sizeof(char));
      strncpy(ftexture[n], dir->dir.name, 8);
      ftexture[n][8] = '\0';
      dir = dir->next;
    }
  if (DoomVersion >= 1)
    {
      /* count the names */
      dir = FindMasterDir(MasterDir, "F2_START");
      dir = dir->next;
      for (n = 0; dir && strcmp(dir->dir.name, "F2_END"); n++)
         dir = dir->next;
      /* get the actual names from master dir. */
      dir = FindMasterDir(MasterDir, "F2_START");
      dir = dir->next;
      ftexture = (char **) ResizeMemory(ftexture, (num_ftexture + n) * sizeof(char *));
      for (m = num_ftexture; m < num_ftexture + n; m++)
        {
          ftexture[m] = (char *) GetMemory(9 * sizeof(char));
          strncpy(ftexture[m], dir->dir.name, 8);
          ftexture[m][8] = '\0';
          dir = dir->next;
        }
      num_ftexture += n;
    }
  if (DoomVersion == 2)
    {
      /* count the names */
      dir = FindMasterDir(MasterDir, "F3_START");
      dir = dir->next;
      for (n = 0; dir && strcmp(dir->dir.name, "F3_END"); n++)
        dir = dir->next;
      /* get the actual names from master dir. */
      dir = FindMasterDir(MasterDir, "F3_START");
      dir = dir->next;
      ftexture = (char **) ResizeMemory(ftexture, (num_ftexture + n) * sizeof(char *));
      for (m = num_ftexture; m < num_ftexture + n; m++)
        {
          ftexture[m] = (char *) GetMemory(9 * sizeof(char));
          strncpy(ftexture[m], dir->dir.name, 8);
          ftexture[m][8] = '\0';
          dir = dir->next;
        }
      num_ftexture += n;
    }
  /* sort the names */
  qsort(ftexture, num_ftexture, sizeof(char *), SortTextures);
}


/*
   forget the floor/ceiling texture names
*/

void ForgetFTextureNames(void)
{
  UInt16 n;

  /* forget all names */
  for (n = 0; n < num_ftexture; n++)
     FreeMemory(ftexture[n]);

  /* forget the array */
  num_ftexture = 0;
  FreeMemory(ftexture);
}

/* end of file */
