Blob Blame History Raw
diff -Naur yadex-1.7.0.orig/src/editloop.cc yadex-1.7.0.allpatches/src/editloop.cc
--- yadex-1.7.0.orig/src/editloop.cc	2013-01-10 17:24:11.554023988 +0100
+++ yadex-1.7.0.allpatches/src/editloop.cc	2013-01-10 17:25:53.187909413 +0100
@@ -43,6 +43,7 @@
 #include "entry.h"
 #include "entry2.h"
 #include "events.h"
+#include "game.h"
 #include "gfx.h"
 #include "gfx2.h"	// show_character_set() show_pcolours()
 #include "gfx3.h"
@@ -94,6 +95,7 @@
 
 /* prototypes of private functions */
 static int SortLevels (const void *item1, const void *item2);
+static char *GetBehaviorFileName (const char *levelname);
 
 /*
  *	SelectLevel
@@ -303,15 +305,28 @@
    "~Quit",       'q',   0,
    NULL);
 
-e.mb_menu[MBM_EDIT] = new Menu (NULL,
-   "~Copy object(s)",          'o',    0,
-   "~Add object",              'I', 0,
-   "~Delete object(s)",        '\b', 0,
-   "~Exchange object numbers", 24,     0,
-   "~Preferences...",          YK_F5,  0,
-   "~Snap to grid",            'y',    MIF_VTICK, &e.grid_snap,		     0,
-   "~Lock grid step",          'z',    MIF_VTICK, &e.grid_step_locked,	     0,
-   NULL);
+if (yg_level_format == YGLF_HEXEN)
+   e.mb_menu[MBM_EDIT] = new Menu (NULL,
+     "~Copy object(s)",          'o',    0,
+      "~Add object",              'I', 0,
+      "~Delete object(s)",        '\b', 0,
+      "~Exchange object numbers", 24,     0,
+      "~Preferences...",          YK_F5,  0,
+      "~Snap to grid",            'y',    MIF_VTICK, &e.grid_snap,                  0,
+      "~Lock grid step",          'z',    MIF_VTICK, &e.grid_step_locked,           0,
+      "Load ~BEHAVIOR lump",      'b',    0,
+      NULL);
+else
+   e.mb_menu[MBM_EDIT] = new Menu (NULL,
+      "~Copy object(s)",          'o',    0,
+      "~Add object",              'I', 0,
+      "~Delete object(s)",        '\b', 0,
+      "~Exchange object numbers", 24,     0,
+      "~Preferences...",          YK_F5,  0,
+      "~Snap to grid",            'y',    MIF_VTICK, &e.grid_snap,                  0,
+      "~Lock grid step",          'z',    MIF_VTICK, &e.grid_step_locked,           0,
+      NULL);
+
 
 // If you change the order of modes here, don't forget
 // to modify the <modes> array.
@@ -2415,6 +2430,30 @@
 	 RedrawMap = 1;
 	 }
 
+      // Load BEHAVIOR lump (JL)
+      else if (is.key == 'b')
+         {
+         char *acsfile;
+         const char *acsname;
+         if (levelname)
+            acsname = levelname;
+         else
+            acsname = "behavior";
+         acsfile = GetBehaviorFileName (acsname);
+         FILE* f = fopen(acsfile, "rb");
+         if (f)
+            {
+            FreeFarMemory(Behavior);
+            fseek(f, 0, SEEK_END);
+            BehaviorSize = ftell(f);
+            Behavior = (u8*)GetFarMemory(BehaviorSize);
+            fseek(f, 0, SEEK_SET);
+            fread(Behavior, BehaviorSize, 1, f);
+            fclose(f);
+            }
+         RedrawMap = 1;
+      }
+
       /* user likes music */
       else if (is.key)
 	 {
@@ -2564,4 +2603,32 @@
   return 0;
 }
 
+/*
+   get the name of the BEHAVIOR lump file (returns NULL on Esc)
+*/
+
+static char *GetBehaviorFileName (const char *levelname)
+{
+#define BUFSZ 79
+  char *outfile = (char *) GetMemory (BUFSZ + 1);
+
+  /* get the file name */
+  // If no name, find a default one
+  if (! levelname)
+  {
+    levelname = "behavior";
+  }
+
+  al_scpslower (outfile, levelname, BUFSZ);
+  al_saps (outfile, ".o", BUFSZ);
+  InputFileName (-1, -1, "Name of the BEHAVIOR script file:", BUFSZ, outfile);
+  /* escape */
+  if (outfile[0] == '\0')
+  {
+    FreeMemory (outfile);
+    return 0;
+  }
+  return outfile;
+}
+
 
diff -Naur yadex-1.7.0.orig/src/levels.cc yadex-1.7.0.allpatches/src/levels.cc
--- yadex-1.7.0.orig/src/levels.cc	2013-01-10 17:24:11.563024067 +0100
+++ yadex-1.7.0.allpatches/src/levels.cc	2013-01-10 17:25:53.193909464 +0100
@@ -58,6 +58,8 @@
 VPtr Vertices;			/* vertex data */
 int NumSectors;			/* number of sectors */
 SPtr Sectors;			/* sectors data */
+u8* Behavior;
+int BehaviorSize;
 
 // FIXME should be somewhere else
 int NumWTexture;		/* number of wall textures */
@@ -95,6 +97,10 @@
 				   the Level has never been saved yet,
 				   an empty string. */
 
