shithub: choc

Download patch

ref: 516a7028994df6718289a7e3db4d07a45c95466b
parent: a15ba75736d15409876c1f0a44fffc99adf1c192
parent: a9996b41e954d85fde5ec5188bbf6a7f4df88011
author: Simon Howard <fraggle@gmail.com>
date: Sat Feb 12 13:42:10 EST 2011

Merge from raven-branch. FEATURE_MULTIPLAYER has been disabled
temporarily until the netgame changes on raven-branch are finished.

Subversion-branch: /branches/strife-branch
Subversion-revision: 2259

--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,380 @@
+2011-01-02 17:45:24 fraggle
+	
+	Remove redundant package version label from top of OS X launcher
+	window.
+
+2011-01-02 02:49:20 fraggle
+	
+	Restore window title when changing video driver in setup tool (thanks
+	AlexXav).
+
+2011-01-02 02:31:20 fraggle
+	
+	Turn off dynamic window resizing feature on OS X, as it adds an ugly
+	resize handle to the corner of the window that overlaps the view of
+	the game.
+
+2010-12-28 16:43:41 fraggle
+	
+	Make demo loop handling of DEMO4 case depend on the executable version
+	being emulated: the Vanilla versions did not have any conditional
+	behavior based on gamemode/gamemission.  This has the side effect of
+	causing the game to exit with an error when playing with Final Doom,
+	but this is Vanilla behavior.
+
+2010-12-25 22:42:40 fraggle
+	
+	Include NOT-BUGS in rpm packages.
+
+2010-12-25 21:51:24 fraggle
+	
+	Pass through all command line arguments specified to the setup tool to
+	the game, to match Vanilla behavior (thanks AlexXav).
+
+2010-12-25 21:04:10 fraggle
+	
+	Remove the -wart parameter (thanks Sander van Dijk).
+
+2010-12-25 20:55:30 fraggle
+	
+	Remove the 32 character limit on the lengths of filenames specified to
+	-record (thanks AlexXav).
+
+2010-12-19 20:15:09 fraggle
+	
+	Change setup tool skill level names to match Doom's "new game" menu
+	exactly (thanks AlexXav).
+
+2010-12-18 23:55:07 fraggle
+	
+	Add a M_CheckParmWithArgs function, that behaves like M_CheckParm but
+	also checks that extra options were provided on the command line
+	(thanks Sander van Dijk).
+
+2010-12-14 20:55:30 fraggle
+	
+	Check that an address is provided to the -query command line option
+	(thanks Sander van Dijk).
+
+2010-12-12 13:11:11 fraggle
+	
+	Add -privateserver and -servername options to chocolate-server
+	manpage.  Add server registration option to setup tool (thanks
+	exp(x)).
+
+2010-12-10 23:56:32 fraggle
+	
+	Fix memory leak when dynamically resizing window in true color video
+	modes.
+
+2010-12-10 22:37:29 fraggle
+	
+	Fix build problem (thanks Proteh).
+
+2010-12-10 22:21:56 fraggle
+	
+	Remove "Error:" from the message displayed by I_Error, to match
+	Vanilla.
+
+2010-12-10 20:53:23 fraggle
+	
+	Update NEWS.
+
+2010-12-10 20:43:05 fraggle
+	
+	Change alignment of actions in a window's action area so that there is
+	equal space either side of the center widget.  This is more
+	aesthetically pleasing.
+
+2010-12-10 20:31:46 fraggle
+	
+	Replace txt_widget_t#selectable with a callback function to query
+	whether the widget is selectable.  This stops the table code from
+	selecting things that aren't really selectable - eg.  empty tables,
+	scrollpanes containing unselectable widgets, etc.  Fixes a bug with
+	the warp menu (thanks Proteh).
+
+2010-12-10 19:15:37 fraggle
+	
+	Add "warp" menu to the main menu of the setup tool, like Vanilla
+	setup.exe (thanks Proteh).
+
+2010-12-10 18:15:12 fraggle
+	
+	Fix typo (thanks Sander van Dijk).
+
+2010-12-10 17:53:50 fraggle
+	
+	Specify master server port explicitly, so that server registration
+	works when using -port.
+
+2010-12-07 23:13:34 rtc_marine
+	
+	- Update chocolate-server codeblocks project
+
+2010-12-07 22:35:17 fraggle
+	
+	Assign the oldest client to be the controller, not the first found in
+	the clients[] array.
+
+2010-12-06 23:37:27 fraggle
+	
+	Fix -solo-net to actually behave the same as other ports.
+
+2010-12-06 22:38:24 fraggle
+	
+	Rename -netdemo command line parameter to -solo-net, for consistency
+	with other ports (-netdemo is still recognised).
+
+2010-12-06 00:04:08 fraggle
+	
+	Update NEWS.
+
+2010-12-05 14:42:09 fraggle
+	
+	Allow textscreen font to be overridden using the TEXTSCREEN_FONT
+	command line variable.
+
+2010-12-04 20:56:04 fraggle
+	
+	Rename search command line options: -search to search the Internet,
+	-localsearch to search local LAN.
+
+2010-12-04 20:48:07 fraggle
+	
+	Add ping time to query output.
+
+2010-12-04 20:40:10 fraggle
+	
+	Fix formatting for -masterquery to match -search.
+
+2010-12-04 20:34:39 fraggle
+	
+	Fix bug when running with -server option.
+
+2010-12-02 21:34:51 fraggle
+	
+	Make multiple query attempts to servers before giving up.  Display a
+	warning if the master server does not respond.
+
+2010-12-02 20:32:52 fraggle
+	
+	Add -servername parameter to allow the owner to change the name
+	returned in response to queries (thanks AlexMax).
+
+2010-12-02 20:11:24 fraggle
+	
+	More refactoring of querying code, to not be specific to the purpose
+	of printing out a list.
+
+2010-12-02 19:26:05 fraggle
+	
+	Refactor query code and add a -masterquery command line parameter to
+	query the master server.
+
+2010-12-02 18:23:09 fraggle
+	
+	Register servers with Internet master server.
+
+2010-11-30 21:52:38 fraggle
+	
+	Oops.
+
+2010-11-30 20:44:20 fraggle
+	
+	Remove "-debugfile" command line option and associated variable.
+
+2010-11-30 20:26:37 fraggle
+	
+	Update NEWS.
+
+2010-11-30 20:09:22 rtc_marine
+	
+	- Update codeblocks project
+
+2010-11-30 20:00:06 fraggle
+	
+	Add support for HACX v1.2 IWAD file.
+
+2010-11-30 01:08:59 fraggle
+	
+	Add NOT-BUGS file with some common Vanilla gotchas.
+
+2010-11-29 20:18:10 fraggle
+	
+	Auto-adjust the screen color depth if the configured color depth is
+	not supported by the hardware.
+
+2010-11-27 23:23:12 fraggle
+	
+	Add dropdown list to setup tool to select screen BPP.
+
+2010-11-27 19:39:14 fraggle
+	
+	When generating the texture name lookup hash table, add new entries to
+	the end of chains.  This way, entries earlier in the texture list
+	trump later entries with the same name.  This fixes a bug with the
+	wrong sky being shown in Spooky01.wad (thanks Porsche Monty).
+
+2010-11-27 15:36:43 fraggle
+	
+	Fix -timer / -avg options to work like Vanilla when playing demos.
+
+2010-11-26 18:56:45 fraggle
+	
+	In non-palettized boxed screen modes, don't update the border areas of
+	the screen.  This is more CPU and memory efficient, and also fixes the
+	"flashing border" bug when palette flashes occur.
+
+2010-11-26 18:36:48 fraggle
+	
+	Turn double buffering on for non-palettized screen modes, as this may
+	be the cause of screen tearing reports.
+
+2010-11-24 23:34:18 fraggle
+	
+	Detect when running on Windows Vista or later, and switch to 32 bpp
+	screen mode.
+
+2010-11-24 22:43:37 fraggle
+	
+	Add configuration file parameter and command line option to specify
+	the screen pixel depth.
+
+2010-11-24 08:09:48 fraggle
+	
+	Add workaround to stop freezeups with old versions of SDL_mixer.
+
+2010-11-21 15:44:43 fraggle
+	
+	Add -8in32 command line parameter to make the game run in 32-bit color
+	mode, scaling up into an intermediate 8-bit buffer first.  This should
+	help with the palette problems experienced by Windows Vista/7 users.
+
+2010-11-09 16:10:52 fraggle
+	
+	Update Python scripts to work in Python 3.
+
+2010-09-19 21:09:36 fraggle
+	
+	Read response file in binary mode, to fix incomplete response file
+	bug.
+
+2010-08-31 21:00:20 fraggle
+	
+	Add weapon cycling buttons to joystick button list.
+
+2010-08-31 20:59:24 fraggle
+	
+	Don't double OPL sample values, as it causes horrible things to happen
+	with the Heretic title screen music.
+
+2010-08-28 19:35:08 fraggle
+	
+	Update NEWS.
+
+2010-08-28 19:28:05 fraggle
+	
+	Include INSTALL file in distribution packages.
+
+2010-08-22 18:59:12 fraggle
+	
+	Shut up compiler warning.
+
+2010-08-22 02:21:27 fraggle
+	
+	Change span drawing functions to work the same as Vanilla, so that in
+	screenshots, floors and ceilings are pixel-perfect identical to
+	Vanilla Doom (thanks Porsche Monty).
+
+2010-08-21 19:49:20 fraggle
+	
+	Change default mouse acceleration in setup tool to match the game's
+	default.
+
+2010-08-21 18:47:24 fraggle
+	
+	"Bug fix".
+
+2010-08-20 14:01:29 fraggle
+	
+	Remove debug printf().
+
+2010-08-20 13:20:58 fraggle
+	
+	Align memory allocated by zone memory system to 8 byte boundaries on
+	64-bit machines.  Possibly fixes problems on sparc64?
+
+2010-08-20 12:30:30 fraggle
+	
+	Extend mouse code to support up to 8 buttons (allows mouse wheel to be
+	used).
+
+2010-08-15 16:23:28 fraggle
+	
+	Fix volume multiply; DBOPL now generating output.
+
+2010-08-15 15:57:37 fraggle
+	
+	Hook DBOPL into OPL library and remove FMOPL.  Does not generate any
+	sound yet.
+
+2010-08-13 19:42:52 fraggle
+	
+	Add C-converted version of DOSbox OPL emulator.
+
+2010-08-09 18:53:10 fraggle
+	
+	Add weapon cycling bindings for mouse and joystick buttons.  Add
+	weapon cycling bindings to configuration file and setup tool.
+
+2010-08-07 18:23:09 fraggle
+	
+	Change back filter frequency.  Add debug code to dump resampled sound
+	effects to WAV files.
+
+2010-08-07 17:07:00 fraggle
+	
+	Fix sound resampling low pass filter.
+
+2010-08-04 19:25:04 fraggle
+	
+	Initial code for previous/next weapon switching keys.
+
+2010-08-03 21:12:36 fraggle
+	
+	When in windowed mode, allow the screen size to be dynamically resized
+	by dragging the window borders.
+
+2010-07-31 20:25:17 fraggle
+	
+	Add multiplayer spy key binding.
+
+2010-07-28 21:39:07 fraggle
+	
+	Add config file parameter to set OPL I/O port.
+
+2010-07-17 01:33:57 fraggle
+	
+	Check for libm, to fix Fedora compile issues.
+
+2010-07-14 21:36:53 fraggle
+	
+	Set MACOSX_DEPLOYMENT_TARGET to target 10.4, so that the launcher will
+	work on older versions.
+
+2010-07-10 17:06:15 fraggle
+	
+	Update NEWS and ChangeLog, bump version number.
+
+2010-07-10 16:56:18 fraggle
+	
+	Add key bindings for multiplayer messaging.
+
+2010-07-10 16:27:52 fraggle
+	
+	Add key binding to change demo recording quit key.
+
 2010-05-30 04:03:44 fraggle
 	
 	Add INSTALL to all distribution packages, add note in README.
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,7 @@
         README.OPL                      \
         TODO                            \
         BUGS                            \
+        NOT-BUGS                        \
         rpm.spec
 
 MAINTAINERCLEANFILES =  $(AUX_DIST_GEN)
--- a/NEWS
+++ b/NEWS
@@ -1,24 +1,83 @@
-1.5.0 (2010-??-??):
+1.6.0 (2011-??-??):
 
+    Bugs fixed:
+     * Menu navigation when using joystick/joypad (thanks Alexandre
+       Xavier).
+     * For configuration file value for shift keys, use scan code for
+       right shift, not left shift (thanks AlexXav).
+     * Default joystick buttons for the setup tool now match Vanilla
+       (thanks twipley).
+
+    libtextscreen:
+     * It is now possible to type a '+' in input boxes (thanks
+       Alexandre Xavier).
+
+1.5.0 (2011-01-02):
+
     Big changes in this version:
      * The DOSbox OPL emulator (DBOPL) has been imported to replace
        the older FMOPL code.  The quality of OPL emulation is now
        therefore much better.
