summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Howard2011-02-12 18:42:10 +0000
committerSimon Howard2011-02-12 18:42:10 +0000
commit516a7028994df6718289a7e3db4d07a45c95466b (patch)
treecf974cb4bd38b8e9d0101a41263b42b8c87e96c7
parenta15ba75736d15409876c1f0a44fffc99adf1c192 (diff)
parenta9996b41e954d85fde5ec5188bbf6a7f4df88011 (diff)
downloadchocolate-doom-516a7028994df6718289a7e3db4d07a45c95466b.tar.gz
chocolate-doom-516a7028994df6718289a7e3db4d07a45c95466b.tar.bz2
chocolate-doom-516a7028994df6718289a7e3db4d07a45c95466b.zip
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
-rw-r--r--ChangeLog377
-rw-r--r--Makefile.am1
-rw-r--r--NEWS100
-rw-r--r--NOT-BUGS135
-rw-r--r--README10
-rw-r--r--codeblocks/config.h6
-rw-r--r--codeblocks/game-res.rc10
-rw-r--r--codeblocks/server.cbp4
-rw-r--r--codeblocks/setup-res.rc8
-rw-r--r--configure.in2
-rwxr-xr-xdata/convert-icon7
-rw-r--r--man/chocolate-server.66
-rwxr-xr-xman/docgen29
-rw-r--r--msvc/config.h6
-rw-r--r--msvc/win32.rc10
-rw-r--r--opl/opl_sdl.c4
-rw-r--r--pkg/Makefile.am3
-rw-r--r--pkg/config.make.in1
-rw-r--r--pkg/osx/LauncherManager.h1
-rw-r--r--pkg/osx/LauncherManager.m1
-rw-r--r--pkg/osx/Resources/launcher.nib/classes.nib47
-rw-r--r--pkg/osx/Resources/launcher.nib/designable.nib2679
-rw-r--r--pkg/osx/Resources/launcher.nib/info.nib23
-rw-r--r--pkg/osx/Resources/launcher.nib/keyedobjects.nibbin19616 -> 24936 bytes
-rwxr-xr-xpkg/wince/wince-cabgen40
-rw-r--r--rpm.spec.in1
-rw-r--r--src/Makefile.am7
-rw-r--r--src/d_iwad.c3
-rw-r--r--src/d_mode.h1
-rw-r--r--src/deh_io.c141
-rw-r--r--src/deh_io.h1
-rw-r--r--src/deh_main.c49
-rw-r--r--src/deh_main.h2
-rw-r--r--src/doom/d_main.c191
-rw-r--r--src/doom/d_net.c770
-rw-r--r--src/doom/d_net.h1
-rw-r--r--src/doom/doomstat.h3
-rw-r--r--src/doom/dstrings.h9
-rw-r--r--src/doom/g_game.c33
-rw-r--r--src/doom/m_menu.c10
-rw-r--r--src/doom/p_map.c2
-rw-r--r--src/doom/p_setup.c12
-rw-r--r--src/doom/p_spec.c9
-rw-r--r--src/doom/r_data.c20
-rw-r--r--src/doomfeatures.h3
-rw-r--r--src/i_sdlsound.c41
-rw-r--r--src/i_system.c8
-rw-r--r--src/i_video.c235
-rw-r--r--src/i_video.h1
-rw-r--r--src/m_argv.c15
-rw-r--r--src/m_argv.h4
-rw-r--r--src/m_config.c28
-rw-r--r--src/net_client.c257
-rw-r--r--src/net_client.h6
-rw-r--r--src/net_dedicated.c2
-rw-r--r--src/net_defs.h33
-rw-r--r--src/net_gui.c18
-rw-r--r--src/net_gui.h2
-rw-r--r--src/net_loop.c22
-rw-r--r--src/net_query.c691
-rw-r--r--src/net_query.h15
-rw-r--r--src/net_sdl.c4
-rw-r--r--src/net_server.c128
-rw-r--r--src/net_server.h4
-rw-r--r--src/net_structrw.c22
-rw-r--r--src/net_structrw.h3
-rw-r--r--src/setup/display.c304
-rw-r--r--src/setup/execute.c55
-rw-r--r--src/setup/execute.h2
-rw-r--r--src/setup/joystick.c4
-rw-r--r--src/setup/mainmenu.c31
-rw-r--r--src/setup/multiplayer.c148
-rw-r--r--src/setup/multiplayer.h1
-rw-r--r--src/setup/txt_joybinput.c1
-rw-r--r--src/setup/txt_keyinput.c1
-rw-r--r--src/setup/txt_mouseinput.c1
-rw-r--r--src/strife/d_main.c105
-rw-r--r--src/strife/d_net.c27
-rw-r--r--src/strife/doomstat.h1
-rw-r--r--src/strife/dstrings.h9
-rw-r--r--src/strife/g_game.c19
-rw-r--r--src/strife/m_menu.c8
-rw-r--r--src/strife/p_map.c2
-rw-r--r--src/strife/p_spec.c7
-rw-r--r--src/strife/r_data.c20
-rw-r--r--src/w_main.c12
-rw-r--r--textscreen/examples/guitest.c13
-rw-r--r--textscreen/txt_button.c1
-rw-r--r--textscreen/txt_checkbox.c9
-rw-r--r--textscreen/txt_dropdown.c1
-rw-r--r--textscreen/txt_inputbox.c2
-rw-r--r--textscreen/txt_label.c2
-rw-r--r--textscreen/txt_radiobutton.c9
-rw-r--r--textscreen/txt_scrollpane.c20
-rw-r--r--textscreen/txt_sdl.c37
-rw-r--r--textscreen/txt_separator.c2
-rw-r--r--textscreen/txt_spinctrl.c1
-rw-r--r--textscreen/txt_strut.c2
-rw-r--r--textscreen/txt_table.c51
-rw-r--r--textscreen/txt_widget.c27
-rw-r--r--textscreen/txt_widget.h14
-rw-r--r--textscreen/txt_window.c39
-rw-r--r--textscreen/txt_window_action.c1
103 files changed, 6095 insertions, 1211 deletions
diff --git a/ChangeLog b/ChangeLog
index 0120d5db..9721eb2c 100644
--- 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.
diff --git a/Makefile.am b/Makefile.am
index 97a18507..09fc5868 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,7 @@ EXTRA_DIST= \
README.OPL \
TODO \
BUGS \
+ NOT-BUGS \
rpm.spec
MAINTAINERCLEANFILES = $(AUX_DIST_GEN)
diff --git a/NEWS b/NEWS
index 8fc37b5f..7e89584c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,33 +1,121 @@
-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
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):
diff --git a/NOT-BUGS b/NOT-BUGS
new file mode 100644
index 00000000..f7d22231
--- /dev/null
+++ b/NOT-BUGS
@@ -0,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
+
diff --git a/README b/README
index 0b9eaf7c..06dc1537 100644
--- a/README
+++ b/README
@@ -65,9 +65,13 @@ Here are some examples:
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
diff --git a/codeblocks/config.h b/codeblocks/config.h
index d092bf73..9026d398 100644
--- 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). */
diff --git a/codeblocks/game-res.rc b/codeblocks/game-res.rc
index eef24852..beef1242 100644
--- a/codeblocks/game-res.rc
+++ b/codeblocks/game-res.rc
@@ -1,21 +1,21 @@
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"
{
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"
}
}
}
diff --git a/codeblocks/server.cbp b/codeblocks/server.cbp
index a34a66dd..8c8adfb8 100644
--- 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>
diff --git a/codeblocks/setup-res.rc b/codeblocks/setup-res.rc
index f1602adb..b3812a74 100644
--- a/codeblocks/setup-res.rc
+++ b/codeblocks/setup-res.rc
@@ -3,21 +3,21 @@
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"
{
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"
}
}
}
diff --git a/configure.in b/configure.in
index 4bea1201..4e63d582 100644
--- 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"
diff --git a/data/convert-icon b/data/convert-icon
index c651bc7f..30fade20 100755
--- a/data/convert-icon
+++ b/data/convert-icon
@@ -1,6 +1,4 @@
-#!/usr/bin/python
-#
-# $Id: convert-icon 704 2006-10-18 00:51:11Z fraggle $
+#!/usr/bin/env python
#
# Copyright(C) 2005 Simon Howard
#
@@ -29,7 +27,7 @@ import re
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)
@@ -71,4 +69,3 @@ def convert_image(filename, output_filename):
convert_image(sys.argv[1], sys.argv[2])
-
diff --git a/man/chocolate-server.6 b/man/chocolate-server.6
index 65d0a3fe..f37173f2 100644
--- a/man/chocolate-server.6
+++ b/man/chocolate-server.6
@@ -29,6 +29,12 @@ mean the netgame will simply not function at all.
.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)
diff --git a/man/docgen b/man/docgen
index 73e8c852..3016dc5a 100755
--- a/man/docgen
+++ b/man/docgen
@@ -150,11 +150,8 @@ config_files = {}
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 @@ def add_parameter(param, line, config_file):
# 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 @@ def print_template(template_file, content):
try:
for line in f:
line = line.replace("@content", content)
- print line.rstrip()
+ print(line.rstrip())
finally:
f.close()
@@ -407,7 +404,7 @@ def wiki_output(targets, template):
read_wikipages()
for t in targets:
- print t.wiki_output()
+ print(t.wiki_output())
def plaintext_output(targets, template_file):
@@ -419,13 +416,13 @@ def plaintext_output(targets, template_file):
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
diff --git a/msvc/config.h b/msvc/config.h
index 006d7c74..d91bd23b 100644
--- 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). */
diff --git a/msvc/win32.rc b/msvc/win32.rc
index 94a35372..3371d993 100644
--- a/msvc/win32.rc
+++ b/msvc/win32.rc
@@ -32,21 +32,21 @@
#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"
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
diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c
index f6a3b229..5059fb5e 100644
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -176,8 +176,8 @@ static void FillBuffer(int16_t *buffer, unsigned int nsamples)
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];
}
}
diff --git a/pkg/Makefile.am b/pkg/Makefile.am
index 63da8f53..438a870a 100644
--- a/pkg/Makefile.am
+++ b/pkg/Makefile.am
@@ -5,8 +5,7 @@ osx/Resources/app.icns \
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 \
diff --git a/pkg/config.make.in b/pkg/config.make.in
index dc2d2888..27e44bd8 100644
--- a/pkg/config.make.in
+++ b/pkg/config.make.in
@@ -25,6 +25,7 @@ DOC_FILES = README \
INSTALL \
NEWS \
BUGS \
+ NOT-BUGS \
CMDLINE \
TODO
diff --git a/pkg/osx/LauncherManager.h b/pkg/osx/LauncherManager.h
index e454ab4f..7e8c35cd 100644
--- a/pkg/osx/LauncherManager.h
+++ b/pkg/osx/LauncherManager.h
@@ -35,7 +35,6 @@
id launchButton;
id commandLineArguments;
- id packageLabel;
}
- (void) launch: (id)sender;
diff --git a/pkg/osx/LauncherManager.m b/pkg/osx/LauncherManager.m
index ae91ef4d..8c523ab4 100644
--- a/pkg/osx/LauncherManager.m
+++ b/pkg/osx/LauncherManager.m
@@ -327,7 +327,6 @@ static NSString *AppendQuotedFilename(NSString *str, NSString *fileName)
- (void) awakeFromNib
{
- [self->packageLabel setStringValue: @PACKAGE_STRING];
[self->launcherWindow setTitle: @PACKAGE_NAME " Launcher"];
[self->launcherWindow center];
[self->launcherWindow setDefaultButtonCell: [self->launchButton cell]];
diff --git a/pkg/osx/Resources/launcher.nib/classes.nib b/pkg/osx/Resources/launcher.nib/classes.nib
deleted file mode 100644
index 7efc837a..00000000
--- a/pkg/osx/Resources/launcher.nib/classes.nib
+++ /dev/null
@@ -1,47 +0,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
diff --git a/pkg/osx/Resources/launcher.nib/designable.nib b/pkg/osx/Resources/launcher.nib/designable.nib
new file mode 100644
index 00000000..856ea06f
--- /dev/null
+++ b/pkg/osx/Resources/launcher.nib/designable.nib
@@ -0,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>
diff --git a/pkg/osx/Resources/launcher.nib/info.nib b/pkg/osx/Resources/launcher.nib/info.nib
deleted file mode 100644
index 512ee6dd..00000000
--- a/pkg/osx/Resources/launcher.nib/info.nib
+++ /dev/null
@@ -1,23 +0,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>
diff --git a/pkg/osx/Resources/launcher.nib/keyedobjects.nib b/pkg/osx/Resources/launcher.nib/keyedobjects.nib
index cc763056..93066265 100644
--- a/pkg/osx/Resources/launcher.nib/keyedobjects.nib
+++ b/pkg/osx/Resources/launcher.nib/keyedobjects.nib
Binary files differ
diff --git a/pkg/wince/wince-cabgen b/pkg/wince/wince-cabgen
index 97cba132..76845cf9 100755
--- 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 @@ DIR_VARIABLES = {
}
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 @@ class StringDictionary:
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 @@ class DirectoryList:
# 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 @@ class FileList:
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 @@ class LinkList:
# 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 @@ class CabHeaderFile:
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 @@ class CabFile:
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 @@ def expand_path(filename):
# 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 @@ def print_dependencies(filename):
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":
diff --git a/rpm.spec.in b/rpm.spec.in
index b37a2876..9717ebfb 100644
--- a/rpm.spec.in
+++ b/rpm.spec.in
@@ -57,5 +57,6 @@ rm -rf $RPM_BUILD_ROOT
%doc COPYING
%doc CMDLINE
%doc BUGS
+%doc NOT-BUGS
/usr/games/*
diff --git a/src/Makefile.am b/src/Makefile.am
index 2ba6dbef..0b747f89 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,6 +34,7 @@ net_dedicated.c net_dedicated.h \
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 @@ EXTRA_LIBS = \
@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)
diff --git a/src/d_iwad.c b/src/d_iwad.c
index e7f25234..a6187c10 100644
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -47,6 +47,7 @@ static iwad_t iwads[] =
{ "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 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
// @arg <file>
//
- iwadparm = M_CheckParm("-iwad");
+ iwadparm = M_CheckParmWithArgs("-iwad", 1);
if (iwadparm)
{
diff --git a/src/d_mode.h b/src/d_mode.h
index 8746b354..20cb89e2 100644
--- a/src/d_mode.h
+++ b/src/d_mode.h
@@ -63,6 +63,7 @@ typedef enum
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)
diff --git a/src/deh_io.c b/src/deh_io.c
index 3386a6fa..92c81632 100644
--- 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,22 +94,41 @@ deh_context_t *DEH_OpenFile(char *filename)
{
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 @@ deh_context_t *DEH_OpenFile(char *filename)
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 @@ int DEH_GetChar(deh_context_t *context)
{
++context->linenum;
}
-
+
context->last_was_newline = result == '\n';
-
+
return result;
}
diff --git a/src/deh_io.h b/src/deh_io.h
index 061a5a0e..9d22b360 100644
--- 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);
diff --git a/src/deh_main.c b/src/deh_main.c
index 75934087..39d59e8c 100644
--- 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 @@ static void DEH_ParseContext(deh_context_t *context)
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 @@ static void DEH_ParseContext(deh_context_t *context)
// end of file?
if (line == NULL)
+ {
return;
+ }
while (line[0] != '\0' && isspace(line[0]))
++line;
@@ -347,6 +347,48 @@ int DEH_LoadFile(char *filename)
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)
@@ -387,4 +429,3 @@ void DEH_Init(void)
}
}
-
diff --git a/src/deh_main.h b/src/deh_main.h
index f9cb44ca..9ac2c6c7 100644
--- 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);
diff --git a/src/doom/d_main.c b/src/doom/d_main.c
index f3a4b037..2f116f1e 100644
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -122,8 +122,6 @@ int startmap;
boolean autostart;
int startloadgame;
-FILE* debugfile;
-
boolean advancedemo;
// Store demo, do not accept any inputs
@@ -424,14 +422,6 @@ void D_DoomLoop (void)
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 @@ void D_DoomLoop (void)
// 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 @@ void D_DoAdvanceDemo (void)
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 @@ void D_StartTitle (void)
// 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 @@ static char *banners[] =
" "
"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 @@ static boolean CheckChex(char *iwadname)
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 @@ static struct
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 @@ static void InitGameVersion(void)
// "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 @@ static void InitGameVersion(void)
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 @@ static void D_Endoom(void)
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 @@ void D_DoomMain (void)
}
//!
+ // @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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
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 @@ void D_DoomMain (void)
// Play back the demo named demo.lmp.
//
- p = M_CheckParm ("-playdemo");
+ p = M_CheckParmWithArgs ("-playdemo", 1);
if (!p)
{
@@ -1319,11 +1364,11 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
D_IdentifyVersion();
InitGameVersion();
LoadChexDeh();
+ LoadHacxDeh();
D_SetGameDescription();
SetSaveGameDir(iwadfile);
@@ -1404,9 +1450,9 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
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 @@ void D_DoomMain (void)
// (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 @@ void D_DoomMain (void)
// 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,24 +1631,24 @@ void D_DoomMain (void)
// 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);
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
diff --git a/src/doom/d_net.c b/src/doom/d_net.c
index 464922d4..dd1ec563 100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -25,7 +25,7 @@
//
//-----------------------------------------------------------------------------
-
+#include <stdlib.h>
#include "doomfeatures.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,11 +93,7 @@ fixed_t offsetms;
// Use new client syncronisation code
-boolean net_cl_new_sync = true;
-
-// Connected but not participating in the game (observer)
-
-boolean drone = false;
+boolean new_sync = true;
// 35 fps clock adjusted by offsetms milliseconds
@@ -99,7 +103,7 @@ static int GetAdjustedTime(void)
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 @@ void NetUpdate (void)
continue;
}
- if (net_cl_new_sync)
+ if (new_sync)
{
// If playing single player, do not allow tics to buffer
// up very far
@@ -200,20 +204,95 @@ void NetUpdate (void)
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
//
@@ -225,169 +304,340 @@ void D_StartGameLoop(void)
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;
+
+ 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");
+ }
-void D_CheckNetGame (void)
+ 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
+
+ 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;
- I_AtExit(D_QuitNetGame, true);
+ //!
+ // @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;
}
+}
+
+boolean D_InitNetGame(net_connect_data_t *connect_data,
+ net_gamesettings_t *settings)
+{
+ net_addr_t *addr = NULL;
+ int i;
- playeringame[0] = true;
#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 (i > 0)
- {
- net_sdl_module.InitClient();
- addr = net_sdl_module.ResolveAddress(myargv[i+1]);
+ if (addr != NULL)
+ {
+ if (M_CheckParm("-drone") > 0)
+ {
+ connect_data->drone = true;
+ }
- if (addr == NULL)
- {
- I_Error("Unable to resolve '%s'\n", myargv[i+1]);
- }
- }
+ //!
+ // @category net
+ //
+ // Run as the left screen in three screen mode.
+ //
+
+ 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;
+}
+
+//
+// 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;
- for (i=0; i<MAXPLAYERS; ++i)
+ 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_CheckNetGame (void)
//
void D_QuitNetGame (void)
{
- if (debugfile)
- fclose (debugfile);
-
#ifdef FEATURE_MULTIPLAYER
NET_SV_Shutdown();
@@ -411,62 +658,172 @@ void D_QuitNetGame (void)
}
-// 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;
+
+ // If we are connected to a server, check if there are any players
+ // in the game.
-#ifdef FEATURE_MULTIPLAYER
if (net_client_connected)
{
- int i;
-
- lowtic = INT_MAX;
-
- for (i=0; i<MAXPLAYERS; ++i)
+ for (i = 0; i < MAXPLAYERS; ++i)
{
- if (playeringame[i])
- {
- if (nettics[i] < lowtic)
- lowtic = nettics[i];
- }
+ result = result || playeringame[i];
}
}
- else
-#endif
+
+ // Whether single or multi-player, unless we are running as a drone,
+ // we are in the game.
+
+ if (!drone)
{
- lowtic = maketic;
+ result = true;
}
- return lowtic;
+ 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])
+ {
+ D_PlayerQuitGame(&players[i]);
+ }
+ }
+
+ 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)
+ {
+ if (i != consoleplayer)
+ {
+ set->ingame[i] = false;
+ }
+ }
}
//
// TryRunTics
//
-int oldnettics;
-int frametics[4];
-int frameon;
-int frameskip[4];
-int oldnettics;
-
-extern boolean advancedemo;
void TryRunTics (void)
{
@@ -492,7 +849,7 @@ void TryRunTics (void)
// decide how many tics to run
- if (net_cl_new_sync)
+ if (new_sync)
{
counts = availabletics;
}
@@ -509,52 +866,9 @@ void TryRunTics (void)
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 @@ void TryRunTics (void)
// 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
}
}
diff --git a/src/doom/d_net.h b/src/doom/d_net.h
index f801d216..a3c1a43c 100644
--- a/src/doom/d_net.h
+++ b/src/doom/d_net.h
@@ -45,7 +45,6 @@ void TryRunTics (void);
// Called at start of game loop to initialize timers
void D_StartGameLoop(void);
-extern boolean drone;
extern boolean net_cl_new_sync;
#endif
diff --git a/src/doom/doomstat.h b/src/doom/doomstat.h
index 40147833..237234d8 100644
--- a/src/doom/doomstat.h
+++ b/src/doom/doomstat.h
@@ -255,7 +255,6 @@ extern int maxammo[NUMAMMO];
// 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 rndindex;
extern int maketic;
extern int nettics[MAXPLAYERS];
-extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
+extern ticcmd_t *netcmds;
extern int ticdup;
diff --git a/src/doom/dstrings.h b/src/doom/dstrings.h
index bdc6b2ce..d47fc1af 100644
--- 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
diff --git a/src/doom/g_game.c b/src/doom/g_game.c
index ad7155a0..5662d3f2 100644
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -138,7 +138,7 @@ int gametic;
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 @@ void G_Ticker (void)
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 @@ void G_WriteDemoTiccmd (ticcmd_t* cmd)
//
// 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 @@ void G_RecordDemo (char* name)
// 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 @@ void G_DoPlayDemo (void)
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
diff --git a/src/doom/m_menu.c b/src/doom/m_menu.c
index a6f7bbfb..478e7f66 100644
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -762,6 +762,8 @@ void M_DrawReadThis1(void)
switch (gameversion)
{
case exe_doom_1_9:
+ case exe_hacx:
+
if (gamemode == commercial)
{
// Doom 2
@@ -1440,23 +1442,23 @@ boolean M_Responder (event_t* ev)
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;
diff --git a/src/doom/p_map.c b/src/doom/p_map.c
index 925e4398..78102bdf 100644
--- a/src/doom/p_map.c
+++ b/src/doom/p_map.c
@@ -1426,7 +1426,7 @@ static void SpechitOverrun(line_t *ld)
// Use the specified magic value when emulating spechit overruns.
//
- p = M_CheckParm("-spechit");
+ p = M_CheckParmWithArgs("-spechit", 1);
if (p > 0)
{
diff --git a/src/doom/p_setup.c b/src/doom/p_setup.c
index 7d9d4318..3fc95cab 100644
--- a/src/doom/p_setup.c
+++ b/src/doom/p_setup.c
@@ -755,17 +755,7 @@ P_SetupLevel
// 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 ();
diff --git a/src/doom/p_spec.c b/src/doom/p_spec.c
index 37beb850..90d0bb7c 100644
--- a/src/doom/p_spec.c
+++ b/src/doom/p_spec.c
@@ -1213,9 +1213,9 @@ static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic,
// 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 @@ void P_SpawnSpecials (void)
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 @@ void P_SpawnSpecials (void)
{
levelTimer = false;
}
-
+
// Init special SECTORs.
sector = sectors;
for (i=0 ; i<numsectors ; i++, sector++)
diff --git a/src/doom/r_data.c b/src/doom/r_data.c
index b999e916..505f1ff7 100644
--- a/src/doom/r_data.c
+++ b/src/doom/r_data.c
@@ -413,6 +413,7 @@ R_GetColumn
static void GenerateTextureHashTable(void)
{
+ texture_t **rover;
int i;
int key;
@@ -429,12 +430,25 @@ static void GenerateTextureHashTable(void)
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];
}
}
diff --git a/src/doomfeatures.h b/src/doomfeatures.h
index c5e6067a..1c994b70 100644
--- 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
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index 7deb683d..1cfafa6f 100644
--- 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 @@ static void I_SDL_UpdateSoundParams(int handle, int vol, int sep)
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 @@ static boolean I_SDL_InitSound(boolean _use_sfx_prefix)
}
#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;
diff --git a/src/i_system.c b/src/i_system.c
index cd8ddcee..0a856ca9 100644
--- a/src/i_system.c
+++ b/src/i_system.c
@@ -189,7 +189,7 @@ byte *I_ZoneBase (int *size)
// Specify the heap size, in MiB (default 16).
//
- p = M_CheckParm("-mb");
+ p = M_CheckParmWithArgs("-mb", 1);
if (p > 0)
{
@@ -327,9 +327,9 @@ void I_Error (char *error, ...)
// 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 @@ void I_Error (char *error, ...)
msgbuf, strlen(msgbuf) + 1,
wmsgbuf, sizeof(wmsgbuf));
- MessageBoxW(NULL, wmsgbuf, L"Error", MB_OK);
+ MessageBoxW(NULL, wmsgbuf, L"", MB_OK);
}
#endif
diff --git a/src/i_video.c b/src/i_video.c
index 8c2cfeac..6bf2f179 100644
--- 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 SDL_Surface *screen;
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 boolean native_surface;
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 @@ static boolean BlitArea(int x1, int y1, int x2, int y2)
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 @@ void I_FinishUpdate (void)
// 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,15 +1382,61 @@ static void AutoAdjustWindowed(void)
}
}
+// 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 @@ static void I_AutoAdjustSettings(void)
// 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 @@ static void CheckCommandLine(void)
// Specify the screen width, in pixels.
//
- i = M_CheckParm("-width");
+ i = M_CheckParmWithArgs("-width", 1);
if (i > 0)
{
@@ -1558,7 +1639,7 @@ static void CheckCommandLine(void)
// Specify the screen height, in pixels.
//
- i = M_CheckParm("-height");
+ i = M_CheckParmWithArgs("-height", 1);
if (i > 0)
{
@@ -1567,12 +1648,39 @@ static void CheckCommandLine(void)
//!
// @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 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
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,7 +1868,12 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
// 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)
{
@@ -1760,18 +1881,30 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
}
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 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
}
}
+ // 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 @@ void I_InitGraphics(void)
// Start with a clear black screen
// (screen will be flipped after we set the palette)
- if (SDL_LockSurface(screen) >= 0)
- {
- byte *screenpixels;
- int y;
-
- screenpixels = (byte *) screen->pixels;
-
- for (y=0; y<screen->h; ++y)
- memset(screenpixels + screen->pitch * y, 0, screen->w);
-
- SDL_UnlockSurface(screen);
- }
+ SDL_FillRect(screenbuffer, NULL, 0);
// 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 @@ void I_InitGraphics(void)
// 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 @@ void I_BindVideoVariables(void)
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 @@ void I_BindVideoVariables(void)
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
}
diff --git a/src/i_video.h b/src/i_video.h
index 82368967..c3c50c4b 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -116,6 +116,7 @@ void I_EnableLoadingDisk(void);
extern char *video_driver;
extern boolean screenvisible;
+
extern float mouse_acceleration;
extern int mouse_threshold;
extern int vanilla_keyboard_mapping;
diff --git a/src/m_argv.c b/src/m_argv.c
index 79702e56..59381b65 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -47,13 +47,13 @@ char** myargv;
// 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 @@ boolean M_ParmExists(char *check)
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 @@ static void LoadResponseFile(int argv_index)
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)
{
diff --git a/src/m_argv.h b/src/m_argv.h
index 859f93e4..2fb76a2a 100644
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -40,6 +40,10 @@ extern char** myargv;
// 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?
diff --git a/src/m_config.c b/src/m_config.c
index 8332750b..bda9828c 100644
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -645,6 +645,12 @@ static default_t extra_defaults_list[] =
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,8 +1294,18 @@ static void SaveDefaultCollection(default_collection_t *collection)
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 @@ void M_LoadDefaults (void)
// 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 @@ void M_LoadDefaults (void)
// 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",
diff --git a/src/net_client.c b/src/net_client.c
index a1697944..ffc9b49b 100644
--- 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_clientstate_t client_state;
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 @@ boolean net_waiting_for_start = false;
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 @@ static fixed_t average_latency;
#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 @@ static void NET_CL_Disconnected(void)
// 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,38 +271,25 @@ static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq)
for (i=0; i<MAXPLAYERS; ++i)
{
- if (i == consoleplayer && !drone)
+ if (i == settings.consoleplayer && !drone)
{
continue;
}
- if (playeringame[i] && !cmd->playeringame[i])
- {
- NET_CL_PlayerQuitGame(&players[i]);
- }
-
- playeringame[i] = cmd->playeringame[i];
-
- if (playeringame[i])
+ if (cmd->playeringame[i])
{
net_ticdiff_t *diff;
- ticcmd_t ticcmd;
diff = &cmd->cmds[i];
// Use the ticcmd diff to patch the previous ticcmd to
// the new ticcmd
- NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmd);
-
- // Save in d_net.c structures
-
- netcmds[i][nettics[i] % BACKUPTICS] = ticcmd;
- ++nettics[i];
+ NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmds[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_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq)
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,66 +334,9 @@ static void NET_CL_Shutdown(void)
}
}
-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
@@ -445,7 +347,7 @@ void NET_CL_StartGame(void)
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 @@ static void NET_CL_SendGameDataACK(void)
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 @@ static void NET_CL_SendTics(int start, int end)
// 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 @@ static void NET_CL_SendTics(int start, int end)
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 @@ void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic)
// Send to server.
- starttic = maketic - extratics;
+ starttic = maketic - settings.extratics;
endtic = maketic;
if (starttic < 0)
@@ -632,14 +534,7 @@ static void NET_CL_ParseWaitingData(net_packet_t *packet)
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 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
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 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
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 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
// 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 @@ static void NET_CL_ParseGameData(net_packet_t *packet)
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 @@ void NET_CL_Run(void)
}
}
-static void NET_CL_SendSYN(void)
+static void NET_CL_SendSYN(net_connect_data_t *data)
{
net_packet_t *packet;
@@ -1114,10 +966,7 @@ static void NET_CL_SendSYN(void)
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,20 +977,13 @@ static void NET_CL_SendSYN(void)
// 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;
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 @@ boolean NET_CL_Connect(net_addr_t *addr)
// necessary module
client_context = NET_NewContext();
-
+
// initialize module for client mode
if (!addr->module->InitClient())
@@ -1185,7 +1027,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
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 @@ boolean NET_CL_Connect(net_addr_t *addr)
// run client code
NET_CL_Run();
-
+
// run the server, just incase we are doing a loopback
// connect
@@ -1215,6 +1057,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
// connected ok!
client_state = CLIENT_STATE_WAITING_START;
+ drone = data->drone;
return true;
}
@@ -1223,9 +1066,23 @@ boolean NET_CL_Connect(net_addr_t *addr)
// 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
diff --git a/src/net_client.h b/src/net_client.h
index 078a19a2..b071d32a 100644
--- 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_wad_md5sum;
extern md5_digest_t net_local_deh_md5sum;
extern unsigned int net_local_is_freedoom;
+extern boolean drone;
#endif /* #ifndef NET_CLIENT_H */
diff --git a/src/net_dedicated.c b/src/net_dedicated.c
index b47a7924..d8e50731 100644
--- a/src/net_dedicated.c
+++ b/src/net_dedicated.c
@@ -74,8 +74,8 @@ void NET_DedicatedServer(void)
CheckForClientOptions();
NET_SV_Init();
-
NET_SV_AddModule(&net_sdl_module);
+ NET_SV_RegisterWithMaster();
while (true)
{
diff --git a/src/net_defs.h b/src/net_defs.h
index a5fddc4d..1fa9a9af 100644
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -128,6 +128,30 @@ typedef enum
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 @@ typedef struct
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)
diff --git a/src/net_gui.c b/src/net_gui.c
index 8c848d1c..f2c4f1e5 100644
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@ -53,9 +53,11 @@ static void EscapePressed(TXT_UNCAST_ARG(widget), void *unused)
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 @@ static void BuildGUI(void)
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 @@ static void UpdateGUI(void)
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 @@ static void CheckMD5Sums(void)
had_warning = true;
}
-void NET_WaitForStart(void)
+void NET_WaitForStart(net_gamesettings_t *settings)
{
if (!TXT_Init())
{
@@ -268,13 +270,13 @@ void NET_WaitForStart(void)
}
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 @@ void NET_WaitForStart(void)
if (!net_client_connected)
{
- I_Error("Disconnected from server");
+ I_Error("Lost connection to server");
}
TXT_Sleep(100);
diff --git a/src/net_gui.h b/src/net_gui.h
index fdcc81be..9d40b0d0 100644
--- 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 */
diff --git a/src/net_loop.c b/src/net_loop.c
index 9f371bcb..acdc2cb6 100644
--- a/src/net_loop.c
+++ b/src/net_loop.c
@@ -137,9 +137,16 @@ static void NET_CL_FreeAddress(net_addr_t *addr)
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 void NET_SV_FreeAddress(net_addr_t *addr)
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 =
diff --git a/src/net_query.c b/src/net_query.c
index b50b4292..ae56dea6 100644
--- 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;
+
+static boolean query_loop_running = false;
+static boolean printed_header = false;
+
+// Resolve the master server address.
+
+net_addr_t *NET_Query_ResolveMaster(net_context_t *context)
+{
+ net_addr_t *addr;
+
+ addr = NET_ResolveAddress(context, MASTER_SERVER_ADDRESS);
+
+ if (addr == NULL)
+ {
+ fprintf(stderr, "Warning: Failed to resolve address "
+ "for master server: %s\n", MASTER_SERVER_ADDRESS);
+ }
+
+ return addr;
+}
-// Add a new address to the list of hosts that has responded
+// Send a registration packet to the master server to register
+// ourselves with the global list.
-static queryresponse_t *AddResponder(net_addr_t *addr,
- net_querydata_t *data)
+void NET_Query_AddToMaster(net_addr_t *master_addr)
{
- queryresponse_t *response;
+ 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.
- responders = realloc(responders,
- sizeof(queryresponse_t) * (num_responses + 1));
+ printf("Failed to register with master server at %s\n",
+ MASTER_SERVER_ADDRESS);
+ }
+ }
+}
- response = &responders[num_responses];
- response->addr = addr;
- response->data = *data;
- ++num_responses;
+// Send a query to the master server.
+
+static void NET_Query_SendMasterQuery(net_addr_t *addr)
+{
+ net_packet_t *packet;
- return response;
+ packet = NET_NewPacket(10);
+ NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_QUERY);
+ NET_SendPacket(addr, packet);
+ NET_FreePacket(packet);
}
-// Returns true if the reply is from a host that has not previously
-// responded.
+// Given the specified address, find the target associated. If no
+// target is found, and 'create' is true, a new target is created.
-static boolean CheckResponder(net_addr_t *addr)
+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 @@ static void NET_Query_SendQuery(net_addr_t *addr)
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;
+
+ // Read the header
- while (i < wide)
+ 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;
+
+ // This might be the master server responding.
- // Have we already received a packet from this host?
+ target = GetTargetForAddr(addr, false);
- if (!CheckResponder(addr))
+ 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;
+}
+
+// Time out servers that have been queried and not responded.
- PrintResponse(response);
+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;
+}
+
+// Loop waiting for responses.
+// The specified callback is invoked when a new server responds.
- last_send_time = -1;
- start_time = I_GetTimeMS();
+static void NET_Query_QueryLoop(net_query_callback_t callback,
+ void *user_data)
+{
+ query_loop_running = true;
- while (I_GetTimeMS() < start_time + 5000)
+ 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 @@ void NET_Query_Init(void)
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.
+
+static query_target_t *FindFirstResponder(void)
+{
+ unsigned int i;
- if (net_addr == NULL)
+ 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();
+
+ // 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;
+ }
+}
+
+int NET_LANQuery(net_query_callback_t callback, void *user_data)
+{
+ query_target_t *target;
+
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;
+
+ NET_Query_QueryLoop(callback, user_data);
+
+ return GetNumResponses();
}
-void NET_LANQuery(void)
+int NET_MasterQuery(net_query_callback_t callback, void *user_data)
{
+ net_addr_t *master;
+ query_target_t *target;
+
NET_Query_Init();
- printf("\nSearching for servers on local LAN ...\n\n");
+ // Resolve master address and add to targets list.
+
+ master = NET_Query_ResolveMaster(query_context);
+
+ if (master == NULL)
+ {
+ return 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;
- if (!NET_Query_QueryLoop(NULL, false))
+ va_start(args, s);
+ i = vprintf(s, args);
+ va_end(args);
+
+ while (i < wide)
{
- I_Error("No servers found");
+ 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) ");
}
- exit(0);
+ NET_SafePuts(data->description);
}
diff --git a/src/net_query.h b/src/net_query.h
index f682d320..01e059cb 100644
--- 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 */
diff --git a/src/net_sdl.c b/src/net_sdl.c
index aa7fbd6e..6a4f24dc 100644
--- a/src/net_sdl.c
+++ b/src/net_sdl.c
@@ -170,7 +170,7 @@ static boolean NET_SDL_InitClient(void)
// the default (2342).
//
- p = M_CheckParm("-port");
+ p = M_CheckParmWithArgs("-port", 1);
if (p > 0)
port = atoi(myargv[p+1]);
@@ -196,7 +196,7 @@ static boolean NET_SDL_InitServer(void)
{
int p;
- p = M_CheckParm("-port");
+ p = M_CheckParmWithArgs("-port", 1);
if (p > 0)
port = atoi(myargv[p+1]);
diff --git a/src/net_server.c b/src/net_server.c
index 904d932e..ae46be4a 100644
--- 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 @@ typedef struct
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_gamemode;
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 void NET_SV_AdvanceWindow(void)
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 @@ static void NET_SV_InitNewClient(net_client_t *client,
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 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
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 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
// 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 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
return;
}
- if (!D_ValidGameMode(cl_gamemission, cl_gamemode))
+ if (!D_ValidGameMode(data.gamemission, data.gamemode))
{
return;
}
@@ -579,7 +600,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
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 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
// 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 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
// 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 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
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 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
}
}
+ settings.num_players = NET_SV_NumPlayers();
+
nowtime = I_GetTimeMS();
// Send start packets to each connected node
@@ -695,8 +718,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
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 @@ void NET_SV_SendQueryResponse(net_addr_t *addr)
{
net_packet_t *reply;
net_querydata_t querydata;
+ int p;
// Version
@@ -1089,9 +1113,22 @@ void NET_SV_SendQueryResponse(net_addr_t *addr)
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.
+ //
+
+ p = M_CheckParmWithArgs("-servername", 1);
- querydata.description = "Chocolate Doom server";
+ if (p > 0)
+ {
+ querydata.description = myargv[p + 1];
+ }
+ else
+ {
+ querydata.description = "Unnamed server";
+ }
// Send it and we're done.
@@ -1109,6 +1146,14 @@ static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
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 @@ void NET_SV_Init(void)
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,12 +1599,21 @@ void NET_SV_Run(void)
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
// to received packets
diff --git a/src/net_server.h b/src/net_server.h
index 93b22fc3..1debbd79 100644
--- a/src/net_server.h
+++ b/src/net_server.h
@@ -41,5 +41,9 @@ void NET_SV_Shutdown(void);
void NET_SV_AddModule(net_module_t *module);
+// Register server with master server.
+
+void NET_SV_RegisterWithMaster(void);
+
#endif /* #ifndef NET_SERVER_H */
diff --git a/src/net_structrw.c b/src/net_structrw.c
index 01933ebb..7380e334 100644
--- 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 @@ void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
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 @@ boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
&& 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)
diff --git a/src/net_structrw.h b/src/net_structrw.h
index 13209778..68971cd3 100644
--- 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);
diff --git a/src/setup/display.c b/src/setup/display.c
index 9fd0963b..f5f190f2 100644
--- a/src/setup/display.c
+++ b/src/setup/display.c
@@ -26,12 +26,40 @@
#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"
#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 @@ static screen_mode_t screen_modes_scaled[] =
// 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 aspect_ratio_correct = 1;
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 usegamma = 0;
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 @@ void SetDisplayDriver(void)
}
}
+// 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 int GoodFullscreenMode(screen_mode_t *mode)
static void BuildFullscreenModesList(void)
{
+ SDL_PixelFormat format;
SDL_Rect **modes;
screen_mode_t *m1;
screen_mode_t *m2;
@@ -194,7 +376,10 @@ static void BuildFullscreenModesList(void)
// 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)
{
@@ -229,6 +414,8 @@ static void BuildFullscreenModesList(void)
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 @@ static void GenerateModesTable(TXT_UNCAST_ARG(widget),
// 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,8 +504,25 @@ static void GenerateModesTable(TXT_UNCAST_ARG(widget),
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)
@@ -331,18 +535,6 @@ static char *win32_video_drivers[] =
"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 @@ static void UpdateVideoDriver(TXT_UNCAST_ARG(widget),
RestartTextscreen();
+ // Rebuild the list of supported pixel depths.
+
+ IdentifyPixelDepths();
+ SetSelectedBPP();
+
// Rebuild the video modes list
BuildFullscreenModesList();
@@ -385,8 +582,18 @@ void ConfigDisplay(void)
{
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}
@@ -400,16 +607,43 @@ void ConfigDisplay(void)
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",
&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 @@ void ConfigDisplay(void)
TXT_AddWidgets(window,
TXT_NewSeparator("Screen mode"),
+ bpp_table = TXT_NewTable(2),
modes_table,
TXT_NewSeparator("Misc."),
NULL);
@@ -458,6 +693,15 @@ void ConfigDisplay(void)
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 @@ void BindDisplayVariables(void)
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
}
diff --git a/src/setup/execute.c b/src/setup/execute.c
index 4be44149..f85b8af4 100644
--- a/src/setup/execute.c
+++ b/src/setup/execute.c
@@ -88,6 +88,42 @@ static char *TempFile(char *s)
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;
@@ -106,25 +142,6 @@ execute_context_t *NewExecuteContext(void)
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, ...)
{
va_list args;
diff --git a/src/setup/execute.h b/src/setup/execute.h
index 24711a16..25f1f10a 100644
--- a/src/setup/execute.h
+++ b/src/setup/execute.h
@@ -35,7 +35,7 @@ typedef struct execute_context_s execute_context_t;
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);
diff --git a/src/setup/joystick.c b/src/setup/joystick.c
index fbe3a3f3..0fc00ea1 100644
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -65,8 +65,8 @@ static int joystick_y_invert = 0;
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
};
//
diff --git a/src/setup/mainmenu.c b/src/setup/mainmenu.c
index c3cb7db5..55496010 100644
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -156,7 +156,7 @@ static void LaunchDoom(void *unused1, void *unused2)
// Launch Doom
exec = NewExecuteContext();
- AddConfigParameters(exec);
+ PassThroughArguments(exec);
ExecuteDoom(exec);
exit(0);
@@ -189,6 +189,7 @@ void MainMenu(void)
{
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 @@ void MainMenu(void)
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 @@ static void SetIcon(void)
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 @@ static void RunGUI(void)
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();
}
diff --git a/src/setup/multiplayer.c b/src/setup/multiplayer.c
index 24cd0670..aed89212 100644
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@ -70,10 +70,10 @@ static char *iwadfile;
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 fast = 0;
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 AddIWADParameter(execute_context_t *exec)
}
}
-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 @@ static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
AddExtraParameters(exec);
AddIWADParameter(exec);
- AddCmdLineParameter(exec, "-server");
AddCmdLineParameter(exec, "-skill %i", skill + 1);
if (gamemission == hexen)
@@ -244,20 +248,6 @@ static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
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,20 +258,55 @@ static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
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);
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,12 +569,27 @@ static txt_widget_t *IWADSelector(void)
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 @@ static txt_window_action_t *WadWindowAction(void)
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 @@ void StartMultiGame(void)
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 @@ void StartMultiGame(void)
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 @@ void StartMultiGame(void)
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 @@ void StartMultiGame(void)
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_AddWidget(window,
+ TXT_NewInvertedCheckBox("Register with master server",
+ &privateserver));
+
+ TXT_AddWidgets(advanced_table,
+ TXT_NewLabel("UDP port"),
+ TXT_NewIntInputBox(&udpport, 5),
+ NULL);
+ }
- TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
+ TXT_AddWidget(window,
+ TXT_NewButton2("Add extra parameters...",
+ OpenExtraParamsWindow, NULL));
- TXT_AddWidgets(advanced_table,
- TXT_NewLabel("UDP port"),
- TXT_NewIntInputBox(&udpport, 5),
- 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 @@ static void DoJoinGame(void *unused1, void *unused2)
M_SaveDefaults();
- AddConfigParameters(exec);
+ PassThroughArguments(exec);
ExecuteDoom(exec);
diff --git a/src/setup/multiplayer.h b/src/setup/multiplayer.h
index 7490bc3c..afc8a2a8 100644
--- 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);
diff --git a/src/setup/txt_joybinput.c b/src/setup/txt_joybinput.c
index 1e132962..cde3d2c2 100644
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -206,6 +206,7 @@ static void TXT_JoystickInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, in
txt_widget_class_t txt_joystick_input_class =
{
+ TXT_AlwaysSelectable,
TXT_JoystickInputSizeCalc,
TXT_JoystickInputDrawer,
TXT_JoystickInputKeyPress,
diff --git a/src/setup/txt_keyinput.c b/src/setup/txt_keyinput.c
index 483c325f..08eb9d8c 100644
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -171,6 +171,7 @@ static void TXT_KeyInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
txt_widget_class_t txt_key_input_class =
{
+ TXT_AlwaysSelectable,
TXT_KeyInputSizeCalc,
TXT_KeyInputDrawer,
TXT_KeyInputKeyPress,
diff --git a/src/setup/txt_mouseinput.c b/src/setup/txt_mouseinput.c
index 8b87e651..4f454c8c 100644
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -164,6 +164,7 @@ static void TXT_MouseInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b
txt_widget_class_t txt_mouse_input_class =
{
+ TXT_AlwaysSelectable,
TXT_MouseInputSizeCalc,
TXT_MouseInputDrawer,
TXT_MouseInputKeyPress,
diff --git a/src/strife/d_main.c b/src/strife/d_main.c
index 4260ca7d..95b1b643 100644
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -126,8 +126,6 @@ int startmap;
boolean autostart;
int startloadgame;
-FILE* debugfile;
-
boolean advancedemo;
// villsa [STRIFE] workparm variable (similar to devparm?)
@@ -475,14 +473,6 @@ void D_DoomLoop (void)
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 @@ void D_QuitGame(void)
// 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 @@ static void InitGameVersion(void)
// "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 @@ void D_DoomMain (void)
}
//!
+ // @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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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,23 +1626,9 @@ void D_DoomMain (void)
// 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);
-
- D_AddFile (file);
- }
+ // Debug:
+// W_PrintDirectory();
//!
// @arg <demo>
@@ -1641,7 +1638,7 @@ void D_DoomMain (void)
// Play back the demo named demo.lmp.
//
- p = M_CheckParm ("-playdemo");
+ p = M_CheckParmWithArgs ("-playdemo", 1);
if (!p)
{
@@ -1653,11 +1650,11 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
// 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 @@ void D_DoomMain (void)
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 @@ void D_DoomMain (void)
// (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 @@ void D_DoomMain (void)
// 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,17 +1951,17 @@ void D_DoomMain (void)
// 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;
}
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 @@ void D_DoomMain (void)
}
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
diff --git a/src/strife/d_net.c b/src/strife/d_net.c
index 3480b0ad..7e1644e8 100644
--- 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 @@ void D_CheckNetGame (void)
if (i > 0)
{
addr = NET_FindLANServer();
+ NET_SV_RegisterWithMaster();
if (addr == NULL)
{
@@ -307,7 +307,7 @@ void D_CheckNetGame (void)
// address.
//
- i = M_CheckParm("-connect");
+ i = M_CheckParmWithArgs("-connect", 1);
if (i > 0)
{
@@ -382,12 +382,22 @@ void D_CheckNetGame (void)
// 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_CheckNetGame (void)
//
void D_QuitNetGame (void)
{
- if (debugfile)
- fclose (debugfile);
-
#ifdef FEATURE_MULTIPLAYER
NET_SV_Shutdown();
diff --git a/src/strife/doomstat.h b/src/strife/doomstat.h
index ae513f78..8cb562b2 100644
--- a/src/strife/doomstat.h
+++ b/src/strife/doomstat.h
@@ -264,7 +264,6 @@ extern int maxammo[NUMAMMO];
// File handling stuff.
extern char * savegamedir;
extern char basedefault[1024];
-extern FILE* debugfile;
// if true, load all graphics at level load
extern boolean precache;
diff --git a/src/strife/dstrings.h b/src/strife/dstrings.h
index bdc6b2ce..d47fc1af 100644
--- 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
diff --git a/src/strife/g_game.c b/src/strife/g_game.c
index b8496a5b..c9d49c18 100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -138,7 +138,7 @@ int gametic;
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 @@ void G_RecordDemo (char* name)
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 @@ void G_RecordDemo (char* name)
// 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 @@ void G_DoPlayDemo (void)
// 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
diff --git a/src/strife/m_menu.c b/src/strife/m_menu.c
index 92564e96..4d16e3e8 100644
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@ -1679,23 +1679,23 @@ boolean M_Responder (event_t* ev)
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;
diff --git a/src/strife/p_map.c b/src/strife/p_map.c
index 6a1d526d..c557f968 100644
--- a/src/strife/p_map.c
+++ b/src/strife/p_map.c
@@ -1628,7 +1628,7 @@ static void SpechitOverrun(line_t *ld)
// Use the specified magic value when emulating spechit overruns.
//
- p = M_CheckParm("-spechit");
+ p = M_CheckParmWithArgs("-spechit", 1);
if (p > 0)
{
diff --git a/src/strife/p_spec.c b/src/strife/p_spec.c
index af070d55..e26d9144 100644
--- a/src/strife/p_spec.c
+++ b/src/strife/p_spec.c
@@ -1713,9 +1713,9 @@ static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic,
// 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 @@ void P_SpawnSpecials (void)
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;
diff --git a/src/strife/r_data.c b/src/strife/r_data.c
index 043df348..46138c85 100644
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@ -409,6 +409,7 @@ R_GetColumn
static void GenerateTextureHashTable(void)
{
+ texture_t **rover;
int i;
int key;
@@ -425,12 +426,25 @@ static void GenerateTextureHashTable(void)
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];
}
}
diff --git a/src/w_main.c b/src/w_main.c
index 92a394dd..ed285498 100644
--- a/src/w_main.c
+++ b/src/w_main.c
@@ -53,7 +53,7 @@ boolean W_ParseCommandLine(void)
// 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 @@ boolean W_ParseCommandLine(void)
// 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 @@ boolean W_ParseCommandLine(void)
// 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 @@ boolean W_ParseCommandLine(void)
// 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 @@ boolean W_ParseCommandLine(void)
// Equivalent to "-af <files> -as <files>".
//
- p = M_CheckParm("-aa");
+ p = M_CheckParmWithArgs("-aa", 1);
if (p > 0)
{
@@ -182,7 +182,7 @@ boolean W_ParseCommandLine(void)
// Load the specified PWAD files.
//
- p = M_CheckParm ("-file");
+ p = M_CheckParmWithArgs ("-file", 1);
if (p)
{
// the parms after p are wadfile/lump names,
diff --git a/textscreen/examples/guitest.c b/textscreen/examples/guitest.c
index 5a931949..df79be2d 100644
--- a/textscreen/examples/guitest.c
+++ b/textscreen/examples/guitest.c
@@ -163,8 +163,8 @@ void Window2(void)
{
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 @@ void Window2(void)
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);
diff --git a/textscreen/txt_button.c b/textscreen/txt_button.c
index a7a2d25a..85517b3d 100644
--- a/textscreen/txt_button.c
+++ b/textscreen/txt_button.c
@@ -96,6 +96,7 @@ static void TXT_ButtonMousePress(TXT_UNCAST_ARG(button), int x, int y, int b)
txt_widget_class_t txt_button_class =
{
+ TXT_AlwaysSelectable,
TXT_ButtonSizeCalc,
TXT_ButtonDrawer,
TXT_ButtonKeyPress,
diff --git a/textscreen/txt_checkbox.c b/textscreen/txt_checkbox.c
index 0cb06bad..35c5739d 100644
--- a/textscreen/txt_checkbox.c
+++ b/textscreen/txt_checkbox.c
@@ -34,9 +34,9 @@ static void TXT_CheckBoxSizeCalc(TXT_UNCAST_ARG(checkbox))
{
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 @@ static void TXT_CheckBoxDrawer(TXT_UNCAST_ARG(checkbox), int selected)
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 @@ static void TXT_CheckBoxDrawer(TXT_UNCAST_ARG(checkbox), int selected)
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 @@ static void TXT_CheckBoxMousePress(TXT_UNCAST_ARG(checkbox), int x, int y, int b
txt_widget_class_t txt_checkbox_class =
{
+ TXT_AlwaysSelectable,
TXT_CheckBoxSizeCalc,
TXT_CheckBoxDrawer,
TXT_CheckBoxKeyPress,
diff --git a/textscreen/txt_dropdown.c b/textscreen/txt_dropdown.c
index efed8d67..c8103302 100644
--- a/textscreen/txt_dropdown.c
+++ b/textscreen/txt_dropdown.c
@@ -262,6 +262,7 @@ static void TXT_DropdownListMousePress(TXT_UNCAST_ARG(list),
txt_widget_class_t txt_dropdown_list_class =
{
+ TXT_AlwaysSelectable,
TXT_DropdownListSizeCalc,
TXT_DropdownListDrawer,
TXT_DropdownListKeyPress,
diff --git a/textscreen/txt_inputbox.c b/textscreen/txt_inputbox.c
index 3e52bae9..852346f3 100644
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -232,6 +232,7 @@ static void TXT_InputBoxMousePress(TXT_UNCAST_ARG(inputbox),
txt_widget_class_t txt_inputbox_class =
{
+ TXT_AlwaysSelectable,
TXT_InputBoxSizeCalc,
TXT_InputBoxDrawer,
TXT_InputBoxKeyPress,
@@ -242,6 +243,7 @@ txt_widget_class_t txt_inputbox_class =
txt_widget_class_t txt_int_inputbox_class =
{
+ TXT_AlwaysSelectable,
TXT_InputBoxSizeCalc,
TXT_InputBoxDrawer,
TXT_IntInputBoxKeyPress,
diff --git a/textscreen/txt_label.c b/textscreen/txt_label.c
index 7ae29c3d..0deea803 100644
--- a/textscreen/txt_label.c
+++ b/textscreen/txt_label.c
@@ -104,6 +104,7 @@ static void TXT_LabelDestructor(TXT_UNCAST_ARG(label))
txt_widget_class_t txt_label_class =
{
+ TXT_NeverSelectable,
TXT_LabelSizeCalc,
TXT_LabelDrawer,
NULL,
@@ -170,7 +171,6 @@ txt_label_t *TXT_NewLabel(char *text)
label = malloc(sizeof(txt_label_t));
TXT_InitWidget(label, &txt_label_class);
- label->widget.selectable = 0;
label->label = NULL;
label->lines = NULL;
diff --git a/textscreen/txt_radiobutton.c b/textscreen/txt_radiobutton.c
index 7ede7211..00c2c4fc 100644
--- a/textscreen/txt_radiobutton.c
+++ b/textscreen/txt_radiobutton.c
@@ -34,9 +34,9 @@ static void TXT_RadioButtonSizeCalc(TXT_UNCAST_ARG(radiobutton))
{
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 @@ static void TXT_RadioButtonDrawer(TXT_UNCAST_ARG(radiobutton), int selected)
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 @@ static void TXT_RadioButtonDrawer(TXT_UNCAST_ARG(radiobutton), int selected)
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 @@ static void TXT_RadioButtonMousePress(TXT_UNCAST_ARG(radiobutton),
txt_widget_class_t txt_radiobutton_class =
{
+ TXT_AlwaysSelectable,
TXT_RadioButtonSizeCalc,
TXT_RadioButtonDrawer,
TXT_RadioButtonKeyPress,
diff --git a/textscreen/txt_scrollpane.c b/textscreen/txt_scrollpane.c
index d81cce4b..17c9bcbf 100644
--- a/textscreen/txt_scrollpane.c
+++ b/textscreen/txt_scrollpane.c
@@ -138,7 +138,7 @@ static void TXT_ScrollPaneSizeCalc(TXT_UNCAST_ARG(scrollpane))
}
if (scrollpane->expand_h)
{
- scrollpane->h = FullWidth(scrollpane);
+ scrollpane->h = FullHeight(scrollpane);
}
scrollpane->widget.w = scrollpane->w;
@@ -486,8 +486,26 @@ static void TXT_ScrollPaneLayout(TXT_UNCAST_ARG(scrollpane))
}
}
+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,
diff --git a/textscreen/txt_sdl.c b/textscreen/txt_sdl.c
index a0cbe3d6..2fbaa316 100644
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -119,6 +119,22 @@ static SDL_Color ega_colors[] =
#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,9 +145,22 @@ static SDL_Color ega_colors[] =
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
@@ -140,6 +169,8 @@ static void ChooseFont(void)
// 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
@@ -357,10 +388,6 @@ static int TranslateKey(SDL_keysym *sym)
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:
return KEY_RSHIFT;
diff --git a/textscreen/txt_separator.c b/textscreen/txt_separator.c
index 2bf74b8f..6b779626 100644
--- a/textscreen/txt_separator.c
+++ b/textscreen/txt_separator.c
@@ -82,6 +82,7 @@ static void TXT_SeparatorDestructor(TXT_UNCAST_ARG(separator))
txt_widget_class_t txt_separator_class =
{
+ TXT_NeverSelectable,
TXT_SeparatorSizeCalc,
TXT_SeparatorDrawer,
NULL,
@@ -97,7 +98,6 @@ txt_separator_t *TXT_NewSeparator(char *label)
separator = malloc(sizeof(txt_separator_t));
TXT_InitWidget(separator, &txt_separator_class);
- separator->widget.selectable = 0;
if (label != NULL)
{
diff --git a/textscreen/txt_spinctrl.c b/textscreen/txt_spinctrl.c
index 2b99f535..d775aecf 100644
--- a/textscreen/txt_spinctrl.c
+++ b/textscreen/txt_spinctrl.c
@@ -358,6 +358,7 @@ static void TXT_SpinControlMousePress(TXT_UNCAST_ARG(spincontrol),
txt_widget_class_t txt_spincontrol_class =
{
+ TXT_AlwaysSelectable,
TXT_SpinControlSizeCalc,
TXT_SpinControlDrawer,
TXT_SpinControlKeyPress,
diff --git a/textscreen/txt_strut.c b/textscreen/txt_strut.c
index e7fe6328..f3a618f3 100644
--- a/textscreen/txt_strut.c
+++ b/textscreen/txt_strut.c
@@ -55,6 +55,7 @@ static int TXT_StrutKeyPress(TXT_UNCAST_ARG(strut), int key)
txt_widget_class_t txt_strut_class =
{
+ TXT_NeverSelectable,
TXT_StrutSizeCalc,
TXT_StrutDrawer,
TXT_StrutKeyPress,
@@ -70,7 +71,6 @@ txt_strut_t *TXT_NewStrut(int width, int height)
strut = malloc(sizeof(txt_strut_t));
TXT_InitWidget(strut, &txt_strut_class);
- strut->widget.selectable = 0;
strut->width = width;
strut->height = height;
diff --git a/textscreen/txt_table.c b/textscreen/txt_table.c
index 1b432681..ffe6fd14 100644
--- a/textscreen/txt_table.c
+++ b/textscreen/txt_table.c
@@ -202,7 +202,7 @@ void TXT_AddWidgets(TXT_UNCAST_ARG(table), ...)
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 @@ static int SelectableWidget(txt_table_t *table, int x, int y)
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,14 +239,14 @@ static int FindSelectableColumn(txt_table_t *table, int row, int start_col)
{
// Search to the right
- if (SelectableWidget(table, start_col + x, row))
+ if (SelectableCell(table, start_col + x, row))
{
return start_col + x;
}
// 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 @@ static int TXT_TableKeyPress(TXT_UNCAST_ARG(table), int key)
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 @@ static int TXT_TableKeyPress(TXT_UNCAST_ARG(table), int key)
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 @@ static int TXT_TableKeyPress(TXT_UNCAST_ARG(table), int key)
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 @@ static void TXT_TableMousePress(TXT_UNCAST_ARG(table), int x, int y, int b)
// 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 @@ static void TXT_TableMousePress(TXT_UNCAST_ARG(table), int x, int y, int b)
}
}
+// 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,
diff --git a/textscreen/txt_widget.c b/textscreen/txt_widget.c
index 2300b32c..760943d5 100644
--- a/textscreen/txt_widget.c
+++ b/textscreen/txt_widget.c
@@ -83,9 +83,8 @@ void TXT_InitWidget(TXT_UNCAST_ARG(widget), txt_widget_class_t *widget_class)
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
@@ -214,3 +213,27 @@ void TXT_LayoutWidget(TXT_UNCAST_ARG(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;
+ }
+}
+
diff --git a/textscreen/txt_widget.h b/textscreen/txt_widget.h
index 9688829d..bb895f92 100644
--- a/textscreen/txt_widget.h
+++ b/textscreen/txt_widget.h
@@ -77,9 +77,11 @@ typedef int (*TxtWidgetKeyPress)(TXT_UNCAST_ARG(widget), int key);
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 @@ struct txt_widget_s
{
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 @@ int TXT_WidgetKeyPress(TXT_UNCAST_ARG(widget), int key);
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.
@@ -134,6 +137,15 @@ void TXT_SignalConnect(TXT_UNCAST_ARG(widget), const char *signal_name,
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 */
diff --git a/textscreen/txt_window.c b/textscreen/txt_window.c
index 33f53d4a..46e71d3a 100644
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -140,6 +140,15 @@ static void CalcWindowPosition(txt_window_t *window)
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
@@ -151,29 +160,43 @@ static void LayoutActionArea(txt_window_t *window)
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;
}
}
diff --git a/textscreen/txt_window_action.c b/textscreen/txt_window_action.c
index a326a5ed..e593b7b6 100644
--- a/textscreen/txt_window_action.c
+++ b/textscreen/txt_window_action.c
@@ -93,6 +93,7 @@ static void TXT_WindowActionMousePress(TXT_UNCAST_ARG(action),
txt_widget_class_t txt_window_action_class =
{
+ TXT_AlwaysSelectable,
TXT_WindowActionSizeCalc,
TXT_WindowActionDrawer,
TXT_WindowActionKeyPress,