+static u8 DefaultBehavior[16] = {
+	'A', 'C', 'S', 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
 void EmptyLevelData (const char *levelname)
 {
 Things = 0;
@@ -109,6 +115,12 @@
 NumSectors = 0;
 Vertices = 0;
 NumVertices = 0;
+if (yg_level_format == YGLF_HEXEN)
+   {
+      BehaviorSize = sizeof(DefaultBehavior);
+      Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+      memcpy(Behavior, DefaultBehavior, BehaviorSize);
+   }
 }
 
 
@@ -199,7 +211,7 @@
    {
    offset = dir->dir.start;
    length = dir->dir.size;
-   if (MainWad == Iwad4)  // Hexen mode
+   if (yg_level_format == YGLF_HEXEN)  // Hexen mode
       {
       NumThings = (int) (length / WAD_HEXEN_THING_BYTES);
       if ((i32) (NumThings * WAD_HEXEN_THING_BYTES) != length)
@@ -234,18 +246,23 @@
       rc = 1;
       goto byebye;
       }
-   if (MainWad == Iwad4)		// Hexen mode
+   if (yg_level_format == YGLF_HEXEN)		// Hexen mode
       for (long n = 0; n < NumThings; n++)
 	 {
          u8 dummy2[6];
-	 wf->read_i16   ();					// Tid
+	 wf->read_i16   (&Things[n].tid  );
 	 wf->read_i16   (&Things[n].xpos );
 	 wf->read_i16   (&Things[n].ypos );
-	 wf->read_i16   ();					// Height
+	 wf->read_i16   (&Things[n].height);
 	 wf->read_i16   (&Things[n].angle);
 	 wf->read_i16   (&Things[n].type );
 	 wf->read_i16   (&Things[n].when );
-         wf->read_bytes (dummy2, sizeof dummy2);
+	 wf->read_u8    (Things[n].special);
+	 wf->read_u8    (Things[n].arg1  );
+	 wf->read_u8    (Things[n].arg2  );
+	 wf->read_u8    (Things[n].arg3  );
+	 wf->read_u8    (Things[n].arg4  );
+	 wf->read_u8    (Things[n].arg5  );
 	 if (wf->error ())
 	    {
 	    err ("%s: error reading thing #%ld", lump_name, n);
@@ -283,7 +300,7 @@
       NumLineDefs = 0;
    else
       {
-      if (MainWad == Iwad4)  // Hexen mode
+      if (yg_level_format == YGLF_HEXEN)  // Hexen mode
 	 {
 	 NumLineDefs = (int) (dir->dir.size / WAD_HEXEN_LINEDEF_BYTES);
 	 if ((i32) (NumLineDefs * WAD_HEXEN_LINEDEF_BYTES) != dir->dir.size)
@@ -310,7 +327,7 @@
 	 rc = 1;
 	 goto byebye;
 	 }
-      if (MainWad == Iwad4)  // Hexen mode
+      if (yg_level_format == YGLF_HEXEN)  // Hexen mode
 	 for (long n = 0; n < NumLineDefs; n++)
 	    {
 	    u8 dummy[6];
@@ -322,6 +339,10 @@
 	    wf->read_i16   (&LineDefs[n].sidedef2);
 	    LineDefs[n].type = dummy[0];
 	    LineDefs[n].tag  = dummy[1];  // arg1 often contains a tag
+		LineDefs[n].arg2 = dummy[2];
+		LineDefs[n].arg3 = dummy[3];
+		LineDefs[n].arg4 = dummy[4];
+		LineDefs[n].arg5 = dummy[5];
 	    if (wf->error ())
 	       {
 	       err ("%s: error reading linedef #%ld", lump_name, n);
@@ -907,6 +928,37 @@
    }
 }
 
+// Read BEHAVIOR
+if (yg_level_format == YGLF_HEXEN)
+{
+const char *lump_name = "BEHAVIOR";
+verbmsg (" behavior\n");
+dir = FindMasterDir (Level, lump_name);
+if (dir)
+   {
+   BehaviorSize = (int)dir->dir.size;
+   if (BehaviorSize > 0)
+      {
+      Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+         {
+         err ("%s: seek error", lump_name);
+         rc = 1;
+         goto byebye;
+         }
+      wf->read_bytes (Behavior, BehaviorSize);
+      if (wf->error ())
+         {
+         err ("%s: error behavior lump", lump_name);
+         rc = 1;
+         goto byebye;
+         }
+      }
+   }
+}
+
 /* Sanity checking on sidedefs: the sector must exist. I don't
    make this a fatal error, though, because it's not exceptional
    to find wads with unused sidedefs with a sector# of -1. Well
@@ -1011,16 +1063,10 @@
 FILE   *file;
 MDirPtr dir;
 int     n;
-long	lump_offset[WAD_LL__];
-size_t	lump_size[WAD_LL__];
+long	lump_offset[WAD_LL__MAX];
+size_t	lump_size[WAD_LL__MAX];
 wad_level_lump_no_t l;
 
-if (yg_level_format == YGLF_HEXEN || ! strcmp (Game, "hexen"))
-   {
-   Notify (-1, -1, "I refuse to save. Hexen mode is still",
-                   "too badly broken. You would lose data.");
-   return 1;
-   }
 if (! level_name || ! levelname2levelno (level_name))
    {
    nf_bug ("SaveLevelData: bad level_name \"%s\", using \"E1M1\" instead.",
@@ -1047,9 +1093,15 @@
   && ! MadeMapChanges
   && yg_level_format != YGLF_ALPHA;
 
+int NumLumps;
+if (yg_level_format == YGLF_HEXEN)
+   NumLumps = WAD_LL__HEXEN;
+else
+   NumLumps = WAD_LL__DOOM;
+
 // Write the pwad header
 WriteBytes (file, "PWAD", 4);		// Pwad file
-file_write_i32 (file, WAD_LL__);	// Number of entries = 11
+file_write_i32 (file, NumLumps);	// Number of entries = 11
 file_write_i32 (file, 0);		// Fix this up later
 if (Level)
    dir = Level->next;
@@ -1067,11 +1119,30 @@
 ObjectsNeeded (OBJ_THINGS, 0);
 for (n = 0; n < NumThings; n++)
    {
-   file_write_i16 (file, Things[n].xpos );
-   file_write_i16 (file, Things[n].ypos );
-   file_write_i16 (file, Things[n].angle);
-   file_write_i16 (file, Things[n].type );
-   file_write_i16 (file, Things[n].when );
+   if (yg_level_format == YGLF_HEXEN)
+      {
+      file_write_i16 (file, Things[n].tid  );
+      file_write_i16 (file, Things[n].xpos );
+      file_write_i16 (file, Things[n].ypos );
+      file_write_i16 (file, Things[n].height);
+      file_write_i16 (file, Things[n].angle);
+      file_write_i16 (file, Things[n].type );
+      file_write_i16 (file, Things[n].when );
+      WriteBytes     (file, &Things[n].special, 1);
+      WriteBytes     (file, &Things[n].arg1, 1 );
+      WriteBytes     (file, &Things[n].arg2, 1 );
+      WriteBytes     (file, &Things[n].arg3, 1 );
+      WriteBytes     (file, &Things[n].arg4, 1 );
+      WriteBytes     (file, &Things[n].arg5, 1 );
+	  }
+   else
+      {
+      file_write_i16 (file, Things[n].xpos );
+      file_write_i16 (file, Things[n].ypos );
+      file_write_i16 (file, Things[n].angle);
+      file_write_i16 (file, Things[n].type );
+      file_write_i16 (file, Things[n].when );
+      }
    }
 lump_size[l] = ftell (file) - lump_offset[l];
 if (Level)
@@ -1083,13 +1154,32 @@
 ObjectsNeeded (OBJ_LINEDEFS, 0);
 for (n = 0; n < NumLineDefs; n++)
    {
-   file_write_i16 (file, LineDefs[n].start   );
-   file_write_i16 (file, LineDefs[n].end     );
-   file_write_i16 (file, LineDefs[n].flags   );
-   file_write_i16 (file, LineDefs[n].type    );
-   file_write_i16 (file, LineDefs[n].tag     );
-   file_write_i16 (file, LineDefs[n].sidedef1);
-   file_write_i16 (file, LineDefs[n].sidedef2);
+   if (yg_level_format == YGLF_HEXEN)
+      {
+      u8 dummy[6];
+      dummy[0] = LineDefs[n].type;
+      dummy[1] = LineDefs[n].tag;
+      dummy[2] = LineDefs[n].arg2;
+      dummy[3] = LineDefs[n].arg3;
+      dummy[4] = LineDefs[n].arg4;
+      dummy[5] = LineDefs[n].arg5;
+      file_write_i16 (file, LineDefs[n].start   );
+      file_write_i16 (file, LineDefs[n].end     );
+      file_write_i16 (file, LineDefs[n].flags   );
+      WriteBytes     (file, dummy, 6);
+      file_write_i16 (file, LineDefs[n].sidedef1);
+      file_write_i16 (file, LineDefs[n].sidedef2);
+	  }
+   else
+      {
+      file_write_i16 (file, LineDefs[n].start   );
+      file_write_i16 (file, LineDefs[n].end     );
+      file_write_i16 (file, LineDefs[n].flags   );
+      file_write_i16 (file, LineDefs[n].type    );
+      file_write_i16 (file, LineDefs[n].tag     );
+      file_write_i16 (file, LineDefs[n].sidedef1);
+      file_write_i16 (file, LineDefs[n].sidedef2);
+	  }
    }
 lump_size[l] = ftell (file) - lump_offset[l];
 if (Level)
@@ -1221,9 +1311,21 @@
 if (Level)
    dir = dir->next;
 
+ 
+// Write the BEHAVIOR lump
+if (yg_level_format == YGLF_HEXEN)
+{
+   l = WAD_LL_BEHAVIOR;
+   lump_offset[l] = ftell (file);
+   WriteBytes(file, Behavior, BehaviorSize);
+   lump_size[l] = BehaviorSize;
+   if (Level)
+      dir = dir->next;
+}
+
 // Write the actual directory
 long dir_offset = ftell (file);
-for (int L = 0; L < (int) WAD_LL__; L++)
+for (int L = 0; L < (int) NumLumps; L++)
    {
    file_write_i32 (file, lump_offset[L]);
    file_write_i32 (file, lump_size[L]);
diff -Naur yadex-1.7.0.orig/src/levels.cc.orig yadex-1.7.0.allpatches/src/levels.cc.orig
--- yadex-1.7.0.orig/src/levels.cc.orig	1970-01-01 01:00:00.000000000 +0100
+++ yadex-1.7.0.allpatches/src/levels.cc.orig	2013-01-10 17:25:53.195909482 +0100
@@ -0,0 +1,1811 @@
+/*
+ *	levels.cc
+ *	Level loading and saving routines,
+ *	global variables used to hold the level data.
+ *	BW & RQ sometime in 1993 or 1994.
+ */
+
+
+/*
+This file is part of Yadex.
+
+Yadex incorporates code from DEU 5.21 that was put in the public domain in
+1994 by RaphaŽl Quinet and Brendon Wyber.
+
+The rest of Yadex is Copyright © 1997-2003 Andrť Majorel and others.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, 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 License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "yadex.h"
+#include "bitvec.h"
+#include "dialog.h"
+#include "game.h"
+#include "levels.h"
+#include "objid.h"
+#include "wstructs.h"
+#include "things.h"
+#include "wadfile.h"
+#include "wads.h"
+#include "wads2.h"
+
+
+/*
+ 	FIXME
+	All these variables should be turned
+	into members of a "Level" class.
+*/
+MDirPtr Level;			/* master dictionary entry for the level */
+int NumThings;			/* number of things */
+TPtr Things;			/* things data */
+int NumLineDefs;		/* number of line defs */
+LDPtr LineDefs;			/* line defs data */
+int NumSideDefs;		/* number of side defs */
+SDPtr SideDefs;			/* side defs data */
+int NumVertices;		/* number of vertexes */
+VPtr Vertices;			/* vertex data */
+int NumSectors;			/* number of sectors */
+SPtr Sectors;			/* sectors data */
+u8* Behavior;
+int BehaviorSize;
+
+// FIXME should be somewhere else
+int NumWTexture;		/* number of wall textures */
+char **WTexture;		/* array of wall texture names */
+
+// FIXME all the flat list stuff should be put in a separate class
+size_t NumFTexture;		/* number of floor/ceiling textures */
+flat_list_entry_t *flat_list;	// List of all flats in the directory
+
+int MapMaxX = -32767;		/* maximum X value of map */
+int MapMaxY = -32767;		/* maximum Y value of map */
+int MapMinX = 32767;		/* minimum X value of map */
+int MapMinY = 32767;		/* minimum Y value of map */
+bool MadeChanges;		/* made changes? */
+bool MadeMapChanges;		/* made changes that need rebuilding? */
+unsigned long things_angles;	// See levels.h for description.
+unsigned long things_types;	// See levels.h for description.
+char Level_name[WAD_NAME + 1];	/* The name of the level (E.G.
+				   "MAP01" or "E1M1"), followed by a
+				   NUL. If the Level has been created as
+				   the result of a "c" command with no
+				   argument, an empty string. The name
+				   is not necesarily in upper case but
+				   it always a valid lump name, not a
+				   command line shortcut like "17". */
+
+y_file_name_t Level_file_name;	/* The name of the file in which
+				   the level would be saved. If the
+				   level has been created as the result
+				   of a "c" command, with or without
+				   argument, an empty string. */
+
+y_file_name_t Level_file_name_saved;  /* The name of the file in
+				   which the level was last saved. If
+				   the Level has never been saved yet,
+				   an empty string. */
+
+static u8 DefaultBehavior[16] = {
+	'A', 'C', 'S', 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void EmptyLevelData (const char *levelname)
+{
+Things = 0;
+NumThings = 0;
+things_angles++;
+things_types++;
+LineDefs = 0;
+NumLineDefs = 0;
+SideDefs = 0;
+NumSideDefs = 0;
+Sectors = 0;
+NumSectors = 0;
+Vertices = 0;
+NumVertices = 0;
+if (yg_level_format == YGLF_HEXEN)
+   {
+      BehaviorSize = sizeof(DefaultBehavior);
+      Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+      memcpy(Behavior, DefaultBehavior, BehaviorSize);
+   }
+}
+
+
+/*
+ *	texno_texname
+ *	A convenience function when loading Doom alpha levels
+ */
+static char *tex_list = 0;
+static size_t ntex = 0;
+static char tex_name[WAD_TEX_NAME + 1];
+inline const char *texno_texname (i16 texno)
+{
+if (texno < 0)
+   return "-";
+else
+   if (yg_texture_format == YGTF_NAMELESS)
+      {
+      sprintf (tex_name, "TEX%04u", (unsigned) texno);
+      return tex_name;
+      }
+   else
+      {
+      if (texno < (i16) ntex)
+	 return tex_list + WAD_TEX_NAME * texno;
+      else
+	 return "unknown";
+      }
+}
+
+
+/*
+   read in the level data
+*/
+
+int ReadLevelData (const char *levelname) /* SWAP! */
+{
+int rc = 0;
+MDirPtr dir;
+int OldNumVertices;
+
+/* No objects are needed: they may be swapped after they have been read */
+ObjectsNeeded (0);
+
+/* Find the various level information from the master directory */
+DisplayMessage (-1, -1, "Reading data for level %s...", levelname);
+Level = FindMasterDir (MasterDir, levelname);
+if (!Level)
+   fatal_error ("level data not found");
+
+/* Get the number of vertices */
+i32 v_offset = 42;
+i32 v_length = 42;
+{
+const char *lump_name = "BUG";
+if (yg_level_format == YGLF_ALPHA)  // Doom alpha
+   lump_name = "POINTS";
+else
+   lump_name = "VERTEXES";
+dir = FindMasterDir (Level, lump_name);
+if (dir == 0)
+   OldNumVertices = 0;
+else
+   {
+   v_offset = dir->dir.start;
+   v_length = dir->dir.size;
+   if (yg_level_format == YGLF_ALPHA)  // Doom alpha: skip leading count
+      {
+      v_offset += 4;
+      v_length -= 4;
+      }
+   OldNumVertices = (int) (v_length / WAD_VERTEX_BYTES);
+   if ((i32) (OldNumVertices * WAD_VERTEX_BYTES) != v_length)
+      warn ("the %s lump has a weird size."
+        " The wad might be corrupt.\n", lump_name);
+   }
+}
+
+// Read THINGS
+{
+const char *lump_name = "THINGS";
+verbmsg ("Reading %s things", levelname);
+i32 offset = 42;
+i32 length;
+dir = FindMasterDir (Level, lump_name);
+if (dir == 0)
+   NumThings = 0;
+else
+   {
+   offset = dir->dir.start;
+   length = dir->dir.size;
+   if (yg_level_format == YGLF_HEXEN)  // Hexen mode
+      {
+      NumThings = (int) (length / WAD_HEXEN_THING_BYTES);
+      if ((i32) (NumThings * WAD_HEXEN_THING_BYTES) != length)
+         warn ("the %s lump has a weird size."
+            " The wad might be corrupt.\n", lump_name);
+      }
+   else                    // Doom/Heretic/Strife mode
+      {
+      if (yg_level_format == YGLF_ALPHA)  // Doom alpha: skip leading count
+	 {
+	 offset += 4;
+	 length -= 4;
+	 }
+      size_t thing_size = yg_level_format == YGLF_ALPHA ? 12 : WAD_THING_BYTES;
+      NumThings = (int) (length / thing_size);
+      if ((i32) (NumThings * thing_size) != length)
+         warn ("the %s lump has a weird size."
+            " The wad might be corrupt.\n", lump_name);
+      }
+   }
+things_angles++;
+things_types++;
+if (NumThings > 0)
+   {
+   Things = (TPtr) GetFarMemory ((unsigned long) NumThings
+      * sizeof (struct Thing));
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (offset);
+   if (wf->error ())
+      {
+      err ("%s: seek error", lump_name);
+      rc = 1;
+      goto byebye;
+      }
+   if (yg_level_format == YGLF_HEXEN)		// Hexen mode
+      for (long n = 0; n < NumThings; n++)
+	 {
+         u8 dummy2[6];
+	 wf->read_i16   (&Things[n].tid  );
+	 wf->read_i16   (&Things[n].xpos );
+	 wf->read_i16   (&Things[n].ypos );
+	 wf->read_i16   (&Things[n].height);
+	 wf->read_i16   (&Things[n].angle);
+	 wf->read_i16   (&Things[n].type );
+	 wf->read_i16   (&Things[n].when );
+	 wf->read_u8    (Things[n].special);
+	 wf->read_u8    (Things[n].arg1  );
+	 wf->read_u8    (Things[n].arg2  );
+	 wf->read_u8    (Things[n].arg3  );
+	 wf->read_u8    (Things[n].arg4  );
+	 wf->read_u8    (Things[n].arg5  );
+	 if (wf->error ())
+	    {
+	    err ("%s: error reading thing #%ld", lump_name, n);
+	    rc = 1;
+	    goto byebye;
+	    }
+	 }
+   else					// Doom/Heretic/Strife mode
+      for (long n = 0; n < NumThings; n++)
+	 {
+	 wf->read_i16 (&Things[n].xpos );
+	 wf->read_i16 (&Things[n].ypos );
+	 wf->read_i16 (&Things[n].angle);
+	 wf->read_i16 (&Things[n].type );
+	 if (yg_level_format == YGLF_ALPHA)
+	    wf->read_i16 ();		// Alpha. Don't know what it's for.
+	 wf->read_i16 (&Things[n].when );
+	 if (wf->error ())
+	    {
+	    err ("%s: error reading thing #%ld", lump_name, n);
+	    rc = 1;
+	    goto byebye;
+	    }
+	 }
+   }
+}
+
+// Read LINEDEFS
+if (yg_level_format != YGLF_ALPHA)
+   {
+   const char *lump_name = "LINEDEFS";
+   verbmsg (" linedefs");
+   dir = FindMasterDir (Level, lump_name);
+   if (dir == 0)
+      NumLineDefs = 0;
+   else
+      {
+      if (yg_level_format == YGLF_HEXEN)  // Hexen mode
+	 {
+	 NumLineDefs = (int) (dir->dir.size / WAD_HEXEN_LINEDEF_BYTES);
+	 if ((i32) (NumLineDefs * WAD_HEXEN_LINEDEF_BYTES) != dir->dir.size)
+	    warn ("the %s lump has a weird size."
+	       " The wad might be corrupt.\n", lump_name);
+	 }
+      else                   // Doom/Heretic/Strife mode
+	 {
+	 NumLineDefs = (int) (dir->dir.size / WAD_LINEDEF_BYTES);
+	 if ((i32) (NumLineDefs * WAD_LINEDEF_BYTES) != dir->dir.size)
+	    warn ("the %s lump has a weird size."
+	       " The wad might be corrupt.\n", lump_name);
+	 }
+      }
+   if (NumLineDefs > 0)
+      {
+      LineDefs = (LDPtr) GetFarMemory ((unsigned long) NumLineDefs
+	 * sizeof (struct LineDef));
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+	 {
+	 err ("%s: seek error", lump_name);
+	 rc = 1;
+	 goto byebye;
+	 }
+      if (yg_level_format == YGLF_HEXEN)  // Hexen mode
+	 for (long n = 0; n < NumLineDefs; n++)
+	    {
+	    u8 dummy[6];
+	    wf->read_i16   (&LineDefs[n].start);
+	    wf->read_i16   (&LineDefs[n].end);
+	    wf->read_i16   (&LineDefs[n].flags);
+	    wf->read_bytes (dummy, sizeof dummy);
+	    wf->read_i16   (&LineDefs[n].sidedef1);
+	    wf->read_i16   (&LineDefs[n].sidedef2);
+	    LineDefs[n].type = dummy[0];
+	    LineDefs[n].tag  = dummy[1];  // arg1 often contains a tag
+		LineDefs[n].arg2 = dummy[2];
+		LineDefs[n].arg3 = dummy[3];
+		LineDefs[n].arg4 = dummy[4];
+		LineDefs[n].arg5 = dummy[5];
+	    if (wf->error ())
+	       {
+	       err ("%s: error reading linedef #%ld", lump_name, n);
+	       rc = 1;
+	       goto byebye;
+	       }
+	    }
+      else                   // Doom/Heretic/Strife mode
+	 for (long n = 0; n < NumLineDefs; n++)
+	    {
+	    wf->read_i16 (&LineDefs[n].start);
+	    wf->read_i16 (&LineDefs[n].end);
+	    wf->read_i16 (&LineDefs[n].flags);
+	    wf->read_i16 (&LineDefs[n].type);
+	    wf->read_i16 (&LineDefs[n].tag);
+	    wf->read_i16 (&LineDefs[n].sidedef1);
+	    wf->read_i16 (&LineDefs[n].sidedef2);
+	    if (wf->error ())
+	       {
+	       err ("%s: error reading linedef #%ld", lump_name, n);
+	       rc = 1;
+	       goto byebye;
+	       }
+	    }
+      }
+   }
+
+// Read SIDEDEFS
+{
+const char *lump_name = "SIDEDEFS";
+verbmsg (" sidedefs");
+dir = FindMasterDir (Level, lump_name);
+if (dir)
+   {
+   NumSideDefs = (int) (dir->dir.size / WAD_SIDEDEF_BYTES);
+   if ((i32) (NumSideDefs * WAD_SIDEDEF_BYTES) != dir->dir.size)
+      warn ("the SIDEDEFS lump has a weird size."
+         " The wad might be corrupt.\n");
+   }
+else
+   NumSideDefs = 0;
+if (NumSideDefs > 0)
+   {
+   SideDefs = (SDPtr) GetFarMemory ((unsigned long) NumSideDefs
+      * sizeof (struct SideDef));
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      err ("%s: seek error", lump_name);
+      rc = 1;
+      goto byebye;
+      }
+   for (long n = 0; n < NumSideDefs; n++)
+      {
+      wf->read_i16   (&SideDefs[n].xoff);
+      wf->read_i16   (&SideDefs[n].yoff);
+      wf->read_bytes (&SideDefs[n].tex1, WAD_TEX_NAME);
+      wf->read_bytes (&SideDefs[n].tex2, WAD_TEX_NAME);
+      wf->read_bytes (&SideDefs[n].tex3, WAD_TEX_NAME);
+      wf->read_i16   (&SideDefs[n].sector);
+      if (wf->error ())
+	 {
+	 err ("%s: error reading sidedef #%ld", lump_name, n);
+	 rc = 1;
+	 goto byebye;
+	 }
+      }
+   }
+}
+
+/* Sanity checkings on linedefs: the 1st and 2nd vertices
+   must exist. The 1st and 2nd sidedefs must exist or be
+   set to -1. */
+for (long n = 0; n < NumLineDefs; n++)
+   {
+   if (LineDefs[n].sidedef1 != -1
+      && outside (LineDefs[n].sidedef1, 0, NumSideDefs - 1))
+      {
+      err ("linedef %ld has bad 1st sidedef number %d, giving up",
+	 n, LineDefs[n].sidedef1);
+      rc = 1;
+      goto byebye;
+      }
+   if (LineDefs[n].sidedef2 != -1
+      && outside (LineDefs[n].sidedef2, 0, NumSideDefs - 1))
+      {
+      err ("linedef %ld has bad 2nd sidedef number %d, giving up",
+	 n, LineDefs[n].sidedef2);
+      rc = 1;
+      goto byebye;
+      }
+   if (outside (LineDefs[n].start, 0, OldNumVertices -1))
+      {
+      err ("linedef %ld has bad 1st vertex number %d, giving up",
+        n, LineDefs[n].start);
+      rc = 1;
+      goto byebye;
+      }
+   if (outside (LineDefs[n].end, 0, OldNumVertices - 1))
+      {
+      err ("linedef %ld has bad 2nd vertex number %d, giving up",
+        n, LineDefs[n].end);
+      rc = 1;
+      goto byebye;
+      }
+   }
+
+// Read LINES (Doom alpha only)
+if (yg_level_format == YGLF_ALPHA)
+   {
+   const char *lump_name = "LINES";
+   verbmsg (" lines");
+   dir = FindMasterDir (Level, lump_name);
+   if (dir)
+      {
+      if ((dir->dir.size - 4) % 36)
+	 warn ("the %s lump has a weird size. The wad might be corrupt.\n",
+	    lump_name);
+      const size_t nlines = dir->dir.size / 36;
+      NumLineDefs = nlines;
+      NumSideDefs = 2 * nlines;  // Worst case. We'll adjust later.
+      LineDefs = (LDPtr) GetFarMemory ((unsigned long) NumLineDefs
+	 * sizeof (struct LineDef));
+      SideDefs = (SDPtr) GetFarMemory ((unsigned long) NumSideDefs
+	 * sizeof (struct SideDef));
+      // Read TEXTURES
+      if (yg_texture_format != YGTF_NAMELESS)
+	 {
+	 const char *lump_name = "TEXTURES";
+	 bool success = false;
+	 ntex = 0;
+	 i32 *offset_table = 0;
+	 MDirPtr d = FindMasterDir (MasterDir, lump_name);
+	 if (! d)
+	    {
+	    warn ("%s: lump not found in directory\n", lump_name);
+	    goto textures_done;
+	    }
+	 {
+	 const Wad_file *wf = d->wadfile;
+	 wf->seek (d->dir.start);
+	 if (wf->error ())
+	    {
+	    warn ("%s: seek error\n", lump_name);
+	    goto textures_done;
+	    }
+	 i32 num;
+	 wf->read_i32 (&num);
+	 if (wf->error ())
+	    {
+	    warn ("%s: error reading texture count\n", lump_name);
+	    }
+	 if (num < 0 || num > 32767)
+	    {
+	    warn ("%s: bad texture count, giving up\n", lump_name);
+	    goto textures_done;
+	    }
+	 ntex = num;
+	 offset_table = new i32[ntex];
+	 for (size_t n = 0; n < ntex; n++)
+	    {
+	    wf->read_i32 (offset_table + n);
+	    if (wf->error ())
+	       {
+	       warn ("%s: error reading offsets table\n");
+	       goto textures_done;
+	       }
+	    }
+	 tex_list = (char *) GetMemory (ntex * WAD_TEX_NAME);
+	 for (size_t n = 0; n < ntex; n++)
+	    {
+	    const long offset = d->dir.start + offset_table[n];
+	    wf->seek (offset);
+	    if (wf->error ())
+	       {
+	       warn ("%s: seek error\n", lump_name);
+	       goto textures_done;
+	       }
+	    wf->read_bytes (tex_list + WAD_TEX_NAME * n, WAD_TEX_NAME);
+	    if (wf->error ())
+	       {
+	       warn ("%s: error reading texture names\n", lump_name);
+	       goto textures_done;
+	       }
+	    }
+	 success = true;
+	 }
+
+	 textures_done:
+	 if (offset_table != 0)
+	    delete[] offset_table;
+	 if (! success)
+	    warn ("%s: errors found, won't be able to import texture names\n",
+	       lump_name);
+	 }
+
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start + 4);
+      if (wf->error ())
+	 {
+	 err ("%s: seek error", lump_name);
+	 rc = 1;
+	 goto byebye;
+	 }
+      size_t s = 0;
+      for (size_t n = 0; n < nlines; n++)
+	 {
+	 LDPtr ld = LineDefs + n;
+	 ld->start   = wf->read_i16 ();
+	 ld->end     = wf->read_i16 ();
+	 ld->flags   = wf->read_i16 ();
+	               wf->read_i16 ();  // Unused ?
+	 ld->type    = wf->read_i16 ();
+	 ld->tag     = wf->read_i16 ();
+	               wf->read_i16 ();  // Unused ?
+	 i16 sector1 = wf->read_i16 ();
+	 i16 xofs1   = wf->read_i16 ();
+	 i16 tex1m   = wf->read_i16 ();
+	 i16 tex1u   = wf->read_i16 ();
+	 i16 tex1l   = wf->read_i16 ();
+	               wf->read_i16 ();  // Unused ?
+	 i16 sector2 = wf->read_i16 ();
+	 i16 xofs2   = wf->read_i16 ();
+	 i16 tex2m   = wf->read_i16 ();
+	 i16 tex2u   = wf->read_i16 ();
+	 i16 tex2l   = wf->read_i16 ();
+	 if (sector1 >= 0)			// Create first sidedef
+	    {
+	    ld->sidedef1 = s;
+	    SDPtr sd = SideDefs + s;
+	    sd->xoff = xofs1;
+	    sd->yoff = 0;
+	    memcpy (sd->tex1, texno_texname (tex1u), sizeof sd->tex1);
+	    memcpy (sd->tex2, texno_texname (tex1l), sizeof sd->tex2);
+	    memcpy (sd->tex3, texno_texname (tex1m), sizeof sd->tex3);
+	    sd->sector = sector1;
+	    s++;
+	    }
+	 else  // No first sidedef !
+	    ld->sidedef1 = -1;
+	 if (ld->flags & 0x04)			// Create second sidedef
+	    {
+	    ld->sidedef2 = s;
+	    SDPtr sd = SideDefs + s;
+	    sd->xoff = xofs2;
+	    sd->yoff = 0;
+	    memcpy (sd->tex1, texno_texname (tex2u), sizeof sd->tex1);
+	    memcpy (sd->tex2, texno_texname (tex2l), sizeof sd->tex2);
+	    memcpy (sd->tex3, texno_texname (tex2m), sizeof sd->tex3);
+	    sd->sector = sector2;
+	    s++;
+	    }
+	 else
+	    ld->sidedef2 = -1;
+	 if (wf->error ())
+	    {
+	    err ("%s: error reading line #%d", lump_name, int (n));
+	    rc = 1;
+	    goto byebye;
+	    }
+	 }
+      // (size_t) to silence GCC warning
+      if ((size_t) NumSideDefs > s)  // Almost always true.
+         {
+	 NumSideDefs = s;
+         SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
+	     (unsigned long) NumSideDefs * sizeof (struct SideDef));
+         }
+      if (tex_list)
+         FreeMemory (tex_list);
+      tex_list = 0;
+      ntex = 0;
+      }
+   }
+
+/* Read the vertices. If the wad has been run through a nodes
+   builder, there is a bunch of vertices at the end that are not
+   used by any linedefs. Those vertices have been created by the
+   nodes builder for the segs. We ignore them, because they're
+   useless to the level designer. However, we do NOT ignore
+   unused vertices in the middle because that's where the
+   "string art" bug came from.
+
+   Note that there is absolutely no guarantee that the nodes
+   builders add their own vertices at the end, instead of at the
+   beginning or right in the middle, AFAIK. It's just that they
+   all seem to do that (1). What if some don't ? Well, we would
+   end up with many unwanted vertices in the level data. Nothing
+   that a good CheckCrossReferences() couldn't take care of. */
+{
+verbmsg (" vertices");
+int last_used_vertex = -1;
+for (long n = 0; n < NumLineDefs; n++)
+   {
+   last_used_vertex = y_max (last_used_vertex, LineDefs[n].start);
+   last_used_vertex = y_max (last_used_vertex, LineDefs[n].end);
+   }
+NumVertices = last_used_vertex + 1;
+// This block is only here to warn me if (1) is false.
+{
+  bitvec_c vertex_used (OldNumVertices);
+  for (long n = 0; n < NumLineDefs; n++)
+     {
+     vertex_used.set (LineDefs[n].start);
+     vertex_used.set (LineDefs[n].end);
+     }
+  int unused = 0;
+  for (long n = 0; n <= last_used_vertex; n++)
+     {
+     if (! vertex_used.get (n))
+	unused++;
+     }
+  if (unused > 0)
+     {
+     warn ("this level has unused vertices in the middle.\n");
+     warn ("total %d, tail %d (%d%%), unused %d (",
+	OldNumVertices,
+	OldNumVertices - NumVertices,
+	NumVertices - unused
+	   ? 100 * (OldNumVertices - NumVertices) / (NumVertices - unused)
+	   : 0,
+	unused);
+     int first = 1;
+     for (int n = 0; n <= last_used_vertex; n++)
+        {
+	if (! vertex_used.get (n))
+	   {
+	   if (n == 0 || vertex_used.get (n - 1))
+	      {
+	      if (first)
+	         first = 0;
+	      else
+	         warn (", ");
+	      warn ("%d", n);
+	      }
+	   else if (n == last_used_vertex || vertex_used.get (n + 1))
+	      warn ("-%d", n);
+	   }
+	}
+     warn (")\n");
+     }
+}
+// Now load all the vertices except the unused ones at the end.
+if (NumVertices > 0)
+   {
+   const char *lump_name = "BUG";
+   Vertices = (VPtr) GetFarMemory ((unsigned long) NumVertices
+      * sizeof (struct Vertex));
+   if (yg_level_format == YGLF_ALPHA)  // Doom alpha
+      lump_name = "POINTS";
+   else
+      lump_name = "VERTEXES";
+   dir = FindMasterDir (Level, lump_name);
+   if (dir == 0)
+      goto vertexes_done;		// FIXME isn't that fatal ?
+   {
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (v_offset);
+   if (wf->error ())
+      {
+      err ("%s: seek error", lump_name);
+      rc = 1;
+      goto byebye;
+      }
+   MapMaxX = -32767;
+   MapMaxY = -32767;
+   MapMinX = 32767;
+   MapMinY = 32767;
+   for (long n = 0; n < NumVertices; n++)
+      {
+      i16 val;
+      wf->read_i16 (&val);
+      if (val < MapMinX)
+	 MapMinX = val;
+      if (val > MapMaxX)
+	 MapMaxX = val;
+      Vertices[n].x = val;
+      wf->read_i16 (&val);
+      if (val < MapMinY)
+	 MapMinY = val;
+      if (val > MapMaxY)
+	 MapMaxY = val;
+      Vertices[n].y = val;
+      if (wf->error ())
+	 {
+	 err ("%s: error reading vertex #%ld", lump_name, n);
+	 rc = 1;
+	 goto byebye;
+	 }
+      }
+   }
+   vertexes_done:
+   ;
+   }
+}
+
+// Ignore SEGS, SSECTORS and NODES
+
+// Read SECTORS
+{
+const char *lump_name = "SECTORS";
+verbmsg (" sectors\n");
+dir = FindMasterDir (Level, lump_name);
+if (yg_level_format != YGLF_ALPHA)
+   {
+   if (dir)
+      {
+      NumSectors = (int) (dir->dir.size / WAD_SECTOR_BYTES);
+      if ((i32) (NumSectors * WAD_SECTOR_BYTES) != dir->dir.size)
+	 warn ("the %s lump has a weird size."
+	   " The wad might be corrupt.\n", lump_name);
+      }
+   else
+      NumSectors = 0;
+   if (NumSectors > 0)
+      {
+      Sectors = (SPtr) GetFarMemory ((unsigned long) NumSectors
+	 * sizeof (struct Sector));
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+	 {
+	 err ("%s: seek error", lump_name);
+	 rc = 1;
+	 goto byebye;
+	 }
+      for (long n = 0; n < NumSectors; n++)
+	 {
+	 wf->read_i16   (&Sectors[n].floorh);
+	 wf->read_i16   (&Sectors[n].ceilh);
+	 wf->read_bytes (&Sectors[n].floort, WAD_FLAT_NAME);
+	 wf->read_bytes (&Sectors[n].ceilt,  WAD_FLAT_NAME);
+	 wf->read_i16   (&Sectors[n].light);
+	 wf->read_i16   (&Sectors[n].special);
+	 wf->read_i16   (&Sectors[n].tag);
+	 if (wf->error ())
+	    {
+	    err ("%s: error reading sector #%ld", lump_name, n);
+	    rc = 1;
+	    goto byebye;
+	    }
+	 }
+      }
+   }
+else  // Doom alpha--a wholly different SECTORS format
+   {
+   i32  *offset_table = 0;
+   i32   nsectors     = 0;
+   i32   nflatnames   = 0;
+   char *flatnames    = 0;
+   if (dir == 0)
+      {
+      warn ("%s: lump not found in directory\n", lump_name);  // FIXME fatal ?
+      goto sectors_alpha_done;
+      }
+   {
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      err ("%s: seek error", lump_name);
+      rc = 1;
+      goto byebye;
+      }
+   wf->read_i32 (&nsectors);
+   if (wf->error ())
+      {
+      err ("%s: error reading sector count", lump_name);
+      rc = 1;
+      goto byebye;
+      }
+   if (nsectors < 0)
+      {
+      warn ("Negative sector count. Clamping to 0.\n");
+      nsectors = 0;
+      }
+   NumSectors = nsectors;
+   Sectors = (SPtr) GetFarMemory ((unsigned long) NumSectors
+      * sizeof (struct Sector));
+   offset_table = new i32[nsectors];
+   for (size_t n = 0; n < (size_t) nsectors; n++)
+      wf->read_i32 (offset_table + n);
+   if (wf->error ())
+      {
+      err ("%s: error reading offsets table", lump_name);
+      rc = 1;
+      goto sectors_alpha_done;
+      }
+   // Load FLATNAME
+   {
+      const char *lump_name = "FLATNAME";
+      bool success = false;
+      MDirPtr dir2 = FindMasterDir (Level, lump_name);
+      if (dir2 == 0)
+	 {
+	 warn ("%s: lump not found in directory\n", lump_name);
+	 goto flatname_done;		// FIXME warn ?
+	 }
+      {
+      const Wad_file *wf = dir2->wadfile;
+      wf->seek (dir2->dir.start);
+      if (wf->error ())
+	 {
+	 warn ("%s: seek error\n", lump_name);
+	 goto flatname_done;
+	 }
+      wf->read_i32 (&nflatnames);
+      if (wf->error ())
+	 {
+	 warn ("%s: error reading flat name count\n", lump_name);
+	 nflatnames = 0;
+	 goto flatname_done;
+	 }
+      if (nflatnames < 0 || nflatnames > 32767)
+	 {
+	 warn ("%s: bad flat name count, giving up\n", lump_name);
+	 nflatnames = 0;
+	 goto flatname_done;
+	 }
+      else
+	 {
+	 flatnames = new char[WAD_FLAT_NAME * nflatnames];
+	 wf->read_bytes (flatnames, WAD_FLAT_NAME * nflatnames);
+	 if (wf->error ())
+	    {
+	    warn ("%s: error reading flat names\n", lump_name);
+	    nflatnames = 0;
+	    goto flatname_done;
+	    }
+	 success = true;
+	 }
+      }
+      flatname_done:
+      if (! success)
+	 warn ("%s: errors found, you'll have to do without flat names\n",
+	       lump_name);
+   }
+   for (size_t n = 0; n < (size_t) nsectors; n++)
+      {
+      wf->seek (dir->dir.start + offset_table[n]);
+      if (wf->error ())
+	 {
+	 err ("%s: seek error", lump_name);
+	 rc = 1;
+	 goto sectors_alpha_done;
+	 }
+      i16 index;
+      wf->read_i16 (&Sectors[n].floorh);
+      wf->read_i16 (&Sectors[n].ceilh );
+      wf->read_i16 (&index);
+      if (nflatnames && flatnames && index >= 0 && index < nflatnames)
+	 memcpy (Sectors[n].floort, flatnames + WAD_FLAT_NAME * index,
+	     WAD_FLAT_NAME);
+      else
+	 strcpy (Sectors[n].floort, "unknown");
+      wf->read_i16 (&index);
+      if (nflatnames && flatnames && index >= 0 && index < nflatnames)
+	 memcpy (Sectors[n].ceilt, flatnames + WAD_FLAT_NAME * index,
+	     WAD_FLAT_NAME);
+      else
+	 strcpy (Sectors[n].ceilt, "unknown");
+      wf->read_i16 (&Sectors[n].light);
+      wf->read_i16 (&Sectors[n].special);
+      wf->read_i16 (&Sectors[n].tag);
+      // Don't know what the tail is for. Ignore it.
+      if (wf->error ())
+	 {
+	 err ("%s: error reading sector #%ld", lump_name, long (n));
+	 rc = 1;
+	 goto sectors_alpha_done;
+	 }
+      }
+   }
+   
+   sectors_alpha_done:
+   if (offset_table != 0)
+      delete[] offset_table;
+   if (flatnames != 0)
+      delete[] flatnames;
+   if (rc != 0)
+      goto byebye;
+   }
+}
+
+// Read BEHAVIOR
+if (yg_level_format == YGLF_HEXEN)
+{
+const char *lump_name = "BEHAVIOR";
+verbmsg (" behavior\n");
+dir = FindMasterDir (Level, lump_name);
+if (dir)
+   {
+   BehaviorSize = (int)dir->dir.size;
+   if (BehaviorSize > 0)
+      {
+      Behavior = (u8*) GetFarMemory ((unsigned long) BehaviorSize );
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+         {
+         err ("%s: seek error", lump_name);
+         rc = 1;
+         goto byebye;
+         }
+      wf->read_bytes (Behavior, BehaviorSize);
+      if (wf->error ())
+         {
+         err ("%s: error behavior lump", lump_name);
+         rc = 1;
+         goto byebye;
+         }
+      }
+   }
+}
+
+/* Sanity checking on sidedefs: the sector must exist. I don't
+   make this a fatal error, though, because it's not exceptional
+   to find wads with unused sidedefs with a sector# of -1. Well
+   known ones include dyst3 (MAP06, MAP07, MAP08), mm (MAP16),
+   mm2 (MAP13, MAP28) and requiem (MAP03, MAP08, ...). */
+for (long n = 0; n < NumSideDefs; n++)
+   {
+   if (outside (SideDefs[n].sector, 0, NumSectors - 1))
+      warn ("sidedef %ld has bad sector number %d\n",
+	 n, SideDefs[n].sector);
+   }
+
+// Ignore REJECT and BLOCKMAP
+
+// Silly statistics
+verbmsg ("  %d things, %d vertices, %d linedefs, %d sidedefs, %d sectors\n",
+   (int) NumThings, (int) NumVertices, (int) NumLineDefs,
+   (int) NumSideDefs, (int) NumSectors);
+verbmsg ("  Map: (%d,%d)-(%d,%d)\n", MapMinX, MapMinY, MapMaxX, MapMaxY);
+
+byebye:
+if (rc != 0)
+   err ("%s: errors found, giving up", levelname);
+return rc;
+}
+
+
+
+/*
+   forget the level data
+*/
+
+void ForgetLevelData () /* SWAP! */
+{
+/* forget the things */
+ObjectsNeeded (OBJ_THINGS, 0);
+NumThings = 0;
+if (Things != 0)
+   FreeFarMemory (Things);
+Things = 0;
+things_angles++;
+things_types++;
+
+/* forget the vertices */
+ObjectsNeeded (OBJ_VERTICES, 0);
+NumVertices = 0;
+if (Vertices != 0)
+   FreeFarMemory (Vertices);
+Vertices = 0;
+
+/* forget the linedefs */
+ObjectsNeeded (OBJ_LINEDEFS, 0);
+NumLineDefs = 0;
+if (LineDefs != 0)
+   FreeFarMemory (LineDefs);
+LineDefs = 0;
+
+/* forget the sidedefs */
+ObjectsNeeded (OBJ_SIDEDEFS, 0);
+NumSideDefs = 0;
+if (SideDefs != 0)
+   FreeFarMemory (SideDefs);
+SideDefs = 0;
+
+/* forget the sectors */
+ObjectsNeeded (OBJ_SECTORS, 0);
+NumSectors = 0;
+if (Sectors != 0)
+   FreeFarMemory (Sectors);
+Sectors = 0;
+ObjectsNeeded (0);
+}
+
+
+/*
+ *	Save the level data to a pwad file
+ *	The name of the level is always obtained from
+ *	<level_name>, whether or not the level was created from
+ *	scratch.
+ *
+ *	The previous contents of the pwad file are lost. Yes, it
+ *	sucks but it's not easy to fix.
+ *
+ *	The lumps are always written in the same order, the same
+ *	as the one in the Doom iwad. The length field of the
+ *	marker lump is always set to 0. Its offset field is
+ *	always set to the offset of the first lump of the level
+ *	(THINGS).
+ *
+ *	If the level has been created by editing an existing
+ *	level and has not been changed in a way that calls for a
+ *	rebuild of the nodes, the VERTEXES, SEGS, SSECTORS,
+ *	NODES, REJECT and BLOCKMAP lumps are copied from the
+ *	original level. Otherwise, they are created with a
+ *	length of 0 bytes and an offset equal to the offset of
+ *	the previous lump plus its length.
+ *
+ *	Returns 0 on success and non-zero on failure (see errno).
+ */
+int SaveLevelData (const char *outfile, const char *level_name) /* SWAP! */
+{
+FILE   *file;
+MDirPtr dir;
+int     n;
+long	lump_offset[WAD_LL__MAX];
+size_t	lump_size[WAD_LL__MAX];
+wad_level_lump_no_t l;
+
+if (! level_name || ! levelname2levelno (level_name))
+   {
+   nf_bug ("SaveLevelData: bad level_name \"%s\", using \"E1M1\" instead.",
+       level_name);
+   level_name = "E1M1";
+   }
+DisplayMessage (-1, -1, "Saving data to \"%s\"...", outfile);
+LogMessage (": Saving data to \"%s\"...\n", outfile);
+if ((file = fopen (outfile, "wb")) == NULL)
+   {
+   char buf1[81];
+   char buf2[81];
+   y_snprintf (buf1, sizeof buf1, "Can't open \"%.64s\"", outfile);
+   y_snprintf (buf2, sizeof buf1, "for writing (%.64s)",  strerror (errno));
+   Notify (-1, -1, buf1, buf2);
+   return 1;
+   }
+
+/* Can we reuse the old nodes ? Not if this is a new level from
+   scratch or if the structure of the level has changed. If the
+   level comes from an alpha version of Doom, we can't either
+   because that version of Doom didn't have SEGS, NODES, etc. */
+bool reuse_nodes = Level
+  && ! MadeMapChanges
+  && yg_level_format != YGLF_ALPHA;
+
+int NumLumps;
+if (yg_level_format == YGLF_HEXEN)
+   NumLumps = WAD_LL__HEXEN;
+else
+   NumLumps = WAD_LL__DOOM;
+
+// Write the pwad header
+WriteBytes (file, "PWAD", 4);		// Pwad file
+file_write_i32 (file, NumLumps);	// Number of entries = 11
+file_write_i32 (file, 0);		// Fix this up later
+if (Level)
+   dir = Level->next;
+else
+   dir = 0;  // Useless except to trap accidental dereferences
+
+// The label (EnMm or MAPnm)
+l = WAD_LL_LABEL;
+lump_offset[l] = ftell (file);	// By definition
+lump_size[l]   =  0;		// By definition
+ 
+// Write the THINGS lump
+l = WAD_LL_THINGS;
+lump_offset[l] = ftell (file);
+ObjectsNeeded (OBJ_THINGS, 0);
+for (n = 0; n < NumThings; n++)
+   {
+   if (yg_level_format == YGLF_HEXEN)
+      {
+      file_write_i16 (file, Things[n].tid  );
+      file_write_i16 (file, Things[n].xpos );
+      file_write_i16 (file, Things[n].ypos );
+      file_write_i16 (file, Things[n].height);
+      file_write_i16 (file, Things[n].angle);
+      file_write_i16 (file, Things[n].type );
+      file_write_i16 (file, Things[n].when );
+      WriteBytes     (file, &Things[n].special, 1);
+      WriteBytes     (file, &Things[n].arg1, 1 );
+      WriteBytes     (file, &Things[n].arg2, 1 );
+      WriteBytes     (file, &Things[n].arg3, 1 );
+      WriteBytes     (file, &Things[n].arg4, 1 );
+      WriteBytes     (file, &Things[n].arg5, 1 );
+	  }
+   else
+      {
+      file_write_i16 (file, Things[n].xpos );
+      file_write_i16 (file, Things[n].ypos );
+      file_write_i16 (file, Things[n].angle);
+      file_write_i16 (file, Things[n].type );
+      file_write_i16 (file, Things[n].when );
+      }
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+// Write the LINEDEFS lump
+l = WAD_LL_LINEDEFS;
+lump_offset[WAD_LL_LINEDEFS] = ftell (file);
+ObjectsNeeded (OBJ_LINEDEFS, 0);
+for (n = 0; n < NumLineDefs; n++)
+   {
+   if (yg_level_format == YGLF_HEXEN)
+      {
+      u8 dummy[6];
+      dummy[0] = LineDefs[n].type;
+      dummy[1] = LineDefs[n].tag;
+      dummy[2] = LineDefs[n].arg2;
+      dummy[3] = LineDefs[n].arg3;
+      dummy[4] = LineDefs[n].arg4;
+      dummy[5] = LineDefs[n].arg5;
+      file_write_i16 (file, LineDefs[n].start   );
+      file_write_i16 (file, LineDefs[n].end     );
+      file_write_i16 (file, LineDefs[n].flags   );
+      WriteBytes     (file, dummy, 6);
+      file_write_i16 (file, LineDefs[n].sidedef1);
+      file_write_i16 (file, LineDefs[n].sidedef2);
+	  }
+   else
+      {
+      file_write_i16 (file, LineDefs[n].start   );
+      file_write_i16 (file, LineDefs[n].end     );
+      file_write_i16 (file, LineDefs[n].flags   );
+      file_write_i16 (file, LineDefs[n].type    );
+      file_write_i16 (file, LineDefs[n].tag     );
+      file_write_i16 (file, LineDefs[n].sidedef1);
+      file_write_i16 (file, LineDefs[n].sidedef2);
+	  }
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+// Write the SIDEDEFS lump
+l = WAD_LL_SIDEDEFS;
+lump_offset[l] = ftell (file);
+ObjectsNeeded (OBJ_SIDEDEFS, 0);
+for (n = 0; n < NumSideDefs; n++)
+   {
+   file_write_i16 (file, SideDefs[n].xoff);
+   file_write_i16 (file, SideDefs[n].yoff);
+   WriteBytes     (file, &(SideDefs[n].tex1), WAD_TEX_NAME);
+   WriteBytes     (file, &(SideDefs[n].tex2), WAD_TEX_NAME);
+   WriteBytes     (file, &(SideDefs[n].tex3), WAD_TEX_NAME);
+   file_write_i16 (file, SideDefs[n].sector);
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+// Write the VERTEXES lump
+l = WAD_LL_VERTEXES;
+lump_offset[WAD_LL_VERTEXES] = ftell (file);
+if (reuse_nodes)
+   {
+   /* Copy the vertices */
+   ObjectsNeeded (0);
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      warn ("%s: seek error\n", wad_level_lump[l]);
+      }
+   copy_bytes (file, wf->fp, dir->dir.size);
+   }
+else
+   {
+   /* Write the vertices */
+   ObjectsNeeded (OBJ_VERTICES, 0);
+   for (n = 0; n < NumVertices; n++)
+      {
+      file_write_i16 (file, Vertices[n].x);
+      file_write_i16 (file, Vertices[n].y);
+      }
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+// Write the SEGS, SSECTORS and NODES lumps
+for (n = 0; n < 3; n++)
+   {
+   if (n == 0)
+      l = WAD_LL_SEGS;
+   else if (n == 1)
+      l = WAD_LL_SSECTORS;
+   else if (n == 2)
+      l = WAD_LL_NODES;
+   lump_offset[l] = ftell (file);
+   if (reuse_nodes)
+      {
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+	 {
+	 warn ("%s: seek error\n", wad_level_lump[l]);
+	 }
+      copy_bytes (file, wf->fp, dir->dir.size);
+      }
+   lump_size[l] = ftell (file) - lump_offset[l];
+   if (Level)
+      dir = dir->next;
+   }
+
+// Write the SECTORS lump
+l = WAD_LL_SECTORS;
+lump_offset[l] = ftell (file);
+ObjectsNeeded (OBJ_SECTORS, 0);
+for (n = 0; n < NumSectors; n++)
+   {
+   file_write_i16 (file, Sectors[n].floorh);
+   file_write_i16 (file, Sectors[n].ceilh );
+   WriteBytes     (file, Sectors[n].floort, WAD_FLAT_NAME);
+   WriteBytes     (file, Sectors[n].ceilt,  WAD_FLAT_NAME);
+   file_write_i16 (file, Sectors[n].light  );
+   file_write_i16 (file, Sectors[n].special);
+   file_write_i16 (file, Sectors[n].tag    );
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+// Write the REJECT lump
+l = WAD_LL_REJECT;
+lump_offset[l] = ftell (file);
+if (reuse_nodes)
+   {
+   /* Copy the REJECT data */
+   ObjectsNeeded (0);
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      warn ("%s: seek error\n", wad_level_lump[l]);
+      }
+   copy_bytes (file, wf->fp, dir->dir.size);
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+// Write the BLOCKMAP lump
+l = WAD_LL_BLOCKMAP;
+lump_offset[l] = ftell (file);
+if (reuse_nodes)
+   {
+   ObjectsNeeded (0);
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      warn ("%s: seek error\n", wad_level_lump[l]);
+      }
+   copy_bytes (file, wf->fp, dir->dir.size);
+   }
+lump_size[l] = ftell (file) - lump_offset[l];
+if (Level)
+   dir = dir->next;
+
+ 
+// Write the BEHAVIOR lump
+if (yg_level_format == YGLF_HEXEN)
+{
+   l = WAD_LL_BEHAVIOR;
+   lump_offset[l] = ftell (file);
+   WriteBytes(file, Behavior, BehaviorSize);
+   lump_size[l] = BehaviorSize;
+   if (Level)
+      dir = dir->next;
+}
+
+// Write the actual directory
+long dir_offset = ftell (file);
+for (int L = 0; L < (int) NumLumps; L++)
+   {
+   file_write_i32 (file, lump_offset[L]);
+   file_write_i32 (file, lump_size[L]);
+   if (L == (int) WAD_LL_LABEL)
+      file_write_name (file, level_name);
+   else
+      file_write_name (file, wad_level_lump[L].name);
+   }
+
+/* Fix up the directory start information */
+if (fseek (file, 8, SEEK_SET))
+   {
+   char buf1[81];
+   char buf2[81];
+   y_snprintf (buf1, sizeof buf1, "%.64s: seek error", outfile);
+   y_snprintf (buf2, sizeof buf2, "(%.64s)",           strerror (errno));
+   Notify (-1, -1, buf1, buf2);
+   fclose (file);
+   return 1;
+   }
+file_write_i32 (file, dir_offset);
+
+/* Close the file */
+if (fclose (file))
+   {
+   char buf1[81];
+   char buf2[81];
+   y_snprintf (buf1, sizeof buf1, "%.64s: write error", outfile);
+   y_snprintf (buf2, sizeof buf2, "(%.64s)",            strerror (errno));
+   Notify (-1, -1, buf1, buf2);
+   return 1;
+   }
+
+/* The file is now up to date */
+if (! Level || MadeMapChanges)
+   remind_to_build_nodes = 1;
+MadeChanges = 0;
+MadeMapChanges = 0;
+ObjectsNeeded (0);
+
+/* Update pointers in Master Directory */
+OpenPatchWad (outfile);
+
+/* This should free the old "*.bak" file */
+CloseUnusedWadFiles ();
+
+/* Update MapMinX, MapMinY, MapMaxX, MapMaxY */
+// Probably not necessary anymore -- AYM 1999-04-05
+ObjectsNeeded (OBJ_VERTICES, 0);
+update_level_bounds ();
+return 0;
+}
+
+
+/*
+ *	flat_list_entry_cmp
+ *	Function used by qsort() to sort the flat_list_entry array
+ *	by ascending flat name.
+ */
+static int flat_list_entry_cmp (const void *a, const void *b)
+{
+return y_strnicmp (
+    ((const flat_list_entry_t *) a)->name,
+    ((const flat_list_entry_t *) b)->name,
+    WAD_FLAT_NAME);
+}
+
+
+/*
+   function used by qsort to sort the texture names
+*/
+static int SortTextures (const void *a, const void *b)
+{
+return y_strnicmp (*((const char *const *)a), *((const char *const *)b),
+    WAD_TEX_NAME);
+}
+
+
+/*
+   read the texture names
+*/
+void ReadWTextureNames ()
+{
+MDirPtr dir;
+int n;
+i32 val;
+
+verbmsg ("Reading texture names\n");
+
+// Doom alpha 0.4 : "TEXTURES", no names
+if (yg_texture_lumps == YGTL_TEXTURES
+ && yg_texture_format == YGTF_NAMELESS)
+   {
+   const char *lump_name = "TEXTURES";
+   dir = FindMasterDir (MasterDir, lump_name);
+   if (dir == NULL)
+      {
+      warn ("%s: lump not found in directory\n", lump_name);
+      goto textures04_done;
+      }
+   {
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      warn ("%s: seek error\n", lump_name);
+      goto textures04_done;
+      }
+   wf->read_i32 (&val);
+   if (wf->error ())
+      {
+      warn ("%s: error reading texture count\n", lump_name);
+      goto textures04_done;
+      }
+   NumWTexture = (int) val + 1;
+   WTexture = (char **) GetMemory ((long) NumWTexture * sizeof *WTexture);
+   WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
+   strcpy (WTexture[0], "-");
+   if (WAD_TEX_NAME < 7) nf_bug ("WAD_TEX_NAME too small");  // Sanity
+   for (long n = 0; n < val; n++)
+      {
+      WTexture[n + 1] = (char *) GetMemory (WAD_TEX_NAME + 1);
+      if (n > 9999)
+	 {
+	 warn ("more than 10,000 textures. Ignoring excess.\n");
+	 break;
+	 }
+      sprintf (WTexture[n + 1], "TEX%04ld", n);
+      }
+   }
+   textures04_done:
+   ;
+   }
+
+// Doom alpha 0.5 : only "TEXTURES"
+else if (yg_texture_lumps == YGTL_TEXTURES
+      && (yg_texture_format == YGTF_NORMAL
+	  || yg_texture_format == YGTF_STRIFE11))
+   {
+   const char *lump_name = "TEXTURES";
+   i32 *offsets = 0;
+   dir = FindMasterDir (MasterDir, lump_name);
+   if (dir == NULL)  // In theory it always exists, though
+      {
+      warn ("%s: lump not found in directory\n", lump_name);
+      goto textures05_done;
+      }
+   {
+   const Wad_file *wf = dir->wadfile;
+   wf->seek (dir->dir.start);
+   if (wf->error ())
+      {
+      warn ("%s: seek error\n", lump_name);
+      goto textures05_done;
+      }
+   wf->read_i32 (&val);
+   if (wf->error ())
+      {
+      warn ("%s: error reading texture count\n", lump_name);
+      goto textures05_done;
+      }
+   NumWTexture = (int) val + 1;
+   /* read in the offsets for texture1 names */
+   offsets = (i32 *) GetMemory ((long) NumWTexture * 4);
+   wf->read_i32 (offsets + 1, NumWTexture - 1);
+   if (wf->error ())
+      {
+      warn ("%s: error reading offsets table\n", lump_name);
+      goto textures05_done;
+      }
+   /* read in the actual names */
+   WTexture = (char **) GetMemory ((long) NumWTexture * sizeof (char *));
+   WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
+   strcpy (WTexture[0], "-");
+   for (n = 1; n < NumWTexture; n++)
+      {
+      WTexture[n] = (char *) GetMemory (WAD_TEX_NAME + 1);
+      long offset = dir->dir.start + offsets[n];
+      wf->seek (offset);
+      if (wf->error ())
+	 {
+	 warn ("%s: error seeking to  error\n", lump_name);
+	 goto textures05_done;		// FIXME cleanup
+	 }
+      wf->read_bytes (WTexture[n], WAD_TEX_NAME);
+      if (wf->error ())
+	 {
+	 warn ("%s: error reading texture names\n", lump_name);
+	 goto textures05_done;		// FIXME cleanup
+	 }
+      WTexture[n][WAD_TEX_NAME] = '\0';
+      }
+   }
+   textures05_done:
+   if (offsets != 0)
+      FreeMemory (offsets);
+   }
+// Other iwads : "TEXTURE1" and possibly "TEXTURE2"
+else if (yg_texture_lumps == YGTL_NORMAL
+      && (yg_texture_format == YGTF_NORMAL
+	  || yg_texture_format == YGTF_STRIFE11))
+   {
+   const char *lump_name = "TEXTURE1";
+   i32 *offsets = 0;
+   dir = FindMasterDir (MasterDir, lump_name);
+   if (dir != NULL)  // In theory it always exists, though
+      {
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+	 {
+	 warn ("%s: seek error\n", lump_name);
+	 // FIXME
+	 }
+      wf->read_i32 (&val);
+      if (wf->error ())
+      {
+	// FIXME
+      }
+      NumWTexture = (int) val + 1;
+      /* read in the offsets for texture1 names */
+      offsets = (i32 *) GetMemory ((long) NumWTexture * 4);
+      wf->read_i32 (offsets + 1, NumWTexture - 1);
+      {
+	// FIXME
+      }
+      /* read in the actual names */
+      WTexture = (char **) GetMemory ((long) NumWTexture * sizeof (char *));
+      WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
+      strcpy (WTexture[0], "-");
+      for (n = 1; n < NumWTexture; n++)
+	 {
+	 WTexture[n] = (char *) GetMemory (WAD_TEX_NAME + 1);
+	 wf->seek (dir->dir.start + offsets[n]);
+	 if (wf->error ())
+	    {
+	    warn ("%s: seek error\n", lump_name);
+	    // FIXME
+	    }
+	 wf->read_bytes (WTexture[n], WAD_TEX_NAME);
+	 if (wf->error ())
+	    {
+	      // FIXME
+	    }
+	 WTexture[n][WAD_TEX_NAME] = '\0';
+	 }
+      FreeMemory (offsets);
+      }
+   {
+   dir = FindMasterDir (MasterDir, "TEXTURE2");
+   if (dir)  /* Doom II has no TEXTURE2 */
+      {
+      const Wad_file *wf = dir->wadfile;
+      wf->seek (dir->dir.start);
+      if (wf->error ())
+	 {
+	 warn ("%s: seek error\n", lump_name);
+	 // FIXME
+	 }
+      wf->read_i32 (&val);
+      if (wf->error ())
+      {
+	// FIXME
+      }
+      /* read in the offsets for texture2 names */
+      offsets = (i32 *) GetMemory ((long) val * 4);
+      wf->read_i32 (offsets, val);
+      if (wf->error ())
+      {
+	// FIXME
+      }
+      /* read in the actual names */
+      WTexture = (char **) ResizeMemory (WTexture,
+	 (NumWTexture + val) * sizeof (char *));
+      for (n = 0; n < val; n++)
+	 {
+	 WTexture[NumWTexture + n] = (char *) GetMemory (WAD_TEX_NAME + 1);
+	 wf->seek (dir->dir.start + offsets[n]);
+	 if (wf->error ())
+	    {
+	    warn ("%s: seek error\n", lump_name);
+	    // FIXME
+	    }
+	 wf->read_bytes (WTexture[NumWTexture + n], WAD_TEX_NAME);
+	 if (wf->error ())
+	   ; // FIXME
+	 WTexture[NumWTexture + n][WAD_TEX_NAME] = '\0';
+	 }
+      NumWTexture += val;
+      FreeMemory (offsets);
+      }
+   }
+   }
+else
+   nf_bug ("Invalid texture_format/texture_lumps combination.");
+
+/* sort the names */
+qsort (WTexture, NumWTexture, sizeof (char *), SortTextures);
+}
+
+
+
+/*
+   forget the texture names
+*/
+
+void ForgetWTextureNames ()
+{
+int n;
+
+/* forget all names */
+for (n = 0; n < NumWTexture; n++)
+   FreeMemory (WTexture[n]);
+
+/* forget the array */
+NumWTexture = 0;
+FreeMemory (WTexture);
+}
+
+
+
+/*
+   read the flat names
+*/
+
+void ReadFTextureNames ()
+{
+MDirPtr dir;
+int n;
+
+verbmsg ("Reading flat names");
+NumFTexture = 0;
+
+for (dir = MasterDir; (dir = FindMasterDir (dir, "F_START", "FF_START"));)
+   {
+   bool ff_start = ! y_strnicmp (dir->dir.name, "FF_START", WAD_NAME);
+   MDirPtr dir0;
+   /* count the names */
+   dir = dir->next;
+   dir0 = dir;
+   for (n = 0; dir && y_strnicmp (dir->dir.name, "F_END", WAD_NAME)
+	 && (! ff_start || y_strnicmp (dir->dir.name, "FF_END", WAD_NAME));
+	 dir = dir->next)
+      {
+      if (dir->dir.start == 0 || dir->dir.size == 0)
+	 {
+	 if (! (toupper (dir->dir.name[0]) == 'F'
+	     && (dir->dir.name[1] == '1'
+	      || dir->dir.name[1] == '2'
+	      || dir->dir.name[1] == '3'
+	      || toupper (dir->dir.name[1]) == 'F')
+	     && dir->dir.name[2] == '_'
+	     && (! y_strnicmp (dir->dir.name + 3, "START", WAD_NAME - 3)
+		 || ! y_strnicmp (dir->dir.name + 3, "END", WAD_NAME - 3))))
+	    warn ("unexpected label \"%.*s\" among flats.\n",
+		  WAD_NAME, dir->dir.name);
+	 continue;
+	 }
+      if (dir->dir.size != 4096)
+	 warn ("flat \"%.*s\" has weird size %lu."
+	     " Using 4096 instead.\n",
+	       WAD_NAME, dir->dir.name, (unsigned long) dir->dir.size);
+      n++;
+      }
+   /* If FF_START/FF_END followed by F_END (mm2.wad), advance
+      past F_END. In fact, this does not work because the F_END
+      that follows has been snatched by OpenPatchWad(), that
+      thinks it replaces the F_END from the iwad. OpenPatchWad()
+      needs to be kludged to take this special case into
+      account. Fortunately, the only consequence is a useless
+      "this wad uses FF_END" warning. -- AYM 1999-07-10 */
+   if (ff_start && dir && ! y_strnicmp (dir->dir.name, "FF_END", WAD_NAME))
+      if (dir->next && ! y_strnicmp (dir->next->dir.name, "F_END", WAD_NAME))
+         dir = dir->next;
+
+   verbmsg (" FF_START/%s %d", dir->dir.name, n);
+   if (dir && ! y_strnicmp (dir->dir.name, "FF_END", WAD_NAME))
+      warn ("this wad uses FF_END. That won't work with Doom."
+	 " Use F_END instead.\n");
+   /* get the actual names from master dir. */
+   flat_list = (flat_list_entry_t *) ResizeMemory (flat_list,
+      (NumFTexture + n) * sizeof *flat_list);
+   for (size_t m = NumFTexture; m < NumFTexture + n; dir0 = dir0->next)
+      {
+      // Skip all labels.
+      if (dir0->dir.start == 0
+	 || dir0->dir.size == 0
+	 || (toupper (dir0->dir.name[0]) == 'F'
+	     && (dir0->dir.name[1] == '1'
+	      || dir0->dir.name[1] == '2'
+	      || dir0->dir.name[1] == '3'
+	      || toupper (dir0->dir.name[1]) == 'F')
+	     && dir0->dir.name[2] == '_'
+	     && (! y_strnicmp (dir0->dir.name + 3, "START", WAD_NAME - 3)
+		 || ! y_strnicmp (dir0->dir.name + 3, "END", WAD_NAME - 3))
+	     ))
+	 continue;
+      *flat_list[m].name = '\0';
+      strncat (flat_list[m].name, dir0->dir.name, sizeof flat_list[m].name - 1);
+      flat_list[m].wadfile = dir0->wadfile;
+      flat_list[m].offset  = dir0->dir.start;
+      m++;
+      }
+   NumFTexture += n;
+   }
+
+verbmsg ("\n");
+
+/* sort the flats by names */
+qsort (flat_list, NumFTexture, sizeof *flat_list, flat_list_entry_cmp);
+
+/* Eliminate all but the last duplicates of a flat. Suboptimal.
+   Would be smarter to start by the end. */
+for (size_t n = 0; n < NumFTexture; n++)
+   {
+   size_t m = n;
+   while (m + 1 < NumFTexture
+     && ! flat_list_entry_cmp (flat_list + n, flat_list + m + 1))
+       m++;
+   // m now contains the index of the last duplicate
+   int nduplicates = m - n;
+   if (nduplicates > 0)
+      {
+      memmove (flat_list + n, flat_list + m,
+                                      (NumFTexture - m) * sizeof *flat_list);
+      NumFTexture -= nduplicates;
+      // Note that I'm too lazy to resize flat_list...
+      }
+   }
+}
+
+
+/*
+ *	is_flat_name_in_list
+ *	FIXME should use bsearch()
+ */
+int is_flat_name_in_list (const char *name)
+{
+  if (! flat_list)
+    return 0;
+  for (size_t n = 0; n < NumFTexture; n++)
+    if (! y_strnicmp (name, flat_list[n].name, WAD_FLAT_NAME))
+      return 1;
+  return 0;
+}
+
+
+/*
+   forget the flat names
+*/
+
+void ForgetFTextureNames ()
+{
+NumFTexture = 0;
+FreeMemory (flat_list);
+flat_list = 0;
+}
+
+
+/*
+ *	update_level_bounds - update Map{Min,Max}{X,Y}
+ */
+void update_level_bounds ()
+{
+MapMaxX = -32767;
+MapMaxY = -32767;
+MapMinX = 32767;
+MapMinY = 32767;
+for (obj_no_t n = 0; n < NumVertices; n++)
+   {
+   int x = Vertices[n].x;
+   if (x < MapMinX)
+      MapMinX = x;
+   if (x > MapMaxX)
+      MapMaxX = x;
+   int y = Vertices[n].y;
+   if (y < MapMinY)
+      MapMinY = y;
+   if (y > MapMaxY)
+      MapMaxY = y;
+   }
+}
+
diff -Naur yadex-1.7.0.orig/src/levels.h yadex-1.7.0.allpatches/src/levels.h
--- yadex-1.7.0.orig/src/levels.h	2001-10-13 16:57:33.000000000 +0200
+++ yadex-1.7.0.allpatches/src/levels.h	2013-01-10 17:25:53.196909491 +0100
@@ -28,6 +28,8 @@
 extern int   NumSegs;		/* number of segments */
 extern int   NumSectors;	/* number of sectors */
 extern SPtr  Sectors;		/* sectors data */
+extern u8*   Behavior;
+extern int   BehaviorSize;
 
 // FIXME should be somewhere else
 extern int   NumWTexture;	/* number of wall textures */
diff -Naur yadex-1.7.0.orig/src/l_prop.cc yadex-1.7.0.allpatches/src/l_prop.cc
--- yadex-1.7.0.orig/src/l_prop.cc	2013-01-10 17:24:11.556024006 +0100
+++ yadex-1.7.0.allpatches/src/l_prop.cc	2013-01-10 17:25:53.196909491 +0100
@@ -111,7 +111,7 @@
 
 void LinedefProperties (int x0, int y0, SelPtr obj)
 {
-  char  *menustr[8];
+  char  *menustr[12];
   char   texname[WAD_TEX_NAME + 1];
   int    n, val;
   SelPtr cur, sdlist;
@@ -132,9 +132,9 @@
   switch (val)
   {
     case 1:
-      for (n = 0; n < 8; n++)
+      for (n = 0; n < 12; n++)
 	menustr[n] = (char *) GetMemory (60);
-      sprintf (menustr[7], "Edit linedef #%d", obj->objnum);
+      sprintf (menustr[11], "Edit linedef #%d", obj->objnum);
       sprintf (menustr[0], "Change flags            (Current: %d)",
 	LineDefs[obj->objnum].flags);
       sprintf (menustr[1], "Change type             (Current: %d)",
@@ -149,7 +149,16 @@
 	LineDefs[obj->objnum].sidedef1);
       sprintf (menustr[6], "Change 2nd sidedef ref. (Current: #%d)",
 	LineDefs[obj->objnum].sidedef2);
-      val = vDisplayMenu (x0 + 42, subwin_y0, menustr[7],
+      sprintf (menustr[7], "Change special arg2     (Current: %d)",
+	LineDefs[obj->objnum].arg2);
+      sprintf (menustr[8], "Change special arg3     (Current: %d)",
+	LineDefs[obj->objnum].arg3);
+      sprintf (menustr[9], "Change special arg4     (Current: %d)",
+	LineDefs[obj->objnum].arg4);
+      sprintf (menustr[10], "Change special arg5     (Current: %d)",
+	LineDefs[obj->objnum].arg5);
+      if (yg_level_format == YGLF_HEXEN)
+         val = vDisplayMenu (x0 + 42, subwin_y0, menustr[11],
 	menustr[0], YK_, 0,
 	menustr[1], YK_, 0,
 	menustr[2], YK_, 0,
@@ -157,8 +166,22 @@
 	menustr[4], YK_, 0,
 	menustr[5], YK_, 0,
 	menustr[6], YK_, 0,
+	menustr[7], YK_, 0,
+	menustr[8], YK_, 0,
+	menustr[9], YK_, 0,
+	menustr[10], YK_, 0,
 	NULL);
-      for (n = 0; n < 8; n++)
+	  else
+         val = vDisplayMenu (x0 + 42, subwin_y0, menustr[11],
+	menustr[0], YK_, 0,
+	menustr[1], YK_, 0,
+	menustr[2], YK_, 0,
+	menustr[3], YK_, 0,
+	menustr[4], YK_, 0,
+	menustr[5], YK_, 0,
+	menustr[6], YK_, 0,
+	NULL);
+      for (n = 0; n < 12; n++)
 	FreeMemory (menustr[n]);
       subsubwin_y0 = subwin_y0 + BOX_BORDER + (2 + val) * FONTH;
       switch (val)
@@ -269,6 +292,50 @@
 	    MadeMapChanges = 1;
 	  }
 	  break;
+
+	case 8:
+	  val = InputIntegerValue (x0 + 84, subsubwin_y0,
+	    0, 255, LineDefs[obj->objnum].arg2);
+	  if (val != IIV_CANCEL)  // Not [esc]
+	  {
+	    for (cur = obj; cur; cur = cur->next)
+	      LineDefs[cur->objnum].arg2 = val;
+	    MadeChanges = 1;
+	  }
+	  break;
+
+	case 9:
+	  val = InputIntegerValue (x0 + 84, subsubwin_y0,
+	    0, 255, LineDefs[obj->objnum].arg3);
+	  if (val != IIV_CANCEL)  // Not [esc]
+	  {
+	    for (cur = obj; cur; cur = cur->next)
+	      LineDefs[cur->objnum].arg3 = val;
+	    MadeChanges = 1;
+	  }
+	  break;
+
+	case 10:
+	  val = InputIntegerValue (x0 + 84, subsubwin_y0,
+	    0, 255, LineDefs[obj->objnum].arg4);
+	  if (val != IIV_CANCEL)  // Not [esc]
+	  {
+	    for (cur = obj; cur; cur = cur->next)
+	      LineDefs[cur->objnum].arg4 = val;
+	    MadeChanges = 1;
+	  }
+	  break;
+
+	case 11:
+	  val = InputIntegerValue (x0 + 84, subsubwin_y0,
+	    0, 255, LineDefs[obj->objnum].arg5);
+	  if (val != IIV_CANCEL)  // Not [esc]
+	  {
+	    for (cur = obj; cur; cur = cur->next)
+	      LineDefs[cur->objnum].arg5 = val;
+	    MadeChanges = 1;
+	  }
+	  break;
      }
      break;
 
diff -Naur yadex-1.7.0.orig/src/objects.cc yadex-1.7.0.allpatches/src/objects.cc
--- yadex-1.7.0.orig/src/objects.cc	2013-01-10 17:24:11.579024205 +0100
+++ yadex-1.7.0.allpatches/src/objects.cc	2013-01-10 17:25:53.197909500 +0100
@@ -465,12 +465,28 @@
 	 Things[last].type  = Things[copyfrom].type;
 	 Things[last].angle = Things[copyfrom].angle;
 	 Things[last].when  = Things[copyfrom].when;
+	 Things[last].tid   = Things[copyfrom].tid;
+	 Things[last].height = Things[copyfrom].height;
+	 Things[last].special = Things[copyfrom].special;
+	 Things[last].arg1  = Things[copyfrom].arg1;
+	 Things[last].arg2  = Things[copyfrom].arg2;
+	 Things[last].arg3  = Things[copyfrom].arg3;
+	 Things[last].arg4  = Things[copyfrom].arg4;
+	 Things[last].arg5  = Things[copyfrom].arg5;
 	 }
       else
 	 {
 	 Things[last].type = default_thing;
 	 Things[last].angle = 0;
 	 Things[last].when  = 0x07;
+	 Things[last].tid   = 0;
+	 Things[last].height = 0;
+	 Things[last].special = 0;
+	 Things[last].arg1  = 0;
+	 Things[last].arg2  = 0;
+	 Things[last].arg3  = 0;
+	 Things[last].arg4  = 0;
+	 Things[last].arg5  = 0;
 	 }
       break;
 
@@ -508,6 +524,10 @@
 	 LineDefs[last].flags = LineDefs[copyfrom].flags;
 	 LineDefs[last].type  = LineDefs[copyfrom].type;
 	 LineDefs[last].tag   = LineDefs[copyfrom].tag;
+	 LineDefs[last].arg2  = LineDefs[copyfrom].arg2;
+	 LineDefs[last].arg3  = LineDefs[copyfrom].arg3;
+	 LineDefs[last].arg4  = LineDefs[copyfrom].arg4;
+	 LineDefs[last].arg5  = LineDefs[copyfrom].arg5;
 	 }
       else
 	 {
@@ -516,6 +536,10 @@
 	 LineDefs[last].flags = 1;
 	 LineDefs[last].type  = 0;
 	 LineDefs[last].tag   = 0;
+	 LineDefs[last].arg2  = 0;
+	 LineDefs[last].arg3  = 0;
+	 LineDefs[last].arg4  = 0;
+	 LineDefs[last].arg5  = 0;
 	 }
       LineDefs[last].sidedef1 = OBJ_NO_NONE;
       LineDefs[last].sidedef2 = OBJ_NO_NONE;
diff -Naur yadex-1.7.0.orig/src/objects.cc.orig yadex-1.7.0.allpatches/src/objects.cc.orig
--- yadex-1.7.0.orig/src/objects.cc.orig	1970-01-01 01:00:00.000000000 +0100
+++ yadex-1.7.0.allpatches/src/objects.cc.orig	2013-01-10 17:25:53.198909508 +0100
@@ -0,0 +1,1252 @@
+/*
+ *	objects.cc
+ *	Object handling routines.
+ *	BW & RQ sometime in 1993 or 1994.
+ */
+
+
+/*
+This file is part of Yadex.
+
+Yadex incorporates code from DEU 5.21 that was put in the public domain in
+1994 by RaphaŽl Quinet and Brendon Wyber.
+
+The rest of Yadex is Copyright © 1997-2003 Andrť Majorel and others.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, 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 License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include "yadex.h"
+#include "drawmap.h"
+#include "gfx.h"
+#include "l_vertices.h"
+#include "levels.h"
+#include "objects.h"
+#include "objid.h"
+#include "s_vertices.h"
+#include "selectn.h"
+#include "things.h"
+
+
+/*
+   highlight the selected objects
+*/
+void HighlightSelection (int objtype, SelPtr list) /* SWAP! */
+{
+SelPtr cur;
+
+if (! list)
+   return;
+for (cur = list; cur; cur = cur->next)
+   HighlightObject (objtype, cur->objnum, GREEN);
+}
+
+
+
+/*
+   get the number of objects of a given type minus one
+*/
+obj_no_t GetMaxObjectNum (int objtype)
+{
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      return NumThings - 1;
+   case OBJ_LINEDEFS:
+      return NumLineDefs - 1;
+   case OBJ_SIDEDEFS:
+      return NumSideDefs - 1;
+   case OBJ_VERTICES:
+      return NumVertices - 1;
+   case OBJ_SECTORS:
+      return NumSectors - 1;
+   }
+return -1;
+}
+
+
+/*
+   highlight the selected object
+*/
+void HighlightObject (int objtype, int objnum, int colour) /* SWAP! */
+{
+int  n, m;
+
+/* use XOR mode : drawing any line twice erases it */
+SetDrawingMode (1);
+set_colour (colour);
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      ObjectsNeeded (OBJ_THINGS, 0);
+      m = (get_thing_radius (Things[objnum].type) * 3) / 2;
+      DrawMapLine (Things[objnum].xpos - m, Things[objnum].ypos - m,
+		   Things[objnum].xpos - m, Things[objnum].ypos + m);
+      DrawMapLine (Things[objnum].xpos - m, Things[objnum].ypos + m,
+		   Things[objnum].xpos + m, Things[objnum].ypos + m);
+      DrawMapLine (Things[objnum].xpos + m, Things[objnum].ypos + m,
+		   Things[objnum].xpos + m, Things[objnum].ypos - m);
+      DrawMapLine (Things[objnum].xpos + m, Things[objnum].ypos - m,
+		   Things[objnum].xpos - m, Things[objnum].ypos - m);
+      DrawMapArrow (Things[objnum].xpos, Things[objnum].ypos,
+		    Things[objnum].angle * 182);
+      break;
+
+   case OBJ_LINEDEFS:
+      ObjectsNeeded (OBJ_LINEDEFS, OBJ_VERTICES, 0);
+      n = (Vertices[LineDefs[objnum].start].x
+	 + Vertices[LineDefs[objnum].end].x) / 2;
+      m = (Vertices[LineDefs[objnum].start].y
+	 + Vertices[LineDefs[objnum].end].y) / 2;
+      DrawMapLine (n, m, n + (Vertices[LineDefs[objnum].end].y
+			    - Vertices[LineDefs[objnum].start].y) / 3,
+			 m + (Vertices[LineDefs[objnum].start].x
+			    - Vertices[LineDefs[objnum].end].x) / 3);
+      SetLineThickness (1);
+      DrawMapVector (Vertices[LineDefs[objnum].start].x,
+		     Vertices[LineDefs[objnum].start].y,
+		     Vertices[LineDefs[objnum].end].x,
+		     Vertices[LineDefs[objnum].end].y);
+      if (colour != LIGHTRED && LineDefs[objnum].tag > 0)
+	 {
+	 for (m = 0; m < NumSectors; m++)
+	    if (Sectors[m].tag == LineDefs[objnum].tag)
+	       HighlightObject (OBJ_SECTORS, m, LIGHTRED);
+	 }
+      SetLineThickness (0);
+      break;
+
+   case OBJ_VERTICES:
+      ObjectsNeeded (OBJ_VERTICES, 0);
+      {
+      int r = vertex_radius (Scale) * 3 / 2;
+      int scrx0 = SCREENX (Vertices[objnum].x) - r;
+      int scrx9 = SCREENX (Vertices[objnum].x) + r;
+      int scry0 = SCREENY (Vertices[objnum].y) - r;
+      int scry9 = SCREENY (Vertices[objnum].y) + r;
+      DrawScreenLine (scrx0, scry0, scrx9, scry0);
+      DrawScreenLine (scrx9, scry0, scrx9, scry9);
+      DrawScreenLine (scrx9, scry9, scrx0, scry9);
+      DrawScreenLine (scrx0, scry9, scrx0, scry0);
+      }
+      break;
+
+   case OBJ_SECTORS:
+      {
+      ObjectsNeeded (OBJ_LINEDEFS, OBJ_SIDEDEFS, OBJ_VERTICES, 0);
+      SetLineThickness (1);
+      const int mapx0 = MAPX (0);
+      const int mapy0 = MAPY (ScrMaxY);
+      const int mapx1 = MAPX (ScrMaxX);
+      const int mapy1 = MAPY (0);
+      for (n = 0; n < NumLineDefs; n++)
+	 if (LineDefs[n].sidedef1 != -1
+	     && SideDefs[LineDefs[n].sidedef1].sector == objnum
+	  || LineDefs[n].sidedef2 != -1
+	     && SideDefs[LineDefs[n].sidedef2].sector == objnum)
+	 {
+	    const struct Vertex *v1 = Vertices + LineDefs[n].start;
+	    const struct Vertex *v2 = Vertices + LineDefs[n].end;
+	    if (v1->x < mapx0 && v2->x < mapx0
+	     || v1->x > mapx1 && v2->x > mapx1
+	     || v1->y < mapy0 && v2->y < mapy0
+	     || v1->y > mapy1 && v2->y > mapy1)
+	       continue;  // Off-screen
+	    DrawMapLine (v1->x, v1->y, v2->x, v2->y);
+	 }
+      if (colour != LIGHTRED && Sectors[objnum].tag > 0)
+	 {
+	 for (m = 0; m < NumLineDefs; m++)
+	    if (LineDefs[m].tag == Sectors[objnum].tag)
+	       HighlightObject (OBJ_LINEDEFS, m, LIGHTRED);
+	 }
+      SetLineThickness (0);
+      }
+      break;
+   }
+/* restore normal write mode */
+SetDrawingMode (0);
+}
+
+
+
+/*
+   delete an object
+*/
+void DeleteObject (const Objid& obj) /* SWAP! */
+{
+SelPtr list;
+
+list = 0;
+SelectObject (&list, obj.num);
+DeleteObjects (obj.type, &list);
+}
+
+
+
+/*
+   delete a group of objects (*recursive*)
+*/
+void DeleteObjects (int objtype, SelPtr *list) /* SWAP! */
+{
+int    n, objnum;
+SelPtr cur;
+
+MadeChanges = 1;
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      ObjectsNeeded (OBJ_THINGS, 0);
+      if (*list)
+	 {
+	 things_angles++;
+	 things_types++;
+	 }
+      while (*list)
+	 {
+	 objnum = (*list)->objnum;
+	 if (objnum < 0 || objnum >= NumThings)  // Paranoia
+	 {
+	    nf_bug ("attempt to delete non-existent thing #%d", objnum);
+	    goto next_thing;
+	 }
+	 // Delete the thing
+	 NumThings--;
+	 if (NumThings > 0)
+	    {
+	    for (n = objnum; n < NumThings; n++)
+	       Things[n] = Things[n + 1];
+	    Things = (TPtr) ResizeFarMemory (Things,
+	       NumThings * sizeof (struct Thing));
+	    }
+	 else
+	    {
+	    FreeFarMemory (Things);
+	    Things = 0;
+	    }
+	 for (cur = (*list)->next; cur; cur = cur->next)
+	    if (cur->objnum > objnum)
+	       cur->objnum--;
+	 next_thing:
+	 UnSelectObject (list, objnum);
+	 }
+      break;
+
+   case OBJ_VERTICES:
+      if (*list)
+         MadeMapChanges = 1;
+      while (*list)
+	 {
+	 objnum = (*list)->objnum;
+	 if (objnum < 0 || objnum >= NumVertices)  // Paranoia
+	 {
+	    nf_bug ("attempt to delete non-existent vertex #%d", objnum);
+	    goto next_vertex;
+	 }
+	 // Delete the linedefs bound to this vertex and change the references
+	 ObjectsNeeded (OBJ_LINEDEFS, 0);
+	 for (n = 0; n < NumLineDefs; n++)
+	    {
+	    if (LineDefs[n].start == objnum || LineDefs[n].end == objnum)
+	       DeleteObject (Objid (OBJ_LINEDEFS, n--));
+	    else
+	       {
+	       if (LineDefs[n].start >= objnum)
+		  LineDefs[n].start--;
+	       if (LineDefs[n].end >= objnum)
+		  LineDefs[n].end--;
+	       }
+	    }
+	 // Delete the vertex
+	 ObjectsNeeded (OBJ_VERTICES, 0);
+	 NumVertices--;
+	 if (NumVertices > 0)
+	    {
+	    for (n = objnum; n < NumVertices; n++)
+	       Vertices[n] = Vertices[n + 1];
+	    Vertices = (VPtr) ResizeFarMemory (Vertices,
+	      NumVertices * sizeof (struct Vertex));
+	    }
+	 else
+	    {
+	    FreeFarMemory (Vertices);
+	    Vertices = 0;
+	    }
+	 for (cur = (*list)->next; cur; cur = cur->next)
+	    if (cur->objnum > objnum)
+	       cur->objnum--;
+	 next_vertex:
+	 UnSelectObject (list, objnum);
+	 }
+      break;
+
+   case OBJ_LINEDEFS:
+      /* In DEU, deleting a linedef was not considered to be a
+	 map change. Deleting a _sidedef_ was. In Yadex,
+	 sidedefs are not automatically deleted when the linedef
+	 is because some sidedefs are shared by more than one
+	 linedef. So we need to set MadeMapChanges here. */
+      if (*list)
+         MadeMapChanges = 1;
+      /* AYM 19980203 I've removed the deletion of sidedefs
+         because if several linedefs use the same sidedef, this
+         would lead to trouble. Instead, I let the xref checking
+         take care of that. */
+      while (*list)
+	 {
+	 ObjectsNeeded (OBJ_LINEDEFS, 0);
+	 objnum = (*list)->objnum;
+	 if (objnum < 0 || objnum >= NumLineDefs)  // Paranoia
+	 {
+	    nf_bug ("attempt to delete non-existent linedef #%d", objnum);
+	    goto next_linedef;
+	 }
+	 // delete the linedef
+	 NumLineDefs--;
+	 if (NumLineDefs > 0)
+	    {
+	    for (n = objnum; n < NumLineDefs; n++)
+	       LineDefs[n] = LineDefs[n + 1];
+	    LineDefs = (LDPtr) ResizeFarMemory (LineDefs,
+	      NumLineDefs * sizeof (struct LineDef));
+	    }
+	 else
+	    {
+	    FreeFarMemory (LineDefs);
+	    LineDefs = 0;
+	    }
+	 for (cur = (*list)->next; cur; cur = cur->next)
+	    if (cur->objnum > objnum)
+	       cur->objnum--;
+	 next_linedef:
+	 UnSelectObject (list, objnum);
+	 }
+      break;
+
+   case OBJ_SIDEDEFS:
+      if (*list)
+         MadeMapChanges = 1;
+      while (*list)
+	 {
+	 objnum = (*list)->objnum;
+	 if (objnum < 0 || objnum >= NumSideDefs)  // Paranoia
+	 {
+	    nf_bug ("attempt to delete non-existent sidedef #%d", objnum);
+	    goto next_sidedef;
+	 }
+	 /* change the linedefs references */
+	 ObjectsNeeded (OBJ_LINEDEFS, 0);
+	 for (n = 0; n < NumLineDefs; n++)
+	    {
+	    if (LineDefs[n].sidedef1 == objnum)
+	       LineDefs[n].sidedef1 = -1;
+	    else if (LineDefs[n].sidedef1 >= objnum)
+	       LineDefs[n].sidedef1--;
+	    if (LineDefs[n].sidedef2 == objnum)
+	       LineDefs[n].sidedef2 = -1;
+	    else if (LineDefs[n].sidedef2 >= objnum)
+	       LineDefs[n].sidedef2--;
+	    }
+	 /* delete the sidedef */
+	 ObjectsNeeded (OBJ_SIDEDEFS, 0);
+	 NumSideDefs--;
+	 if (NumSideDefs > 0)
+	    {
+	    for (n = objnum; n < NumSideDefs; n++)
+	       SideDefs[n] = SideDefs[n + 1];
+	    SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
+	       NumSideDefs * sizeof (struct SideDef));
+	    }
+	 else
+	    {
+	    FreeFarMemory (SideDefs);
+	    SideDefs = 0;
+	    }
+	 for (cur = (*list)->next; cur; cur = cur->next)
+	    if (cur->objnum > objnum)
+	       cur->objnum--;
+	 next_sidedef:
+	 UnSelectObject (list, objnum);
+	 }
+      break;
+   case OBJ_SECTORS:
+      while (*list)
+	{
+	objnum = (*list)->objnum;
+	 if (objnum < 0 || objnum >= NumSectors)  // Paranoia
+	 {
+	    nf_bug ("attempt to delete non-existent sector #%d", objnum);
+	    goto next_sector;
+	 }
+	// Delete the sidedefs bound to this sector and change the references
+	// AYM 19980203: Hmm, hope this is OK with multiply used sidedefs...
+	ObjectsNeeded (OBJ_SIDEDEFS, 0);
+	for (n = 0; n < NumSideDefs; n++)
+	   if (SideDefs[n].sector == objnum)
+	      DeleteObject (Objid (OBJ_SIDEDEFS, n--));
+	   else if (SideDefs[n].sector >= objnum)
+	      SideDefs[n].sector--;
+	/* delete the sector */
+	ObjectsNeeded (OBJ_SECTORS, 0);
+	NumSectors--;
+	if (NumSectors > 0)
+	   {
+	   for (n = objnum; n < NumSectors; n++)
+	      Sectors[n] = Sectors[n + 1];
+	   Sectors = (SPtr) ResizeFarMemory (Sectors,
+	      NumSectors * sizeof (struct Sector));
+	   }
+	else
+	   {
+	   FreeFarMemory (Sectors);
+	   Sectors = 0;
+	   }
+	for (cur = (*list)->next; cur; cur = cur->next)
+	   if (cur->objnum > objnum)
+	      cur->objnum--;
+	next_sector:
+	UnSelectObject (list, objnum);
+	}
+      break;
+   default:
+      nf_bug ("DeleteObjects: bad objtype %d", (int) objtype);
+   }
+}
+
+
+
+/*
+ *	InsertObject
+ *	Insert a new object of type <objtype> at map coordinates
+ *	(<xpos>, <ypos>).
+ *
+ *	If <copyfrom> is a valid object number, the other properties
+ *	of the new object are set from the properties of that object,
+ *	with the exception of sidedef numbers, which are forced
+ *	to OBJ_NO_NONE.
+ *
+ *	The object is inserted at the exact coordinates given.
+ *	No snapping to grid is done.
+ */
+void InsertObject (obj_type_t objtype, obj_no_t copyfrom, int xpos, int ypos)
+								/* SWAP! */
+{
+int last;
+
+ObjectsNeeded (objtype, 0);
+MadeChanges = 1;
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      last = NumThings++;
+      if (last > 0)
+	 Things = (TPtr) ResizeFarMemory (Things,
+	   (unsigned long) NumThings * sizeof (struct Thing));
+      else
+	 Things = (TPtr) GetFarMemory (sizeof (struct Thing));
+      Things[last].xpos = xpos;
+      Things[last].ypos = ypos;
+      things_angles++;
+      things_types++;
+      if (is_obj (copyfrom))
+	 {
+	 Things[last].type  = Things[copyfrom].type;
+	 Things[last].angle = Things[copyfrom].angle;
+	 Things[last].when  = Things[copyfrom].when;
+	 Things[last].tid   = Things[copyfrom].tid;
+	 Things[last].height = Things[copyfrom].height;
+	 Things[last].special = Things[copyfrom].special;
+	 Things[last].arg1  = Things[copyfrom].arg1;
+	 Things[last].arg2  = Things[copyfrom].arg2;
+	 Things[last].arg3  = Things[copyfrom].arg3;
+	 Things[last].arg4  = Things[copyfrom].arg4;
+	 Things[last].arg5  = Things[copyfrom].arg5;
+	 }
+      else
+	 {
+	 Things[last].type = default_thing;
+	 Things[last].angle = 0;
+	 Things[last].when  = 0x07;
+	 Things[last].tid   = 0;
+	 Things[last].height = 0;
+	 Things[last].special = 0;
+	 Things[last].arg1  = 0;
+	 Things[last].arg2  = 0;
+	 Things[last].arg3  = 0;
+	 Things[last].arg4  = 0;
+	 Things[last].arg5  = 0;
+	 }
+      break;
+
+   case OBJ_VERTICES:
+      last = NumVertices++;
+      if (last > 0)
+	 Vertices = (VPtr) ResizeFarMemory (Vertices,
+	   (unsigned long) NumVertices * sizeof (struct Vertex));
+      else
+	 Vertices = (VPtr) GetFarMemory (sizeof (struct Vertex));
+      Vertices[last].x = xpos;
+      Vertices[last].y = ypos;
+      if (Vertices[last].x < MapMinX)
+	 MapMinX = Vertices[last].x;
+      if (Vertices[last].x > MapMaxX)
+	 MapMaxX = Vertices[last].x;
+      if (Vertices[last].y < MapMinY)
+	 MapMinY = Vertices[last].y;
+      if (Vertices[last].y > MapMaxY)
+	 MapMaxY = Vertices[last].y;
+      MadeMapChanges = 1;
+      break;
+
+   case OBJ_LINEDEFS:
+      last = NumLineDefs++;
+      if (last > 0)
+	 LineDefs = (LDPtr) ResizeFarMemory (LineDefs,
+	   (unsigned long) NumLineDefs * sizeof (struct LineDef));
+      else
+	 LineDefs = (LDPtr) GetFarMemory (sizeof (struct LineDef));
+      if (is_obj (copyfrom))
+	 {
+	 LineDefs[last].start = LineDefs[copyfrom].start;
+	 LineDefs[last].end   = LineDefs[copyfrom].end;
+	 LineDefs[last].flags = LineDefs[copyfrom].flags;
+	 LineDefs[last].type  = LineDefs[copyfrom].type;
+	 LineDefs[last].tag   = LineDefs[copyfrom].tag;
+	 LineDefs[last].arg2  = LineDefs[copyfrom].arg2;
+	 LineDefs[last].arg3  = LineDefs[copyfrom].arg3;
+	 LineDefs[last].arg4  = LineDefs[copyfrom].arg4;
+	 LineDefs[last].arg5  = LineDefs[copyfrom].arg5;
+	 }
+      else
+	 {
+	 LineDefs[last].start = 0;
+	 LineDefs[last].end   = NumVertices - 1;
+	 LineDefs[last].flags = 1;
+	 LineDefs[last].type  = 0;
+	 LineDefs[last].tag   = 0;
+	 LineDefs[last].arg2  = 0;
+	 LineDefs[last].arg3  = 0;
+	 LineDefs[last].arg4  = 0;
+	 LineDefs[last].arg5  = 0;
+	 }
+      LineDefs[last].sidedef1 = OBJ_NO_NONE;
+      LineDefs[last].sidedef2 = OBJ_NO_NONE;
+      break;
+
+   case OBJ_SIDEDEFS:
+      last = NumSideDefs++;
+      if (last > 0)
+	 SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
+	   (unsigned long) NumSideDefs * sizeof (struct SideDef));
+      else
+	 SideDefs = (SDPtr) GetFarMemory (sizeof (struct SideDef));
+      if (is_obj (copyfrom))
+	 {
+	 SideDefs[last].xoff = SideDefs[copyfrom].xoff;
+	 SideDefs[last].yoff = SideDefs[copyfrom].yoff;
+	 strncpy (SideDefs[last].tex1, SideDefs[copyfrom].tex1, WAD_TEX_NAME);
+	 strncpy (SideDefs[last].tex2, SideDefs[copyfrom].tex2, WAD_TEX_NAME);
+	 strncpy (SideDefs[last].tex3, SideDefs[copyfrom].tex3, WAD_TEX_NAME);
+	 SideDefs[last].sector = SideDefs[copyfrom].sector;
+	 }
+      else
+	 {
+	 SideDefs[last].xoff = 0;
+	 SideDefs[last].yoff = 0;
+	 strcpy (SideDefs[last].tex1, "-");
+	 strcpy (SideDefs[last].tex2, "-");
+	 strcpy (SideDefs[last].tex3, default_middle_texture);
+	 SideDefs[last].sector = NumSectors - 1;
+	 }
+      MadeMapChanges = 1;
+      break;
+
+   case OBJ_SECTORS:
+      last = NumSectors++;
+      if (last > 0)
+	 Sectors = (SPtr) ResizeFarMemory (Sectors,
+			  (unsigned long) NumSectors * sizeof (struct Sector));
+      else
+	 Sectors = (SPtr) GetFarMemory (sizeof (struct Sector));
+      if (is_obj (copyfrom))
+	 {
+	 Sectors[last].floorh  = Sectors[copyfrom].floorh;
+	 Sectors[last].ceilh   = Sectors[copyfrom].ceilh;
+	 strncpy (Sectors[last].floort, Sectors[copyfrom].floort, WAD_FLAT_NAME);
+	 strncpy (Sectors[last].ceilt, Sectors[copyfrom].ceilt, WAD_FLAT_NAME);
+	 Sectors[last].light   = Sectors[copyfrom].light;
+	 Sectors[last].special = Sectors[copyfrom].special;
+	 Sectors[last].tag     = Sectors[copyfrom].tag;
+	 }
+      else
+	 {
+	 Sectors[last].floorh  = default_floor_height;
+	 Sectors[last].ceilh   = default_ceiling_height;
+	 strncpy (Sectors[last].floort, default_floor_texture, WAD_FLAT_NAME);
+	 strncpy (Sectors[last].ceilt, default_ceiling_texture, WAD_FLAT_NAME);
+	 Sectors[last].light   = default_light_level;
+	 Sectors[last].special = 0;
+	 Sectors[last].tag     = 0;
+	 }
+      break;
+
+   default:
+      nf_bug ("InsertObject: bad objtype %d", (int) objtype);
+   }
+}
+
+
+
+/*
+   check if a (part of a) LineDef is inside a given block
+*/
+bool IsLineDefInside (int ldnum, int x0, int y0, int x1, int y1) /* SWAP - needs Vertices & LineDefs */
+{
+int lx0 = Vertices[LineDefs[ldnum].start].x;
+int ly0 = Vertices[LineDefs[ldnum].start].y;
+int lx1 = Vertices[LineDefs[ldnum].end].x;
+int ly1 = Vertices[LineDefs[ldnum].end].y;
+int i;
+
+/* do you like mathematics? */
+if (lx0 >= x0 && lx0 <= x1 && ly0 >= y0 && ly0 <= y1)
+   return 1; /* the linedef start is entirely inside the square */
+if (lx1 >= x0 && lx1 <= x1 && ly1 >= y0 && ly1 <= y1)
+   return 1; /* the linedef end is entirely inside the square */
+if ((ly0 > y0) != (ly1 > y0))
+   {
+   i = lx0 + (int) ((long) (y0 - ly0) * (long) (lx1 - lx0) / (long) (ly1 - ly0));
+   if (i >= x0 && i <= x1)
+      return true; /* the linedef crosses the y0 side (left) */
+   }
+if ((ly0 > y1) != (ly1 > y1))
+   {
+   i = lx0 + (int) ((long) (y1 - ly0) * (long) (lx1 - lx0) / (long) (ly1 - ly0));
+   if (i >= x0 && i <= x1)
+      return true; /* the linedef crosses the y1 side (right) */
+   }
+if ((lx0 > x0) != (lx1 > x0))
+   {
+   i = ly0 + (int) ((long) (x0 - lx0) * (long) (ly1 - ly0) / (long) (lx1 - lx0));
+   if (i >= y0 && i <= y1)
+      return true; /* the linedef crosses the x0 side (down) */
+   }
+if ((lx0 > x1) != (lx1 > x1))
+   {
+   i = ly0 + (int) ((long) (x1 - lx0) * (long) (ly1 - ly0) / (long) (lx1 - lx0));
+   if (i >= y0 && i <= y1)
+      return true; /* the linedef crosses the x1 side (up) */
+   }
+return false;
+}
+
+
+
+/*
+   get the sector number of the sidedef opposite to this sidedef
+   (returns -1 if it cannot be found)
+*/
+int GetOppositeSector (int ld1, bool firstside) /* SWAP! */
+{
+int x0, y0, dx0, dy0;
+int x1, y1, dx1, dy1;
+int x2, y2, dx2, dy2;
+int ld2, dist;
+int bestld, bestdist, bestmdist;
+
+/* get the coords for this LineDef */
+ObjectsNeeded (OBJ_LINEDEFS, OBJ_VERTICES, 0);
+x0  = Vertices[LineDefs[ld1].start].x;
+y0  = Vertices[LineDefs[ld1].start].y;
+dx0 = Vertices[LineDefs[ld1].end].x - x0;
+dy0 = Vertices[LineDefs[ld1].end].y - y0;
+
+/* find the normal vector for this LineDef */
+x1  = (dx0 + x0 + x0) / 2;
+y1  = (dy0 + y0 + y0) / 2;
+if (firstside)
+   {
+   dx1 = dy0;
+   dy1 = -dx0;
+   }
+else
+   {
+   dx1 = -dy0;
+   dy1 = dx0;
+   }
+
+bestld = -1;
+/* use a parallel to an axis instead of the normal vector (faster method) */
+if (abs (dy1) > abs (dx1))
+   {
+   if (dy1 > 0)
+      {
+      /* get the nearest LineDef in that direction (increasing Y's: North) */
+      bestdist = 32767;
+      bestmdist = 32767;
+      for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+	 if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].x > x1)
+			 != (Vertices[LineDefs[ld2].end].x > x1)))
+	    {
+	    x2  = Vertices[LineDefs[ld2].start].x;
+	    y2  = Vertices[LineDefs[ld2].start].y;
+	    dx2 = Vertices[LineDefs[ld2].end].x - x2;
+	    dy2 = Vertices[LineDefs[ld2].end].y - y2;
+	    dist = y2 + (int) ((long) (x1 - x2) * (long) dy2 / (long) dx2);
+	    if (dist > y1 && (dist < bestdist
+	     || (dist == bestdist && (y2 + dy2 / 2) < bestmdist)))
+	       {
+	       bestld = ld2;
+	       bestdist = dist;
+	       bestmdist = y2 + dy2 / 2;
+	       }
+	    }
+      }
+   else
+      {
+      /* get the nearest LineDef in that direction (decreasing Y's: South) */
+      bestdist = -32767;
+      bestmdist = -32767;
+      for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+	 if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].x > x1)
+			 != (Vertices[LineDefs[ld2].end].x > x1)))
+	    {
+	    x2  = Vertices[LineDefs[ld2].start].x;
+	    y2  = Vertices[LineDefs[ld2].start].y;
+	    dx2 = Vertices[LineDefs[ld2].end].x - x2;
+	    dy2 = Vertices[LineDefs[ld2].end].y - y2;
+	    dist = y2 + (int) ((long) (x1 - x2) * (long) dy2 / (long) dx2);
+	    if (dist < y1 && (dist > bestdist
+	     || (dist == bestdist && (y2 + dy2 / 2) > bestmdist)))
+	       {
+	       bestld = ld2;
+	       bestdist = dist;
+	       bestmdist = y2 + dy2 / 2;
+	       }
+	    }
+      }
+   }
+else
+   {
+   if (dx1 > 0)
+      {
+      /* get the nearest LineDef in that direction (increasing X's: East) */
+      bestdist = 32767;
+      bestmdist = 32767;
+      for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+	 if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].y > y1)
+			 != (Vertices[LineDefs[ld2].end].y > y1)))
+	    {
+	    x2  = Vertices[LineDefs[ld2].start].x;
+	    y2  = Vertices[LineDefs[ld2].start].y;
+	    dx2 = Vertices[LineDefs[ld2].end].x - x2;
+	    dy2 = Vertices[LineDefs[ld2].end].y - y2;
+	    dist = x2 + (int) ((long) (y1 - y2) * (long) dx2 / (long) dy2);
+	    if (dist > x1 && (dist < bestdist
+	     || (dist == bestdist && (x2 + dx2 / 2) < bestmdist)))
+	       {
+	       bestld = ld2;
+	       bestdist = dist;
+	       bestmdist = x2 + dx2 / 2;
+	       }
+	    }
+      }
+   else
+      {
+      /* get the nearest LineDef in that direction (decreasing X's: West) */
+      bestdist = -32767;
+      bestmdist = -32767;
+      for (ld2 = 0; ld2 < NumLineDefs; ld2++)
+	 if (ld2 != ld1 && ((Vertices[LineDefs[ld2].start].y > y1)
+			 != (Vertices[LineDefs[ld2].end].y > y1)))
+	    {
+	    x2  = Vertices[LineDefs[ld2].start].x;
+	    y2  = Vertices[LineDefs[ld2].start].y;
+	    dx2 = Vertices[LineDefs[ld2].end].x - x2;
+	    dy2 = Vertices[LineDefs[ld2].end].y - y2;
+	    dist = x2 + (int) ((long) (y1 - y2) * (long) dx2 / (long) dy2);
+	    if (dist < x1 && (dist > bestdist
+	     || (dist == bestdist && (x2 + dx2 / 2) > bestmdist)))
+	       {
+	       bestld = ld2;
+	       bestdist = dist;
+	       bestmdist = x2 + dx2 / 2;
+	       }
+	    }
+      }
+   }
+
+/* no intersection: the LineDef was pointing outwards! */
+if (bestld < 0)
+   return -1;
+
+/* now look if this LineDef has a SideDef bound to one sector */
+if (abs (dy1) > abs (dx1))
+   {
+   if ((Vertices[LineDefs[bestld].start].x
+	< Vertices[LineDefs[bestld].end].x) == (dy1 > 0))
+      x0 = LineDefs[bestld].sidedef1;
+   else
+      x0 = LineDefs[bestld].sidedef2;
+   }
+else
+   {
+   if ((Vertices[LineDefs[bestld].start].y
+      < Vertices[LineDefs[bestld].end].y) != (dx1 > 0))
+      x0 = LineDefs[bestld].sidedef1;
+   else
+      x0 = LineDefs[bestld].sidedef2;
+   }
+
+/* there is no SideDef on this side of the LineDef! */
+if (x0 < 0)
+   return -1;
+
+/* OK, we got it -- return the Sector number */
+ObjectsNeeded (OBJ_SIDEDEFS, 0);
+return SideDefs[x0].sector;
+}
+
+
+
+/*
+   copy a group of objects to a new position
+*/
+void CopyObjects (int objtype, SelPtr obj) /* SWAP! */
+{
+int        n, m;
+SelPtr     cur;
+SelPtr     list1, list2;
+SelPtr     ref1, ref2;
+
+if (! obj)
+   return;
+ObjectsNeeded (objtype, 0);
+/* copy the object(s) */
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      for (cur = obj; cur; cur = cur->next)
+	 {
+	 InsertObject (OBJ_THINGS, cur->objnum, Things[cur->objnum].xpos,
+						Things[cur->objnum].ypos);
+	 cur->objnum = NumThings - 1;
+	 }
+      MadeChanges = 1;
+      break;
+
+   case OBJ_VERTICES:
+      for (cur = obj; cur; cur = cur->next)
+	 {
+	 InsertObject (OBJ_VERTICES, cur->objnum, Vertices[cur->objnum].x,
+						  Vertices[cur->objnum].y);
+	 cur->objnum = NumVertices - 1;
+	 }
+      MadeChanges = 1;
+      MadeMapChanges = 1;
+      break;
+
+   case OBJ_LINEDEFS:
+      list1 = 0;
+      list2 = 0;
+
+      // Create the linedefs and maybe the sidedefs
+      for (cur = obj; cur; cur = cur->next)
+	 {
+         int old = cur->objnum;	// No. of original linedef
+         int New;		// No. of duplicate linedef
+
+	 InsertObject (OBJ_LINEDEFS, old, 0, 0);
+         New = NumLineDefs - 1;
+
+         if (copy_linedef_reuse_sidedefs)
+	    {
+	    /* AYM 1997-07-25: not very orthodox (the New linedef and 
+	       the old one use the same sidedefs). but, in the case where
+	       you're copying into the same sector, it's much better than
+	       having to create the New sidedefs manually. plus it saves
+	       space in the .wad and also it makes editing easier (editing
+	       one sidedef impacts all linedefs that use it). */
+	    LineDefs[New].sidedef1 = LineDefs[old].sidedef1; 
+	    LineDefs[New].sidedef2 = LineDefs[old].sidedef2; 
+	    }
+         else
+            {
+            /* AYM 1998-11-08: duplicate sidedefs too.
+               DEU 5.21 just left the sidedef references to -1. */
+            if (is_sidedef (LineDefs[old].sidedef1))
+	       {
+	       InsertObject (OBJ_SIDEDEFS, LineDefs[old].sidedef1, 0, 0);
+	       LineDefs[New].sidedef1 = NumSideDefs - 1;
+	       }
+            if (is_sidedef (LineDefs[old].sidedef2))
+	       {
+	       InsertObject (OBJ_SIDEDEFS, LineDefs[old].sidedef2, 0, 0);
+	       LineDefs[New].sidedef2 = NumSideDefs - 1; 
+	       }
+            }
+	 cur->objnum = New;
+	 if (!IsSelected (list1, LineDefs[New].start))
+	    {
+	    SelectObject (&list1, LineDefs[New].start);
+	    SelectObject (&list2, LineDefs[New].start);
+	    }
+	 if (!IsSelected (list1, LineDefs[New].end))
+	    {
+	    SelectObject (&list1, LineDefs[New].end);
+	    SelectObject (&list2, LineDefs[New].end);
+	    }
+	 }
+
+      // Create the vertices
+      CopyObjects (OBJ_VERTICES, list2);
+      ObjectsNeeded (OBJ_LINEDEFS, 0);
+
+      // Update the references to the vertices
+      for (ref1 = list1, ref2 = list2;
+	   ref1 && ref2;
+	   ref1 = ref1->next, ref2 = ref2->next)
+	 {
+	 for (cur = obj; cur; cur = cur->next)
+	    {
+	    if (ref1->objnum == LineDefs[cur->objnum].start)
+	      LineDefs[cur->objnum].start = ref2->objnum;
+	    if (ref1->objnum == LineDefs[cur->objnum].end)
+	      LineDefs[cur->objnum].end = ref2->objnum;
+	    }
+	 }
+      ForgetSelection (&list1);
+      ForgetSelection (&list2);
+      break;
+
+   case OBJ_SECTORS:
+      ObjectsNeeded (OBJ_LINEDEFS, OBJ_SIDEDEFS, 0);
+      list1 = 0;
+      list2 = 0;
+      // Create the linedefs (and vertices)
+      for (cur = obj; cur; cur = cur->next)
+	 {
+	 for (n = 0; n < NumLineDefs; n++)
+	    if  ((((m = LineDefs[n].sidedef1) >= 0
+		       && SideDefs[m].sector == cur->objnum)
+		|| ((m = LineDefs[n].sidedef2) >= 0
+		       && SideDefs[m].sector == cur->objnum))
+		       && ! IsSelected (list1, n))
+	       {
+	       SelectObject (&list1, n);
+	       SelectObject (&list2, n);
+	       }
+	 }
+      CopyObjects (OBJ_LINEDEFS, list2);
+      /* create the sidedefs */
+      ObjectsNeeded (OBJ_LINEDEFS, 0);
+      for (ref1 = list1, ref2 = list2;
+	   ref1 && ref2;
+	   ref1 = ref1->next, ref2 = ref2->next)
+	 {
+	 if ((n = LineDefs[ref1->objnum].sidedef1) >= 0)
+	    {
+	    InsertObject (OBJ_SIDEDEFS, n, 0, 0);
+	    n = NumSideDefs - 1;
+	    ObjectsNeeded (OBJ_LINEDEFS, 0);
+	    LineDefs[ref2->objnum].sidedef1 = n;
+	    }
+	 if ((m = LineDefs[ref1->objnum].sidedef2) >= 0)
+	    {
+	    InsertObject (OBJ_SIDEDEFS, m, 0, 0);
+	    m = NumSideDefs - 1;
+	    ObjectsNeeded (OBJ_LINEDEFS, 0);
+	    LineDefs[ref2->objnum].sidedef2 = m;
+	    }
+	 ref1->objnum = n;
+	 ref2->objnum = m;
+	 }
+      /* create the Sectors */
+      for (cur = obj; cur; cur = cur->next)
+	 {
+	 InsertObject (OBJ_SECTORS, cur->objnum, 0, 0);
+	 ObjectsNeeded (OBJ_SIDEDEFS, 0);
+	 for (ref1 = list1, ref2 = list2;
+	      ref1 && ref2;
+	      ref1 = ref1->next, ref2 = ref2->next)
+	    {
+	    if (ref1->objnum >= 0
+               && SideDefs[ref1->objnum].sector == cur->objnum)
+	       SideDefs[ref1->objnum].sector = NumSectors - 1;
+	    if (ref2->objnum >= 0
+               && SideDefs[ref2->objnum].sector == cur->objnum)
+	       SideDefs[ref2->objnum].sector = NumSectors - 1;
+	    }
+	 cur->objnum = NumSectors - 1;
+	 }
+      ForgetSelection (&list1);
+      ForgetSelection (&list2);
+      break;
+   }
+}
+
+
+
+/*
+ *	MoveObjectsToCoords
+ *	Move a group of objects to a new position
+ *
+ *	You must first call it with obj == NULL and newx and newy
+ *	set to the coordinates of the reference point (E.G. the
+ *	object being dragged).
+ *	Then, every time the object being dragged has changed its
+ *	coordinates, call the it again with newx and newy set to
+ *	the new position and obj set to the selection.
+ *
+ *	Returns <>0 iff an object was moved.
+ */
+bool MoveObjectsToCoords (
+   int objtype,
+   SelPtr obj,
+   int newx,
+   int newy,
+   int grid) /* SWAP! */
+{
+int        dx, dy;
+SelPtr     cur, vertices;
+static int refx, refy; /* previous position */
+
+ObjectsNeeded (objtype, 0);
+if (grid > 0)
+   {
+   newx = (newx + grid / 2) & ~(grid - 1);
+   newy = (newy + grid / 2) & ~(grid - 1);
+   }
+
+// Only update the reference point ?
+if (! obj)
+   {
+   refx = newx;
+   refy = newy;
+   return true;
+   }
+
+/* compute the displacement */
+dx = newx - refx;
+dy = newy - refy;
+/* nothing to do? */
+if (dx == 0 && dy == 0)
+   return false;
+
+/* move the object(s) */
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      for (cur = obj; cur; cur = cur->next)
+	 {
+	 Things[cur->objnum].xpos += dx;
+	 Things[cur->objnum].ypos += dy;
+	 }
+      refx = newx;
+      refy = newy;
+      MadeChanges = 1;
+      break;
+
+   case OBJ_VERTICES:
+      for (cur = obj; cur; cur = cur->next)
+	 {
+	 Vertices[cur->objnum].x += dx;
+	 Vertices[cur->objnum].y += dy;
+	 }
+      refx = newx;
+      refy = newy;
+      MadeChanges = 1;
+      MadeMapChanges = 1;
+      break;
+
+   case OBJ_LINEDEFS:
+      vertices = list_vertices_of_linedefs (obj);
+      MoveObjectsToCoords (OBJ_VERTICES, vertices, newx, newy, grid);
+      ForgetSelection (&vertices);
+      break;
+
+   case OBJ_SECTORS:
+      ObjectsNeeded (OBJ_LINEDEFS, OBJ_SIDEDEFS, 0);
+      vertices = list_vertices_of_sectors (obj);
+      MoveObjectsToCoords (OBJ_VERTICES, vertices, newx, newy, grid);
+      ForgetSelection (&vertices);
+      break;
+   }
+return true;
+}
+
+
+
+/*
+   get the coordinates (approx.) of an object
+*/
+void GetObjectCoords (int objtype, int objnum, int *xpos, int *ypos) /* SWAP! */
+{
+int  n, v1, v2, sd1, sd2;
+long accx, accy, num;
+
+switch (objtype)
+   {
+   case OBJ_THINGS:
+      if (! is_thing (objnum))		// Can't happen
+	 {
+	 nf_bug ("GetObjectCoords: bad thing# %d", objnum);
+	 *xpos = 0;
+	 *ypos = 0;
+	 return;
+	 }
+      ObjectsNeeded (OBJ_THINGS, 0);
+      *xpos = Things[objnum].xpos;
+      *ypos = Things[objnum].ypos;
+      break;
+
+   case OBJ_VERTICES:
+      if (! is_vertex (objnum))		// Can't happen
+	 {
+	 nf_bug ("GetObjectCoords: bad vertex# %d", objnum);
+	 *xpos = 0;
+	 *ypos = 0;
+	 return;
+	 }
+      ObjectsNeeded (OBJ_VERTICES, 0);
+      *xpos = Vertices[objnum].x;
+      *ypos = Vertices[objnum].y;
+      break;
+
+   case OBJ_LINEDEFS:
+      if (! is_linedef (objnum))	// Can't happen
+	 {
+	 nf_bug ("GetObjectCoords: bad linedef# %d", objnum);
+	 *xpos = 0;
+	 *ypos = 0;
+	 return;
+	 }
+      ObjectsNeeded (OBJ_LINEDEFS, 0);
+      v1 = LineDefs[objnum].start;
+      v2 = LineDefs[objnum].end;
+      ObjectsNeeded (OBJ_VERTICES, 0);
+      *xpos = (Vertices[v1].x + Vertices[v2].x) / 2;
+      *ypos = (Vertices[v1].y + Vertices[v2].y) / 2;
+      break;
+
+   case OBJ_SIDEDEFS:
+      if (! is_sidedef (objnum))	// Can't happen
+	 {
+	 nf_bug ("GetObjectCoords: bad sidedef# %d", objnum);
+	 *xpos = 0;
+	 *ypos = 0;
+	 return;
+	 }
+      ObjectsNeeded (OBJ_LINEDEFS, 0);
+      for (n = 0; n < NumLineDefs; n++)
+	 if (LineDefs[n].sidedef1 == objnum || LineDefs[n].sidedef2 == objnum)
+	    {
+	    v1 = LineDefs[n].start;
+	    v2 = LineDefs[n].end;
+	    ObjectsNeeded (OBJ_VERTICES, 0);
+	    *xpos = (Vertices[v1].x + Vertices[v2].x) / 2;
+	    *ypos = (Vertices[v1].y + Vertices[v2].y) / 2;
+	    return;
+	    }
+      *xpos = (MapMinX + MapMaxX) / 2;
+      *ypos = (MapMinY + MapMaxY) / 2;
+      // FIXME is the fall through intentional ? -- AYM 2000-11-08
+
+   case OBJ_SECTORS:
+      if (! is_sector (objnum))		// Can't happen
+	 {
+	 nf_bug ("GetObjectCoords: bad sector# %d", objnum);
+	 *xpos = 0;
+	 *ypos = 0;
+	 return;
+	 }
+      accx = 0L;
+      accy = 0L;
+      num = 0L;
+      for (n = 0; n < NumLineDefs; n++)
+	 {
+	 ObjectsNeeded (OBJ_LINEDEFS, 0);
+	 sd1 = LineDefs[n].sidedef1;
+	 sd2 = LineDefs[n].sidedef2;
+	 v1 = LineDefs[n].start;
+	 v2 = LineDefs[n].end;
+	 ObjectsNeeded (OBJ_SIDEDEFS, 0);
+	 if ((sd1 >= 0 && SideDefs[sd1].sector == objnum)
+	       || (sd2 >= 0 && SideDefs[sd2].sector == objnum))
+	    {
+	    ObjectsNeeded (OBJ_VERTICES, 0);
+	    /* if the Sector is closed, all Vertices will be counted twice */
+	    accx += (long) Vertices[v1].x;
+	    accy += (long) Vertices[v1].y;
+	    num++;
+	    accx += (long) Vertices[v2].x;
+	    accy += (long) Vertices[v2].y;
+	    num++;
+	    }
+	 }
+      if (num > 0)
+	 {
+	 *xpos = (int) ((accx + num / 2L) / num);
+	 *ypos = (int) ((accy + num / 2L) / num);
+	 }
+      else
+	 {
+	 *xpos = (MapMinX + MapMaxX) / 2;
+	 *ypos = (MapMinY + MapMaxY) / 2;
+	 }
+      break;
+
+   default:
+      nf_bug ("GetObjectCoords: bad objtype %d", objtype);  // Can't happen
+      *xpos = 0;
+      *ypos = 0;
+   }
+}
+
+
+
+/*
+   find a free tag number
+*/
+int FindFreeTag () /* SWAP! */
+{
+int  tag, n;
+bool ok;
+
+ObjectsNeeded (OBJ_LINEDEFS, OBJ_SECTORS, 0);
+tag = 1;
+ok = false;
+while (! ok)
+   {
+   ok = true;
+   for (n = 0; n < NumLineDefs; n++)
+      if (LineDefs[n].tag == tag)
+	 {
+	 ok = false;
+	 break;
+	 }
+   if (ok)
+      for (n = 0; n < NumSectors; n++)
+	 if (Sectors[n].tag == tag)
+	    {
+	    ok = false;
+	    break;
+	    }
+   tag++;
+   }
+return tag - 1;
+}
+
+
diff -Naur yadex-1.7.0.orig/src/sanity.cc yadex-1.7.0.allpatches/src/sanity.cc
--- yadex-1.7.0.orig/src/sanity.cc	2003-03-28 13:37:32.000000000 +0100
+++ yadex-1.7.0.allpatches/src/sanity.cc	2013-01-10 17:25:53.231909795 +0100
@@ -67,10 +67,10 @@
   assert_size (i16, 2);
   assert_size (u32, 4);
   assert_size (i32, 4);