+     * The game can now run in screen modes at any color depth (not
+       just 8-bit modes).  This is mainly to work around problems with
+       Windows Vista/7, where 8-bit color modes don't always work
+       properly.
+     * Multiplayer servers now register themselves with an Internet
+       master server.  Use the -search command line parameter to
+       find servers on the Internet to play on.  You can also use
+       DoomSeeker (http://skulltag.net/doomseeker/) which supports
+       this functionality.
      * When running in windowed mode, it is now possible to
        dynamically resize the window by dragging the window borders.
+     * Names can be specified for servers with the -servername command
+       line parameter.
      * There are now keyboard, mouse and joystick bindings to cycle
        through available weapons, making play with joypads or mobile
        devices (ie. without a proper keyboard) much more practical.
      * There is now a key binding to change the multiplayer spy key
        (usually F12).
-     * There is now a configuration file parameter to set the OPL I/O
-       port, for cards that don't use port 0x388.
+     * The setup tool now has a "warp" button on the main menu, like
+       Vanilla setup.exe (thanks Proteh).
      * Up to 8 mouse buttons are now supported (including the
        mousewheel).
+     * A new command line parameter has been added (-solo-net) which
+       can be used to simulate being in a single player netgame.
+     * There is now a configuration file parameter to set the OPL I/O
+       port, for cards that don't use port 0x388.
+     * The Python scripts used for building Chocolate Doom now work
+       with Python 3 (but also continue to work with Python 2)
+       (thanks arin).
+     * There is now a NOT-BUGS file included that lists some common
+       Vanilla Doom bugs/limitations that you might encounter
+       (thanks to Sander van Dijk for feedback).
 
+    Compatibility:
+     * The -timer and -avg options now work the same as Vanilla when
+       playing back demos (thanks xttl)
+     * A texture lookup bug was fixed that caused the wrong sky to be
+       displayed in Spooky01.wad (thanks Porsche Monty).
+     * The HacX v1.2 IWAD file is now supported, and can be used
+       standalone without the need for the Doom II IWAD (thanks
+       atyth).
+     * The I_Error function doesn't display "Error:" before the error
+       message, matching the Vanilla behavior.  "Error" has also been
+       removed from the title of the dialog box that appears on
+       Windows when this happens.  This is desirable as not all such
+       messages are actually errors (thanks Proteh).
+     * The setup tool now passes through all command line arguments
+       when launching the game (thanks AlexXav).
+     * Demo loop behavior (ie. whether to play DEMO4) now depends on
+       the version being emulated.  When playing Final Doom the game
+       will exit unexpectedly as it tries to play the fourth demo -
+       this is Vanilla behaviour (thanks AlexXav).
+
     Bugs fixed:
+     * A workaround has been a bug in old versions of SDL_mixer
+       (v1.2.8 and earlier) that could cause the game to lock up.
+       Please upgrade to a newer version if you haven't already.
      * It is now possible to use OPL emulation at 11025Hz sound
-       sampling rate (thanks to the new OPL emulator).
+       sampling rate, due to the new OPL emulator (thanks Porsche
+       Monty).
      * The span renderer function (used for drawing floors and
        ceilings) now behaves the same as Vanilla Doom, so screenshots
        are pixel-perfect identical to Vanilla Doom (thanks Porsche
@@ -25,9 +84,38 @@
        Monty).
      * The zone memory system now aligns allocated memory to 8-byte
        boundaries on 64-bit systems, which may fix crashes on systems
-       such as sparc64.
+       such as sparc64 (thanks Ryan Freeman and Edd Barrett).
      * The configure script now checks for libm, fixing compile
-       problems on Fedora Linux.
+       problems on Fedora Linux (thanks Sander van Dijk).
+     * Sound distortion with certain music files when played back
+       using OPL (eg. Heretic title screen).
+     * Error in Windows when reading response files (thanks Porsche
+       Monty, xttl, Janizdreg).
+     * Windows Vista/7 8-bit color mode issues (the default is now to
+       run in 32-bit color depth on these versions) (thanks to
+       everybody who reported this and helped test the fix).
+     * Screen borders no longer flash when running on widescreen
+       monitors, if you choose a true-color screen mode (thanks
+       exp(x)).
+     * The controller player in a netgame is the first player to join,
+       instead of just being someone who gets lucky.
+     * Command line arguments that take an option now check that an
+       option is provided (thanks Sander van Dijk).
+     * Skill level names in the setup tool are now written the same as
+       they are on the in-game "new game" menu (thanks AlexXav).
+     * There is no longer a limit on the lengths of filenames provided
+       to the -record command line parameter (thanks AlexXav).
+     * Window title is not lost in setup tool when changing video
+       driver (thanks AlexXav).
+
+    libtextscreen:
+     * The font used for the textscreen library can be forced by
+       setting the TEXTSCREEN_FONT environment variable to "small" or
+       "normal".
+     * Tables or scroll panes that don't contain any selectable widgets
+       are now themselves not selectable (thanks Proteh).
+     * The actions displayed at the bottom of windows are now laid out
+       in a more aesthetically pleasing way.
 
 1.4.0 (2010-07-10):
 
--- /dev/null
+++ b/NOT-BUGS
@@ -1,0 +1,135 @@
+
+The aim of Chocolate Doom is to behave as closely to Vanilla Doom as
+possible.  As a result, you may experience problems that you would
+also experience when using Vanilla Doom.  These are not "bugs" as
+Chocolate Doom is behaving as intended.
+
+This is not intended to be a comprehensive list of Vanilla Doom bugs.
+For more information, consult the "engine bugs" page of the Doom Wiki.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== Game exits after title screen with message about game version ==
+
+The game may exit after the title screen is shown, with a message like
+the following:
+
+    Demo is from a different game version!
+    (read 106, should be 109)
+
+    *** You may need to upgrade your version of Doom to v1.9. ***
+        See: http://doomworld.com/files/patches.shtml
+        This appears to be v1.6/v1.666.
+
+This usually indicates that your IWAD file that you are using to play
+the game (usually named doom.wad or doom2.wad) is out of date.
+Chocolate Doom only supports the v1.9 IWAD file.
+
+To fix the problem, you must upgrade to the v1.9 IWAD file.  The URL
+in the message has downloadable upgrade patches that you can use to
+upgrade.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== Game exits in demo loop when playing Final Doom ==
+
+When playing with the Final Doom IWAD files (tnt.wad, plutonia.wad),
+if you leave the game at the title screen to play through the demo
+loop, it will eventually exit with the following error message:
+
+    W_GetNumForName: demo4 not found!
+
+This is the same behavior as the Vanilla executables that were
+bundled with Final Doom.  When Ultimate Doom was developed, a fourth
+demo was added to the demo loop, and this change was retained in the
+Final Doom version of the executable.  However, the Final Doom IWADs
+do not include a fourth demo, so the game crashes.
+
+One way to work around this problem is to make the game emulate the
+original (pre-Ultimate Doom) v1.9 executable.  To do this, add the
+command line argument "-gameversion 1.9" when running the game.
+However, be aware this version does have some subtle differences that
+will affect the playback of Final Doom demos (lost soul bouncing,
+teleport behavior).
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== Game exits when accessing the options menu ==
+
+The game may exit with the message "Bad V_DrawPatch" when accessing
+the options menu, if you have your mouse sensitivity set high.
+
+The Doom options menu has a slider that allows the mouse sensitivity
+to be controlled; however, it has only a very limited range. It is
+common for experienced players to set a mouse sensitivity that is much
+higher than what can be set via the options menu. The setup program
+allows a larger range of values to be set.
+
+However, setting very high sensitivity values causes the game to exit
+when accessing the options menu under Vanilla Doom. Because Chocolate
+Doom aims to emulate Vanilla Doom as closely as possible, it does the
+same thing.
+
+One solution to the problem is to set a lower mouse sensitivity.
+Alternatively, all of the settings in the options menu can be
+controlled through Doom's key bindings anyway:
+
+    End game: F7
+    Messages on/off: F8
+    Graphic detail high/low: F5
+    Screen size smaller/larger: -/+
+    Sound volume menu: F4
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== Game exits with "Savegame buffer overrun" when saving the game ==
+
+If you are playing on a particularly large level, it is possible that
+when you save the game, the game will quit with the message "Savegame
+buffer overrun".
+
+Vanilla Doom has a limited size memory buffer that it uses for saving
+games.  If you are playing on a large level, the buffer may be too
+small for the entire savegame to fit.  Chocolate Doom allows the limit
+to be disabled: in the setup tool, go to the "compatibility" menu and
+disable the "Vanilla savegame limit" option.
+
+If this error happens to you, your game has not been lost!  A file
+named temp.dsg is saved; rename this to doomsav0.dsg to make it appear
+in the first slot in the "load game" menu.  (On Unix systems, you will
+need to look in the .chocolate-doom/savegames directory in your home
+directory)
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== Game ends suddenly when recording a demo ==
+
+If you are recording a very long demo, the game may exit suddenly.
+Vanilla Doom has a limited size memory buffer that it uses to save the
+demo into.  When the buffer is full, the game exits.  You can tell if
+this happens, as the demo file will be around 131,072 bytes in size.
+
+You can work around this by using the -maxdemo command line parameter
+to specify a larger buffer size.  Alternatively, the limit can be
+disabled: in the setup tool, go to the compatibility menu and disable
+the "Vanilla demo limit" option.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+== Game exits with a message about "visplanes" ==
+
+The game may exit with one of these messages:
+
+    R_FindPlane: no more visplanes
+    R_DrawPlanes: visplane overflow (129)
+
+This is known as the "visplane overflow" limit and is one of the most
+well-known Vanilla Doom engine limits.  You should only ever experience
+this when trying to play an add-on level.  The level you are trying to
+play is too complex; it was most likely designed to work with a limit
+removing source port.
+
+More information can be found here:
+
+    http://rome.ro/lee_killough/editing/visplane.shtml
+
--- a/README
+++ b/README
@@ -65,9 +65,13 @@
    You are encouraged to sign up and contribute any useful information
    you may have regarding the port!
 
- * Chocolate Doom is not perfect.  See the BUGS file for a list of
-   known issues. New bug reports can be submitted to the Chocolate
-   Doom bug tracker on Sourceforge.  See:
+ * Chocolate Doom is not perfect. See the BUGS file for a list of
+   known issues. Because of the nature of the project, you may also
+   encounter Vanilla Doom bugs; these are intentionally present; see
+   the NOT-BUGS file for more information.
+
+   New bug reports can be submitted to the Chocolate Doom bug tracker
+   on Sourceforge.  See:
 
      http://sourceforge.net/projects/chocolate-doom
 
--- a/codeblocks/config.h
+++ b/codeblocks/config.h
@@ -9,19 +9,19 @@
 #define PACKAGE_NAME "Chocolate Doom"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 1.4.0"
+#define PACKAGE_STRING "Chocolate Doom 1.5.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "chocolate-doom"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.4.0"
+#define PACKAGE_VERSION "1.5.0"
 
 /* Define to 1 if you have the ANSI C header files. */
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "1.4.0"
+#define VERSION "1.5.0"
 
 /* Define to 1 if your processor stores words with the most significant byte
    first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/codeblocks/game-res.rc
+++ b/codeblocks/game-res.rc
@@ -1,8 +1,8 @@
 1 ICON "../data/doom.ico"
 
 1 VERSIONINFO
-PRODUCTVERSION 1,4,0,0
-FILEVERSION 1,4,0,0
+PRODUCTVERSION 1,5,0,0
+FILEVERSION 1,5,0,0
 FILETYPE 1
 {
  BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
  {
   BLOCK "040904E4"
   {
-   VALUE "FileVersion", "1.4.0"
-   VALUE "FileDescription", "1.4.0"
+   VALUE "FileVersion", "1.5.0"
+   VALUE "FileDescription", "1.5.0"
    VALUE "InternalName", "Chocolate-Doom"
    VALUE "CompanyName", "Chocolate-Doom"
    VALUE "LegalCopyright", "GNU General Public License"
    VALUE "ProductName", "Chocolate-Doom"
-   VALUE "ProductVersion", "1.4.0"
+   VALUE "ProductVersion", "1.5.0"
   }
  }
 }
--- a/codeblocks/server.cbp
+++ b/codeblocks/server.cbp
@@ -84,6 +84,10 @@
 			<Option compilerVar="CC" />
 		</Unit>
 		<Unit filename="..\src\net_packet.h" />
+		<Unit filename="..\src\net_query.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="..\src\net_query.h" />
 		<Unit filename="..\src\net_sdl.c">
 			<Option compilerVar="CC" />
 		</Unit>
--- a/codeblocks/setup-res.rc
+++ b/codeblocks/setup-res.rc
@@ -3,8 +3,8 @@
 CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "setup-manifest.xml"
 
 1 VERSIONINFO
-PRODUCTVERSION 1,4,0,0
-FILEVERSION 1,4,0,0
+PRODUCTVERSION 1,5,0,0
+FILEVERSION 1,5,0,0
 FILETYPE 1
 {
  BLOCK "StringFileInfo"
@@ -11,13 +11,13 @@
  {
   BLOCK "040904E4"
   {
-   VALUE "FileVersion", "1.4.0"
+   VALUE "FileVersion", "1.5.0"
    VALUE "FileDescription", "Chocolate-Doom Setup"
    VALUE "InternalName", "chocolate-setup"
    VALUE "CompanyName", "fraggle@gmail.com"
    VALUE "LegalCopyright", "GNU General Public License"
    VALUE "ProductName", "Chocolate-Doom Setup"
-   VALUE "ProductVersion", "1.4.0"
+   VALUE "ProductVersion", "1.5.0"
   }
  }
 }
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-AC_INIT(Chocolate Doom, 1.4.0, fraggle@gmail.com, chocolate-doom)
+AC_INIT(Chocolate Doom, 1.5.0, fraggle@gmail.com, chocolate-doom)
 
 PACKAGE_SHORTDESC="Conservative Doom source port"
 PACKAGE_COPYRIGHT="Copyright (C) 1993-2010"
--- a/data/convert-icon
+++ b/data/convert-icon
@@ -1,7 +1,5 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #
-# $Id: convert-icon 704 2006-10-18 00:51:11Z fraggle $
-#
 # Copyright(C) 2005 Simon Howard
 #
 # This program is free software; you can redistribute it and/or
@@ -29,7 +27,7 @@
 try:
         import Image
 except ImportError:
-        print "WARNING: Could not update %s.  Please install the Python Imaging library." % sys.argv[2]
+        print("WARNING: Could not update %s.  Please install the Python Imaging library." % sys.argv[2])
         sys.exit(0)
 
 
@@ -70,5 +68,4 @@
     outfile.write("};\n")
 
 convert_image(sys.argv[1], sys.argv[2])
-
 
--- a/man/chocolate-server.6
+++ b/man/chocolate-server.6
@@ -29,6 +29,12 @@
 .TP
 \fB-port <n>\fR
 Use the specified UDP port for communications, instead of the default (2342). 
+.TP
+\fB-privateserver\fR
+Don't register with the global master server.
+.TP
+\fB-servername <name>\fR
+Specify a name for the server.
 .SH SEE ALSO
 \fBchocolate-doom\fR(6),
 \fBchocolate-setup\fR(6)
--- a/man/docgen
+++ b/man/docgen
@@ -150,11 +150,8 @@
 show_vanilla_options = True
 
 class Parameter:
-    def __cmp__(self, other):
-        if self.name < other.name:
-            return -1
-        else:
-            return 1
+    def __lt__(self, other):
+        return self.name < other.name
 
     def __init__(self):
         self.text = ""
@@ -296,10 +293,10 @@
 
     # Is this documenting a command line parameter?
 
-    match = re.search('M_CheckParm\s*\(\s*"(.*?)"\s*\)', line)
+    match = re.search('M_CheckParm(WithArgs)?\s*\(\s*"(.*?)"', line)
 
     if match:
-        param.name = match.group(1)
+        param.name = match.group(2)
         categories[param.category].add_param(param)
         return
 
@@ -389,7 +386,7 @@
     try:
         for line in f:
             line = line.replace("@content", content)
-            print line.rstrip()
+            print(line.rstrip())
 
     finally:
         f.close()
@@ -407,7 +404,7 @@
     read_wikipages()
 
     for t in targets:
-        print t.wiki_output()
+        print(t.wiki_output())
 
 def plaintext_output(targets, template_file):
 
@@ -419,13 +416,13 @@
     print_template(template_file, content)
 
 def usage():
-    print "Usage: %s [-V] [-c filename ]( -m | -w | -p ) <directory>" \
-            % sys.argv[0]
-    print "   -c :  Provide documentation for the specified configuration file"
-    print "   -m :  Manpage output"
-    print "   -w :  Wikitext output"
-    print "   -p :  Plaintext output"
-    print "   -V :  Don't show Vanilla Doom options"
+    print("Usage: %s [-V] [-c filename ]( -m | -w | -p ) <directory>" \
+            % sys.argv[0])
+    print("   -c :  Provide documentation for the specified configuration file")
+    print("   -m :  Manpage output")
+    print("   -w :  Wikitext output")
+    print("   -p :  Plaintext output")
+    print("   -V :  Don't show Vanilla Doom options")
     sys.exit(0)
 
 # Parse command line
--- a/msvc/config.h
+++ b/msvc/config.h
@@ -11,16 +11,16 @@
 #define PACKAGE_NAME "Chocolate Doom"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 1.4.0"
+#define PACKAGE_STRING "Chocolate Doom 1.5.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "chocolate-doom"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.4.0"
+#define PACKAGE_VERSION "1.5.0"
 
 /* Version number of package */
-#define VERSION "1.4.0"
+#define VERSION "1.5.0"
 
 /* Define to 1 if your processor stores words with the most significant byte
    first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/msvc/win32.rc
+++ b/msvc/win32.rc
@@ -32,8 +32,8 @@
 #endif
 
 1 VERSIONINFO
-PRODUCTVERSION 1,4,0,0
-FILEVERSION 1,4,0,0
+PRODUCTVERSION 1,5,0,0
+FILEVERSION 1,5,0,0
 FILETYPE 1
 BEGIN
 	BLOCK "StringFileInfo"
@@ -40,13 +40,13 @@
 	BEGIN
 		BLOCK "040904E4"
 		BEGIN
-			VALUE "FileVersion", "1.4.0"
-			VALUE "FileDescription", "Chocolate Doom 1.4.0"
+			VALUE "FileVersion", "1.5.0"
+			VALUE "FileDescription", "Chocolate Doom 1.5.0"
 			VALUE "InternalName", "chocolate-doom"
 			VALUE "CompanyName", "fraggle@gmail.com"
 			VALUE "LegalCopyright", "GNU General Public License"
 			VALUE "ProductName", "Chocolate Doom"
-			VALUE "ProductVersion", "1.4.0"
+			VALUE "ProductVersion", "1.5.0"
 		END
 	END
 END
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -176,8 +176,8 @@
 
     for (i=0; i<nsamples; ++i)
     {
-        buffer[i * 2] = (int16_t) (mix_buffer[i] * 2);
-        buffer[i * 2 + 1] = (int16_t) (mix_buffer[i] * 2);
+        buffer[i * 2] = (int16_t) mix_buffer[i];
+        buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
     }
 }
 
--- a/pkg/Makefile.am
+++ b/pkg/Makefile.am
@@ -5,8 +5,7 @@
 osx/Resources/app.png                                                   \
 osx/Resources/wadfile.icns                                              \
 osx/Resources/wadfile.png                                               \
-osx/Resources/launcher.nib/classes.nib                                  \
-osx/Resources/launcher.nib/info.nib                                     \
+osx/Resources/launcher.nib/designable.nib                               \
 osx/Resources/launcher.nib/keyedobjects.nib                             \
 osx/GNUmakefile                                                         \
 osx/Info.plist.in       osx/Info-gnustep.plist.in                       \
--- a/pkg/config.make.in
+++ b/pkg/config.make.in
@@ -25,6 +25,7 @@
             INSTALL      \
             NEWS         \
             BUGS         \
+            NOT-BUGS     \
             CMDLINE      \
             TODO
 
--- a/pkg/osx/LauncherManager.h
+++ b/pkg/osx/LauncherManager.h
@@ -35,7 +35,6 @@
     id launchButton;
 
     id commandLineArguments;
-    id packageLabel;
 }
 
 - (void) launch: (id)sender;
--- a/pkg/osx/LauncherManager.m
+++ b/pkg/osx/LauncherManager.m
@@ -327,7 +327,6 @@
 
 - (void) awakeFromNib
 {
-    [self->packageLabel setStringValue: @PACKAGE_STRING];
     [self->launcherWindow setTitle: @PACKAGE_NAME " Launcher"];
     [self->launcherWindow center];
     [self->launcherWindow setDefaultButtonCell: [self->launchButton cell]];
--- a/pkg/osx/Resources/launcher.nib/classes.nib
+++ /dev/null
@@ -1,47 +1,0 @@
-{
-    IBClasses = (
-        {
-            CLASS = AppController; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {launcherManager = id; }; 
-            SUPERCLASS = NSObject; 
-        }, 
-        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
-        {
-            ACTIONS = {closeConfigWindow = id; openConfigWindow = id; }; 
-            CLASS = IWADController; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {
-                chex = id; 
-                configWindow = id; 
-                doom1 = id; 
-                doom2 = id; 
-                iwadSelector = id; 
-                plutonia = id; 
-                tnt = id; 
-            }; 
-            SUPERCLASS = NSObject; 
-        }, 
-        {
-            ACTIONS = {setButtonClicked = id; }; 
-            CLASS = IWADLocation; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {locationConfigBox = id; }; 
-            SUPERCLASS = NSObject; 
-        }, 
-        {
-            ACTIONS = {launch = id; openTerminal = id; runSetup = id; }; 
-            CLASS = LauncherManager; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {
-                commandLineArguments = id; 
-                iwadController = id; 
-                launchButton = id; 
-                launcherWindow = id; 
-                packageLabel = id; 
-            }; 
-            SUPERCLASS = NSObject; 
-        }
-    ); 
-    IBVersion = 1; 
-}
\ No newline at end of file
--- /dev/null
+++ b/pkg/osx/Resources/launcher.nib/designable.nib
@@ -1,0 +1,2679 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">10F569</string>
+		<string key="IBDocument.InterfaceBuilderVersion">823</string>
+		<string key="IBDocument.AppKitVersion">1038.29</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">823</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="29"/>
+			<integer value="2"/>
+			<integer value="227"/>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="500532821">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSCustomObject" id="675889750">
+				<object class="NSMutableString" key="NSClassName">
+					<characters key="NS.bytes">NSApplication</characters>
+				</object>
+			</object>
+			<object class="NSCustomObject" id="379361643">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="226652452">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSWindowTemplate" id="790120634">
+				<int key="NSWindowStyleMask">7</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{350, 488}, {530, 190}}</string>
+				<int key="NSWTFlags">1886912512</int>
+				<string key="NSWindowTitle">(Package Name) Launcher</string>
+				<string key="NSWindowClass">NSWindow</string>
+				<object class="NSMutableString" key="NSViewClass">
+					<characters key="NS.bytes">View</characters>
+				</object>
+				<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+				<string key="NSWindowContentMinSize">{243.529, 107}</string>
+				<object class="NSView" key="NSWindowView" id="588857216">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<object class="NSMutableArray" key="NSSubviews">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSTextField" id="233433370">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 159}, {360, 11}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="840496048">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string type="base64-UTF8" key="NSContents">U2VsZWN0IGEgZ2FtZToKA</string>
+								<object class="NSFont" key="NSSupport" id="22">
+									<string key="NSName">LucidaGrande</string>
+									<double key="NSSize">9</double>
+									<int key="NSfFlags">3614</int>
+								</object>
+								<reference key="NSControlView" ref="233433370"/>
+								<object class="NSColor" key="NSBackgroundColor" id="77619338">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">controlColor</string>
+									<object class="NSColor" key="NSColor">
+										<int key="NSColorSpace">3</int>
+										<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+									</object>
+								</object>
+								<object class="NSColor" key="NSTextColor" id="206071849">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">controlTextColor</string>
+									<object class="NSColor" key="NSColor" id="773009915">
+										<int key="NSColorSpace">3</int>
+										<bytes key="NSWhite">MAA</bytes>
+									</object>
+								</object>
+							</object>
+						</object>
+						<object class="NSPopUpButton" id="936658753">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 127}, {259, 26}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSPopUpButtonCell" key="NSCell" id="968990884">
+								<int key="NSCellFlags">-2076049856</int>
+								<int key="NSCellFlags2">2048</int>
+								<object class="NSFont" key="NSSupport" id="407649812">
+									<string key="NSName">LucidaGrande</string>
+									<double key="NSSize">13</double>
+									<int key="NSfFlags">1044</int>
+								</object>
+								<reference key="NSControlView" ref="936658753"/>
+								<int key="NSButtonFlags">109199615</int>
+								<int key="NSButtonFlags2">1</int>
+								<object class="NSFont" key="NSAlternateImage" id="813720862">
+									<string key="NSName">LucidaGrande</string>
+									<double key="NSSize">13</double>
+									<int key="NSfFlags">16</int>
+								</object>
+								<string key="NSAlternateContents"/>
+								<object class="NSMutableString" key="NSKeyEquivalent">
+									<characters key="NS.bytes"/>
+								</object>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+								<object class="NSMenuItem" key="NSMenuItem" id="532676330">
+									<reference key="NSMenu" ref="562767686"/>
+									<string key="NSTitle">Game name</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<int key="NSState">1</int>
+									<object class="NSCustomResource" key="NSOnImage" id="819247708">
+										<string key="NSClassName">NSImage</string>
+										<string key="NSResourceName">NSMenuCheckmark</string>
+									</object>
+									<object class="NSCustomResource" key="NSMixedImage" id="94574589">
+										<string key="NSClassName">NSImage</string>
+										<string key="NSResourceName">NSMenuMixedState</string>
+									</object>
+									<string key="NSAction">_popUpItemAction:</string>
+									<reference key="NSTarget" ref="968990884"/>
+								</object>
+								<bool key="NSMenuItemRespectAlignment">YES</bool>
+								<object class="NSMenu" key="NSMenu" id="562767686">
+									<object class="NSMutableString" key="NSTitle">
+										<characters key="NS.bytes">OtherViews</characters>
+									</object>
+									<object class="NSMutableArray" key="NSMenuItems">
+										<bool key="EncodedWithXMLCoder">YES</bool>
+										<reference ref="532676330"/>
+									</object>
+								</object>
+								<int key="NSPreferredEdge">3</int>
+								<bool key="NSUsesItemFromMenu">YES</bool>
+								<bool key="NSAltersState">YES</bool>
+								<int key="NSArrowPosition">1</int>
+							</object>
+						</object>
+						<object class="NSImageView" id="837745433">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<object class="NSMutableSet" key="NSDragTypes">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSArray" key="set.sortedObjects">
+									<bool key="EncodedWithXMLCoder">YES</bool>
+									<string>Apple PDF pasteboard type</string>
+									<string>Apple PICT pasteboard type</string>
+									<string>Apple PNG pasteboard type</string>
+									<string>NSFilenamesPboardType</string>
+									<string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+									<string>NeXT TIFF v4.0 pasteboard type</string>
+								</object>
+							</object>
+							<string key="NSFrame">{{382, 51}, {128, 128}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSImageCell" key="NSCell" id="606342952">
+								<int key="NSCellFlags">130560</int>
+								<int key="NSCellFlags2">33554432</int>
+								<object class="NSCustomResource" key="NSContents">
+									<string key="NSClassName">NSImage</string>
+									<string key="NSResourceName">128x128</string>
+								</object>
+								<int key="NSAlign">0</int>
+								<int key="NSScale">1</int>
+								<int key="NSStyle">0</int>
+								<bool key="NSAnimates">YES</bool>
+							</object>
+							<bool key="NSEditable">YES</bool>
+						</object>
+						<object class="NSButton" id="63012253">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{281, 129}, {93, 23}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="339458432">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Configure...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="63012253"/>
+								<int key="NSButtonFlags">-2038021889</int>
+								<int key="NSButtonFlags2">32</int>
+								<reference key="NSAlternateImage" ref="813720862"/>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+							</object>
+						</object>
+						<object class="NSTextField" id="270224583">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 100}, {360, 11}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="442641975">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string type="base64-UTF8" key="NSContents">Q29tbWFuZCBsaW5lIGFyZ3VtZW50czoKA</string>
+								<reference key="NSSupport" ref="22"/>
+								<reference key="NSControlView" ref="270224583"/>
+								<reference key="NSBackgroundColor" ref="77619338"/>
+								<reference key="NSTextColor" ref="206071849"/>
+							</object>
+						</object>
+						<object class="NSTextField" id="165411811">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{20, 70}, {354, 22}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="263205275">
+								<int key="NSCellFlags">-1804468671</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="165411811"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<object class="NSColor" key="NSBackgroundColor" id="612330193">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">textBackgroundColor</string>
+									<object class="NSColor" key="NSColor">
+										<int key="NSColorSpace">3</int>
+										<bytes key="NSWhite">MQA</bytes>
+									</object>
+								</object>
+								<object class="NSColor" key="NSTextColor" id="943265597">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">textColor</string>
+									<reference key="NSColor" ref="773009915"/>
+								</object>
+							</object>
+						</object>
+						<object class="NSButton" id="521193242">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{387, 12}, {129, 32}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="660416141">
+								<int key="NSCellFlags">-2080244224</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Launch Game</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="521193242"/>
+								<int key="NSButtonFlags">-2038284033</int>
+								<int key="NSButtonFlags2">1</int>
+								<reference key="NSAlternateImage" ref="407649812"/>
+								<string key="NSAlternateContents"/>
+								<object class="NSMutableString" key="NSKeyEquivalent">
+									<characters key="NS.bytes"/>
+								</object>
+								<int key="NSPeriodicDelay">200</int>
+								<int key="NSPeriodicInterval">25</int>
+							</object>
+						</object>
+						<object class="NSButton" id="858516582">
+							<reference key="NSNextResponder" ref="588857216"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{14, 12}, {149, 32}}</string>
+							<reference key="NSSuperview" ref="588857216"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="139359493">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Run Setup Tool...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="858516582"/>
+								<int key="NSButtonFlags">-2038284033</int>
+								<int key="NSButtonFlags2">1</int>
+								<reference key="NSAlternateImage" ref="407649812"/>
+								<string key="NSAlternateContents"/>
+								<object class="NSMutableString" key="NSKeyEquivalent">
+									<characters key="NS.bytes"/>
+								</object>
+								<int key="NSPeriodicDelay">200</int>
+								<int key="NSPeriodicInterval">25</int>
+							</object>
+						</object>
+					</object>
+					<string key="NSFrameSize">{530, 190}</string>
+					<reference key="NSSuperview"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
+				<string key="NSMinSize">{243.529, 129}</string>
+				<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+			</object>
+			<object class="NSMenu" id="624798014">
+				<string key="NSTitle">MainMenu</string>
+				<object class="NSMutableArray" key="NSMenuItems">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="NSMenuItem" id="231654028">
+						<reference key="NSMenu" ref="624798014"/>
+						<string key="NSTitle">Launcher</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="819247708"/>
+						<reference key="NSMixedImage" ref="94574589"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="499410461">
+							<string key="NSTitle">Launcher</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="320960707">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">About...</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="329501564">
+									<reference key="NSMenu" ref="499410461"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="786583327">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">IWAD configuration...</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="461135404">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">Command Prompt...</string>
+									<string key="NSKeyEquiv">t</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="594796138">
+									<reference key="NSMenu" ref="499410461"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="1021771665">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="883685847">
+										<object class="NSMutableString" key="NSTitle">
+											<characters key="NS.bytes">Services</characters>
+										</object>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+										</object>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="741321419">
+									<reference key="NSMenu" ref="499410461"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="156272785">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">Hide</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="770461341">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="644979712">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="276734846">
+									<reference key="NSMenu" ref="499410461"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="730606146">
+									<reference key="NSMenu" ref="499410461"/>
+									<string key="NSTitle">Quit</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+							</object>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="729612487">
+						<reference key="NSMenu" ref="624798014"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="819247708"/>
+						<reference key="NSMixedImage" ref="94574589"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="324022003">
+							<object class="NSMutableString" key="NSTitle">
+								<characters key="NS.bytes">Edit</characters>
+							</object>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="495532942">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Undo</string>
+									<string key="NSKeyEquiv">z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="47062370">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Redo</string>
+									<string key="NSKeyEquiv">Z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="922159417">
+									<reference key="NSMenu" ref="324022003"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="463629067">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Cut</string>
+									<string key="NSKeyEquiv">x</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="854781678">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="140484273">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Paste</string>
+									<string key="NSKeyEquiv">v</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="252305464">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Delete</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="915916931">
+									<reference key="NSMenu" ref="324022003"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="419477060">
+						<reference key="NSMenu" ref="624798014"/>
+						<string key="NSTitle">Window</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="819247708"/>
+						<reference key="NSMixedImage" ref="94574589"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="893534477">
+							<object class="NSMutableString" key="NSTitle">
+								<characters key="NS.bytes">Window</characters>
+							</object>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="105811130">
+									<reference key="NSMenu" ref="893534477"/>
+									<string key="NSTitle">Minimize</string>
+									<string key="NSKeyEquiv">m</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="590253993">
+									<reference key="NSMenu" ref="893534477"/>
+									<string key="NSTitle">Zoom</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="1009742520">
+									<reference key="NSMenu" ref="893534477"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+								<object class="NSMenuItem" id="575456822">
+									<reference key="NSMenu" ref="893534477"/>
+									<string key="NSTitle">Bring All to Front</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="819247708"/>
+									<reference key="NSMixedImage" ref="94574589"/>
+								</object>
+							</object>
+							<string key="NSName">_NSWindowsMenu</string>
+						</object>
+					</object>
+				</object>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSCustomObject" id="590266459">
+				<string key="NSClassName">LauncherManager</string>
+			</object>
+			<object class="NSCustomObject" id="938927474">
+				<string key="NSClassName">IWADController</string>
+			</object>
+			<object class="NSWindowTemplate" id="193084417">
+				<int key="NSWindowStyleMask">7</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{377, 409}, {480, 316}}</string>
+				<int key="NSWTFlags">1886912512</int>
+				<string key="NSWindowTitle">IWAD configuration</string>
+				<object class="NSMutableString" key="NSWindowClass">
+					<characters key="NS.bytes">NSWindow</characters>
+				</object>
+				<object class="NSMutableString" key="NSViewClass">
+					<characters key="NS.bytes">View</characters>
+				</object>
+				<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+				<string key="NSWindowContentMinSize">{213, 107}</string>
+				<object class="NSView" key="NSWindowView" id="145141922">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<object class="NSMutableArray" key="NSSubviews">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSTextField" id="348674481">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 285}, {446, 11}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="522582983">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string key="NSContents">Doom IWAD location (doom.wad):</string>
+								<reference key="NSSupport" ref="22"/>
+								<reference key="NSControlView" ref="348674481"/>
+								<reference key="NSBackgroundColor" ref="77619338"/>
+								<reference key="NSTextColor" ref="206071849"/>
+							</object>
+						</object>
+						<object class="NSTextField" id="215057262">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{20, 255}, {369, 22}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="596525351">
+								<int key="NSCellFlags">-1804468671</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="215057262"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<reference key="NSBackgroundColor" ref="612330193"/>
+								<reference key="NSTextColor" ref="943265597"/>
+							</object>
+						</object>
+						<object class="NSButton" id="410786529">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{397, 255}, {63, 23}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="673476660">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Set...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="410786529"/>
+								<int key="NSButtonFlags">-2038021889</int>
+								<int key="NSButtonFlags2">32</int>
+								<reference key="NSAlternateImage" ref="813720862"/>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+							</object>
+						</object>
+						<object class="NSTextField" id="316721564">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 236}, {446, 11}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="663066257">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string key="NSContents">Doom II IWAD location (doom2.wad):</string>
+								<reference key="NSSupport" ref="22"/>
+								<reference key="NSControlView" ref="316721564"/>
+								<reference key="NSBackgroundColor" ref="77619338"/>
+								<reference key="NSTextColor" ref="206071849"/>
+							</object>
+						</object>
+						<object class="NSTextField" id="458378991">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{20, 206}, {369, 22}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="848829815">
+								<int key="NSCellFlags">-1804468671</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="458378991"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<reference key="NSBackgroundColor" ref="612330193"/>
+								<reference key="NSTextColor" ref="943265597"/>
+							</object>
+						</object>
+						<object class="NSButton" id="644218899">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{397, 206}, {63, 23}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="63361904">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Set...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="644218899"/>
+								<int key="NSButtonFlags">-2038021889</int>
+								<int key="NSButtonFlags2">32</int>
+								<reference key="NSAlternateImage" ref="813720862"/>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+							</object>
+						</object>
+						<object class="NSTextField" id="179636494">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 187}, {446, 11}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="84857374">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string type="base64-UTF8" key="NSContents">RmluYWwgRG9vbTogVE5UOiBFdmlsdXRpb24gbG9jYXRpb24gKHRudC53YWQpOgo</string>
+								<reference key="NSSupport" ref="22"/>
+								<reference key="NSControlView" ref="179636494"/>
+								<reference key="NSBackgroundColor" ref="77619338"/>
+								<reference key="NSTextColor" ref="206071849"/>
+							</object>
+						</object>
+						<object class="NSTextField" id="1021143679">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{20, 157}, {369, 22}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="629867670">
+								<int key="NSCellFlags">-1804468671</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="1021143679"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<reference key="NSBackgroundColor" ref="612330193"/>
+								<reference key="NSTextColor" ref="943265597"/>
+							</object>
+						</object>
+						<object class="NSButton" id="539465960">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{397, 157}, {63, 23}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="979277836">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Set...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="539465960"/>
+								<int key="NSButtonFlags">-2038021889</int>
+								<int key="NSButtonFlags2">32</int>
+								<reference key="NSAlternateImage" ref="813720862"/>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+							</object>
+						</object>
+						<object class="NSTextField" id="712311825">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 138}, {446, 11}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="131918744">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string type="base64-UTF8" key="NSContents">RmluYWwgRG9vbTogUGx1dG9uaWEgRXhwZXJpbWVudCBsb2NhdGlvbiAocGx1dG9uaWEud2FkKToKA</string>
+								<reference key="NSSupport" ref="22"/>
+								<reference key="NSControlView" ref="712311825"/>
+								<reference key="NSBackgroundColor" ref="77619338"/>
+								<reference key="NSTextColor" ref="206071849"/>
+							</object>
+						</object>
+						<object class="NSTextField" id="355049668">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{20, 108}, {369, 22}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="17259252">
+								<int key="NSCellFlags">-1804468671</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="355049668"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<reference key="NSBackgroundColor" ref="612330193"/>
+								<reference key="NSTextColor" ref="943265597"/>
+							</object>
+						</object>
+						<object class="NSButton" id="602477213">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{397, 108}, {63, 23}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="406066834">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Set...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="602477213"/>
+								<int key="NSButtonFlags">-2038021889</int>
+								<int key="NSButtonFlags2">32</int>
+								<reference key="NSAlternateImage" ref="813720862"/>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+							</object>
+						</object>
+						<object class="NSTextField" id="452288864">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{17, 89}, {446, 11}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="969661180">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">272629760</int>
+								<string key="NSContents">Chex Quest IWAD location (chex.wad):</string>
+								<reference key="NSSupport" ref="22"/>
+								<reference key="NSControlView" ref="452288864"/>
+								<reference key="NSBackgroundColor" ref="77619338"/>
+								<reference key="NSTextColor" ref="206071849"/>
+							</object>
+						</object>
+						<object class="NSTextField" id="625273251">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{20, 59}, {369, 22}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="857754300">
+								<int key="NSCellFlags">-1804468671</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="625273251"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<reference key="NSBackgroundColor" ref="612330193"/>
+								<reference key="NSTextColor" ref="943265597"/>
+							</object>
+						</object>
+						<object class="NSButton" id="680095551">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{397, 59}, {63, 23}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="1012408786">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Set...</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="680095551"/>
+								<int key="NSButtonFlags">-2038021889</int>
+								<int key="NSButtonFlags2">32</int>
+								<reference key="NSAlternateImage" ref="813720862"/>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">400</int>
+								<int key="NSPeriodicInterval">75</int>
+							</object>
+						</object>
+						<object class="NSButton" id="658359713">
+							<reference key="NSNextResponder" ref="145141922"/>
+							<int key="NSvFlags">256</int>
+							<string key="NSFrame">{{384, 12}, {82, 32}}</string>
+							<reference key="NSSuperview" ref="145141922"/>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="235375789">
+								<int key="NSCellFlags">67239424</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Close</string>
+								<reference key="NSSupport" ref="407649812"/>
+								<reference key="NSControlView" ref="658359713"/>
+								<int key="NSButtonFlags">-2038284033</int>
+								<int key="NSButtonFlags2">1</int>
+								<reference key="NSAlternateImage" ref="407649812"/>
+								<string key="NSAlternateContents"/>
+								<object class="NSMutableString" key="NSKeyEquivalent">
+									<characters key="NS.bytes"/>
+								</object>
+								<int key="NSPeriodicDelay">200</int>
+								<int key="NSPeriodicInterval">25</int>
+							</object>
+						</object>
+					</object>
+					<string key="NSFrameSize">{480, 316}</string>
+					<reference key="NSSuperview"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
+				<string key="NSMinSize">{213, 129}</string>
+				<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+			</object>
+			<object class="NSCustomObject" id="805322722">
+				<string key="NSClassName">IWADLocation</string>
+			</object>
+			<object class="NSCustomObject" id="328707004">
+				<string key="NSClassName">IWADLocation</string>
+			</object>
+			<object class="NSCustomObject" id="808449249">
+				<string key="NSClassName">IWADLocation</string>
+			</object>
+			<object class="NSCustomObject" id="645425571">
+				<string key="NSClassName">IWADLocation</string>
+			</object>
+			<object class="NSCustomObject" id="142365428">
+				<string key="NSClassName">IWADLocation</string>
+			</object>
+			<object class="NSCustomObject" id="825061065">
+				<string key="NSClassName">AppController</string>
+			</object>
+			<object class="NSCustomObject" id="201870239">
+				<string key="NSClassName">LauncherManager</string>
+			</object>
+			<object class="NSCustomObject" id="895790931">
+				<string key="NSClassName">LauncherManager</string>
+			</object>
+			<object class="NSCustomObject" id="366010945">
+				<string key="NSClassName">LauncherManager</string>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performMiniaturize:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="105811130"/>
+					</object>
+					<int key="connectionID">37</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">arrangeInFront:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="575456822"/>
+					</object>
+					<int key="connectionID">39</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="675889750"/>
+						<reference key="destination" ref="730606146"/>
+					</object>
+					<int key="connectionID">139</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontStandardAboutPanel:</string>
+						<reference key="source" ref="675889750"/>
+						<reference key="destination" ref="320960707"/>
+					</object>
+					<int key="connectionID">142</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hideOtherApplications:</string>
+						<reference key="source" ref="675889750"/>
+						<reference key="destination" ref="770461341"/>
+					</object>
+					<int key="connectionID">146</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hide:</string>
+						<reference key="source" ref="675889750"/>
+						<reference key="destination" ref="156272785"/>
+					</object>
+					<int key="connectionID">152</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unhideAllApplications:</string>
+						<reference key="source" ref="675889750"/>
+						<reference key="destination" ref="644979712"/>
+					</object>
+					<int key="connectionID">153</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performZoom:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="590253993"/>
+					</object>
+					<int key="connectionID">198</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">launcherWindow</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="790120634"/>
+					</object>
+					<int key="connectionID">207</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">commandLineArguments</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="165411811"/>
+					</object>
+					<int key="connectionID">222</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runSetup:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="858516582"/>
+					</object>
+					<int key="connectionID">223</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">launch:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="521193242"/>
+					</object>
+					<int key="connectionID">224</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openConfigWindow:</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="63012253"/>
+					</object>
+					<int key="connectionID">226</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">locationConfigBox</string>
+						<reference key="source" ref="805322722"/>
+						<reference key="destination" ref="215057262"/>
+					</object>
+					<int key="connectionID">251</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">locationConfigBox</string>
+						<reference key="source" ref="328707004"/>
+						<reference key="destination" ref="458378991"/>
+					</object>
+					<int key="connectionID">252</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">locationConfigBox</string>
+						<reference key="source" ref="808449249"/>
+						<reference key="destination" ref="1021143679"/>
+					</object>
+					<int key="connectionID">253</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">locationConfigBox</string>
+						<reference key="source" ref="142365428"/>
+						<reference key="destination" ref="625273251"/>
+					</object>
+					<int key="connectionID">254</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">setButtonClicked:</string>
+						<reference key="source" ref="805322722"/>
+						<reference key="destination" ref="410786529"/>
+					</object>
+					<int key="connectionID">255</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">setButtonClicked:</string>
+						<reference key="source" ref="328707004"/>
+						<reference key="destination" ref="644218899"/>
+					</object>
+					<int key="connectionID">256</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">setButtonClicked:</string>
+						<reference key="source" ref="808449249"/>
+						<reference key="destination" ref="539465960"/>
+					</object>
+					<int key="connectionID">257</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">setButtonClicked:</string>
+						<reference key="source" ref="142365428"/>
+						<reference key="destination" ref="680095551"/>
+					</object>
+					<int key="connectionID">258</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">closeConfigWindow:</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="658359713"/>
+					</object>
+					<int key="connectionID">259</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">doom1</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="805322722"/>
+					</object>
+					<int key="connectionID">260</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">doom2</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="328707004"/>
+					</object>
+					<int key="connectionID">261</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">tnt</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="808449249"/>
+					</object>
+					<int key="connectionID">262</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">plutonia</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="645425571"/>
+					</object>
+					<int key="connectionID">263</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">chex</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="142365428"/>
+					</object>
+					<int key="connectionID">264</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">configWindow</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="193084417"/>
+					</object>
+					<int key="connectionID">265</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">iwadSelector</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="936658753"/>
+					</object>
+					<int key="connectionID">266</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">locationConfigBox</string>
+						<reference key="source" ref="645425571"/>
+						<reference key="destination" ref="355049668"/>
+					</object>
+					<int key="connectionID">267</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openConfigWindow:</string>
+						<reference key="source" ref="938927474"/>
+						<reference key="destination" ref="786583327"/>
+					</object>
+					<int key="connectionID">268</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">iwadController</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="938927474"/>
+					</object>
+					<int key="connectionID">269</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="675889750"/>
+						<reference key="destination" ref="825061065"/>
+					</object>
+					<int key="connectionID">271</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">launcherManager</string>
+						<reference key="source" ref="825061065"/>
+						<reference key="destination" ref="590266459"/>
+					</object>
+					<int key="connectionID">272</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">setButtonClicked:</string>
+						<reference key="source" ref="645425571"/>
+						<reference key="destination" ref="602477213"/>
+					</object>
+					<int key="connectionID">273</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">paste:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="140484273"/>
+					</object>
+					<int key="connectionID">306</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">delete:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="252305464"/>
+					</object>
+					<int key="connectionID">307</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">cut:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="463629067"/>
+					</object>
+					<int key="connectionID">310</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">undo:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="495532942"/>
+					</object>
+					<int key="connectionID">313</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copy:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="854781678"/>
+					</object>
+					<int key="connectionID">315</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">selectAll:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="915916931"/>
+					</object>
+					<int key="connectionID">317</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">redo:</string>
+						<reference key="source" ref="379361643"/>
+						<reference key="destination" ref="47062370"/>
+					</object>
+					<int key="connectionID">318</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openTerminal:</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="461135404"/>
+					</object>
+					<int key="connectionID">321</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">launchButton</string>
+						<reference key="source" ref="590266459"/>
+						<reference key="destination" ref="521193242"/>
+					</object>
+					<int key="connectionID">322</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<object class="NSArray" key="object" id="0">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="children" ref="500532821"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="675889750"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="379361643"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">21</int>
+						<reference key="object" ref="790120634"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="588857216"/>
+						</object>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Launcher Window</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">2</int>
+						<reference key="object" ref="588857216"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="233433370"/>
+							<reference ref="936658753"/>
+							<reference ref="63012253"/>
+							<reference ref="270224583"/>
+							<reference ref="165411811"/>
+							<reference ref="837745433"/>
+							<reference ref="521193242"/>
+							<reference ref="858516582"/>
+						</object>
+						<reference key="parent" ref="790120634"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">209</int>
+						<reference key="object" ref="233433370"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="840496048"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">211</int>
+						<reference key="object" ref="936658753"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="968990884"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">216</int>
+						<reference key="object" ref="837745433"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="606342952"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="63012253"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="339458432"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">218</int>
+						<reference key="object" ref="270224583"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="442641975"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">219</int>
+						<reference key="object" ref="165411811"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="263205275"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">220</int>
+						<reference key="object" ref="521193242"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="660416141"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">221</int>
+						<reference key="object" ref="858516582"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="139359493"/>
+						</object>
+						<reference key="parent" ref="588857216"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="624798014"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="419477060"/>
+							<reference ref="231654028"/>
+							<reference ref="729612487"/>
+						</object>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">MainMenu</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">19</int>
+						<reference key="object" ref="419477060"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="893534477"/>
+						</object>
+						<reference key="parent" ref="624798014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">24</int>
+						<reference key="object" ref="893534477"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="575456822"/>
+							<reference ref="105811130"/>
+							<reference ref="1009742520"/>
+							<reference ref="590253993"/>
+						</object>
+						<reference key="parent" ref="419477060"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">5</int>
+						<reference key="object" ref="575456822"/>
+						<reference key="parent" ref="893534477"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">23</int>
+						<reference key="object" ref="105811130"/>
+						<reference key="parent" ref="893534477"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">92</int>
+						<reference key="object" ref="1009742520"/>
+						<reference key="parent" ref="893534477"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="590253993"/>
+						<reference key="parent" ref="893534477"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="231654028"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="499410461"/>
+						</object>
+						<reference key="parent" ref="624798014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="499410461"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="320960707"/>
+							<reference ref="786583327"/>
+							<reference ref="1021771665"/>
+							<reference ref="156272785"/>
+							<reference ref="730606146"/>
+							<reference ref="594796138"/>
+							<reference ref="741321419"/>
+							<reference ref="770461341"/>
+							<reference ref="276734846"/>
+							<reference ref="644979712"/>
+							<reference ref="329501564"/>
+							<reference ref="461135404"/>
+						</object>
+						<reference key="parent" ref="231654028"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">58</int>
+						<reference key="object" ref="320960707"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="786583327"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="1021771665"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="883685847"/>
+						</object>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="883685847"/>
+						<reference key="parent" ref="1021771665"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="156272785"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="730606146"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="594796138"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="741321419"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="770461341"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="276734846"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="644979712"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">196</int>
+						<reference key="object" ref="329501564"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">320</int>
+						<reference key="object" ref="461135404"/>
+						<reference key="parent" ref="499410461"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">274</int>
+						<reference key="object" ref="729612487"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="324022003"/>
+						</object>
+						<reference key="parent" ref="624798014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">275</int>
+						<reference key="object" ref="324022003"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="463629067"/>
+							<reference ref="854781678"/>
+							<reference ref="140484273"/>
+							<reference ref="252305464"/>
+							<reference ref="495532942"/>
+							<reference ref="915916931"/>
+							<reference ref="47062370"/>
+							<reference ref="922159417"/>
+						</object>
+						<reference key="parent" ref="729612487"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">281</int>
+						<reference key="object" ref="463629067"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">282</int>
+						<reference key="object" ref="854781678"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">287</int>
+						<reference key="object" ref="140484273"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">289</int>
+						<reference key="object" ref="252305464"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">290</int>
+						<reference key="object" ref="495532942"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">291</int>
+						<reference key="object" ref="915916931"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">293</int>
+						<reference key="object" ref="47062370"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">301</int>
+						<reference key="object" ref="922159417"/>
+						<reference key="parent" ref="324022003"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">206</int>
+						<reference key="object" ref="590266459"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">LauncherManager</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">225</int>
+						<reference key="object" ref="938927474"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">IWADController</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">227</int>
+						<reference key="object" ref="193084417"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="145141922"/>
+						</object>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Configuration Window</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">228</int>
+						<reference key="object" ref="145141922"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="348674481"/>
+							<reference ref="215057262"/>
+							<reference ref="410786529"/>
+							<reference ref="316721564"/>
+							<reference ref="458378991"/>
+							<reference ref="644218899"/>
+							<reference ref="179636494"/>
+							<reference ref="1021143679"/>
+							<reference ref="539465960"/>
+							<reference ref="712311825"/>
+							<reference ref="355049668"/>
+							<reference ref="602477213"/>
+							<reference ref="452288864"/>
+							<reference ref="625273251"/>
+							<reference ref="680095551"/>
+							<reference ref="658359713"/>
+						</object>
+						<reference key="parent" ref="193084417"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">234</int>
+						<reference key="object" ref="348674481"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="522582983"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">235</int>
+						<reference key="object" ref="215057262"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="596525351"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="410786529"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="673476660"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">238</int>
+						<reference key="object" ref="316721564"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="663066257"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">239</int>
+						<reference key="object" ref="458378991"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="848829815"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">240</int>
+						<reference key="object" ref="644218899"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="63361904"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">241</int>
+						<reference key="object" ref="179636494"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="84857374"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">242</int>
+						<reference key="object" ref="1021143679"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="629867670"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">243</int>
+						<reference key="object" ref="539465960"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="979277836"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">244</int>
+						<reference key="object" ref="712311825"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="131918744"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">245</int>
+						<reference key="object" ref="355049668"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="17259252"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">246</int>
+						<reference key="object" ref="602477213"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="406066834"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">247</int>
+						<reference key="object" ref="452288864"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="969661180"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">248</int>
+						<reference key="object" ref="625273251"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="857754300"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">249</int>
+						<reference key="object" ref="680095551"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1012408786"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">250</int>
+						<reference key="object" ref="658359713"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="235375789"/>
+						</object>
+						<reference key="parent" ref="145141922"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">229</int>
+						<reference key="object" ref="805322722"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Doom1IWAD</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">230</int>
+						<reference key="object" ref="328707004"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Doom2IWAD</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">231</int>
+						<reference key="object" ref="808449249"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">TNTIWAD</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">232</int>
+						<reference key="object" ref="645425571"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">PlutoniaIWAD</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">233</int>
+						<reference key="object" ref="142365428"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">ChexIWAD</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">270</int>
+						<reference key="object" ref="825061065"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">AppController</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">325</int>
+						<reference key="object" ref="840496048"/>
+						<reference key="parent" ref="233433370"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">326</int>
+						<reference key="object" ref="968990884"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="562767686"/>
+						</object>
+						<reference key="parent" ref="936658753"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">327</int>
+						<reference key="object" ref="606342952"/>
+						<reference key="parent" ref="837745433"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">328</int>
+						<reference key="object" ref="339458432"/>
+						<reference key="parent" ref="63012253"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">329</int>
+						<reference key="object" ref="442641975"/>
+						<reference key="parent" ref="270224583"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">330</int>
+						<reference key="object" ref="263205275"/>
+						<reference key="parent" ref="165411811"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">331</int>
+						<reference key="object" ref="660416141"/>
+						<reference key="parent" ref="521193242"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">332</int>
+						<reference key="object" ref="139359493"/>
+						<reference key="parent" ref="858516582"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">333</int>
+						<reference key="object" ref="522582983"/>
+						<reference key="parent" ref="348674481"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">334</int>
+						<reference key="object" ref="596525351"/>
+						<reference key="parent" ref="215057262"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">335</int>
+						<reference key="object" ref="673476660"/>
+						<reference key="parent" ref="410786529"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">336</int>
+						<reference key="object" ref="663066257"/>
+						<reference key="parent" ref="316721564"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">337</int>
+						<reference key="object" ref="848829815"/>
+						<reference key="parent" ref="458378991"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">338</int>
+						<reference key="object" ref="63361904"/>
+						<reference key="parent" ref="644218899"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">339</int>
+						<reference key="object" ref="84857374"/>
+						<reference key="parent" ref="179636494"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">340</int>
+						<reference key="object" ref="629867670"/>
+						<reference key="parent" ref="1021143679"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">341</int>
+						<reference key="object" ref="979277836"/>
+						<reference key="parent" ref="539465960"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">342</int>
+						<reference key="object" ref="131918744"/>
+						<reference key="parent" ref="712311825"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">343</int>
+						<reference key="object" ref="17259252"/>
+						<reference key="parent" ref="355049668"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">344</int>
+						<reference key="object" ref="406066834"/>
+						<reference key="parent" ref="602477213"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">345</int>
+						<reference key="object" ref="969661180"/>
+						<reference key="parent" ref="452288864"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">346</int>
+						<reference key="object" ref="857754300"/>
+						<reference key="parent" ref="625273251"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">347</int>
+						<reference key="object" ref="1012408786"/>
+						<reference key="parent" ref="680095551"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">348</int>
+						<reference key="object" ref="235375789"/>
+						<reference key="parent" ref="658359713"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">212</int>
+						<reference key="object" ref="562767686"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="532676330"/>
+						</object>
+						<reference key="parent" ref="968990884"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">213</int>
+						<reference key="object" ref="532676330"/>
+						<reference key="parent" ref="562767686"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="226652452"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">349</int>
+						<reference key="object" ref="201870239"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">350</int>
+						<reference key="object" ref="895790931"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">351</int>
+						<reference key="object" ref="366010945"/>
+						<reference key="parent" ref="0"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>129.IBPluginDependency</string>
+					<string>129.ImportedFromIB2</string>
+					<string>130.IBPluginDependency</string>
+					<string>130.ImportedFromIB2</string>
+					<string>131.IBPluginDependency</string>
+					<string>131.ImportedFromIB2</string>
+					<string>134.IBPluginDependency</string>
+					<string>134.ImportedFromIB2</string>
+					<string>136.IBPluginDependency</string>
+					<string>136.ImportedFromIB2</string>
+					<string>143.IBPluginDependency</string>
+					<string>143.ImportedFromIB2</string>
+					<string>144.IBPluginDependency</string>
+					<string>144.ImportedFromIB2</string>
+					<string>145.IBPluginDependency</string>
+					<string>145.ImportedFromIB2</string>
+					<string>149.IBPluginDependency</string>
+					<string>149.ImportedFromIB2</string>
+					<string>150.IBPluginDependency</string>
+					<string>150.ImportedFromIB2</string>
+					<string>19.IBPluginDependency</string>
+					<string>19.ImportedFromIB2</string>
+					<string>196.IBPluginDependency</string>
+					<string>196.ImportedFromIB2</string>
+					<string>197.IBPluginDependency</string>
+					<string>197.ImportedFromIB2</string>
+					<string>2.IBPluginDependency</string>
+					<string>2.ImportedFromIB2</string>
+					<string>206.ImportedFromIB2</string>
+					<string>209.IBPluginDependency</string>
+					<string>209.IBViewBoundsToFrameTransform</string>
+					<string>209.ImportedFromIB2</string>
+					<string>21.IBEditorWindowLastContentRect</string>
+					<string>21.IBPluginDependency</string>
+					<string>21.IBWindowTemplateEditedContentRect</string>
+					<string>21.ImportedFromIB2</string>
+					<string>21.NSWindowTemplate.visibleAtLaunch</string>
+					<string>21.windowTemplate.hasMinSize</string>
+					<string>21.windowTemplate.minSize</string>
+					<string>211.IBPluginDependency</string>
+					<string>211.IBViewBoundsToFrameTransform</string>
+					<string>211.ImportedFromIB2</string>
+					<string>212.IBPluginDependency</string>
+					<string>212.ImportedFromIB2</string>
+					<string>213.IBPluginDependency</string>
+					<string>213.ImportedFromIB2</string>
+					<string>216.IBPluginDependency</string>
+					<string>216.IBViewBoundsToFrameTransform</string>
+					<string>216.ImportedFromIB2</string>
+					<string>217.IBPluginDependency</string>
+					<string>217.IBViewBoundsToFrameTransform</string>
+					<string>217.ImportedFromIB2</string>
+					<string>218.IBPluginDependency</string>
+					<string>218.IBViewBoundsToFrameTransform</string>
+					<string>218.ImportedFromIB2</string>
+					<string>219.IBPluginDependency</string>
+					<string>219.IBViewBoundsToFrameTransform</string>
+					<string>219.ImportedFromIB2</string>
+					<string>220.IBPluginDependency</string>
+					<string>220.IBViewBoundsToFrameTransform</string>
+					<string>220.ImportedFromIB2</string>
+					<string>221.IBPluginDependency</string>
+					<string>221.IBViewBoundsToFrameTransform</string>
+					<string>221.ImportedFromIB2</string>
+					<string>225.ImportedFromIB2</string>
+					<string>227.IBEditorWindowLastContentRect</string>
+					<string>227.IBPluginDependency</string>
+					<string>227.IBWindowTemplateEditedContentRect</string>
+					<string>227.ImportedFromIB2</string>
+					<string>227.windowTemplate.hasMinSize</string>
+					<string>227.windowTemplate.minSize</string>
+					<string>228.IBPluginDependency</string>
+					<string>228.ImportedFromIB2</string>
+					<string>229.ImportedFromIB2</string>
+					<string>23.IBPluginDependency</string>
+					<string>23.ImportedFromIB2</string>
+					<string>230.ImportedFromIB2</string>
+					<string>231.ImportedFromIB2</string>
+					<string>232.ImportedFromIB2</string>
+					<string>233.ImportedFromIB2</string>
+					<string>234.IBPluginDependency</string>
+					<string>234.ImportedFromIB2</string>
+					<string>235.IBPluginDependency</string>
+					<string>235.ImportedFromIB2</string>
+					<string>236.IBPluginDependency</string>
+					<string>236.ImportedFromIB2</string>
+					<string>238.IBPluginDependency</string>
+					<string>238.ImportedFromIB2</string>
+					<string>239.IBPluginDependency</string>
+					<string>239.ImportedFromIB2</string>
+					<string>24.IBPluginDependency</string>
+					<string>24.ImportedFromIB2</string>
+					<string>240.IBPluginDependency</string>
+					<string>240.ImportedFromIB2</string>
+					<string>241.IBPluginDependency</string>
+					<string>241.ImportedFromIB2</string>
+					<string>242.IBPluginDependency</string>
+					<string>242.ImportedFromIB2</string>
+					<string>243.IBPluginDependency</string>
+					<string>243.ImportedFromIB2</string>
+					<string>244.IBPluginDependency</string>
+					<string>244.ImportedFromIB2</string>
+					<string>245.IBPluginDependency</string>
+					<string>245.ImportedFromIB2</string>
+					<string>246.IBPluginDependency</string>
+					<string>246.ImportedFromIB2</string>
+					<string>247.IBPluginDependency</string>
+					<string>247.ImportedFromIB2</string>
+					<string>248.IBPluginDependency</string>
+					<string>248.ImportedFromIB2</string>
+					<string>249.IBPluginDependency</string>
+					<string>249.ImportedFromIB2</string>
+					<string>250.IBPluginDependency</string>
+					<string>250.ImportedFromIB2</string>
+					<string>270.ImportedFromIB2</string>
+					<string>274.IBPluginDependency</string>
+					<string>274.ImportedFromIB2</string>
+					<string>275.IBPluginDependency</string>
+					<string>275.ImportedFromIB2</string>
+					<string>281.IBPluginDependency</string>
+					<string>281.ImportedFromIB2</string>
+					<string>282.IBPluginDependency</string>
+					<string>282.ImportedFromIB2</string>
+					<string>287.IBPluginDependency</string>
+					<string>287.ImportedFromIB2</string>
+					<string>289.IBPluginDependency</string>
+					<string>289.ImportedFromIB2</string>
+					<string>29.IBEditorWindowLastContentRect</string>
+					<string>29.IBPluginDependency</string>
+					<string>29.ImportedFromIB2</string>
+					<string>290.IBPluginDependency</string>
+					<string>290.ImportedFromIB2</string>
+					<string>291.IBPluginDependency</string>
+					<string>291.ImportedFromIB2</string>
+					<string>293.IBPluginDependency</string>
+					<string>293.ImportedFromIB2</string>
+					<string>301.IBPluginDependency</string>
+					<string>301.ImportedFromIB2</string>
+					<string>320.IBPluginDependency</string>
+					<string>320.ImportedFromIB2</string>
+					<string>349.IBPluginDependency</string>
+					<string>350.IBPluginDependency</string>
+					<string>351.IBPluginDependency</string>
+					<string>5.IBPluginDependency</string>
+					<string>5.ImportedFromIB2</string>
+					<string>56.IBPluginDependency</string>
+					<string>56.ImportedFromIB2</string>
+					<string>57.IBPluginDependency</string>
+					<string>57.ImportedFromIB2</string>
+					<string>58.IBPluginDependency</string>
+					<string>58.ImportedFromIB2</string>
+					<string>92.IBPluginDependency</string>
+					<string>92.ImportedFromIB2</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABBiAAAwyEAAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>{{337, 406}, {530, 190}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{337, 406}, {530, 190}}</string>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>{243.529, 107}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABBiAAAwxAAAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">AUO/AABCTAAAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABDjIAAww8AAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABBiAAAwwoAAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABBoAAAwu4AAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABDwYAAwgQAAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwgQAAA</bytes>
+					</object>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>{{329, 484}, {480, 316}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{329, 484}, {480, 316}}</string>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>{213, 107}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>{{329, 814}, {223, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<boolean value="YES"/>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">351</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">AppController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">launcherManager</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">launcherManager</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">launcherManager</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBUserSource</string>
+						<string key="minorKey"/>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">FirstResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBUserSource</string>
+						<string key="minorKey"/>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">IWADController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>closeConfigWindow:</string>
+							<string>openConfigWindow:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>closeConfigWindow:</string>
+							<string>openConfigWindow:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBActionInfo">
+								<string key="name">closeConfigWindow:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openConfigWindow:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="outlets">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>chex</string>
+							<string>configWindow</string>
+							<string>doom1</string>
+							<string>doom2</string>
+							<string>iwadSelector</string>
+							<string>plutonia</string>
+							<string>tnt</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>chex</string>
+							<string>configWindow</string>
+							<string>doom1</string>
+							<string>doom2</string>
+							<string>iwadSelector</string>
+							<string>plutonia</string>
+							<string>tnt</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBToOneOutletInfo">
+								<string key="name">chex</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">configWindow</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">doom1</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">doom2</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">iwadSelector</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">plutonia</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">tnt</string>
+								<string key="candidateClassName">id</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBUserSource</string>
+						<string key="minorKey"/>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">IWADLocation</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<string key="NS.key.0">setButtonClicked:</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<string key="NS.key.0">setButtonClicked:</string>
+						<object class="IBActionInfo" key="NS.object.0">
+							<string key="name">setButtonClicked:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">locationConfigBox</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">locationConfigBox</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">locationConfigBox</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBUserSource</string>
+						<string key="minorKey"/>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">LauncherManager</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>launch:</string>
+							<string>openTerminal:</string>
+							<string>runSetup:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>launch:</string>
+							<string>openTerminal:</string>
+							<string>runSetup:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBActionInfo">
+								<string key="name">launch:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openTerminal:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">runSetup:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="outlets">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>commandLineArguments</string>
+							<string>iwadController</string>
+							<string>launchButton</string>
+							<string>launcherWindow</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>commandLineArguments</string>
+							<string>iwadController</string>
+							<string>launchButton</string>
+							<string>launcherWindow</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBToOneOutletInfo">
+								<string key="name">commandLineArguments</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">iwadController</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">launchButton</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">launcherWindow</string>
+								<string key="candidateClassName">id</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBUserSource</string>
+						<string key="minorKey"/>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+			<integer value="1060" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="3000" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<nil key="IBDocument.LastKnownRelativeProjectPath"/>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>128x128</string>
+				<string>NSMenuCheckmark</string>
+				<string>NSMenuMixedState</string>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>{128, 128}</string>
+				<string>{9, 8}</string>
+				<string>{7, 2}</string>
+			</object>
+		</object>
+	</data>
+</archive>
--- a/pkg/osx/Resources/launcher.nib/info.nib
+++ /dev/null
@@ -1,23 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>IBDocumentLocation</key>
-	<string>325 73 612 260 0 0 1440 878 </string>
-	<key>IBEditorPositions</key>
-	<dict>
-		<key>29</key>
-		<string>221 322 205 44 0 0 1440 878 </string>
-	</dict>
-	<key>IBFramework Version</key>
-	<string>446.1</string>
-	<key>IBOpenObjects</key>
-	<array>
-		<integer>29</integer>
-		<integer>21</integer>
-		<integer>227</integer>
-	</array>
-	<key>IBSystem Version</key>
-	<string>8S2167</string>
-</dict>
-</plist>
binary files a/pkg/osx/Resources/launcher.nib/keyedobjects.nib b/pkg/osx/Resources/launcher.nib/keyedobjects.nib differ
--- a/pkg/wince/wince-cabgen
+++ b/pkg/wince/wince-cabgen
@@ -3,10 +3,11 @@
 import os
 import re
 import shutil
+import struct
 import sys
 import tempfile
 
-CAB_HEADER = "MSCE"
+CAB_HEADER = "MSCE".encode("ascii")
 
 ARCHITECTURES = {
     "shx-sh3":        103,
@@ -58,16 +59,10 @@
 }
 
 def write_int16(f, value):
-    b1 = value & 0xff
-    b2 = (value >> 8) & 0xff
-    f.write("%c%c" % (b1, b2))
+    f.write(struct.pack("<H", value))
 
 def write_int32(f, value):
-    b1 = value & 0xff
-    b2 = (value >> 8) & 0xff
-    b3 = (value >> 16) & 0xff
-    b4 = (value >> 24) & 0xff
-    f.write("%c%c%c%c" % (b1, b2, b3, b4))
+    f.write(struct.pack("<I", value))
 
 # Pad a string with NUL characters so that it has a length that is 
 # a multiple of 4.  At least one NUL is always added.
@@ -208,7 +203,7 @@
         for i, s in self.string_list:
             write_int16(stream, i)
             write_int16(stream, len(s))
-            stream.write(s)
+            stream.write(s.encode("ascii"))
 
 class DirectoryList:
     def __init__(self, cab_header):
@@ -252,7 +247,7 @@
             #    dir_path = dir_path[1:]
             dir_path = [ dir ]
 
-            dir_path = map(lambda x: dictionary.get(x), dir_path)
+            dir_path = list(map(lambda x: dictionary.get(x), dir_path))
 
             self.directories[key] = self.index
             self.directories_list.append((self.index, dir_path))
@@ -334,7 +329,7 @@
             write_int16(stream, file_no)
             write_int32(stream, flags)
             write_int16(stream, len(filename))
-            stream.write(filename)
+            stream.write(filename.encode("ascii"))
 
 # TODO?
 
@@ -412,7 +407,7 @@
         # Map dirs that make up the path to strings.
 
         dictionary = self.cab_header.dictionary
-        dest_path = map(lambda x: dictionary.get(x), dest_path)
+        dest_path = list(map(lambda x: dictionary.get(x), dest_path))
 
         self.links.append((self.index, target_type, target_id,
                            base_dir, dest_path))
@@ -492,6 +487,7 @@
             section.write(stream)
             pos = stream.tell()
             if pos != old_pos + len(section):
+                print(section)
                 raise Exception("Section is %i bytes long, but %i written" % \
                                 (len(section), pos - old_pos))
             old_pos = pos
@@ -574,7 +570,7 @@
         basename = self.__shorten_name(self.files[0], 0)
         filename = os.path.join(dir, basename)
 
-        stream = file(filename, "w")
+        stream = open(filename, "wb")
         self.cab_header.write(stream)
         stream.close()
 
@@ -625,17 +621,17 @@
     # Expand $(xyz) path variables to their Windows equivalents:
 
     def replace_var(match):
-	var_name = match.group(1)
+        var_name = match.group(1)
 
-	if not var_name in DIR_VARIABLES:
-	    raise Exception("Unknown variable '%s'" % var_name)
-	else:
-	    return DIR_VARIABLES[var_name]
+        if not var_name in DIR_VARIABLES:
+            raise Exception("Unknown variable '%s'" % var_name)
+        else:
+            return DIR_VARIABLES[var_name]
 
     return re.sub(r"\$\((.*?)\)", replace_var, filename)
 
 def read_config_file(filename):
-    f = file(filename)
+    f = open(filename)
 
     data = f.readlines()
     data = "".join(data)
@@ -656,10 +652,10 @@
     files_list = config["files"]
 
     for dest, source_file in files_list.items():
-        print source_file
+        print(source_file)
 
 if len(sys.argv) < 3:
-    print "Usage: %s <config file> <output file>" % sys.argv[0]
+    print("Usage: %s <config file> <output file>" % sys.argv[0])
     sys.exit(0)
 
 if sys.argv[1] == "-d":
--- a/rpm.spec.in
+++ b/rpm.spec.in
@@ -57,5 +57,6 @@
 %doc COPYING
 %doc CMDLINE
 %doc BUGS
+%doc NOT-BUGS
 /usr/games/*
 
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,6 +34,7 @@
 net_io.c             net_io.h              \
 net_packet.c         net_packet.h          \
 net_sdl.c            net_sdl.h             \
+net_query.c          net_query.h           \
 net_server.c         net_server.h          \
 net_structrw.c       net_structrw.h        \
 z_native.c           z_zone.h
@@ -142,9 +143,11 @@
                @SDLNET_LIBS@
 
 if HAVE_WINDRES
-@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
+@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc \
+               $(FEATURE_MULTIPLAYER_SOURCE_FILES)
 else
-@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH)
+@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) \
+               $(FEATURE_MULTIPLAYER_SOURCE_FILES)
 endif
 
 @PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS)
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -47,6 +47,7 @@
     { "doom.wad",     doom,      retail,     "Doom" },
     { "doom1.wad",    doom,      shareware,  "Doom Shareware" },
     { "chex.wad",     doom,      shareware,  "Chex Quest" },
+    { "hacx.wad",     doom2,     commercial, "Hacx" },
     { "heretic.wad",  heretic,   retail,     "Heretic" },
     { "heretic1.wad", heretic,   shareware,  "Heretic Shareware" },
     { "hexen.wad",    hexen,     commercial, "Hexen" },
@@ -657,7 +658,7 @@
     // @arg <file>
     //
 
-    iwadparm = M_CheckParm("-iwad");
+    iwadparm = M_CheckParmWithArgs("-iwad", 1);
 
     if (iwadparm)
     {
--- a/src/d_mode.h
+++ b/src/d_mode.h
@@ -63,6 +63,7 @@
 typedef enum
 {
     exe_doom_1_9,    // Doom 1.9: used for shareware, registered and commercial
+    exe_hacx,        // Hacx
     exe_ultimate,    // Ultimate Doom (retail)
     exe_final,       // Final Doom
     exe_chex,        // Chex Quest executable (based on Final Doom)
--- a/src/deh_io.c
+++ b/src/deh_io.c
@@ -30,21 +30,63 @@
 #include <string.h>
 
 #include "i_system.h"
+#include "w_wad.h"
 #include "z_zone.h"
 
 #include "deh_defs.h"
 #include "deh_io.h"
 
+typedef enum
+{
+    DEH_INPUT_FILE,
+    DEH_INPUT_LUMP
+} deh_input_type_t;
+
 struct deh_context_s
 {
-    FILE *stream;
+    deh_input_type_t type;
     char *filename;
+
+    // If the input comes from a memory buffer, pointer to the memory
+    // buffer.
+
+    unsigned char *input_buffer;
+    size_t input_buffer_len;
+    unsigned int input_buffer_pos;
+    int lumpnum;
+
+    // If the input comes from a file, the file stream for reading
+    // data.
+
+    FILE *stream;
+
+    // Current line number that we have reached:
+
     int linenum;
+
+    // Used by DEH_ReadLine:
+
     boolean last_was_newline;
     char *readbuffer;
     int readbuffer_size;
 };
 
+static deh_context_t *DEH_NewContext(void)
+{
+    deh_context_t *context;
+
+    context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
+
+    // Initial read buffer size of 128 bytes
+
+    context->readbuffer_size = 128;
+    context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
+    context->linenum = 0;
+    context->last_was_newline = true;
+
+    return context;
+}
+
 // Open a dehacked file for reading
 // Returns NULL if open failed
 
@@ -52,23 +94,42 @@
 {
     FILE *fstream;
     deh_context_t *context;
-    
+
     fstream = fopen(filename, "r");
 
     if (fstream == NULL)
         return NULL;
 
-    context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
+    context = DEH_NewContext();
+
+    context->type = DEH_INPUT_FILE;
     context->stream = fstream;
-    
-    // Initial read buffer size of 128 bytes
+    context->filename = strdup(filename);
 
-    context->readbuffer_size = 128;
-    context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
-    context->filename = filename;
-    context->linenum = 0;
-    context->last_was_newline = true;
+    return context;
+}
 
+// Open a WAD lump for reading.
+
+deh_context_t *DEH_OpenLump(int lumpnum)
+{
+    deh_context_t *context;
+    void *lump;
+
+    lump = W_CacheLumpNum(lumpnum, PU_STATIC);
+
+    context = DEH_NewContext();
+
+    context->type = DEH_INPUT_LUMP;
+    context->lumpnum = lumpnum;
+    context->input_buffer = lump;
+    context->input_buffer_len = W_LumpLength(lumpnum);
+    context->input_buffer_pos = 0;
+
+    context->filename = malloc(9);
+    strncpy(context->filename, lumpinfo[lumpnum].name, 8);
+    context->filename[8] = '\0';
+
     return context;
 }
 
@@ -76,33 +137,67 @@
 
 void DEH_CloseFile(deh_context_t *context)
 {
-    fclose(context->stream);
+    if (context->type == DEH_INPUT_FILE)
+    {
+        fclose(context->stream);
+    }
+    else if (context->type == DEH_INPUT_LUMP)
+    {
+        W_ReleaseLumpNum(context->lumpnum);
+    }
+
     Z_Free(context->readbuffer);
     Z_Free(context);
 }
 
+int DEH_GetCharFile(deh_context_t *context)
+{
+    if (feof(context->stream))
+    {
+        // end of file
+
+        return -1;
+    }
+
+    return fgetc(context->stream);
+}
+
+int DEH_GetCharLump(deh_context_t *context)
+{
+    int result;
+
+    if (context->input_buffer_pos >= context->input_buffer_len)
+    {
+        return -1;
+    }
+
+    result = context->input_buffer[context->input_buffer_pos];
+    ++context->input_buffer_pos;
+
+    return result;
+}
+
 // Reads a single character from a dehacked file
 
 int DEH_GetChar(deh_context_t *context)
 {
     int result;
-   
+
     // Read characters, but ignore carriage returns
     // Essentially this is a DOS->Unix conversion
 
-    do 
+    do
     {
-        if (feof(context->stream))
+        switch (context->type)
         {
-            // end of file
+            case DEH_INPUT_FILE:
+                result = DEH_GetCharFile(context);
+                break;
 
-            result = -1;
+            case DEH_INPUT_LUMP:
+                result = DEH_GetCharLump(context);
+                break;
         }
-        else
-        {
-            result = fgetc(context->stream);
-        }
-
     } while (result == '\r');
 
     // Track the current line number
@@ -111,9 +206,9 @@
     {
         ++context->linenum;
     }
-    
+
     context->last_was_newline = result == '\n';
-    
+
     return result;
 }
 
--- a/src/deh_io.h
+++ b/src/deh_io.h
@@ -30,6 +30,7 @@
 #include "deh_defs.h"
 
 deh_context_t *DEH_OpenFile(char *filename);
+deh_context_t *DEH_OpenLump(int lumpnum);
 void DEH_CloseFile(deh_context_t *context);
 int DEH_GetChar(deh_context_t *context);
 char *DEH_ReadLine(deh_context_t *context);
--- a/src/deh_main.c
+++ b/src/deh_main.c
@@ -32,6 +32,7 @@
 #include "doomtype.h"
 #include "d_iwad.h"
 #include "m_argv.h"
+#include "w_wad.h"
 
 #include "deh_defs.h"
 #include "deh_io.h"
@@ -246,9 +247,6 @@
         DEH_Error(context, "This is not a valid dehacked patch file!");
     }
 
-    deh_allow_long_strings = false;
-    deh_allow_long_cheats = false;
-    
     // Read the file
     
     for (;;) 
@@ -260,7 +258,9 @@
         // end of file?
 
         if (line == NULL)
+        {
             return;
+        }
 
         while (line[0] != '\0' && isspace(line[0]))
             ++line;
@@ -347,6 +347,48 @@
     return 1;
 }
 
+// Load dehacked file from WAD lump.
+
+int DEH_LoadLump(int lumpnum)
+{
+    deh_context_t *context;
+
+    // If it's in a lump, it's probably designed for a modern source port,
+    // so allow it to do long string and cheat replacements.
+
+    deh_allow_long_strings = true;
+    deh_allow_long_cheats = true;
+
+    context = DEH_OpenLump(lumpnum);
+
+    if (context == NULL)
+    {
+        fprintf(stderr, "DEH_LoadFile: Unable to open lump %i\n", lumpnum);
+        return 0;
+    }
+
+    DEH_ParseContext(context);
+
+    DEH_CloseFile(context);
+
+    return 1;
+}
+
+int DEH_LoadLumpByName(char *name)
+{
+    int lumpnum;
+
+    lumpnum = W_CheckNumForName(name);
+
+    if (lumpnum == -1)
+    {
+        fprintf(stderr, "DEH_LoadLumpByName: '%s' lump not found\n", name);
+        return 0;
+    }
+
+    return DEH_LoadLump(lumpnum);
+}
+
 // Checks the command line for -deh argument
 
 void DEH_Init(void)
@@ -386,5 +428,4 @@
         }
     }
 }
-
 
--- a/src/deh_main.h
+++ b/src/deh_main.h
@@ -41,6 +41,8 @@
 
 void DEH_Init(void);
 int DEH_LoadFile(char *filename);
+int DEH_LoadLump(int lumpnum);
+int DEH_LoadLumpByName(char *name);
 
 boolean DEH_ParseAssignment(char *line, char **variable_name, char **value);
 
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -122,8 +122,6 @@
 boolean		autostart;
 int             startloadgame;
 
-FILE*		debugfile;
-
 boolean		advancedemo;
 
 // Store demo, do not accept any inputs
@@ -424,14 +422,6 @@
     if (demorecording)
 	G_BeginRecording ();
 		
-    if (M_CheckParm ("-debugfile"))
-    {
-	char    filename[20];
-	sprintf (filename,"debug%i.txt",consoleplayer);
-	printf ("debug output to: %s\n",filename);
-	debugfile = fopen (filename,"w");
-    }
-
     TryRunTics();
 
     I_SetWindowTitle(gamedescription);
@@ -457,9 +447,12 @@
 	// process one or more tics
 	if (singletics)
 	{
+            static ticcmd_t cmds[MAXPLAYERS];
+
 	    I_StartTic ();
 	    D_ProcessEvents ();
-	    G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
+            netcmds = cmds;
+	    G_BuildTiccmd(&cmds[consoleplayer]);
 	    if (advancedemo)
 		D_DoAdvanceDemo ();
 	    M_Ticker ();
@@ -533,7 +526,13 @@
     paused = false;
     gameaction = ga_nothing;
 
-    if (gamemode == retail && gameversion != exe_chex)
+    // The Ultimate Doom executable changed the demo sequence to add
+    // a DEMO4 demo.  Final Doom was based on Ultimate, so also
+    // includes this change; however, the Final Doom IWADs do not
+    // include a DEMO4 lump, so the game bombs out with an error
+    // when it reaches this point in the demo sequence.
+
+    if (gameversion == exe_ultimate || gameversion == exe_final)
       demosequence = (demosequence+1)%7;
     else
       demosequence = (demosequence+1)%6;
@@ -608,8 +607,12 @@
 // These are from the original source: some of them are perhaps
 // not used in any dehacked patches
 
-static char *banners[] = 
+static char *banners[] =
 {
+    // doom2.wad
+    "                         "
+    "DOOM 2: Hell on Earth v%i.%i"
+    "                           ",
     // doom1.wad
     "                            "
     "DOOM Shareware Startup v%i.%i"
@@ -626,10 +629,6 @@
     "                         "
     "The Ultimate DOOM Startup v%i.%i"
     "                        ",
-    // doom2.wad
-    "                         "
-    "DOOM 2: Hell on Earth v%i.%i"
-    "                           ",
     // tnt.wad
     "                     "
     "DOOM 2: TNT - Evilution v%i.%i"
@@ -830,6 +829,18 @@
                     chex_iwadname));
 }
 
+// Check if the IWAD file is the Hacx IWAD.
+// Returns true if this is hacx.wad.
+
+static boolean CheckHacx(char *iwadname)
+{
+    char *hacx_iwadname = "hacx.wad";
+
+    return (strlen(iwadname) > strlen(hacx_iwadname)
+     && !strcasecmp(iwadname + strlen(iwadname) - strlen(hacx_iwadname),
+                    hacx_iwadname));
+}
+
 //      print title for every printed line
 char            title[128];
 
@@ -900,6 +911,7 @@
     GameVersion_t version;
 } gameversions[] = {
     {"Doom 1.9",             "1.9",        exe_doom_1_9},
+    {"Hacx",                 "hacx",       exe_hacx},
     {"Ultimate Doom",        "ultimate",   exe_ultimate},
     {"Final Doom",           "final",      exe_final},
     {"Chex Quest",           "chex",       exe_chex},
@@ -921,9 +933,9 @@
     // "ultimate" and "final".
     //
 
-    p = M_CheckParm("-gameversion");
+    p = M_CheckParmWithArgs("-gameversion", 1);
 
-    if (p > 0)
+    if (p)
     {
         for (i=0; gameversions[i].description != NULL; ++i)
         {
@@ -957,6 +969,12 @@
 
             gameversion = exe_chex;
         }
+        else if (CheckHacx(iwadfile))
+        {
+            // hacx exe: identified by iwad filename
+
+            gameversion = exe_hacx;
+        }
         else if (gamemode == shareware || gamemode == registered)
         {
             // original
@@ -1057,6 +1075,20 @@
     I_Endoom(endoom);
 }
 
+static void LoadHacxDeh(void)
+{
+    // If this is the HACX IWAD, we need to load the DEHACKED lump.
+
+    if (gameversion == exe_hacx)
+    {
+        if (!DEH_LoadLumpByName("DEHACKED"))
+        {
+            I_Error("DEHACKED lump not found.  Please check that this is the "
+                    "Hacx v1.2 IWAD.");
+        }
+    }
+}
+
 //
 // D_DoomMain
 //
@@ -1094,6 +1126,21 @@
     }
 
     //!
+    // @category net
+    //
+    // Query the Internet master server for a global list of active
+    // servers.
+    //
+
+    if (M_CheckParm("-search"))
+    {
+        printf("\nSearching for servers on Internet ...\n");
+        p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
+        printf("\n%i server(s) found.\n", p);
+        exit(0);
+    }
+
+    //!
     // @arg <address>
     // @category net
     //
@@ -1101,11 +1148,12 @@
     // address.
     //
 
-    p = M_CheckParm("-query");
+    p = M_CheckParmWithArgs("-query", 1);
 
-    if (p > 0)
+    if (p)
     {
         NET_QueryAddress(myargv[p+1]);
+        exit(0);
     }
 
     //!
@@ -1114,8 +1162,13 @@
     // Search the local LAN for running servers.
     //
 
-    if (M_CheckParm("-search"))
-        NET_LANQuery();
+    if (M_CheckParm("-localsearch"))
+    {
+        printf("\nSearching for servers on local LAN ...\n");
+        p = NET_LANQuery(NET_QueryPrintCallback, NULL);
+        printf("\n%i server(s) found.\n", p);
+        exit(0);
+    }
 
 #endif
             
@@ -1265,40 +1318,32 @@
     D_AddFile(iwadfile);
     modifiedgame = W_ParseCommandLine();
 
-    // add any files specified on the command line with -file wadfile
-    // to the wad list
+    //!
+    // @arg <files>
+    // @vanilla
     //
-    // convenience hack to allow -wart e m to add a wad file
-    // prepend a tilde to the filename so wadfile will be reloadable
-    p = M_CheckParm ("-wart");
+    // Load the specified PWAD files.
+    //
+
+    p = M_CheckParmWithArgs("-file", 1);
     if (p)
     {
-	myargv[p][4] = 'p';     // big hack, change to -warp
+	// the parms after p are wadfile/lump names,
+	// until end of parms or another - preceded parm
+	modifiedgame = true;            // homebrew levels
+	while (++p != myargc && myargv[p][0] != '-')
+        {
+            char *filename;
 
-	// Map name handling.
-	switch (gamemode )
-	{
-	  case shareware:
-	  case retail:
-	  case registered:
-	    sprintf (file,"~"DEVMAPS"E%cM%c.wad",
-		     myargv[p+1][0], myargv[p+2][0]);
-	    printf("Warping to Episode %s, Map %s.\n",
-		   myargv[p+1],myargv[p+2]);
-	    break;
-	    
-	  case commercial:
-	  default:
-	    p = atoi (myargv[p+1]);
-	    if (p<10)
-	      sprintf (file,"~"DEVMAPS"cdata/map0%i.wad", p);
-	    else
-	      sprintf (file,"~"DEVMAPS"cdata/map%i.wad", p);
-	    break;
-	}
-	D_AddFile (file);
+            filename = D_TryFindWADByName(myargv[p]);
+
+	    D_AddFile(filename);
+        }
     }
 
+    // Debug:
+//    W_PrintDirectory();
+
     //!
     // @arg <demo>
     // @category demo
@@ -1307,7 +1352,7 @@
     // Play back the demo named demo.lmp.
     //
 
-    p = M_CheckParm ("-playdemo");
+    p = M_CheckParmWithArgs ("-playdemo", 1);
 
     if (!p)
     {
@@ -1319,11 +1364,11 @@
         // Play back the demo named demo.lmp, determining the framerate
         // of the screen.
         //
-	p = M_CheckParm ("-timedemo");
+	p = M_CheckParmWithArgs("-timedemo", 1);
 
     }
 
-    if (p && p < myargc-1)
+    if (p)
     {
         if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
         {
@@ -1362,6 +1407,7 @@
     D_IdentifyVersion();
     InitGameVersion();
     LoadChexDeh();
+    LoadHacxDeh();
     D_SetGameDescription();
     SetSaveGameDir(iwadfile);
 
@@ -1404,9 +1450,9 @@
     // 0 disables all monsters.
     //
 
-    p = M_CheckParm ("-skill");
+    p = M_CheckParmWithArgs("-skill", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
 	startskill = myargv[p+1][0]-'1';
 	autostart = true;
@@ -1419,9 +1465,9 @@
     // Start playing on episode n (1-4)
     //
 
-    p = M_CheckParm ("-episode");
+    p = M_CheckParmWithArgs("-episode", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
 	startepisode = myargv[p+1][0]-'0';
 	startmap = 1;
@@ -1438,12 +1484,11 @@
     // For multiplayer games: exit each level after n minutes.
     //
 
-    p = M_CheckParm ("-timer");
+    p = M_CheckParmWithArgs("-timer", 1);
 
-    if (p && p < myargc-1 && deathmatch)
+    if (p)
     {
 	timelimit = atoi(myargv[p+1]);
-	printf("timer: %i\n", timelimit);
     }
 
     //!
@@ -1455,10 +1500,8 @@
 
     p = M_CheckParm ("-avg");
 
-    if (p && p < myargc-1 && deathmatch)
+    if (p)
     {
-        DEH_printf("Austin Virtual Gaming: Levels will end "
-                       "after 20 minutes\n");
 	timelimit = 20;
     }
 
@@ -1470,9 +1513,9 @@
     // (Doom 2)
     //
 
-    p = M_CheckParm ("-warp");
+    p = M_CheckParmWithArgs("-warp", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
         if (gamemode == commercial)
             startmap = atoi (myargv[p+1]);
@@ -1516,9 +1559,9 @@
     // Load the game in slot s.
     //
 
-    p = M_CheckParm ("-loadgame");
+    p = M_CheckParmWithArgs("-loadgame", 1);
     
-    if (p && p < myargc-1)
+    if (p)
     {
         startloadgame = atoi(myargv[p+1]);
     }
@@ -1588,16 +1631,16 @@
     // Record a demo named x.lmp.
     //
 
-    p = M_CheckParm ("-record");
+    p = M_CheckParmWithArgs("-record", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
 	G_RecordDemo (myargv[p+1]);
 	autostart = true;
     }
 
-    p = M_CheckParm ("-playdemo");
-    if (p && p < myargc-1)
+    p = M_CheckParmWithArgs("-playdemo", 1);
+    if (p)
     {
 	singledemo = true;              // quit after one demo
 	G_DeferedPlayDemo (demolumpname);
@@ -1604,8 +1647,8 @@
 	D_DoomLoop ();  // never returns
     }
 	
-    p = M_CheckParm ("-timedemo");
-    if (p && p < myargc-1)
+    p = M_CheckParmWithArgs("-timedemo", 1);
+    if (p)
     {
 	G_TimeDemo (demolumpname);
 	D_DoomLoop ();  // never returns
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -25,8 +25,8 @@
 //
 //-----------------------------------------------------------------------------
 
+#include <stdlib.h>
 
-
 #include "doomfeatures.h"
 
 #include "d_main.h"
@@ -49,26 +49,34 @@
 #include "net_sdl.h"
 #include "net_loop.h"
 
+// The complete set of data for a particular tic.
 
+typedef struct
+{
+    ticcmd_t cmds[MAXPLAYERS];
+    boolean ingame[MAXPLAYERS];
+} ticcmd_set_t;
+
 //
 // NETWORKING
 //
 // gametic is the tic about to (or currently being) run
-// maketic is the tick that hasn't had control made for it yet
-// nettics[] has the maketics for all players 
+// maketic is the tic that hasn't had control made for it yet
+// recvtic is the latest tic received from the server.
 //
-// a gametic cannot be run until nettics[] > gametic for all players
+// a gametic cannot be run until ticcmds are received for it
+// from all players.
 //
 
-ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
-int         	nettics[MAXPLAYERS];
+ticcmd_set_t ticdata[BACKUPTICS];
+ticcmd_t *netcmds;
 
 int             maketic;
+int             recvtic;
 
 // Used for original sync code.
 
-int		lastnettic;
-int             skiptics = 0;
+static int      skiptics = 0;
 
 // Reduce the bandwidth needed by sampling game input less and transmitting
 // less.  If ticdup is 2, sample half normal, 3 = one third normal, etc.
@@ -85,12 +93,8 @@
 
 // Use new client syncronisation code
 
-boolean         net_cl_new_sync = true;
+boolean         new_sync = true;
 
-// Connected but not participating in the game (observer)
-
-boolean drone = false;
-
 // 35 fps clock adjusted by offsetms milliseconds
 
 static int GetAdjustedTime(void)
@@ -99,7 +103,7 @@
 
     time_ms = I_GetTimeMS();
 
-    if (net_cl_new_sync)
+    if (new_sync)
     {
 	// Use the adjustments from net_client.c only if we are
 	// using the new sync mode.
@@ -177,7 +181,7 @@
             continue;
         }
 	
-        if (net_cl_new_sync)
+        if (new_sync)
         { 
            // If playing single player, do not allow tics to buffer
            // up very far
@@ -200,21 +204,96 @@
 	G_BuildTiccmd(&cmd);
 
 #ifdef FEATURE_MULTIPLAYER
-        
-        if (netgame && !demoplayback)
+
+        if (net_client_connected)
         {
             NET_CL_SendTiccmd(&cmd, maketic);
         }
 
 #endif
-        netcmds[consoleplayer][maketic % BACKUPTICS] = cmd;
+        ticdata[maketic % BACKUPTICS].cmds[consoleplayer] = cmd;
+        ticdata[maketic % BACKUPTICS].ingame[consoleplayer] = true;
 
 	++maketic;
-        nettics[consoleplayer] = maketic;
     }
 }
 
+// Called when a player leaves the game
+
+static void D_PlayerQuitGame(player_t *player)
+{
+    static char exitmsg[80];
+    unsigned int player_num;
+
+    player_num = player - players;
+
+    // Do this the same way as Vanilla Doom does, to allow dehacked
+    // replacements of this message
+
+    strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
+    exitmsg[sizeof(exitmsg) - 1] = '\0';
+
+    exitmsg[7] += player_num;
+
+    playeringame[player_num] = false;
+    players[consoleplayer].message = exitmsg;
+
+    // TODO: check if it is sensible to do this:
+
+    if (demorecording) 
+    {
+        G_CheckDemoStatus ();
+    }
+}
+
+static void D_Disconnected(void)
+{
+    // In drone mode, the game cannot continue once disconnected.
+
+    if (drone)
+    { 
+        I_Error("Disconnected from server in drone mode.");
+    }
+
+    // disconnected from server
+
+    printf("Disconnected from server.\n");
+}
+
 //
+// Invoked by the network engine when a complete set of ticcmds is
+// available.
+//
+
+void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
+{
+    int i;
+
+    // Disconnected from server?
+
+    if (ticcmds == NULL && players_mask == NULL)
+    {
+        D_Disconnected();
+        return;
+    }
+
+    for (i = 0; i < MAXPLAYERS; ++i)
+    {
+        if (!drone && i == consoleplayer)
+        {
+            // This is us.  Don't overwrite it.
+        }
+        else
+        {
+            ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
+            ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];
+        }
+    }
+
+    ++recvtic;
+}
+
+//
 // Start game loop
 //
 // Called after the screen is set but before the game starts running.
@@ -225,169 +304,340 @@
     lasttime = GetAdjustedTime() / ticdup;
 }
 
+// Load game settings from the specified structure and 
+// set global variables.
 
-//
-// D_CheckNetGame
-// Works out player numbers among the net participants
-//
-extern	int			viewangleoffset;
+static void LoadGameSettings(net_gamesettings_t *settings)
+{
+    unsigned int i;
 
-void D_CheckNetGame (void)
+    deathmatch = settings->deathmatch;
+    ticdup = settings->ticdup;
+    extratics = settings->extratics;
+    startepisode = settings->episode;
+    startmap = settings->map;
+    startskill = settings->skill;
+    startloadgame = settings->loadgame;
+    lowres_turn = settings->lowres_turn;
+    nomonsters = settings->nomonsters;
+    fastparm = settings->fast_monsters;
+    respawnparm = settings->respawn_monsters;
+    timelimit = settings->timelimit;
+
+    if (lowres_turn)
+    {
+        printf("NOTE: Turning resolution is reduced; this is probably "
+               "because there is a client recording a Vanilla demo.\n");
+    }
+
+    new_sync = settings->new_sync;
+
+    if (new_sync == false)
+    {
+	printf("Syncing netgames like Vanilla Doom.\n");
+    }
+
+    if (!drone)
+    {
+        consoleplayer = settings->consoleplayer;
+    }
+    else
+    {
+        consoleplayer = 0;
+    }
+    
+    for (i=0; i<MAXPLAYERS; ++i) 
+    {
+        playeringame[i] = i < settings->num_players;
+    }
+}
+
+// Save the game settings from global variables to the specified
+// game settings structure.
+
+static void SaveGameSettings(net_gamesettings_t *settings,
+                             net_connect_data_t *connect_data)
 {
     int i;
-    int num_players;
 
-    // Call D_QuitNetGame on exit 
+    // Fill in game settings structure with appropriate parameters
+    // for the new game
 
-    I_AtExit(D_QuitNetGame, true);
+    settings->deathmatch = deathmatch;
+    settings->episode = startepisode;
+    settings->map = startmap;
+    settings->skill = startskill;
+    settings->loadgame = startloadgame;
+    settings->gameversion = gameversion;
+    settings->nomonsters = nomonsters;
+    settings->fast_monsters = fastparm;
+    settings->respawn_monsters = respawnparm;
+    settings->timelimit = timelimit;
 
+    settings->lowres_turn = M_CheckParm("-record") > 0
+                         && M_CheckParm("-longtics") == 0;
+
+    //!
+    // @category net
+    //
+    // Use original game sync code.
+    //
+
+    if (M_CheckParm("-oldsync") > 0)
+	settings->new_sync = 0;
+    else
+	settings->new_sync = 1;
+    
+    //!
+    // @category net
+    // @arg <n>
+    //
+    // Send n extra tics in every packet as insurance against dropped
+    // packets.
+    //
+
+    i = M_CheckParmWithArgs("-extratics", 1);
+
+    if (i > 0)
+        settings->extratics = atoi(myargv[i+1]);
+    else
+        settings->extratics = 1;
+
+    //!
+    // @category net
+    // @arg <n>
+    //
+    // Reduce the resolution of the game by a factor of n, reducing
+    // the amount of network bandwidth needed.
+    //
+
+    i = M_CheckParmWithArgs("-dup", 1);
+
+    if (i > 0)
+        settings->ticdup = atoi(myargv[i+1]);
+    else
+        settings->ticdup = 1;
+
+    //
+    // Connect data
+    //
+
+    // Game type fields:
+
+    connect_data->gamemode = gamemode;
+    connect_data->gamemission = gamemission;
+
+    // Drone mode?
+
+    connect_data->drone = M_CheckParm("-drone") > 0;
+
+    // Are we recording a demo? Possibly set lowres turn mode
+
+    connect_data->lowres_turn = settings->lowres_turn;
+}
+
+void D_InitSinglePlayerGame(net_gamesettings_t *settings)
+{
     // default values for single player
 
-    consoleplayer = 0;
+    settings->consoleplayer = 0;
+    settings->num_players = 1;
+
     netgame = false;
-    ticdup = 1;
-    extratics = 1;
-    lowres_turn = false;
-    offsetms = 0;
-    
-    for (i=0; i<MAXPLAYERS; i++)
+
+    //!
+    // @category net
+    //
+    // Start the game playing as though in a netgame with a single
+    // player.  This can also be used to play back single player netgame
+    // demos.
+    //
+
+    if (M_CheckParm("-solo-net") > 0)
     {
-        playeringame[i] = false;
-       	nettics[i] = 0;
+        netgame = true;
     }
+}
 
-    playeringame[0] = true;
+boolean D_InitNetGame(net_connect_data_t *connect_data,
+                      net_gamesettings_t *settings)
+{
+    net_addr_t *addr = NULL;
+    int i;
 
+
 #ifdef FEATURE_MULTIPLAYER
 
+    //!
+    // @category net
+    //
+    // Start a multiplayer server, listening for connections.
+    //
+
+    if (M_CheckParm("-server") > 0)
     {
-        net_addr_t *addr = NULL;
+        NET_SV_Init();
+        NET_SV_AddModule(&net_loop_server_module);
+        NET_SV_AddModule(&net_sdl_module);
+        NET_SV_RegisterWithMaster();
 
-        //!
+        net_loop_client_module.InitClient();
+        addr = net_loop_client_module.ResolveAddress(NULL);
+    }
+    else
+    {
+        //! 
         // @category net
         //
-        // Start a multiplayer server, listening for connections.
+        // Automatically search the local LAN for a multiplayer
+        // server and join it.
         //
 
-        if (M_CheckParm("-server") > 0)
+        i = M_CheckParm("-autojoin");
+
+        if (i > 0)
         {
-            NET_SV_Init();
-            NET_SV_AddModule(&net_loop_server_module);
-            NET_SV_AddModule(&net_sdl_module);
+            addr = NET_FindLANServer();
 
-            net_loop_client_module.InitClient();
-            addr = net_loop_client_module.ResolveAddress(NULL);
+            if (addr == NULL)
+            {
+                I_Error("No server found on local LAN");
+            }
         }
-        else
-        {
-            //! 
-            // @category net
-            //
-            // Automatically search the local LAN for a multiplayer
-            // server and join it.
-            //
 
-            i = M_CheckParm("-autojoin");
+        //!
+        // @arg <address>
+        // @category net
+        //
+        // Connect to a multiplayer server running on the given 
+        // address.
+        //
+        
+        i = M_CheckParmWithArgs("-connect", 1);
 
-            if (i > 0)
-            {
-                addr = NET_FindLANServer();
+        if (i > 0)
+        {
+            net_sdl_module.InitClient();
+            addr = net_sdl_module.ResolveAddress(myargv[i+1]);
 
-                if (addr == NULL)
-                {
-                    I_Error("No server found on local LAN");
-                }
+            if (addr == NULL)
+            {
+                I_Error("Unable to resolve '%s'\n", myargv[i+1]);
             }
+        }
+    }
 
-            //!
-            // @arg <address>
-            // @category net
-            //
-            // Connect to a multiplayer server running on the given 
-            // address.
-            //
-            
-            i = M_CheckParm("-connect");
+    if (addr != NULL)
+    {
+        if (M_CheckParm("-drone") > 0)
+        {
+            connect_data->drone = true;
+        }
 
-            if (i > 0)
-            {
-                net_sdl_module.InitClient();
-                addr = net_sdl_module.ResolveAddress(myargv[i+1]);
+        //!
+        // @category net
+        //
+        // Run as the left screen in three screen mode.
+        //
 
-                if (addr == NULL)
-                {
-                    I_Error("Unable to resolve '%s'\n", myargv[i+1]);
-                }
-            }
+        if (M_CheckParm("-left") > 0)
+        {
+            viewangleoffset = ANG90;
+            connect_data->drone = true;
         }
 
-        if (addr != NULL)
+        //! 
+        // @category net
+        //
+        // Run as the right screen in three screen mode.
+        //
+
+        if (M_CheckParm("-right") > 0)
         {
-            if (M_CheckParm("-drone") > 0)
-            {
-                drone = true;
-            }
+            viewangleoffset = ANG270;
+            connect_data->drone = true;
+        }
 
-            //!
-            // @category net
-            //
-            // Run as the left screen in three screen mode.
-            //
+        if (!NET_CL_Connect(addr, connect_data))
+        {
+            I_Error("D_CheckNetGame: Failed to connect to %s\n", 
+                    NET_AddrToString(addr));
+        }
 
-            if (M_CheckParm("-left") > 0)
-            {
-                viewangleoffset = ANG90;
-                drone = true;
-            }
+        printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
 
-            //! 
-            // @category net
-            //
-            // Run as the right screen in three screen mode.
-            //
+        // Wait for game start message received from server.
 
-            if (M_CheckParm("-right") > 0)
-            {
-                viewangleoffset = ANG270;
-                drone = true;
-            }
+        NET_WaitForStart(settings);
 
-            if (!NET_CL_Connect(addr))
-            {
-                I_Error("D_CheckNetGame: Failed to connect to %s\n", 
-                        NET_AddrToString(addr));
-            }
+        // Read the game settings that were received.
 
-            printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
+        NET_CL_GetSettings(settings);
 
-            NET_WaitForStart();
-        }
+        return true;
     }
 
 #endif
 
-    num_players = 0;
+    return false;
+}
 
-    for (i=0; i<MAXPLAYERS; ++i)
+//
+// D_CheckNetGame
+// Works out player numbers among the net participants
+//
+extern	int			viewangleoffset;
+
+void D_CheckNetGame (void)
+{
+    net_connect_data_t connect_data;
+    net_gamesettings_t settings;
+
+    offsetms = 0;
+    recvtic = 0;
+
+    // Call D_QuitNetGame on exit 
+
+    I_AtExit(D_QuitNetGame, true);
+
+    SaveGameSettings(&settings, &connect_data);
+
+    if (D_InitNetGame(&connect_data, &settings))
     {
-        if (playeringame[i])
-            ++num_players;
+        netgame = true;
+        autostart = true;
     }
+    else
+    {
+        D_InitSinglePlayerGame(&settings);
+    }
 
+    LoadGameSettings(&settings);
+
     DEH_printf("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
                startskill, deathmatch, startmap, startepisode);
 	
     DEH_printf("player %i of %i (%i nodes)\n",
-               consoleplayer+1, num_players, num_players);
+               consoleplayer+1, settings.num_players, settings.num_players);
 
     // Show players here; the server might have specified a time limit
 
-    if (timelimit > 0)
+    if (timelimit > 0 && deathmatch)
     {
-	DEH_printf("Levels will end after %d minute", timelimit);
-	if (timelimit > 1)
-	    printf("s");
-	printf(".\n");
+        // Gross hack to work like Vanilla:
+
+        if (timelimit == 20 && M_CheckParm("-avg"))
+        {
+            DEH_printf("Austin Virtual Gaming: Levels will end "
+                           "after 20 minutes\n");
+        }
+        else
+        {
+            DEH_printf("Levels will end after %d minute", timelimit);
+            if (timelimit > 1)
+                printf("s");
+            printf(".\n");
+        }
     }
 }
 
@@ -399,9 +649,6 @@
 //
 void D_QuitNetGame (void)
 {
-    if (debugfile)
-	fclose (debugfile);
-
 #ifdef FEATURE_MULTIPLAYER
 
     NET_SV_Shutdown();
@@ -411,63 +658,173 @@
 
 }
 
-// Returns true if there are currently any players in the game.
+static int GetLowTic(void)
+{
+    int lowtic;
 
-static boolean PlayersInGame(void)
+    lowtic = maketic;
+
+#ifdef FEATURE_MULTIPLAYER
+    if (net_client_connected)
+    {
+        if (drone || recvtic < lowtic)
+        {
+            lowtic = recvtic;
+        }
+    }
+#endif
+
+    return lowtic;
+}
+
+int	frametics[4];
+int	frameon;
+int	frameskip[4];
+int	oldnettics;
+
+static void OldNetSync(void)
 {
-    int i;
+    unsigned int i;
+    unsigned int keyplayer = -1;
 
-    for (i=0; i<MAXPLAYERS; ++i)
+    frameon++;
+
+    // ideally maketic should be 1 - 3 tics above lowtic
+    // if we are consistantly slower, speed up time
+
+    for (i=0 ; i<MAXPLAYERS ; i++)
     {
+        // TODO: playeringame should not be used here.
+
         if (playeringame[i])
         {
-            return true;
+            keyplayer = i;
+            break;
         }
     }
 
-    return false;
+    if (keyplayer < 0)
+    {
+        // If there are no players, we can never advance anyway
+
+        return;
+    }
+
+    if (consoleplayer == keyplayer)
+    {
+        // the key player does not adapt
+    }
+    else
+    {
+        if (maketic <= recvtic)
+        {
+            lasttime--;
+            // printf ("-");
+        }
+
+        frameskip[frameon & 3] = oldnettics > recvtic;
+        oldnettics = maketic;
+
+        if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
+        {
+            skiptics = 1;
+            // printf ("+");
+        }
+    }
 }
 
-static int GetLowTic(void)
+// Returns true if there are players in the game:
+
+static boolean PlayersInGame(void)
 {
-    int lowtic;
+    boolean result = false;
+    unsigned int i;
 
-#ifdef FEATURE_MULTIPLAYER
+    // If we are connected to a server, check if there are any players
+    // in the game.
+
     if (net_client_connected)
     {
-        int i;
+        for (i = 0; i < MAXPLAYERS; ++i)
+        {
+            result = result || playeringame[i];
+        }
+    }
 
-        lowtic = INT_MAX;
-    
-        for (i=0; i<MAXPLAYERS; ++i)
+    // Whether single or multi-player, unless we are running as a drone,
+    // we are in the game.
+
+    if (!drone)
+    {
+        result = true;
+    }
+
+    return result;
+}
+
+// When using ticdup, certain values must be cleared out when running
+// the duplicate ticcmds.
+
+static void TicdupSquash(ticcmd_set_t *set)
+{
+    ticcmd_t *cmd;
+    unsigned int i;
+                    
+    for (i = 0; i < MAXPLAYERS ; ++i)
+    {
+        cmd = &set->cmds[i];
+        cmd->chatchar = 0;
+        if (cmd->buttons & BT_SPECIAL)
+            cmd->buttons = 0;
+    }
+}
+
+static void D_RunTic(ticcmd_set_t *set)
+{
+    extern boolean advancedemo;
+    unsigned int i;
+
+    // Check for player quits.
+
+    for (i = 0; i < MAXPLAYERS; ++i)
+    {
+        if (playeringame[i] && !set->ingame[i])
         {
-            if (playeringame[i])
-            {
-                if (nettics[i] < lowtic)
-                    lowtic = nettics[i];
-            }
+            D_PlayerQuitGame(&players[i]);
         }
     }
-    else
-#endif
+
+    netcmds = set->cmds;
+
+    // check that there are players in the game.  if not, we cannot
+    // run a tic.
+
+    if (advancedemo)
+        D_DoAdvanceDemo ();
+
+    G_Ticker ();
+}
+
+// When running in single player mode, clear all the ingame[] array
+// except the consoleplayer.
+
+static void SinglePlayerClear(ticcmd_set_t *set)
+{
+    unsigned int i;
+
+    for (i = 0; i < MAXPLAYERS; ++i)
     {
-        lowtic = maketic;
+        if (i != consoleplayer)
+        {
+            set->ingame[i] = false;
+        }
     }
-
-    return lowtic;
 }
 
 //
 // TryRunTics
 //
-int	oldnettics;
-int	frametics[4];
-int	frameon;
-int	frameskip[4];
-int	oldnettics;
 
-extern	boolean	advancedemo;
-
 void TryRunTics (void)
 {
     int	i;
@@ -492,7 +849,7 @@
     
     // decide how many tics to run
     
-    if (net_cl_new_sync)
+    if (new_sync)
     {
 	counts = availabletics;
     }
@@ -509,52 +866,9 @@
         if (counts < 1)
             counts = 1;
                     
-        frameon++;
-
-        if (!demoplayback)
+        if (net_client_connected)
         {
-	    int keyplayer = -1;
-
-            // ideally maketic should be 1 - 3 tics above lowtic
-            // if we are consistantly slower, speed up time
-
-            for (i=0 ; i<MAXPLAYERS ; i++)
-	    {
-                if (playeringame[i])
-		{
-		    keyplayer = i;
-                    break;
-		}
-	    }
-
-	    if (keyplayer < 0)
-	    {
-		// If there are no players, we can never advance anyway
-
-		return;
-	    }
-
-            if (consoleplayer == keyplayer)
-            {
-                // the key player does not adapt
-            }
-            else
-            {
-                if (maketic <= nettics[keyplayer])
-                {
-                    lasttime--;
-                    // printf ("-");
-                }
-
-                frameskip[frameon & 3] = (oldnettics > nettics[keyplayer]);
-                oldnettics = maketic;
-
-                if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
-                {
-                    skiptics = 1;
-                    // printf ("+");
-                }
-            }
+            OldNetSync();
         }
     }
 
@@ -586,41 +900,33 @@
     // run the count * ticdup dics
     while (counts--)
     {
+        ticcmd_set_t *set;
+
+        if (!PlayersInGame())
+        {
+            return;
+        }
+
+        set = &ticdata[(gametic / ticdup) % BACKUPTICS];
+
+        if (!net_client_connected)
+        {
+            SinglePlayerClear(set);
+        }
+
 	for (i=0 ; i<ticdup ; i++)
 	{
-            // check that there are players in the game.  if not, we cannot
-            // run a tic.
-        
-            if (!PlayersInGame())
-            {
-                return;
-            }
-    
-	    if (gametic/ticdup > lowtic)
-		I_Error ("gametic>lowtic");
-	    if (advancedemo)
-		D_DoAdvanceDemo ();
+            if (gametic/ticdup > lowtic)
+                I_Error ("gametic>lowtic");
 
-	    G_Ticker ();
+            D_RunTic(set);
 	    gametic++;
 	    
 	    // modify command for duplicated tics
-	    if (i != ticdup-1)
-	    {
-		ticcmd_t	*cmd;
-		int			buf;
-		int			j;
-				
-		buf = (gametic/ticdup)%BACKUPTICS; 
-		for (j=0 ; j<MAXPLAYERS ; j++)
-		{
-		    cmd = &netcmds[j][buf];
-		    cmd->chatchar = 0;
-		    if (cmd->buttons & BT_SPECIAL)
-			cmd->buttons = 0;
-		}
-	    }
+
+            TicdupSquash(set);
 	}
+
 	NetUpdate ();	// check for new console commands
     }
 }
--- a/src/doom/d_net.h
+++ b/src/doom/d_net.h
@@ -45,7 +45,6 @@
 // Called at start of game loop to initialize timers
 void D_StartGameLoop(void);
 
-extern boolean drone;
 extern boolean net_cl_new_sync;
 
 #endif
--- a/src/doom/doomstat.h
+++ b/src/doom/doomstat.h
@@ -255,7 +255,6 @@
 // File handling stuff.
 extern  char *          savegamedir;
 extern	char		basedefault[1024];
-extern  FILE*		debugfile;
 
 // if true, load all graphics at level load
 extern  boolean         precache;
@@ -289,7 +288,7 @@
 extern	int		maketic;
 extern  int             nettics[MAXPLAYERS];
 
-extern  ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
+extern  ticcmd_t       *netcmds;
 extern	int		ticdup;
 
 
--- a/src/doom/dstrings.h
+++ b/src/doom/dstrings.h
@@ -38,15 +38,6 @@
 #define SAVEGAMENAME	"doomsav"
 
 
-//
-// File locations,
-//  relative to current position.
-// Path names are OS-sensitive.
-//
-#define DEVMAPS "devmaps"
-#define DEVDATA "devdata"
-
-
 // QuitDOOM messages
 // 8 per each game type
 #define NUM_QUITMESSAGES   8
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -138,7 +138,7 @@
 int             levelstarttic;          // gametic at level start 
 int             totalkills, totalitems, totalsecret;    // for intermission 
  
-char            demoname[32]; 
+char           *demoname;
 boolean         demorecording; 
 boolean         longtics;               // cph's doom 1.91 longtics hack
 boolean         lowres_turn;            // low resolution turning for longtics
@@ -986,9 +986,9 @@
 	if (playeringame[i]) 
 	{ 
 	    cmd = &players[i].cmd; 
- 
-	    memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t)); 
- 
+
+	    memcpy(cmd, &netcmds[i], sizeof(ticcmd_t));
+
 	    if (demoplayback) 
 		G_ReadDemoTiccmd (cmd); 
 	    if (demorecording) 
@@ -1978,14 +1978,14 @@
 //
 // G_RecordDemo 
 // 
-void G_RecordDemo (char* name) 
+void G_RecordDemo (char *name) 
 { 
     int             i; 
     int				maxsize;
 	
     usergame = false; 
-    strcpy (demoname, name); 
-    strcat (demoname, ".lmp"); 
+    demoname = Z_Malloc(strlen(name) + 5, PU_STATIC, NULL);
+    sprintf(demoname, "%s.lmp", name);
     maxsize = 0x20000;
 
     //!
@@ -1996,8 +1996,8 @@
     // Specify the demo buffer size (KiB)
     //
 
-    i = M_CheckParm ("-maxdemo");
-    if (i && i<myargc-1)
+    i = M_CheckParmWithArgs("-maxdemo", 1);
+    if (i)
 	maxsize = atoi(myargv[i+1])*1024;
     demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); 
     demoend = demobuffer + maxsize;
@@ -2145,16 +2145,11 @@
     for (i=0 ; i<MAXPLAYERS ; i++) 
 	playeringame[i] = *demo_p++; 
 
-    //!
-    // @category demo
-    // 
-    // Play back a demo recorded in a netgame with a single player.
-    //
-
-    if (playeringame[1] || M_CheckParm("-netdemo") > 0) 
-    { 
-	netgame = true; 
-	netdemo = true; 
+    if (playeringame[1] || M_CheckParm("-solo-net") > 0
+                        || M_CheckParm("-netdemo") > 0)
+    {
+	netgame = true;
+	netdemo = true;
     }
 
     // don't spend a lot of time in loadlevel 
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -762,6 +762,8 @@
     switch (gameversion)
     {
         case exe_doom_1_9:
+        case exe_hacx:
+
             if (gamemode == commercial)
             {
                 // Doom 2
@@ -1440,23 +1442,23 @@
 	
     if (ev->type == ev_joystick && joywait < I_GetTime())
     {
-	if (ev->data3 == -1)
+	if (ev->data3 < 0)
 	{
 	    key = key_menu_up;
 	    joywait = I_GetTime() + 5;
 	}
-	else if (ev->data3 == 1)
+	else if (ev->data3 > 0)
 	{
 	    key = key_menu_down;
 	    joywait = I_GetTime() + 5;
 	}
 		
-	if (ev->data2 == -1)
+	if (ev->data2 < 0)
 	{
 	    key = key_menu_left;
 	    joywait = I_GetTime() + 2;
 	}
-	else if (ev->data2 == 1)
+	else if (ev->data2 > 0)
 	{
 	    key = key_menu_right;
 	    joywait = I_GetTime() + 2;
--- a/src/doom/p_map.c
+++ b/src/doom/p_map.c
@@ -1426,7 +1426,7 @@
         // Use the specified magic value when emulating spechit overruns.
         //
 
-        p = M_CheckParm("-spechit");
+        p = M_CheckParmWithArgs("-spechit", 1);
         
         if (p > 0)
         {
--- a/src/doom/p_setup.c
+++ b/src/doom/p_setup.c
@@ -755,17 +755,7 @@
     // Make sure all sounds are stopped before Z_FreeTags.
     S_Start ();			
 
-    
-#if 0 // UNUSED
-    if (debugfile)
-    {
-	Z_FreeTags (PU_LEVEL, INT_MAX);
-	Z_FileDumpHeap (debugfile);
-    }
-    else
-#endif
-	Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
-
+    Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
 
     // UNUSED W_Profile ();
     P_InitThinkers ();
--- a/src/doom/p_spec.c
+++ b/src/doom/p_spec.c
@@ -1213,9 +1213,9 @@
         // system.  The default (if this option is not specified) is to
         // emulate the behavior when running under Windows 98.
 
-        p = M_CheckParm("-donut");
+        p = M_CheckParmWithArgs("-donut", 2);
 
-        if (p > 0 && p < myargc - 2)
+        if (p > 0)
         {
             // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008
             //
@@ -1389,10 +1389,9 @@
     if (W_CheckNumForName(DEH_String("texture2")) >= 0)
 	episode = 2;
 
-    
     // See if -TIMER was specified.
 
-    if (timelimit > 0)
+    if (timelimit > 0 && deathmatch)
     {
         levelTimer = true;
         levelTimeCount = timelimit * 60 * TICRATE;
@@ -1401,7 +1400,7 @@
     {
 	levelTimer = false;
     }
-    
+
     //	Init special SECTORs.
     sector = sectors;
     for (i=0 ; i<numsectors ; i++, sector++)
--- a/src/doom/r_data.c
+++ b/src/doom/r_data.c
@@ -413,6 +413,7 @@
 
 static void GenerateTextureHashTable(void)
 {
+    texture_t **rover;
     int i;
     int key;
 
@@ -429,12 +430,25 @@
 
         textures[i]->index = i;
 
-        // Hook into hash table
+        // Vanilla Doom does a linear search of the texures array
+        // and stops at the first entry it finds.  If there are two
+        // entries with the same name, the first one in the array
+        // wins. The new entry must therefore be added at the end
+        // of the hash chain, so that earlier entries win.
 
         key = W_LumpNameHash(textures[i]->name) % numtextures;
 
-        textures[i]->next = textures_hashtable[key];
-        textures_hashtable[key] = textures[i];
+        rover = &textures_hashtable[key];
+
+        while (*rover != NULL)
+        {
+            rover = &(*rover)->next;
+        }
+
+        // Hook into hash table
+
+        textures[i]->next = NULL;
+        *rover = textures[i];
     }
 }
 
--- a/src/doomfeatures.h
+++ b/src/doomfeatures.h
@@ -36,8 +36,9 @@
 #define FEATURE_DEHACKED 1
 
 // Enables multiplayer support (network games)
+// STRIFE-TODO: multiplayer is currently broken ...
 
-//#define FEATURE_MULTIPLAYER 1
+// #define FEATURE_MULTIPLAYER 1
 
 // Enables sound output
 
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -53,6 +53,8 @@
 #define MAX_SOUND_SLICE_TIME 70 /* ms */
 #define NUM_CHANNELS 16
 
+static boolean setpanning_workaround = false;
+
 static boolean sound_initialized = false;
 
 static sfxinfo_t *channels_playing[NUM_CHANNELS];
@@ -632,10 +634,19 @@
     if (right < 0) right = 0;
     else if (right > 255) right = 255;
 
+    // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
+    // function.  A workaround is to call Mix_UnregisterAllEffects for
+    // the channel before calling it.  This is undesirable as it may lead
+    // to the channel volumes resetting briefly.
+
+    if (setpanning_workaround)
+    {
+        Mix_UnregisterAllEffects(handle);
+    }
+
     Mix_SetPanning(handle, left, right);
 }
 