-  assert_size (struct LineDef, 14);
+  assert_size (struct LineDef, 18);
   assert_size (struct Sector,  26);
   assert_size (struct SideDef, 30);
-  assert_size (struct Thing,   10);
+  assert_size (struct Thing,   20);
   assert_size (struct Vertex,   4);
   assert_wrap (u8,          255,           0);
   assert_wrap (i8,          127,        -128);
diff -Naur yadex-1.7.0.orig/src/t_prop.cc yadex-1.7.0.allpatches/src/t_prop.cc
--- yadex-1.7.0.orig/src/t_prop.cc	2013-01-10 17:24:11.560024040 +0100
+++ yadex-1.7.0.allpatches/src/t_prop.cc	2013-01-10 17:25:53.234909821 +0100
@@ -47,6 +47,7 @@
 static const char *PrintThinggroup (void *ptr);
 static const char *PrintThingdef (void *ptr);
 int InputThingType (int x0, int y0, int *number);
+int InputLinedefType (int x0, int y0, int *number);
 
 
 /*
@@ -61,9 +62,9 @@
 SelPtr cur;
 int    subwin_y0;
 
-for (n = 0; n < 6; n++)
+for (n = 0; n < 14; n++)
    menustr[n] = (char *) GetMemory (60);
-sprintf (menustr[5], "Edit thing #%d", obj->objnum);
+sprintf (menustr[13], "Edit thing #%d", obj->objnum);
 sprintf (menustr[0], "Change type          (Current: %s)",
          get_thing_name (Things[obj->objnum].type));
 sprintf (menustr[1], "Change angle         (Current: %s)",
@@ -74,14 +75,47 @@
          Things[obj->objnum].xpos);
 sprintf (menustr[4], "Change Y position    (Current: %d)",
          Things[obj->objnum].ypos);
-val = vDisplayMenu (x0, y0, menustr[5],
+sprintf (menustr[5], "Change Z position    (Current: %d)",
+         Things[obj->objnum].height);
+sprintf (menustr[6], "Change TID           (Current: %d)",
+         Things[obj->objnum].tid);
+sprintf (menustr[7], "Change special       (Current: %d)",
+         Things[obj->objnum].special);
+sprintf (menustr[8], "Change arg1          (Current: %d)",
+         Things[obj->objnum].arg1);
+sprintf (menustr[9], "Change arg2          (Current: %d)",
+         Things[obj->objnum].arg2);
+sprintf (menustr[10], "Change arg3          (Current: %d)",
+         Things[obj->objnum].arg3);
+sprintf (menustr[11], "Change arg4          (Current: %d)",
+         Things[obj->objnum].arg4);
+sprintf (menustr[12], "Change arg5          (Current: %d)",
+         Things[obj->objnum].arg5);
+if (yg_level_format == YGLF_HEXEN)		// Hexen mode
+val = vDisplayMenu (x0, y0, menustr[13],
    menustr[0], YK_, 0,
    menustr[1], YK_, 0,
    menustr[2], YK_, 0,
    menustr[3], YK_, 0,
    menustr[4], YK_, 0,
+   menustr[5], YK_, 0,
+   menustr[6], YK_, 0,
+   menustr[7], YK_, 0,
+   menustr[8], YK_, 0,
+   menustr[9], YK_, 0,
+   menustr[10], YK_, 0,
+   menustr[11], YK_, 0,
+   menustr[12], YK_, 0,
    NULL);
-for (n = 0; n < 6; n++)
+else
+val = vDisplayMenu (x0, y0, menustr[13],
+   menustr[0], YK_, 0,
+   menustr[1], YK_, 0,
+   menustr[2], YK_, 0,
+   menustr[3], YK_, 0,
+   menustr[4], YK_, 0,
+   NULL);
+for (n = 0; n < 14; n++)
    FreeMemory (menustr[n]);
 subwin_y0 = y0 + BOX_BORDER + (2 + val) * FONTH;
 switch (val)
@@ -242,6 +276,93 @@
 	MadeChanges = 1;
         }
      break;
+
+  case 6:
+     val = InputIntegerValue (x0 + 42, subwin_y0, -32768, 32767,
+                              Things[obj->objnum].height);
+     if (val != IIV_CANCEL)
+        {
+	n = val - Things[obj->objnum].height;
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].height += n;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 7:
+     val = InputIntegerValue (x0 + 42, subwin_y0, -32768, 32767,
+                              Things[obj->objnum].tid);
+     if (val != IIV_CANCEL)
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].tid = val;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 8:
+     if (! InputLinedefType (x0 + 42, subwin_y0, &val))
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].special = val;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 9:
+     val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+                              Things[obj->objnum].arg1);
+     if (val != IIV_CANCEL)
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].arg1 = val;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 10:
+     val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+                              Things[obj->objnum].arg2);
+     if (val != IIV_CANCEL)
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].arg2 = val;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 11:
+     val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+                              Things[obj->objnum].arg3);
+     if (val != IIV_CANCEL)
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].arg3 = val;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 12:
+     val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+                              Things[obj->objnum].arg4);
+     if (val != IIV_CANCEL)
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].arg4 = val;
+	MadeChanges = 1;
+        }
+     break;
+
+  case 13:
+     val = InputIntegerValue (x0 + 42, subwin_y0, 0, 255,
+                              Things[obj->objnum].arg5);
+     if (val != IIV_CANCEL)
+        {
+	for (cur = obj; cur; cur = cur->next)
+	   Things[cur->objnum].arg5 = val;
+	MadeChanges = 1;
+        }
+     break;
   }
 }
 
diff -Naur yadex-1.7.0.orig/src/wstructs.h yadex-1.7.0.allpatches/src/wstructs.h
--- yadex-1.7.0.orig/src/wstructs.h	2003-03-28 13:37:32.000000000 +0100
+++ yadex-1.7.0.allpatches/src/wstructs.h	2013-01-10 17:25:53.235909830 +0100
@@ -84,14 +84,30 @@
 typedef i16 wad_tflags_t;
 struct Thing
 {
+  i16              tid;
   wad_coord_t      xpos;		// FIXME rename to "x"
   wad_coord_t      ypos;		// FIXME rename to "y"
+  wad_z_t          height;
   wad_tangle_t     angle;
   wad_ttype_t      type;
   wad_tflags_t     when;		// FIXME rename to "flags"
+  u8               special;
+  u8               arg1;
+  u8               arg2;
+  u8               arg3;
+  u8               arg4;
+  u8               arg5;
 };
 typedef struct
 {
+  wad_coord_t      xpos;		// FIXME rename to "x"
+  wad_coord_t      ypos;		// FIXME rename to "y"
+  wad_tangle_t     angle;
+  wad_ttype_t      type;
+  wad_tflags_t     when;		// FIXME rename to "flags"
+} wad_doom_thing_t;
+typedef struct
+{
   i16              tid;
   wad_coord_t      x;
   wad_coord_t      y;
@@ -121,11 +137,25 @@
   wad_ldflags_t flags;
   wad_ldtype_t  type;
   wad_tag_t     tag;
+  u8            arg2;
+  u8            arg3;
+  u8            arg4;
+  u8            arg5;
   wad_sdn_t     sidedef1;		// # of first (right) sidedef
   wad_sdn_t     sidedef2;		// # of second (left) sidedef or 0xffff
 };
 typedef struct
 {
+  wad_vn_t      start;			// # of start vertex
+  wad_vn_t      end;			// # of end vertex
+  wad_ldflags_t flags;
+  wad_ldtype_t  type;
+  wad_tag_t     tag;
+  wad_sdn_t     sidedef1;		// # of first (right) sidedef
+  wad_sdn_t     sidedef2;		// # of second (left) sidedef or 0xffff
+} wad_doom_linedef_t;
+typedef struct
+{
   wad_vn_t      start;
   wad_vn_t      end;
   wad_ldflags_t flags;
@@ -197,8 +227,11 @@
   WAD_LL_SECTORS,
   WAD_LL_REJECT,
   WAD_LL_BLOCKMAP,
-		      // Hexen has a BEHAVIOR lump here
-  WAD_LL__
+  WAD_LL_BEHAVIOR,
+
+  WAD_LL__MAX,
+  WAD_LL__HEXEN = WAD_LL__MAX,
+  WAD_LL__DOOM = WAD_LL_BEHAVIOR
 } wad_level_lump_no_t;
 
 typedef struct
@@ -207,7 +240,7 @@
   size_t item_size;
 } wad_level_lump_def_t;
 
-const wad_level_lump_def_t wad_level_lump[WAD_LL__] =
+const wad_level_lump_def_t wad_level_lump[WAD_LL__MAX] =
 {
   { 0,          0                 },  // Label -- no fixed name
   { "THINGS",   WAD_THING_BYTES   },
@@ -219,8 +252,8 @@
   { "NODES",    0                 },
   { "SECTORS",  WAD_SECTOR_BYTES  },
   { "REJECT",   0                 },
-  { "BLOCKMAP", 0                 }
-				      // Hexen has a BEHAVIOR lump here
+  { "BLOCKMAP", 0                 },
+  { "BEHAVIOR", 0                 }
 };
 
 
diff -Naur yadex-1.7.0.orig/ygd/hexen.ygd yadex-1.7.0.allpatches/ygd/hexen.ygd
--- yadex-1.7.0.orig/ygd/hexen.ygd	2002-12-31 03:53:33.000000000 +0100
+++ yadex-1.7.0.allpatches/ygd/hexen.ygd	2013-01-10 17:25:53.237909848 +0100
@@ -108,6 +108,7 @@
 ldt 101 x "?? Scroll right"  "??  Scroll_Texture_Right"
 ldt 102 x "?? Scroll up"     "??  Scroll_Texture_Up"
 ldt 103 x "?? Scroll down"   "??  Scroll_Texture_Down"
+ldt 109 L "?? Force lightng" "??  Light_ForceLightning"
 ldt 110 L "?? Raise light"   "??  Light_RaiseByValue"
 ldt 111 L "?? Lower light"   "??  Light_LowerByValue"
 ldt 112 L "?? Set light"     "??  Light_ChangeToValue"
@@ -136,25 +137,12 @@
 #
 
 st   0 "  Normal"       "Normal"
-st   1 "  Phased light" "light phased"           # FIXME
-st   2 "  Light start"  "lightsequencestart"     # FIXME
-st   3 "  Light specl1" "lightsequencespecial1"  # FIXME
-st   4 "  Light specl2" "lightsequencespecial2"  # FIXME
-st   9 "  Secret"       "Sector counts toward secret count"
+st   1 "  Phased light" "light phased"
+st   2 "  Light start"  "lightsequencestart"
+st   3 "  Light specl1" "lightsequencespecial1"
+st   4 "  Light specl2" "lightsequencespecial2"
 st  26 "  Stairs1"      "Stairs special 1"
 st  27 "  Stairs2"      "Stairs special 2"
-st  40 "  Wind east 1"  "Wind east force 1"
-st  41 "  Wind east 2"  "Wind east force 2"
-st  42 "  Wind east 3"  "Wind east force 3"
-st  43 "  Wind north 1" "Wind north force 1"
-st  44 "  Wind north 2" "Wind north force 2"
-st  45 "  Wind north 3" "Wind north force 3"
-st  46 "  Wind south 1" "Wind south force 1"
-st  47 "  Wind south 2" "Wind south force 2"
-st  48 "  Wind south 3" "Wind south force 3"
-st  49 "  Wind west 1"  "Wind west force 1"
-st  50 "  Wind west 2"  "Wind west force 2"
-st  51 "  Wind west 3"  "Wind west force 3"
 st 198 "  Lightning 64" "Indoor lightning, +64 units"
 st 199 "  Lightning 32" "Indoor lightning, +32 units"
 st 200 "  Sky2"         "Use MAPINFO sky2"
@@ -191,17 +179,21 @@
 thinggroup p rgb:4/f/4 "Player"
 thinggroup m rgb:f/0/0 "Monster"
 thinggroup w rgb:f/a/0 "Weapon"
-thinggroup a rgb:8/5/0 "Ammunition"
+thinggroup a rgb:8/5/0 "Mana"
 thinggroup h rgb:2/8/0 "Health & armour"
+thinggroup A rgb:2/8/0 "Artifacts"
+thinggroup q rgb:2/8/0 "Quest items"
 thinggroup b rgb:2/8/0 "Misc. bonus"
 thinggroup k rgb:f/0/f "Key"
 thinggroup P rgb:6/6/c "Plants"
+thinggroup t rgb:6/6/c "Stalagm. & stalact."
+thinggroup g rgb:6/6/c "Gargoyle statues"
+thinggroup T rgb:6/6/c "Table stuff"
+thinggroup D rgb:6/6/c "Dungeon stuff"
 thinggroup d rgb:6/6/c "Misc. decoration"
 thinggroup l rgb:6/6/c "Light source"
-#thinggroup g rgb:6/6/c "Gory decoration"
-#thinggroup c rgb:6/6/c "Corpse"
-thinggroup e rgb:0/b/d "Environment sound"
-thinggroup s rgb:0/b/d "Ambient sound"
+thinggroup s rgb:0/b/d "Sound"
+thinggroup S rgb:0/b/d "Special"
 
 #
 #	Definition of things
@@ -210,56 +202,268 @@
 #	<desc> must not exceed 19 characters.
 #
 
-thing     1 p -   16 "Player 1 start"		PLAY
-thing     2 p -   16 "Player 2 start"		PLAY
-thing     3 p -   16 "Player 3 start"		PLAY
-thing     4 p -   16 "Player 4 start"		PLAY
+thing     1 p -   16 "Player 1 start *"		PLAY
+thing     2 p -   16 "Player 2 start *"		PLAY
+thing     3 p -   16 "Player 3 start *"		PLAY
+thing     4 p -   16 "Player 4 start *"		PLAY
 thing    11 p -   16 "Deathmatch start"		PLAYF1
 thing    14 p -   16 "Teleport exit"		TELE
+thing  9100 p -   16 "Player 5 start *"		PLAY
+thing  9101 p -   16 "Player 6 start *"		PLAY
+thing  9102 p -   16 "Player 7 start *"		PLAY
+thing  9103 p -   16 "Player 8 start *"		PLAY
 
-thing    31 m -   32 "Demon"			DEMN
+thing    31 m -   32 "Chaos serpent"		DEMN
+thing    34 m -   40 "Reiver"			WRTH
 thing   107 m -   20 "Centaur"			CENT
+thing   114 m -   44 "Dark bishop"		BISH
 thing   115 m -   20 "Centaur leader"		CENTF
-thing   120 m -   17 "Serpent leader"		SSPTK	# Not too sure...
-thing   121 m -   17 "Serpent"			SSDV	# Not too sure...
-thing  8020 m -   22 "Ice guy"			ICEY
+thing   120 m -   17 "Stalker leader"		SSPTK
+thing   121 m -   17 "Stalker"			SSDV
+thing   254 m -   40 "Death wyvern *"		DRAG
+thing  8020 m -   22 "Wendigo"			ICEY
+thing  8080 m -   64 "Chaos serpent (gas)"	DEM2
+thing 10011 m -   40 "Reiver leader"		WRTH
 thing 10030 m -   25 "Ettin"			ETTN
-thing 10060 m -   20 "Fire demon"		FDMN
-
-thing    10 w -   17 "Serpent staff"		WCSS   # Cleric
-thing    53 w -   17 "Frost chards"		WMCS   # Mage
-thing  8010 w -   17 "Timon's axe"		WFAX   # Fighter
+thing 10060 m -   20 "Fire gargoyle"		FDMN
+thing 10080 m -   80 "Heresiarch"		SORC
+thing 10100 m -   32 "Zedek (fighter)"		PLAYA8
+thing 10101 m -   32 "Traductus (cleric)"	CLERA8
+thing 10102 m -   32 "Menelkir (mage)"		MAGEA8
+thing 10200 m -   20 "Korax"			KORX
+
+thing    10 w -   17 "Serpent staff"		WCSS
+thing    12 w -   20 "Quietus blade"		WFR1
+thing    13 w -   20 "Quietus guard"		WFR2
+thing    16 w -   20 "Quietus hilt"		WFR3
+thing    18 w -   20 "Wraithverge head"		WCH1
+thing    19 w -   20 "Wraithverge center"	WCH2
+thing    20 w -   20 "Wraithverge grip"		WCH3
+thing    21 w -   20 "Bloodscourge head"	WMS1
+thing    22 w -   20 "Bloodscourge center"	WMS2
+thing    23 w -   20 "Bloodscourge grip"	WMS3
+thing    53 w -   17 "Frost chards"		WMCS
+thing   123 w -   10 "Hammer of retributn"	WFHM
+thing  8009 w -   10 "Firestorm"		WCFM
+thing  8010 w -   17 "Timon's axe"		WFAX
+thing  8040 w -   20 "Arc of death"		WMLG
 
 thing   122 a -   17 "Blue manna"		MAN1
 thing   124 a -   17 "Green manna"		MAN2
 thing  8004 a -   17 "Combined manna"		MAN3
 
 thing    81 h -   17 "Crystal vial"		PTN1
-thing    82 h -   17 "Quartz flask"		PTN2
-thing  8000 h -   17 "Flechette"		PSBG
+thing  8005 h -   10 "Mesh armor"		ARM1
+thing  8006 h -   20 "Falcon shield"		ARM2
 thing  8007 h -   17 "Platinum helmet"		ARM3
+thing  8008 h -   20 "Amulet of warding"	ARM4
 
-thing   314 k -   17 "Stone (?) key"		KEY7
+thing    30 A -   20 "Porkalator"		ARTIPORK
+thing    32 A -   20 "Mystic urn"		ARTISPHL
+thing    33 A -   20 "Torch"			ARTITRCH
+thing    36 A -   20 "Chaos device"		ARTIATLP
+thing    82 A -   17 "Quartz flask"		PTN2
+thing    83 A -   20 "Wings of wrath"		SOAR
+thing    84 A -   20 "Icon of defender"		INVU
+thing    86 A -   15 "Dark servant"		ARTISUMN
+thing  8000 A -   17 "Flechette"		PSBG
+thing  8002 A -   20 "Boots of speed"		ARTISPED
+thing  8003 A -   20 "Krater of might"		ARTIBMAN
+thing  8041 A -   20 "Dragonskin bracers"	ARTIBRAC
+thing 10040 A -   20 "Banishment device"	ARTITELO
+thing 10110 A -   20 "Disc of repulsion"	ARTIBLST
+thing 10120 A -   20 "Mystic ambient inc"	ARTIHRAD
+
+thing  9002 q -   20 "Yorick's skull"		ARTISKLL
+thing  9003 q -   20 "Heart of D'Sparil"	ARTIBGEM
+thing  9004 q -   20 "Ruby planet"		ARTIGEMR
+thing  9005 q -   20 "Emerald planet 1"		ARTIGEMG
+thing  9006 q -   20 "Sapphire planet 1"	ARTIGEMB
+thing  9007 q -   20 "Daemon codex"		ABK1
+thing  9008 q -   20 "Liber oscura"		ABK2
+thing  9009 q -   20 "Emerald planet 2"		ARTIGMG2
+thing  9010 q -   20 "Sapphire planet 2"	ARTIGMB2
+thing  9014 q -   20 "Flame mask"		ARTISKL2
+thing  9015 q -   20 "Glaive seal"		ARTIFWEP
+thing  9016 q -   20 "Holy relic"		ARTICWEP
+thing  9017 q -   20 "Sigil of the Magus"	ARTIMWEP
+thing  9018 q -   20 "Clock gear 1"		ARTIGEAR
+thing  9019 q -   20 "Clock gear 2"		ARTIGER2
+thing  9020 q -   20 "Clock gear 3"		ARTIGER3
+thing  9021 q -   20 "Clock gear 4"		ARTIGER4
+
+thing  8030 k -   10 "Steel key"		KEY1
+thing  8031 k -   10 "Cave key"			KEY2
+thing  8032 k -   10 "Axe key"			KEY3
+thing  8033 k -   10 "Fire key"			KEY4
+thing  8034 k -   10 "Emerald key"		KEY5
+thing  8035 k -   10 "Dungeon key"		KEY6
+thing  8036 k -   10 "Silver key"		KEY7
+thing  8037 k -   10 "Rusted key"		KEY8
+thing  8038 k -   10 "Horn key"			KEY9
+thing  8039 k -   10 "Swamp key"		KEYA
+thing  8200 k -   10 "Castle key"		KEYB
 
+thing    24 P -   20 "Tree trunk (brown)"	TRE1
+thing    25 P -   20 "Tree trunk (brownD)"	TRE1
 thing    26 P -   17 "Swamp tree tall"		TRE2
 thing    27 P -   17 "Swamp tree short"		TRE3
+thing    28 P -   20 "Tree stump (splint)"	STM1
+thing    29 P -   20 "Tree stump"		STM2
+thing    39 P -   20 "Mushroom (large, L)"	MSH1
+thing    40 P -   20 "Mushroom (large, R)"	MSH2
+thing    41 P -   20 "Mushroom (med, L)"	MSH3
+thing    42 P -   20 "Mushroom (small, R)"	MSH4
+thing    44 P -   20 "Mushroom (small)"		MSH5
+thing    45 P -   20 "Mushroom (small, F)"	MSH6
+thing    46 P -   20 "Mushroom (small, B)"	MSH7
+thing    47 P -   20 "Mushroom (small, M)"	MSH8
+thing    60 P -   20 "Vine"			TRE3
 thing    78 P -   17 "Tree leaning right"	TRE4
 thing    79 P -   17 "Tree leaning left"	TRE5
 thing    80 P -   17 "Gnarled tree right"	TRE6
 thing    87 P -   17 "Gnarled tree left"	TRE7
+thing   113 P -   10 "Blowing leaves"		LEF1
 thing  8062 P -   17 "Dead tree"		TRDT
 thing  8068 P -   17 "Conic tree"		XMAS
-
-thing    48 d -   17 "Stalactite+gmite"		SGMP
-thing    72 d -   17 "Statue"			STT2
-thing    74 d -   17 "Short statue"		STT4
+thing  8101 P -   20 "Shrub (small)"		SHB1
+thing  8102 P -   20 "Shrub (large)"		SHB2
+thing  8103 P -   20 "Bucket (hanging)"		BCKT
+thing  8104 P -   20 "Mushroom (explodng)"	SHRM
+
+thing    48 t -   17 "Stalactite+gmite"		SGMP
+thing    49 t -   20 "Stalagmite (large)"	SGM1
+thing    50 t -   20 "Stalagmite (medium)"	SGM2
+thing    51 t -   20 "Stalagmite (small)"	SGM3
+thing    52 t -   20 "Stalactite (large)"	SLC1
+thing    56 t -   20 "Stalactite (medium)"	SLC2
+thing    57 t -   20 "Stalactite (small)"	SLC3
+thing    89 t -   20 "Ice stalactite(lrg)"	ICT1
+thing    90 t -   20 "Ice stalactite(med)"	ICT2
+thing    91 t -   20 "Ice stalactite(sml)"	ICT3
+thing    92 t -   20 "Ice stalactite(tny)"	ICT4
+thing    93 t -   20 "Ice stalagmite(lrg)"	ICM1
+thing    94 t -   20 "Ice stalagmite(med)"	ICM2
+thing    95 t -   20 "Ice stalagmite(sml)"	ICM3
+thing    96 t -   20 "Ice stalagmite(tny)"	ICM4
+
+thing     5 g -   20 "Gargoyle statue w/s"	STTW
+thing    72 g -   17 "Stone gargoyle tall"	STT2
+thing    73 g -   20 "Ice gargoyle tall"	STT3
+thing    74 g -   17 "Stone gargoyle shrt"	STT4
+thing    76 g -   20 "Ice gargoyle short"	STT5
+thing  8044 g -   20 "Wooden gargoyle tal"	GAR1
+thing  8045 g -   20 "Fire gargoyle tall"	GAR2
+thing  8046 g -   20 "Red gargoyle tall"	GAR3
+thing  8047 g -   20 "Wooden gargoyle tal"	GAR4
+thing  8048 g -   20 "Metal gargoyle tall"	GAR5
+thing  8049 g -   20 "Fire gargoyle short"	GAR6
+thing  8050 g -   20 "Red gargoyle short"	GAR7
+thing  8051 g -   20 "Wooden gargoyle srt"	GAR8
+thing  8052 g -   20 "Metal gargoyle shrt"	GAR9
+thing  9011 g -   20 "Gargoyle statue"		STWN
+
+thing  8500 T -   20 "Stein (tall)"		TST1
+thing  8501 T -   20 "Stein (short)"		TST2
+thing  8502 T -   20 "Candle w/spider web"	TST3
+thing  8503 T -   20 "Candle (short)"		TST4
+thing  8504 T -   20 "Candle (tall)"		TST5
+thing  8505 T -   20 "Goblet (spilled)"		TST6
+thing  8506 T -   20 "Goblet (tall)"		TST7
+thing  8507 T -   20 "Goblet (short)"		TST8
+thing  8508 T -   20 "Goblet w/silver bnd"	TST9
+thing  8509 T -   20 "Meat cleaver"		TST0
+
+thing    61 D -   20 "Corpse impaled"		CPS1
+thing    62 D -   20 "Corpse sleeping"		CPS2
+thing    71 D -   20 "Corpse hung legs"		CPS3
+thing   108 D -   20 "Corpse hung"		CPS4
+thing   109 D -   20 "Corpse bleeding"		CPS5
+thing   110 D -   20 "Corpse chained"		CPS6
+thing   111 D -   10 "Pool of blood"		BDPL
+thing  8067 D -   20 "Iron maiden"		IRON
+thing  8071 D -   20 "Chain (short)"		CHNS
+thing  8072 D -   20 "Chain (long)"		CHNSB0
+thing  8073 D -   20 "Chain heart on hook"	CHNSC0
+thing  8074 D -   20 "Chain w/large hook"	CHNSD0
+thing  8075 D -   20 "Chain w/small hook"	CHNSE0
+thing  8076 D -   20 "Chain w/spiked ball"	CHNSF0
+thing  8077 D -   20 "Chain skull on hook"	CHNSG0
+
+thing     6 d -   20 "Rock w/moss (tiny)"	RCK1
+thing     7 d -   20 "Rock w/moss (small)"	RCK2
+thing     9 d -   20 "Rock w/moss (medium)"	RCK3
+thing    15 d -   20 "Rock w/moss (large)"	RCK4
+thing    37 d -   20 "Stump w/moss (short)"	STM3
+thing    38 d -   20 "Stump w/moss (tall)"	STM4
+thing    58 d -   20 "Moss (three strands)"	MSS1
+thing    59 d -   20 "Moss (one strand)"	MSS2
+thing    63 d -   20 "Tombstone (R.I.P.)"	TMS1
+thing    64 d -   20 "Tombstone (Shane)"	TMS2
+thing    65 d -   20 "Tombstone (large cross)"	TMS3
+thing    66 d -   20 "Tombstone (Brian R.)"	TMS4
+thing    67 d -   20 "Tombstone (circular cross)"	TMS5
+thing    68 d -   20 "Tombstone (small cross on pedestal)"	TMS6
+thing    69 d -   20 "Tombstone (Brian P.)"	TMS7
 thing    77 d -   17 "Banner"			BNR1
-
-thing    54 l -   17 "Torch"			WLTR
-thing  8061 l -   17 "FIXME"			BRTR
-
-thing   314 e -   17 "Bogus sound"
-
-thing   314 s -   17 "Bogus sound"
-
-
+thing    88 d -   20 "Log"			LOGG
+thing    97 d -   20 "Rock formation (large, brown)"	RKBL
+thing    98 d -   20 "Rock formation (small, brown)"	RKBS
+thing    99 d -   20 "Rock formation (small, gray)"	RKBK
+thing   100 d -   20 "Rubble (large)"		RBL1
+thing   101 d -   20 "Rubble (small)"		RBL2
+thing   102 d -   20 "Rubble (medium)"		RBL3
+thing   103 d -   20 "Vase on pedestal"		VASE
+thing   104 d -   20 "Pot (tall, skinny) *"	POT1
+thing   105 d -   20 "Pot (medium, skinny) *"	POT2
+thing   106 d -   20 "Pot (short, chipped) *"	POT3
+thing   140 d -   20 "Sparkling red smoke"	TSMK
+thing  8064 d -   20 "Suit of armor *"		SUIT
+thing  8065 d -   40 "Bell"			BBLL
+thing  8100 d -   20 "Barrel"			BARL
+thing  9012 d -   20 "Pedestal"			GMPD
+thing 10001 d -   20 "Fog (small) *"		FOGS
+thing 10002 d -   20 "Fog (medium) *"		FOGM
+thing 10003 d -   20 "Fog (large) *"		FOGL
+thing 10090 d -   20 "Spike (down)"		TSPKC0
+thing 10091 d -   20 "Spike (up)"		TSPK
+
+thing    17 l -   20 "Chandelier w/flame"	CDLR
+thing    54 l -   17 "Wall torch w/flame"	WLTR
+thing    55 l -   20 "Wall torch"		WLTRI0
+thing   116 l -   10 "Brazier w/flame"		TWTR
+thing   117 l -   10 "Brazier"			TWTRI0
+thing   119 l -   20 "Candles"			CNDL
+thing  8042 l -   20 "Minotaur statue f"	FBUL
+thing  8043 l -   20 "Minotaur statue"		FBULH0
+thing  8060 l -   20 "Fire skull"		FSKL
+thing  8061 l -   17 "Brazier w/flame sm"	BRTR
+thing  8063 l -   20 "Chandelier"		CDLRD0
+thing  8066 l -   20 "Candle (blue)"		CAND
+thing  8069 l -   20 "Cauldron (w/flame)"	CDRNB0
+thing  8070 l -   20 "Cauldron"			CDRN
+thing 10500 l -   20 "Flame (small, t) *"	FFSM
+thing 10501 l -   20 "Flame (small, cnt)"	FFSMC0
+thing 10502 l -   20 "Flame (large, t) *"	FFLG
+thing 10503 l -   20 "Flame (large, cnt)"	FFLGF0
+
+thing  1400 s -   20 "Stone"			TELE
+thing  1401 s -   20 "Heavy"			TELE
+thing  1402 s -   20 "Metal"			TELE
+thing  1403 s -   20 "Creak"			TELE
+thing  1404 s -   20 "Silent"			TELE
+thing  1405 s -   20 "Lava"			TELE
+thing  1406 s -   20 "Water"			TELE
+thing  1407 s -   20 "Ice"			TELE
+thing  1408 s -   20 "Earth crack"		TELE
+thing  1409 s -   20 "Metal2"			TELE
+thing  1410 s -   20 "Wind blowing"		TELE
+
+thing   118 S -   10 "Magic step"		TLGL
+thing  3000 S -   20 "Polyobject anchor"	ICPRD0
+thing  3001 S -   20 "Start spot"		SBFXH0
+thing  3002 S -   20 "Start spot w/crush"	SBFXC0
+thing  9001 S -   10 "Map spot *"		TELEC0
+thing  9013 S -   10 "Map spot w/gravity"	TELED0
+thing 10000 S -   20 "Spawn fog *"		SPIRK0
+thing 10225 S -   20 "Spawn bat *"		ABATC3C7