-
 //
 // Starting a sound means adding it
 //  to the current list of active sounds
@@ -822,8 +833,34 @@
     }
 #endif
 
+    // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
+    // function that can cause the game to lock up.  If we're using an old
+    // version, we need to apply a workaround.  But the workaround has its
+    // own drawbacks ...
+
+    {
+        const SDL_version *mixer_version;
+        int v;
+
+        mixer_version = Mix_Linked_Version();
+        v = SDL_VERSIONNUM(mixer_version->major,
+                           mixer_version->minor,
+                           mixer_version->patch);
+
+        if (v <= SDL_VERSIONNUM(1, 2, 8))
+        {
+            setpanning_workaround = true;
+            fprintf(stderr, "\n"
+              "ATTENTION: You are using an old version of SDL_mixer!\n"
+              "           This version has a bug that may cause "
+                          "your sound to stutter.\n"
+              "           Please upgrade to a newer version!\n"
+              "\n");
+        }
+    }
+
     Mix_AllocateChannels(NUM_CHANNELS);
-    
+
     SDL_PauseAudio(0);
 
     sound_initialized = true;
--- a/src/i_system.c
+++ b/src/i_system.c
@@ -189,7 +189,7 @@
     // Specify the heap size, in MiB (default 16).
     //
 
-    p = M_CheckParm("-mb");
+    p = M_CheckParmWithArgs("-mb", 1);
 
     if (p > 0)
     {
@@ -327,9 +327,9 @@
     
     // Message first.
     va_start(argptr, error);
-    fprintf(stderr, "\nError: ");
+    //fprintf(stderr, "\nError: ");
     vfprintf(stderr, error, argptr);
-    fprintf(stderr, "\n");
+    fprintf(stderr, "\n\n");
     va_end(argptr);
     fflush(stderr);
 
@@ -362,7 +362,7 @@
                             msgbuf, strlen(msgbuf) + 1,
                             wmsgbuf, sizeof(wmsgbuf));
 
-        MessageBoxW(NULL, wmsgbuf, L"Error", MB_OK);
+        MessageBoxW(NULL, wmsgbuf, L"", MB_OK);
     }
 #endif
 
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -31,6 +31,11 @@
 #include <math.h>
 #include <string.h>
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
 #include "icon.c"
 
 #include "config.h"
@@ -139,6 +144,12 @@
 
 static char *window_title = "";
 
+// Intermediate 8-bit buffer that we draw to instead of 'screen'.
+// This is used when we are rendering in 32-bit screen mode.
+// When in a real 8-bit screen mode, screenbuffer == screen.
+
+static SDL_Surface *screenbuffer = NULL;
+
 // palette
 
 static SDL_Color palette[256];
@@ -172,6 +183,10 @@
 static int screen_width = SCREENWIDTH;
 static int screen_height = SCREENHEIGHT;
 
+// Color depth.
+
+int screen_bpp = 8;
+
 // Automatically adjust video settings if the selected mode is 
 // not a valid video mode.
 
@@ -911,17 +926,18 @@
 	return true;
     }
 
-    x_offset = (screen->w - screen_mode->width) / 2;
-    y_offset = (screen->h - screen_mode->height) / 2;
+    x_offset = (screenbuffer->w - screen_mode->width) / 2;
+    y_offset = (screenbuffer->h - screen_mode->height) / 2;
 
-    if (SDL_LockSurface(screen) >= 0)
+    if (SDL_LockSurface(screenbuffer) >= 0)
     {
         I_InitScale(I_VideoBuffer,
-                    (byte *) screen->pixels + (y_offset * screen->pitch)
-                                            + x_offset, 
-                    screen->pitch);
+                    (byte *) screenbuffer->pixels
+                                + (y_offset * screenbuffer->pitch)
+                                + x_offset,
+                    screenbuffer->pitch);
         result = screen_mode->DrawScreen(x1, y1, x2, y2);
-      	SDL_UnlockSurface(screen);
+      	SDL_UnlockSurface(screenbuffer);
     }
     else
     {
@@ -1055,19 +1071,37 @@
     // draw to screen
 
     BlitArea(0, 0, SCREENWIDTH, SCREENHEIGHT);
-    
-    // If we have a palette to set, the act of setting the palette
-    // updates the screen
 
     if (palette_to_set)
     {
-        SDL_SetColors(screen, palette, 0, 256);
+        SDL_SetColors(screenbuffer, palette, 0, 256);
         palette_to_set = false;
+
+        // In native 8-bit mode, if we have a palette to set, the act
+        // of setting the palette updates the screen
+
+        if (screenbuffer == screen)
+        {
+            return;
+        }
     }
-    else
+
+    // In 8in32 mode, we must blit from the fake 8-bit screen buffer
+    // to the real screen before doing a screen flip.
+
+    if (screenbuffer != screen)
     {
-        SDL_Flip(screen);
+        SDL_Rect dst_rect;
+
+        // Center the buffer within the full screen space.
+
+        dst_rect.x = (screen->w - screenbuffer->w) / 2;
+        dst_rect.y = (screen->h - screenbuffer->h) / 2;
+
+        SDL_BlitSurface(screenbuffer, NULL, screen, &dst_rect);
     }
+
+    SDL_Flip(screen);
 }
 
 
@@ -1348,16 +1382,62 @@
     }
 }
 
+// Auto-adjust to a valid color depth.
+
+static void AutoAdjustColorDepth(void)
+{
+    SDL_Rect **modes;
+    SDL_PixelFormat format;
+    const SDL_VideoInfo *info;
+    int flags;
+
+    if (fullscreen)
+    {
+        flags = SDL_FULLSCREEN;
+    }
+    else
+    {
+        flags = 0;
+    }
+
+    format.BitsPerPixel = screen_bpp;
+    format.BytesPerPixel = (screen_bpp + 7) / 8;
+
+    // Are any screen modes supported at the configured color depth?
+
+    modes = SDL_ListModes(&format, flags);
+
+    // If not, we must autoadjust to something sensible.
+
+    if (modes == NULL)
+    {
+        printf("I_InitGraphics: %ibpp color depth not supported.\n",
+               screen_bpp);
+
+        info = SDL_GetVideoInfo();
+
+        if (info != NULL && info->vfmt != NULL)
+        {
+            screen_bpp = info->vfmt->BitsPerPixel;
+        }
+    }
+}
+
 // If the video mode set in the configuration file is not available,
 // try to choose a different mode.
 
 static void I_AutoAdjustSettings(void)
 {
-    int old_screen_w, old_screen_h;
+    int old_screen_w, old_screen_h, old_screen_bpp;
 
     old_screen_w = screen_width;
     old_screen_h = screen_height;
+    old_screen_bpp = screen_bpp;
 
+    // Possibly adjust color depth.
+
+    AutoAdjustColorDepth();
+
     // If we are running fullscreen, try to autoadjust to a valid fullscreen
     // mode.  If this is impossible, switch to windowed.
 
@@ -1375,10 +1455,11 @@
 
     // Have the settings changed?  Show a message.
 
-    if (screen_width != old_screen_w || screen_height != old_screen_h)
+    if (screen_width != old_screen_w || screen_height != old_screen_h
+     || screen_bpp != old_screen_bpp)
     {
-        printf("I_InitGraphics: Auto-adjusted to %ix%i.\n",
-               screen_width, screen_height);
+        printf("I_InitGraphics: Auto-adjusted to %ix%ix%ibpp.\n",
+               screen_width, screen_height, screen_bpp);
 
         printf("NOTE: Your video settings have been adjusted.  "
                "To disable this behavior,\n"
@@ -1544,7 +1625,7 @@
     // Specify the screen width, in pixels.
     //
 
-    i = M_CheckParm("-width");
+    i = M_CheckParmWithArgs("-width", 1);
 
     if (i > 0)
     {
@@ -1558,7 +1639,7 @@
     // Specify the screen height, in pixels.
     //
 
-    i = M_CheckParm("-height");
+    i = M_CheckParmWithArgs("-height", 1);
 
     if (i > 0)
     {
@@ -1567,12 +1648,39 @@
 
     //!
     // @category video
+    // @arg <bpp>
+    //
+    // Specify the color depth of the screen, in bits per pixel.
+    //
+
+    i = M_CheckParmWithArgs("-bpp", 1);
+
+    if (i > 0)
+    {
+        screen_bpp = atoi(myargv[i + 1]);
+    }
+
+    // Because we love Eternity:
+
+    //!
+    // @category video
+    //
+    // Set the color depth of the screen to 32 bits per pixel.
+    //
+
+    if (M_CheckParm("-8in32"))
+    {
+        screen_bpp = 32;
+    }
+
+    //!
+    // @category video
     // @arg <WxY>
     //
     // Specify the screen mode (when running fullscreen) or the window
     // dimensions (when running in windowed mode).
 
-    i = M_CheckParm("-geometry");
+    i = M_CheckParmWithArgs("-geometry", 1);
 
     if (i > 0)
     {
@@ -1743,6 +1851,14 @@
 
     doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
 
+    // If we are already running and in a true color mode, we need
+    // to free the screenbuffer surface before setting the new mode.
+
+    if (screenbuffer != NULL && screen != screenbuffer)
+    {
+        SDL_FreeSurface(screenbuffer);
+    }
+
     // Generate lookup tables before setting the video mode.
 
     if (mode != NULL && mode->InitMode != NULL)
@@ -1752,8 +1868,13 @@
 
     // Set the video mode.
 
-    flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
+    flags |= SDL_SWSURFACE | SDL_DOUBLEBUF;
 
+    if (screen_bpp == 8)
+    {
+        flags |= SDL_HWPALETTE;
+    }
+
     if (fullscreen)
     {
         flags |= SDL_FULLSCREEN;
@@ -1760,18 +1881,30 @@
     }
     else
     {
+        // In windowed mode, the window can be resized while the game is
+        // running.  This feature is disabled on OS X, as it adds an ugly
+        // scroll handle to the corner of the screen.
+
+#ifndef __MACOSX__
         flags |= SDL_RESIZABLE;
+#endif
         // villsa - center window
         SDL_putenv("SDL_VIDEO_CENTERED=1");
     }
 
-    screen = SDL_SetVideoMode(w, h, 8, flags);
+    screen = SDL_SetVideoMode(w, h, screen_bpp, flags);
 
     if (screen == NULL)
     {
-        I_Error("Error setting video mode: %s\n", SDL_GetError());
+        I_Error("Error setting video mode %ix%ix%ibpp: %s\n",
+                w, h, screen_bpp, SDL_GetError());
     }
 
+    // Blank out the full screen area in case there is any junk in
+    // the borders that won't otherwise be overwritten.
+
+    SDL_FillRect(screen, NULL, 0);
+
     // If mode was not set, it must be set now that we know the
     // screen size.
 
@@ -1793,6 +1926,22 @@
         }
     }
 
+    // Create the screenbuffer surface; if we have a real 8-bit palettized
+    // screen, then we can use the screen as the screenbuffer.
+
+    if (screen->format->BitsPerPixel == 8)
+    {
+        screenbuffer = screen;
+    }
+    else
+    {
+        screenbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
+                                            mode->width, mode->height, 8,
+                                            0, 0, 0, 0);
+
+        SDL_FillRect(screenbuffer, NULL, 0);
+    }
+
     // Save screen mode.
 
     screen_mode = mode;
@@ -1910,24 +2059,13 @@
     // Start with a clear black screen
     // (screen will be flipped after we set the palette)
 
-    if (SDL_LockSurface(screen) >= 0)
-    {
-        byte *screenpixels;
-        int y;
+    SDL_FillRect(screenbuffer, NULL, 0);
 
-        screenpixels = (byte *) screen->pixels;
-
-        for (y=0; y<screen->h; ++y)
-            memset(screenpixels + screen->pitch * y, 0, screen->w);
-
-        SDL_UnlockSurface(screen);
-    }
-
     // Set the palette
 
     doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
     I_SetPalette(doompal);
-    SDL_SetColors(screen, palette, 0, 256);
+    SDL_SetColors(screenbuffer, palette, 0, 256);
 
     CreateCursors();
 
@@ -1949,7 +2087,8 @@
     // Likewise if the screen pitch is not the same as the width
     // If we have to multiply, drawing is done to a separate 320x200 buf
 
-    native_surface = !SDL_MUSTLOCK(screen)
+    native_surface = screen == screenbuffer
+                  && !SDL_MUSTLOCK(screen)
                   && screen_mode == &mode_scale_1x
                   && screen->pitch == SCREENWIDTH
                   && aspect_ratio_correct;
@@ -2013,6 +2152,7 @@
     M_BindVariable("startup_delay",             &startup_delay);
     M_BindVariable("screen_width",              &screen_width);
     M_BindVariable("screen_height",             &screen_height);
+    M_BindVariable("screen_bpp",                &screen_bpp);
     M_BindVariable("grabmouse",                 &grabmouse);
     M_BindVariable("mouse_acceleration",        &mouse_acceleration);
     M_BindVariable("mouse_threshold",           &mouse_threshold);
@@ -2020,5 +2160,26 @@
     M_BindVariable("usegamma",                  &usegamma);
     M_BindVariable("vanilla_keyboard_mapping",  &vanilla_keyboard_mapping);
     M_BindVariable("novert",                    &novert);
+
+    // Windows Vista or later?  Set screen color depth to
+    // 32 bits per pixel, as 8-bit palettized screen modes
+    // don't work properly in recent versions.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+    {
+        OSVERSIONINFOEX version_info;
+
+        ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
+        version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+        GetVersionEx((OSVERSIONINFO *) &version_info);
+
+        if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
+         && version_info.dwMajorVersion >= 6)
+        {
+            screen_bpp = 32;
+        }
+    }
+#endif
 }
 
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -116,6 +116,7 @@
 
 extern char *video_driver;
 extern boolean screenvisible;
+
 extern float mouse_acceleration;
 extern int mouse_threshold;
 extern int vanilla_keyboard_mapping;
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -47,13 +47,13 @@
 // or 0 if not present
 //
 
-int M_CheckParm (char *check)
+int M_CheckParmWithArgs(char *check, int num_args)
 {
-    int		i;
+    int i;
 
-    for (i = 1;i<myargc;i++)
+    for (i = 1; i < myargc - num_args; i++)
     {
-	if ( !strcasecmp(check, myargv[i]) )
+	if (!strcasecmp(check, myargv[i]))
 	    return i;
     }
 
@@ -72,6 +72,11 @@
     return M_CheckParm(check) != 0;
 }
 
+int M_CheckParm(char *check)
+{
+    return M_CheckParmWithArgs(check, 0);
+}
+
 #define MAXARGVS        100
 
 static void LoadResponseFile(int argv_index)
@@ -88,7 +93,7 @@
     response_filename = myargv[argv_index] + 1;
 
     // Read the response file into memory
-    handle = fopen(response_filename, "r");
+    handle = fopen(response_filename, "rb");
 
     if (handle == NULL)
     {
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -40,6 +40,10 @@
 // in the arg list (0 if not found).
 int M_CheckParm (char* check);
 
+// Same as M_CheckParm, but checks that num_args arguments are available
+// following the specified argument.
+int M_CheckParmWithArgs(char *check, int num_args);
+
 void M_FindResponseFile(void);
 
 // Parameter has been specified?
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -645,6 +645,12 @@
     CONFIG_VARIABLE_INT(screen_height), 
 
     //!
+    // Color depth of the screen, in bits.
+    //
+
+    CONFIG_VARIABLE_INT(screen_bpp),
+
+    //!
     // If this is non-zero, the mouse will be "grabbed" when running
     // in windowed mode so that it can be used as an input device.
     // When running full screen, this has no effect.
@@ -1288,9 +1294,19 @@
                 
                 v = * (int *) defaults[i].location;
 
-                if (defaults[i].untranslated
-                 && v == defaults[i].original_translated)
+                if (v == KEY_RSHIFT)
                 {
+                    // Special case: for shift, force scan code for
+                    // right shift, as this is what Vanilla uses.
+                    // This overrides the change check below, to fix
+                    // configuration files made by old versions that
+                    // mistakenly used the scan code for left shift.
+
+                    v = 54;
+                }
+                else if (defaults[i].untranslated
+                      && v == defaults[i].original_translated)
+                {
                     // Has not been changed since the last time we
                     // read the config file.
 
@@ -1507,9 +1523,9 @@
     // configuration file (for Doom) is named default.cfg.
     //
 
-    i = M_CheckParm ("-config");
+    i = M_CheckParmWithArgs("-config", 1);
 
-    if (i && i<myargc-1)
+    if (i)
     {
 	doom_defaults.filename = myargv[i+1];
 	printf ("	default file: %s\n",doom_defaults.filename);
@@ -1530,9 +1546,9 @@
     // configuration file for Doom is named chocolate-doom.cfg.
     //
 
-    i = M_CheckParm("-extraconfig");
+    i = M_CheckParmWithArgs("-extraconfig", 1);
 
-    if (i && i<myargc-1)
+    if (i)
     {
         extra_defaults.filename = myargv[i+1];
         printf("        extra configuration file: %s\n", 
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -28,13 +28,12 @@
 
 #include "config.h"
 #include "doomtype.h"
-#include "doomstat.h"
 #include "deh_main.h"
 #include "deh_str.h"
-#include "g_game.h"
 #include "i_system.h"
 #include "i_timer.h"
 #include "m_argv.h"
+#include "m_fixed.h"
 #include "m_config.h"
 #include "net_client.h"
 #include "net_common.h"
@@ -47,6 +46,8 @@
 #include "w_checksum.h"
 #include "w_wad.h"
 
+extern void D_ReceiveTic(ticcmd_t *ticcmds, boolean *playeringame);
+
 typedef enum
 {
     // waiting for the game to start
@@ -105,6 +106,10 @@
 static net_addr_t *server_addr;
 static net_context_t *client_context;
 
+// game settings, as received from the server when the game started
+
+static net_gamesettings_t settings;
+
 // true if the client code is in use
 
 boolean net_client_connected;
@@ -152,6 +157,10 @@
 
 char *net_player_name = NULL;
 
+// Connected but not participating in the game (observer)
+
+boolean drone = false;
+
 // The last ticcmd constructed
 
 static ticcmd_t last_ticcmd;
@@ -187,53 +196,11 @@
 
 #define NET_CL_ExpandTicNum(b) NET_ExpandTicNum(recvwindow_start, (b))
 
-// Called when a player leaves the game
-
-static void NET_CL_PlayerQuitGame(player_t *player)
-{
-    static char exitmsg[80];
-
-    // Do this the same way as Vanilla Doom does, to allow dehacked
-    // replacements of this message
-
-    strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
-    exitmsg[sizeof(exitmsg) - 1] = '\0';
-
-    exitmsg[7] += player - players;
-
-    players[consoleplayer].message = exitmsg;
-
-    // TODO: check if it is sensible to do this:
-
-    if (demorecording) 
-    {
-        G_CheckDemoStatus ();
-    }
-}
-
 // Called when we become disconnected from the server
 
 static void NET_CL_Disconnected(void)
 {
-    int i;
-
-    // In drone mode, the game cannot continue once disconnected.
-
-    if (drone)
-    { 
-        I_Error("Disconnected from server in drone mode.");
-    }
-
-    // disconnected from server
-
-    players[consoleplayer].message = "Disconnected from server";
-    printf("Disconnected from server.\n");
-
-    for (i=0; i<MAXPLAYERS; ++i)
-    {
-        if (i != consoleplayer)
-            playeringame[i] = false;
-    }
+    D_ReceiveTic(NULL, NULL);
 }
 
 // Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as
@@ -241,7 +208,8 @@
 // the d_net.c structures (netcmds/nettics) and save the new ticcmd
 // back into recvwindow_cmd_base.
 
-static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq)
+static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq,
+                                    ticcmd_t *ticcmds)
 {
     int latency;
     fixed_t adjustment;
@@ -303,22 +271,14 @@
     
     for (i=0; i<MAXPLAYERS; ++i)
     {
-        if (i == consoleplayer && !drone)
+        if (i == settings.consoleplayer && !drone)
         {
             continue;
         }
         
-        if (playeringame[i] && !cmd->playeringame[i])
+        if (cmd->playeringame[i])
         {
-            NET_CL_PlayerQuitGame(&players[i]);
-        }
-        
-        playeringame[i] = cmd->playeringame[i];
-
-        if (playeringame[i])
-        {
             net_ticdiff_t *diff;
-            ticcmd_t ticcmd;
 
             diff = &cmd->cmds[i];
 
@@ -325,16 +285,11 @@
             // Use the ticcmd diff to patch the previous ticcmd to
             // the new ticcmd
 
-            NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmd);
+            NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmds[i]);
 
-            // Save in d_net.c structures
-
-            netcmds[i][nettics[i] % BACKUPTICS] = ticcmd;
-            ++nettics[i];
-
             // Store a copy for next time
 
-            recvwindow_cmd_base[i] = ticcmd;
+            recvwindow_cmd_base[i] = ticcmds[i];
         }
     }
 }
@@ -343,11 +298,15 @@
 
 static void NET_CL_AdvanceWindow(void)
 {
+    ticcmd_t ticcmds[MAXPLAYERS];
+
     while (recvwindow[0].active)
     {
         // Expand tic diff data into d_net.c structures
 
-        NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd, recvwindow_start);
+        NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd, recvwindow_start,
+                                ticcmds);
+        D_ReceiveTic(ticcmds, recvwindow[0].cmd.playeringame);
 
         // Advance the window
 
@@ -375,67 +334,10 @@
     }
 }
 
-void NET_CL_StartGame(void)
+void NET_CL_StartGame(net_gamesettings_t *settings)
 {
     net_packet_t *packet;
-    net_gamesettings_t settings;
-    int i;
 
-    // Fill in game settings structure with appropriate parameters
-    // for the new game
-
-    settings.deathmatch = deathmatch;
-    settings.episode = startepisode;
-    settings.map = startmap;
-    settings.skill = startskill;
-    settings.loadgame = startloadgame;
-    settings.gameversion = gameversion;
-    settings.nomonsters = nomonsters;
-    settings.fast_monsters = fastparm;
-    settings.respawn_monsters = respawnparm;
-    settings.timelimit = timelimit;
-
-    //!
-    // @category net
-    //
-    // Use original game sync code.
-    //
-
-    if (M_CheckParm("-oldsync") > 0)
-	settings.new_sync = 0;
-    else
-	settings.new_sync = 1;
-    
-    //!
-    // @category net
-    // @arg <n>
-    //
-    // Send n extra tics in every packet as insurance against dropped
-    // packets.
-    //
-
-    i = M_CheckParm("-extratics");
-
-    if (i > 0)
-        settings.extratics = atoi(myargv[i+1]);
-    else
-        settings.extratics = 1;
-
-    //!
-    // @category net
-    // @arg <n>
-    //
-    // Reduce the resolution of the game by a factor of n, reducing
-    // the amount of network bandwidth needed.
-    //
-
-    i = M_CheckParm("-dup");
-
-    if (i > 0)
-        settings.ticdup = atoi(myargv[i+1]);
-    else
-        settings.ticdup = 1;
-
     // Start from a ticcmd of all zeros
 
     memset(&last_ticcmd, 0, sizeof(ticcmd_t));
@@ -445,7 +347,7 @@
     packet = NET_Conn_NewReliable(&client_connection, 
                                   NET_PACKET_TYPE_GAMESTART);
 
-    NET_WriteSettings(packet, &settings);
+    NET_WriteSettings(packet, settings);
 }
 
 static void NET_CL_SendGameDataACK(void)
@@ -455,7 +357,7 @@
     packet = NET_NewPacket(10);
 
     NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_ACK);
-    NET_WriteInt8(packet, (gametic / ticdup) & 0xff);
+    NET_WriteInt8(packet, recvwindow_start & 0xff);
 
     NET_Conn_SendPacket(&client_connection, packet);
 
@@ -487,7 +389,7 @@
     // Write the start tic and number of tics.  Send only the low byte
     // of start - it can be inferred by the server.
 
-    NET_WriteInt8(packet, (gametic / ticdup) & 0xff);
+    NET_WriteInt8(packet, recvwindow_start & 0xff);
     NET_WriteInt8(packet, start & 0xff);
     NET_WriteInt8(packet, end - start + 1);
 
@@ -501,7 +403,7 @@
 
         NET_WriteInt16(packet, average_latency / FRACUNIT);
 
-        NET_WriteTiccmdDiff(packet, &sendobj->cmd, lowres_turn);
+        NET_WriteTiccmdDiff(packet, &sendobj->cmd, settings.lowres_turn);
     }
     
     // Send the packet
@@ -541,7 +443,7 @@
 
     // Send to server.
 
-    starttic = maketic - extratics;
+    starttic = maketic - settings.extratics;
     endtic = maketic;
 
     if (starttic < 0)
@@ -632,14 +534,7 @@
 
 static void NET_CL_ParseGameStart(net_packet_t *packet)
 {
-    net_gamesettings_t settings;
-    unsigned int num_players;
-    signed int player_number;
-    unsigned int i;
-
-    if (!NET_ReadInt8(packet, &num_players)
-     || !NET_ReadSInt8(packet, &player_number)
-     || !NET_ReadSettings(packet, &settings))
+    if (!NET_ReadSettings(packet, &settings))
     {
         return;
     }
@@ -649,14 +544,15 @@
         return;
     }
 
-    if (num_players > MAXPLAYERS || player_number >= (signed int) num_players)
+    if (settings.num_players > MAXPLAYERS
+     || settings.consoleplayer >= (signed int) settings.num_players)
     {
         // insane values
         return;
     }
 
-    if ((drone && player_number >= 0)
-     || (!drone && player_number < 0))
+    if ((drone && settings.consoleplayer >= 0)
+     || (!drone && settings.consoleplayer < 0))
     {
         // Invalid player number: must be positive for real players,
         // negative for drones
@@ -664,49 +560,8 @@
         return;
     }
 
-    // Start the game
-
-    if (!drone)
-    {
-        consoleplayer = player_number;
-    }
-    else
-    {
-        consoleplayer = 0;
-    }
-    
-    for (i=0; i<MAXPLAYERS; ++i) 
-    {
-        playeringame[i] = i < num_players;
-    }
-
     client_state = CLIENT_STATE_IN_GAME;
 
-    deathmatch = settings.deathmatch;
-    ticdup = settings.ticdup;
-    extratics = settings.extratics;
-    startepisode = settings.episode;
-    startmap = settings.map;
-    startskill = settings.skill;
-    startloadgame = settings.loadgame;
-    lowres_turn = settings.lowres_turn;
-    nomonsters = settings.nomonsters;
-    fastparm = settings.fast_monsters;
-    respawnparm = settings.respawn_monsters;
-    net_cl_new_sync = settings.new_sync != 0;
-    timelimit = settings.timelimit;
-
-    if (net_cl_new_sync == false)
-    {
-	printf("Syncing netgames like Vanilla Doom.\n");
-    }
-
-    if (lowres_turn)
-    {
-        printf("NOTE: Turning resolution is reduced; this is probably "
-               "because there is a client recording a Vanilla demo.\n");
-    }
-
     // Clear the receive window
 
     memset(recvwindow, 0, sizeof(recvwindow));
@@ -716,9 +571,6 @@
     // Clear the send queue
 
     memset(&send_queue, 0x00, sizeof(send_queue));
-
-    netgame = true;
-    autostart = true;
 }
 
 static void NET_CL_SendResendRequest(int start, int end)
@@ -865,7 +717,7 @@
 
         index = seq - recvwindow_start + i;
 
-        if (!NET_ReadFullTiccmd(packet, &cmd, lowres_turn))
+        if (!NET_ReadFullTiccmd(packet, &cmd, settings.lowres_turn))
         {
             return;
         }
@@ -1106,7 +958,7 @@
     }
 }
 
-static void NET_CL_SendSYN(void)
+static void NET_CL_SendSYN(net_connect_data_t *data)
 {
     net_packet_t *packet;
 
@@ -1114,10 +966,7 @@
     NET_WriteInt16(packet, NET_PACKET_TYPE_SYN);
     NET_WriteInt32(packet, NET_MAGIC_NUMBER);
     NET_WriteString(packet, PACKAGE_STRING);
-    NET_WriteInt16(packet, gamemode);
-    NET_WriteInt16(packet, gamemission);
-    NET_WriteInt8(packet, lowres_turn);
-    NET_WriteInt8(packet, drone);
+    NET_WriteConnectData(packet, data);
     NET_WriteMD5Sum(packet, net_local_wad_md5sum);
     NET_WriteMD5Sum(packet, net_local_deh_md5sum);
     NET_WriteInt8(packet, net_local_is_freedoom);
@@ -1128,7 +977,7 @@
 
 // connect to a server
 
-boolean NET_CL_Connect(net_addr_t *addr)
+boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data)
 {
     int start_time;
     int last_send_time;
@@ -1135,13 +984,6 @@
 
     server_addr = addr;
 
-    // Are we recording a demo? Possibly set lowres turn mode
-
-    if (M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0)
-    {
-        lowres_turn = true;
-    }
-
     // Read checksums of our WAD directory and dehacked information
 
     W_Checksum(net_local_wad_md5sum);
@@ -1155,7 +997,7 @@
     // necessary module
 
     client_context = NET_NewContext();
-    
+
     // initialize module for client mode
 
     if (!addr->module->InitClient())
@@ -1185,7 +1027,7 @@
 
         if (nowtime - last_send_time > 1000 || last_send_time < 0)
         {
-            NET_CL_SendSYN();
+            NET_CL_SendSYN(data);
             last_send_time = nowtime;
         }
  
@@ -1199,7 +1041,7 @@
         // run client code
 
         NET_CL_Run();
-        
+
         // run the server, just incase we are doing a loopback
         // connect
 
@@ -1215,6 +1057,7 @@
         // connected ok!
 
         client_state = CLIENT_STATE_WAITING_START;
+        drone = data->drone;
 
         return true;
     }
@@ -1223,9 +1066,23 @@
         // failed to connect
 
         NET_CL_Shutdown();
-        
+
         return false;
     }
+}
+
+// read game settings received from server
+
+boolean NET_CL_GetSettings(net_gamesettings_t *_settings)
+{
+    if (client_state != CLIENT_STATE_IN_GAME)
+    {
+        return false;
+    }
+
+    memcpy(_settings, &settings, sizeof(net_gamesettings_t));
+
+    return true;
 }
 
 // disconnect from the server
--- a/src/net_client.h
+++ b/src/net_client.h
@@ -31,12 +31,13 @@
 
 #define MAXPLAYERNAME 30
 
-boolean NET_CL_Connect(net_addr_t *addr);
+boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data);
 void NET_CL_Disconnect(void);
 void NET_CL_Run(void);
 void NET_CL_Init(void);
-void NET_CL_StartGame();
+void NET_CL_StartGame(net_gamesettings_t *settings);
 void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic);
+boolean NET_CL_GetSettings(net_gamesettings_t *_settings);
 void NET_Init(void);
 
 void NET_BindVariables(void);
@@ -59,6 +60,7 @@
 extern md5_digest_t net_local_deh_md5sum;
 extern unsigned int net_local_is_freedoom;
 
+extern boolean drone;
 
 #endif /* #ifndef NET_CLIENT_H */
 
--- a/src/net_dedicated.c
+++ b/src/net_dedicated.c
@@ -74,8 +74,8 @@
     CheckForClientOptions();
 
     NET_SV_Init();
-
     NET_SV_AddModule(&net_sdl_module);
+    NET_SV_RegisterWithMaster();
 
     while (true)
     {
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -128,6 +128,30 @@
     NET_PACKET_TYPE_QUERY_RESPONSE,
 } net_packet_type_t;
 
+typedef enum
+{
+    NET_MASTER_PACKET_TYPE_ADD,
+    NET_MASTER_PACKET_TYPE_ADD_RESPONSE,
+    NET_MASTER_PACKET_TYPE_QUERY,
+    NET_MASTER_PACKET_TYPE_QUERY_RESPONSE
+} net_master_packet_type_t;
+
+// Settings specified when the client connects to the server.
+
+typedef struct
+{
+    int gamemode;
+    int gamemission;
+    int lowres_turn;
+    int drone;
+    // TODO: is_freedoom in here?  WAD/DEH checksums?
+    // TODO: [Hexen] Requested player class
+
+} net_connect_data_t;
+
+// Game settings sent by client to server when initiating game start,
+// and received from the server by clients when the game starts.
+
 typedef struct 
 {
     int ticdup;
@@ -144,6 +168,15 @@
     int new_sync;
     int timelimit;
     int loadgame;
+
+    // These fields are only used by the server when sending a game
+    // start message:
+
+    int num_players;
+    int consoleplayer;
+
+    // TODO: [Hexen] Array of player classes, one for each player.
+
 } net_gamesettings_t;
 
 #define NET_TICDIFF_FORWARD      (1 << 0)
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@ -53,9 +53,11 @@
     I_Quit();
 }
 
-static void StartGame(TXT_UNCAST_ARG(widget), void *unused)
+static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(settings))
 {
-    NET_CL_StartGame();
+    TXT_CAST_ARG(net_gamesettings_t, settings);
+
+    NET_CL_StartGame(settings);
 }
 
 static void BuildGUI(void)
@@ -101,7 +103,7 @@
     TXT_SetWindowAction(window, TXT_HORIZ_LEFT, cancel);
 }
 
-static void UpdateGUI(void)
+static void UpdateGUI(net_gamesettings_t *settings)
 {
     txt_window_action_t *startgame;
     char buf[50];
@@ -144,7 +146,7 @@
     if (net_client_controller)
     {
         startgame = TXT_NewWindowAction(' ', "Start game");
-        TXT_SignalConnect(startgame, "pressed", StartGame, NULL);
+        TXT_SignalConnect(startgame, "pressed", StartGame, settings);
     }
     else
     {
@@ -259,7 +261,7 @@
     had_warning = true;
 }
 
-void NET_WaitForStart(void)
+void NET_WaitForStart(net_gamesettings_t *settings)
 {
     if (!TXT_Init())
     {
@@ -268,13 +270,13 @@
     }
 
     I_SetWindowTitle("Waiting for game start");
-    I_SetWindowIcon();
+    //I_SetWindowIcon();
 
     BuildGUI();
 
     while (net_waiting_for_start)
     {
-        UpdateGUI();
+        UpdateGUI(settings);
         CheckMD5Sums();
 
         TXT_DispatchEvents();
@@ -285,7 +287,7 @@
 
         if (!net_client_connected)
         {
-            I_Error("Disconnected from server");
+            I_Error("Lost connection to server");
         }
 
         TXT_Sleep(100);
--- a/src/net_gui.h
+++ b/src/net_gui.h
@@ -30,7 +30,7 @@
 
 #include "doomtype.h"
 
-extern void NET_WaitForStart();
+extern void NET_WaitForStart(net_gamesettings_t *settings);
 
 #endif /* #ifndef NET_GUI_H */
 
--- a/src/net_loop.c
+++ b/src/net_loop.c
@@ -137,9 +137,16 @@
 
 static net_addr_t *NET_CL_ResolveAddress(char *address)
 {
-    client_addr.module = &net_loop_client_module;
+    if (address == NULL)
+    {
+        client_addr.module = &net_loop_client_module;
 
-    return &client_addr;
+        return &client_addr;
+    }
+    else
+    {
+        return NULL;
+    }
 }
 
 net_module_t net_loop_client_module =
@@ -206,8 +213,15 @@
 
 static net_addr_t *NET_SV_ResolveAddress(char *address)
 {
-    server_addr.module = &net_loop_server_module;
-    return &server_addr;
+    if (address == NULL)
+    {
+        server_addr.module = &net_loop_server_module;
+        return &server_addr;
+    }
+    else
+    {
+        return NULL;
+    }
 }
 
 net_module_t net_loop_server_module =
--- a/src/net_query.c
+++ b/src/net_query.c
@@ -37,50 +37,166 @@
 #include "net_structrw.h"
 #include "net_sdl.h"
 
-typedef struct 
+// DNS address of the Internet master server.
+
+#define MASTER_SERVER_ADDRESS "master.chocolate-doom.org:2342"
+
+// Time to wait for a response before declaring a timeout.
+
+#define QUERY_TIMEOUT_SECS 2
+
+// Number of query attempts to make before giving up on a server.
+
+#define QUERY_MAX_ATTEMPTS 3
+
+typedef enum
 {
+    QUERY_TARGET_SERVER,       // Normal server target.
+    QUERY_TARGET_MASTER,       // The master server.
+    QUERY_TARGET_BROADCAST     // Send a broadcast query
+} query_target_type_t;
+
+typedef enum
+{
+    QUERY_TARGET_QUEUED,       // Query not yet sent
+    QUERY_TARGET_QUERIED,      // Query sent, waiting response
+    QUERY_TARGET_RESPONDED,    // Response received
+    QUERY_TARGET_NO_RESPONSE
+} query_target_state_t;
+
+typedef struct
+{
+    query_target_type_t type;
+    query_target_state_t state;
     net_addr_t *addr;
     net_querydata_t data;
-} queryresponse_t;
+    unsigned int ping_time;
+    unsigned int query_time;
+    unsigned int query_attempts;
+    boolean printed;
+} query_target_t;
 
+// Transmit a query packet
+
+static boolean registered_with_master = false;
+
 static net_context_t *query_context;
-static queryresponse_t *responders;
-static int num_responses;
+static query_target_t *targets;
+static int num_targets;
 
-// Add a new address to the list of hosts that has responded
+static boolean query_loop_running = false;
+static boolean printed_header = false;
 
-static queryresponse_t *AddResponder(net_addr_t *addr, 
-                                     net_querydata_t *data)
+// Resolve the master server address.
+
+net_addr_t *NET_Query_ResolveMaster(net_context_t *context)
 {
-    queryresponse_t *response;
+    net_addr_t *addr;
 
-    responders = realloc(responders, 
-                         sizeof(queryresponse_t) * (num_responses + 1));
+    addr = NET_ResolveAddress(context, MASTER_SERVER_ADDRESS);
 
-    response = &responders[num_responses];
-    response->addr = addr;
-    response->data = *data;
-    ++num_responses;
+    if (addr == NULL)
+    {
+        fprintf(stderr, "Warning: Failed to resolve address "
+                        "for master server: %s\n", MASTER_SERVER_ADDRESS);
+    }
 
-    return response;
+    return addr;
 }
 
-// Returns true if the reply is from a host that has not previously
-// responded.
+// Send a registration packet to the master server to register
+// ourselves with the global list.
 
-static boolean CheckResponder(net_addr_t *addr)
+void NET_Query_AddToMaster(net_addr_t *master_addr)
 {
+    net_packet_t *packet;
+
+    packet = NET_NewPacket(10);
+    NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_ADD);
+    NET_SendPacket(master_addr, packet);
+    NET_FreePacket(packet);
+}
+
+// Process a packet received from the master server.
+
+void NET_Query_MasterResponse(net_packet_t *packet)
+{
+    unsigned int packet_type;
+    unsigned int result;
+
+    if (!NET_ReadInt16(packet, &packet_type)
+     || !NET_ReadInt16(packet, &result))
+    {
+        return;
+    }
+
+    if (packet_type == NET_MASTER_PACKET_TYPE_ADD_RESPONSE)
+    {
+        if (result != 0)
+        {
+            // Only show the message once.
+
+            if (!registered_with_master)
+            {
+                printf("Registered with master server at %s\n",
+                       MASTER_SERVER_ADDRESS);
+                registered_with_master = true;
+            }
+        }
+        else
+        {
+            // Always show rejections.
+
+            printf("Failed to register with master server at %s\n",
+                   MASTER_SERVER_ADDRESS);
+        }
+    }
+}
+
+// Send a query to the master server.
+
+static void NET_Query_SendMasterQuery(net_addr_t *addr)
+{
+    net_packet_t *packet;
+
+    packet = NET_NewPacket(10);
+    NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_QUERY);
+    NET_SendPacket(addr, packet);
+    NET_FreePacket(packet);
+}
+
+// Given the specified address, find the target associated.  If no
+// target is found, and 'create' is true, a new target is created.
+
+static query_target_t *GetTargetForAddr(net_addr_t *addr, boolean create)
+{
+    query_target_t *target;
     int i;
 
-    for (i=0; i<num_responses; ++i)
+    for (i=0; i<num_targets; ++i)
     {
-        if (responders[i].addr == addr)
+        if (targets[i].addr == addr)
         {
-            return false;
+            return &targets[i];
         }
     }
 
-    return true;
+    if (!create)
+    {
+        return NULL;
+    }
+
+    targets = realloc(targets, sizeof(query_target_t) * (num_targets + 1));
+
+    target = &targets[num_targets];
+    target->type = QUERY_TARGET_SERVER;
+    target->state = QUERY_TARGET_QUEUED;
+    target->printed = false;
+    target->query_attempts = 0;
+    target->addr = addr;
+    ++num_targets;
+
+    return target;
 }
 
 // Transmit a query packet
@@ -104,166 +220,254 @@
     NET_FreePacket(request);
 }
 
-static void formatted_printf(int wide, char *s, ...)
+static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet,
+                                    net_query_callback_t callback,
+                                    void *user_data)
 {
-    va_list args;
-    int i;
-    
-    va_start(args, s);
-    i = vprintf(s, args);
-    va_end(args);
+    unsigned int packet_type;
+    net_querydata_t querydata;
+    query_target_t *target;
 
-    while (i < wide) 
+    // Read the header
+
+    if (!NET_ReadInt16(packet, &packet_type)
+     || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE)
     {
-        putchar(' ');
-        ++i;
-    } 
-}
+        return;
+    }
 
-static char *GameDescription(GameMode_t mode, GameMission_t mission)
-{
-    switch (mode)
+    // Read query data
+
+    if (!NET_ReadQueryData(packet, &querydata))
     {
-        case shareware:
-            return "shareware";
-        case registered:
-            return "registered";
-        case retail:
-            return "ultimate";
-        case commercial:
-            if (mission == doom2)
-                return "doom2";
-            else if (mission == pack_tnt)
-                return "tnt";
-            else if (mission == pack_plut)
-                return "plutonia";
-        default:
-            return "unknown";
+        return;
     }
-}
 
-static void PrintHeader(void)
-{
-    int i;
+    // Find the target that responded, or potentially add a new target
+    // if it was not already known (for LAN broadcast search)
 
-    formatted_printf(18, "Address");
-    formatted_printf(8, "Players");
-    puts("Description");
+    target = GetTargetForAddr(addr, true);
 
-    for (i=0; i<70; ++i)
-        putchar('=');
-    putchar('\n');
+    if (target->state != QUERY_TARGET_RESPONDED)
+    {
+        target->state = QUERY_TARGET_RESPONDED;
+        memcpy(&target->data, &querydata, sizeof(net_querydata_t));
+
+        // Calculate RTT.
+
+        target->ping_time = I_GetTimeMS() - target->query_time;
+
+        // Invoke callback to signal that we have a new address.
+
+        callback(addr, &target->data, target->ping_time, user_data);
+    }
 }
 
-static void PrintResponse(queryresponse_t *response)
+// Parse a response packet from the master server.
+
+static void NET_Query_ParseMasterResponse(net_addr_t *master_addr,
+                                          net_packet_t *packet)
 {
-    formatted_printf(18, "%s: ", NET_AddrToString(response->addr));
-    formatted_printf(8, "%i/%i", response->data.num_players, 
-                                 response->data.max_players);
+    unsigned int packet_type;
+    query_target_t *target;
+    char *addr_str;
+    net_addr_t *addr;
 
-    if (response->data.gamemode != indetermined)
+    // Read the header.  We are only interested in query responses.
+
+    if (!NET_ReadInt16(packet, &packet_type)
+     || packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE)
     {
-        printf("(%s) ", GameDescription(response->data.gamemode, 
-                                        response->data.gamemission));
+        return;
     }
 
-    if (response->data.server_state)
+    // Read a list of strings containing the addresses of servers
+    // that the master knows about.
+
+    for (;;)
     {
-        printf("(game running) ");
+        addr_str = NET_ReadString(packet);
+
+        if (addr_str == NULL)
+        {
+            break;
+        }
+
+        // Resolve address and add to targets list if it is not already
+        // there.
+
+        addr = NET_ResolveAddress(query_context, addr_str);
+
+        if (addr != NULL)
+        {
+            GetTargetForAddr(addr, true);
+        }
     }
 
-    NET_SafePuts(response->data.description);
+    // Mark the master as having responded.
+
+    target = GetTargetForAddr(master_addr, true);
+    target->state = QUERY_TARGET_RESPONDED;
 }
 
-static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet)
+static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet,
+                                  net_query_callback_t callback,
+                                  void *user_data)
 {
-    unsigned int packet_type;
-    net_querydata_t querydata;
-    queryresponse_t *response;
+    query_target_t *target;
 
-    // Have we already received a packet from this host?
+    // This might be the master server responding.
 
-    if (!CheckResponder(addr))
+    target = GetTargetForAddr(addr, false);
+
+    if (target != NULL && target->type == QUERY_TARGET_MASTER)
     {
-        return;
+        NET_Query_ParseMasterResponse(addr, packet);
     }
+    else
+    {
+        NET_Query_ParseResponse(addr, packet, callback, user_data);
+    }
+}
 
-    // Read the header
+static void NET_Query_GetResponse(net_query_callback_t callback,
+                                  void *user_data)
+{
+    net_addr_t *addr;
+    net_packet_t *packet;
 
-    if (!NET_ReadInt16(packet, &packet_type)
-     || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE)
+    if (NET_RecvPacket(query_context, &addr, &packet))
     {
-        return;
+        NET_Query_ParsePacket(addr, packet, callback, user_data);
+        NET_FreePacket(packet);
     }
+}
 
-    // Read query data
+// Find a target we have not yet queried and send a query.
 
-    if (!NET_ReadQueryData(packet, &querydata))
+static void SendOneQuery(void)
+{
+    unsigned int now;
+    unsigned int i;
+
+    now = I_GetTimeMS();
+
+    for (i = 0; i < num_targets; ++i)
     {
+        // Not queried yet?
+        // Or last query timed out without a response?
+
+        if (targets[i].state == QUERY_TARGET_QUEUED
+         || (targets[i].state == QUERY_TARGET_QUERIED
+             && now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000))
+        {
+            break;
+        }
+    }
+
+    if (i >= num_targets)
+    {
         return;
     }
 
-    if (num_responses <= 0)
+    // Found a target to query.  Send a query; how to do this depends on
+    // the target type.
+
+    switch (targets[i].type)
     {
-        // If this is the first response, print the table header
+        case QUERY_TARGET_SERVER:
+            NET_Query_SendQuery(targets[i].addr);
+            break;
 
-        PrintHeader();
+        case QUERY_TARGET_BROADCAST:
+            NET_Query_SendQuery(NULL);
+            break;
+
+        case QUERY_TARGET_MASTER:
+            NET_Query_SendMasterQuery(targets[i].addr);
+            break;
     }
 
-    response = AddResponder(addr, &querydata);
+    //printf("Queried %s\n", NET_AddrToString(targets[i].addr));
+    targets[i].state = QUERY_TARGET_QUERIED;
+    targets[i].query_time = I_GetTimeMS();
+    ++targets[i].query_attempts;
+}
 
-    PrintResponse(response);
+// Time out servers that have been queried and not responded.
+
+static void CheckTargetTimeouts(void)
+{
+    unsigned int i;
+    unsigned int now;
+
+    now = I_GetTimeMS();
+
+    for (i = 0; i < num_targets; ++i)
+    {
+        // We declare a target to be "no response" when we've sent
+        // multiple query packets to it (QUERY_MAX_ATTEMPTS) and
+        // received no response to any of them.
+
+        if (targets[i].state == QUERY_TARGET_QUERIED
+         && targets[i].query_attempts >= QUERY_MAX_ATTEMPTS
+         && now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000)
+        {
+            targets[i].state = QUERY_TARGET_NO_RESPONSE;
+        }
+    }
 }
 
-static void NET_Query_GetResponse(void)
+// If all targets have responded or timed out, returns true.
+
+static boolean AllTargetsDone(void)
 {
-    net_addr_t *addr;
-    net_packet_t *packet;
+    unsigned int i;
 
-    if (NET_RecvPacket(query_context, &addr, &packet))
+    for (i = 0; i < num_targets; ++i)
     {
-        NET_Query_ParsePacket(addr, packet);
-        NET_FreePacket(packet);
+        if (targets[i].state != QUERY_TARGET_RESPONDED
+         && targets[i].state != QUERY_TARGET_NO_RESPONSE)
+        {
+            return false;
+        }
     }
+
+    return true;
 }
 
-static net_addr_t *NET_Query_QueryLoop(net_addr_t *addr, 
-                                       boolean find_one)
+// Stop the query loop
+
+static void NET_Query_ExitLoop(void)
 {
-    int start_time;
-    int last_send_time;
+    query_loop_running = false;
+}
 
-    last_send_time = -1;
-    start_time = I_GetTimeMS();
+// Loop waiting for responses.
+// The specified callback is invoked when a new server responds.
 
-    while (I_GetTimeMS() < start_time + 5000)
+static void NET_Query_QueryLoop(net_query_callback_t callback,
+                                void *user_data)
+{
+    query_loop_running = true;
+
+    while (query_loop_running && !AllTargetsDone())
     {
-        // Send a query once every second
+        // Send a query.  This will only send a single query.
+        // Because of the delay below, this is therefore rate limited.
 
-        if (last_send_time < 0 || I_GetTimeMS() > last_send_time + 1000)
-        {
-            NET_Query_SendQuery(addr);
-            last_send_time = I_GetTimeMS();
-        }
+        SendOneQuery();
 
         // Check for a response
 
-        NET_Query_GetResponse();
+        NET_Query_GetResponse(callback, user_data);
 
-        // Found a response?
-
-        if (find_one && num_responses > 0)
-            break;
-        
         // Don't thrash the CPU
-        
-        I_Sleep(100);
-    }
 
-    if (num_responses > 0)
-        return responders[0].addr;
-    else
-        return NULL;
+        I_Sleep(50);
+
+        CheckTargetTimeouts();
+    }
 }
 
 void NET_Query_Init(void)
@@ -272,51 +476,256 @@
     NET_AddModule(query_context, &net_sdl_module);
     net_sdl_module.InitClient();
 
-    responders = NULL;
-    num_responses = 0;
+    targets = NULL;
+    num_targets = 0;
+
+    printed_header = false;
 }
 
-void NET_QueryAddress(char *addr)
+// Callback that exits the query loop when the first server is found.
+
+static void NET_Query_ExitCallback(net_addr_t *addr, net_querydata_t *data,
+                                   unsigned int ping_time, void *user_data)
 {
-    net_addr_t *net_addr;
-    
-    NET_Query_Init();
+    NET_Query_ExitLoop();
+}
 
-    net_addr = NET_ResolveAddress(query_context, addr);
+// Search the targets list and find a target that has responded.
+// If none have responded, returns NULL.
 
-    if (net_addr == NULL)
+static query_target_t *FindFirstResponder(void)
+{
+    unsigned int i;
+
+    for (i = 0; i < num_targets; ++i)
     {
-        I_Error("NET_QueryAddress: Host '%s' not found!", addr);
+        if (targets[i].type == QUERY_TARGET_SERVER
+         && targets[i].state == QUERY_TARGET_RESPONDED)
+        {
+            return &targets[i];
+        }
     }
 
-    printf("\nQuerying '%s'...\n\n", addr);
+    return NULL;
+}
 
-    if (!NET_Query_QueryLoop(net_addr, true))
+// Return a count of the number of responses.
+
+static int GetNumResponses(void)
+{
+    unsigned int i;
+    int result;
+
+    result = 0;
+
+    for (i = 0; i < num_targets; ++i)
     {
-        I_Error("No response from '%s'", addr);
+        if (targets[i].type == QUERY_TARGET_SERVER
+         && targets[i].state == QUERY_TARGET_RESPONDED)
+        {
+            ++result;
+        }
     }
 
-    exit(0);
+    return result;
 }
 
+void NET_QueryAddress(char *addr_str)
+{
+    net_addr_t *addr;
+    query_target_t *target;
+
+    NET_Query_Init();
+
+    addr = NET_ResolveAddress(query_context, addr_str);
+
+    if (addr == NULL)
+    {
+        I_Error("NET_QueryAddress: Host '%s' not found!", addr_str);
+    }
+
+    // Add the address to the list of targets.
+
+    target = GetTargetForAddr(addr, true);
+
+    printf("\nQuerying '%s'...\n", addr_str);
+
+    // Run query loop.
+
+    NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);
+
+    // Check if the target responded.
+
+    if (target->state == QUERY_TARGET_RESPONDED)
+    {
+        NET_QueryPrintCallback(addr, &target->data, target->ping_time, NULL);
+    }
+    else
+    {
+        I_Error("No response from '%s'", addr_str);
+    }
+}
+
 net_addr_t *NET_FindLANServer(void)
 {
+    query_target_t *target;
+    query_target_t *responder;
+
     NET_Query_Init();
 
-    return NET_Query_QueryLoop(NULL, true);
+    // Add a broadcast target to the list.
+
+    target = GetTargetForAddr(NULL, true);
+    target->type = QUERY_TARGET_BROADCAST;
+
+    // Run the query loop, and stop at the first target found.
+
+    NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);
+
+    responder = FindFirstResponder();
+
+    if (responder != NULL)
+    {
+        return responder->addr;
+    }
+    else
+    {
+        return NULL;
+    }
 }
 
-void NET_LANQuery(void)
+int NET_LANQuery(net_query_callback_t callback, void *user_data)
 {
+    query_target_t *target;
+
     NET_Query_Init();
 
-    printf("\nSearching for servers on local LAN ...\n\n");
+    // Add a broadcast target to the list.
 
-    if (!NET_Query_QueryLoop(NULL, false))
+    target = GetTargetForAddr(NULL, true);
+    target->type = QUERY_TARGET_BROADCAST;
+
+    NET_Query_QueryLoop(callback, user_data);
+
+    return GetNumResponses();
+}
+
+int NET_MasterQuery(net_query_callback_t callback, void *user_data)
+{
+    net_addr_t *master;
+    query_target_t *target;
+
+    NET_Query_Init();
+
+    // Resolve master address and add to targets list.
+
+    master = NET_Query_ResolveMaster(query_context);
+
+    if (master == NULL)
     {
-        I_Error("No servers found");
+        return 0;
     }
 
-    exit(0);
+    target = GetTargetForAddr(master, true);
+    target->type = QUERY_TARGET_MASTER;
+
+    NET_Query_QueryLoop(callback, user_data);
+
+    // Check that we got a response from the master, and display
+    // a warning if we didn't.
+
+    if (target->state == QUERY_TARGET_NO_RESPONSE)
+    {
+        fprintf(stderr, "NET_MasterQuery: no response from master server.\n");
+    }
+
+    return GetNumResponses();
+}
+
+static void formatted_printf(int wide, char *s, ...)
+{
+    va_list args;
+    int i;
+
+    va_start(args, s);
+    i = vprintf(s, args);
+    va_end(args);
+
+    while (i < wide)
+    {
+        putchar(' ');
+        ++i;
+    }
+}
+
+static char *GameDescription(GameMode_t mode, GameMission_t mission)
+{
+    switch (mode)
+    {
+        case shareware:
+            return "shareware";
+        case registered:
+            return "registered";
+        case retail:
+            return "ultimate";
+        case commercial:
+            if (mission == doom2)
+                return "doom2";
+            else if (mission == pack_tnt)
+                return "tnt";
+            else if (mission == pack_plut)
+                return "plutonia";
+        default:
+            return "unknown";
+    }
+}
+
+static void PrintHeader(void)
+{
+    int i;
+
+    putchar('\n');
+    formatted_printf(5, "Ping");
+    formatted_printf(18, "Address");
+    formatted_printf(8, "Players");
+    puts("Description");
+
+    for (i=0; i<70; ++i)
+        putchar('=');
+    putchar('\n');
+}
+
+// Callback function that just prints information in a table.
+
+void NET_QueryPrintCallback(net_addr_t *addr,
+                            net_querydata_t *data,
+                            unsigned int ping_time,
+                            void *user_data)
+{
+    // If this is the first server, print the header.
+
+    if (!printed_header)
+    {
+        PrintHeader();
+        printed_header = true;
+    }
+
+    formatted_printf(5, "%4i", ping_time);
+    formatted_printf(18, "%s: ", NET_AddrToString(addr));
+    formatted_printf(8, "%i/%i", data->num_players, 
+                                 data->max_players);
+
+    if (data->gamemode != indetermined)
+    {
+        printf("(%s) ", GameDescription(data->gamemode, 
+                                        data->gamemission));
+    }
+
+    if (data->server_state)
+    {
+        printf("(game running) ");
+    }
+
+    NET_SafePuts(data->description);
 }
 
--- a/src/net_query.h
+++ b/src/net_query.h
@@ -27,9 +27,22 @@
 
 #include "net_defs.h"
 
+typedef void (*net_query_callback_t)(net_addr_t *addr,
+                                     net_querydata_t *querydata,
+                                     unsigned int ping_time,
+                                     void *user_data);
+
+extern int NET_LANQuery(net_query_callback_t callback, void *user_data);
+extern int NET_MasterQuery(net_query_callback_t callback, void *user_data);
 extern void NET_QueryAddress(char *addr);
-extern void NET_LANQuery(void);
 extern net_addr_t *NET_FindLANServer(void);
+
+extern void NET_QueryPrintCallback(net_addr_t *addr, net_querydata_t *data,
+                                   unsigned int ping_time, void *user_data);
+
+extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context);
+extern void NET_Query_AddToMaster(net_addr_t *master_addr);
+extern void NET_Query_MasterResponse(net_packet_t *packet);
 
 #endif /* #ifndef NET_QUERY_H */
 
--- a/src/net_sdl.c
+++ b/src/net_sdl.c
@@ -170,7 +170,7 @@
     // the default (2342).
     //
 
-    p = M_CheckParm("-port");
+    p = M_CheckParmWithArgs("-port", 1);
     if (p > 0)
         port = atoi(myargv[p+1]);
 
@@ -196,7 +196,7 @@
 {
     int p;
     
-    p = M_CheckParm("-port");
+    p = M_CheckParmWithArgs("-port", 1);
     if (p > 0)
         port = atoi(myargv[p+1]);
 
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -41,10 +41,15 @@
 #include "net_io.h"
 #include "net_loop.h"
 #include "net_packet.h"
+#include "net_query.h"
 #include "net_server.h"
 #include "net_sdl.h"
 #include "net_structrw.h"
 
+// How often to refresh our registration with the master server.
+
+#define MASTER_REFRESH_PERIOD 20 * 60 /* 20 minutes */
+
 typedef enum
 {
     // waiting for the game to start
@@ -65,6 +70,11 @@
     int last_send_time;
     char *name;
 
+    // Time that this client connected to the server.
+    // This is used to determine the controller (oldest client).
+
+    unsigned int connect_time;
+
     // Last time new gamedata was received from this client
     
     int last_gamedata_time;
@@ -128,6 +138,11 @@
 static unsigned int sv_gamemission;
 static net_gamesettings_t sv_settings;
 
+// For registration with master server:
+
+static net_addr_t *master_server = NULL;
+static unsigned int master_refresh_time;
+
 // receive window
 
 static unsigned int recvwindow_start;
@@ -372,19 +387,29 @@
 
 static net_client_t *NET_SV_Controller(void)
 {
+    net_client_t *best;
     int i;
 
-    // first client in the list is the controller
+    // Find the oldest client (first to connect).
 
+    best = NULL;
+
     for (i=0; i<MAXNETNODES; ++i)
     {
-        if (ClientConnected(&clients[i]) && !clients[i].drone)
+        // Can't be controller?
+
+        if (!ClientConnected(&clients[i]) || clients[i].drone)
         {
-            return &clients[i];
+            continue;
         }
+
+        if (best == NULL || clients[i].connect_time < best->connect_time)
+        {
+            best = &clients[i];
+        }
     }
 
-    return NULL;
+    return best;
 }
 
 // Given an address, find the corresponding client
@@ -424,6 +449,7 @@
                                  char *player_name)
 {
     client->active = true;
+    client->connect_time = I_GetTimeMS();
     NET_Conn_InitServer(&client->connection, addr);
     client->addr = addr;
     client->last_send_time = -1;
@@ -447,10 +473,8 @@
                             net_addr_t *addr)
 {
     unsigned int magic;
-    unsigned int cl_gamemode, cl_gamemission;
-    unsigned int cl_recording_lowres;
-    unsigned int cl_drone;
     unsigned int is_freedoom;
+    net_connect_data_t data;
     md5_digest_t deh_md5sum, wad_md5sum;
     char *player_name;
     char *client_version;
@@ -501,10 +525,7 @@
 
     // read the game mode and mission
 
-    if (!NET_ReadInt16(packet, &cl_gamemode) 
-     || !NET_ReadInt16(packet, &cl_gamemission)
-     || !NET_ReadInt8(packet, &cl_recording_lowres)
-     || !NET_ReadInt8(packet, &cl_drone)
+    if (!NET_ReadConnectData(packet, &data)
      || !NET_ReadMD5Sum(packet, wad_md5sum)
      || !NET_ReadMD5Sum(packet, deh_md5sum)
      || !NET_ReadInt8(packet, &is_freedoom))
@@ -512,7 +533,7 @@
         return;
     }
 
-    if (!D_ValidGameMode(cl_gamemission, cl_gamemode))
+    if (!D_ValidGameMode(data.gamemission, data.gamemode))
     {
         return;
     }
@@ -579,7 +600,7 @@
         NET_SV_AssignPlayers();
         num_players = NET_SV_NumPlayers();
 
-        if ((!cl_drone && num_players >= MAXPLAYERS)
+        if ((!data.drone && num_players >= MAXPLAYERS)
          || NET_SV_NumClients() >= MAXNETNODES)
         {
             NET_SV_SendReject(addr, "Server is full!");
@@ -592,10 +613,10 @@
 
         // Adopt the game mode and mission of the first connecting client
 
-        if (num_players == 0 && !cl_drone)
+        if (num_players == 0 && !data.drone)
         {
-            sv_gamemode = cl_gamemode;
-            sv_gamemission = cl_gamemission;
+            sv_gamemode = data.gamemode;
+            sv_gamemission = data.gamemission;
         }
 
         // Save the MD5 checksums
@@ -607,7 +628,7 @@
         // Check the connecting client is playing the same game as all
         // the other clients
 
-        if (cl_gamemode != sv_gamemode || cl_gamemission != sv_gamemission)
+        if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission)
         {
             NET_SV_SendReject(addr, "You are playing the wrong game!");
             return;
@@ -617,8 +638,8 @@
 
         NET_SV_InitNewClient(client, addr, player_name);
 
-        client->recording_lowres = cl_recording_lowres;
-        client->drone = cl_drone;
+        client->recording_lowres = data.lowres_turn;
+        client->drone = data.drone;
     }
 
     if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
@@ -681,6 +702,8 @@
         }
     }
 
+    settings.num_players = NET_SV_NumPlayers();
+
     nowtime = I_GetTimeMS();
 
     // Send start packets to each connected node
@@ -695,8 +718,8 @@
         startpacket = NET_Conn_NewReliable(&clients[i].connection,
                                            NET_PACKET_TYPE_GAMESTART);
 
-        NET_WriteInt8(startpacket, NET_SV_NumPlayers());
-        NET_WriteInt8(startpacket, clients[i].player_number);
+        settings.consoleplayer = clients[i].player_number;
+
         NET_WriteSettings(startpacket, &settings);
     }
 
@@ -1070,6 +1093,7 @@
 {
     net_packet_t *reply;
     net_querydata_t querydata;
+    int p;
 
     // Version
 
@@ -1089,10 +1113,23 @@
     querydata.gamemode = sv_gamemode;
     querydata.gamemission = sv_gamemission;
 
-    // Server description.  This is currently hard-coded.
+    //!
+    // @arg <name>
+    //
+    // When starting a network server, specify a name for the server.
+    //
 
-    querydata.description = "Chocolate Doom server";
+    p = M_CheckParmWithArgs("-servername", 1);
 
+    if (p > 0)
+    {
+        querydata.description = myargv[p + 1];
+    }
+    else
+    {
+        querydata.description = "Unnamed server";
+    }
+
     // Send it and we're done.
 
     reply = NET_NewPacket(64);
@@ -1109,6 +1146,14 @@
     net_client_t *client;
     unsigned int packet_type;
 
+    // Response from master server?
+
+    if (addr != NULL && addr == master_server)
+    {
+        NET_Query_MasterResponse(packet);
+        return;
+    }
+
     // Find which client this packet came from
 
     client = NET_SV_FindClient(addr);
@@ -1514,6 +1559,32 @@
     server_initialized = true;
 }
 
+void NET_SV_RegisterWithMaster(void)
+{
+    //!
+    // When running a server, don't register with the global master server.
+    //
+    // @category net
+    //
+
+    if (!M_CheckParm("-privateserver"))
+    {
+        master_server = NET_Query_ResolveMaster(server_context);
+    }
+    else
+    {
+        master_server = NULL;
+    }
+
+    // Send request.
+
+    if (master_server != NULL)
+    {
+        NET_Query_AddToMaster(master_server);
+        master_refresh_time = I_GetTimeMS();
+    }
+}
+
 // Run server code to check for new packets/send packets as the server
 // requires
 
@@ -1528,10 +1599,19 @@
         return;
     }
 
-    while (NET_RecvPacket(server_context, &addr, &packet)) 
+    while (NET_RecvPacket(server_context, &addr, &packet))
     {
         NET_SV_Packet(packet, addr);
         NET_FreePacket(packet);
+    }
+
+    // Possibly refresh our registration with the master server.
+
+    if (master_server != NULL
+     && I_GetTimeMS() - master_refresh_time > MASTER_REFRESH_PERIOD * 1000)
+    {
+        NET_Query_AddToMaster(master_server);
+        master_refresh_time = I_GetTimeMS();
     }
 
     // "Run" any clients that may have things to do, independent of responses
--- a/src/net_server.h
+++ b/src/net_server.h
@@ -41,5 +41,9 @@
 
 void NET_SV_AddModule(net_module_t *module);
 
+// Register server with master server.
+
+void NET_SV_RegisterWithMaster(void);
+
 #endif /* #ifndef NET_SERVER_H */
 
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -30,6 +30,22 @@
 #include "net_packet.h"
 #include "net_structrw.h"
 
+void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data)
+{
+    NET_WriteInt8(packet, data->gamemode);
+    NET_WriteInt8(packet, data->gamemission);
+    NET_WriteInt8(packet, data->lowres_turn);
+    NET_WriteInt8(packet, data->drone);
+}
+
+boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data)
+{
+    return NET_ReadInt8(packet, (unsigned int *) &data->gamemode)
+        && NET_ReadInt8(packet, (unsigned int *) &data->gamemission)
+        && NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn)
+        && NET_ReadInt8(packet, (unsigned int *) &data->drone);
+}
+
 void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
 {
     NET_WriteInt8(packet, settings->ticdup);
@@ -46,6 +62,8 @@
     NET_WriteInt8(packet, settings->new_sync);
     NET_WriteInt32(packet, settings->timelimit);
     NET_WriteInt8(packet, settings->loadgame);
+    NET_WriteInt8(packet, settings->num_players);
+    NET_WriteInt8(packet, settings->consoleplayer);
 }
 
 boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
@@ -63,7 +81,9 @@
         && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn)
         && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync)
         && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit)
-        && NET_ReadSInt8(packet, (signed int *) &settings->loadgame);
+        && NET_ReadSInt8(packet, (signed int *) &settings->loadgame)
+        && NET_ReadInt8(packet, (unsigned int *) &settings->num_players)
+        && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer);
 }
 
 boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query)
--- a/src/net_structrw.h
+++ b/src/net_structrw.h
@@ -26,6 +26,9 @@
 #include "net_defs.h"
 #include "net_packet.h"
 
+void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data);
+boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data);
+
 extern void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings);
 extern boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings);
 
--- a/src/setup/display.c
+++ b/src/setup/display.c
@@ -26,6 +26,11 @@
 #include "libc_wince.h"
 #endif
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
 #include "textscreen.h"
 #include "m_config.h"
 #include "mode.h"
@@ -32,6 +37,29 @@
 
 #include "display.h"
 
+extern void RestartTextscreen(void);
+
+typedef struct
+{
+    char *description;
+    int bpp;
+} pixel_depth_t;
+
+// List of supported pixel depths.
+
+static pixel_depth_t pixel_depths[] =
+{
+    { "8-bit",    8 },
+    { "16-bit",   16 },
+    { "24-bit",   24 },
+    { "32-bit",   32 },
+};
+
+// List of strings containing supported pixel depths.
+
+static char **supported_bpps;
+static int num_supported_bpps;
+
 typedef struct 
 {
     int w, h;
@@ -69,6 +97,7 @@
 // List of fullscreen modes generated at runtime
 
 static screen_mode_t *screen_modes_fullscreen = NULL;
+static int num_screen_modes_fullscreen;
 
 static int vidmode = 0;
 
@@ -78,6 +107,7 @@
 static int fullscreen = 1;
 static int screen_width = 320;
 static int screen_height = 200;
+static int screen_bpp = 8;
 static int startup_delay = 1000;
 static int graphical_startup = 1;
 static int show_endoom = 1;
@@ -90,6 +120,10 @@
 
 static int selected_screen_width = 0, selected_screen_height;
 
+// Index into the supported_bpps of the selected pixel depth.
+
+static int selected_bpp = 0;
+
 static int system_video_env_set;
 
 // Set the SDL_VIDEODRIVER environment variable
@@ -133,6 +167,153 @@
     }
 }
 
+// Query SDL as to whether any fullscreen modes are available for the
+// specified pixel depth.
+
+static int PixelDepthSupported(int bpp)
+{
+    SDL_PixelFormat format;
+    SDL_Rect **modes;
+
+    format.BitsPerPixel = bpp;
+    format.BytesPerPixel = (bpp + 7) / 8;
+
+    modes = SDL_ListModes(&format, SDL_FULLSCREEN);
+
+    return modes != NULL;
+}
+
+// Query SDL and populate the supported_bpps array.
+
+static void IdentifyPixelDepths(void)
+{
+    unsigned int i;
+    unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+
+    if (supported_bpps != NULL)
+    {
+        free(supported_bpps);
+    }
+
+    supported_bpps = malloc(sizeof(char *) * num_depths);
+    num_supported_bpps = 0;
+
+    // Check each bit depth to determine if modes are available.
+
+    for (i = 0; i < num_depths; ++i)
+    {
+        // If modes are available, add this bit depth to the list.
+
+        if (PixelDepthSupported(pixel_depths[i].bpp))
+        {
+            supported_bpps[num_supported_bpps] = pixel_depths[i].description;
+            ++num_supported_bpps;
+        }
+    }
+
+    // No supported pixel depths?  That's kind of a problem.  Add 8bpp
+    // as a fallback.
+
+    if (num_supported_bpps == 0)
+    {
+        supported_bpps[0] = pixel_depths[0].description;
+        ++num_supported_bpps;
+    }
+}
+
+// Get the screen pixel depth corresponding to what selected_bpp is set to.
+
+static int GetSelectedBPP(void)
+{
+    unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+    unsigned int i;
+
+    // Find which pixel depth is selected, and set screen_bpp.
+
+    for (i = 0; i < num_depths; ++i)
+    {
+        if (pixel_depths[i].description == supported_bpps[selected_bpp])
+        {
+            return pixel_depths[i].bpp;
+        }
+    }
+
+    // Default fallback value.
+
+    return 8;
+}
+
+// Get the index into supported_bpps of the specified pixel depth string.
+
+static int GetSupportedBPPIndex(char *description)
+{
+    unsigned int i;
+
+    for (i = 0; i < num_supported_bpps; ++i)
+    {
+        if (supported_bpps[i] == description)
+        {
+            return i;
+        }
+    }
+
+    // Shouldn't happen; fall back to the first in the list.
+
+    return 0;
+}
+
+// Set selected_bpp to match screen_bpp.
+
+static int TrySetSelectedBPP(void)
+{
+    unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+    unsigned int i;
+
+    // Search pixel_depths, find the bpp that corresponds to screen_bpp,
+    // then set selected_bpp to match.
+
+    for (i = 0; i < num_depths; ++i)
+    {
+        if (pixel_depths[i].bpp == screen_bpp)
+        {
+            selected_bpp = GetSupportedBPPIndex(pixel_depths[i].description);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void SetSelectedBPP(void)
+{
+    const SDL_VideoInfo *info;
+
+    if (TrySetSelectedBPP())
+    {
+        return;
+    }
+
+    // screen_bpp does not match any supported pixel depth.  Query SDL
+    // to find out what it recommends using.
+
+    info = SDL_GetVideoInfo();
+
+    if (info != NULL && info->vfmt != NULL)
+    {
+        screen_bpp = info->vfmt->BitsPerPixel;
+    }
+
+    // Try again.
+
+    if (!TrySetSelectedBPP())
+    {
+        // Give up and just use the first in the list.
+
+        selected_bpp = 0;
+        screen_bpp = GetSelectedBPP();
+    }
+}
+
 static void ModeSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(mode))
 {
     TXT_CAST_ARG(screen_mode_t, mode);
@@ -177,6 +358,7 @@
 
 static void BuildFullscreenModesList(void)
 {
+    SDL_PixelFormat format;
     SDL_Rect **modes;
     screen_mode_t *m1;
     screen_mode_t *m2;
@@ -194,8 +376,11 @@
     // Get a list of fullscreen modes and find out how many
     // modes are in the list.
 
-    modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
+    format.BitsPerPixel = screen_bpp;
+    format.BytesPerPixel = (screen_bpp + 7) / 8;
 
+    modes = SDL_ListModes(&format, SDL_FULLSCREEN);
+
     if (modes == NULL || modes == (SDL_Rect **) -1)
     {
         num_modes = 0;
@@ -229,6 +414,8 @@
         memcpy(m1, m2, sizeof(screen_mode_t));
         memcpy(m2, &m, sizeof(screen_mode_t));
     }
+
+    num_screen_modes_fullscreen = num_modes;
 }
 
 static int FindBestMode(screen_mode_t *modes)
@@ -295,7 +482,7 @@
     // Build the table
  
     TXT_ClearTable(modes_table);
-    TXT_SetColumnWidths(modes_table, 15, 15, 15);
+    TXT_SetColumnWidths(modes_table, 14, 14, 14, 14, 14);
 
     for (i=0; modes[i].w != 0; ++i) 
     {
@@ -317,10 +504,27 @@
 
     vidmode = FindBestMode(modes);
 
-    screen_width = modes[vidmode].w;
-    screen_height = modes[vidmode].h;
+    if (vidmode > 0)
+    {
+        screen_width = modes[vidmode].w;
+        screen_height = modes[vidmode].h;
+    }
 }
 
+// Callback invoked when the BPP selector is changed.
+
+static void UpdateBPP(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(modes_table))
+{
+    TXT_CAST_ARG(txt_table_t, modes_table);
+
+    screen_bpp = GetSelectedBPP();
+
+    // Rebuild list of fullscreen modes.
+
+    BuildFullscreenModesList();
+    GenerateModesTable(NULL, modes_table);
+}
+
 #if defined(_WIN32) && !defined(_WIN32_WCE)
 
 static int win32_video_driver = 0;
@@ -331,18 +535,6 @@
     "Windows GDI",
 };
 
-// Restart the textscreen library.  Used when the video_driver variable
-// is changed.
-
-static void RestartTextscreen(void)
-{
-    TXT_Shutdown();
-
-    SetDisplayDriver();
-
-    TXT_Init();
-}
-
 static void SetWin32VideoDriver(void)
 {
     if (!strcmp(video_driver, "windib"))
@@ -372,6 +564,11 @@
 
     RestartTextscreen();
 
+    // Rebuild the list of supported pixel depths.
+
+    IdentifyPixelDepths();
+    SetSelectedBPP();
+
     // Rebuild the video modes list
 
     BuildFullscreenModesList();
@@ -385,9 +582,19 @@
 {
     txt_window_t *window;
     txt_table_t *modes_table;
+    txt_table_t *bpp_table;
     txt_checkbox_t *fs_checkbox;
     txt_checkbox_t *ar_checkbox;
+    txt_dropdown_list_t *bpp_selector;
+    int num_columns;
+    int window_y;
 
+    // What color depths are supported?  Generate supported_bpps array
+    // and set selected_bpp to match the current value of screen_bpp.
+
+    IdentifyPixelDepths();
+    SetSelectedBPP();
+
     // First time in? Initialise selected_screen_{width,height}
 
     if (selected_screen_width == 0)
@@ -400,9 +607,6 @@
     
     window = TXT_NewWindow("Display Configuration");
 
-    TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, 
-                                  TXT_SCREEN_W / 2, 5);
-
     TXT_AddWidgets(window, 
                    fs_checkbox = TXT_NewCheckBox("Fullscreen", &fullscreen),
                    ar_checkbox = TXT_NewCheckBox("Correct aspect ratio",
@@ -409,8 +613,38 @@
                                                  &aspect_ratio_correct),
                    NULL);
 
-    modes_table = TXT_NewTable(3);
+    // Some machines can have lots of video modes.  This tries to
+    // keep a limit of six lines by increasing the number of
+    // columns.  In extreme cases, the window is moved up slightly.
 
+    BuildFullscreenModesList();
+
+    window_y = 5;
+
+    if (num_screen_modes_fullscreen <= 18)
+    {
+        num_columns = 3;
+    }
+    else if (num_screen_modes_fullscreen <= 24)
+    {
+        num_columns = 4;
+    }
+    else
+    {
+        num_columns = 5;
+        window_y -= 3;
+    }
+
+    modes_table = TXT_NewTable(num_columns);
+
+    // The window is set at a fixed vertical position.  This keeps
+    // the top of the window stationary when switching between
+    // fullscreen and windowed mode (which causes the window's
+    // height to change).
+
+    TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, 
+                                  TXT_SCREEN_W / 2, window_y);
+
     // On Windows, there is an extra control to change between 
     // the Windows GDI and DirectX video drivers.
 
@@ -442,6 +676,7 @@
 
     TXT_AddWidgets(window,
                    TXT_NewSeparator("Screen mode"),
+                   bpp_table = TXT_NewTable(2),
                    modes_table,
                    TXT_NewSeparator("Misc."),
                    NULL);
@@ -458,6 +693,15 @@
                       TXT_NewCheckBox("Show ENDOOM screen", &show_endoom));
     }
 
+    TXT_AddWidgets(bpp_table,
+                   TXT_NewLabel("Color depth: "),
+                   bpp_selector = TXT_NewDropdownList(&selected_bpp,
+                                                      supported_bpps,
+                                                      num_supported_bpps),
+                   NULL);
+
+
+    TXT_SignalConnect(bpp_selector, "changed", UpdateBPP, modes_table);
     TXT_SignalConnect(fs_checkbox, "changed", GenerateModesTable, modes_table);
     TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);
 
@@ -486,5 +730,25 @@
         M_BindVariable("graphical_startup",        &graphical_startup);
     }
 
+    // Windows Vista or later?  Set screen color depth to
+    // 32 bits per pixel, as 8-bit palettized screen modes
+    // don't work properly in recent versions.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+    {
+        OSVERSIONINFOEX version_info;
+
+        ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
+        version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+        GetVersionEx((OSVERSIONINFO *) &version_info);
+
+        if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
+         && version_info.dwMajorVersion >= 6)
+        {
+            screen_bpp = 32;
+        }
+    }
+#endif
 }
 
--- a/src/setup/execute.c
+++ b/src/setup/execute.c
@@ -88,6 +88,42 @@
     return result;
 }
 
+static int ArgumentNeedsEscape(char *arg)
+{
+    char *p;
+
+    for (p = arg; *p != '\0'; ++p)
+    {
+        if (isspace(*p))
+        {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+// Arguments passed to the setup tool should be passed through to the
+// game when launching a game.  Calling this adds all arguments from
+// myargv to the output context.
+
+void PassThroughArguments(execute_context_t *context)
+{
+    int i;
+
+    for (i = 1; i < myargc; ++i)
+    {
+        if (ArgumentNeedsEscape(myargv[i]))
+        {
+            AddCmdLineParameter(context, "\"%s\"", myargv[i]);
+        }
+        else
+        {
+            AddCmdLineParameter(context, "%s", myargv[i]);
+        }
+    }
+}
+
 execute_context_t *NewExecuteContext(void)
 {
     execute_context_t *result;
@@ -104,25 +140,6 @@
     }
     
     return result;
-}
-
-void AddConfigParameters(execute_context_t *context)
-{
-    int p;
-
-    p = M_CheckParm("-config");
-
-    if (p > 0)
-    {
-        AddCmdLineParameter(context, "-config \"%s\"", myargv[p + 1]);
-    }
-
-    p = M_CheckParm("-extraconfig");
-
-    if (p > 0)
-    {
-        AddCmdLineParameter(context, "-extraconfig \"%s\"", myargv[p + 1]);
-    }
 }
 
 void AddCmdLineParameter(execute_context_t *context, char *s, ...)
--- a/src/setup/execute.h
+++ b/src/setup/execute.h
@@ -35,7 +35,7 @@
 
 execute_context_t *NewExecuteContext(void);
 void AddCmdLineParameter(execute_context_t *context, char *s, ...);
-void AddConfigParameters(execute_context_t *context);
+void PassThroughArguments(execute_context_t *context);
 int ExecuteDoom(execute_context_t *context);
 int FindInstalledIWADs(void);
 
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -65,8 +65,8 @@
 static txt_button_t *joystick_button;
 
 static int *all_joystick_buttons[] = {
-        &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
-        &joybuse, &joybstrafe, &joybjump
+    &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
+    &joybuse, &joybstrafe, &joybprevweapon, &joybnextweapon, &joybjump
 };
 
 //
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -156,7 +156,7 @@
     // Launch Doom
 
     exec = NewExecuteContext();
-    AddConfigParameters(exec);
+    PassThroughArguments(exec);
     ExecuteDoom(exec);
 
     exit(0);
@@ -189,6 +189,7 @@
 {
     txt_window_t *window;
     txt_window_action_t *quit_action;
+    txt_window_action_t *warp_action;
 
     window = TXT_NewWindow("Main Menu");
 
@@ -230,8 +231,12 @@
           NULL);
 
     quit_action = TXT_NewWindowAction(KEY_ESCAPE, "Quit");
+    warp_action = TXT_NewWindowAction(KEY_F1, "Warp");
     TXT_SignalConnect(quit_action, "pressed", QuitConfirm, NULL);
+    TXT_SignalConnect(warp_action, "pressed",
+                      (TxtWidgetSignalFunc) WarpMenu, NULL);
     TXT_SetWindowAction(window, TXT_HORIZ_LEFT, quit_action);
+    TXT_SetWindowAction(window, TXT_HORIZ_CENTER, warp_action);
 
     TXT_SetKeyListener(window, MainMenuKeyPress, NULL);
 }
@@ -292,11 +297,9 @@
     free(mask);
 }
 
-// 
-// Initialize and run the textscreen GUI.
-//
+// Initialize the textscreen library.
 
-static void RunGUI(void)
+static void InitTextscreen(void)
 {
     SetDisplayDriver();
 
@@ -308,6 +311,24 @@
 
     TXT_SetDesktopTitle(PACKAGE_NAME " Setup ver " PACKAGE_VERSION);
     SetIcon();
+}
+
+// Restart the textscreen library.  Used when the video_driver variable
+// is changed.
+
+void RestartTextscreen(void)
+{
+    TXT_Shutdown();
+    InitTextscreen();
+}
+
+// 
+// Initialize and run the textscreen GUI.
+//
+
+static void RunGUI(void)
+{
+    InitTextscreen();
 
     TXT_GUIMainLoop();
 }
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@ -70,10 +70,10 @@
 
 static char *doom_skills[] = 
 {
-    "I'm too young to die!",
+    "I'm too young to die.",
     "Hey, not too rough.",
     "Hurt me plenty.",
-    "Ultra-violence",
+    "Ultra-Violence.",
     "NIGHTMARE!",
 };
 
@@ -144,6 +144,7 @@
 static int respawn = 0;
 static int udpport = 2342;
 static int timer = 0;
+static int privateserver = 0;
 
 static txt_dropdown_list_t *skillbutton;
 static txt_button_t *warpbutton;
@@ -209,7 +210,11 @@
     }
 }
 
-static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
+// Callback function invoked to launch the game.
+// This is used when starting a server and also when starting a
+// single player game via the "warp" menu.
+
+static void StartGame(int multiplayer)
 {
     execute_context_t *exec;
 
@@ -221,7 +226,6 @@
     AddExtraParameters(exec);
 
     AddIWADParameter(exec);
-    AddCmdLineParameter(exec, "-server");
     AddCmdLineParameter(exec, "-skill %i", skill + 1);
 
     if (gamemission == hexen)
@@ -244,20 +248,6 @@
         AddCmdLineParameter(exec, "-respawn");
     }
 
-    if (deathmatch == 1)
-    {
-        AddCmdLineParameter(exec, "-deathmatch");
-    }
-    else if (deathmatch == 2)
-    {
-        AddCmdLineParameter(exec, "-altdeath");
-    }
-
-    if (timer > 0)
-    {
-        AddCmdLineParameter(exec, "-timer %i", timer);
-    }
-
     if (warptype == WARP_ExMy)
     {
         // TODO: select IWAD based on warp type
@@ -268,14 +258,39 @@
         AddCmdLineParameter(exec, "-warp %i", warpmap);
     }
 
-    AddCmdLineParameter(exec, "-port %i", udpport);
+    // Multiplayer-specific options:
 
+    if (multiplayer)
+    {
+        AddCmdLineParameter(exec, "-server");
+        AddCmdLineParameter(exec, "-port %i", udpport);
+
+        if (deathmatch == 1)
+        {
+            AddCmdLineParameter(exec, "-deathmatch");
+        }
+        else if (deathmatch == 2)
+        {
+            AddCmdLineParameter(exec, "-altdeath");
+        }
+
+        if (timer > 0)
+        {
+            AddCmdLineParameter(exec, "-timer %i", timer);
+        }
+
+        if (privateserver)
+        {
+            AddCmdLineParameter(exec, "-privateserver");
+        }
+    }
+
     AddWADs(exec);
 
     TXT_Shutdown();
     
     M_SaveDefaults();
-    AddConfigParameters(exec);
+    PassThroughArguments(exec);
 
     ExecuteDoom(exec);
 
@@ -282,6 +297,16 @@
     exit(0);
 }
 
+static void StartServerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+    StartGame(1);
+}
+
+static void StartSinglePlayerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+    StartGame(0);
+}
+
 static void UpdateWarpButton(void)
 {
     char buf[10];
@@ -544,13 +569,28 @@
     return result;
 }
 
-static txt_window_action_t *StartGameAction(void)
+// Create the window action button to start the game.  This invokes
+// a different callback depending on whether to start a multiplayer
+// or single player game.
+
+static txt_window_action_t *StartGameAction(int multiplayer)
 {
     txt_window_action_t *action;
+    TxtWidgetSignalFunc callback;
 
     action = TXT_NewWindowAction(KEY_F10, "Start");
-    TXT_SignalConnect(action, "pressed", StartGame, NULL);
 
+    if (multiplayer)
+    {
+        callback = StartServerGame;
+    }
+    else
+    {
+        callback = StartSinglePlayerGame;
+    }
+
+    TXT_SignalConnect(action, "pressed", callback, NULL);
+
     return action;
 }
 
@@ -591,7 +631,11 @@
     return action;
 }
 
-void StartMultiGame(void)
+// "Start game" menu.  This is used for the start server window
+// and the single player warp menu.  The parameters specify
+// the window title and whether to display multiplayer options.
+
+static void StartGameMenu(char *window_title, int multiplayer)
 {
     txt_window_t *window;
     txt_table_t *gameopt_table;
@@ -599,7 +643,7 @@
     txt_widget_t *iwad_selector;
     int num_mult_types = 2;
 
-    window = TXT_NewWindow("Start multiplayer game");
+    window = TXT_NewWindow(window_title);
 
     TXT_AddWidgets(window, 
                    gameopt_table = TXT_NewTable(2),
@@ -609,14 +653,12 @@
                    TXT_NewCheckBox("Respawning monsters", &respawn),
                    TXT_NewSeparator("Advanced"),
                    advanced_table = TXT_NewTable(2),
-                   TXT_NewButton2("Add extra parameters...", 
-                                  OpenExtraParamsWindow, NULL),
                    NULL);
 
     TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction());
-    TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction());
+    TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction(multiplayer));
     
-    TXT_SetColumnWidths(gameopt_table, 12, 12);
+    TXT_SetColumnWidths(gameopt_table, 12, 6);
 
     if (gamemission == doom)
     {
@@ -632,14 +674,8 @@
            iwad_selector = IWADSelector(),
            TXT_NewLabel("Skill"),
            skillbutton = TXT_NewDropdownList(&skill, doom_skills, 5),
-           TXT_NewLabel("Game type"),
-           TXT_NewDropdownList(&deathmatch, gamemodes, num_mult_types),
            TXT_NewLabel("Level warp"),
            warpbutton = TXT_NewButton2("????", LevelSelectDialog, NULL),
-           TXT_NewLabel("Time limit"),
-           TXT_NewHorizBox(TXT_NewIntInputBox(&timer, 2),
-                           TXT_NewLabel("minutes"),
-                           NULL),
            NULL);
 
     if (gamemission == hexen)
@@ -651,19 +687,49 @@
                        NULL);
     }
 
-    TXT_SetColumnWidths(advanced_table, 12, 12);
+    if (multiplayer)
+    {
+        TXT_AddWidgets(gameopt_table,
+               TXT_NewLabel("Game type"),
+               TXT_NewDropdownList(&deathmatch, gamemodes, num_mult_types),
+               TXT_NewLabel("Time limit"),
+               TXT_NewHorizBox(TXT_NewIntInputBox(&timer, 2),
+                               TXT_NewLabel("minutes"),
+                               NULL),
+               NULL);
 
-    TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
+        TXT_AddWidget(window,
+                      TXT_NewInvertedCheckBox("Register with master server",
+                                              &privateserver));
 
-    TXT_AddWidgets(advanced_table, 
-                   TXT_NewLabel("UDP port"),
-                   TXT_NewIntInputBox(&udpport, 5),
-                   NULL);
+        TXT_AddWidgets(advanced_table,
+                       TXT_NewLabel("UDP port"),
+                       TXT_NewIntInputBox(&udpport, 5),
+                       NULL);
+    }
 
+    TXT_AddWidget(window,
+                  TXT_NewButton2("Add extra parameters...", 
+                                 OpenExtraParamsWindow, NULL));
+
+    TXT_SetColumnWidths(advanced_table, 12, 6);
+
+    TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
+
     UpdateWarpType(NULL, NULL);
     UpdateWarpButton();
 }
 
+void StartMultiGame(void)
+{
+    StartGameMenu("Start multiplayer game", 1);
+}
+
+void WarpMenu(void)
+{
+    StartGameMenu("Level Warp", 0);
+}
+
 static void DoJoinGame(void *unused1, void *unused2)
 {
     execute_context_t *exec;
@@ -695,7 +761,7 @@
     
     M_SaveDefaults();
 
-    AddConfigParameters(exec);
+    PassThroughArguments(exec);
 
     ExecuteDoom(exec);
 
--- a/src/setup/multiplayer.h
+++ b/src/setup/multiplayer.h
@@ -23,6 +23,7 @@
 #define SETUP_MULTIPLAYER_H
 
 void StartMultiGame(void);
+void WarpMenu(void);
 void JoinMultiGame(void);
 void MultiplayerConfig(void);
 
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -206,6 +206,7 @@
 
 txt_widget_class_t txt_joystick_input_class =
 {
+    TXT_AlwaysSelectable,
     TXT_JoystickInputSizeCalc,
     TXT_JoystickInputDrawer,
     TXT_JoystickInputKeyPress,
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -171,6 +171,7 @@
 
 txt_widget_class_t txt_key_input_class =
 {
+    TXT_AlwaysSelectable,
     TXT_KeyInputSizeCalc,
     TXT_KeyInputDrawer,
     TXT_KeyInputKeyPress,
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -164,6 +164,7 @@
 
 txt_widget_class_t txt_mouse_input_class =
 {
+    TXT_AlwaysSelectable,
     TXT_MouseInputSizeCalc,
     TXT_MouseInputDrawer,
     TXT_MouseInputKeyPress,
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -126,8 +126,6 @@
 boolean		autostart;
 int             startloadgame;
 
-FILE*		debugfile;
-
 boolean		advancedemo;
 
 // villsa [STRIFE] workparm variable (similar to devparm?)
@@ -475,14 +473,6 @@
     if (demorecording)
         G_BeginRecording ();
 
-    if (M_CheckParm ("-debugfile"))
-    {
-        char    filename[20];
-        sprintf (filename,"debug%i.txt",consoleplayer);
-        printf ("debug output to: %s\n",filename);
-        debugfile = fopen (filename,"w");
-    }
-
     TryRunTics();
 
     if(!showintro) // [STRIFE]
@@ -745,7 +735,7 @@
 // These are from the original source: some of them are perhaps
 // not used in any dehacked patches
 
-static char *banners[] = 
+static char *banners[] =
 {
     // strife1.wad:
 
@@ -1040,9 +1030,9 @@
     // "ultimate" and "final".
     //
 
-    p = M_CheckParm("-gameversion");
+    p = M_CheckParmWithArgs("-gameversion", 1);
 
-    if (p > 0)
+    if (p)
     {
         for (i=0; gameversions[i].description != NULL; ++i)
         {
@@ -1431,6 +1421,21 @@
     }
 
     //!
+    // @category net
+    //
+    // Query the Internet master server for a global list of active
+    // servers.
+    //
+
+    if (M_CheckParm("-search"))
+    {
+        printf("\nSearching for servers on Internet ...\n");
+        p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
+        printf("\n%i server(s) found.\n", p);
+        exit(0);
+    }
+
+    //!
     // @arg <address>
     // @category net
     //
@@ -1438,11 +1443,12 @@
     // address.
     //
 
-    p = M_CheckParm("-query");
+    p = M_CheckParmWithArgs("-query", 1);
 
-    if (p > 0)
+    if (p)
     {
         NET_QueryAddress(myargv[p+1]);
+        exit(0);
     }
 
     //!
@@ -1451,8 +1457,13 @@
     // Search the local LAN for running servers.
     //
 
-    if (M_CheckParm("-search"))
-        NET_LANQuery();
+    if (M_CheckParm("-localsearch"))
+    {
+        printf("\nSearching for servers on local LAN ...\n");
+        p = NET_LANQuery(NET_QueryPrintCallback, NULL);
+        printf("\n%i server(s) found.\n", p);
+        exit(0);
+    }
 
 #endif
             
@@ -1615,24 +1626,10 @@
     // add any files specified on the command line with -file wadfile
     // to the wad list
     //
-    // convenience hack to allow -wart e m to add a wad file
-    // prepend a tilde to the filename so wadfile will be reloadable
-    p = M_CheckParm ("-wart");
-    if (p)
-    {
-        myargv[p][4] = 'p';     // big hack, change to -warp
 
-        // Map name handling.
-        // [STRIFE]: looks for f:/st/data
-        p = atoi (myargv[p+1]);
-        if (p<10)
-            sprintf (file,"~f:/st/data/map0%i.wad", p);
-        else
-            sprintf (file,"~f:/st/data/map%i.wad", p);
+    // Debug:
+//    W_PrintDirectory();
 
-        D_AddFile (file);
-    }
-
     //!
     // @arg <demo>
     // @category demo
@@ -1641,7 +1638,7 @@
     // Play back the demo named demo.lmp.
     //
 
-    p = M_CheckParm ("-playdemo");
+    p = M_CheckParmWithArgs ("-playdemo", 1);
 
     if (!p)
     {
@@ -1653,11 +1650,11 @@
         // Play back the demo named demo.lmp, determining the framerate
         // of the screen.
         //
-        p = M_CheckParm ("-timedemo");
+	p = M_CheckParmWithArgs("-timedemo", 1);
 
     }
 
-    if (p && p < myargc-1)
+    if (p)
     {
         if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
         {
@@ -1746,9 +1743,9 @@
     // 0 disables all monsters.
     //
 
-    p = M_CheckParm ("-skill");
+    p = M_CheckParmWithArgs("-skill", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
         startskill = myargv[p+1][0]-'1';
         autostart = true;
@@ -1761,9 +1758,9 @@
     // Start playing on episode n (1-4)
     //
 
-    p = M_CheckParm ("-episode");
+    p = M_CheckParmWithArgs("-episode", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
         startepisode = myargv[p+1][0]-'0';
         startmap = 1;
@@ -1780,9 +1777,9 @@
     // For multiplayer games: exit each level after n minutes.
     //
 
-    p = M_CheckParm ("-timer");
+    p = M_CheckParmWithArgs("-timer", 1);
 
-    if (p && p < myargc-1 && deathmatch)
+    if (p)
     {
         timelimit = atoi(myargv[p+1]);
         printf("timer: %i\n", timelimit);
@@ -1797,10 +1794,8 @@
 
     p = M_CheckParm ("-avg");
 
-    if (p && p < myargc-1 && deathmatch)
+    if (p)
     {
-        DEH_printf("Austin Virtual Gaming: Levels will end "
-                       "after 20 minutes\n");
         timelimit = 20;
     }
 
@@ -1812,9 +1807,9 @@
     // (Doom 2)
     //
 
-    p = M_CheckParm ("-warp");
+    p = M_CheckParmWithArgs("-warp", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
         if (gamemode == commercial)
             startmap = atoi (myargv[p+1]);
@@ -1858,9 +1853,9 @@
     // Load the game in slot s.
     //
 
-    p = M_CheckParm ("-loadgame");
+    p = M_CheckParmWithArgs("-loadgame", 1);
     
-    if (p && p < myargc-1)
+    if (p)
     {
         startloadgame = atoi(myargv[p+1]);
     }
@@ -1956,9 +1951,9 @@
     // Record a demo named x.lmp.
     //
 
-    p = M_CheckParm ("-record");
+    p = M_CheckParmWithArgs("-record", 1);
 
-    if (p && p < myargc-1)
+    if (p)
     {
         G_RecordDemo (myargv[p+1]);
         autostart = true;
@@ -1965,8 +1960,8 @@
     }
     D_IntroTick(); // [STRIFE]
 
-    p = M_CheckParm ("-playdemo");
-    if (p && p < myargc-1)
+    p = M_CheckParmWithArgs("-playdemo", 1);
+    if (p)
     {
         singledemo = true;              // quit after one demo
         G_DeferedPlayDemo (demolumpname);
@@ -1974,8 +1969,8 @@
     }
     D_IntroTick(); // [STRIFE]
 
-    p = M_CheckParm ("-timedemo");
-    if (p && p < myargc-1)
+    p = M_CheckParmWithArgs("-timedemo", 1);
+    if (p)
     {
         G_TimeDemo (demolumpname);
         D_DoomLoop ();  // never returns
--- a/src/strife/d_net.c
+++ b/src/strife/d_net.c
@@ -49,7 +49,6 @@
 #include "net_sdl.h"
 #include "net_loop.h"
 
-
 //
 // NETWORKING
 //
@@ -292,6 +291,7 @@
             if (i > 0)
             {
                 addr = NET_FindLANServer();
+                NET_SV_RegisterWithMaster();
 
                 if (addr == NULL)
                 {
@@ -307,7 +307,7 @@
             // address.
             //
             
-            i = M_CheckParm("-connect");
+            i = M_CheckParmWithArgs("-connect", 1);
 
             if (i > 0)
             {
@@ -382,12 +382,22 @@
 
     // Show players here; the server might have specified a time limit
 
-    if (timelimit > 0)
+    if (timelimit > 0 && deathmatch)
     {
-	DEH_printf("Levels will end after %d minute", timelimit);
-	if (timelimit > 1)
-	    printf("s");
-	printf(".\n");
+        // Gross hack to work like Vanilla:
+
+        if (timelimit == 20 && M_CheckParm("-avg"))
+        {
+            DEH_printf("Austin Virtual Gaming: Levels will end "
+                           "after 20 minutes\n");
+        }
+        else
+        {
+            DEH_printf("Levels will end after %d minute", timelimit);
+            if (timelimit > 1)
+                printf("s");
+            printf(".\n");
+        }
     }
 }
 
@@ -399,9 +409,6 @@
 //
 void D_QuitNetGame (void)
 {
-    if (debugfile)
-	fclose (debugfile);
-
 #ifdef FEATURE_MULTIPLAYER
 
     NET_SV_Shutdown();
--- a/src/strife/doomstat.h
+++ b/src/strife/doomstat.h
@@ -264,7 +264,6 @@
 // File handling stuff.
 extern  char *          savegamedir;
 extern	char		basedefault[1024];
-extern  FILE*		debugfile;
 
 // if true, load all graphics at level load
 extern  boolean         precache;
--- a/src/strife/dstrings.h
+++ b/src/strife/dstrings.h
@@ -38,15 +38,6 @@
 #define SAVEGAMENAME	"doomsav"
 
 
-//
-// File locations,
-//  relative to current position.
-// Path names are OS-sensitive.
-//
-#define DEVMAPS "devmaps"
-#define DEVDATA "devdata"
-
-
 // QuitDOOM messages
 // 8 per each game type
 #define NUM_QUITMESSAGES   8
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -138,7 +138,7 @@
 int             levelstarttic;          // gametic at level start 
 int             totalkills, /*totalitems,*/ totalsecret;    // for intermission 
  
-char            demoname[32]; 
+char           *demoname;
 boolean         demorecording; 
 boolean         longtics;               // cph's doom 1.91 longtics hack
 boolean         lowres_turn;            // low resolution turning for longtics
@@ -2280,8 +2280,8 @@
     int             maxsize;
 
     usergame = false; 
-    strcpy (demoname, name); 
-    strcat (demoname, ".lmp"); 
+    demoname = Z_Malloc(strlen(name) + 5, PU_STATIC, NULL);
+    sprintf(demoname, "%s.lmp", name);
     maxsize = 0x20000;
 
     //!
@@ -2292,8 +2292,8 @@
     // Specify the demo buffer size (KiB)
     //
 
-    i = M_CheckParm ("-maxdemo");
-    if (i && i<myargc-1)
+    i = M_CheckParmWithArgs("-maxdemo", 1);
+    if (i)
         maxsize = atoi(myargv[i+1])*1024;
     demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); 
     demoend = demobuffer + maxsize;
@@ -2450,10 +2450,11 @@
     // Play back a demo recorded in a netgame with a single player.
     //
 
-    if (playeringame[1] || M_CheckParm("-netdemo") > 0) 
-    { 
-        netgame = true; 
-        netdemo = true; 
+    if (playeringame[1] || M_CheckParm("-solo-net") > 0
+                        || M_CheckParm("-netdemo") > 0)
+    {
+	netgame = true;
+	netdemo = true;
     }
 
     // don't spend a lot of time in loadlevel 
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@ -1679,23 +1679,23 @@
 
     if (ev->type == ev_joystick && joywait < I_GetTime())
     {
-        if (ev->data3 == -1)
+        if (ev->data3 < 0)
         {
             key = key_menu_up;
             joywait = I_GetTime() + 5;
         }
-        else if (ev->data3 == 1)
+        else if (ev->data3 > 0)
         {
             key = key_menu_down;
             joywait = I_GetTime() + 5;
         }
 
-        if (ev->data2 == -1)
+        if (ev->data2 < 0)
         {
             key = key_menu_left;
             joywait = I_GetTime() + 2;
         }
-        else if (ev->data2 == 1)
+        else if (ev->data2 > 0)
         {
             key = key_menu_right;
             joywait = I_GetTime() + 2;
--- a/src/strife/p_map.c
+++ b/src/strife/p_map.c
@@ -1628,7 +1628,7 @@
         // Use the specified magic value when emulating spechit overruns.
         //
 
-        p = M_CheckParm("-spechit");
+        p = M_CheckParmWithArgs("-spechit", 1);
         
         if (p > 0)
         {
--- a/src/strife/p_spec.c
+++ b/src/strife/p_spec.c
@@ -1713,9 +1713,9 @@
         // system.  The default (if this option is not specified) is to
         // emulate the behavior when running under Windows 98.
 
-        p = M_CheckParm("-donut");
+        p = M_CheckParmWithArgs("-donut", 2);
 
-        if (p > 0 && p < myargc - 2)
+        if (p > 0)
         {
             // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008
             //
@@ -1893,10 +1893,9 @@
     if (W_CheckNumForName(DEH_String("texture2")) >= 0)
         episode = 2;
 
-
     // See if -TIMER was specified.
 
-    if (timelimit > 0)
+    if (timelimit > 0 && deathmatch)
     {
         levelTimer = true;
         levelTimeCount = timelimit * 60 * TICRATE;
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@ -409,6 +409,7 @@
 
 static void GenerateTextureHashTable(void)
 {
+    texture_t **rover;
     int i;
     int key;
 
@@ -425,12 +426,25 @@
 
         textures[i]->index = i;
 
-        // Hook into hash table
+        // Vanilla Doom does a linear search of the texures array
+        // and stops at the first entry it finds.  If there are two
+        // entries with the same name, the first one in the array
+        // wins. The new entry must therefore be added at the end
+        // of the hash chain, so that earlier entries win.
 
         key = W_LumpNameHash(textures[i]->name) % numtextures;
 
-        textures[i]->next = textures_hashtable[key];
-        textures_hashtable[key] = textures[i];
+        rover = &textures_hashtable[key];
+
+        while (*rover != NULL)
+        {
+            rover = &(*rover)->next;
+        }
+
+        // Hook into hash table
+
+        textures[i]->next = NULL;
+        *rover = textures[i];
     }
 }
 
--- a/src/w_main.c
+++ b/src/w_main.c
@@ -53,7 +53,7 @@
     // into the main IWAD.  Multiple files may be specified.
     //
 
-    p = M_CheckParm("-merge");
+    p = M_CheckParmWithArgs("-merge", 1);
 
     if (p > 0)
     {
@@ -81,7 +81,7 @@
     // Simulates the behavior of NWT's -merge option.  Multiple files
     // may be specified.
 
-    p = M_CheckParm("-nwtmerge");
+    p = M_CheckParmWithArgs("-nwtmerge", 1);
 
     if (p > 0)
     {
@@ -108,7 +108,7 @@
     // the main IWAD directory.  Multiple files may be specified.
     //
 
-    p = M_CheckParm("-af");
+    p = M_CheckParmWithArgs("-af", 1);
 
     if (p > 0)
     {
@@ -133,7 +133,7 @@
     // into the main IWAD directory.  Multiple files may be specified.
     //
 
-    p = M_CheckParm("-as");
+    p = M_CheckParmWithArgs("-as", 1);
 
     if (p > 0)
     {
@@ -156,7 +156,7 @@
     // Equivalent to "-af <files> -as <files>".
     //
 
-    p = M_CheckParm("-aa");
+    p = M_CheckParmWithArgs("-aa", 1);
 
     if (p > 0)
     {
@@ -182,7 +182,7 @@
     // Load the specified PWAD files.
     //
 
-    p = M_CheckParm ("-file");
+    p = M_CheckParmWithArgs ("-file", 1);
     if (p)
     {
 	// the parms after p are wadfile/lump names,
--- a/textscreen/examples/guitest.c
+++ b/textscreen/examples/guitest.c
@@ -163,8 +163,8 @@
 {
     txt_window_t *window;
     txt_table_t *table;
+    txt_table_t *unselectable_table;
     txt_scrollpane_t *scrollpane;
-    int i;
 
     window = TXT_NewWindow("Another test");
     TXT_SetWindowPosition(window, 
@@ -172,10 +172,13 @@
                           TXT_VERT_TOP, 
                           TXT_SCREEN_W - 1, 1);
 
-    for (i=0; i<5; ++i)
-    {
-        TXT_AddWidget(window, TXT_NewButton("hello there blah blah blah blah"));
-    }
+    TXT_AddWidgets(window,
+                   TXT_NewScrollPane(40, 1,
+                        TXT_NewLabel("* Unselectable scroll pane *")),
+                   unselectable_table = TXT_NewTable(1),
+                   NULL);
+
+    TXT_AddWidget(unselectable_table, TXT_NewLabel("* Unselectable table *"));
 
     TXT_AddWidget(window, TXT_NewSeparator("Input boxes"));
     table = TXT_NewTable(2);
--- a/textscreen/txt_button.c
+++ b/textscreen/txt_button.c
@@ -96,6 +96,7 @@
 
 txt_widget_class_t txt_button_class =
 {
+    TXT_AlwaysSelectable,
     TXT_ButtonSizeCalc,
     TXT_ButtonDrawer,
     TXT_ButtonKeyPress,
--- a/textscreen/txt_checkbox.c
+++ b/textscreen/txt_checkbox.c
@@ -34,9 +34,9 @@
 {
     TXT_CAST_ARG(txt_checkbox_t, checkbox);
 
-    // Minimum width is the string length + two spaces for padding
+    // Minimum width is the string length + right-side space for padding
 
-    checkbox->widget.w = strlen(checkbox->label) + 6;
+    checkbox->widget.w = strlen(checkbox->label) + 5;
     checkbox->widget.h = 1;
 }
 
@@ -50,7 +50,7 @@
 
     TXT_BGColor(TXT_COLOR_BLUE, 0);
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
-    TXT_DrawString(" (");
+    TXT_DrawString("(");
 
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
@@ -76,7 +76,7 @@
 
     TXT_DrawString(checkbox->label);
     
-    for (i=strlen(checkbox->label); i < w-6; ++i)
+    for (i=strlen(checkbox->label); i < w-5; ++i)
     {
         TXT_DrawString(" ");
     }
@@ -117,6 +117,7 @@
 
 txt_widget_class_t txt_checkbox_class =
 {
+    TXT_AlwaysSelectable,
     TXT_CheckBoxSizeCalc,
     TXT_CheckBoxDrawer,
     TXT_CheckBoxKeyPress,
--- a/textscreen/txt_dropdown.c
+++ b/textscreen/txt_dropdown.c
@@ -262,6 +262,7 @@
 
 txt_widget_class_t txt_dropdown_list_class =
 {
+    TXT_AlwaysSelectable,
     TXT_DropdownListSizeCalc,
     TXT_DropdownListDrawer,
     TXT_DropdownListKeyPress,
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -232,6 +232,7 @@
 
 txt_widget_class_t txt_inputbox_class =
 {
+    TXT_AlwaysSelectable,
     TXT_InputBoxSizeCalc,
     TXT_InputBoxDrawer,
     TXT_InputBoxKeyPress,
@@ -242,6 +243,7 @@
 
 txt_widget_class_t txt_int_inputbox_class =
 {
+    TXT_AlwaysSelectable,
     TXT_InputBoxSizeCalc,
     TXT_InputBoxDrawer,
     TXT_IntInputBoxKeyPress,
--- a/textscreen/txt_label.c
+++ b/textscreen/txt_label.c
@@ -104,6 +104,7 @@
 
 txt_widget_class_t txt_label_class =
 {
+    TXT_NeverSelectable,
     TXT_LabelSizeCalc,
     TXT_LabelDrawer,
     NULL,
@@ -170,7 +171,6 @@
     label = malloc(sizeof(txt_label_t));
 
     TXT_InitWidget(label, &txt_label_class);
-    label->widget.selectable = 0;
     label->label = NULL;
     label->lines = NULL;
 
--- a/textscreen/txt_radiobutton.c
+++ b/textscreen/txt_radiobutton.c
@@ -34,9 +34,9 @@
 {
     TXT_CAST_ARG(txt_radiobutton_t, radiobutton);
 
-    // Minimum width is the string length + two spaces for padding
+    // Minimum width is the string length + right-side spaces for padding
 
-    radiobutton->widget.w = strlen(radiobutton->label) + 6;
+    radiobutton->widget.w = strlen(radiobutton->label) + 5;
     radiobutton->widget.h = 1;
 }
 
@@ -50,7 +50,7 @@
 
     TXT_BGColor(TXT_COLOR_BLUE, 0);
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
-    TXT_DrawString(" (");
+    TXT_DrawString("(");
 
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
@@ -76,7 +76,7 @@
 
     TXT_DrawString(radiobutton->label);
     
-    for (i=strlen(radiobutton->label); i < w-6; ++i)
+    for (i=strlen(radiobutton->label); i < w-5; ++i)
     {
         TXT_DrawString(" ");
     }
@@ -121,6 +121,7 @@
 
 txt_widget_class_t txt_radiobutton_class =
 {
+    TXT_AlwaysSelectable,
     TXT_RadioButtonSizeCalc,
     TXT_RadioButtonDrawer,
     TXT_RadioButtonKeyPress,
--- a/textscreen/txt_scrollpane.c
+++ b/textscreen/txt_scrollpane.c
@@ -138,7 +138,7 @@
     }
     if (scrollpane->expand_h)
     {
-        scrollpane->h = FullWidth(scrollpane);
+        scrollpane->h = FullHeight(scrollpane);
     }
 
     scrollpane->widget.w = scrollpane->w;
@@ -486,8 +486,26 @@
     }
 }
 
+static int TXT_ScrollPaneSelectable(TXT_UNCAST_ARG(scrollpane))
+{
+    TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
+
+    // If scroll bars are displayed, the scroll pane must be selectable
+    // so that we can use the arrow keys to scroll around.
+
+    if (NeedsScrollbars(scrollpane))
+    {
+        return 1;
+    }
+
+    // Otherwise, whether this is selectable depends on the child widget.
+
+    return TXT_SelectableWidget(scrollpane->child);
+}
+
 txt_widget_class_t txt_scrollpane_class =
 {
+    TXT_ScrollPaneSelectable,
     TXT_ScrollPaneSizeCalc,
     TXT_ScrollPaneDrawer,
     TXT_ScrollPaneKeyPress,
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -119,6 +119,22 @@
 
 #endif
 
+static txt_font_t *FontForName(char *name)
+{
+    if (!strcmp(name, "small"))
+    {
+        return &small_font;
+    }
+    else if (!strcmp(name, "normal"))
+    {
+        return &main_font;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
 //
 // Select the font to use, based on screen resolution
 //
@@ -129,10 +145,23 @@
 static void ChooseFont(void)
 {
     SDL_Rect **modes;
+    char *env;
     int i;
 
-    font = &main_font;
+    // Allow normal selection to be overridden from an environment variable:
 
+    env = getenv("TEXTSCREEN_FONT");
+
+    if (env != NULL)
+    {
+        font = FontForName(env);
+
+        if (font != NULL)
+        {
+            return;
+        }
+    }
+
     // Check all modes
 
     modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
@@ -140,6 +169,8 @@
     // If in doubt and we can't get a list, always prefer to
     // fall back to the normal font:
 
+    font = &main_font;
+
     if (modes == NULL || modes == (SDL_Rect **) -1 || *modes == NULL)
     {
 #ifdef _WIN32_WCE
@@ -356,10 +387,6 @@
         case SDLK_DELETE:      return KEY_DEL;
 
         case SDLK_PAUSE:       return KEY_PAUSE;
-
-#if !SDL_VERSION_ATLEAST(1, 3, 0)
-        case SDLK_EQUALS:      return KEY_EQUALS;
-#endif
 
         case SDLK_LSHIFT:
         case SDLK_RSHIFT:
--- a/textscreen/txt_separator.c
+++ b/textscreen/txt_separator.c
@@ -82,6 +82,7 @@
 
 txt_widget_class_t txt_separator_class =
 {
+    TXT_NeverSelectable,
     TXT_SeparatorSizeCalc,
     TXT_SeparatorDrawer,
     NULL,
@@ -97,7 +98,6 @@
     separator = malloc(sizeof(txt_separator_t));
 
     TXT_InitWidget(separator, &txt_separator_class);
-    separator->widget.selectable = 0;
 
     if (label != NULL)
     {
--- a/textscreen/txt_spinctrl.c
+++ b/textscreen/txt_spinctrl.c
@@ -358,6 +358,7 @@
 
 txt_widget_class_t txt_spincontrol_class =
 {
+    TXT_AlwaysSelectable,
     TXT_SpinControlSizeCalc,
     TXT_SpinControlDrawer,
     TXT_SpinControlKeyPress,
--- a/textscreen/txt_strut.c
+++ b/textscreen/txt_strut.c
@@ -55,6 +55,7 @@
 
 txt_widget_class_t txt_strut_class =
 {
+    TXT_NeverSelectable,
     TXT_StrutSizeCalc,
     TXT_StrutDrawer,
     TXT_StrutKeyPress,
@@ -70,7 +71,6 @@
     strut = malloc(sizeof(txt_strut_t));
 
     TXT_InitWidget(strut, &txt_strut_class);
-    strut->widget.selectable = 0;
     strut->width = width;
     strut->height = height;
 
--- a/textscreen/txt_table.c
+++ b/textscreen/txt_table.c
@@ -202,7 +202,7 @@
     va_end(args);
 }
 
-static int SelectableWidget(txt_table_t *table, int x, int y)
+static int SelectableCell(txt_table_t *table, int x, int y)
 {
     txt_widget_t *widget;
     int i;
@@ -217,7 +217,9 @@
     if (i >= 0 && i < table->num_widgets)
     {
         widget = table->widgets[i];
-        return widget != NULL && widget->selectable && widget->visible;
+        return widget != NULL
+            && TXT_SelectableWidget(widget)
+            && widget->visible;
     }
 
     return 0;
@@ -237,7 +239,7 @@
     {
         // Search to the right
 
-        if (SelectableWidget(table, start_col + x, row))
+        if (SelectableCell(table, start_col + x, row))
         {
             return start_col + x;
         }
@@ -244,7 +246,7 @@
 
         // Search to the left
 
-        if (SelectableWidget(table, start_col - x, row))
+        if (SelectableCell(table, start_col - x, row))
         {
             return start_col - x;
         }
@@ -270,7 +272,7 @@
     if (selected >= 0 && selected < table->num_widgets)
     {
         if (table->widgets[selected] != NULL
-         && table->widgets[selected]->selectable
+         && TXT_SelectableWidget(table->widgets[selected])
          && TXT_WidgetKeyPress(table->widgets[selected], key))
         {
             return 1;
@@ -329,7 +331,7 @@
 
         for (new_x = table->selected_x - 1; new_x >= 0; --new_x)
         {
-            if (SelectableWidget(table, new_x, table->selected_y))
+            if (SelectableCell(table, new_x, table->selected_y))
             {
                 // Found a selectable widget!
 
@@ -348,7 +350,7 @@
 
         for (new_x = table->selected_x + 1; new_x < table->columns; ++new_x)
         {
-            if (SelectableWidget(table, new_x, table->selected_y))
+            if (SelectableCell(table, new_x, table->selected_y))
             {
                 // Found a selectable widget!
 
@@ -547,7 +549,7 @@
 
                 // Select the cell if the widget is selectable
 
-                if (widget->selectable)
+                if (TXT_SelectableWidget(widget))
                 {
                     table->selected_x = i % table->columns;
                     table->selected_y = i / table->columns;
@@ -563,8 +565,41 @@
     }
 }
 
+// Determine whether the table is selectable.
+
+static int TXT_TableSelectable(TXT_UNCAST_ARG(table))
+{
+    TXT_CAST_ARG(txt_table_t, table);
+    int i;
+
+    // Is the currently-selected cell selectable?
+
+    if (SelectableCell(table, table->selected_x, table->selected_y))
+    {
+        return 1;
+    }
+
+    // Find the first selectable cell and set selected_x, selected_y.
+
+    for (i = 0; i < table->num_widgets; ++i)
+    {
+        if (table->widgets[i] != NULL
+         && TXT_SelectableWidget(table->widgets[i]))
+        {
+            table->selected_x = i % table->columns;
+            table->selected_y = i / table->columns;
+            return 1;
+        }
+    }
+
+    // No selectable widgets exist within the table.
+
+    return 0;
+}
+
 txt_widget_class_t txt_table_class =
 {
+    TXT_TableSelectable,
     TXT_CalcTableSize,
     TXT_TableDrawer,
     TXT_TableKeyPress,
--- a/textscreen/txt_widget.c
+++ b/textscreen/txt_widget.c
@@ -83,9 +83,8 @@
     widget->widget_class = widget_class;
     widget->callback_table = TXT_NewCallbackTable();
 
-    // Default values: visible and selectable
+    // Visible by default.
 
-    widget->selectable = 1;
     widget->visible = 1;
 
     // Align left by default
@@ -211,6 +210,30 @@
     if (widget->widget_class->layout != NULL)
     {
         widget->widget_class->layout(widget);
+    }
+}
+
+int TXT_AlwaysSelectable(TXT_UNCAST_ARG(widget))
+{
+    return 1;
+}
+
+int TXT_NeverSelectable(TXT_UNCAST_ARG(widget))
+{
+    return 0;
+}
+
+int TXT_SelectableWidget(TXT_UNCAST_ARG(widget))
+{
+    TXT_CAST_ARG(txt_widget_t, widget);
+
+    if (widget->widget_class->selectable != NULL)
+    {
+        return widget->widget_class->selectable(widget);
+    }
+    else
+    {
+        return 0;
     }
 }
 
--- a/textscreen/txt_widget.h
+++ b/textscreen/txt_widget.h
@@ -77,9 +77,11 @@
 typedef void (*TxtWidgetSignalFunc)(TXT_UNCAST_ARG(widget), void *user_data);
 typedef void (*TxtMousePressFunc)(TXT_UNCAST_ARG(widget), int x, int y, int b);
 typedef void (*TxtWidgetLayoutFunc)(TXT_UNCAST_ARG(widget));
+typedef int (*TxtWidgetSelectableFunc)(TXT_UNCAST_ARG(widget));
 
 struct txt_widget_class_s
 {
+    TxtWidgetSelectableFunc selectable;
     TxtWidgetSizeCalc size_calc;
     TxtWidgetDrawer drawer;
     TxtWidgetKeyPress key_press;
@@ -92,7 +94,6 @@
 {
     txt_widget_class_t *widget_class;
     txt_callback_table_t *callback_table;
-    int selectable;
     int visible;
     txt_horiz_align_t align;
 
@@ -111,6 +112,8 @@
 void TXT_WidgetMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b);
 void TXT_DestroyWidget(TXT_UNCAST_ARG(widget));
 void TXT_LayoutWidget(TXT_UNCAST_ARG(widget));
+int TXT_AlwaysSelectable(TXT_UNCAST_ARG(widget));
+int TXT_NeverSelectable(TXT_UNCAST_ARG(widget));
 
 /**
  * Set a callback function to be invoked when a signal occurs.
@@ -133,6 +136,15 @@
  */
 
 void TXT_SetWidgetAlign(TXT_UNCAST_ARG(widget), txt_horiz_align_t horiz_align);
+
+/**
+ * Query whether a widget is selectable with the cursor.
+ *
+ * @param widget       The widget.
+ * @return             Non-zero if the widget is selectable.
+ */
+
+int TXT_SelectableWidget(TXT_UNCAST_ARG(widget));
 
 #endif /* #ifndef TXT_WIDGET_H */
 
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -140,7 +140,16 @@
 static void LayoutActionArea(txt_window_t *window)
 {
     txt_widget_t *widget;
+    int space_available;
+    int space_left_offset;
 
+    // We need to calculate the available horizontal space for the center
+    // action widget, so that we can center it within it.
+    // To start with, we have the entire action area available.
+
+    space_available = window->window_w;
+    space_left_offset = 0;
+
     // Left action
 
     if (window->actions[TXT_HORIZ_LEFT] != NULL)
@@ -151,29 +160,43 @@
 
         widget->x = window->window_x + 2;
         widget->y = window->window_y + window->window_h - widget->h - 1;
+
+        // Adjust available space:
+
+        space_available -= widget->w;
+        space_left_offset += widget->w;
     }
 
-    // Draw the center action
+    // Draw the right action
 
-    if (window->actions[TXT_HORIZ_CENTER] != NULL)
+    if (window->actions[TXT_HORIZ_RIGHT] != NULL)
     {
-        widget = (txt_widget_t *) window->actions[TXT_HORIZ_CENTER];
+        widget = (txt_widget_t *) window->actions[TXT_HORIZ_RIGHT];
 
         TXT_CalcWidgetSize(widget);
 
-        widget->x = window->window_x + (window->window_w - widget->w - 2) / 2;
+        widget->x = window->window_x + window->window_w - 2 - widget->w;
         widget->y = window->window_y + window->window_h - widget->h - 1;
+
+        // Adjust available space:
+
+        space_available -= widget->w;
     }
 
-    // Draw the right action
+    // Draw the center action
 
-    if (window->actions[TXT_HORIZ_RIGHT] != NULL)
+    if (window->actions[TXT_HORIZ_CENTER] != NULL)
     {
-        widget = (txt_widget_t *) window->actions[TXT_HORIZ_RIGHT];
+        widget = (txt_widget_t *) window->actions[TXT_HORIZ_CENTER];
 
         TXT_CalcWidgetSize(widget);
 
-        widget->x = window->window_x + window->window_w - 2 - widget->w;
+        // The left and right widgets have left a space sandwiched between
+        // them.  Center this widget within that space.
+
+        widget->x = window->window_x
+                  + space_left_offset
+                  + (space_available - widget->w) / 2;
         widget->y = window->window_y + window->window_h - widget->h - 1;
     }
 }
--- a/textscreen/txt_window_action.c
+++ b/textscreen/txt_window_action.c
@@ -93,6 +93,7 @@
 
 txt_widget_class_t txt_window_action_class =
 {
+    TXT_AlwaysSelectable,
     TXT_WindowActionSizeCalc,
     TXT_WindowActionDrawer,
     TXT_WindowActionKeyPress,