foxygit / doom Log in
commit 120d90c67b2a4aa0a8883c4897241dee2222acd2
Merge: 79268587 22fc4057
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Thu Sep 9 23:13:06 2010 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Thu Sep 9 23:13:06 2010 +0000

    Merge from raven-branch.

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

 .gitignore                 |   19 +-
 .lvimrc                    |    3 +-
 BUGS                       |   15 -
 ChangeLog                  |  887 ++++++++++++++++++++++++
 INSTALL                    |  159 +++--
 Makefile.am                |    5 +-
 NEWS                       |   66 +-
 README                     |   55 +-
 README.OPL                 |  107 +++
 TODO                       |   20 +-
 codeblocks/config.h        |    6 +-
 codeblocks/game-res.rc     |   10 +-
 codeblocks/setup-res.rc    |    8 +-
 configure.in               |   13 +-
 man/manpage.template       |   11 +
 msvc/config.h              |    6 +-
 msvc/win32.rc              |   10 +-
 opl/.gitignore             |    7 +
 opl/Makefile.am            |   19 +
 opl/dbopl.c                | 1602 ++++++++++++++++++++++++++++++++++++++++++++
 opl/dbopl.h                |  203 ++++++
 opl/examples/.gitignore    |    7 +
 opl/examples/Makefile.am   |    8 +
 opl/examples/droplay.c     |  217 ++++++
 opl/ioperm_sys.c           |  361 ++++++++++
 opl/ioperm_sys.h           |   36 +
 opl/opl.c                  |  466 +++++++++++++
 opl/opl.h                  |  137 ++++
 opl/opl_internal.h         |   64 ++
 opl/opl_linux.c            |  110 +++
 opl/opl_obsd.c             |  125 ++++
 opl/opl_queue.c            |  280 ++++++++
 opl/opl_queue.h            |   45 ++
 opl/opl_sdl.c              |  510 ++++++++++++++
 opl/opl_timer.c            |  251 +++++++
 opl/opl_timer.h            |   42 ++
 opl/opl_win32.c            |  172 +++++
 pkg/config.make.in         |    1 +
 pkg/osx/GNUmakefile        |    4 +
 pkg/win32/GNUmakefile      |    2 +
 rpm.spec.in                |    4 +-
 src/.gitignore             |    4 +
 src/Makefile.am            |   14 +-
 src/deh_main.c             |   10 +-
 src/deh_mapping.c          |  140 +++-
 src/deh_mapping.h          |   22 +-
 src/deh_str.c              |  227 +++++++
 src/deh_str.h              |    9 +
 src/doom/.gitignore        |    4 -
 src/doom/am_map.c          |    6 +-
 src/doom/d_main.c          |  196 +-----
 src/doom/d_net.c           |   10 +-
 src/doom/f_finale.c        |    2 +-
 src/doom/g_game.c          |  188 +++++-
 src/doom/hu_stuff.c        |   14 +-
 src/doom/m_menu.c          |    4 +-
 src/doom/p_saveg.c         |   24 +-
 src/doom/p_saveg.h         |    1 +
 src/doom/p_setup.c         |  100 ++-
 src/doom/r_draw.c          |  111 +--
 src/doom/s_sound.c         |    9 +
 src/doom/st_stuff.c        |   22 +-
 src/doom/wi_stuff.c        |   47 +-
 src/heretic/.gitignore     |    7 +-
 src/heretic/Makefile.am    |   13 +-
 src/heretic/am_map.c       |    7 +-
 src/heretic/ct_chat.c      |   13 +-
 src/heretic/d_main.c       |  102 ++-
 src/heretic/deh_ammo.c     |  122 ++++
 src/heretic/deh_frame.c    |  344 ++++++++++
 src/heretic/deh_htext.c    |  856 +++++++++++++++++++++++
 src/heretic/deh_htic.c     |  186 +++++
 src/heretic/deh_htic.h     |   60 ++
 src/heretic/deh_sound.c    |  118 ++++
 src/heretic/deh_thing.c    |  150 +++++
 src/heretic/deh_weapon.c   |  131 ++++
 src/heretic/doomdef.h      |    1 +
 src/heretic/dstrings.h     |  177 -----
 src/heretic/f_finale.c     |   38 +-
 src/heretic/g_game.c       |   21 +-
 src/heretic/in_lude.c      |   73 +-
 src/heretic/info.c         |  162 +----
 src/heretic/info.h         |   15 +-
 src/heretic/mn_menu.c      |   60 +-
 src/heretic/p_action.h     |  160 +++++
 src/heretic/p_doors.c      |    7 +-
 src/heretic/p_inter.c      |   75 ++-
 src/heretic/p_pspr.c       |   11 +
 src/heretic/p_spec.c       |   40 +-
 src/heretic/p_switch.c     |    5 +-
 src/heretic/p_user.c       |    3 +-
 src/heretic/r_data.c       |   40 +-
 src/heretic/r_draw.c       |   49 +-
 src/heretic/r_plane.c      |    3 +-
 src/heretic/r_things.c     |   14 +-
 src/heretic/s_sound.c      |   21 +-
 src/heretic/sb_bar.c       |  170 ++---
 src/heretic/sounds.h       |    5 +
 src/hexen/.gitignore       |    6 +-
 src/hexen/h2_main.c        |   28 +-
 src/hexen/mn_menu.c        |   47 +-
 src/hexen/p_acs.c          |    2 +-
 src/hexen/p_spec.c         |    2 +
 src/hexen/r_things.c       |    5 +-
 src/hexen/s_sound.c        |   16 +-
 src/i_oplmusic.c           | 1464 ++++++++++++++++++++++++++++++++++++++++
 src/i_scale.c              |   14 +-
 src/i_sdlmusic.c           |    2 -
 src/i_sdlsound.c           |   72 +-
 src/i_sound.c              |   10 +-
 src/i_video.c              |  281 +++++---
 src/i_video.h              |    4 +-
 src/m_argv.c               |    2 +-
 src/m_config.c             |  132 +++-
 src/m_controls.c           |   47 +-
 src/m_controls.h           |   17 +-
 src/m_misc.c               |   34 +
 src/m_misc.h               |    2 +-
 src/midifile.c             |  814 ++++++++++++++++++++++
 src/midifile.h             |  175 +++++
 src/net_client.c           |    3 +-
 src/setup/.gitignore       |    3 +-
 src/setup/joystick.c       |    2 +
 src/setup/keyboard.c       |   27 +-
 src/setup/keyboard.h       |   53 --
 src/setup/mainmenu.c       |   60 ++
 src/setup/mouse.c          |    9 +-
 src/setup/multiplayer.c    |   35 +
 src/setup/sound.c          |   37 +-
 src/setup/txt_keyinput.c   |    2 +-
 src/setup/txt_mouseinput.c |    4 +-
 src/strife/.gitignore      |    4 -
 src/strife/am_map.c        |    6 +-
 src/strife/d_main.c        |  196 +-----
 src/strife/d_net.c         |   10 +-
 src/strife/f_finale.c      |    2 +-
 src/strife/g_game.c        |  183 ++++-
 src/strife/hu_stuff.c      |   14 +-
 src/strife/m_menu.c        |    4 +-
 src/strife/p_saveg.c       |   24 +-
 src/strife/p_saveg.h       |    1 +
 src/strife/p_setup.c       |  100 ++-
 src/strife/r_draw.c        |  111 +--
 src/strife/st_stuff.c      |    4 +-
 src/strife/wi_stuff.c      |   47 +-
 src/w_main.c               |  206 ++++++
 src/w_main.h               |   32 +
 src/z_zone.c               |    3 +-
 textscreen/Makefile.am     |    6 +-
 149 files changed, 13145 insertions(+), 1715 deletions(-)

diff --cc src/setup/sound.c
index 45787eba,627549b5..d8dc129e
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@@ -201,15 -212,7 +213,15 @@@ void ConfigSound(void
                     TXT_NewSpinControl(&sfxVolume, 0, 15),
                     NULL);

 +    if (gamemission == strife)
 +    {
 +        TXT_AddWidgets(sfx_table,
 +                       TXT_NewLabel("Voice volume"),
 +                       TXT_NewSpinControl(&voiceVolume, 0, 15),
 +                       NULL);
 +    }
 +
-     TXT_SetColumnWidths(music_table, 20, 5);
+     TXT_SetColumnWidths(music_table, 20, 14);

      TXT_AddWidgets(music_table,
                     TXT_NewLabel("Music"),
diff --cc src/strife/.gitignore
index 973a0073,00000000..d4e88e5a
mode 100644,000000..100644
--- a/src/strife/.gitignore
+++ b/src/strife/.gitignore
@@@ -1,9 -1,0 +1,5 @@@
 +Makefile
 +Makefile.in
 +.deps
- *.rc
- chocolate-doom
- chocolate-server
- *.exe
 +tags
 +TAGS
diff --cc src/strife/am_map.c
index b1eaa216,00000000..c9d0f659
mode 100644,000000..100644
--- a/src/strife/am_map.c
+++ b/src/strife/am_map.c
@@@ -1,1374 -1,0 +1,1374 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +//
 +// DESCRIPTION:  the automap code
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <stdio.h>
 +
 +#include "deh_main.h"
 +
 +#include "z_zone.h"
 +#include "doomkeys.h"
 +#include "doomdef.h"
 +#include "st_stuff.h"
 +#include "p_local.h"
 +#include "w_wad.h"
 +
 +#include "m_cheat.h"
 +#include "m_controls.h"
 +#include "i_system.h"
 +
 +// Needs access to LFB.
 +#include "v_video.h"
 +
 +// State.
 +#include "doomstat.h"
 +#include "r_state.h"
 +
 +// Data.
 +#include "dstrings.h"
 +
 +#include "am_map.h"
 +
 +
 +// For use if I do walls with outsides/insides
 +#define REDS		(256-5*16)
 +#define REDRANGE	16
 +#define BLUES		(256-4*16+8)
 +#define BLUERANGE	8
 +#define GREENS		(7*16)
 +#define GREENRANGE	16
 +#define GRAYS		(6*16)
 +#define GRAYSRANGE	16
 +#define BROWNS		(4*16)
 +#define BROWNRANGE	16
 +#define YELLOWS		(256-32+7)
 +#define YELLOWRANGE	1
 +#define BLACK		0
 +#define WHITE		(256-47)
 +
 +// Automap colors
 +#define BACKGROUND	BLACK
 +#define YOURCOLORS	WHITE
 +#define YOURRANGE	0
 +#define WALLCOLORS	REDS
 +#define WALLRANGE	REDRANGE
 +#define TSWALLCOLORS	GRAYS
 +#define TSWALLRANGE	GRAYSRANGE
 +#define FDWALLCOLORS	BROWNS
 +#define FDWALLRANGE	BROWNRANGE
 +#define CDWALLCOLORS	YELLOWS
 +#define CDWALLRANGE	YELLOWRANGE
 +#define THINGCOLORS	GREENS
 +#define THINGRANGE	GREENRANGE
 +#define SECRETWALLCOLORS WALLCOLORS
 +#define SECRETWALLRANGE WALLRANGE
 +#define GRIDCOLORS	(GRAYS + GRAYSRANGE/2)
 +#define GRIDRANGE	0
 +#define XHAIRCOLORS	GRAYS
 +
 +// drawing stuff
 +
 +#define AM_NUMMARKPOINTS 10
 +
 +// scale on entry
 +#define INITSCALEMTOF (.2*FRACUNIT)
 +// how much the automap moves window per tic in frame-buffer coordinates
 +// moves 140 pixels in 1 second
 +#define F_PANINC	4
 +// how much zoom-in per tic
 +// goes to 2x in 1 second
 +#define M_ZOOMIN        ((int) (1.02*FRACUNIT))
 +// how much zoom-out per tic
 +// pulls out to 0.5x in 1 second
 +#define M_ZOOMOUT       ((int) (FRACUNIT/1.02))
 +
 +// translates between frame-buffer and map distances
 +#define FTOM(x) FixedMul(((x)<<16),scale_ftom)
 +#define MTOF(x) (FixedMul((x),scale_mtof)>>16)
 +// translates between frame-buffer and map coordinates
 +#define CXMTOF(x)  (f_x + MTOF((x)-m_x))
 +#define CYMTOF(y)  (f_y + (f_h - MTOF((y)-m_y)))
 +
 +// the following is crap
 +#define LINE_NEVERSEE ML_DONTDRAW
 +
 +typedef struct
 +{
 +    int x, y;
 +} fpoint_t;
 +
 +typedef struct
 +{
 +    fpoint_t a, b;
 +} fline_t;
 +
 +typedef struct
 +{
 +    fixed_t		x,y;
 +} mpoint_t;
 +
 +typedef struct
 +{
 +    mpoint_t a, b;
 +} mline_t;
 +
 +typedef struct
 +{
 +    fixed_t slp, islp;
 +} islope_t;
 +
 +
 +
 +//
 +// The vector graphics for the automap.
 +//  A line drawing of the player pointing right,
 +//   starting from the middle.
 +//
 +#define R ((8*PLAYERRADIUS)/7)
 +mline_t player_arrow[] = {
 +    { { -R+R/8, 0 }, { R, 0 } }, // -----
 +    { { R, 0 }, { R-R/2, R/4 } },  // ----->
 +    { { R, 0 }, { R-R/2, -R/4 } },
 +    { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >---->
 +    { { -R+R/8, 0 }, { -R-R/8, -R/4 } },
 +    { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>--->
 +    { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }
 +};
 +#undef R
 +
 +#define R ((8*PLAYERRADIUS)/7)
 +mline_t cheat_player_arrow[] = {
 +    { { -R+R/8, 0 }, { R, 0 } }, // -----
 +    { { R, 0 }, { R-R/2, R/6 } },  // ----->
 +    { { R, 0 }, { R-R/2, -R/6 } },
 +    { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >----->
 +    { { -R+R/8, 0 }, { -R-R/8, -R/6 } },
 +    { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>----->
 +    { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } },
 +    { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d--->
 +    { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } },
 +    { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } },
 +    { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd-->
 +    { { -R/6, -R/6 }, { 0, -R/6 } },
 +    { { 0, -R/6 }, { 0, R/4 } },
 +    { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt->
 +    { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } },
 +    { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } }
 +};
 +#undef R
 +
 +#define R (FRACUNIT)
 +mline_t triangle_guy[] = {
 +    { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } },
 +    { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0      ), (fixed_t)(R    ) } },
 +    { { (fixed_t)(0      ), (fixed_t)(R    ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } }
 +};
 +#undef R
 +
 +#define R (FRACUNIT)
 +mline_t thintriangle_guy[] = {
 +    { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)(R    ), (fixed_t)(0    ) } },
 +    { { (fixed_t)(R    ), (fixed_t)(0    ) }, { (fixed_t)(-.5*R), (fixed_t)(.7*R ) } },
 +    { { (fixed_t)(-.5*R), (fixed_t)(.7*R ) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } }
 +};
 +#undef R
 +
 +
 +
 +
 +static int 	cheating = 0;
 +static int 	grid = 0;
 +
 +static int 	leveljuststarted = 1; 	// kluge until AM_LevelInit() is called
 +
 +boolean    	automapactive = false;
 +static int 	finit_width = SCREENWIDTH;
 +static int 	finit_height = SCREENHEIGHT - 32;
 +
 +// location of window on screen
 +static int 	f_x;
 +static int	f_y;
 +
 +// size of window on screen
 +static int 	f_w;
 +static int	f_h;
 +
 +static int 	lightlev; 		// used for funky strobing effect
 +static byte*	fb; 			// pseudo-frame buffer
 +static int 	amclock;
 +
 +static mpoint_t m_paninc; // how far the window pans each tic (map coords)
 +static fixed_t 	mtof_zoommul; // how far the window zooms in each tic (map coords)
 +static fixed_t 	ftom_zoommul; // how far the window zooms in each tic (fb coords)
 +
 +static fixed_t 	m_x, m_y;   // LL x,y where the window is on the map (map coords)
 +static fixed_t 	m_x2, m_y2; // UR x,y where the window is on the map (map coords)
 +
 +//
 +// width/height of window on map (map coords)
 +//
 +static fixed_t 	m_w;
 +static fixed_t	m_h;
 +
 +// based on level size
 +static fixed_t 	min_x;
 +static fixed_t	min_y;
 +static fixed_t 	max_x;
 +static fixed_t  max_y;
 +
 +static fixed_t 	max_w; // max_x-min_x,
 +static fixed_t  max_h; // max_y-min_y
 +
 +// based on player size
 +static fixed_t 	min_w;
 +static fixed_t  min_h;
 +
 +
 +static fixed_t 	min_scale_mtof; // used to tell when to stop zooming out
 +static fixed_t 	max_scale_mtof; // used to tell when to stop zooming in
 +
 +// old stuff for recovery later
 +static fixed_t old_m_w, old_m_h;
 +static fixed_t old_m_x, old_m_y;
 +
 +// old location used by the Follower routine
 +static mpoint_t f_oldloc;
 +
 +// used by MTOF to scale from map-to-frame-buffer coords
 +static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF;
 +// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)
 +static fixed_t scale_ftom;
 +
 +static player_t *plr; // the player represented by an arrow
 +
 +static patch_t *marknums[10]; // numbers used for marking by the automap
 +static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
 +static int markpointnum = 0; // next point to be assigned
 +
 +static int followplayer = 1; // specifies whether to follow the player around
 +
 +cheatseq_t cheat_amap = CHEAT("iddt", 0);
 +
 +static boolean stopped = true;
 +
 +extern boolean viewactive;
 +
 +void
 +V_MarkRect
 +( int	x,
 +  int	y,
 +  int	width,
 +  int	height );
 +
 +// Calculates the slope and slope according to the x-axis of a line
 +// segment in map coordinates (with the upright y-axis n' all) so
 +// that it can be used with the brain-dead drawing stuff.
 +
 +void
 +AM_getIslope
 +( mline_t*	ml,
 +  islope_t*	is )
 +{
 +    int dx, dy;
 +
 +    dy = ml->a.y - ml->b.y;
 +    dx = ml->b.x - ml->a.x;
 +    if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX);
 +    else is->islp = FixedDiv(dx, dy);
 +    if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX);
 +    else is->slp = FixedDiv(dy, dx);
 +
 +}
 +
 +//
 +//
 +//
 +void AM_activateNewScale(void)
 +{
 +    m_x += m_w/2;
 +    m_y += m_h/2;
 +    m_w = FTOM(f_w);
 +    m_h = FTOM(f_h);
 +    m_x -= m_w/2;
 +    m_y -= m_h/2;
 +    m_x2 = m_x + m_w;
 +    m_y2 = m_y + m_h;
 +}
 +
 +//
 +//
 +//
 +void AM_saveScaleAndLoc(void)
 +{
 +    old_m_x = m_x;
 +    old_m_y = m_y;
 +    old_m_w = m_w;
 +    old_m_h = m_h;
 +}
 +
 +//
 +//
 +//
 +void AM_restoreScaleAndLoc(void)
 +{
 +
 +    m_w = old_m_w;
 +    m_h = old_m_h;
 +    if (!followplayer)
 +    {
 +	m_x = old_m_x;
 +	m_y = old_m_y;
 +    } else {
 +	m_x = plr->mo->x - m_w/2;
 +	m_y = plr->mo->y - m_h/2;
 +    }
 +    m_x2 = m_x + m_w;
 +    m_y2 = m_y + m_h;
 +
 +    // Change the scaling multipliers
 +    scale_mtof = FixedDiv(f_w<<FRACBITS, m_w);
 +    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
 +}
 +
 +//
 +// adds a marker at the current location
 +//
 +void AM_addMark(void)
 +{
 +    markpoints[markpointnum].x = m_x + m_w/2;
 +    markpoints[markpointnum].y = m_y + m_h/2;
 +    markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
 +
 +}
 +
 +//
 +// Determines bounding box of all vertices,
 +// sets global variables controlling zoom range.
 +//
 +void AM_findMinMaxBoundaries(void)
 +{
 +    int i;
 +    fixed_t a;
 +    fixed_t b;
 +
 +    min_x = min_y =  INT_MAX;
 +    max_x = max_y = -INT_MAX;
 +
 +    for (i=0;i<numvertexes;i++)
 +    {
 +	if (vertexes[i].x < min_x)
 +	    min_x = vertexes[i].x;
 +	else if (vertexes[i].x > max_x)
 +	    max_x = vertexes[i].x;
 +
 +	if (vertexes[i].y < min_y)
 +	    min_y = vertexes[i].y;
 +	else if (vertexes[i].y > max_y)
 +	    max_y = vertexes[i].y;
 +    }
 +
 +    max_w = max_x - min_x;
 +    max_h = max_y - min_y;
 +
 +    min_w = 2*PLAYERRADIUS; // const? never changed?
 +    min_h = 2*PLAYERRADIUS;
 +
 +    a = FixedDiv(f_w<<FRACBITS, max_w);
 +    b = FixedDiv(f_h<<FRACBITS, max_h);
 +
 +    min_scale_mtof = a < b ? a : b;
 +    max_scale_mtof = FixedDiv(f_h<<FRACBITS, 2*PLAYERRADIUS);
 +
 +}
 +
 +
 +//
 +//
 +//
 +void AM_changeWindowLoc(void)
 +{
 +    if (m_paninc.x || m_paninc.y)
 +    {
 +	followplayer = 0;
 +	f_oldloc.x = INT_MAX;
 +    }
 +
 +    m_x += m_paninc.x;
 +    m_y += m_paninc.y;
 +
 +    if (m_x + m_w/2 > max_x)
 +	m_x = max_x - m_w/2;
 +    else if (m_x + m_w/2 < min_x)
 +	m_x = min_x - m_w/2;
 +
 +    if (m_y + m_h/2 > max_y)
 +	m_y = max_y - m_h/2;
 +    else if (m_y + m_h/2 < min_y)
 +	m_y = min_y - m_h/2;
 +
 +    m_x2 = m_x + m_w;
 +    m_y2 = m_y + m_h;
 +}
 +
 +
 +//
 +//
 +//
 +void AM_initVariables(void)
 +{
 +    int pnum;
 +    static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 };
 +
 +    automapactive = true;
 +    fb = I_VideoBuffer;
 +
 +    f_oldloc.x = INT_MAX;
 +    amclock = 0;
 +    lightlev = 0;
 +
 +    m_paninc.x = m_paninc.y = 0;
 +    ftom_zoommul = FRACUNIT;
 +    mtof_zoommul = FRACUNIT;
 +
 +    m_w = FTOM(f_w);
 +    m_h = FTOM(f_h);
 +
 +    // find player to center on initially
 +    if (playeringame[consoleplayer])
 +    {
 +        plr = &players[consoleplayer];
 +    }
 +    else
 +    {
 +        plr = &players[0];
 +
 +	for (pnum=0;pnum<MAXPLAYERS;pnum++)
 +        {
 +	    if (playeringame[pnum])
 +            {
 +                plr = &players[pnum];
 +		break;
 +            }
 +        }
 +    }
 +
 +    m_x = plr->mo->x - m_w/2;
 +    m_y = plr->mo->y - m_h/2;
 +    AM_changeWindowLoc();
 +
 +    // for saving & restoring
 +    old_m_x = m_x;
 +    old_m_y = m_y;
 +    old_m_w = m_w;
 +    old_m_h = m_h;
 +
 +    // inform the status bar of the change
 +    ST_Responder(&st_notify);
 +
 +}
 +
 +//
 +// AM_loadPics
 +//
 +// haleyjd 08/27/10: [STRIFE] Changed marknums to PLMNUM%d
 +//
 +void AM_loadPics(void)
 +{
 +    int i;
 +    char namebuf[9];
 +
 +    for (i=0;i<10;i++)
 +    {
-         sprintf(namebuf, DEH_String("PLMNUM%d"), i);
++        DEH_snprintf(namebuf, 9, "PLMNUM%d", i);
 +        marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
 +    }
 +
 +}
 +
 +void AM_unloadPics(void)
 +{
 +    int i;
 +    char namebuf[9];
 +
 +    for (i=0;i<10;i++)
 +    {
-         sprintf(namebuf, DEH_String("PLMNUM%d"), i); // haleyjd: Choco change.
++        DEH_snprintf(namebuf, 9, "PLMNUM%d", i);
 +        W_ReleaseLumpName(namebuf);
 +    }
 +}
 +
 +void AM_clearMarks(void)
 +{
 +    int i;
 +
 +    for (i=0;i<AM_NUMMARKPOINTS;i++)
 +	markpoints[i].x = -1; // means empty
 +    markpointnum = 0;
 +}
 +
 +//
 +// should be called at the start of every level
 +// right now, i figure it out myself
 +//
 +void AM_LevelInit(void)
 +{
 +    leveljuststarted = 0;
 +
 +    f_x = f_y = 0;
 +    f_w = finit_width;
 +    f_h = finit_height;
 +
 +    AM_clearMarks();
 +
 +    AM_findMinMaxBoundaries();
 +    scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT));
 +    if (scale_mtof > max_scale_mtof)
 +	scale_mtof = min_scale_mtof;
 +    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
 +}
 +
 +
 +
 +
 +//
 +//
 +//
 +void AM_Stop (void)
 +{
 +    static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 };
 +
 +    AM_unloadPics();
 +    automapactive = false;
 +    ST_Responder(&st_notify);
 +    stopped = true;
 +}
 +
 +//
 +//
 +//
 +void AM_Start (void)
 +{
 +    static int lastlevel = -1, lastepisode = -1;
 +
 +    if (!stopped) AM_Stop();
 +    stopped = false;
 +    if (lastlevel != gamemap || lastepisode != gameepisode)
 +    {
 +	AM_LevelInit();
 +	lastlevel = gamemap;
 +	lastepisode = gameepisode;
 +    }
 +    AM_initVariables();
 +    AM_loadPics();
 +}
 +
 +//
 +// set the window scale to the maximum size
 +//
 +void AM_minOutWindowScale(void)
 +{
 +    scale_mtof = min_scale_mtof;
 +    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
 +    AM_activateNewScale();
 +}
 +
 +//
 +// set the window scale to the minimum size
 +//
 +void AM_maxOutWindowScale(void)
 +{
 +    scale_mtof = max_scale_mtof;
 +    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
 +    AM_activateNewScale();
 +}
 +
 +
 +//
 +// Handle events (user inputs) in automap mode
 +//
 +boolean
 +AM_Responder
 +( event_t*	ev )
 +{
 +
 +    int rc;
 +    static int cheatstate=0;
 +    static int bigstate=0;
 +    static char buffer[20];
 +    int key;
 +
 +    rc = false;
 +
 +    if (!automapactive)
 +    {
 +	if (ev->type == ev_keydown && ev->data1 == key_map_toggle)
 +	{
 +	    AM_Start ();
 +	    viewactive = false;
 +	    rc = true;
 +	}
 +    }
 +    else if (ev->type == ev_keydown)
 +    {
 +	rc = true;
 +        key = ev->data1;
 +
 +        if (key == key_map_east)          // pan right
 +        {
 +            if (!followplayer) m_paninc.x = FTOM(F_PANINC);
 +            else rc = false;
 +        }
 +        else if (key == key_map_west)     // pan left
 +        {
 +            if (!followplayer) m_paninc.x = -FTOM(F_PANINC);
 +            else rc = false;
 +        }
 +        else if (key == key_map_north)    // pan up
 +        {
 +            if (!followplayer) m_paninc.y = FTOM(F_PANINC);
 +            else rc = false;
 +        }
 +        else if (key == key_map_south)    // pan down
 +        {
 +            if (!followplayer) m_paninc.y = -FTOM(F_PANINC);
 +            else rc = false;
 +        }
 +        else if (key == key_map_zoomout)  // zoom out
 +        {
 +            mtof_zoommul = M_ZOOMOUT;
 +            ftom_zoommul = M_ZOOMIN;
 +        }
 +        else if (key == key_map_zoomin)   // zoom in
 +        {
 +            mtof_zoommul = M_ZOOMIN;
 +            ftom_zoommul = M_ZOOMOUT;
 +        }
 +        else if (key == key_map_toggle)
 +        {
 +            bigstate = 0;
 +            viewactive = true;
 +            AM_Stop ();
 +        }
 +        else if (key == key_map_maxzoom)
 +        {
 +            bigstate = !bigstate;
 +            if (bigstate)
 +            {
 +                AM_saveScaleAndLoc();
 +                AM_minOutWindowScale();
 +            }
 +            else AM_restoreScaleAndLoc();
 +        }
 +        else if (key == key_map_follow)
 +        {
 +            followplayer = !followplayer;
 +            f_oldloc.x = INT_MAX;
 +            if (followplayer)
 +                plr->message = DEH_String(AMSTR_FOLLOWON);
 +            else
 +                plr->message = DEH_String(AMSTR_FOLLOWOFF);
 +        }
 +        else if (key == key_map_grid)
 +        {
 +            grid = !grid;
 +            if (grid)
 +                plr->message = DEH_String(AMSTR_GRIDON);
 +            else
 +                plr->message = DEH_String(AMSTR_GRIDOFF);
 +        }
 +        else if (key == key_map_mark)
 +        {
 +            sprintf(buffer, "%s %d", DEH_String(AMSTR_MARKEDSPOT), markpointnum);
 +            plr->message = buffer;
 +            AM_addMark();
 +        }
 +        else if (key == key_map_clearmark)
 +        {
 +            AM_clearMarks();
 +            plr->message = DEH_String(AMSTR_MARKSCLEARED);
 +        }
 +        else
 +        {
 +            cheatstate=0;
 +            rc = false;
 +        }
 +
 +	if (!deathmatch && cht_CheckCheat(&cheat_amap, ev->data2))
 +	{
 +	    rc = false;
 +	    cheating = (cheating+1) % 3;
 +	}
 +    }
 +    else if (ev->type == ev_keyup)
 +    {
 +        rc = false;
 +        key = ev->data1;
 +
 +        if (key == key_map_east)
 +        {
 +            if (!followplayer) m_paninc.x = 0;
 +        }
 +        else if (key == key_map_west)
 +        {
 +            if (!followplayer) m_paninc.x = 0;
 +        }
 +        else if (key == key_map_north)
 +        {
 +            if (!followplayer) m_paninc.y = 0;
 +        }
 +        else if (key == key_map_south)
 +        {
 +            if (!followplayer) m_paninc.y = 0;
 +        }
 +        else if (key == key_map_zoomout || key == key_map_zoomin)
 +        {
 +            mtof_zoommul = FRACUNIT;
 +            ftom_zoommul = FRACUNIT;
 +        }
 +    }
 +
 +    return rc;
 +
 +}
 +
 +
 +//
 +// Zooming
 +//
 +void AM_changeWindowScale(void)
 +{
 +
 +    // Change the scaling multipliers
 +    scale_mtof = FixedMul(scale_mtof, mtof_zoommul);
 +    scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
 +
 +    if (scale_mtof < min_scale_mtof)
 +	AM_minOutWindowScale();
 +    else if (scale_mtof > max_scale_mtof)
 +	AM_maxOutWindowScale();
 +    else
 +	AM_activateNewScale();
 +}
 +
 +
 +//
 +//
 +//
 +void AM_doFollowPlayer(void)
 +{
 +
 +    if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
 +    {
 +	m_x = FTOM(MTOF(plr->mo->x)) - m_w/2;
 +	m_y = FTOM(MTOF(plr->mo->y)) - m_h/2;
 +	m_x2 = m_x + m_w;
 +	m_y2 = m_y + m_h;
 +	f_oldloc.x = plr->mo->x;
 +	f_oldloc.y = plr->mo->y;
 +
 +	//  m_x = FTOM(MTOF(plr->mo->x - m_w/2));
 +	//  m_y = FTOM(MTOF(plr->mo->y - m_h/2));
 +	//  m_x = plr->mo->x - m_w/2;
 +	//  m_y = plr->mo->y - m_h/2;
 +
 +    }
 +
 +}
 +
 +//
 +//
 +//
 +void AM_updateLightLev(void)
 +{
 +    static int nexttic = 0;
 +    //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 };
 +    static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 };
 +    static int litelevelscnt = 0;
 +
 +    // Change light level
 +    if (amclock>nexttic)
 +    {
 +	lightlev = litelevels[litelevelscnt++];
 +	if (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0;
 +	nexttic = amclock + 6 - (amclock % 6);
 +    }
 +
 +}
 +
 +
 +//
 +// Updates on Game Tick
 +//
 +void AM_Ticker (void)
 +{
 +
 +    if (!automapactive)
 +	return;
 +
 +    amclock++;
 +
 +    if (followplayer)
 +	AM_doFollowPlayer();
 +
 +    // Change the zoom if necessary
 +    if (ftom_zoommul != FRACUNIT)
 +	AM_changeWindowScale();
 +
 +    // Change x,y location
 +    if (m_paninc.x || m_paninc.y)
 +	AM_changeWindowLoc();
 +
 +    // Update light level
 +    // AM_updateLightLev();
 +
 +}
 +
 +
 +//
 +// Clear automap frame buffer.
 +//
 +void AM_clearFB(int color)
 +{
 +    memset(fb, color, f_w*f_h);
 +}
 +
 +
 +//
 +// Automap clipping of lines.
 +//
 +// Based on Cohen-Sutherland clipping algorithm but with a slightly
 +// faster reject and precalculated slopes.  If the speed is needed,
 +// use a hash algorithm to handle  the common cases.
 +//
 +boolean
 +AM_clipMline
 +( mline_t*	ml,
 +  fline_t*	fl )
 +{
 +    enum
 +    {
 +	LEFT	=1,
 +	RIGHT	=2,
 +	BOTTOM	=4,
 +	TOP	=8
 +    };
 +
 +    register int	outcode1 = 0;
 +    register int	outcode2 = 0;
 +    register int	outside;
 +
 +    fpoint_t	tmp;
 +    int		dx;
 +    int		dy;
 +
 +
 +#define DOOUTCODE(oc, mx, my) \
 +    (oc) = 0; \
 +    if ((my) < 0) (oc) |= TOP; \
 +    else if ((my) >= f_h) (oc) |= BOTTOM; \
 +    if ((mx) < 0) (oc) |= LEFT; \
 +    else if ((mx) >= f_w) (oc) |= RIGHT;
 +
 +
 +    // do trivial rejects and outcodes
 +    if (ml->a.y > m_y2)
 +	outcode1 = TOP;
 +    else if (ml->a.y < m_y)
 +	outcode1 = BOTTOM;
 +
 +    if (ml->b.y > m_y2)
 +	outcode2 = TOP;
 +    else if (ml->b.y < m_y)
 +	outcode2 = BOTTOM;
 +
 +    if (outcode1 & outcode2)
 +	return false; // trivially outside
 +
 +    if (ml->a.x < m_x)
 +	outcode1 |= LEFT;
 +    else if (ml->a.x > m_x2)
 +	outcode1 |= RIGHT;
 +
 +    if (ml->b.x < m_x)
 +	outcode2 |= LEFT;
 +    else if (ml->b.x > m_x2)
 +	outcode2 |= RIGHT;
 +
 +    if (outcode1 & outcode2)
 +	return false; // trivially outside
 +
 +    // transform to frame-buffer coordinates.
 +    fl->a.x = CXMTOF(ml->a.x);
 +    fl->a.y = CYMTOF(ml->a.y);
 +    fl->b.x = CXMTOF(ml->b.x);
 +    fl->b.y = CYMTOF(ml->b.y);
 +
 +    DOOUTCODE(outcode1, fl->a.x, fl->a.y);
 +    DOOUTCODE(outcode2, fl->b.x, fl->b.y);
 +
 +    if (outcode1 & outcode2)
 +	return false;
 +
 +    while (outcode1 | outcode2)
 +    {
 +	// may be partially inside box
 +	// find an outside point
 +	if (outcode1)
 +	    outside = outcode1;
 +	else
 +	    outside = outcode2;
 +
 +	// clip to each side
 +	if (outside & TOP)
 +	{
 +	    dy = fl->a.y - fl->b.y;
 +	    dx = fl->b.x - fl->a.x;
 +	    tmp.x = fl->a.x + (dx*(fl->a.y))/dy;
 +	    tmp.y = 0;
 +	}
 +	else if (outside & BOTTOM)
 +	{
 +	    dy = fl->a.y - fl->b.y;
 +	    dx = fl->b.x - fl->a.x;
 +	    tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy;
 +	    tmp.y = f_h-1;
 +	}
 +	else if (outside & RIGHT)
 +	{
 +	    dy = fl->b.y - fl->a.y;
 +	    dx = fl->b.x - fl->a.x;
 +	    tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx;
 +	    tmp.x = f_w-1;
 +	}
 +	else if (outside & LEFT)
 +	{
 +	    dy = fl->b.y - fl->a.y;
 +	    dx = fl->b.x - fl->a.x;
 +	    tmp.y = fl->a.y + (dy*(-fl->a.x))/dx;
 +	    tmp.x = 0;
 +	}
 +        else
 +        {
 +            tmp.x = 0;
 +            tmp.y = 0;
 +        }
 +
 +	if (outside == outcode1)
 +	{
 +	    fl->a = tmp;
 +	    DOOUTCODE(outcode1, fl->a.x, fl->a.y);
 +	}
 +	else
 +	{
 +	    fl->b = tmp;
 +	    DOOUTCODE(outcode2, fl->b.x, fl->b.y);
 +	}
 +
 +	if (outcode1 & outcode2)
 +	    return false; // trivially outside
 +    }
 +
 +    return true;
 +}
 +#undef DOOUTCODE
 +
 +
 +//
 +// Classic Bresenham w/ whatever optimizations needed for speed
 +//
 +void
 +AM_drawFline
 +( fline_t*	fl,
 +  int		color )
 +{
 +    register int x;
 +    register int y;
 +    register int dx;
 +    register int dy;
 +    register int sx;
 +    register int sy;
 +    register int ax;
 +    register int ay;
 +    register int d;
 +
 +    static int fuck = 0;
 +
 +    // For debugging only
 +    if (      fl->a.x < 0 || fl->a.x >= f_w
 +	   || fl->a.y < 0 || fl->a.y >= f_h
 +	   || fl->b.x < 0 || fl->b.x >= f_w
 +	   || fl->b.y < 0 || fl->b.y >= f_h)
 +    {
- 	fprintf(stderr, DEH_String("fuck %d \r"), fuck++);
++        DEH_fprintf(stderr, "fuck %d \r", fuck++);
 +	return;
 +    }
 +
 +#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc)
 +
 +    dx = fl->b.x - fl->a.x;
 +    ax = 2 * (dx<0 ? -dx : dx);
 +    sx = dx<0 ? -1 : 1;
 +
 +    dy = fl->b.y - fl->a.y;
 +    ay = 2 * (dy<0 ? -dy : dy);
 +    sy = dy<0 ? -1 : 1;
 +
 +    x = fl->a.x;
 +    y = fl->a.y;
 +
 +    if (ax > ay)
 +    {
 +	d = ay - ax/2;
 +	while (1)
 +	{
 +	    PUTDOT(x,y,color);
 +	    if (x == fl->b.x) return;
 +	    if (d>=0)
 +	    {
 +		y += sy;
 +		d -= ax;
 +	    }
 +	    x += sx;
 +	    d += ay;
 +	}
 +    }
 +    else
 +    {
 +	d = ax - ay/2;
 +	while (1)
 +	{
 +	    PUTDOT(x, y, color);
 +	    if (y == fl->b.y) return;
 +	    if (d >= 0)
 +	    {
 +		x += sx;
 +		d -= ay;
 +	    }
 +	    y += sy;
 +	    d += ax;
 +	}
 +    }
 +}
 +
 +
 +//
 +// Clip lines, draw visible part sof lines.
 +//
 +void
 +AM_drawMline
 +( mline_t*	ml,
 +  int		color )
 +{
 +    static fline_t fl;
 +
 +    if (AM_clipMline(ml, &fl))
 +	AM_drawFline(&fl, color); // draws it on frame buffer using fb coords
 +}
 +
 +
 +
 +//
 +// Draws flat (floor/ceiling tile) aligned grid lines.
 +//
 +void AM_drawGrid(int color)
 +{
 +    fixed_t x, y;
 +    fixed_t start, end;
 +    mline_t ml;
 +
 +    // Figure out start of vertical gridlines
 +    start = m_x;
 +    if ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS))
 +	start += (MAPBLOCKUNITS<<FRACBITS)
 +	    - ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS));
 +    end = m_x + m_w;
 +
 +    // draw vertical gridlines
 +    ml.a.y = m_y;
 +    ml.b.y = m_y+m_h;
 +    for (x=start; x<end; x+=(MAPBLOCKUNITS<<FRACBITS))
 +    {
 +	ml.a.x = x;
 +	ml.b.x = x;
 +	AM_drawMline(&ml, color);
 +    }
 +
 +    // Figure out start of horizontal gridlines
 +    start = m_y;
 +    if ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS))
 +	start += (MAPBLOCKUNITS<<FRACBITS)
 +	    - ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS));
 +    end = m_y + m_h;
 +
 +    // draw horizontal gridlines
 +    ml.a.x = m_x;
 +    ml.b.x = m_x + m_w;
 +    for (y=start; y<end; y+=(MAPBLOCKUNITS<<FRACBITS))
 +    {
 +	ml.a.y = y;
 +	ml.b.y = y;
 +	AM_drawMline(&ml, color);
 +    }
 +
 +}
 +
 +//
 +// Determines visible lines, draws them.
 +// This is LineDef based, not LineSeg based.
 +//
 +void AM_drawWalls(void)
 +{
 +    int i;
 +    static mline_t l;
 +
 +    for (i=0;i<numlines;i++)
 +    {
 +	l.a.x = lines[i].v1->x;
 +	l.a.y = lines[i].v1->y;
 +	l.b.x = lines[i].v2->x;
 +	l.b.y = lines[i].v2->y;
 +	if (cheating || (lines[i].flags & ML_MAPPED))
 +	{
 +	    if ((lines[i].flags & LINE_NEVERSEE) && !cheating)
 +		continue;
 +	    if (!lines[i].backsector)
 +	    {
 +		AM_drawMline(&l, WALLCOLORS+lightlev);
 +	    }
 +	    else
 +	    {
 +		if (lines[i].special == 39)
 +		{ // teleporters
 +		    AM_drawMline(&l, WALLCOLORS+WALLRANGE/2);
 +		}
 +		else if (lines[i].flags & ML_SECRET) // secret door
 +		{
 +		    if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev);
 +		    else AM_drawMline(&l, WALLCOLORS+lightlev);
 +		}
 +		else if (lines[i].backsector->floorheight
 +			   != lines[i].frontsector->floorheight) {
 +		    AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
 +		}
 +		else if (lines[i].backsector->ceilingheight
 +			   != lines[i].frontsector->ceilingheight) {
 +		    AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
 +		}
 +		else if (cheating) {
 +		    AM_drawMline(&l, TSWALLCOLORS+lightlev);
 +		}
 +	    }
 +	}
 +	else if (plr->powers[pw_allmap])
 +	{
 +	    if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3);
 +	}
 +    }
 +}
 +
 +
 +//
 +// Rotation in 2D.
 +// Used to rotate player arrow line character.
 +//
 +void
 +AM_rotate
 +( fixed_t*	x,
 +  fixed_t*	y,
 +  angle_t	a )
 +{
 +    fixed_t tmpx;
 +
 +    tmpx =
 +	FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])
 +	- FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]);
 +
 +    *y   =
 +	FixedMul(*x,finesine[a>>ANGLETOFINESHIFT])
 +	+ FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]);
 +
 +    *x = tmpx;
 +}
 +
 +void
 +AM_drawLineCharacter
 +( mline_t*	lineguy,
 +  int		lineguylines,
 +  fixed_t	scale,
 +  angle_t	angle,
 +  int		color,
 +  fixed_t	x,
 +  fixed_t	y )
 +{
 +    int		i;
 +    mline_t	l;
 +
 +    for (i=0;i<lineguylines;i++)
 +    {
 +	l.a.x = lineguy[i].a.x;
 +	l.a.y = lineguy[i].a.y;
 +
 +	if (scale)
 +	{
 +	    l.a.x = FixedMul(scale, l.a.x);
 +	    l.a.y = FixedMul(scale, l.a.y);
 +	}
 +
 +	if (angle)
 +	    AM_rotate(&l.a.x, &l.a.y, angle);
 +
 +	l.a.x += x;
 +	l.a.y += y;
 +
 +	l.b.x = lineguy[i].b.x;
 +	l.b.y = lineguy[i].b.y;
 +
 +	if (scale)
 +	{
 +	    l.b.x = FixedMul(scale, l.b.x);
 +	    l.b.y = FixedMul(scale, l.b.y);
 +	}
 +
 +	if (angle)
 +	    AM_rotate(&l.b.x, &l.b.y, angle);
 +
 +	l.b.x += x;
 +	l.b.y += y;
 +
 +	AM_drawMline(&l, color);
 +    }
 +}
 +
 +void AM_drawPlayers(void)
 +{
 +    int		i;
 +    player_t*	p;
 +    static int 	their_colors[] = { GREENS, GRAYS, BROWNS, REDS };
 +    int		their_color = -1;
 +    int		color;
 +
 +    if (!netgame)
 +    {
 +	if (cheating)
 +	    AM_drawLineCharacter
 +		(cheat_player_arrow, arrlen(cheat_player_arrow), 0,
 +		 plr->mo->angle, WHITE, plr->mo->x, plr->mo->y);
 +	else
 +	    AM_drawLineCharacter
 +		(player_arrow, arrlen(player_arrow), 0, plr->mo->angle,
 +		 WHITE, plr->mo->x, plr->mo->y);
 +	return;
 +    }
 +
 +    for (i=0;i<MAXPLAYERS;i++)
 +    {
 +	their_color++;
 +	p = &players[i];
 +
 +	if ( (deathmatch && !singledemo) && p != plr)
 +	    continue;
 +
 +	if (!playeringame[i])
 +	    continue;
 +
 +	if (p->powers[pw_invisibility])
 +	    color = 246; // *close* to black
 +	else
 +	    color = their_colors[their_color];
 +
 +	AM_drawLineCharacter
 +	    (player_arrow, arrlen(player_arrow), 0, p->mo->angle,
 +	     color, p->mo->x, p->mo->y);
 +    }
 +
 +}
 +
 +void
 +AM_drawThings
 +( int	colors,
 +  int 	colorrange)
 +{
 +    int		i;
 +    mobj_t*	t;
 +
 +    for (i=0;i<numsectors;i++)
 +    {
 +	t = sectors[i].thinglist;
 +	while (t)
 +	{
 +	    AM_drawLineCharacter
 +		(thintriangle_guy, arrlen(thintriangle_guy),
 +		 16<<FRACBITS, t->angle, colors+lightlev, t->x, t->y);
 +	    t = t->snext;
 +	}
 +    }
 +}
 +
 +void AM_drawMarks(void)
 +{
 +    int i, fx, fy, w, h;
 +
 +    for (i=0;i<AM_NUMMARKPOINTS;i++)
 +    {
 +	if (markpoints[i].x != -1)
 +	{
 +	    //      w = SHORT(marknums[i]->width);
 +	    //      h = SHORT(marknums[i]->height);
 +	    w = 5; // because something's wrong with the wad, i guess
 +	    h = 6; // because something's wrong with the wad, i guess
 +	    fx = CXMTOF(markpoints[i].x);
 +	    fy = CYMTOF(markpoints[i].y);
 +	    if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)
 +		V_DrawPatch(fx, fy, marknums[i]);
 +	}
 +    }
 +
 +}
 +
 +void AM_drawCrosshair(int color)
 +{
 +    fb[(f_w*(f_h+1))/2] = color; // single point for now
 +
 +}
 +
 +void AM_Drawer (void)
 +{
 +    if (!automapactive) return;
 +
 +    AM_clearFB(BACKGROUND);
 +    if (grid)
 +	AM_drawGrid(GRIDCOLORS);
 +    AM_drawWalls();
 +    AM_drawPlayers();
 +    if (cheating==2)
 +	AM_drawThings(THINGCOLORS, THINGRANGE);
 +    AM_drawCrosshair(XHAIRCOLORS);
 +
 +    AM_drawMarks();
 +
 +    V_MarkRect(f_x, f_y, f_w, f_h);
 +
 +}
diff --cc src/strife/d_main.c
index 8dcbcf8c,00000000..e7ed01be
mode 100644,000000..100644
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@@ -1,1843 -1,0 +1,1697 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
 +//	plus functions to determine game mode (shareware, registered),
 +//	parse command line parameters, configure game parameters (turbo),
 +//	and call the startup functions.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <ctype.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "config.h"
 +#include "deh_main.h"
 +#include "doomdef.h"
 +#include "doomstat.h"
 +
 +#include "dstrings.h"
 +#include "doomfeatures.h"
 +#include "sounds.h"
 +
 +#include "d_iwad.h"
 +
 +#include "z_zone.h"
++#include "w_main.h"
 +#include "w_wad.h"
- #include "w_merge.h"
 +#include "s_sound.h"
 +#include "v_video.h"
 +
 +#include "f_finale.h"
 +#include "f_wipe.h"
 +
 +#include "m_argv.h"
 +#include "m_config.h"
 +#include "m_controls.h"
 +#include "m_misc.h"
 +#include "m_menu.h"
 +#include "p_saveg.h"
 +
 +#include "i_endoom.h"
 +#include "i_joystick.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "i_swap.h"
 +
 +#include "g_game.h"
 +
 +#include "hu_stuff.h"
 +#include "wi_stuff.h"
 +#include "st_stuff.h"
 +#include "am_map.h"
 +#include "net_client.h"
 +#include "net_dedicated.h"
 +#include "net_query.h"
 +
 +#include "p_setup.h"
 +#include "r_local.h"
 +
 +
 +#include "d_main.h"
 +
 +//
 +// D-DoomLoop()
 +// Not a globally visible function,
 +//  just included for source reference,
 +//  called by D_DoomMain, never exits.
 +// Manages timing and IO,
 +//  calls all ?_Responder, ?_Ticker, and ?_Drawer,
 +//  calls I_GetTime, I_StartFrame, and I_StartTic
 +//
 +void D_DoomLoop (void);
 +
 +// Location where savegames are stored
 +
 +char *          savegamedir;
 +
 +// location of IWAD and WAD files
 +
 +char *          iwadfile;
 +
 +
 +boolean		devparm;	// started game with -devparm
 +boolean         nomonsters;	// checkparm of -nomonsters
 +boolean         respawnparm;	// checkparm of -respawn
 +boolean         fastparm;	// checkparm of -fast
 +
 +boolean		singletics = false; // debug flag to cancel adaptiveness
 +
 +
 +//extern int soundVolume;
 +//extern  int	sfxVolume;
 +//extern  int	musicVolume;
 +
 +extern  boolean	inhelpscreens;
 +
 +skill_t		startskill;
 +int             startepisode;
 +int		startmap;
 +boolean		autostart;
 +int             startloadgame;
 +
 +FILE*		debugfile;
 +
 +boolean		advancedemo;
 +
 +// Store demo, do not accept any inputs
 +// haleyjd [STRIFE] Unused.
 +//boolean         storedemo;
 +
 +
 +char		wadfile[1024];		// primary wad file
 +char		mapdir[1024];           // directory of development maps
 +
 +int             show_endoom = 1;
 +
 +
 +void D_CheckNetGame (void);
 +void D_ProcessEvents (void);
 +void G_BuildTiccmd (ticcmd_t* cmd);
 +void D_DoAdvanceDemo (void);
 +
 +
 +//
 +// D_ProcessEvents
 +// Send all the events of the given timestamp down the responder chain
 +//
 +void D_ProcessEvents (void)
 +{
 +    event_t*	ev;
 +
 +    // haleyjd 08/22/2010: [STRIFE] there is no such thing as a "store demo"
 +    // version of Strife
 +
 +    // IF STORE DEMO, DO NOT ACCEPT INPUT
 +    //if (storedemo)
 +    //    return;
 +
 +    while ((ev = D_PopEvent()) != NULL)
 +    {
 +        if (M_Responder (ev))
 +            continue;               // menu ate the event
 +        G_Responder (ev);
 +    }
 +}
 +
 +
 +
 +
 +//
 +// D_Display
 +//  draw current display, possibly wiping it from the previous
 +//
 +// wipegamestate can be set to -1 to force a wipe on the next draw
 +//
 +// haleyjd 08/23/10: [STRIFE]:
 +// * Changes to eliminate intermission and change timing of screenwipe
 +// * 9/01/10: Added ST_DrawExternal and popupactivestate static variable
 +//
 +gamestate_t     wipegamestate = GS_DEMOSCREEN;
 +extern  boolean setsizeneeded;
 +extern  int             showMessages;
 +void R_ExecuteSetViewSize (void);
 +
 +void D_Display (void)
 +{
 +    static  boolean             viewactivestate = false;
 +    static  boolean             menuactivestate = false;
 +    static  boolean             inhelpscreensstate = false;
 +    static  boolean             popupactivestate = false; // [STRIFE]
 +    static  boolean             fullscreen = false;
 +    static  gamestate_t         oldgamestate = -1;
 +    static  int                 borderdrawcount;
 +    int                         nowtime;
 +    int                         tics;
 +    int                         wipestart;
 +    int                         y;
 +    boolean                     done;
 +    boolean                     wipe;
 +    boolean                     redrawsbar;
 +
 +    if (nodrawers)
 +        return;                    // for comparative timing / profiling
 +
 +    redrawsbar = false;
 +
 +    // change the view size if needed
 +    if (setsizeneeded)
 +    {
 +        R_ExecuteSetViewSize ();
 +        oldgamestate = -1;                      // force background redraw
 +        borderdrawcount = 3;
 +    }
 +
 +    // save the current screen if about to wipe
 +    if (gamestate != wipegamestate)
 +    {
 +        wipe = true;
 +        wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
 +    }
 +    else
 +        wipe = false;
 +
 +    if (gamestate == GS_LEVEL && gametic)
 +        HU_Erase();
 +
 +    // do buffered drawing
 +    switch (gamestate)
 +    {
 +    case GS_LEVEL:
 +        if (!gametic)
 +            break;
 +        if (automapactive)
 +            AM_Drawer ();
 +        if (wipe || (viewheight != 200 && fullscreen) )
 +            redrawsbar = true;
 +        // haleyjd 08/29/10: [STRIFE] Always redraw sbar if menu is/was active
 +        if (menuactivestate || (inhelpscreensstate && !inhelpscreens))
 +            redrawsbar = true;              // just put away the help screen
 +        ST_Drawer (viewheight == 200, redrawsbar );
 +        fullscreen = viewheight == 200;
 +        break;
 +
 +     // haleyjd 08/23/2010: [STRIFE] No intermission
 +     /*
 +     case GS_INTERMISSION:
 +         WI_Drawer ();
 +         break;
 +     */
 +    case GS_FINALE:
 +        F_Drawer ();
 +        break;
 +
 +    case GS_DEMOSCREEN:
 +        D_PageDrawer ();
 +        break;
 +    }
 +
 +    // draw buffered stuff to screen
 +    I_UpdateNoBlit ();
 +
 +    // draw the view directly
 +    if (gamestate == GS_LEVEL && !automapactive && gametic)
 +        R_RenderPlayerView (&players[displayplayer]);
 +
 +    if (gamestate == GS_LEVEL && gametic)
 +    {
 +        HU_Drawer ();
 +        if(ST_DrawExternal())
 +            popupactivestate = true;
 +        else if(popupactivestate)
 +        {
 +            popupactivestate = false;
 +            menuactivestate = 1;
 +        }
 +    }
 +
 +    // clean up border stuff
 +    if (gamestate != oldgamestate && gamestate != GS_LEVEL)
 +        I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE));
 +
 +    // see if the border needs to be initially drawn
 +    if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL)
 +    {
 +        viewactivestate = false;        // view was not active
 +        R_FillBackScreen ();    // draw the pattern into the back screen
 +    }
 +
 +    // see if the border needs to be updated to the screen
 +    if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320)
 +    {
 +        if (menuactive || menuactivestate || !viewactivestate)
 +        {
 +            borderdrawcount = 3;
 +            popupactivestate = false;
 +        }
 +        if (borderdrawcount)
 +        {
 +            R_DrawViewBorder ();    // erase old menu stuff
 +            borderdrawcount--;
 +        }
 +
 +    }
 +
 +    if (testcontrols)
 +    {
 +        // Box showing current mouse speed
 +
 +        G_DrawMouseSpeedBox();
 +    }
 +
 +    menuactivestate = menuactive;
 +    viewactivestate = viewactive;
 +    inhelpscreensstate = inhelpscreens;
 +    oldgamestate = wipegamestate = gamestate;
 +
 +    // draw pause pic
 +    if (paused)
 +    {
 +        if (automapactive)
 +            y = 4;
 +        else
 +            y = viewwindowy+4;
 +        V_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, y,
 +                          W_CacheLumpName (DEH_String("M_PAUSE"), PU_CACHE));
 +    }
 +
 +
 +    // menus go directly to the screen
 +    M_Drawer ();          // menu is drawn even on top of everything
 +    NetUpdate ();         // send out any new accumulation
 +
 +
 +    // normal update
 +    if (!wipe)
 +    {
 +        I_FinishUpdate ();              // page flip or blit buffer
 +        return;
 +    }
 +
 +    // wipe update
 +    wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
 +
 +    wipestart = I_GetTime () - 1;
 +
 +    do
 +    {
 +        do
 +        {
 +            nowtime = I_GetTime ();
 +            tics = nowtime - wipestart;
 +            I_Sleep(1);
 +        } while (tics < 3); // haleyjd 08/23/2010: [STRIFE] Changed from == 0 to < 3
 +
 +        // haleyjd 08/26/10: [STRIFE] Changed to use ColorXForm wipe.
 +        wipestart = nowtime;
 +        done = wipe_ScreenWipe(wipe_ColorXForm
 +                               , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics);
 +        I_UpdateNoBlit ();
 +        M_Drawer ();                            // menu is drawn even on top of wipes
 +        I_FinishUpdate ();                      // page flip or blit buffer
 +    } while (!done);
 +}
 +
 +//
 +// Add configuration file variable bindings.
 +//
 +
 +void D_BindVariables(void)
 +{
 +    int i;
 +
 +    M_ApplyPlatformDefaults();
 +
 +    I_BindVideoVariables();
 +    I_BindJoystickVariables();
 +    I_BindSoundVariables();
 +
 +    M_BindBaseControls();
 +    M_BindWeaponControls();
 +    M_BindMapControls();
 +    M_BindMenuControls();
 +    M_BindStrifeControls(); // haleyjd 09/01/10: [STRIFE]
++    M_BindChatControls(MAXPLAYERS);
++
++    key_multi_msgplayer[0] = HUSTR_KEYGREEN;
++    key_multi_msgplayer[1] = HUSTR_KEYINDIGO;
++    key_multi_msgplayer[2] = HUSTR_KEYBROWN;
++    key_multi_msgplayer[3] = HUSTR_KEYRED;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    NET_BindVariables();
 +#endif
 +
 +    // haleyjd 08/29/10: [STRIFE]
 +    // * Added voice volume
 +    // * Added back flat
 +    M_BindVariable("mouse_sensitivity",      &mouseSensitivity);
 +    M_BindVariable("sfx_volume",             &sfxVolume);
 +    M_BindVariable("music_volume",           &musicVolume);
 +    M_BindVariable("voice_volume",           &voiceVolume);
 +    M_BindVariable("show_messages",          &showMessages);
 +    M_BindVariable("screenblocks",           &screenblocks);
 +    M_BindVariable("detaillevel",            &detailLevel);
 +    M_BindVariable("snd_channels",           &snd_channels);
 +    M_BindVariable("vanilla_savegame_limit", &vanilla_savegame_limit);
 +    M_BindVariable("vanilla_demo_limit",     &vanilla_demo_limit);
 +    M_BindVariable("show_endoom",            &show_endoom);
 +    M_BindVariable("back_flat",              &back_flat);
 +
 +    // Multiplayer chat macros
 +
 +    for (i=0; i<10; ++i)
 +    {
 +        char buf[12];
 +
 +        sprintf(buf, "chatmacro%i", i);
 +        M_BindVariable(buf, &chat_macros[i]);
 +    }
 +}
 +
 +//
 +// D_GrabMouseCallback
 +//
 +// Called to determine whether to grab the mouse pointer
 +//
 +
 +boolean D_GrabMouseCallback(void)
 +{
 +    // Drone players don't need mouse focus
 +
 +    if (drone)
 +        return false;
 +
 +    // when menu is active or game is paused, release the mouse
 +
 +    if (menuactive || paused)
 +        return false;
 +
 +    // only grab mouse when playing levels (but not demos)
 +
 +    return (gamestate == GS_LEVEL) && !demoplayback;
 +}
 +
 +//
 +//  D_DoomLoop
 +//
 +//  haleyjd 08/23/10: [STRIFE] Verified unmodified.
 +//
 +extern  boolean         demorecording;
 +
 +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);
 +    I_InitGraphics();
 +    I_EnableLoadingDisk();
 +    I_SetGrabMouseCallback(D_GrabMouseCallback);
 +
 +    V_RestoreBuffer();
 +    R_ExecuteSetViewSize();
 +
 +    D_StartGameLoop();
 +
 +    if (testcontrols)
 +    {
 +        wipegamestate = gamestate;
 +    }
 +
 +    while (1)
 +    {
 +	// frame syncronous IO operations
 +	I_StartFrame ();
 +
 +	// process one or more tics
 +	if (singletics)
 +	{
 +	    I_StartTic ();
 +	    D_ProcessEvents ();
 +	    G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
 +	    if (advancedemo)
 +		D_DoAdvanceDemo ();
 +	    M_Ticker ();
 +	    G_Ticker ();
 +	    gametic++;
 +	    maketic++;
 +	}
 +	else
 +	{
 +	    TryRunTics (); // will run at least one tic
 +	}
 +
 +	S_UpdateSounds (players[consoleplayer].mo);// move positional sounds
 +
 +	// Update display, next frame, with current state.
 +        if (screenvisible)
 +            D_Display ();
 +    }
 +}
 +
 +
 +
 +//
 +//  DEMO LOOP
 +//
 +int             demosequence;
 +int             pagetic;
 +char                    *pagename;
 +
 +
 +//
 +// D_PageTicker
 +// Handles timing for warped projection
 +//
 +// haleyjd 08/22/2010: [STRIFE] verified unmodified
 +//
 +void D_PageTicker (void)
 +{
 +    if (--pagetic < 0)
 +	D_AdvanceDemo ();
 +}
 +
 +
 +
 +//
 +// D_PageDrawer
 +//
 +// haleyjd 08/22/2010: [STRIFE] verified unmodified
 +//
 +void D_PageDrawer (void)
 +{
 +    V_DrawPatch (0, 0, W_CacheLumpName(pagename, PU_CACHE));
 +}
 +
 +
 +//
 +// D_AdvanceDemo
 +// Called after each demo or intro demosequence finishes
 +//
 +// haleyjd 08/22/2010: [STRIFE] verified unmodified
 +//
 +void D_AdvanceDemo (void)
 +{
 +    advancedemo = true;
 +}
 +
 +
 +//
 +// This cycles through the demo sequences.
 +// FIXME - version dependend demo numbers?
 +//
 +void D_DoAdvanceDemo (void)
 +{
 +    players[consoleplayer].playerstate = PST_LIVE;  // not reborn
 +    advancedemo = false;
 +    usergame = false;               // no save / end game here
 +    paused = false;
 +    gameaction = ga_nothing;
 +
 +    if (gamemode == retail && gameversion != exe_chex)
 +      demosequence = (demosequence+1)%7;
 +    else
 +      demosequence = (demosequence+1)%6;
 +
 +    switch (demosequence)
 +    {
 +      case 0:
 +	if ( gamemode == commercial )
 +	    pagetic = TICRATE * 11;
 +	else
 +	    pagetic = 170;
 +	gamestate = GS_DEMOSCREEN;
 +	pagename = DEH_String("TITLEPIC");
 +	if ( gamemode == commercial )
 +	  S_StartMusic(mus_logo);   // villsa [STRIFE] TODO - fix music
 +	else
 +	  S_StartMusic (mus_intro);
 +	break;
 +      case 1:
 +	G_DeferedPlayDemo(DEH_String("demo1"));
 +	break;
 +      case 2:
 +	pagetic = 200;
 +	gamestate = GS_DEMOSCREEN;
 +	pagename = DEH_String("CREDIT");
 +	break;
 +      case 3:
 +	G_DeferedPlayDemo(DEH_String("demo2"));
 +	break;
 +      case 4:
 +	gamestate = GS_DEMOSCREEN;
 +	if ( gamemode == commercial)
 +	{
 +	    pagetic = TICRATE * 11;
 +	    pagename = DEH_String("TITLEPIC");
 +	    S_StartMusic(mus_logo); // villsa [STRIFE] TODO - fix music
 +	}
 +	else
 +	{
 +	    pagetic = 200;
 +
 +	    if ( gamemode == retail )
 +	      pagename = DEH_String("CREDIT");
 +	    else
 +	      pagename = DEH_String("HELP2");
 +	}
 +	break;
 +      case 5:
 +	G_DeferedPlayDemo(DEH_String("demo3"));
 +	break;
 +        // THE DEFINITIVE DOOM Special Edition demo
 +      case 6:
 +	G_DeferedPlayDemo(DEH_String("demo4"));
 +	break;
 +    }
 +}
 +
 +
 +
 +//
 +// D_StartTitle
 +//
 +void D_StartTitle (void)
 +{
 +    // STRIFE-FIXME: some poorly understood changes are pending here
 +    gameaction = ga_nothing;
 +    demosequence = -1;
 +    D_AdvanceDemo ();
 +}
 +
 +// Strings for dehacked replacements of the startup banner
 +//
 +// These are from the original source: some of them are perhaps
 +// not used in any dehacked patches
 +
 +static char *banners[] =
 +{
 +    // strife1.wad:
 +
 +    "                      "
 +    "STRIFE:  Quest for the Sigil v1.31"
 +    "                                 "
 +};
 +
 +//
 +// Get game name: if the startup banner has been replaced, use that.
 +// Otherwise, use the name given
 +//
 +
 +static char *GetGameName(char *gamename)
 +{
 +    size_t i;
 +    char *deh_sub;
 +
 +    for (i=0; i<arrlen(banners); ++i)
 +    {
 +        // Has the banner been replaced?
 +
 +        deh_sub = DEH_String(banners[i]);
 +
 +        if (deh_sub != banners[i])
 +        {
 +            // Has been replaced
 +            // We need to expand via printf to include the Doom version
 +            // number
 +            // We also need to cut off spaces to get the basic name
 +
 +            gamename = Z_Malloc(strlen(deh_sub) + 10, PU_STATIC, 0);
 +            sprintf(gamename, deh_sub, DOOM_VERSION / 100, DOOM_VERSION % 100);
 +
 +            while (gamename[0] != '\0' && isspace(gamename[0]))
 +                strcpy(gamename, gamename+1);
 +
 +            while (gamename[0] != '\0' && isspace(gamename[strlen(gamename)-1]))
 +                gamename[strlen(gamename) - 1] = '\0';
 +
 +            return gamename;
 +        }
 +    }
 +
 +    return gamename;
 +}
 +
 +//
 +// Find out what version of Doom is playing.
 +//
 +
 +void D_IdentifyVersion(void)
 +{
 +    // gamemission is set up by the D_FindIWAD function.  But if
 +    // we specify '-iwad', we have to identify using
 +    // IdentifyIWADByName.  However, if the iwad does not match
 +    // any known IWAD name, we may have a dilemma.  Try to
 +    // identify by its contents.
 +
 +    if (gamemission == none)
 +    {
 +        unsigned int i;
 +
 +        for (i=0; i<numlumps; ++i)
 +        {
 +            if (!strncasecmp(lumpinfo[i].name, "MAP01", 8))
 +            {
 +                gamemission = doom2;
 +                break;
 +            }
 +            else if (!strncasecmp(lumpinfo[i].name, "E1M1", 8))
 +            {
 +                gamemission = doom;
 +                break;
 +            }
 +        }
 +
 +        if (gamemission == none)
 +        {
 +            // Still no idea.  I don't think this is going to work.
 +
 +            I_Error("Unknown or invalid IWAD file.");
 +        }
 +    }
 +
 +    // Make sure gamemode is set up correctly
 +
 +    if (gamemission == doom)
 +    {
 +        // Doom 1.  But which version?
 +
 +        if (W_CheckNumForName("E4M1") > 0)
 +        {
 +            // Ultimate Doom
 +
 +            gamemode = retail;
 +        }
 +        else if (W_CheckNumForName("E3M1") > 0)
 +        {
 +            gamemode = registered;
 +        }
 +        else
 +        {
 +            gamemode = shareware;
 +        }
 +    }
 +    else
 +    {
 +        // Doom 2 of some kind.
 +
 +        gamemode = commercial;
 +    }
 +}
 +
 +#if 0
 +//
 +// DoTimeBomb
 +//
 +// haleyjd 08/23/2010: [STRIFE] New function
 +// Code with no xrefs; probably left over from a private alpha or beta.
 +// Translated here because it explains what the SERIAL lump was meant to do.
 +//
 +void DoTimeBomb(void)
 +{
 +    dosdate_t date;
 +    char *serial;
 +    int serialnum;
 +    int serial_year;
 +    int serial_month;
 +
 +    serial = W_CacheLumpName("serial", PU_CACHE);
 +    serialnum = atoi(serial);
 +
 +    // Rogue, much like Governor Mourel, were lousy liars. These deceptive
 +    // error messages are pretty low :P
 +    dos_getdate(&date);
 +    if(date.year > 1996 || date.day > 15 && date.month > 4)
 +        I_Error("Data error! Corrupted WAD File!");
 +    serial_year = serialnum / 10000;
 +    serial_month = serialnum / 100 - 100 * serial_year;
 +    if(date.year < serial_year ||
 +       date.day < serialnum - 100 * serial_month - 10000 * serial_year &&
 +       date.month < serial_month)
 +       I_Error("Bad wadfile");
 +}
 +#endif
 +
 +// Set the gamedescription string
 +
 +void D_SetGameDescription(void)
 +{
 +    gamedescription = GetGameName("Strife: Quest for the Sigil");
 +}
 +
 +static void SetSaveGameDir(char *iwad_filename)
 +{
 +    char *sep;
 +    char *basefile;
 +
 +    // Extract the base filename
 +
 +    sep = strrchr(iwad_filename, DIR_SEPARATOR);
 +
 +    if (sep == NULL)
 +    {
 +        basefile = iwad_filename;
 +    }
 +    else
 +    {
 +        basefile = sep + 1;
 +    }
 +
 +    // ~/.chocolate-doom/savegames/
 +
 +    savegamedir = Z_Malloc(strlen(configdir) + 30, PU_STATIC, 0);
 +    sprintf(savegamedir, "%ssavegames%c", configdir,
 +                         DIR_SEPARATOR);
 +
 +    M_MakeDirectory(savegamedir);
 +
 +    // eg. ~/.chocolate-doom/savegames/doom2.wad/
 +
 +    sprintf(savegamedir + strlen(savegamedir), "%s%c",
 +            basefile, DIR_SEPARATOR);
 +
 +    M_MakeDirectory(savegamedir);
 +}
 +
 +// Check if the IWAD file is the Chex Quest IWAD.
 +// Returns true if this is chex.wad.
 +// haleyjd 08/23/10: there is no Chex Quest based on Strife,
 +// though I must admit that makes an intriguing idea....
 +/*
 +static boolean CheckChex(char *iwadname)
 +{
 +    char *chex_iwadname = "chex.wad";
 +
 +    return (strlen(iwadname) > strlen(chex_iwadname)
 +     && !strcasecmp(iwadname + strlen(iwadname) - strlen(chex_iwadname),
 +                    chex_iwadname));
 +}
 +*/
 +
 +//      print title for every printed line
 +char            title[128];
 +
-
 +static boolean D_AddFile(char *filename)
 +{
 +    wad_file_t *handle;
 +
 +    printf(" adding %s\n", filename);
 +    handle = W_AddFile(filename);
 +
 +    return handle != NULL;
 +}
 +
 +// Copyright message banners
 +// Some dehacked mods replace these.  These are only displayed if they are
 +// replaced by dehacked.
 +// haleyjd 08/22/2010: [STRIFE] altered to match strings from binary
 +static char *copyright_banners[] =
 +{
 +    "===========================================================================\n"
 +    "ATTENTION:  This version of STRIFE has extra files added to it.\n"
 +    "        You will not receive technical support for modified games.\n"
 +    "===========================================================================\n",
 +
 +    "===========================================================================\n"
 +    "             This version is NOT SHAREWARE, do not distribute!\n"
 +    "         Please report software piracy to the SPA: 1-800-388-PIR8\n"
 +    "===========================================================================\n",
 +
 +    "===========================================================================\n"
 +    "                                Shareware!\n"
 +    "===========================================================================\n"
 +};
 +
 +// Prints a message only if it has been modified by dehacked.
 +
 +void PrintDehackedBanners(void)
 +{
 +    size_t i;
 +
 +    for (i=0; i<arrlen(copyright_banners); ++i)
 +    {
 +        char *deh_s;
 +
 +        deh_s = DEH_String(copyright_banners[i]);
 +
 +        if (deh_s != copyright_banners[i])
 +        {
 +            printf("%s", deh_s);
 +
 +            // Make sure the modified banner always ends in a newline character.
 +            // If it doesn't, add a newline.  This fixes av.wad.
 +
 +            if (deh_s[strlen(deh_s) - 1] != '\n')
 +            {
 +                printf("\n");
 +            }
 +        }
 +    }
 +}
 +
 +static struct
 +{
 +    char *description;
 +    char *cmdline;
 +    GameVersion_t version;
 +} gameversions[] = {
 +    {"Doom 1.9",             "1.9",        exe_doom_1_9},
 +    {"Ultimate Doom",        "ultimate",   exe_ultimate},
 +    {"Final Doom",           "final",      exe_final},
 +    {"Chex Quest",           "chex",       exe_chex},
 +    { NULL,                  NULL,         0},
 +};
 +
 +// Initialize the game version
 +
 +static void InitGameVersion(void)
 +{
 +    int p;
 +    int i;
 +
 +    //!
 +    // @arg <version>
 +    // @category compat
 +    //
 +    // Emulate a specific version of Doom.  Valid values are "1.9",
 +    // "ultimate" and "final".
 +    //
 +
 +    p = M_CheckParm("-gameversion");
 +
 +    if (p > 0)
 +    {
 +        for (i=0; gameversions[i].description != NULL; ++i)
 +        {
 +            if (!strcmp(myargv[p+1], gameversions[i].cmdline))
 +            {
 +                gameversion = gameversions[i].version;
 +                break;
 +            }
 +        }
 +
 +        if (gameversions[i].description == NULL)
 +        {
 +            printf("Supported game versions:\n");
 +
 +            for (i=0; gameversions[i].description != NULL; ++i)
 +            {
 +                printf("\t%s (%s)\n", gameversions[i].cmdline,
 +                        gameversions[i].description);
 +            }
 +
 +            I_Error("Unknown game version '%s'", myargv[p+1]);
 +        }
 +    }
 +    else
 +    {
 +        // Determine automatically
 +
 +        // haleyjd 08/23/10: Removed Chex mode, as it is irrelevant to Strife
 +        if (gamemode == shareware || gamemode == registered)
 +        {
 +            // original
 +
 +            gameversion = exe_doom_1_9;
 +        }
 +        else if (gamemode == retail)
 +        {
 +            gameversion = exe_ultimate;
 +        }
 +        else if (gamemode == commercial)
 +        {
 +            if (gamemission == doom2)
 +            {
 +                gameversion = exe_doom_1_9;
 +            }
 +            else
 +            {
 +                // Final Doom: tnt or plutonia
 +
 +                gameversion = exe_final;
 +            }
 +        }
 +    }
 +
 +    // The original exe does not support retail - 4th episode not supported
 +
 +    if (gameversion < exe_ultimate && gamemode == retail)
 +    {
 +        gamemode = registered;
 +    }
 +
 +    // EXEs prior to the Final Doom exes do not support Final Doom.
 +
 +    if (gameversion < exe_final && gamemode == commercial)
 +    {
 +        gamemission = doom2;
 +    }
 +}
 +
 +void PrintGameVersion(void)
 +{
 +    int i;
 +
 +    for (i=0; gameversions[i].description != NULL; ++i)
 +    {
 +        if (gameversions[i].version == gameversion)
 +        {
 +            printf("Emulating the behavior of the "
 +                   "'%s' executable.\n", gameversions[i].description);
 +            break;
 +        }
 +    }
 +}
 +
 +// Load the Chex Quest dehacked file, if we are in Chex mode.
 +// haleyjd 08/23/2010: Removed, as irrelevant to Strife.
 +/*
 +static void LoadChexDeh(void)
 +{
 +    char *chex_deh;
 +
 +    if (gameversion == exe_chex)
 +    {
 +        chex_deh = D_FindWADByName("chex.deh");
 +
 +        if (chex_deh == NULL)
 +        {
 +            I_Error("Unable to find Chex Quest dehacked file (chex.deh).\n"
 +                    "The dehacked file is required in order to emulate\n"
 +                    "chex.exe correctly.  It can be found in your nearest\n"
 +                    "/idgames repository mirror at:\n\n"
 +                    "   utils/exe_edit/patches/chexdeh.zip");
 +        }
 +
 +        if (!DEH_LoadFile(chex_deh))
 +        {
 +            I_Error("Failed to load chex.deh needed for emulating chex.exe.");
 +        }
 +    }
 +}
 +*/
 +
 +// Function called at exit to display the ENDOOM screen
 +
 +static void D_Endoom(void)
 +{
 +    byte *endoom;
 +
 +    // Don't show ENDOOM if we have it disabled, or we're running
 +    // in screensaver or control test mode.
 +
 +    if (!show_endoom || screensaver_mode || M_CheckParm("-testcontrols") > 0)
 +    {
 +        return;
 +    }
 +
 +    // haleyjd 08/27/10: [STRIFE] ENDOOM -> ENDSTRF
 +    endoom = W_CacheLumpName(DEH_String("ENDSTRF"), PU_STATIC);
 +
 +    I_Endoom(endoom);
 +}
 +
 +//=============================================================================
 +//
 +// haleyjd: Chocolate Strife Specifics
 +//
 +// None of the code in here is from the original executable, but is needed for
 +// other reasons.
 +
 +//
 +// D_PatchClipCallback
 +//
 +// haleyjd 08/28/10: Clip patches to the framebuffer without errors.
 +// Returns false if V_DrawPatch should return without drawing.
 +//
 +static boolean D_PatchClipCallback(patch_t *patch, int x, int y)
 +{
 +    // note that offsets were already accounted for in V_DrawPatch
 +    return (x >= 0 && y >= 0
 +            && x + SHORT(patch->width) <= SCREENWIDTH
 +            && y + SHORT(patch->height) <= SCREENHEIGHT);
 +}
 +
 +//
 +// D_InitChocoStrife
 +//
 +// haleyjd 08/28/10: Take care of some Strife-specific initialization
 +// that is necessitated by Chocolate Doom issues, such as setting global
 +// callbacks.
 +//
 +static void D_InitChocoStrife(void)
 +{
 +    // set the V_DrawPatch clipping callback
 +    V_SetPatchClipCallback(D_PatchClipCallback);
 +}
 +
 +//
 +// End Chocolate Strife Specifics
 +//
 +//=============================================================================
 +
 +//
 +// D_DoomMain
 +//
 +void D_DoomMain (void)
 +{
 +    int             p;
 +    char            file[256];
 +    char            demolumpname[9];
 +
 +    I_AtExit(D_Endoom, false);
 +
 +    M_FindResponseFile ();
 +
 +    // print banner
 +
 +    I_PrintBanner(PACKAGE_STRING);
 +
-     printf (DEH_String("Z_Init: Init zone memory allocation daemon. \n"));
++    DEH_printf("Z_Init: Init zone memory allocation daemon. \n");
 +    Z_Init ();
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    //!
 +    // @category net
 +    //
 +    // Start a dedicated server, routing packets but not participating
 +    // in the game itself.
 +    //
 +
 +    if (M_CheckParm("-dedicated") > 0)
 +    {
 +        printf("Dedicated server mode.\n");
 +        NET_DedicatedServer();
 +
 +        // Never returns
 +    }
 +
 +    //!
 +    // @arg <address>
 +    // @category net
 +    //
 +    // Query the status of the server running on the given IP
 +    // address.
 +    //
 +
 +    p = M_CheckParm("-query");
 +
 +    if (p > 0)
 +    {
 +        NET_QueryAddress(myargv[p+1]);
 +    }
 +
 +    //!
 +    // @category net
 +    //
 +    // Search the local LAN for running servers.
 +    //
 +
 +    if (M_CheckParm("-search"))
 +        NET_LANQuery();
 +
 +#endif
 +
 +#ifdef FEATURE_DEHACKED
 +    printf("DEH_Init: Init Dehacked support.\n");
 +    DEH_Init();
 +#endif
 +
 +    iwadfile = D_FindIWAD(IWAD_MASK_STRIFE, &gamemission);
 +
 +    // None found?
 +
 +    if (iwadfile == NULL)
 +    {
 +        I_Error("Game mode indeterminate.  No IWAD file was found.  Try\n"
 +                "specifying one with the '-iwad' command line parameter.\n");
 +    }
 +
 +    modifiedgame = false;
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Disable monsters.
 +    //
 +
 +    nomonsters = M_CheckParm ("-nomonsters");
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Monsters respawn after being killed.
 +    //
 +
 +    respawnparm = M_CheckParm ("-respawn");
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Monsters move faster.
 +    //
 +
 +    fastparm = M_CheckParm ("-fast");
 +
 +    //!
 +    // @vanilla
 +    //
 +    // Developer mode.  F1 saves a screenshot in the current working
 +    // directory.
 +    //
 +
 +    devparm = M_CheckParm ("-devparm");
 +
 +    I_DisplayFPSDots(devparm);
 +
 +    //!
 +    // @category net
 +    // @vanilla
 +    //
 +    // Start a deathmatch game.
 +    //
 +
 +    if (M_CheckParm ("-deathmatch"))
 +	deathmatch = 1;
 +
 +    //!
 +    // @category net
 +    // @vanilla
 +    //
 +    // Start a deathmatch 2.0 game.  Weapons do not stay in place and
 +    // all items respawn after 30 seconds.
 +    //
 +
 +    if (M_CheckParm ("-altdeath"))
 +	deathmatch = 2;
 +
 +    if (devparm)
- 	printf(DEH_String(D_DEVSTR));
++	DEH_printf(D_DEVSTR);
 +
 +    // find which dir to use for config files
 +
 +#ifdef _WIN32
 +
 +    //!
 +    // @platform windows
 +    // @vanilla
 +    //
 +    // Save configuration data and savegames in c:\doomdata,
 +    // allowing play from CD.
 +    //
 +
 +    if (M_CheckParm("-cdrom") > 0)
 +    {
 +        printf(D_CDROM);
 +
 +        // haleyjd 08/22/2010: [STRIFE] Use strife.cd folder for -cdrom
 +        M_SetConfigDir("c:\\strife.cd\\");
 +    }
 +    else
 +#endif
 +    {
 +        // Auto-detect the configuration dir.
 +
 +        M_SetConfigDir(NULL);
 +    }
 +
 +    //!
 +    // @arg <x>
 +    // @vanilla
 +    //
 +    // Turbo mode.  The player's speed is multiplied by x%.  If unspecified,
 +    // x defaults to 200.  Values are rounded up to 10 and down to 400.
 +    //
 +
 +    if ( (p=M_CheckParm ("-turbo")) )
 +    {
 +	int     scale = 200;
 +	extern int forwardmove[2];
 +	extern int sidemove[2];
 +
 +	if (p<myargc-1)
 +	    scale = atoi (myargv[p+1]);
 +	if (scale < 10)
 +	    scale = 10;
 +	if (scale > 400)
 +	    scale = 400;
- 	printf (DEH_String("turbo scale: %i%%\n"),scale);
++        DEH_printf("turbo scale: %i%%\n", scale);
 +	forwardmove[0] = forwardmove[0]*scale/100;
 +	forwardmove[1] = forwardmove[1]*scale/100;
 +	sidemove[0] = sidemove[0]*scale/100;
 +	sidemove[1] = sidemove[1]*scale/100;
 +    }
 +
 +    // init subsystems
-     printf(DEH_String("V_Init: allocate screens.\n"));
-     V_Init();
++    DEH_printf("V_Init: allocate screens.\n");
++    V_Init ();
 +
 +    // Load configuration files before initialising other subsystems.
 +    // haleyjd 08/22/2010: [STRIFE] - use strife.cfg
-     printf(DEH_String("M_LoadDefaults: Load system defaults.\n"));
++    DEH_printf("M_LoadDefaults: Load system defaults.\n");
 +    M_SetConfigFilenames("strife.cfg", PROGRAM_PREFIX "strife.cfg");
 +    D_BindVariables();
 +    M_LoadDefaults();
 +
 +    // Save configuration at exit.
 +    I_AtExit(M_SaveDefaults, false);
 +
-     printf (DEH_String("W_Init: Init WADfiles.\n"));
++    DEH_printf("W_Init: Init WADfiles.\n");
 +    D_AddFile(iwadfile);
-
- #ifdef FEATURE_WAD_MERGE
-
-     // Merged PWADs are loaded first, because they are supposed to be
-     // modified IWADs.
-
-     //!
-     // @arg <files>
-     // @category mod
-     //
-     // Simulates the behavior of deutex's -merge option, merging a PWAD
-     // into the main IWAD.  Multiple files may be specified.
-     //
-
-     p = M_CheckParm("-merge");
-
-     if (p > 0)
-     {
-         for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
-         {
-             char *filename;
-
-             filename = D_TryFindWADByName(myargv[p]);
-
-             printf(" merging %s\n", filename);
-             W_MergeFile(filename);
-         }
-     }
-
-     // NWT-style merging:
-
-     // NWT's -merge option:
-
-     //!
-     // @arg <files>
-     // @category mod
-     //
-     // Simulates the behavior of NWT's -merge option.  Multiple files
-     // may be specified.
-
-     p = M_CheckParm("-nwtmerge");
-
-     if (p > 0)
-     {
-         for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
-         {
-             char *filename;
-
-             filename = D_TryFindWADByName(myargv[p]);
-
-             printf(" performing NWT-style merge of %s\n", filename);
-             W_NWTDashMerge(filename);
-         }
-     }
-
-     // Add flats
-
-     //!
-     // @arg <files>
-     // @category mod
-     //
-     // Simulates the behavior of NWT's -af option, merging flats into
-     // the main IWAD directory.  Multiple files may be specified.
-     //
-
-     p = M_CheckParm("-af");
-
-     if (p > 0)
-     {
-         for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
-         {
-             char *filename;
-
-             filename = D_TryFindWADByName(myargv[p]);
-
-             printf(" merging flats from %s\n", filename);
-             W_NWTMergeFile(filename, W_NWT_MERGE_FLATS);
-         }
-     }
-
-     //!
-     // @arg <files>
-     // @category mod
-     //
-     // Simulates the behavior of NWT's -as option, merging sprites
-     // into the main IWAD directory.  Multiple files may be specified.
-     //
-
-     p = M_CheckParm("-as");
-
-     if (p > 0)
-     {
-         for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
-         {
-             char *filename;
-
-             filename = D_TryFindWADByName(myargv[p]);
-
-             printf(" merging sprites from %s\n", filename);
-             W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES);
-         }
-     }
-
-     //!
-     // @arg <files>
-     // @category mod
-     //
-     // Equivalent to "-af <files> -as <files>".
-     //
-
-     p = M_CheckParm("-aa");
-
-     if (p > 0)
-     {
-         for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
-         {
-             char *filename;
-
-             filename = D_TryFindWADByName(myargv[p]);
-
-             printf(" merging sprites and flats from %s\n", filename);
-             W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS);
-         }
-     }
-
- #endif
-
-     //!
-     // @arg <files>
-     // @vanilla
-     //
-     // Load the specified PWAD files.
-     //
-
-     p = M_CheckParm ("-file");
-     if (p)
-     {
- 	// 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;
-
-             filename = D_TryFindWADByName(myargv[p]);
-
- 	    D_AddFile(filename);
-         }
-     }
-
-     // Debug:
- //    W_PrintDirectory();
++    modifiedgame = W_ParseCommandLine();
 +
 +    // 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.
 +	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);
 +    }
 +
 +    //!
 +    // @arg <demo>
 +    // @category demo
 +    // @vanilla
 +    //
 +    // Play back the demo named demo.lmp.
 +    //
 +
 +    p = M_CheckParm ("-playdemo");
 +
 +    if (!p)
 +    {
 +        //!
 +        // @arg <demo>
 +        // @category demo
 +        // @vanilla
 +        //
 +        // Play back the demo named demo.lmp, determining the framerate
 +        // of the screen.
 +        //
 +	p = M_CheckParm ("-timedemo");
 +
 +    }
 +
 +    if (p && p < myargc-1)
 +    {
 +        if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
 +        {
 +            strcpy(file, myargv[p + 1]);
 +        }
 +        else
 +        {
 +	    sprintf (file,"%s.lmp", myargv[p+1]);
 +        }
 +
 +	if (D_AddFile (file))
 +        {
 +            strncpy(demolumpname, lumpinfo[numlumps - 1].name, 8);
 +            demolumpname[8] = '\0';
 +
 +            printf("Playing demo %s.\n", file);
 +        }
 +        else
 +        {
 +            // If file failed to load, still continue trying to play
 +            // the demo in the same way as Vanilla Doom.  This makes
 +            // tricks like "-playdemo demo1" possible.
 +
 +            strncpy(demolumpname, myargv[p + 1], 8);
 +            demolumpname[8] = '\0';
 +        }
 +
 +    }
 +
 +    I_AtExit((atexit_func_t) G_CheckDemoStatus, true);
 +
 +    // Generate the WAD hash table.  Speed things up a bit.
 +
 +    W_GenerateHashTable();
 +
 +    D_IdentifyVersion();
 +    InitGameVersion();
 +    //LoadChexDeh(); - haleyjd: removed, as irrelevant to Strife
 +    D_SetGameDescription();
 +    SetSaveGameDir(iwadfile);
 +
 +    // Check for -file in shareware
 +    if (modifiedgame)
 +    {
 +        // These are the lumps that will be checked in IWAD,
 +        // if any one is not present, execution will be aborted.
 +        // haleyjd 08/22/2010: [STRIFE] Check for Strife lumps.
 +        char name[3][8]=
 +        {
 +            "map23", "map30", "ROB3E1"
 +        };
 +        int i;
 +
 +        // haleyjd 08/22/2010: [STRIFE] Changed string to match binary
 +        // STRIFE-FIXME: Needs to test isdemoversion variable
 +        if ( gamemode == shareware)
 +            I_Error(DEH_String("\nYou cannot -file with the demo "
 +                               "version. You must buy the real game!"));
 +
 +        // Check for fake IWAD with right name,
 +        // but w/o all the lumps of the registered version.
 +        // STRIFE-FIXME: Needs to test isregistered variable
 +        if (gamemode == registered)
 +            for (i = 0; i < 3; i++)
 +                if (W_CheckNumForName(name[i])<0)
 +                    I_Error(DEH_String("\nThis is not the registered version."));
 +    }
 +
 +    // get skill / episode / map from parms
 +    startskill = sk_medium;
 +    startepisode = 1;
 +    startmap = 1;
 +    autostart = false;
 +
 +    //!
 +    // @arg <skill>
 +    // @vanilla
 +    //
 +    // Set the game skill, 1-5 (1: easiest, 5: hardest).  A skill of
 +    // 0 disables all monsters.
 +    //
 +
 +    p = M_CheckParm ("-skill");
 +
 +    if (p && p < myargc-1)
 +    {
 +	startskill = myargv[p+1][0]-'1';
 +	autostart = true;
 +    }
 +
 +    //!
 +    // @arg <n>
 +    // @vanilla
 +    //
 +    // Start playing on episode n (1-4)
 +    //
 +
 +    p = M_CheckParm ("-episode");
 +
 +    if (p && p < myargc-1)
 +    {
 +	startepisode = myargv[p+1][0]-'0';
 +	startmap = 1;
 +	autostart = true;
 +    }
 +
 +    timelimit = 0;
 +
 +    //!
 +    // @arg <n>
 +    // @category net
 +    // @vanilla
 +    //
 +    // For multiplayer games: exit each level after n minutes.
 +    //
 +
 +    p = M_CheckParm ("-timer");
 +
 +    if (p && p < myargc-1 && deathmatch)
 +    {
 +	timelimit = atoi(myargv[p+1]);
 +	printf("timer: %i\n", timelimit);
 +    }
 +
 +    //!
 +    // @category net
 +    // @vanilla
 +    //
 +    // Austin Virtual Gaming: end levels after 20 minutes.
 +    //
 +
 +    p = M_CheckParm ("-avg");
 +
 +    if (p && p < myargc-1 && deathmatch)
 +    {
- 	printf(DEH_String("Austin Virtual Gaming: Levels will end "
- 			  "after 20 minutes\n"));
++        DEH_printf("Austin Virtual Gaming: Levels will end "
++                       "after 20 minutes\n");
 +	timelimit = 20;
 +    }
 +
 +    //!
 +    // @arg [<x> <y> | <xy>]
 +    // @vanilla
 +    //
 +    // Start a game immediately, warping to ExMy (Doom 1) or MAPxy
 +    // (Doom 2)
 +    //
 +
 +    p = M_CheckParm ("-warp");
 +
 +    if (p && p < myargc-1)
 +    {
 +        if (gamemode == commercial)
 +            startmap = atoi (myargv[p+1]);
 +        else
 +        {
 +            startepisode = myargv[p+1][0]-'0';
 +
 +            if (p + 2 < myargc)
 +            {
 +                startmap = myargv[p+2][0]-'0';
 +            }
 +            else
 +            {
 +                startmap = 1;
 +            }
 +        }
 +        autostart = true;
 +    }
 +
 +    // Undocumented:
 +    // Invoked by setup to test the controls.
 +
 +    p = M_CheckParm("-testcontrols");
 +
 +    if (p > 0)
 +    {
 +        startepisode = 1;
 +        startmap = 1;
 +        autostart = true;
 +        testcontrols = true;
 +    }
 +
 +    // Check for load game parameter
 +    // We do this here and save the slot number, so that the network code
 +    // can override it or send the load slot to other players.
 +
 +    //!
 +    // @arg <s>
 +    // @vanilla
 +    //
 +    // Load the game in slot s.
 +    //
 +
 +    p = M_CheckParm ("-loadgame");
 +
 +    if (p && p < myargc-1)
 +    {
 +        startloadgame = atoi(myargv[p+1]);
 +    }
 +    else
 +    {
 +        // Not loading a game
 +        startloadgame = -1;
 +    }
 +
 +    if (W_CheckNumForName("SS_START") >= 0
 +     || W_CheckNumForName("FF_END") >= 0)
 +    {
 +        I_PrintDivider();
 +        printf(" WARNING: The loaded WAD file contains modified sprites or\n"
 +               " floor textures.  You may want to use the '-merge' command\n"
 +               " line option instead of '-file'.\n");
 +    }
 +
 +    I_PrintStartupBanner(gamedescription);
 +    PrintDehackedBanners();
 +
 +    // haleyjd 08/28/10: Init Choco Strife stuff.
 +    D_InitChocoStrife();
 +
-     printf (DEH_String("M_Init: Init miscellaneous info.\n"));
++    DEH_printf("M_Init: Init miscellaneous info.\n");
 +    M_Init ();
 +
 +    // haleyjd 08/22/2010: [STRIFE] Modified string to match binary
-     printf (DEH_String("R_Init: Loading Graphics - "));
++    DEH_printf("R_Init: Loading Graphics - ");
 +    R_Init ();
 +
-     printf (DEH_String("\nP_Init: Init Playloop state.\n"));
++    DEH_printf("\nP_Init: Init Playloop state.\n");
 +    P_Init ();
 +
-     printf (DEH_String("I_Init: Setting up machine state.\n"));
++    DEH_printf("I_Init: Setting up machine state.\n");
 +    I_CheckIsScreensaver();
 +    I_InitTimer();
 +    I_InitJoystick();
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    printf ("NET_Init: Init network subsystem.\n");
 +    NET_Init ();
 +#endif
 +
-     printf (DEH_String("S_Init: Setting up sound.\n"));
++    DEH_printf("S_Init: Setting up sound.\n");
 +    S_Init (sfxVolume * 8, musicVolume * 8);
 +
-     printf (DEH_String("D_CheckNetGame: Checking network game status.\n"));
++    DEH_printf("D_CheckNetGame: Checking network game status.\n");
 +    D_CheckNetGame ();
 +
 +    PrintGameVersion();
 +
-     printf (DEH_String("HU_Init: Setting up heads up display.\n"));
++    DEH_printf("HU_Init: Setting up heads up display.\n");
 +    HU_Init ();
 +
-     printf (DEH_String("ST_Init: Init status bar.\n"));
++    DEH_printf("ST_Init: Init status bar.\n");
 +    ST_Init ();
 +
 +    // If Doom II without a MAP01 lump, this is a store demo.
 +    // Moved this here so that MAP01 isn't constantly looked up
 +    // in the main loop.
 +    // haleyjd 08/23/2010: [STRIFE] There is no storedemo version of Strife
 +    /*
 +    if (gamemode == commercial && W_CheckNumForName("map01") < 0)
 +        storedemo = true;
 +    */
 +
 +    //!
 +    // @arg <x>
 +    // @category demo
 +    // @vanilla
 +    //
 +    // Record a demo named x.lmp.
 +    //
 +
 +    p = M_CheckParm ("-record");
 +
 +    if (p && p < myargc-1)
 +    {
 +	G_RecordDemo (myargv[p+1]);
 +	autostart = true;
 +    }
 +
 +    p = M_CheckParm ("-playdemo");
 +    if (p && p < myargc-1)
 +    {
 +	singledemo = true;              // quit after one demo
 +	G_DeferedPlayDemo (demolumpname);
 +	D_DoomLoop ();  // never returns
 +    }
 +
 +    p = M_CheckParm ("-timedemo");
 +    if (p && p < myargc-1)
 +    {
 +	G_TimeDemo (demolumpname);
 +	D_DoomLoop ();  // never returns
 +    }
 +
 +    if (startloadgame >= 0)
 +    {
 +        strcpy(file, P_SaveGameFile(startloadgame));
 +	G_LoadGame (file);
 +    }
 +
 +    if (gameaction != ga_loadgame )
 +    {
 +	if (autostart || netgame)
 +	    G_InitNew (startskill, startepisode, startmap);
 +	else
 +	    D_StartTitle ();                // start up intro loop
 +    }
 +
 +    D_DoomLoop ();  // never returns
 +}
 +
diff --cc src/strife/d_net.c
index c8742c71,00000000..bacc4e23
mode 100644,000000..100644
--- a/src/strife/d_net.c
+++ b/src/strife/d_net.c
@@@ -1,628 -1,0 +1,628 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	DOOM Network game communication and protocol,
 +//	all OS independend parts.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +
 +#include "doomfeatures.h"
 +
 +#include "d_main.h"
 +#include "m_argv.h"
 +#include "m_menu.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "g_game.h"
 +#include "doomdef.h"
 +#include "doomstat.h"
 +
 +#include "deh_main.h"
 +
 +#include "net_client.h"
 +#include "net_gui.h"
 +#include "net_io.h"
 +#include "net_query.h"
 +#include "net_server.h"
 +#include "net_sdl.h"
 +#include "net_loop.h"
 +
 +
 +//
 +// 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
 +//
 +// a gametic cannot be run until nettics[] > gametic for all players
 +//
 +
 +ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
 +int         	nettics[MAXPLAYERS];
 +
 +int             maketic;
 +
 +// Used for original sync code.
 +
 +int		lastnettic;
 +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.
 +
 +int		ticdup;
 +
 +// Send this many extra (backup) tics in each packet.
 +
 +int             extratics;
 +
 +// Amount to offset the timer for game sync.
 +
 +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;
 +
 +// 35 fps clock adjusted by offsetms milliseconds
 +
 +static int GetAdjustedTime(void)
 +{
 +    int time_ms;
 +
 +    time_ms = I_GetTimeMS();
 +
 +    if (net_cl_new_sync)
 +    {
 +	// Use the adjustments from net_client.c only if we are
 +	// using the new sync mode.
 +
 +        time_ms += (offsetms / FRACUNIT);
 +    }
 +
 +    return (time_ms * TICRATE) / 1000;
 +}
 +
 +//
 +// NetUpdate
 +// Builds ticcmds for console player,
 +// sends out a packet
 +//
 +int      lasttime;
 +
 +void NetUpdate (void)
 +{
 +    int nowtime;
 +    int newtics;
 +    int	i;
 +    int	gameticdiv;
 +
 +    // If we are running with singletics (timing a demo), this
 +    // is all done separately.
 +
 +    if (singletics)
 +        return;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    // Run network subsystems
 +
 +    NET_CL_Run();
 +    NET_SV_Run();
 +
 +#endif
 +
 +    // check time
 +    nowtime = GetAdjustedTime() / ticdup;
 +    newtics = nowtime - lasttime;
 +
 +    lasttime = nowtime;
 +
 +    if (skiptics <= newtics)
 +    {
 +        newtics -= skiptics;
 +        skiptics = 0;
 +    }
 +    else
 +    {
 +        skiptics -= newtics;
 +        newtics = 0;
 +    }
 +
 +    // build new ticcmds for console player
 +    gameticdiv = gametic/ticdup;
 +
 +    for (i=0 ; i<newtics ; i++)
 +    {
 +        ticcmd_t cmd;
 +
 +	I_StartTic ();
 +	D_ProcessEvents ();
 +
 +        // Always run the menu
 +
 +        M_Ticker ();
 +
 +        if (drone)
 +        {
 +            // In drone mode, do not generate any ticcmds.
 +
 +            continue;
 +        }
 +
 +        if (net_cl_new_sync)
 +        {
 +           // If playing single player, do not allow tics to buffer
 +           // up very far
 +
 +           if ((!netgame || demoplayback) && maketic - gameticdiv > 2)
 +               break;
 +
 +           // Never go more than ~200ms ahead
 +
 +           if (maketic - gameticdiv > 8)
 +               break;
 +        }
 +	else
 +	{
 +           if (maketic - gameticdiv >= 5)
 +               break;
 +	}
 +
 +	//printf ("mk:%i ",maketic);
 +	G_BuildTiccmd(&cmd);
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +        if (netgame && !demoplayback)
 +        {
 +            NET_CL_SendTiccmd(&cmd, maketic);
 +        }
 +
 +#endif
 +        netcmds[consoleplayer][maketic % BACKUPTICS] = cmd;
 +
 +	++maketic;
 +        nettics[consoleplayer] = maketic;
 +    }
 +}
 +
 +//
 +// Start game loop
 +//
 +// Called after the screen is set but before the game starts running.
 +//
 +
 +void D_StartGameLoop(void)
 +{
 +    lasttime = GetAdjustedTime() / ticdup;
 +}
 +
 +
 +//
 +// D_CheckNetGame
 +// Works out player numbers among the net participants
 +//
 +extern	int			viewangleoffset;
 +
 +void D_CheckNetGame (void)
 +{
 +    int i;
 +    int num_players;
 +
 +    // Call D_QuitNetGame on exit
 +
 +    I_AtExit(D_QuitNetGame, true);
 +
 +    // default values for single player
 +
 +    consoleplayer = 0;
 +    netgame = false;
 +    ticdup = 1;
 +    extratics = 1;
 +    lowres_turn = false;
 +    offsetms = 0;
 +
 +    for (i=0; i<MAXPLAYERS; i++)
 +    {
 +        playeringame[i] = false;
 +       	nettics[i] = 0;
 +    }
 +
 +    playeringame[0] = true;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    {
 +        net_addr_t *addr = NULL;
 +
 +        //!
 +        // @category net
 +        //
 +        // Start a multiplayer server, listening for connections.
 +        //
 +
 +        if (M_CheckParm("-server") > 0)
 +        {
 +            NET_SV_Init();
 +            NET_SV_AddModule(&net_loop_server_module);
 +            NET_SV_AddModule(&net_sdl_module);
 +
 +            net_loop_client_module.InitClient();
 +            addr = net_loop_client_module.ResolveAddress(NULL);
 +        }
 +        else
 +        {
 +            //!
 +            // @category net
 +            //
 +            // Automatically search the local LAN for a multiplayer
 +            // server and join it.
 +            //
 +
 +            i = M_CheckParm("-autojoin");
 +
 +            if (i > 0)
 +            {
 +                addr = NET_FindLANServer();
 +
 +                if (addr == NULL)
 +                {
 +                    I_Error("No server found on local LAN");
 +                }
 +            }
 +
 +            //!
 +            // @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)
 +                {
 +                    I_Error("Unable to resolve '%s'\n", myargv[i+1]);
 +                }
 +            }
 +        }
 +
 +        if (addr != NULL)
 +        {
 +            if (M_CheckParm("-drone") > 0)
 +            {
 +                drone = true;
 +            }
 +
 +            //!
 +            // @category net
 +            //
 +            // Run as the left screen in three screen mode.
 +            //
 +
 +            if (M_CheckParm("-left") > 0)
 +            {
 +                viewangleoffset = ANG90;
 +                drone = true;
 +            }
 +
 +            //!
 +            // @category net
 +            //
 +            // Run as the right screen in three screen mode.
 +            //
 +
 +            if (M_CheckParm("-right") > 0)
 +            {
 +                viewangleoffset = ANG270;
 +                drone = true;
 +            }
 +
 +            if (!NET_CL_Connect(addr))
 +            {
 +                I_Error("D_CheckNetGame: Failed to connect to %s\n",
 +                        NET_AddrToString(addr));
 +            }
 +
 +            printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
 +
 +            NET_WaitForStart();
 +        }
 +    }
 +
 +#endif
 +
 +    num_players = 0;
 +
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        if (playeringame[i])
 +            ++num_players;
 +    }
 +
-     printf (DEH_String("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n"),
- 	    startskill, deathmatch, startmap, startepisode);
++    DEH_printf("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
++               startskill, deathmatch, startmap, startepisode);
 +
-     printf(DEH_String("player %i of %i (%i nodes)\n"),
- 	    consoleplayer+1, num_players, num_players);
++    DEH_printf("player %i of %i (%i nodes)\n",
++               consoleplayer+1, num_players, num_players);
 +
 +    // Show players here; the server might have specified a time limit
 +
 +    if (timelimit > 0)
 +    {
- 	printf(DEH_String("Levels will end after %d minute"),timelimit);
++	DEH_printf("Levels will end after %d minute", timelimit);
 +	if (timelimit > 1)
 +	    printf("s");
 +	printf(".\n");
 +    }
 +}
 +
 +
 +//
 +// D_QuitNetGame
 +// Called before quitting to leave a net game
 +// without hanging the other players
 +//
 +void D_QuitNetGame (void)
 +{
 +    if (debugfile)
 +	fclose (debugfile);
 +
 +#ifdef FEATURE_MULTIPLAYER
 +
 +    NET_SV_Shutdown();
 +    NET_CL_Disconnect();
 +
 +#endif
 +
 +}
 +
 +// Returns true if there are currently any players in the game.
 +
 +static boolean PlayersInGame(void)
 +{
 +    int i;
 +
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        if (playeringame[i])
 +        {
 +            return true;
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +static int GetLowTic(void)
 +{
 +    int lowtic;
 +
 +#ifdef FEATURE_MULTIPLAYER
 +    if (net_client_connected)
 +    {
 +        int i;
 +
 +        lowtic = INT_MAX;
 +
 +        for (i=0; i<MAXPLAYERS; ++i)
 +        {
 +            if (playeringame[i])
 +            {
 +                if (nettics[i] < lowtic)
 +                    lowtic = nettics[i];
 +            }
 +        }
 +    }
 +    else
 +#endif
 +    {
 +        lowtic = maketic;
 +    }
 +
 +    return lowtic;
 +}
 +
 +//
 +// TryRunTics
 +//
 +int	oldnettics;
 +int	frametics[4];
 +int	frameon;
 +int	frameskip[4];
 +int	oldnettics;
 +
 +extern	boolean	advancedemo;
 +
 +void TryRunTics (void)
 +{
 +    int	i;
 +    int	lowtic;
 +    int	entertic;
 +    static int oldentertics;
 +    int realtics;
 +    int	availabletics;
 +    int	counts;
 +
 +    // get real tics
 +    entertic = I_GetTime() / ticdup;
 +    realtics = entertic - oldentertics;
 +    oldentertics = entertic;
 +
 +    // get available tics
 +    NetUpdate ();
 +
 +    lowtic = GetLowTic();
 +
 +    availabletics = lowtic - gametic/ticdup;
 +
 +    // decide how many tics to run
 +
 +    if (net_cl_new_sync)
 +    {
 +	counts = availabletics;
 +    }
 +    else
 +    {
 +        // decide how many tics to run
 +        if (realtics < availabletics-1)
 +            counts = realtics+1;
 +        else if (realtics < availabletics)
 +            counts = realtics;
 +        else
 +            counts = availabletics;
 +
 +        if (counts < 1)
 +            counts = 1;
 +
 +        frameon++;
 +
 +        if (!demoplayback)
 +        {
 +	    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 ("+");
 +                }
 +            }
 +        }
 +    }
 +
 +    if (counts < 1)
 +	counts = 1;
 +
 +    // wait for new tics if needed
 +
 +    while (!PlayersInGame() || lowtic < gametic/ticdup + counts)
 +    {
 +	NetUpdate ();
 +
 +        lowtic = GetLowTic();
 +
 +	if (lowtic < gametic/ticdup)
 +	    I_Error ("TryRunTics: lowtic < gametic");
 +
 +        // Don't stay in this loop forever.  The menu is still running,
 +        // so return to update the screen
 +
 +	if (I_GetTime() / ticdup - entertic > 0)
 +	{
 +	    return;
 +	}
 +
 +        I_Sleep(1);
 +    }
 +
 +    // run the count * ticdup dics
 +    while (counts--)
 +    {
 +	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 ();
 +
 +	    G_Ticker ();
 +	    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;
 +		}
 +	    }
 +	}
 +	NetUpdate ();	// check for new console commands
 +    }
 +}
 +
diff --cc src/strife/f_finale.c
index 9e0aca93,00000000..68c07af1
mode 100644,000000..100644
--- a/src/strife/f_finale.c
+++ b/src/strife/f_finale.c
@@@ -1,731 -1,0 +1,731 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Game completion, final screen animation.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <stdio.h>
 +#include <ctype.h>
 +
 +// Functions.
 +#include "deh_main.h"
 +#include "i_system.h"
 +#include "i_swap.h"
 +#include "z_zone.h"
 +#include "v_video.h"
 +#include "w_wad.h"
 +#include "s_sound.h"
 +
 +// Data.
 +#include "d_main.h"
 +#include "dstrings.h"
 +#include "sounds.h"
 +
 +#include "doomstat.h"
 +#include "r_state.h"
 +
 +typedef enum
 +{
 +    F_STAGE_TEXT,
 +    F_STAGE_ARTSCREEN,
 +    F_STAGE_CAST,
 +} finalestage_t;
 +
 +// ?
 +//#include "doomstat.h"
 +//#include "r_local.h"
 +//#include "f_finale.h"
 +
 +// Stage of animation:
 +finalestage_t finalestage;
 +
 +unsigned int finalecount;
 +
 +#define	TEXTSPEED	3
 +#define	TEXTWAIT	250
 +
 +typedef struct
 +{
 +    GameMission_t mission;
 +    int episode, level;
 +    char *background;
 +    char *text;
 +} textscreen_t;
 +
 +static textscreen_t textscreens[] =
 +{
 +    { doom,      1, 8,  "FLOOR4_8",  E1TEXT},
 +    { doom,      2, 8,  "SFLR6_1",   E2TEXT},
 +    { doom,      3, 8,  "MFLR8_4",   E3TEXT},
 +    { doom,      4, 8,  "MFLR8_3",   E4TEXT},
 +
 +    { doom2,     1, 6,  "SLIME16",   C1TEXT},
 +    { doom2,     1, 11, "RROCK14",   C2TEXT},
 +    { doom2,     1, 20, "RROCK07",   C3TEXT},
 +    { doom2,     1, 30, "RROCK17",   C4TEXT},
 +    { doom2,     1, 15, "RROCK13",   C5TEXT},
 +    { doom2,     1, 31, "RROCK19",   C6TEXT},
 +
 +    { pack_tnt,  1, 6,  "SLIME16",   T1TEXT},
 +    { pack_tnt,  1, 11, "RROCK14",   T2TEXT},
 +    { pack_tnt,  1, 20, "RROCK07",   T3TEXT},
 +    { pack_tnt,  1, 30, "RROCK17",   T4TEXT},
 +    { pack_tnt,  1, 15, "RROCK13",   T5TEXT},
 +    { pack_tnt,  1, 31, "RROCK19",   T6TEXT},
 +
 +    { pack_plut, 1, 6,  "SLIME16",   P1TEXT},
 +    { pack_plut, 1, 11, "RROCK14",   P2TEXT},
 +    { pack_plut, 1, 20, "RROCK07",   P3TEXT},
 +    { pack_plut, 1, 30, "RROCK17",   P4TEXT},
 +    { pack_plut, 1, 15, "RROCK13",   P5TEXT},
 +    { pack_plut, 1, 31, "RROCK19",   P6TEXT},
 +};
 +
 +char*	finaletext;
 +char*	finaleflat;
 +
 +void	F_StartCast (void);
 +void	F_CastTicker (void);
 +boolean F_CastResponder (event_t *ev);
 +void	F_CastDrawer (void);
 +
 +//
 +// F_StartFinale
 +//
 +void F_StartFinale (void)
 +{
 +    size_t i;
 +
 +    gameaction = ga_nothing;
 +    gamestate = GS_FINALE;
 +    viewactive = false;
 +    automapactive = false;
 +
 +    if (gamemission == doom)
 +    {
 +        S_ChangeMusic(mus_logo, true);  // villsa [STRIFE] TODO - fix music
 +    }
 +    else
 +    {
 +        S_ChangeMusic(mus_logo, true);  // villsa [STRIFE] TODO - fix music
 +    }
 +
 +    // Find the right screen and set the text and background
 +
 +    for (i=0; i<arrlen(textscreens); ++i)
 +    {
 +        textscreen_t *screen = &textscreens[i];
 +
 +        // Hack for Chex Quest
 +
 +        if (gameversion == exe_chex && screen->mission == doom)
 +        {
 +            screen->level = 5;
 +        }
 +
 +        if (gamemission == screen->mission
 +         && (gamemission != doom || gameepisode == screen->episode)
 +         && gamemap == screen->level)
 +        {
 +            finaletext = screen->text;
 +            finaleflat = screen->background;
 +        }
 +    }
 +
 +    // Do dehacked substitutions of strings
 +
 +    finaletext = DEH_String(finaletext);
 +    finaleflat = DEH_String(finaleflat);
 +
 +    finalestage = F_STAGE_TEXT;
 +    finalecount = 0;
 +
 +}
 +
 +
 +
 +boolean F_Responder (event_t *event)
 +{
 +    if (finalestage == F_STAGE_CAST)
 +	return F_CastResponder (event);
 +
 +    return false;
 +}
 +
 +
 +//
 +// F_Ticker
 +//
 +void F_Ticker (void)
 +{
 +    size_t		i;
 +
 +    // check for skipping
 +    if ( (gamemode == commercial)
 +      && ( finalecount > 50) )
 +    {
 +      // go on to the next level
 +      for (i=0 ; i<MAXPLAYERS ; i++)
 +	if (players[i].cmd.buttons)
 +	  break;
 +
 +      if (i < MAXPLAYERS)
 +      {
 +	if (gamemap == 30)
 +	  F_StartCast ();
 +	else
 +	  gameaction = ga_worlddone;
 +      }
 +    }
 +
 +    // advance animation
 +    finalecount++;
 +
 +    if (finalestage == F_STAGE_CAST)
 +    {
 +	F_CastTicker ();
 +	return;
 +    }
 +
 +    if ( gamemode == commercial)
 +	return;
 +
 +    if (finalestage == F_STAGE_TEXT
 +     && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
 +    {
 +	finalecount = 0;
 +	finalestage = F_STAGE_ARTSCREEN;
 +	wipegamestate = -1;		// force a wipe
 +	if (gameepisode == 3)
 +	    S_StartMusic (mus_logo);    // villsa [STRIFE] TODO - fix music
 +    }
 +}
 +
 +
 +
 +//
 +// F_TextWrite
 +//
 +
 +#include "hu_stuff.h"
 +extern	patch_t *hu_font[HU_FONTSIZE];
 +
 +
 +void F_TextWrite (void)
 +{
 +    byte*	src;
 +    byte*	dest;
 +
 +    int		x,y,w;
 +    signed int	count;
 +    char*	ch;
 +    int		c;
 +    int		cx;
 +    int		cy;
 +
 +    // erase the entire screen to a tiled background
 +    src = W_CacheLumpName ( finaleflat , PU_CACHE);
 +    dest = I_VideoBuffer;
 +
 +    for (y=0 ; y<SCREENHEIGHT ; y++)
 +    {
 +	for (x=0 ; x<SCREENWIDTH/64 ; x++)
 +	{
 +	    memcpy (dest, src+((y&63)<<6), 64);
 +	    dest += 64;
 +	}
 +	if (SCREENWIDTH&63)
 +	{
 +	    memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63);
 +	    dest += (SCREENWIDTH&63);
 +	}
 +    }
 +
 +    V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
 +
 +    // draw some of the text onto the screen
 +    cx = 10;
 +    cy = 10;
 +    ch = finaletext;
 +
 +    count = ((signed int) finalecount - 10) / TEXTSPEED;
 +    if (count < 0)
 +	count = 0;
 +    for ( ; count ; count-- )
 +    {
 +	c = *ch++;
 +	if (!c)
 +	    break;
 +	if (c == '\n')
 +	{
 +	    cx = 10;
 +	    cy += 11;
 +	    continue;
 +	}
 +
 +	c = toupper(c) - HU_FONTSTART;
 +	if (c < 0 || c> HU_FONTSIZE)
 +	{
 +	    cx += 4;
 +	    continue;
 +	}
 +
 +	w = SHORT (hu_font[c]->width);
 +	if (cx+w > SCREENWIDTH)
 +	    break;
 +	V_DrawPatch(cx, cy, hu_font[c]);
 +	cx+=w;
 +    }
 +
 +}
 +
 +//
 +// Final DOOM 2 animation
 +// Casting by id Software.
 +//   in order of appearance
 +//
 +typedef struct
 +{
 +    char		*name;
 +    mobjtype_t	type;
 +} castinfo_t;
 +
 +// villsa [STRIFE] TODO - proper cast fix
 +castinfo_t	castorder[] = {
 +    {CC_ZOMBIE, MT_PLAYER},
 +    {CC_SHOTGUN, MT_PLAYER},
 +    {CC_HEAVY, MT_PLAYER},
 +    {CC_IMP, MT_PLAYER},
 +    {CC_DEMON, MT_PLAYER},
 +    {CC_LOST, MT_PLAYER},
 +    {CC_CACO, MT_PLAYER},
 +    {CC_HELL, MT_PLAYER},
 +    {CC_BARON, MT_PLAYER},
 +    {CC_ARACH, MT_PLAYER},
 +    {CC_PAIN, MT_PLAYER},
 +    {CC_REVEN, MT_PLAYER},
 +    {CC_MANCU, MT_PLAYER},
 +    {CC_ARCH, MT_PLAYER},
 +    {CC_SPIDER, MT_PLAYER},
 +    {CC_CYBER, MT_PLAYER},
 +    {CC_HERO, MT_PLAYER},
 +
 +    {NULL,0}
 +};
 +
 +int		castnum;
 +int		casttics;
 +state_t*	caststate;
 +boolean		castdeath;
 +int		castframes;
 +int		castonmelee;
 +boolean		castattacking;
 +
 +
 +//
 +// F_StartCast
 +//
 +extern	gamestate_t     wipegamestate;
 +
 +
 +void F_StartCast (void)
 +{
 +    wipegamestate = -1;		// force a screen wipe
 +    castnum = 0;
 +    caststate = &states[mobjinfo[castorder[castnum].type].seestate];
 +    casttics = caststate->tics;
 +    castdeath = false;
 +    finalestage = F_STAGE_CAST;
 +    castframes = 0;
 +    castonmelee = 0;
 +    castattacking = false;
 +    S_ChangeMusic(mus_logo, true);  // villsa [STRIFE] TODO - fix music
 +}
 +
 +
 +//
 +// F_CastTicker
 +//
 +void F_CastTicker (void)
 +{
 +    int		st;
 +    int		sfx = sfx_swish;    // villsa [STRIFE] TODO - fix me!
 +
 +    if (--casttics > 0)
 +	return;			// not time to change state yet
 +
 +    if (caststate->tics == -1 || caststate->nextstate == S_NULL)
 +    {
 +	// switch from deathstate to next monster
 +	castnum++;
 +	castdeath = false;
 +	if (castorder[castnum].name == NULL)
 +	    castnum = 0;
 +	if (mobjinfo[castorder[castnum].type].seesound)
 +	    S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
 +	caststate = &states[mobjinfo[castorder[castnum].type].seestate];
 +	castframes = 0;
 +    }
 +    else
 +    {
 +	// just advance to next state in animation
 +	if (caststate == &states[S_PLAY_05])    // villsa [STRIFE] TODO - update later
 +	    goto stopattack;	// Oh, gross hack!
 +	st = caststate->nextstate;
 +	caststate = &states[st];
 +	castframes++;
 +
 +	// sound hacks....
 +	/*switch (st)
 +	{
 +            // villsa [STRIFE] TODO - fix sounds
 +	  case S_PLAY_ATK1:	sfx = sfx_swish; break;
 +	  case S_POSS_ATK2:	sfx = sfx_swish; break;
 +	  case S_SPOS_ATK2:	sfx = sfx_swish; break;
 +	  case S_VILE_ATK2:	sfx = sfx_swish; break;
 +	  case S_SKEL_FIST2:	sfx = sfx_swish; break;
 +	  case S_SKEL_FIST4:	sfx = sfx_swish; break;
 +	  case S_SKEL_MISS2:	sfx = sfx_swish; break;
 +	  case S_FATT_ATK8:
 +	  case S_FATT_ATK5:
 +	  case S_FATT_ATK2:	sfx = sfx_swish; break;
 +	  case S_CPOS_ATK2:
 +	  case S_CPOS_ATK3:
 +	  case S_CPOS_ATK4:	sfx = sfx_swish; break;
 +	  case S_TROO_ATK3:	sfx = sfx_swish; break;
 +	  case S_SARG_ATK2:	sfx = sfx_swish; break;
 +	  case S_BOSS_ATK2:
 +	  case S_BOS2_ATK2:
 +	  case S_HEAD_ATK2:	sfx = sfx_swish; break;
 +	  case S_SKULL_ATK2:	sfx = sfx_swish; break;
 +	  case S_SPID_ATK2:
 +	  case S_SPID_ATK3:	sfx = sfx_swish; break;
 +	  case S_BSPI_ATK2:	sfx = sfx_plasma; break;
 +	  case S_CYBER_ATK2:
 +	  case S_CYBER_ATK4:
 +	  case S_CYBER_ATK6:	sfx = sfx_rlaunc; break;
 +	  case S_PAIN_ATK3:	sfx = sfx_swish; break;
 +	  default: sfx = 0; break;
 +	}*/
 +
 +	if (sfx)
 +	    S_StartSound (NULL, sfx);
 +    }
 +
 +    if (castframes == 12)
 +    {
 +	// go into attack frame
 +	castattacking = true;
 +	if (castonmelee)
 +	    caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
 +	else
 +	    caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
 +	castonmelee ^= 1;
 +	if (caststate == &states[S_NULL])
 +	{
 +	    if (castonmelee)
 +		caststate=
 +		    &states[mobjinfo[castorder[castnum].type].meleestate];
 +	    else
 +		caststate=
 +		    &states[mobjinfo[castorder[castnum].type].missilestate];
 +	}
 +    }
 +
 +    if (castattacking)
 +    {
 +	if (castframes == 24
 +	    ||	caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
 +	{
 +	  stopattack:
 +	    castattacking = false;
 +	    castframes = 0;
 +	    caststate = &states[mobjinfo[castorder[castnum].type].seestate];
 +	}
 +    }
 +
 +    casttics = caststate->tics;
 +    if (casttics == -1)
 +	casttics = 15;
 +}
 +
 +
 +//
 +// F_CastResponder
 +//
 +
 +boolean F_CastResponder (event_t* ev)
 +{
 +    if (ev->type != ev_keydown)
 +	return false;
 +
 +    if (castdeath)
 +	return true;			// already in dying frames
 +
 +    // go into death frame
 +    castdeath = true;
 +    caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
 +    casttics = caststate->tics;
 +    castframes = 0;
 +    castattacking = false;
 +    if (mobjinfo[castorder[castnum].type].deathsound)
 +	S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
 +
 +    return true;
 +}
 +
 +
 +void F_CastPrint (char* text)
 +{
 +    char*	ch;
 +    int		c;
 +    int		cx;
 +    int		w;
 +    int		width;
 +
 +    // find width
 +    ch = text;
 +    width = 0;
 +
 +    while (ch)
 +    {
 +	c = *ch++;
 +	if (!c)
 +	    break;
 +	c = toupper(c) - HU_FONTSTART;
 +	if (c < 0 || c> HU_FONTSIZE)
 +	{
 +	    width += 4;
 +	    continue;
 +	}
 +
 +	w = SHORT (hu_font[c]->width);
 +	width += w;
 +    }
 +
 +    // draw it
 +    cx = 160-width/2;
 +    ch = text;
 +    while (ch)
 +    {
 +	c = *ch++;
 +	if (!c)
 +	    break;
 +	c = toupper(c) - HU_FONTSTART;
 +	if (c < 0 || c> HU_FONTSIZE)
 +	{
 +	    cx += 4;
 +	    continue;
 +	}
 +
 +	w = SHORT (hu_font[c]->width);
 +	V_DrawPatch(cx, 180, hu_font[c]);
 +	cx+=w;
 +    }
 +
 +}
 +
 +
 +//
 +// F_CastDrawer
 +//
 +
 +void F_CastDrawer (void)
 +{
 +    spritedef_t*	sprdef;
 +    spriteframe_t*	sprframe;
 +    int			lump;
 +    boolean		flip;
 +    patch_t*		patch;
 +
 +    // erase the entire screen to a background
 +    V_DrawPatch (0, 0, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE));
 +
 +    F_CastPrint (DEH_String(castorder[castnum].name));
 +
 +    // draw the current frame in the middle of the screen
 +    sprdef = &sprites[caststate->sprite];
 +    sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];
 +    lump = sprframe->lump[0];
 +    flip = (boolean)sprframe->flip[0];
 +
 +    patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE);
 +    if (flip)
 +	V_DrawPatchFlipped(160, 170, patch);
 +    else
 +	V_DrawPatch(160, 170, patch);
 +}
 +
 +
 +//
 +// F_DrawPatchCol
 +//
 +void
 +F_DrawPatchCol
 +( int		x,
 +  patch_t*	patch,
 +  int		col )
 +{
 +    column_t*	column;
 +    byte*	source;
 +    byte*	dest;
 +    byte*	desttop;
 +    int		count;
 +
 +    column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
 +    desttop = I_VideoBuffer + x;
 +
 +    // step through the posts in a column
 +    while (column->topdelta != 0xff )
 +    {
 +	source = (byte *)column + 3;
 +	dest = desttop + column->topdelta*SCREENWIDTH;
 +	count = column->length;
 +
 +	while (count--)
 +	{
 +	    *dest = *source++;
 +	    dest += SCREENWIDTH;
 +	}
 +	column = (column_t *)(  (byte *)column + column->length + 4 );
 +    }
 +}
 +
 +
 +//
 +// F_BunnyScroll
 +//
 +void F_BunnyScroll (void)
 +{
 +    signed int  scrolled;
 +    int		x;
 +    patch_t*	p1;
 +    patch_t*	p2;
 +    char	name[10];
 +    int		stage;
 +    static int	laststage;
 +
 +    p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL);
 +    p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL);
 +
 +    V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
 +
 +    scrolled = (320 - ((signed int) finalecount-230)/2);
 +    if (scrolled > 320)
 +	scrolled = 320;
 +    if (scrolled < 0)
 +	scrolled = 0;
 +
 +    for ( x=0 ; x<SCREENWIDTH ; x++)
 +    {
 +	if (x+scrolled < 320)
 +	    F_DrawPatchCol (x, p1, x+scrolled);
 +	else
 +	    F_DrawPatchCol (x, p2, x+scrolled - 320);
 +    }
 +
 +    if (finalecount < 1130)
 +	return;
 +    if (finalecount < 1180)
 +    {
 +        V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
 +                    (SCREENHEIGHT - 8 * 8) / 2,
 +                    W_CacheLumpName(DEH_String("END0"), PU_CACHE));
 +	laststage = 0;
 +	return;
 +    }
 +
 +    stage = (finalecount-1180) / 5;
 +    if (stage > 6)
 +	stage = 6;
 +    if (stage > laststage)
 +    {
 +	S_StartSound (NULL, sfx_swish); // villsa [STRIFE] TODO - fix sounds
 +	laststage = stage;
 +    }
 +
-     sprintf (name, DEH_String("END%i"), stage);
++    DEH_snprintf(name, 10, "END%i", stage);
 +    V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
 +                (SCREENHEIGHT - 8 * 8) / 2,
 +                W_CacheLumpName (name,PU_CACHE));
 +}
 +
 +static void F_ArtScreenDrawer(void)
 +{
 +    char *lumpname;
 +
 +    if (gameepisode == 3)
 +    {
 +        F_BunnyScroll();
 +    }
 +    else
 +    {
 +        switch (gameepisode)
 +        {
 +            case 1:
 +                if (gamemode == retail)
 +                {
 +                    lumpname = "CREDIT";
 +                }
 +                else
 +                {
 +                    lumpname = "HELP2";
 +                }
 +                break;
 +            case 2:
 +                lumpname = "VICTORY2";
 +                break;
 +            case 4:
 +                lumpname = "ENDPIC";
 +                break;
 +            default:
 +                return;
 +        }
 +
 +        lumpname = DEH_String(lumpname);
 +
 +        V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE));
 +    }
 +}
 +
 +//
 +// F_Drawer
 +//
 +void F_Drawer (void)
 +{
 +    switch (finalestage)
 +    {
 +        case F_STAGE_CAST:
 +            F_CastDrawer();
 +            break;
 +        case F_STAGE_TEXT:
 +            F_TextWrite();
 +            break;
 +        case F_STAGE_ARTSCREEN:
 +            F_ArtScreenDrawer();
 +            break;
 +    }
 +}
 +
 +
diff --cc src/strife/g_game.c
index 25838468,00000000..42c8bdd3
mode 100644,000000..100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@@ -1,2106 -1,0 +1,2249 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:  none
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +
 +#include <string.h>
 +#include <stdlib.h>
 +#include <math.h>
 +
 +#include "doomdef.h"
 +#include "doomkeys.h"
 +#include "doomstat.h"
 +
 +#include "deh_main.h"
 +#include "deh_misc.h"
 +
 +#include "z_zone.h"
 +#include "f_finale.h"
 +#include "m_argv.h"
 +#include "m_controls.h"
 +#include "m_misc.h"
 +#include "m_menu.h"
 +#include "m_random.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +
 +#include "p_setup.h"
 +#include "p_saveg.h"
 +#include "p_tick.h"
 +
 +#include "d_main.h"
 +
 +#include "wi_stuff.h"
 +#include "hu_stuff.h"
 +#include "st_stuff.h"
 +#include "am_map.h"
 +
 +// Needs access to LFB.
 +#include "v_video.h"
 +
 +#include "w_wad.h"
 +
 +#include "p_local.h"
 +
 +#include "s_sound.h"
 +
 +// Data.
 +#include "dstrings.h"
 +#include "sounds.h"
 +
 +// SKY handling - still the wrong place.
 +#include "r_data.h"
 +#include "r_sky.h"
 +
 +
 +
 +#include "g_game.h"
 +
 +
 +#define SAVEGAMESIZE	0x2c000
 +
 +boolean	G_CheckDemoStatus (void);
 +void	G_ReadDemoTiccmd (ticcmd_t* cmd);
 +void	G_WriteDemoTiccmd (ticcmd_t* cmd);
 +void	G_PlayerReborn (int player);
 +void	G_InitNew (skill_t skill, int episode, int map);
 +
 +void	G_DoReborn (int playernum);
 +
 +void	G_DoLoadLevel (void);
 +void	G_DoNewGame (void);
 +void	G_DoLoadGame (void);
 +void	G_DoPlayDemo (void);
 +void	G_DoCompleted (void);
 +void	G_DoVictory (void);
 +void	G_DoWorldDone (void);
 +void	G_DoSaveGame (void);
 +
 +// Gamestate the last time G_Ticker was called.
 +
 +gamestate_t     oldgamestate;
 +
 +gameaction_t    gameaction;
 +gamestate_t     gamestate;
 +skill_t         gameskill;
 +boolean		respawnmonsters;
 +int             gameepisode;
 +int             gamemap;
 +
 +// haleyjd 08/24/10: [STRIFE] New variables
 +int             destmap;   // current destination map when exiting
 +int             riftdest;  // destination spot for player
 +angle_t         riftangle; // player angle saved during exit
 +
 +// If non-zero, exit the level after this number of minutes.
 +
 +int             timelimit;
 +
 +boolean         paused;
 +boolean         sendpause;             	// send a pause event next tic
 +boolean         sendsave;             	// send a save event next tic
 +boolean         usergame;               // ok to save / end game
 +
 +boolean         timingdemo;             // if true, exit with report on completion
 +boolean         nodrawers;              // for comparative timing purposes
 +int             starttime;          	// for comparative timing purposes
 +
 +boolean         viewactive;
 +
 +boolean         deathmatch;           	// only if started as net death
 +boolean         netgame;                // only true if packets are broadcast
 +boolean         playeringame[MAXPLAYERS];
 +player_t        players[MAXPLAYERS];
 +
 +boolean         turbodetected[MAXPLAYERS];
 +
 +int             consoleplayer;          // player taking events and displaying
 +int             displayplayer;          // view being displayed
 +int             gametic;
 +int             levelstarttic;          // gametic at level start
 +int             totalkills, totalitems, totalsecret;    // for intermission
 +
 +char            demoname[32];
 +boolean         demorecording;
 +boolean         longtics;               // cph's doom 1.91 longtics hack
 +boolean         lowres_turn;            // low resolution turning for longtics
 +boolean         demoplayback;
 +boolean		netdemo;
 +byte*		demobuffer;
 +byte*		demo_p;
 +byte*		demoend;
 +boolean         singledemo;            	// quit after playing a demo from cmdline
 +
 +boolean         precache = true;        // if true, load all graphics at start
 +
 +boolean         testcontrols = false;    // Invoked by setup to test controls
 +
 +wbstartstruct_t wminfo;               	// parms for world map / intermission
 +
 +byte		consistancy[MAXPLAYERS][BACKUPTICS];
 +
 +#define MAXPLMOVE		(forwardmove[1])
 +
 +#define TURBOTHRESHOLD	0x32
 +
 +fixed_t         forwardmove[2] = {0x19, 0x32};
 +fixed_t         sidemove[2] = {0x18, 0x28};
 +fixed_t         angleturn[3] = {640, 1280, 320};    // + slow turn
 +
 +int mouse_fire_countdown = 0;    // villsa [STRIFE]
 +
 +static int *weapon_keys[] = {
 +    &key_weapon1,
 +    &key_weapon2,
 +    &key_weapon3,
 +    &key_weapon4,
 +    &key_weapon5,
 +    &key_weapon6,
 +    &key_weapon7,
 +    &key_weapon8
 +};
 +
++// Set to -1 or +1 to switch to the previous or next weapon.
++
++static int next_weapon = 0;
++
++// Used for prev/next weapon keys.
++// STRIFE-TODO: Check this table makes sense.
++
++static const struct
++{
++    weapontype_t weapon;
++    weapontype_t weapon_num;
++} weapon_order_table[] = {
++    { wp_fist,                  wp_fist },
++    { wp_elecbow,               wp_elecbow },
++    { wp_poisonbow,             wp_elecbow },
++    { wp_rifle,                 wp_rifle },
++    { wp_missile,               wp_missile },
++    { wp_hegrenade,             wp_hegrenade },
++    { wp_wpgrenade,             wp_hegrenade },
++    { wp_flame,                 wp_flame },
++    { wp_mauler,                wp_mauler },
++    { wp_torpedo,               wp_mauler },
++    { wp_sigil,                 wp_sigil },
++};
++
 +#define SLOWTURNTICS	6
 +
 +#define NUMKEYS		256
++#define MAX_JOY_BUTTONS 20
 +
 +static boolean  gamekeydown[NUMKEYS];
 +static int      turnheld;		// for accelerative turning
 +
- static boolean  mousearray[4];
++static boolean  mousearray[MAX_MOUSE_BUTTONS + 1];
 +static boolean *mousebuttons = &mousearray[1];  // allow [-1]
 +
 +// mouse values are used once
 +int             mousex;
 +int             mousey;
 +
 +static int      dclicktime;
 +static boolean  dclickstate;
 +static int      dclicks;
 +static int      dclicktime2;
 +static boolean  dclickstate2;
 +static int      dclicks2;
 +
- #define MAX_JOY_BUTTONS 20
-
 +// joystick values are repeated
 +static int      joyxmove;
 +static int      joyymove;
 +static boolean  joyarray[MAX_JOY_BUTTONS + 1];
 +static boolean *joybuttons = &joyarray[1];		// allow [-1]
 +
 +static int      savegameslot;
 +static char     savedescription[32];
 +
 +static int      testcontrols_mousespeed;
 +
 +#define	BODYQUESIZE	32
 +
 +mobj_t*		bodyque[BODYQUESIZE];
 +int		bodyqueslot;
 +
 +int             vanilla_savegame_limit = 1;
 +int             vanilla_demo_limit = 1;
 +
 +
 +#define MOUSE_SPEED_BOX_WIDTH 16
 +#define COLOR_RED    0xb0
 +#define COLOR_BLACK  0x00
 +#define COLOR_WHITE  0x04
 +#define COLOR_YELLOW 0xe7
 +
 +void G_DrawMouseSpeedBox(void)
 +{
 +    extern int usemouse;
 +    int i;
 +    int box_x, box_y;
 +    int original_speed;
 +    int x, y;
 +    int redline_x;
 +    int linelen;
 +    char *lumpname;
 +    int color;
 +
 +    // If the mouse is turned off or acceleration is turned off, don't
 +    // draw the box at all.
 +
 +    if (!usemouse || fabs(mouse_acceleration - 1) < 0.01)
 +    {
 +        return;
 +    }
 +
 +    // Calculate box position
 +
 +    box_x = SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH * 8;
 +    box_y = SCREENHEIGHT - 9;
 +
 +    // Draw the box.
 +
 +    x = box_x;
 +
 +    for (i=0; i<MOUSE_SPEED_BOX_WIDTH; ++i)
 +    {
 +        if (i == 0)
 +        {
 +            lumpname = "M_LSLEFT";
 +        }
 +        else if (i == MOUSE_SPEED_BOX_WIDTH - 1)
 +        {
 +            lumpname = "M_LSRGHT";
 +        }
 +        else
 +        {
 +            lumpname = "M_LSCNTR";
 +        }
 +
 +        V_DrawPatchDirect(x, box_y,
 +                          W_CacheLumpName(DEH_String(lumpname), PU_CACHE));
 +        x += 8;
 +    }
 +
 +    // Calculate the position of the red line.  This is 1/3 of the way
 +    // along the box.
 +
 +    redline_x = (MOUSE_SPEED_BOX_WIDTH / 3) * 8;
 +
 +    // Undo acceleration and get back the original mouse speed
 +
 +    if (testcontrols_mousespeed < mouse_threshold)
 +    {
 +        original_speed = testcontrols_mousespeed;
 +    }
 +    else
 +    {
 +        original_speed = testcontrols_mousespeed - mouse_threshold;
 +        original_speed = (int) (original_speed / mouse_acceleration);
 +        original_speed += mouse_threshold;
 +    }
 +
 +    // Calculate line length
 +
 +    linelen = (original_speed * redline_x) / mouse_threshold;
 +
 +    // Draw horizontal "thermometer"
 +
 +    for (x=0; x<(MOUSE_SPEED_BOX_WIDTH - 1) * 8; ++x)
 +    {
 +        if (x < linelen)
 +        {
 +            if (x < redline_x)
 +            {
 +                color = COLOR_WHITE;
 +            }
 +            else
 +            {
 +                color = COLOR_YELLOW;
 +            }
 +        }
 +        else
 +        {
 +            color = COLOR_BLACK;
 +        }
 +
 +        I_VideoBuffer[(box_y - 4) * SCREENWIDTH + box_x + x + 1] = color;
 +    }
 +
 +    // Draw red line
 +
 +    for (y=box_y - 8; y<box_y; ++y)
 +    {
 +        I_VideoBuffer[y * SCREENWIDTH + box_x + redline_x] = COLOR_RED;
 +    }
 +}
 +
 +int G_CmdChecksum (ticcmd_t* cmd)
 +{
 +    size_t		i;
 +    int		sum = 0;
 +
 +    for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++)
 +	sum += ((int *)cmd)[i];
 +
 +    return sum;
 +}
-
++
++static boolean WeaponSelectable(weapontype_t weapon)
++{
++    // Can't select a weapon if we don't own it.
++
++    if (!players[consoleplayer].weaponowned[weapon])
++    {
++        return false;
++    }
++
++    // STRIFE-TODO: Special weapon cycling rules?
++
++    return true;
++}
++
++static int G_NextWeapon(int direction)
++{
++    weapontype_t weapon;
++    int i;
++
++    // Find index in the table.
++
++    if (players[consoleplayer].pendingweapon == wp_nochange)
++    {
++        weapon = players[consoleplayer].readyweapon;
++    }
++    else
++    {
++        weapon = players[consoleplayer].pendingweapon;
++    }
++
++    for (i=0; i<arrlen(weapon_order_table); ++i)
++    {
++        if (weapon_order_table[i].weapon == weapon)
++        {
++            break;
++        }
++    }
++
++    // Switch weapon.
++
++    do
++    {
++        i += direction;
++        i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table);
++    } while (!WeaponSelectable(weapon_order_table[i].weapon));
++
++    return weapon_order_table[i].weapon_num;
++}
 +
 +//
 +// G_BuildTiccmd
 +// Builds a ticcmd from all of the available inputs
 +// or reads it from the demo buffer.
 +// If recording a demo, write it out
 +//
 +void G_BuildTiccmd (ticcmd_t* cmd)
 +{
 +    int		i;
 +    boolean	strafe;
 +    boolean	bstrafe;
 +    int		speed;
 +    int		tspeed;
 +    int		forward;
 +    int		side;
 +
 +    memset(cmd, 0, sizeof(ticcmd_t));
 +
 +    cmd->consistancy =
 +	consistancy[consoleplayer][maketic%BACKUPTICS];
 +
 +    // villsa [STRIFE]
 +    if(gamekeydown[key_lookup])
 +        cmd->buttons2 |= BT2_LOOKUP;
 +    if (gamekeydown[key_lookdown])
 +        cmd->buttons2 |= BT2_LOOKDOWN;
 +    if (gamekeydown[key_usehealth])
 +        cmd->buttons2 |= BT2_HEALTH;
 +
 +
 +
 +    strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
 +	|| joybuttons[joybstrafe];
 +
 +    // fraggle: support the old "joyb_speed = 31" hack which
 +    // allowed an autorun effect
 +
 +    speed = key_speed >= NUMKEYS
 +         || joybspeed >= MAX_JOY_BUTTONS
 +         || gamekeydown[key_speed]
 +         || joybuttons[joybspeed];
 +
 +    forward = side = 0;
 +
 +    // villsa [STRIFE] running causes centerview to occur
 +    if(speed)
 +        cmd->buttons2 |= BT2_CENTERVIEW;
 +
 +    // villsa [STRIFE] disable running if low on health
 +    if (players[consoleplayer].health <= 15)
 +        speed = 0;
 +
 +    // use two stage accelerative turning
 +    // on the keyboard and joystick
 +    if (joyxmove < 0
 +	|| joyxmove > 0
 +	|| gamekeydown[key_right]
 +	|| gamekeydown[key_left])
 +	turnheld += ticdup;
 +    else
 +	turnheld = 0;
 +
 +    if (turnheld < SLOWTURNTICS)
 +	tspeed = 2;             // slow turn
 +    else
 +	tspeed = speed;
 +
 +    // let movement keys cancel each other out
 +    if (strafe)
 +    {
 +	if (gamekeydown[key_right])
 +	{
 +	    // fprintf(stderr, "strafe right\n");
 +	    side += sidemove[speed];
 +	}
 +	if (gamekeydown[key_left])
 +	{
 +	    //	fprintf(stderr, "strafe left\n");
 +	    side -= sidemove[speed];
 +	}
 +	if (joyxmove > 0)
 +	    side += sidemove[speed];
 +	if (joyxmove < 0)
 +	    side -= sidemove[speed];
 +
 +    }
 +    else
 +    {
 +	if (gamekeydown[key_right])
 +	    cmd->angleturn -= angleturn[tspeed];
 +	if (gamekeydown[key_left])
 +	    cmd->angleturn += angleturn[tspeed];
 +	if (joyxmove > 0)
 +	    cmd->angleturn -= angleturn[tspeed];
 +	if (joyxmove < 0)
 +	    cmd->angleturn += angleturn[tspeed];
 +    }
 +
 +    if (gamekeydown[key_up])
 +    {
 +	// fprintf(stderr, "up\n");
 +	forward += forwardmove[speed];
 +    }
 +    if (gamekeydown[key_down])
 +    {
 +	// fprintf(stderr, "down\n");
 +	forward -= forwardmove[speed];
 +    }
 +
 +    if (joyymove < 0)
 +        forward += forwardmove[speed];
 +    if (joyymove > 0)
 +        forward -= forwardmove[speed];
 +
 +    if (gamekeydown[key_strafeleft]
 +     || joybuttons[joybstrafeleft]
 +     || mousebuttons[mousebstrafeleft])
 +    {
 +        side -= sidemove[speed];
 +    }
 +
 +    if (gamekeydown[key_straferight]
 +     || joybuttons[joybstraferight]
 +     || mousebuttons[mousebstraferight])
 +    {
 +        side += sidemove[speed];
 +    }
 +
 +    // buttons
 +    cmd->chatchar = HU_dequeueChatChar();
 +
 +    // villsa [STRIFE] TODO - add mouse button support for jump
 +    if(gamekeydown[key_jump] /*|| mousebuttons[mousebjump]*/)
 +        cmd->buttons2 |= BT2_JUMP;
 +
 +    // villsa [STRIFE]: Moved mousebuttons[mousebfire] to below
 +    if (gamekeydown[key_fire] || joybuttons[joybfire])
 +        cmd->buttons |= BT_ATTACK;
 +
 +    // villsa [STRIFE]
 +    if(mousebuttons[mousebfire])
 +    {
 +         if(mouse_fire_countdown <= 0)
 +             cmd->buttons |= BT_ATTACK;
 +         else
 +             --mouse_fire_countdown;
 +    }
 +
 +    if (gamekeydown[key_use]
 +     || joybuttons[joybuse]
 +     || mousebuttons[mousebuse])
 +    {
 +	cmd->buttons |= BT_USE;
 +	// clear double clicks if hit use button
 +	dclicks = 0;
 +    }
 +
-     // chainsaw overrides
++    // If the previous or next weapon button is pressed, the
++    // next_weapon variable is set to change weapons when
++    // we generate a ticcmd.  Choose a new weapon.
 +
-     for (i=0; i<arrlen(weapon_keys); ++i)
++    if (next_weapon != 0)
 +    {
-         int key = *weapon_keys[i];
++        i = G_NextWeapon(next_weapon);
++        cmd->buttons |= BT_CHANGE;
++        cmd->buttons |= i << BT_WEAPONSHIFT;
++        next_weapon = 0;
++    }
++    else
++    {
++        // Check weapon keys.
 +
-         if (gamekeydown[key])
++        for (i=0; i<arrlen(weapon_keys); ++i)
 +        {
- 	    cmd->buttons |= BT_CHANGE;
- 	    cmd->buttons |= i<<BT_WEAPONSHIFT;
- 	    break;
++            int key = *weapon_keys[i];
++
++            if (gamekeydown[key])
++            {
++                cmd->buttons |= BT_CHANGE;
++                cmd->buttons |= i<<BT_WEAPONSHIFT;
++                break;
++            }
 +        }
 +    }
-
++
 +    // mouse
 +    if (mousebuttons[mousebforward])
 +    {
 +	forward += forwardmove[speed];
 +    }
 +    if (mousebuttons[mousebbackward])
 +    {
 +        forward -= forwardmove[speed];
 +    }
 +
 +    if (dclick_use)
 +    {
 +        // forward double click
 +        if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
 +        {
 +            dclickstate = mousebuttons[mousebforward];
 +            if (dclickstate)
 +                dclicks++;
 +            if (dclicks == 2)
 +            {
 +                cmd->buttons |= BT_USE;
 +                dclicks = 0;
 +            }
 +            else
 +                dclicktime = 0;
 +        }
 +        else
 +        {
 +            dclicktime += ticdup;
 +            if (dclicktime > 20)
 +            {
 +                dclicks = 0;
 +                dclickstate = 0;
 +            }
 +        }
 +
 +        // strafe double click
 +        bstrafe =
 +            mousebuttons[mousebstrafe]
 +            || joybuttons[joybstrafe];
 +        if (bstrafe != dclickstate2 && dclicktime2 > 1 )
 +        {
 +            dclickstate2 = bstrafe;
 +            if (dclickstate2)
 +                dclicks2++;
 +            if (dclicks2 == 2)
 +            {
 +                cmd->buttons |= BT_USE;
 +                dclicks2 = 0;
 +            }
 +            else
 +                dclicktime2 = 0;
 +        }
 +        else
 +        {
 +            dclicktime2 += ticdup;
 +            if (dclicktime2 > 20)
 +            {
 +                dclicks2 = 0;
 +                dclickstate2 = 0;
 +            }
 +        }
 +    }
 +
 +    forward += mousey;
 +
 +    if (strafe)
 +	side += mousex*2;
 +    else
 +	cmd->angleturn -= mousex*0x8;
 +
 +    if (mousex == 0)
 +    {
 +        // No movement in the previous frame
 +
 +        testcontrols_mousespeed = 0;
 +    }
 +
 +    mousex = mousey = 0;
 +
 +    if (forward > MAXPLMOVE)
 +	forward = MAXPLMOVE;
 +    else if (forward < -MAXPLMOVE)
 +	forward = -MAXPLMOVE;
 +    if (side > MAXPLMOVE)
 +	side = MAXPLMOVE;
 +    else if (side < -MAXPLMOVE)
 +	side = -MAXPLMOVE;
 +
 +    cmd->forwardmove += forward;
 +    cmd->sidemove += side;
 +
 +    // special buttons
 +    if (sendpause)
 +    {
 +	sendpause = false;
 +	cmd->buttons = BT_SPECIAL | BTS_PAUSE;
 +    }
 +
 +    if (sendsave)
 +    {
 +	sendsave = false;
 +	cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
 +    }
 +
 +    // low-res turning
 +
 +    if (lowres_turn)
 +    {
 +        static signed short carry = 0;
 +        signed short desired_angleturn;
 +
 +        desired_angleturn = cmd->angleturn + carry;
 +
 +        // round angleturn to the nearest 256 unit boundary
 +        // for recording demos with single byte values for turn
 +
 +        cmd->angleturn = (desired_angleturn + 128) & 0xff00;
 +
 +        // Carry forward the error from the reduced resolution to the
 +        // next tic, so that successive small movements can accumulate.
 +
 +        carry = desired_angleturn - cmd->angleturn;
 +    }
 +}
 +
 +
 +//
 +// G_DoLoadLevel
 +//
 +extern  gamestate_t     wipegamestate;
 +
 +void G_DoLoadLevel (void)
 +{
 +    int             i;
 +
 +    // Set the sky map.
 +    // First thing, we have a dummy sky texture name,
 +    //  a flat. The data is in the WAD only because
 +    //  we look for an actual index, instead of simply
 +    //  setting one.
 +
 +    skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME));
 +
 +    levelstarttic = gametic;        // for time calculation
 +
 +    if (wipegamestate == GS_LEVEL)
 +	wipegamestate = -1;             // force a wipe
 +
 +    gamestate = GS_LEVEL;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	turbodetected[i] = false;
 +	if (playeringame[i] && players[i].playerstate == PST_DEAD)
 +	    players[i].playerstate = PST_REBORN;
 +	memset (players[i].frags,0,sizeof(players[i].frags));
 +    }
 +
 +    P_SetupLevel (gameepisode, gamemap, 0, gameskill);
 +    displayplayer = consoleplayer;		// view the guy you are playing
 +    gameaction = ga_nothing;
 +    Z_CheckHeap ();
 +
 +    // clear cmd building stuff
 +
 +    memset (gamekeydown, 0, sizeof(gamekeydown));
 +    joyxmove = joyymove = 0;
 +    mousex = mousey = 0;
 +    sendpause = sendsave = paused = false;
 +    memset (mousebuttons, 0, sizeof(mousebuttons));
 +    memset (joybuttons, 0, sizeof(joybuttons));
 +
 +    if (testcontrols)
 +    {
 +        players[consoleplayer].message = "Press escape to quit.";
 +    }
 +
 +    P_DialogLoad(); // villsa [STRIFE]
 +}
-
 +
 +static void SetJoyButtons(unsigned int buttons_mask)
 +{
 +    int i;
 +
 +    for (i=0; i<MAX_JOY_BUTTONS; ++i)
 +    {
-         joybuttons[i] = (buttons_mask & (1 << i)) != 0;
++        int button_on = (buttons_mask & (1 << i)) != 0;
++
++        // Detect button press:
++
++        if (!joybuttons[i] && button_on)
++        {
++            // Weapon cycling:
++
++            if (i == joybprevweapon)
++            {
++                next_weapon = -1;
++            }
++            else if (i == joybnextweapon)
++            {
++                next_weapon = 1;
++            }
++        }
++
++        joybuttons[i] = button_on;
 +    }
 +}
-
++
++static void SetMouseButtons(unsigned int buttons_mask)
++{
++    int i;
++
++    for (i=0; i<MAX_MOUSE_BUTTONS; ++i)
++    {
++        unsigned int button_on = (buttons_mask & (1 << i)) != 0;
++
++        // Detect button press:
++
++        if (!mousebuttons[i] && button_on)
++        {
++            if (i == mousebprevweapon)
++            {
++                next_weapon = -1;
++            }
++            else if (i == mousebnextweapon)
++            {
++                next_weapon = 1;
++            }
++        }
++
++	mousebuttons[i] = button_on;
++    }
++}
++
 +//
 +// G_Responder
 +// Get info needed to make ticcmd_ts for the players.
 +//
 +boolean G_Responder (event_t* ev)
 +{
 +    // allow spy mode changes even during the demo
 +    if (gamestate == GS_LEVEL && ev->type == ev_keydown
-      && ev->data1 == KEY_F12 && (singledemo || !deathmatch) )
++     && ev->data1 == key_spy && (singledemo || !deathmatch) )
 +    {
 +	// spy mode
 +	do
 +	{
 +	    displayplayer++;
 +	    if (displayplayer == MAXPLAYERS)
 +		displayplayer = 0;
 +	} while (!playeringame[displayplayer] && displayplayer != consoleplayer);
 +	return true;
 +    }
 +
 +    // any other key pops up menu if in demos
 +    if (gameaction == ga_nothing && !singledemo &&
 +	(demoplayback || gamestate == GS_DEMOSCREEN)
 +	)
 +    {
 +	if (ev->type == ev_keydown ||
 +	    (ev->type == ev_mouse && ev->data1) ||
 +	    (ev->type == ev_joystick && ev->data1) )
 +	{
 +	    M_StartControlPanel ();
 +	    return true;
 +	}
 +	return false;
 +    }
 +
 +    if (gamestate == GS_LEVEL)
 +    {
 +#if 0
 +	if (devparm && ev->type == ev_keydown && ev->data1 == ';')
 +	{
 +	    G_DeathMatchSpawnPlayer (0);
 +	    return true;
 +	}
 +#endif
 +	if (HU_Responder (ev))
 +	    return true;	// chat ate the event
 +	if (ST_Responder (ev))
 +	    return true;	// status window ate it
 +	if (AM_Responder (ev))
 +	    return true;	// automap ate it
 +    }
 +
 +    if (gamestate == GS_FINALE)
 +    {
 +	if (F_Responder (ev))
 +	    return true;	// finale ate the event
 +    }
 +
 +    if (testcontrols && ev->type == ev_mouse)
 +    {
 +        // If we are invoked by setup to test the controls, save the
 +        // mouse speed so that we can display it on-screen.
 +        // Perform a low pass filter on this so that the thermometer
 +        // appears to move smoothly.
 +
 +        testcontrols_mousespeed = abs(ev->data2);
 +    }
 +
++    // If the next/previous weapon keys are pressed, set the next_weapon
++    // variable to change weapons when the next ticcmd is generated.
++
++    if (ev->type == ev_keydown && ev->data1 == key_prevweapon)
++    {
++        next_weapon = -1;
++    }
++    else if (ev->type == ev_keydown && ev->data1 == key_nextweapon)
++    {
++        next_weapon = 1;
++    }
++
 +    switch (ev->type)
 +    {
 +      case ev_keydown:
 +	if (ev->data1 == key_pause)
 +	{
 +	    sendpause = true;
 +	}
 +        else if (ev->data1 <NUMKEYS)
 +        {
 +	    gamekeydown[ev->data1] = true;
 +        }
 +
 +	return true;    // eat key down events
 +
 +      case ev_keyup:
 +	if (ev->data1 <NUMKEYS)
 +	    gamekeydown[ev->data1] = false;
 +	return false;   // always let key up events filter down
 +
 +      case ev_mouse:
- 	mousebuttons[0] = ev->data1 & 1;
- 	mousebuttons[1] = ev->data1 & 2;
- 	mousebuttons[2] = ev->data1 & 4;
++        SetMouseButtons(ev->data1);
 +	mousex = ev->data2*(mouseSensitivity+5)/10;
 +	mousey = ev->data3*(mouseSensitivity+5)/10;
 +	return true;    // eat events
 +
 +      case ev_joystick:
 +        SetJoyButtons(ev->data1);
 +	joyxmove = ev->data2;
 +	joyymove = ev->data3;
 +	return true;    // eat events
 +
 +      default:
 +	break;
 +    }
 +
 +    return false;
 +}
 +
 +//
 +// G_Ticker
 +// Make ticcmd_ts for the players.
 +//
 +void G_Ticker (void)
 +{
 +    int		i;
 +    int		buf;
 +    ticcmd_t*	cmd;
 +
 +    // do player reborns if needed
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +	if (playeringame[i] && players[i].playerstate == PST_REBORN)
 +	    G_DoReborn (i);
 +
 +    // do things to change the game state
 +    while (gameaction != ga_nothing)
 +    {
 +	switch (gameaction)
 +	{
 +	  case ga_loadlevel:
 +	    G_DoLoadLevel ();
 +	    break;
 +	  case ga_newgame:
 +	    G_DoNewGame ();
 +	    break;
 +	  case ga_loadgame:
 +	    G_DoLoadGame ();
 +	    break;
 +	  case ga_savegame:
 +	    G_DoSaveGame ();
 +	    break;
 +	  case ga_playdemo:
 +	    G_DoPlayDemo ();
 +	    break;
 +	  case ga_completed:
 +	    G_DoCompleted ();
 +	    break;
 +	  case ga_victory:
 +	    F_StartFinale ();
 +	    break;
 +	  case ga_worlddone:
 +	    G_DoWorldDone ();
 +	    break;
 +	  case ga_screenshot:
 +	    V_ScreenShot("DOOM%02i.pcx");
 +            players[consoleplayer].message = DEH_String("screen shot");
 +	    gameaction = ga_nothing;
 +	    break;
 +	  case ga_nothing:
 +	    break;
 +	}
 +    }
 +
 +    // get commands, check consistancy,
 +    // and build new consistancy check
 +    buf = (gametic/ticdup)%BACKUPTICS;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (playeringame[i])
 +	{
 +	    cmd = &players[i].cmd;
 +
 +	    memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
 +
 +	    if (demoplayback)
 +		G_ReadDemoTiccmd (cmd);
 +	    if (demorecording)
 +		G_WriteDemoTiccmd (cmd);
 +
 +	    // check for turbo cheats
 +
 +            // check ~ 4 seconds whether to display the turbo message.
 +            // store if the turbo threshold was exceeded in any tics
 +            // over the past 4 seconds.  offset the checking period
 +            // for each player so messages are not displayed at the
 +            // same time.
 +
 +            if (cmd->forwardmove > TURBOTHRESHOLD)
 +            {
 +                turbodetected[i] = true;
 +            }
 +
 +	    if ((gametic & 31) == 0
 +             && ((gametic >> 5) % MAXPLAYERS) == i
 +             && turbodetected[i])
 +	    {
 +		static char turbomessage[80];
 +		extern char *player_names[4];
 +		sprintf (turbomessage, "%s is turbo!",player_names[i]);
 +		players[consoleplayer].message = turbomessage;
 +                turbodetected[i] = false;
 +	    }
 +
 +	    if (netgame && !netdemo && !(gametic%ticdup) )
 +	    {
 +		if (gametic > BACKUPTICS
 +		    && consistancy[i][buf] != cmd->consistancy)
 +		{
 +		    I_Error ("consistency failure (%i should be %i)",
 +			     cmd->consistancy, consistancy[i][buf]);
 +		}
 +		if (players[i].mo)
 +		    consistancy[i][buf] = players[i].mo->x;
 +		else
 +		    consistancy[i][buf] = rndindex;
 +	    }
 +	}
 +    }
 +
 +    // check for special buttons
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (playeringame[i])
 +	{
 +	    if (players[i].cmd.buttons & BT_SPECIAL)
 +	    {
 +		switch (players[i].cmd.buttons & BT_SPECIALMASK)
 +		{
 +		  case BTS_PAUSE:
 +		    paused ^= 1;
 +		    if (paused)
 +			S_PauseSound ();
 +		    else
 +			S_ResumeSound ();
 +		    break;
 +
 +		  case BTS_SAVEGAME:
 +		    if (!savedescription[0])
 +			strcpy (savedescription, "NET GAME");
 +		    savegameslot =
 +			(players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
 +		    gameaction = ga_savegame;
 +		    break;
 +		}
 +	    }
 +	}
 +    }
 +
 +    // Have we just finished displaying an intermission screen?
 +    // haleyjd 08/23/10: [STRIFE] No intermission.
 +    /*
 +    if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION)
 +    {
 +        WI_End();
 +    }
 +    */
 +
 +    oldgamestate = gamestate;
 +
 +    // do main actions
 +    switch (gamestate)
 +    {
 +    case GS_LEVEL:
 +        P_Ticker ();
 +        ST_Ticker ();
 +        AM_Ticker ();
 +        HU_Ticker ();
 +        break;
 +
 +        // haleyjd 08/23/10: [STRIFE] No intermission.
 +        /*
 +    case GS_INTERMISSION:
 +        WI_Ticker ();
 +        break;
 +        */
 +
 +    case GS_FINALE:
 +        F_Ticker ();
 +        break;
 +
 +    case GS_DEMOSCREEN:
 +        D_PageTicker ();
 +        break;
 +    }
 +}
 +
 +
 +//
 +// PLAYER STRUCTURE FUNCTIONS
 +// also see P_SpawnPlayer in P_Things
 +//
 +
 +//
 +// G_InitPlayer
 +// Called at the start.
 +// Called by the game initialization functions.
 +//
 +void G_InitPlayer (int player)
 +{
 +    player_t*	p;
 +
 +    // set up the saved info
 +    p = &players[player];
 +
 +    // clear everything else to defaults
 +    G_PlayerReborn (player);
 +
 +}
 +
 +
 +
 +//
 +// G_PlayerFinishLevel
 +// Can when a player completes a level.
 +//
 +void G_PlayerFinishLevel (int player)
 +{
 +    player_t*	p;
 +
 +    p = &players[player];
 +
 +    memset (p->powers, 0, sizeof (p->powers));
 +    memset (p->cards, 0, sizeof (p->cards));
 +    p->mo->flags &= ~MF_SHADOW;		// cancel invisibility
 +    p->extralight = 0;			// cancel gun flashes
 +    p->fixedcolormap = 0;		// cancel ir gogles
 +    p->damagecount = 0;			// no palette changes
 +    p->bonuscount = 0;
 +}
 +
 +
 +//
 +// G_PlayerReborn
 +// Called after a player dies
 +// almost everything is cleared and initialized
 +//
 +void G_PlayerReborn (int player)
 +{
 +    player_t*	p;
 +    int		i;
 +    int		frags[MAXPLAYERS];
 +    int		killcount;
 +
 +    memcpy (frags,players[player].frags,sizeof(frags));
 +    killcount = players[player].killcount;
 +
 +    p = &players[player];
 +    memset (p, 0, sizeof(*p));
 +
 +    memcpy (players[player].frags, frags, sizeof(players[player].frags));
 +    players[player].killcount = killcount;
 +
 +    p->usedown = p->attackdown = true;	// don't do anything immediately
 +    p->playerstate = PST_LIVE;
 +    p->health = deh_initial_health;     // Use dehacked value
 +    p->readyweapon = p->pendingweapon = wp_fist; // villsa [STRIFE] default to fists
 +    p->weaponowned[wp_fist] = true;
 +    //p->weaponowned[wp_pistol] = true; // villsa [STRIFE] unused
 +    //p->ammo[am_clip] = deh_initial_bullets;   // villsa [STRIFE] unused
 +
 +    for (i=0 ; i<NUMAMMO ; i++)
 +	p->maxammo[i] = maxammo[i];
 +
 +}
 +
 +//
 +// G_CheckSpot
 +// Returns false if the player cannot be respawned
 +// at the given mapthing_t spot
 +// because something is occupying it
 +//
 +void P_SpawnPlayer (mapthing_t* mthing);
 +
 +boolean
 +G_CheckSpot
 +( int		playernum,
 +  mapthing_t*	mthing )
 +{
 +    fixed_t		x;
 +    fixed_t		y;
 +    subsector_t*	ss;
 +    unsigned		an;
 +    mobj_t*		mo;
 +    int			i;
 +
 +    if (!players[playernum].mo)
 +    {
 +	// first spawn of level, before corpses
 +	for (i=0 ; i<playernum ; i++)
 +	    if (players[i].mo->x == mthing->x << FRACBITS
 +		&& players[i].mo->y == mthing->y << FRACBITS)
 +		return false;
 +	return true;
 +    }
 +
 +    x = mthing->x << FRACBITS;
 +    y = mthing->y << FRACBITS;
 +
 +    if (!P_CheckPosition (players[playernum].mo, x, y) )
 +	return false;
 +
 +    // flush an old corpse if needed
 +    if (bodyqueslot >= BODYQUESIZE)
 +	P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]);
 +    bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo;
 +    bodyqueslot++;
 +
 +    // spawn a teleport fog
 +    ss = R_PointInSubsector (x,y);
 +    an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) >> ANGLETOFINESHIFT;
 +
 +    mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]
 +		      , ss->sector->floorheight
 +		      , MT_TFOG);
 +
 +    if (players[consoleplayer].viewz != 1)
 +	S_StartSound (mo, sfx_telept);	// don't start sound on first frame
 +
 +    return true;
 +}
 +
 +
 +//
 +// G_DeathMatchSpawnPlayer
 +// Spawns a player at one of the random death match spots
 +// called at level load and each death
 +//
 +void G_DeathMatchSpawnPlayer (int playernum)
 +{
 +    int             i,j;
 +    int				selections;
 +
 +    selections = deathmatch_p - deathmatchstarts;
 +    if (selections < 4)
 +	I_Error ("Only %i deathmatch spots, 4 required", selections);
 +
 +    for (j=0 ; j<20 ; j++)
 +    {
 +	i = P_Random() % selections;
 +	if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
 +	{
 +	    deathmatchstarts[i].type = playernum+1;
 +	    P_SpawnPlayer (&deathmatchstarts[i]);
 +	    return;
 +	}
 +    }
 +
 +    // no good spot, so the player will probably get stuck
 +    P_SpawnPlayer (&playerstarts[playernum]);
 +}
 +
 +//
 +// G_DoReborn
 +//
 +void G_DoReborn (int playernum)
 +{
 +    int                             i;
 +
 +    if (!netgame)
 +    {
 +	// reload the level from scratch
 +	gameaction = ga_loadlevel;
 +    }
 +    else
 +    {
 +	// respawn at the start
 +
 +	// first dissasociate the corpse
 +	players[playernum].mo->player = NULL;
 +
 +	// spawn at random spot if in death match
 +	if (deathmatch)
 +	{
 +	    G_DeathMatchSpawnPlayer (playernum);
 +	    return;
 +	}
 +
 +	if (G_CheckSpot (playernum, &playerstarts[playernum]) )
 +	{
 +	    P_SpawnPlayer (&playerstarts[playernum]);
 +	    return;
 +	}
 +
 +	// try to spawn at one of the other players spots
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (G_CheckSpot (playernum, &playerstarts[i]) )
 +	    {
 +		playerstarts[i].type = playernum+1;	// fake as other player
 +		P_SpawnPlayer (&playerstarts[i]);
 +		playerstarts[i].type = i+1;		// restore
 +		return;
 +	    }
 +	    // he's going to be inside something.  Too bad.
 +	}
 +	P_SpawnPlayer (&playerstarts[playernum]);
 +    }
 +}
 +
 +
 +void G_ScreenShot (void)
 +{
 +    gameaction = ga_screenshot;
 +}
 +
 +// haleyjd 08/23/2010: [STRIFE] Removed par times.
 +
 +//
 +// G_DoCompleted
 +//
 +//boolean         secretexit;
 +extern char*	pagename;
 +
 +//
 +// G_RiftExitLevel
 +//
 +// haleyjd 08/24/10: [STRIFE] New function
 +// * Called from some exit linedefs to exit to a specific riftspot in the
 +//   given destination map.
 +//
 +void G_RiftExitLevel(int map, int spot, angle_t angle)
 +{
 +    gameaction = ga_completed;
 +    // STRIFE-TODO: special handling for post-Sigil map changes
 +    /*
 +    if(players[0].weaponowned[wp_sigil])
 +    {
 +        if(map == 3) // Front Base -> Abandoned Front Base
 +            map = 30;
 +        if(map == 7) // Castle -> New Front Base
 +            map = 10;
 +    }
 +    */
 +    // no rifting in deathmatch games
 +    if(deathmatch)
 +        spot = 0;
 +    riftangle = angle;
 +    riftdest  = spot;
 +    destmap   = map;
 +}
 +
 +//
 +// G_ExitLevel
 +//
 +// haleyjd 08/24/10: [STRIFE]:
 +// * Default to next map in numeric order; init destmap and riftdest.
 +//
 +void G_ExitLevel (int dest)
 +{
 +    if(dest == 0)
 +        dest = gamemap + 1;
 +    destmap = dest;
 +    riftdest = 0;
 +    gameaction = ga_completed;
 +}
 +
 +/*
 +// haleyjd 08/23/2010: [STRIFE] No secret exits in Strife.
 +// Here's for the german edition.
 +void G_SecretExitLevel (void)
 +{
 +    // IF NO WOLF3D LEVELS, NO SECRET EXIT!
 +    if ( (gamemode == commercial)
 +        && (W_CheckNumForName("map31")<0))
 +        secretexit = false;
 +    else
 +        secretexit = true;
 +    gameaction = ga_completed;
 +}
 +*/
 +
 +//
 +// G_DoCompleted
 +//
 +// haleyjd 08/23/10: [STRIFE]:
 +// * Removed G_PlayerFinishLevel call for now... STRIFE-TODO
 +// * Removed Chex, as not relevant to Strife.
 +// * Removed DOOM level transfer logic
 +// * Removed intermission code.
 +// * Added setting gameaction to ga_worlddone.
 +//
 +void G_DoCompleted (void)
 +{
 +    // STRIFE-TODO: save automap powerup state (possibly inlined from G_PlayerFinishLevel);
 +    // set stonecold to 0
 +
 +    if (automapactive)
 +        AM_Stop ();
 +
 +    // STRIFE-TODO: needs call to G_DoSaveGame for hubs
 +    // if(!deathmatch)
 +    //     G_DoSaveGame(savepath2);
 +
 +    gameaction = ga_worlddone;
 +}
 +
 +
 +// haleyjd 08/24/10: [STRIFE] No secret exits.
 +/*
 +//
 +// G_WorldDone
 +//
 +void G_WorldDone (void)
 +{
 +    gameaction = ga_worlddone;
 +
 +    if (secretexit)
 +        players[consoleplayer].didsecret = true;
 +
 +    if ( gamemode == commercial )
 +    {
 +	switch (gamemap)
 +	{
 +	  case 15:
 +	  case 31:
 +	    if (!secretexit)
 +		break;
 +	  case 6:
 +	  case 11:
 +	  case 20:
 +	  case 30:
 +	    F_StartFinale ();
 +	    break;
 +	}
 +    }
 +}
 +*/
 +
 +//
 +// G_RiftPlayer
 +//
 +// haleyjd 08/24/10: [STRIFE] New function
 +// Teleports the player to the appropriate rift spot.
 +//
 +void G_RiftPlayer(void)
 +{
 +    if(riftdest)
 +    {
 +        P_TeleportMove(players[0].mo,
 +                       riftSpots[riftdest - 1].x << FRACBITS,
 +                       riftSpots[riftdest - 1].y << FRACBITS);
 +        players[0].mo->angle  = riftangle;
 +        players[0].mo->health = players[0].health;
 +    }
 +}
 +
 +//
 +// G_RiftCheat
 +//
 +// haleyjd 08/24/10: [STRIFE] New function
 +// Called from the cheat code to jump to a rift spot.
 +//
 +boolean G_RiftCheat(int riftSpotNum)
 +{
 +    return P_TeleportMove(players[0].mo,
 +                          riftSpots[riftSpotNum - 1].x << FRACBITS,
 +                          riftSpots[riftSpotNum - 1].y << FRACBITS);
 +}
 +
 +//
 +// G_DoWorldDone
 +//
 +// haleyjd 08/24/10: [STRIFE] Added destmap -> gamemap set.
 +// STRIFE-TODO: Load hub save and other changes.
 +//
 +void G_DoWorldDone (void)
 +{
 +    gamestate = GS_LEVEL;
 +    gamemap = destmap;
 +    G_DoLoadLevel ();
 +    gameaction = ga_nothing;
 +    viewactive = true;
 +}
 +
 +
 +
 +//
 +// G_InitFromSavegame
 +// Can be called by the startup code or the menu task.
 +//
 +extern boolean setsizeneeded;
 +void R_ExecuteSetViewSize (void);
 +
 +char	savename[256];
 +
 +void G_LoadGame (char* name)
 +{
 +    strcpy (savename, name);
 +    gameaction = ga_loadgame;
 +}
 +
 +#define VERSIONSIZE		16
 +
 +
 +void G_DoLoadGame (void)
 +{
 +    int savedleveltime;
 +
 +    gameaction = ga_nothing;
 +
 +    save_stream = fopen(savename, "rb");
 +
 +    if (save_stream == NULL)
 +    {
 +        return;
 +    }
 +
++    savegame_error = false;
++
 +    if (!P_ReadSaveGameHeader())
 +    {
 +        fclose(save_stream);
 +        return;
 +    }
 +
 +    savedleveltime = leveltime;
 +
 +    // load a base level
 +    G_InitNew (gameskill, gameepisode, gamemap);
 +
 +    leveltime = savedleveltime;
 +
 +    // dearchive all the modifications
 +    P_UnArchivePlayers ();
 +    P_UnArchiveWorld ();
 +    P_UnArchiveThinkers ();
 +    P_UnArchiveSpecials ();
 +
 +    if (!P_ReadSaveGameEOF())
 +	I_Error ("Bad savegame");
 +
 +    fclose(save_stream);
 +
 +    if (setsizeneeded)
 +	R_ExecuteSetViewSize ();
 +
 +    // draw the pattern into the back screen
 +    R_FillBackScreen ();
 +}
 +
 +
 +//
 +// G_SaveGame
 +// Called by the menu task.
 +// Description is a 24 byte text string
 +//
 +void
 +G_SaveGame
 +( int	slot,
 +  char*	description )
 +{
 +    savegameslot = slot;
 +    strcpy (savedescription, description);
 +    sendsave = true;
 +}
 +
 +void G_DoSaveGame (void)
 +{
 +    char *savegame_file;
 +    char *temp_savegame_file;
 +
 +    temp_savegame_file = P_TempSaveGameFile();
 +    savegame_file = P_SaveGameFile(savegameslot);
 +
 +    // Open the savegame file for writing.  We write to a temporary file
 +    // and then rename it at the end if it was successfully written.
 +    // This prevents an existing savegame from being overwritten by
 +    // a corrupted one, or if a savegame buffer overrun occurs.
 +
 +    save_stream = fopen(temp_savegame_file, "wb");
 +
 +    if (save_stream == NULL)
 +    {
 +        return;
 +    }
 +
++    savegame_error = false;
++
 +    P_WriteSaveGameHeader(savedescription);
 +
 +    P_ArchivePlayers ();
 +    P_ArchiveWorld ();
 +    P_ArchiveThinkers ();
 +    P_ArchiveSpecials ();
 +
 +    P_WriteSaveGameEOF();
 +
 +    // Enforce the same savegame size limit as in Vanilla Doom,
 +    // except if the vanilla_savegame_limit setting is turned off.
 +
 +    if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE)
 +    {
 +        I_Error ("Savegame buffer overrun");
 +    }
 +
 +    // Finish up, close the savegame file.
 +
 +    fclose(save_stream);
 +
 +    // Now rename the temporary savegame file to the actual savegame
 +    // file, overwriting the old savegame if there was one there.
 +
 +    remove(savegame_file);
 +    rename(temp_savegame_file, savegame_file);
 +
 +    gameaction = ga_nothing;
 +    strcpy(savedescription, "");
 +
 +    players[consoleplayer].message = DEH_String(GGSAVED);
 +
 +    // draw the pattern into the back screen
 +    R_FillBackScreen ();
 +}
 +
 +
 +//
 +// G_InitNew
 +// Can be called by the startup code or the menu task,
 +// consoleplayer, displayplayer, playeringame[] should be set.
 +//
 +skill_t	d_skill;
 +int     d_episode;
 +int     d_map;
 +
 +void
 +G_DeferedInitNew
 +( skill_t	skill,
 +  int		episode,
 +  int		map)
 +{
 +    d_skill = skill;
 +    d_episode = episode;
 +    d_map = map;
 +    gameaction = ga_newgame;
 +}
 +
 +
 +void G_DoNewGame (void)
 +{
 +    demoplayback = false;
 +    netdemo = false;
 +    netgame = false;
 +    deathmatch = false;
 +    playeringame[1] = playeringame[2] = playeringame[3] = 0;
 +    respawnparm = false;
 +    fastparm = false;
 +    nomonsters = false;
 +    consoleplayer = 0;
 +    G_InitNew (d_skill, d_episode, d_map);
 +    gameaction = ga_nothing;
 +}
 +
 +// The sky texture to be used instead of the F_SKY1 dummy.
 +extern  int	skytexture;
 +
 +//
 +// G_InitNew
 +//
 +// haleyjd 08/24/10: [STRIFE]:
 +// * Added riftdest initialization
 +//
 +void
 +G_InitNew
 +( skill_t	skill,
 +  int		episode,
 +  int		map )
 +{
 +    char *skytexturename;
 +    int             i;
 +
 +    if (paused)
 +    {
 +	paused = false;
 +	S_ResumeSound ();
 +    }
 +
 +
 +    if (skill > sk_nightmare)
 +	skill = sk_nightmare;
 +
 +
 +    // This was quite messy with SPECIAL and commented parts.
 +    // Supposedly hacks to make the latest edition work.
 +    // It might not work properly.
 +    if (episode < 1)
 +      episode = 1;
 +
 +    if ( gamemode == retail )
 +    {
 +      if (episode > 4)
 +	episode = 4;
 +    }
 +    else if ( gamemode == shareware )
 +    {
 +      if (episode > 1)
 +	   episode = 1;	// only start episode 1 on shareware
 +    }
 +    else
 +    {
 +      if (episode > 3)
 +	episode = 3;
 +    }
 +
 +
 +
 +    if (map < 1)
 +	map = 1;
 +
 +    if ( (map > 9)
 +	 && ( gamemode != commercial) )
 +      map = 9;
 +
 +    M_ClearRandom ();
 +
 +    if (skill == sk_nightmare || respawnparm )
 +	respawnmonsters = true;
 +    else
 +	respawnmonsters = false;
 +
 +    // STRIFE-TODO: (broken) Strife skill level mobjinfo/states tweaking
 +    /*if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare) )
 +    {
 +	for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
 +	    states[i].tics >>= 1;
 +	mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;
 +	mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;
 +	mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;
 +    }
 +    else if (skill != sk_nightmare && gameskill == sk_nightmare)
 +    {
 +	for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
 +	    states[i].tics <<= 1;
 +	mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;
 +	mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;
 +	mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;
 +    } */
 +
 +
 +    // force players to be initialized upon first level load
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +	players[i].playerstate = PST_REBORN;
 +
 +    usergame = true;                // will be set false if a demo
 +    paused = false;
 +    demoplayback = false;
 +    automapactive = false;
 +    viewactive = true;
 +    gameepisode = episode;
 +    gamemap = map;
 +    gameskill = skill;
 +    riftdest = 0; // haleyjd 08/24/10: [STRIFE] init riftdest to zero on new game
 +
 +    viewactive = true;
 +
 +    // Set the sky to use.
 +    //
 +    // Note: This IS broken, but it is how Vanilla Doom behaves.
 +    // See http://doom.wikia.com/wiki/Sky_never_changes_in_Doom_II.
 +    //
 +    // Because we set the sky here at the start of a game, not at the
 +    // start of a level, the sky texture never changes unless we
 +    // restore from a saved game.  This was fixed before the Doom
 +    // source release, but this IS the way Vanilla DOS Doom behaves.
 +
 +    // STRIFE-TODO: Strife skies (of which there are but two)
 +    // villsa [STRIFE] setting all skies to SKY03 as a placeholder
 +    if (gamemode == commercial)
 +    {
 +        if (gamemap < 12)
 +            skytexturename = "SKY03";
 +        else if (gamemap < 21)
 +            skytexturename = "SKY03";
 +        else
 +            skytexturename = "SKY03";
 +    }
 +    else
 +    {
 +        switch (gameepisode)
 +        {
 +          default:
 +          case 1:
 +            skytexturename = "SKY03";
 +            break;
 +          case 2:
 +            skytexturename = "SKY03";
 +            break;
 +          case 3:
 +            skytexturename = "SKY03";
 +            break;
 +          case 4:        // Special Edition sky
 +            skytexturename = "SKY03";
 +            break;
 +        }
 +    }
 +
 +    skytexturename = DEH_String(skytexturename);
 +
 +    skytexture = R_TextureNumForName(skytexturename);
 +
 +    // STRIFE-TODO:
 +    // G_LoadPath(gamemap)
 +    G_DoLoadLevel ();
 +}
 +
 +
 +//
 +// DEMO RECORDING
 +//
 +#define DEMOMARKER		0x80
 +
 +
 +void G_ReadDemoTiccmd (ticcmd_t* cmd)
 +{
 +    if (*demo_p == DEMOMARKER)
 +    {
 +	// end of demo data stream
 +	G_CheckDemoStatus ();
 +	return;
 +    }
 +    cmd->forwardmove = ((signed char)*demo_p++);
 +    cmd->sidemove = ((signed char)*demo_p++);
 +
 +    // If this is a longtics demo, read back in higher resolution
 +
 +    if (longtics)
 +    {
 +        cmd->angleturn = *demo_p++;
 +        cmd->angleturn |= (*demo_p++) << 8;
 +    }
 +    else
 +    {
 +        cmd->angleturn = ((unsigned char) *demo_p++)<<8;
 +    }
 +
 +    cmd->buttons = (unsigned char)*demo_p++;
 +}
 +
 +// Increase the size of the demo buffer to allow unlimited demos
 +
 +static void IncreaseDemoBuffer(void)
 +{
 +    int current_length;
 +    byte *new_demobuffer;
 +    byte *new_demop;
 +    int new_length;
 +
 +    // Find the current size
 +
 +    current_length = demoend - demobuffer;
 +
 +    // Generate a new buffer twice the size
 +    new_length = current_length * 2;
 +
 +    new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0);
 +    new_demop = new_demobuffer + (demo_p - demobuffer);
 +
 +    // Copy over the old data
 +
 +    memcpy(new_demobuffer, demobuffer, current_length);
 +
 +    // Free the old buffer and point the demo pointers at the new buffer.
 +
 +    Z_Free(demobuffer);
 +
 +    demobuffer = new_demobuffer;
 +    demo_p = new_demop;
 +    demoend = demobuffer + new_length;
 +}
 +
 +void G_WriteDemoTiccmd (ticcmd_t* cmd)
 +{
 +    byte *demo_start;
 +
-     if (gamekeydown['q'])           // press q to end demo recording
++    if (gamekeydown[key_demo_quit])           // press q to end demo recording
 +	G_CheckDemoStatus ();
 +
 +    demo_start = demo_p;
 +
 +    *demo_p++ = cmd->forwardmove;
 +    *demo_p++ = cmd->sidemove;
 +
 +    // If this is a longtics demo, record in higher resolution
 +
 +    if (longtics)
 +    {
 +        *demo_p++ = (cmd->angleturn & 0xff);
 +        *demo_p++ = (cmd->angleturn >> 8) & 0xff;
 +    }
 +    else
 +    {
 +        *demo_p++ = cmd->angleturn >> 8;
 +    }
 +
 +    *demo_p++ = cmd->buttons;
 +
 +    // reset demo pointer back
 +    demo_p = demo_start;
 +
 +    if (demo_p > demoend - 16)
 +    {
 +        if (vanilla_demo_limit)
 +        {
 +            // no more space
 +            G_CheckDemoStatus ();
 +            return;
 +        }
 +        else
 +        {
 +            // Vanilla demo limit disabled: unlimited
 +            // demo lengths!
 +
 +            IncreaseDemoBuffer();
 +        }
 +    }
 +
 +    G_ReadDemoTiccmd (cmd);         // make SURE it is exactly the same
 +}
 +
 +
 +
 +//
 +// G_RecordDemo
 +//
 +void G_RecordDemo (char* name)
 +{
 +    int             i;
 +    int				maxsize;
 +
 +    usergame = false;
 +    strcpy (demoname, name);
 +    strcat (demoname, ".lmp");
 +    maxsize = 0x20000;
 +
 +    //!
 +    // @arg <size>
 +    // @category demo
 +    // @vanilla
 +    //
 +    // Specify the demo buffer size (KiB)
 +    //
 +
 +    i = M_CheckParm ("-maxdemo");
 +    if (i && i<myargc-1)
 +	maxsize = atoi(myargv[i+1])*1024;
 +    demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL);
 +    demoend = demobuffer + maxsize;
 +
 +    demorecording = true;
 +}
 +
 +
 +void G_BeginRecording (void)
 +{
 +    int             i;
 +
 +    //!
 +    // @category demo
 +    //
 +    // Record a high resolution "Doom 1.91" demo.
 +    //
 +
 +    longtics = M_CheckParm("-longtics") != 0;
 +
 +    // If not recording a longtics demo, record in low res
 +
 +    lowres_turn = !longtics;
 +
 +    demo_p = demobuffer;
 +
 +    // Save the right version code for this demo
 +
 +    if (longtics)
 +    {
 +        *demo_p++ = DOOM_191_VERSION;
 +    }
 +    else
 +    {
 +        *demo_p++ = DOOM_VERSION;
 +    }
 +
 +    *demo_p++ = gameskill;
 +    *demo_p++ = gameepisode;
 +    *demo_p++ = gamemap;
 +    *demo_p++ = deathmatch;
 +    *demo_p++ = respawnparm;
 +    *demo_p++ = fastparm;
 +    *demo_p++ = nomonsters;
 +    *demo_p++ = consoleplayer;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +	*demo_p++ = playeringame[i];
 +}
 +
 +
 +//
 +// G_PlayDemo
 +//
 +
 +char*	defdemoname;
 +
 +void G_DeferedPlayDemo (char* name)
 +{
 +    defdemoname = name;
 +    gameaction = ga_playdemo;
 +}
 +
 +// Generate a string describing a demo version
 +
 +static char *DemoVersionDescription(int version)
 +{
 +    static char resultbuf[16];
 +
 +    switch (version)
 +    {
 +        case 104:
 +            return "v1.4";
 +        case 105:
 +            return "v1.5";
 +        case 106:
 +            return "v1.6/v1.666";
 +        case 107:
 +            return "v1.7/v1.7a";
 +        case 108:
 +            return "v1.8";
 +        case 109:
 +            return "v1.9";
 +        default:
 +            break;
 +    }
 +
 +    // Unknown version.  Perhaps this is a pre-v1.4 IWAD?  If the version
 +    // byte is in the range 0-4 then it can be a v1.0-v1.2 demo.
 +
 +    if (version >= 0 && version <= 4)
 +    {
 +        return "v1.0/v1.1/v1.2";
 +    }
 +    else
 +    {
 +        sprintf(resultbuf, "%i.%i (unknown)", version / 100, version % 100);
 +        return resultbuf;
 +    }
 +}
 +
 +void G_DoPlayDemo (void)
 +{
 +    skill_t skill;
 +    int             i, episode, map;
 +    int demoversion;
 +
 +    gameaction = ga_nothing;
 +    demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
 +
 +    demoversion = *demo_p++;
 +
 +    if (demoversion == DOOM_VERSION)
 +    {
 +        longtics = false;
 +    }
 +    else if (demoversion == DOOM_191_VERSION)
 +    {
 +        // demo recorded with cph's modified "v1.91" doom exe
 +        longtics = true;
 +    }
 +    else
 +    {
 +        char *message = "Demo is from a different game version!\n"
 +                        "(read %i, should be %i)\n"
 +                        "\n"
 +                        "*** You may need to upgrade your version "
 +                            "of Doom to v1.9. ***\n"
 +                        "    See: http://doomworld.com/files/patches.shtml\n"
 +                        "    This appears to be %s.";
 +
 +        I_Error(message, demoversion, DOOM_VERSION,
 +                         DemoVersionDescription(demoversion));
 +    }
 +
 +    skill = *demo_p++;
 +    episode = *demo_p++;
 +    map = *demo_p++;
 +    deathmatch = *demo_p++;
 +    respawnparm = *demo_p++;
 +    fastparm = *demo_p++;
 +    nomonsters = *demo_p++;
 +    consoleplayer = *demo_p++;
 +
 +    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;
 +    }
 +
 +    // don't spend a lot of time in loadlevel
 +    precache = false;
 +    G_InitNew (skill, episode, map);
 +    precache = true;
 +    starttime = I_GetTime ();
 +
 +    usergame = false;
 +    demoplayback = true;
 +}
 +
 +//
 +// G_TimeDemo
 +//
 +void G_TimeDemo (char* name)
 +{
 +    //!
 +    // @vanilla
 +    //
 +    // Disable rendering the screen entirely.
 +    //
 +
 +    nodrawers = M_CheckParm ("-nodraw");
 +
 +    timingdemo = true;
 +    singletics = true;
 +
 +    defdemoname = name;
 +    gameaction = ga_playdemo;
 +}
 +
 +
 +/*
 +===================
 +=
 += G_CheckDemoStatus
 +=
 += Called after a death or level completion to allow demos to be cleaned up
 += Returns true if a new demo loop action will take place
 +===================
 +*/
 +
 +boolean G_CheckDemoStatus (void)
 +{
 +    int             endtime;
 +
 +    if (timingdemo)
 +    {
 +        float fps;
 +        int realtics;
 +
 +	endtime = I_GetTime ();
 +        realtics = endtime - starttime;
 +        fps = ((float) gametic * TICRATE) / realtics;
 +
 +        // Prevent recursive calls
 +        timingdemo = false;
 +        demoplayback = false;
 +
 +	I_Error ("timed %i gametics in %i realtics (%f fps)",
 +                 gametic, realtics, fps);
 +    }
 +
 +    if (demoplayback)
 +    {
 +        W_ReleaseLumpName(defdemoname);
 +	demoplayback = false;
 +	netdemo = false;
 +	netgame = false;
 +	deathmatch = false;
 +	playeringame[1] = playeringame[2] = playeringame[3] = 0;
 +	respawnparm = false;
 +	fastparm = false;
 +	nomonsters = false;
 +	consoleplayer = 0;
 +
 +        if (singledemo)
 +            I_Quit ();
 +        else
 +            D_AdvanceDemo ();
 +
 +	return true;
 +    }
 +
 +    if (demorecording)
 +    {
 +	*demo_p++ = DEMOMARKER;
 +	M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
 +	Z_Free (demobuffer);
 +	demorecording = false;
 +	I_Error ("Demo %s recorded",demoname);
 +    }
 +
 +    return false;
 +}
 +
 +
 +
diff --cc src/strife/hu_stuff.c
index 3ddf402e,00000000..0358bd63
mode 100644,000000..100644
--- a/src/strife/hu_stuff.c
+++ b/src/strife/hu_stuff.c
@@@ -1,512 -1,0 +1,504 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:  Heads-up displays
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <ctype.h>
 +
 +#include "doomdef.h"
 +#include "doomkeys.h"
 +
 +#include "z_zone.h"
 +
 +#include "deh_main.h"
 +#include "i_swap.h"
 +#include "i_video.h"
 +
 +#include "hu_stuff.h"
 +#include "hu_lib.h"
 +#include "m_controls.h"
 +#include "w_wad.h"
 +
 +#include "s_sound.h"
 +
 +#include "doomstat.h"
 +
 +// Data.
 +#include "dstrings.h"
 +#include "sounds.h"
 +
 +//
 +// Locally used constants, shortcuts.
 +//
 +#define HU_TITLE	(mapnames[gamemap-1])
 +#define HU_TITLEHEIGHT	1
 +#define HU_TITLEX	0
 +
 +// haleyjd 09/01/10: [STRIFE] 167 -> 160 to move up level name
 +#define HU_TITLEY	(160 - SHORT(hu_font[0]->height))
 +
 +#define HU_INPUTTOGGLE	't'
 +#define HU_INPUTX	HU_MSGX
 +#define HU_INPUTY	(HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1))
 +#define HU_INPUTWIDTH	64
 +#define HU_INPUTHEIGHT	1
 +
 +
 +
 +char *chat_macros[10] =
 +{
 +    HUSTR_CHATMACRO0,
 +    HUSTR_CHATMACRO1,
 +    HUSTR_CHATMACRO2,
 +    HUSTR_CHATMACRO3,
 +    HUSTR_CHATMACRO4,
 +    HUSTR_CHATMACRO5,
 +    HUSTR_CHATMACRO6,
 +    HUSTR_CHATMACRO7,
 +    HUSTR_CHATMACRO8,
 +    HUSTR_CHATMACRO9
 +};
 +
 +char*	player_names[] =
 +{
 +    HUSTR_PLRGREEN,
 +    HUSTR_PLRINDIGO,
 +    HUSTR_PLRBROWN,
 +    HUSTR_PLRRED
 +};
 +
 +char			chat_char; // remove later.
 +static player_t*	plr;
 +patch_t*		hu_font[HU_FONTSIZE];
 +static hu_textline_t	w_title;
 +boolean			chat_on;
 +static hu_itext_t	w_chat;
 +static boolean		always_off = false;
 +static char		chat_dest[MAXPLAYERS];
 +static hu_itext_t w_inputbuffer[MAXPLAYERS];
 +
 +static boolean		message_on;
 +boolean			message_dontfuckwithme;
 +static boolean		message_nottobefuckedwith;
 +
 +static hu_stext_t	w_message;
 +static int		message_counter;
 +
 +extern int		showMessages;
 +extern boolean		automapactive;
 +
 +static boolean		headsupactive = false;
 +
 +//
 +// Builtin map names.
 +// The actual names can be found in DStrings.h.
 +//
 +
 +// haleyjd 08/31/10: [STRIFE] Changed for Strife level names.
 +// List of names for levels.
 +
 +char *mapnames[] =
 +{
 +    // Strife map names
 +
 +    // First "episode" - Quest to destroy the Order's Castle
 +    HUSTR_1,
 +    HUSTR_2,
 +    HUSTR_3,
 +    HUSTR_4,
 +    HUSTR_5,
 +    HUSTR_6,
 +    HUSTR_7,
 +    HUSTR_8,
 +    HUSTR_9,
 +
 +    // Second "episode" - Kill the Bishop and Make a Choice
 +    HUSTR_10,
 +    HUSTR_11,
 +    HUSTR_12,
 +    HUSTR_13,
 +    HUSTR_14,
 +    HUSTR_15,
 +    HUSTR_16,
 +    HUSTR_17,
 +    HUSTR_18,
 +    HUSTR_19,
 +
 +    // Third "episode" - Shut down Factory, kill Loremaster and Entity
 +    HUSTR_20,
 +    HUSTR_21,
 +    HUSTR_22,
 +    HUSTR_23,
 +    HUSTR_24,
 +    HUSTR_25,
 +    HUSTR_26,
 +    HUSTR_27,
 +    HUSTR_28,
 +    HUSTR_29,
 +
 +    // "Secret" levels - Abandoned Base and Training Facility
 +    HUSTR_30,
 +    HUSTR_31,
 +
 +    // Demo version maps
 +    HUSTR_32,
 +    HUSTR_33,
 +    HUSTR_34
 +};
 +
 +void HU_Init(void)
 +{
 +
 +    int		i;
 +    int		j;
 +    char	buffer[9];
 +
 +    // load the heads-up font
 +    j = HU_FONTSTART;
 +    for (i=0;i<HU_FONTSIZE;i++)
 +    {
- 	sprintf(buffer, DEH_String("STCFN%.3d"), j++);
++	DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
 +	hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
 +    }
 +
 +}
 +
 +void HU_Stop(void)
 +{
 +    headsupactive = false;
 +}
 +
 +void HU_Start(void)
 +{
 +
 +    int		i;
 +    char*	s;
 +
 +    if (headsupactive)
 +	HU_Stop();
 +
 +    plr = &players[consoleplayer];
 +    message_on = false;
 +    message_dontfuckwithme = false;
 +    message_nottobefuckedwith = false;
 +    chat_on = false;
 +
 +    // create the message widget
 +    HUlib_initSText(&w_message,
 +		    HU_MSGX, HU_MSGY, HU_MSGHEIGHT,
 +		    hu_font,
 +		    HU_FONTSTART, &message_on);
 +
 +    // create the map title widget
 +    HUlib_initTextLine(&w_title,
 +		       HU_TITLEX, HU_TITLEY,
 +		       hu_font,
 +		       HU_FONTSTART);
 +
 +
 +    // haleyjd 08/31/10: [STRIFE] Get proper map name.
 +    s = HU_TITLE;
 +
 +    // [STRIFE] Removed Chex Quest stuff.
 +
 +    // dehacked substitution to get modified level name
 +    s = DEH_String(s);
 +
 +    while (*s)
 +	HUlib_addCharToTextLine(&w_title, *(s++));
 +
 +    // create the chat widget
 +    HUlib_initIText(&w_chat,
 +		    HU_INPUTX, HU_INPUTY,
 +		    hu_font,
 +		    HU_FONTSTART, &chat_on);
 +
 +    // create the inputbuffer widgets
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +	HUlib_initIText(&w_inputbuffer[i], 0, 0, 0, 0, &always_off);
 +
 +    headsupactive = true;
 +
 +}
 +
 +void HU_Drawer(void)
 +{
 +
 +    HUlib_drawSText(&w_message);
 +    HUlib_drawIText(&w_chat);
 +    if (automapactive)
 +	HUlib_drawTextLine(&w_title, false);
 +
 +}
 +
 +void HU_Erase(void)
 +{
 +
 +    HUlib_eraseSText(&w_message);
 +    HUlib_eraseIText(&w_chat);
 +    HUlib_eraseTextLine(&w_title);
 +
 +}
 +
 +void HU_Ticker(void)
 +{
 +
 +    int i, rc;
 +    char c;
 +
 +    // tick down message counter if message is up
 +    if (message_counter && !--message_counter)
 +    {
 +	message_on = false;
 +	message_nottobefuckedwith = false;
 +    }
 +
 +    if (showMessages || message_dontfuckwithme)
 +    {
 +
 +	// display message if necessary
 +	if ((plr->message && !message_nottobefuckedwith)
 +	    || (plr->message && message_dontfuckwithme))
 +	{
 +	    HUlib_addMessageToSText(&w_message, 0, plr->message);
 +	    plr->message = 0;
 +	    message_on = true;
 +	    message_counter = HU_MSGTIMEOUT;
 +	    message_nottobefuckedwith = message_dontfuckwithme;
 +	    message_dontfuckwithme = 0;
 +	}
 +
 +    } // else message_on = false;
 +
 +    // check for incoming chat characters
 +    if (netgame)
 +    {
 +	for (i=0 ; i<MAXPLAYERS; i++)
 +	{
 +	    if (!playeringame[i])
 +		continue;
 +	    if (i != consoleplayer
 +		&& (c = players[i].cmd.chatchar))
 +	    {
 +		if (c <= HU_BROADCAST)
 +		    chat_dest[i] = c;
 +		else
 +		{
 +		    rc = HUlib_keyInIText(&w_inputbuffer[i], c);
 +		    if (rc && c == KEY_ENTER)
 +		    {
 +			if (w_inputbuffer[i].l.len
 +			    && (chat_dest[i] == consoleplayer+1
 +				|| chat_dest[i] == HU_BROADCAST))
 +			{
 +			    HUlib_addMessageToSText(&w_message,
 +						    DEH_String(player_names[i]),
 +						    w_inputbuffer[i].l.l);
 +
 +			    message_nottobefuckedwith = true;
 +			    message_on = true;
 +			    message_counter = HU_MSGTIMEOUT;
 +			    if ( gamemode == commercial )
 +			      S_StartSound(0, sfx_radio);
 +			    else
 +			      S_StartSound(0, sfx_swish);   // villsa [STRIFE] TODO - fix sounds
 +			}
 +			HUlib_resetIText(&w_inputbuffer[i]);
 +		    }
 +		}
 +		players[i].cmd.chatchar = 0;
 +	    }
 +	}
 +    }
 +
 +}
 +
 +#define QUEUESIZE		128
 +
 +static char	chatchars[QUEUESIZE];
 +static int	head = 0;
 +static int	tail = 0;
 +
 +
 +void HU_queueChatChar(char c)
 +{
 +    if (((head + 1) & (QUEUESIZE-1)) == tail)
 +    {
 +	plr->message = DEH_String(HUSTR_MSGU);
 +    }
 +    else
 +    {
 +	chatchars[head] = c;
 +	head = (head + 1) & (QUEUESIZE-1);
 +    }
 +}
 +
 +char HU_dequeueChatChar(void)
 +{
 +    char c;
 +
 +    if (head != tail)
 +    {
 +	c = chatchars[tail];
 +	tail = (tail + 1) & (QUEUESIZE-1);
 +    }
 +    else
 +    {
 +	c = 0;
 +    }
 +
 +    return c;
 +}
 +
 +boolean HU_Responder(event_t *ev)
 +{
 +
 +    static char		lastmessage[HU_MAXLINELENGTH+1];
 +    char*		macromessage;
 +    boolean		eatkey = false;
 +    static boolean	shiftdown = false;
 +    static boolean	altdown = false;
 +    unsigned char 	c;
 +    int			i;
 +    int			numplayers;
 +
-     static char		destination_keys[MAXPLAYERS] =
-     {
- 	HUSTR_KEYGREEN,
- 	HUSTR_KEYINDIGO,
- 	HUSTR_KEYBROWN,
- 	HUSTR_KEYRED
-     };
-
 +    static int		num_nobrainers = 0;
 +
 +    numplayers = 0;
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +	numplayers += playeringame[i];
 +
 +    if (ev->data1 == KEY_RSHIFT)
 +    {
 +	shiftdown = ev->type == ev_keydown;
 +	return false;
 +    }
 +    else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT)
 +    {
 +	altdown = ev->type == ev_keydown;
 +	return false;
 +    }
 +
 +    if (ev->type != ev_keydown)
 +	return false;
 +
 +    if (!chat_on)
 +    {
 +	if (ev->data1 == key_message_refresh)
 +	{
 +	    message_on = true;
 +	    message_counter = HU_MSGTIMEOUT;
 +	    eatkey = true;
 +	}
- 	else if (netgame && ev->data2 == HU_INPUTTOGGLE)
++	else if (netgame && ev->data2 == key_multi_msg)
 +	{
 +	    eatkey = chat_on = true;
 +	    HUlib_resetIText(&w_chat);
 +	    HU_queueChatChar(HU_BROADCAST);
 +	}
 +	else if (netgame && numplayers > 2)
 +	{
 +	    for (i=0; i<MAXPLAYERS ; i++)
 +	    {
- 		if (ev->data2 == destination_keys[i])
++		if (ev->data2 == key_multi_msgplayer[i])
 +		{
 +		    if (playeringame[i] && i!=consoleplayer)
 +		    {
 +			eatkey = chat_on = true;
 +			HUlib_resetIText(&w_chat);
 +			HU_queueChatChar(i+1);
 +			break;
 +		    }
 +		    else if (i == consoleplayer)
 +		    {
 +			num_nobrainers++;
 +			if (num_nobrainers < 3)
 +			    plr->message = DEH_String(HUSTR_TALKTOSELF1);
 +			else if (num_nobrainers < 6)
 +			    plr->message = DEH_String(HUSTR_TALKTOSELF2);
 +			else if (num_nobrainers < 9)
 +			    plr->message = DEH_String(HUSTR_TALKTOSELF3);
 +			else if (num_nobrainers < 32)
 +			    plr->message = DEH_String(HUSTR_TALKTOSELF4);
 +			else
 +			    plr->message = DEH_String(HUSTR_TALKTOSELF5);
 +		    }
 +		}
 +	    }
 +	}
 +    }
 +    else
 +    {
 +	c = ev->data2;
 +	// send a macro
 +	if (altdown)
 +	{
 +	    c = c - '0';
 +	    if (c > 9)
 +		return false;
 +	    // fprintf(stderr, "got here\n");
 +	    macromessage = chat_macros[c];
 +
 +	    // kill last message with a '\n'
 +	    HU_queueChatChar(KEY_ENTER); // DEBUG!!!
 +
 +	    // send the macro message
 +	    while (*macromessage)
 +		HU_queueChatChar(*macromessage++);
 +	    HU_queueChatChar(KEY_ENTER);
 +
 +	    // leave chat mode and notify that it was sent
 +	    chat_on = false;
 +	    strcpy(lastmessage, chat_macros[c]);
 +	    plr->message = lastmessage;
 +	    eatkey = true;
 +	}
 +	else
 +	{
 +	    eatkey = HUlib_keyInIText(&w_chat, c);
 +	    if (eatkey)
 +	    {
 +		// static unsigned char buf[20]; // DEBUG
 +		HU_queueChatChar(c);
 +
 +		// sprintf(buf, "KEY: %d => %d", ev->data1, c);
 +		//      plr->message = buf;
 +	    }
 +	    if (c == KEY_ENTER)
 +	    {
 +		chat_on = false;
 +		if (w_chat.l.len)
 +		{
 +		    strcpy(lastmessage, w_chat.l.l);
 +		    plr->message = lastmessage;
 +		}
 +	    }
 +	    else if (c == KEY_ESCAPE)
 +		chat_on = false;
 +	}
 +    }
 +
 +    return eatkey;
 +
 +}
diff --cc src/strife/m_menu.c
index 337c663b,00000000..ef868236
mode 100644,000000..100644
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@@ -1,2128 -1,0 +1,2128 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	DOOM selection menu, options, episode etc.
 +//	Sliders and icons. Kinda widget stuff.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <stdlib.h>
 +#include <ctype.h>
 +
 +
 +#include "doomdef.h"
 +#include "doomkeys.h"
 +#include "dstrings.h"
 +
 +#include "d_main.h"
 +#include "deh_main.h"
 +
 +#include "i_swap.h"
 +#include "i_system.h"
 +#include "i_timer.h"
 +#include "i_video.h"
 +#include "z_zone.h"
 +#include "v_video.h"
 +#include "w_wad.h"
 +
 +#include "r_local.h"
 +
 +
 +#include "hu_stuff.h"
 +
 +#include "g_game.h"
 +
 +#include "m_argv.h"
 +#include "m_controls.h"
 +#include "p_saveg.h"
 +
 +#include "s_sound.h"
 +
 +#include "doomstat.h"
 +
 +// Data.
 +#include "sounds.h"
 +
 +#include "m_menu.h"
 +
 +
 +extern void M_QuitDOOM(int);
 +
 +extern patch_t*		hu_font[HU_FONTSIZE];
 +extern boolean		message_dontfuckwithme;
 +
 +extern boolean		chat_on;		// in heads-up code
 +
 +//
 +// defaulted values
 +//
 +int			mouseSensitivity = 5;
 +
 +// Show messages has default, 0 = off, 1 = on
 +int			showMessages = 1;
 +
 +
 +// Blocky mode, has default, 0 = high, 1 = normal
 +int			detailLevel = 0;
 +int			screenblocks = 9;
 +
 +// temp for screenblocks (0-9)
 +int			screenSize;
 +
 +// -1 = no quicksave slot picked!
 +int			quickSaveSlot;
 +
 + // 1 = message to be printed
 +int			messageToPrint;
 +// ...and here is the message string!
 +char*			messageString;
 +
 +// message x & y
 +int			messx;
 +int			messy;
 +int			messageLastMenuActive;
 +
 +// timed message = no input from user
 +boolean			messageNeedsInput;
 +
 +void    (*messageRoutine)(int response);
 +
 +char gammamsg[5][26] =
 +{
 +    GAMMALVL0,
 +    GAMMALVL1,
 +    GAMMALVL2,
 +    GAMMALVL3,
 +    GAMMALVL4
 +};
 +
 +// we are going to be entering a savegame string
 +int			saveStringEnter;
 +int             	saveSlot;	// which slot to save in
 +int			saveCharIndex;	// which char we're editing
 +// old save description before edit
 +char			saveOldString[SAVESTRINGSIZE];
 +
 +boolean                 inhelpscreens;
 +boolean                 menuactive;
 +boolean                 menupause;     // haleyjd 08/29/10: [STRIFE] New global
 +int                     menupausetime; // haleyjd 09/04/10: [STRIFE] New global
 +boolean                 menuindialog;  // haleyjd 09/04/10: ditto
 +
 +// haleyjd 08/27/10: [STRIFE] SKULLXOFF == -28, LINEHEIGHT == 19
 +#define CURSORXOFF		-28
 +#define LINEHEIGHT		19
 +
 +extern boolean		sendpause;
 +char			savegamestrings[10][SAVESTRINGSIZE];
 +
 +char	endstring[160];
 +
 +// haleyjd 09/04/10: [STRIFE] Moved menuitem / menu structures into header
 +// because they are needed externally by the dialog engine.
 +
 +// haleyjd 08/27/10: [STRIFE] skull* stuff changed to cursor* stuff
 +short		itemOn;			// menu item skull is on
 +short		cursorAnimCounter;	// skull animation counter
 +short		whichCursor;		// which skull to draw
 +
 +// graphic name of cursors
 +// haleyjd 08/27/10: [STRIFE] M_SKULL* -> M_CURS*
 +char    *cursorName[8] = {"M_CURS1", "M_CURS2", "M_CURS3", "M_CURS4",
 +                          "M_CURS5", "M_CURS6", "M_CURS7", "M_CURS8" };
 +
 +// current menudef
 +menu_t*	currentMenu;
 +
 +//
 +// PROTOTYPES
 +//
 +void M_NewGame(int choice);
 +void M_Episode(int choice);
 +void M_ChooseSkill(int choice);
 +void M_LoadGame(int choice);
 +void M_SaveGame(int choice);
 +void M_Options(int choice);
 +void M_EndGame(int choice);
 +void M_ReadThis(int choice);
 +void M_ReadThis2(int choice);
 +void M_ReadThis3(int choice); // [STRIFE]
 +void M_QuitDOOM(int choice);
 +
 +void M_ChangeMessages(int choice);
 +void M_ChangeSensitivity(int choice);
 +void M_SfxVol(int choice);
 +void M_VoiceVol(int choice); // [STRIFE]
 +void M_MusicVol(int choice);
 +void M_SizeDisplay(int choice);
 +void M_StartGame(int choice);
 +void M_Sound(int choice);
 +
 +//void M_FinishReadThis(int choice); - [STRIFE] unused
 +void M_LoadSelect(int choice);
 +void M_SaveSelect(int choice);
 +void M_ReadSaveStrings(void);
 +void M_QuickSave(void);
 +void M_QuickLoad(void);
 +
 +void M_DrawMainMenu(void);
 +void M_DrawReadThis1(void);
 +void M_DrawReadThis2(void);
 +void M_DrawReadThis3(void); // [STRIFE]
 +void M_DrawNewGame(void);
 +void M_DrawEpisode(void);
 +void M_DrawOptions(void);
 +void M_DrawSound(void);
 +void M_DrawLoad(void);
 +void M_DrawSave(void);
 +
 +void M_DrawSaveLoadBorder(int x,int y);
 +void M_SetupNextMenu(menu_t *menudef);
 +void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
 +void M_DrawEmptyCell(menu_t *menu,int item);
 +void M_DrawSelCell(menu_t *menu,int item);
 +int  M_StringWidth(char *string);
 +int  M_StringHeight(char *string);
 +void M_StartControlPanel(void);
 +void M_StartMessage(char *string,void *routine,boolean input);
 +void M_StopMessage(void);
 +void M_ClearMenus (int choice);
 +
 +
 +
 +
 +//
 +// DOOM MENU
 +//
 +enum
 +{
 +    newgame = 0,
 +    options,
 +    loadgame,
 +    savegame,
 +    readthis,
 +    quitdoom,
 +    main_end
 +} main_e;
 +
 +menuitem_t MainMenu[]=
 +{
 +    {1,"M_NGAME",M_NewGame,'n'},
 +    {1,"M_OPTION",M_Options,'o'},
 +    {1,"M_LOADG",M_LoadGame,'l'},
 +    {1,"M_SAVEG",M_SaveGame,'s'},
 +    // Another hickup with Special edition.
 +    {1,"M_RDTHIS",M_ReadThis,'h'}, // haleyjd 08/28/10: 'r' -> 'h'
 +    {1,"M_QUITG",M_QuitDOOM,'q'}
 +};
 +
 +menu_t  MainDef =
 +{
 +    main_end,
 +    NULL,
 +    MainMenu,
 +    M_DrawMainMenu,
 +    97,45, // haleyjd 08/28/10: [STRIFE] changed y coord
 +    0
 +};
 +
 +
 +//
 +// EPISODE SELECT
 +//
 +/*
 +enum
 +{
 +    ep1,
 +    ep2,
 +    ep3,
 +    ep4,
 +    ep_end
 +} episodes_e;
 +
 +menuitem_t EpisodeMenu[]=
 +{
 +    {1,"M_EPI1", M_Episode,'k'},
 +    {1,"M_EPI2", M_Episode,'t'},
 +    {1,"M_EPI3", M_Episode,'i'},
 +    {1,"M_EPI4", M_Episode,'t'}
 +};
 +
 +menu_t  EpiDef =
 +{
 +    ep_end,		// # of menu items
 +    &MainDef,		// previous menu
 +    EpisodeMenu,	// menuitem_t ->
 +    M_DrawEpisode,	// drawing routine ->
 +    48,63,              // x,y
 +    ep1			// lastOn
 +};
 +*/
 +
 +//
 +// NEW GAME
 +//
 +enum
 +{
 +    killthings,
 +    toorough,
 +    hurtme,
 +    violence,
 +    nightmare,
 +    newg_end
 +} newgame_e;
 +
 +menuitem_t NewGameMenu[]=
 +{
 +    // haleyjd 08/28/10: [STRIFE] changed all shortcut letters
 +    {1,"M_JKILL",	M_ChooseSkill, 't'},
 +    {1,"M_ROUGH",	M_ChooseSkill, 'r'},
 +    {1,"M_HURT",	M_ChooseSkill, 'v'},
 +    {1,"M_ULTRA",	M_ChooseSkill, 'e'},
 +    {1,"M_NMARE",	M_ChooseSkill, 'b'}
 +};
 +
 +menu_t  NewDef =
 +{
 +    newg_end,           // # of menu items
 +    &MainDef,           // previous menu - haleyjd [STRIFE] changed to MainDef
 +    NewGameMenu,        // menuitem_t ->
 +    M_DrawNewGame,      // drawing routine ->
 +    48,63,              // x,y
 +    toorough            // lastOn - haleyjd [STRIFE]: default to skill 1
 +};
 +
 +
 +
 +//
 +// OPTIONS MENU
 +//
 +enum
 +{
 +    // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail.
 +    endgame,
 +    scrnsize,
 +    option_empty1,
 +    soundvol,
 +    opt_end
 +} options_e;
 +
 +menuitem_t OptionsMenu[]=
 +{
 +    // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail.
 +    {1,"M_ENDGAM",	M_EndGame,'e'},
 +    {2,"M_SCRNSZ",	M_SizeDisplay,'s'},
 +    {-1,"",0,'\0'},
 +    {1,"M_SVOL",	M_Sound,'s'}
 +};
 +
 +menu_t  OptionsDef =
 +{
 +    opt_end,
 +    &MainDef,
 +    OptionsMenu,
 +    M_DrawOptions,
 +    60,37,
 +    0
 +};
 +
 +//
 +// Read This! MENU 1 & 2 & [STRIFE] 3
 +//
 +enum
 +{
 +    rdthsempty1,
 +    read1_end
 +} read_e;
 +
 +menuitem_t ReadMenu1[] =
 +{
 +    {1,"",M_ReadThis2,0}
 +};
 +
 +menu_t  ReadDef1 =
 +{
 +    read1_end,
 +    &MainDef,
 +    ReadMenu1,
 +    M_DrawReadThis1,
 +    280,185,
 +    0
 +};
 +
 +enum
 +{
 +    rdthsempty2,
 +    read2_end
 +} read_e2;
 +
 +menuitem_t ReadMenu2[]=
 +{
 +    {1,"",M_ReadThis3,0} // haleyjd 08/28/10: [STRIFE] Go to ReadThis3
 +};
 +
 +menu_t  ReadDef2 =
 +{
 +    read2_end,
 +    &ReadDef1,
 +    ReadMenu2,
 +    M_DrawReadThis2,
 +    250,185, // haleyjd 08/28/10: [STRIFE] changed coords
 +    0
 +};
 +
 +// haleyjd 08/28/10: Added Read This! menu 3
 +enum
 +{
 +    rdthsempty3,
 +    read3_end
 +} read_e3;
 +
 +menuitem_t ReadMenu3[]=
 +{
 +    {1,"",M_ClearMenus,0}
 +};
 +
 +menu_t  ReadDef3 =
 +{
 +    read3_end,
 +    &ReadDef2,
 +    ReadMenu3,
 +    M_DrawReadThis3,
 +    250, 185,
 +    0
 +};
 +
 +//
 +// SOUND VOLUME MENU
 +//
 +enum
 +{
 +    sfx_vol,
 +    sfx_empty1,
 +    music_vol,
 +    sfx_empty2,
 +    voice_vol,
 +    sfx_empty3,
 +    sfx_mouse,
 +    sfx_empty4,
 +    sound_end
 +} sound_e;
 +
 +// haleyjd 08/29/10:
 +// [STRIFE]
 +// * Added voice volume
 +// * Moved mouse sensitivity here (who knows why...)
 +menuitem_t SoundMenu[]=
 +{
 +    {2,"M_SFXVOL",M_SfxVol,'s'},
 +    {-1,"",0,'\0'},
 +    {2,"M_MUSVOL",M_MusicVol,'m'},
 +    {-1,"",0,'\0'},
 +    {2,"M_VOIVOL",M_VoiceVol,'v'},
 +    {-1,"",0,'\0'},
 +    {2,"M_MSENS",M_ChangeSensitivity,'m'},
 +    {-1,"",0,'\0'}
 +};
 +
 +menu_t  SoundDef =
 +{
 +    sound_end,
 +    &OptionsDef,
 +    SoundMenu,
 +    M_DrawSound,
 +    80,35,       // [STRIFE] changed y coord 64 -> 35
 +    0
 +};
 +
 +//
 +// LOAD GAME MENU
 +//
 +enum
 +{
 +    load1,
 +    load2,
 +    load3,
 +    load4,
 +    load5,
 +    load6,
 +    load_end
 +} load_e;
 +
 +menuitem_t LoadMenu[]=
 +{
 +    {1,"", M_LoadSelect,'1'},
 +    {1,"", M_LoadSelect,'2'},
 +    {1,"", M_LoadSelect,'3'},
 +    {1,"", M_LoadSelect,'4'},
 +    {1,"", M_LoadSelect,'5'},
 +    {1,"", M_LoadSelect,'6'}
 +};
 +
 +menu_t  LoadDef =
 +{
 +    load_end,
 +    &MainDef,
 +    LoadMenu,
 +    M_DrawLoad,
 +    80,54,
 +    0
 +};
 +
 +//
 +// SAVE GAME MENU
 +//
 +menuitem_t SaveMenu[]=
 +{
 +    {1,"", M_SaveSelect,'1'},
 +    {1,"", M_SaveSelect,'2'},
 +    {1,"", M_SaveSelect,'3'},
 +    {1,"", M_SaveSelect,'4'},
 +    {1,"", M_SaveSelect,'5'},
 +    {1,"", M_SaveSelect,'6'}
 +};
 +
 +menu_t  SaveDef =
 +{
 +    load_end,
 +    &MainDef,
 +    SaveMenu,
 +    M_DrawSave,
 +    80,54,
 +    0
 +};
 +
 +
 +//
 +// M_ReadSaveStrings
 +//  read the strings from the savegame files
 +//
 +void M_ReadSaveStrings(void)
 +{
 +    FILE   *handle;
 +    int     count;
 +    int     i;
 +    char    name[256];
 +
 +    for (i = 0;i < load_end;i++)
 +    {
 +        strcpy(name, P_SaveGameFile(i));
 +
 +	handle = fopen(name, "rb");
 +	if (handle == NULL)
 +	{
 +	    strcpy(&savegamestrings[i][0], EMPTYSTRING);
 +	    LoadMenu[i].status = 0;
 +	    continue;
 +	}
 +	count = fread(&savegamestrings[i], 1, SAVESTRINGSIZE, handle);
 +	fclose(handle);
 +	LoadMenu[i].status = 1;
 +    }
 +}
 +
 +
 +//
 +// M_LoadGame & Cie.
 +//
 +void M_DrawLoad(void)
 +{
 +    int             i;
 +
 +    V_DrawPatchDirect(72, 28,
 +                      W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE));
 +
 +    for (i = 0;i < load_end; i++)
 +    {
 +	M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
 +	M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
 +    }
 +}
 +
 +
 +
 +//
 +// Draw border for the savegame description
 +//
 +void M_DrawSaveLoadBorder(int x,int y)
 +{
 +    int             i;
 +
 +    V_DrawPatchDirect(x - 8, y + 7,
 +                      W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE));
 +
 +    for (i = 0;i < 24;i++)
 +    {
 +	V_DrawPatchDirect(x, y + 7,
 +                          W_CacheLumpName(DEH_String("M_LSCNTR"), PU_CACHE));
 +	x += 8;
 +    }
 +
 +    V_DrawPatchDirect(x, y + 7,
 +                      W_CacheLumpName(DEH_String("M_LSRGHT"), PU_CACHE));
 +}
 +
 +
 +
 +//
 +// User wants to load this game
 +//
 +void M_LoadSelect(int choice)
 +{
 +    char    name[256];
 +
 +    strcpy(name, P_SaveGameFile(choice));
 +
 +    G_LoadGame (name);
 +    M_ClearMenus (0);
 +}
 +
 +//
 +// Selected from DOOM menu
 +//
 +void M_LoadGame (int choice)
 +{
 +    if (netgame)
 +    {
 +	M_StartMessage(DEH_String(LOADNET),NULL,false);
 +	return;
 +    }
 +
 +    M_SetupNextMenu(&LoadDef);
 +    M_ReadSaveStrings();
 +}
 +
 +
 +//
 +//  M_SaveGame & Cie.
 +//
 +void M_DrawSave(void)
 +{
 +    int             i;
 +
 +    V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE));
 +    for (i = 0;i < load_end; i++)
 +    {
 +	M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
 +	M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
 +    }
 +
 +    if (saveStringEnter)
 +    {
 +	i = M_StringWidth(savegamestrings[saveSlot]);
 +	M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_");
 +    }
 +}
 +
 +//
 +// M_Responder calls this when user is finished
 +//
 +void M_DoSave(int slot)
 +{
 +    G_SaveGame (slot,savegamestrings[slot]);
 +    M_ClearMenus (0);
 +
 +    // PICK QUICKSAVE SLOT YET?
 +    if (quickSaveSlot == -2)
 +	quickSaveSlot = slot;
 +}
 +
 +//
 +// User wants to save. Start string input for M_Responder
 +//
 +void M_SaveSelect(int choice)
 +{
 +    // we are going to be intercepting all chars
 +    saveStringEnter = 1;
 +
 +    saveSlot = choice;
 +    strcpy(saveOldString,savegamestrings[choice]);
 +    if (!strcmp(savegamestrings[choice],EMPTYSTRING))
 +	savegamestrings[choice][0] = 0;
 +    saveCharIndex = strlen(savegamestrings[choice]);
 +}
 +
 +//
 +// Selected from DOOM menu
 +//
 +void M_SaveGame (int choice)
 +{
 +    if (!usergame)
 +    {
 +	M_StartMessage(DEH_String(SAVEDEAD),NULL,false);
 +	return;
 +    }
 +
 +    if (gamestate != GS_LEVEL)
 +	return;
 +
 +    M_SetupNextMenu(&SaveDef);
 +    M_ReadSaveStrings();
 +}
 +
 +
 +
 +//
 +//      M_QuickSave
 +//
 +char    tempstring[80];
 +
 +void M_QuickSaveResponse(int key)
 +{
 +    if (key == key_menu_confirm)
 +    {
 +	M_DoSave(quickSaveSlot);
 +	S_StartSound(NULL,sfx_swish);   // villsa [STRIFE] TODO - fix sounds
 +    }
 +}
 +
 +void M_QuickSave(void)
 +{
 +    if (!usergame)
 +    {
 +	S_StartSound(NULL,sfx_oof);
 +	return;
 +    }
 +
 +    if (gamestate != GS_LEVEL)
 +	return;
 +
 +    if (quickSaveSlot < 0)
 +    {
 +	M_StartControlPanel();
 +	M_ReadSaveStrings();
 +	M_SetupNextMenu(&SaveDef);
 +	quickSaveSlot = -2;	// means to pick a slot now
 +	return;
 +    }
-     sprintf(tempstring,DEH_String(QSPROMPT),savegamestrings[quickSaveSlot]);
++    DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]);
 +    M_StartMessage(tempstring,M_QuickSaveResponse,true);
 +}
 +
 +
 +
 +//
 +// M_QuickLoad
 +//
 +void M_QuickLoadResponse(int key)
 +{
 +    if (key == key_menu_confirm)
 +    {
 +	M_LoadSelect(quickSaveSlot);
 +	S_StartSound(NULL,sfx_swish);   // villsa [STRIFE] TODO - fix sounds
 +    }
 +}
 +
 +
 +void M_QuickLoad(void)
 +{
 +    if (netgame)
 +    {
 +	M_StartMessage(DEH_String(QLOADNET),NULL,false);
 +	return;
 +    }
 +
 +    if (quickSaveSlot < 0)
 +    {
 +	M_StartMessage(DEH_String(QSAVESPOT),NULL,false);
 +	return;
 +    }
-     sprintf(tempstring,DEH_String(QLPROMPT),savegamestrings[quickSaveSlot]);
++    DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]);
 +    M_StartMessage(tempstring,M_QuickLoadResponse,true);
 +}
 +
 +
 +
 +
 +//
 +// Read This Menus
 +// Had a "quick hack to fix romero bug"
 +// haleyjd 08/28/10: [STRIFE] Draw HELP1, unconditionally.
 +//
 +void M_DrawReadThis1(void)
 +{
 +    inhelpscreens = true;
 +
 +    V_DrawPatchDirect (0, 0, W_CacheLumpName(DEH_String("HELP1"), PU_CACHE));
 +}
 +
 +
 +
 +//
 +// Read This Menus
 +// haleyjd 08/28/10: [STRIFE] Not optional, draws HELP2
 +//
 +void M_DrawReadThis2(void)
 +{
 +    inhelpscreens = true;
 +
 +    V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP2"), PU_CACHE));
 +}
 +
 +
 +//
 +// Read This Menus
 +// haleyjd 08/28/10: [STRIFE] New function to draw HELP3.
 +//
 +void M_DrawReadThis3(void)
 +{
 +    inhelpscreens = true;
 +
 +    V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP3"), PU_CACHE));
 +}
 +
 +//
 +// Change Sfx & Music volumes
 +//
 +// haleyjd 08/29/10: [STRIFE]
 +// * Changed title graphic coordinates
 +// * Added voice volume and sensitivity sliders
 +//
 +void M_DrawSound(void)
 +{
 +    V_DrawPatchDirect (100, 10, W_CacheLumpName(DEH_String("M_SVOL"), PU_CACHE));
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
 +                 16,sfxVolume);
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
 +                 16,musicVolume);
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(voice_vol+1),
 +                 16,voiceVolume);
 +
 +    M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_mouse+1),
 +                 16,mouseSensitivity);
 +}
 +
 +void M_Sound(int choice)
 +{
 +    M_SetupNextMenu(&SoundDef);
 +}
 +
 +void M_SfxVol(int choice)
 +{
 +    switch(choice)
 +    {
 +      case 0:
 +	if (sfxVolume)
 +	    sfxVolume--;
 +	break;
 +      case 1:
 +	if (sfxVolume < 15)
 +	    sfxVolume++;
 +	break;
 +    }
 +
 +    S_SetSfxVolume(sfxVolume * 8);
 +}
 +
 +//
 +// M_VoiceVol
 +//
 +// haleyjd 08/29/10: [STRIFE] New function
 +// Sets voice volume level.
 +//
 +void M_VoiceVol(int choice)
 +{
 +    switch(choice)
 +    {
 +    case 0:
 +        if (voiceVolume)
 +            voiceVolume--;
 +        break;
 +    case 1:
 +        if (voiceVolume < 15)
 +            voiceVolume++;
 +        break;
 +    }
 +
 +    // STRIFE-TODO: Voice volume setting
 +    //S_SetVoiceVolume(voiceVolume * 8);
 +}
 +
 +void M_MusicVol(int choice)
 +{
 +    switch(choice)
 +    {
 +      case 0:
 +	if (musicVolume)
 +	    musicVolume--;
 +	break;
 +      case 1:
 +	if (musicVolume < 15)
 +	    musicVolume++;
 +	break;
 +    }
 +
 +    S_SetMusicVolume(musicVolume * 8);
 +}
 +
 +
 +
 +
 +//
 +// M_DrawMainMenu
 +//
 +// haleyjd 08/27/10: [STRIFE] Changed x coordinate; M_DOOM -> M_STRIFE
 +//
 +void M_DrawMainMenu(void)
 +{
 +    V_DrawPatchDirect(84, 2,
 +                      W_CacheLumpName(DEH_String("M_STRIFE"), PU_CACHE));
 +}
 +
 +
 +
 +
 +//
 +// M_NewGame
 +//
 +// haleyjd 08/31/10: [STRIFE] Changed M_NEWG -> M_NGAME
 +//
 +void M_DrawNewGame(void)
 +{
 +    V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String("M_NGAME"), PU_CACHE));
 +    V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_SKILL"), PU_CACHE));
 +}
 +
 +void M_NewGame(int choice)
 +{
 +    if (netgame && !demoplayback)
 +    {
 +        M_StartMessage(DEH_String(NEWGAME),NULL,false);
 +        return;
 +    }
 +    // haleyjd 09/07/10: [STRIFE] Removed Chex Quest and DOOM gamemodes
 +    M_SetupNextMenu(&NewDef);
 +}
 +
 +
 +//
 +//      M_Episode
 +//
 +int     epi;
 +
 +void M_DrawEpisode(void)
 +{
 +    V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_EPISOD"), PU_CACHE));
 +}
 +
 +/*
 +// haleyjd: [STRIFE] Unused
 +void M_VerifyNightmare(int key)
 +{
 +    if (key != key_menu_confirm)
 +        return;
 +
 +    G_DeferedInitNew(nightmare,epi+1,1);
 +    M_ClearMenus (0);
 +}
 +*/
 +
 +void M_ChooseSkill(int choice)
 +{
 +    // haleyjd 09/07/10: Removed nightmare confirmation
 +    G_DeferedInitNew(choice,epi+1,1);
 +    M_ClearMenus (0);
 +}
 +
 +/*
 +// haleyjd [STRIFE] Unused
 +void M_Episode(int choice)
 +{
 +    if ( (gamemode == shareware)
 +	 && choice)
 +    {
 +	M_StartMessage(DEH_String(SWSTRING),NULL,false);
 +	M_SetupNextMenu(&ReadDef1);
 +	return;
 +    }
 +
 +    // Yet another hack...
 +    if ( (gamemode == registered)
 +	 && (choice > 2))
 +    {
 +      fprintf( stderr,
 +	       "M_Episode: 4th episode requires UltimateDOOM\n");
 +      choice = 0;
 +    }
 +
 +    epi = choice;
 +    M_SetupNextMenu(&NewDef);
 +}
 +*/
 +
 +
 +//
 +// M_Options
 +//
 +char    detailNames[2][9]	= {"M_GDHIGH","M_GDLOW"};
 +char	msgNames[2][9]		= {"M_MSGOFF","M_MSGON"};
 +
 +
 +void M_DrawOptions(void)
 +{
 +    // haleyjd 08/27/10: [STRIFE] M_OPTTTL -> M_OPTION
 +    V_DrawPatchDirect(108, 15,
 +                      W_CacheLumpName(DEH_String("M_OPTION"), PU_CACHE));
 +
 +    // haleyjd 08/26/10: [STRIFE] Removed messages, sensitivity, detail.
 +
 +    M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
 +                 9,screenSize);
 +}
 +
 +void M_Options(int choice)
 +{
 +    M_SetupNextMenu(&OptionsDef);
 +}
 +
 +
 +
 +//
 +//      Toggle messages on/off
 +//
 +void M_ChangeMessages(int choice)
 +{
 +    // warning: unused parameter `int choice'
 +    choice = 0;
 +    showMessages = 1 - showMessages;
 +
 +    if (!showMessages)
 +	players[consoleplayer].message = DEH_String(MSGOFF);
 +    else
 +	players[consoleplayer].message = DEH_String(MSGON);
 +
 +    message_dontfuckwithme = true;
 +}
 +
 +
 +//
 +// M_EndGame
 +//
 +void M_EndGameResponse(int key)
 +{
 +    if (key != key_menu_confirm)
 +	return;
 +
 +    currentMenu->lastOn = itemOn;
 +    M_ClearMenus (0);
 +    D_StartTitle ();
 +}
 +
 +void M_EndGame(int choice)
 +{
 +    choice = 0;
 +    if (!usergame)
 +    {
 +	S_StartSound(NULL,sfx_oof);
 +	return;
 +    }
 +
 +    if (netgame)
 +    {
 +	M_StartMessage(DEH_String(NETEND),NULL,false);
 +	return;
 +    }
 +
 +    M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true);
 +}
 +
 +
 +
 +
 +//
 +// M_ReadThis
 +//
 +void M_ReadThis(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&ReadDef1);
 +}
 +
 +//
 +// M_ReadThis2
 +//
 +// haleyjd 08/28/10: [STRIFE] Eliminated DOOM stuff.
 +//
 +void M_ReadThis2(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&ReadDef2);
 +}
 +
 +//
 +// M_ReadThis3
 +//
 +// haleyjd 08/28/10: [STRIFE] New function.
 +//
 +void M_ReadThis3(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&ReadDef3);
 +}
 +
 +/*
 +// haleyjd 08/28/10: [STRIFE] Not used.
 +void M_FinishReadThis(int choice)
 +{
 +    choice = 0;
 +    M_SetupNextMenu(&MainDef);
 +}
 +*/
 +
 +
 +
 +//
 +// M_QuitDOOM
 +//
 +// villsa [STRIFE] TODO - fix sounds
 +int     quitsounds[8] =
 +{
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish
 +};
 +
 +int     quitsounds2[8] =
 +{
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish,
 +    sfx_swish
 +};
 +
 +
 +
 +void M_QuitResponse(int key)
 +{
 +    if (key != key_menu_confirm)
 +	return;
 +    if (!netgame)
 +    {
 +	if (gamemode == commercial)
 +	    S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
 +	else
 +	    S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
 +	I_WaitVBL(105);
 +    }
 +    I_Quit ();
 +}
 +
 +
 +static char *M_SelectEndMessage(void)
 +{
 +    char **endmsg;
 +
 +    if (gamemission == doom)
 +    {
 +        // Doom 1
 +
 +        endmsg = doom1_endmsg;
 +    }
 +    else
 +    {
 +        // Doom 2
 +
 +        endmsg = doom2_endmsg;
 +    }
 +
 +    return endmsg[gametic % NUM_QUITMESSAGES];
 +}
 +
 +
 +void M_QuitDOOM(int choice)
 +{
 +    sprintf(endstring,
 +            DEH_String("%s\n\n" DOSY),
 +            DEH_String(M_SelectEndMessage()));
 +
 +    M_StartMessage(endstring,M_QuitResponse,true);
 +}
 +
 +
 +
 +
 +void M_ChangeSensitivity(int choice)
 +{
 +    switch(choice)
 +    {
 +      case 0:
 +	if (mouseSensitivity)
 +	    mouseSensitivity--;
 +	break;
 +      case 1:
 +	if (mouseSensitivity < 9)
 +	    mouseSensitivity++;
 +	break;
 +    }
 +}
 +
 +/*
 +// haleyjd [STRIFE] Unused
 +void M_ChangeDetail(int choice)
 +{
 +    choice = 0;
 +    detailLevel = 1 - detailLevel;
 +
 +    R_SetViewSize (screenblocks, detailLevel);
 +
 +    if (!detailLevel)
 +	players[consoleplayer].message = DEH_String(DETAILHI);
 +    else
 +	players[consoleplayer].message = DEH_String(DETAILLO);
 +}
 +*/
 +
 +void M_SizeDisplay(int choice)
 +{
 +    switch(choice)
 +    {
 +      case 0:
 +	if (screenSize > 0)
 +	{
 +	    screenblocks--;
 +	    screenSize--;
 +	}
 +	break;
 +      case 1:
 +	if (screenSize < 8)
 +	{
 +	    screenblocks++;
 +	    screenSize++;
 +	}
 +	break;
 +    }
 +
 +
 +    R_SetViewSize (screenblocks, detailLevel);
 +}
 +
 +
 +
 +
 +//
 +//      Menu Functions
 +//
 +
 +//
 +// M_DrawThermo
 +//
 +// haleyjd 08/28/10: [STRIFE] Changes to some patch coordinates.
 +//
 +void
 +M_DrawThermo
 +( int	x,
 +  int	y,
 +  int	thermWidth,
 +  int	thermDot )
 +{
 +    int         xx;
 +    int         yy; // [STRIFE] Needs a temp y coordinate variable
 +    int         i;
 +
 +    xx = x;
 +    yy = y + 6; // [STRIFE] +6 to y coordinate
 +    V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERML"), PU_CACHE));
 +    xx += 8;
 +    for (i=0;i<thermWidth;i++)
 +    {
 +        V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERMM"), PU_CACHE));
 +        xx += 8;
 +    }
 +    V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERMR"), PU_CACHE));
 +
 +    // [STRIFE] +2 to initial y coordinate
 +    V_DrawPatchDirect((x + 8) + thermDot * 8, y + 2,
 +                      W_CacheLumpName(DEH_String("M_THERMO"), PU_CACHE));
 +}
 +
 +
 +
 +void
 +M_DrawEmptyCell
 +( menu_t*	menu,
 +  int		item )
 +{
 +    V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,
 +                      W_CacheLumpName(DEH_String("M_CELL1"), PU_CACHE));
 +}
 +
 +void
 +M_DrawSelCell
 +( menu_t*	menu,
 +  int		item )
 +{
 +    V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,
 +                      W_CacheLumpName(DEH_String("M_CELL2"), PU_CACHE));
 +}
 +
 +
 +void
 +M_StartMessage
 +( char*		string,
 +  void*		routine,
 +  boolean	input )
 +{
 +    messageLastMenuActive = menuactive;
 +    messageToPrint = 1;
 +    messageString = string;
 +    messageRoutine = routine;
 +    messageNeedsInput = input;
 +    menuactive = true;
 +    return;
 +}
 +
 +
 +
 +void M_StopMessage(void)
 +{
 +    menuactive = messageLastMenuActive;
 +    messageToPrint = 0;
 +}
 +
 +
 +
 +//
 +// Find string width from hu_font chars
 +//
 +int M_StringWidth(char* string)
 +{
 +    size_t             i;
 +    int             w = 0;
 +    int             c;
 +
 +    for (i = 0;i < strlen(string);i++)
 +    {
 +	c = toupper(string[i]) - HU_FONTSTART;
 +	if (c < 0 || c >= HU_FONTSIZE)
 +	    w += 4;
 +	else
 +	    w += SHORT (hu_font[c]->width);
 +    }
 +
 +    return w;
 +}
 +
 +
 +
 +//
 +//      Find string height from hu_font chars
 +//
 +int M_StringHeight(char* string)
 +{
 +    size_t             i;
 +    int             h;
 +    int             height = SHORT(hu_font[0]->height);
 +
 +    h = height;
 +    for (i = 0;i < strlen(string);i++)
 +	if (string[i] == '\n')
 +	    h += height;
 +
 +    return h;
 +}
 +
 +
 +//
 +// M_WriteText
 +//
 +// Write a string using the hu_font
 +// haleyjd 09/04/10: [STRIFE]
 +// * Rogue made a lot of changes to this for the dialog system.
 +//
 +int
 +M_WriteText
 +( int		x,
 +  int		y,
 +  char*		string)
 +{
 +    int		w;
 +    char*	ch;
 +    int		c;
 +    int		cx;
 +    int		cy;
 +
 +    ch = string;
 +    cx = x;
 +    cy = y;
 +
 +    while(1)
 +    {
 +        c = *ch++;
 +        if (!c)
 +            break;
 +
 +        // haleyjd 09/04/10: [STRIFE] Don't draw spaces at the start of lines.
 +        if(c == ' ' && cx == x)
 +            continue;
 +
 +        if (c == '\n')
 +        {
 +            cx = x;
 +            cy += 11; // haleyjd 09/04/10: [STRIFE]: Changed 12 -> 11
 +            continue;
 +        }
 +
 +        c = toupper(c) - HU_FONTSTART;
 +        if (c < 0 || c>= HU_FONTSIZE)
 +        {
 +            cx += 4;
 +            continue;
 +        }
 +
 +        w = SHORT (hu_font[c]->width);
 +
 +        // haleyjd 09/04/10: [STRIFE] Different linebreak handling
 +        if (cx + w > SCREENWIDTH - 20)
 +        {
 +            cx = x;
 +            cy += 11;
 +            --ch;
 +        }
 +        else
 +        {
 +            V_DrawPatchDirect(cx, cy, hu_font[c]);
 +            cx += w;
 +        }
 +    }
 +
 +    // [STRIFE] Return final y coordinate.
 +    return cy + 12;
 +}
 +
 +//
 +// M_DialogDimMsg
 +//
 +// [STRIFE] New function
 +// haleyjd 09/04/10: Painstakingly transformed from the assembly code, as the
 +// decompiler could not touch it. Redimensions a string to fit on screen, leaving
 +// at least a 20 pixel margin on the right side. The string passed in must be
 +// writable.
 +//
 +void M_DialogDimMsg(int x, int y, char *str, boolean useyfont)
 +{
 +    int rightbound = (SCREENWIDTH - 20) - x;
 +    patch_t **fontarray;  // ebp
 +    int linewidth = 0;    // esi
 +    int i = 0;            // edx
 +    char *message = str;  // edi
 +    char  bl;             // bl
 +
 +    /*
 +    STRIFE-TODO:
 +    if(useyfont)
 +       fontarray = yfont;
 +    else
 +       fontarray = hu_font;
 +    */
 +    fontarray = hu_font;
 +
 +    bl = toupper(*message);
 +
 +    if(!bl)
 +        return;
 +
 +    // outer loop - run to end of string
 +    do
 +    {
 +        if(bl != '\n')
 +        {
 +            int charwidth; // eax
 +            int tempwidth; // ecx
 +
 +            if(bl < HU_FONTSTART || bl > HU_FONTEND)
 +                charwidth = 4;
 +            else
 +                charwidth = SHORT(fontarray[bl - HU_FONTSTART]->width);
 +
 +            tempwidth = linewidth + charwidth;
 +
 +            // Test if the line still fits within the boundary...
 +            if(tempwidth >= rightbound)
 +            {
 +                // Doesn't fit...
 +                char *tempptr = &message[i]; // ebx
 +                char  al;                    // al
 +
 +                // inner loop - run backward til a space (or the start of the
 +                // string) is found, subtracting width off the current line.
 +                // BUG: shouldn't we stop at a previous '\n' too?
 +                while(*tempptr != ' ' && i > 0)
 +                {
 +                    tempptr--;
 +                    // BUG: they didn't add the first char to linewidth yet...
 +                    linewidth -= charwidth;
 +                    i--;
 +                    al = toupper(*tempptr);
 +                    if(al < HU_FONTSTART || al > HU_FONTEND)
 +                        charwidth = 4;
 +                    else
 +                        charwidth = SHORT(fontarray[al - HU_FONTSTART]->width);
 +                }
 +                // Replace the space with a linebreak.
 +                // BUG: what if i is zero? ... infinite loop time!
 +                message[i] = '\n';
 +                linewidth = 0;
 +            }
 +            else
 +            {
 +                // The line does fit.
 +                // Spaces at the start of a line don't count though.
 +                if(!(bl == ' ' && linewidth == 0))
 +                    linewidth += charwidth;
 +            }
 +        }
 +        else
 +            linewidth = 0; // '\n' seen, so reset the line width
 +    }
 +    while((bl = toupper(message[++i])) != 0); // step to the next character
 +}
 +
 +
 +//
 +// CONTROL PANEL
 +//
 +
 +//
 +// M_Responder
 +//
 +boolean M_Responder (event_t* ev)
 +{
 +    int             ch;
 +    int             key;
 +    int             i;
 +    static  int     joywait = 0;
 +    static  int     mousewait = 0;
 +    static  int     mousey = 0;
 +    static  int     lasty = 0;
 +    static  int     mousex = 0;
 +    static  int     lastx = 0;
 +
 +    // "close" button pressed on window?
 +    if (ev->type == ev_quit)
 +    {
 +        S_StartSound(NULL, sfx_swtchn);
 +        M_QuitDOOM(0);
 +        return true;
 +    }
 +
 +    // key is the key pressed, ch is the actual character typed
 +
 +    ch = 0;
 +    key = -1;
 +
 +    if (ev->type == ev_joystick && joywait < I_GetTime())
 +    {
 +        if (ev->data3 == -1)
 +        {
 +            key = key_menu_up;
 +            joywait = I_GetTime() + 5;
 +        }
 +        else if (ev->data3 == 1)
 +        {
 +            key = key_menu_down;
 +            joywait = I_GetTime() + 5;
 +        }
 +
 +        if (ev->data2 == -1)
 +        {
 +            key = key_menu_left;
 +            joywait = I_GetTime() + 2;
 +        }
 +        else if (ev->data2 == 1)
 +        {
 +            key = key_menu_right;
 +            joywait = I_GetTime() + 2;
 +        }
 +
 +        if (ev->data1&1)
 +        {
 +            key = key_menu_forward;
 +            joywait = I_GetTime() + 5;
 +        }
 +        if (ev->data1&2)
 +        {
 +            key = key_menu_back;
 +            joywait = I_GetTime() + 5;
 +        }
 +    }
 +    else
 +    {
 +        if (ev->type == ev_mouse && mousewait < I_GetTime())
 +        {
 +            mousey += ev->data3;
 +            if (mousey < lasty-30)
 +            {
 +                key = key_menu_down;
 +                mousewait = I_GetTime() + 5;
 +                mousey = lasty -= 30;
 +            }
 +            else if (mousey > lasty+30)
 +            {
 +                key = key_menu_up;
 +                mousewait = I_GetTime() + 5;
 +                mousey = lasty += 30;
 +            }
 +
 +            mousex += ev->data2;
 +            if (mousex < lastx-30)
 +            {
 +                key = key_menu_left;
 +                mousewait = I_GetTime() + 5;
 +                mousex = lastx -= 30;
 +            }
 +            else if (mousex > lastx+30)
 +            {
 +                key = key_menu_right;
 +                mousewait = I_GetTime() + 5;
 +                mousex = lastx += 30;
 +            }
 +
 +            if (ev->data1&1)
 +            {
 +                key = key_menu_forward;
 +                mousewait = I_GetTime() + 15;
 +                mouse_fire_countdown = 5;   // villsa [STRIFE]
 +            }
 +
 +            if (ev->data1&2)
 +            {
 +                key = key_menu_back;
 +                mousewait = I_GetTime() + 15;
 +            }
 +        }
 +        else
 +        {
 +            if (ev->type == ev_keydown)
 +            {
 +                key = ev->data1;
 +                ch = ev->data2;
 +            }
 +        }
 +    }
 +
 +    if (key == -1)
 +        return false;
 +
 +    // In testcontrols mode, none of the function keys should do anything
 +    // - the only key is escape to quit.
 +
 +    if (testcontrols)
 +    {
 +        if (key == key_menu_activate || key == key_menu_quit)
 +        {
 +            I_Quit();
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +    // Save Game string input
 +    if (saveStringEnter)
 +    {
 +        switch(key)
 +        {
 +        case KEY_BACKSPACE:
 +            if (saveCharIndex > 0)
 +            {
 +                saveCharIndex--;
 +                savegamestrings[saveSlot][saveCharIndex] = 0;
 +            }
 +            break;
 +
 +        case KEY_ESCAPE:
 +            saveStringEnter = 0;
 +            strcpy(&savegamestrings[saveSlot][0],saveOldString);
 +            break;
 +
 +        case KEY_ENTER:
 +            saveStringEnter = 0;
 +            if (savegamestrings[saveSlot][0])
 +                M_DoSave(saveSlot);
 +            break;
 +
 +        default:
 +            // Entering a character - use the 'ch' value, not the key
 +
 +            ch = toupper(ch);
 +
 +            if (ch != ' '
 +                && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE))
 +            {
 +                break;
 +            }
 +
 +            if (ch >= 32 && ch <= 127 &&
 +                saveCharIndex < SAVESTRINGSIZE-1 &&
 +                M_StringWidth(savegamestrings[saveSlot]) <
 +                (SAVESTRINGSIZE-2)*8)
 +            {
 +                savegamestrings[saveSlot][saveCharIndex++] = ch;
 +                savegamestrings[saveSlot][saveCharIndex] = 0;
 +            }
 +            break;
 +        }
 +        return true;
 +    }
 +
 +    // Take care of any messages that need input
 +    if (messageToPrint)
 +    {
 +        if (messageNeedsInput)
 +        {
 +            if (key != ' ' && key != KEY_ESCAPE
 +                && key != key_menu_confirm && key != key_menu_abort)
 +            {
 +                return false;
 +            }
 +        }
 +
 +        menuactive = messageLastMenuActive;
 +        messageToPrint = 0;
 +        if (messageRoutine)
 +            messageRoutine(key);
 +
 +        menuactive = false;
 +        S_StartSound(NULL,sfx_swish);   // villsa [STRIFE] TODO - fix sounds
 +        return true;
 +    }
 +
 +    if (devparm && key == key_menu_help)
 +    {
 +        G_ScreenShot ();
 +        return true;
 +    }
 +
 +    // F-Keys
 +    if (!menuactive)
 +    {
 +        if (key == key_menu_decscreen)      // Screen size down
 +        {
 +            if (automapactive || chat_on)
 +                return false;
 +            M_SizeDisplay(0);
 +            S_StartSound(NULL,sfx_stnmov);
 +            return true;
 +        }
 +        else if (key == key_menu_incscreen) // Screen size up
 +        {
 +            if (automapactive || chat_on)
 +                return false;
 +            M_SizeDisplay(1);
 +            S_StartSound(NULL,sfx_stnmov);
 +            return true;
 +        }
 +        else if (key == key_menu_help)     // Help key
 +        {
 +            M_StartControlPanel ();
 +            // haleyjd 08/29/10: [STRIFE] always ReadDef1
 +            currentMenu = &ReadDef1;
 +
 +            itemOn = 0;
 +            S_StartSound(NULL,sfx_swtchn);
 +            return true;
 +        }
 +        else if (key == key_menu_save)     // Save
 +        {
 +            M_StartControlPanel();
 +            S_StartSound(NULL,sfx_swtchn);
 +            M_SaveGame(0);
 +            return true;
 +        }
 +        else if (key == key_menu_load)     // Load
 +        {
 +            M_StartControlPanel();
 +            S_StartSound(NULL,sfx_swtchn);
 +            M_LoadGame(0);
 +            return true;
 +        }
 +        else if (key == key_menu_volume)   // Sound Volume
 +        {
 +            M_StartControlPanel ();
 +            currentMenu = &SoundDef;
 +            itemOn = sfx_vol;
 +            S_StartSound(NULL,sfx_swtchn);
 +            return true;
 +        }
 +        /*
 +        else if (key == key_menu_detail)   // Detail toggle
 +        {
 +            M_ChangeDetail(0);
 +            S_StartSound(NULL,sfx_swtchn);
 +            return true;
 +        }
 +        */
 +        else if (key == key_menu_qsave)    // Quicksave
 +        {
 +            S_StartSound(NULL,sfx_swtchn);
 +            M_QuickSave();
 +            return true;
 +        }
 +        else if (key == key_menu_endgame)  // End game
 +        {
 +            S_StartSound(NULL,sfx_swtchn);
 +            M_EndGame(0);
 +            return true;
 +        }
 +        else if (key == key_menu_messages) // Toggle messages
 +        {
 +            M_ChangeMessages(0);
 +            S_StartSound(NULL,sfx_swtchn);
 +            return true;
 +        }
 +        else if (key == key_menu_qload)    // Quickload
 +        {
 +            S_StartSound(NULL,sfx_swtchn);
 +            M_QuickLoad();
 +            return true;
 +        }
 +        else if (key == key_menu_quit)     // Quit DOOM
 +        {
 +            S_StartSound(NULL,sfx_swtchn);
 +            M_QuitDOOM(0);
 +            return true;
 +        }
 +        else if (key == key_menu_gamma)    // gamma toggle
 +        {
 +            usegamma++;
 +            if (usegamma > 4)
 +                usegamma = 0;
 +            players[consoleplayer].message = DEH_String(gammamsg[usegamma]);
 +            I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE));
 +            return true;
 +        }
 +    }
 +
 +    // Pop-up menu?
 +    if (!menuactive)
 +    {
 +        if (key == key_menu_activate)
 +        {
 +            M_StartControlPanel ();
 +            S_StartSound(NULL,sfx_swtchn);
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    // Keys usable within menu
 +
 +    if (key == key_menu_down)
 +    {
 +        // Move down to next item
 +
 +        do
 +        {
 +            if (itemOn+1 > currentMenu->numitems-1)
 +                itemOn = 0;
 +            else itemOn++;
 +            S_StartSound(NULL,sfx_pstop);
 +        } while(currentMenu->menuitems[itemOn].status==-1);
 +
 +        return true;
 +    }
 +    else if (key == key_menu_up)
 +    {
 +        // Move back up to previous item
 +
 +        do
 +        {
 +            if (!itemOn)
 +                itemOn = currentMenu->numitems-1;
 +            else itemOn--;
 +            S_StartSound(NULL,sfx_pstop);
 +        } while(currentMenu->menuitems[itemOn].status==-1);
 +
 +        return true;
 +    }
 +    else if (key == key_menu_left)
 +    {
 +        // Slide slider left
 +
 +        if (currentMenu->menuitems[itemOn].routine &&
 +            currentMenu->menuitems[itemOn].status == 2)
 +        {
 +            S_StartSound(NULL,sfx_stnmov);
 +            currentMenu->menuitems[itemOn].routine(0);
 +        }
 +        return true;
 +    }
 +    else if (key == key_menu_right)
 +    {
 +        // Slide slider right
 +
 +        if (currentMenu->menuitems[itemOn].routine &&
 +            currentMenu->menuitems[itemOn].status == 2)
 +        {
 +            S_StartSound(NULL,sfx_stnmov);
 +            currentMenu->menuitems[itemOn].routine(1);
 +        }
 +        return true;
 +    }
 +    else if (key == key_menu_forward)
 +    {
 +        // Activate menu item
 +
 +        if (currentMenu->menuitems[itemOn].routine &&
 +            currentMenu->menuitems[itemOn].status)
 +        {
 +            currentMenu->lastOn = itemOn;
 +            if (currentMenu->menuitems[itemOn].status == 2)
 +            {
 +                currentMenu->menuitems[itemOn].routine(1);      // right arrow
 +                S_StartSound(NULL,sfx_stnmov);
 +            }
 +            else
 +            {
 +                currentMenu->menuitems[itemOn].routine(itemOn);
 +                S_StartSound(NULL,sfx_swish);   // villsa [STRIFE] TODO - fix sounds
 +            }
 +        }
 +        return true;
 +    }
 +    else if (key == key_menu_activate)
 +    {
 +        // Deactivate menu
 +
 +        currentMenu->lastOn = itemOn;
 +        M_ClearMenus (0);
 +        S_StartSound(NULL,sfx_swish);   // villsa [STRIFE] TODO - fix sounds
 +        return true;
 +    }
 +    else if (key == key_menu_back)
 +    {
 +        // Go back to previous menu
 +
 +        currentMenu->lastOn = itemOn;
 +        if (currentMenu->prevMenu)
 +        {
 +            currentMenu = currentMenu->prevMenu;
 +            itemOn = currentMenu->lastOn;
 +            S_StartSound(NULL,sfx_swtchn);
 +        }
 +        return true;
 +    }
 +    else if (ch != 0)
 +    {
 +        // Keyboard shortcut?
 +
 +        for (i = itemOn+1;i < currentMenu->numitems;i++)
 +        {
 +            if (currentMenu->menuitems[i].alphaKey == ch)
 +            {
 +                itemOn = i;
 +                S_StartSound(NULL,sfx_pstop);
 +                return true;
 +            }
 +        }
 +
 +        for (i = 0;i <= itemOn;i++)
 +        {
 +            if (currentMenu->menuitems[i].alphaKey == ch)
 +            {
 +                itemOn = i;
 +                S_StartSound(NULL,sfx_pstop);
 +                return true;
 +            }
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +
 +
 +//
 +// M_StartControlPanel
 +//
 +void M_StartControlPanel (void)
 +{
 +    // intro might call this repeatedly
 +    if (menuactive)
 +        return;
 +
 +    menuactive = 1;
 +    menupause = true;
 +    currentMenu = &MainDef;         // JDC
 +    itemOn = currentMenu->lastOn;   // JDC
 +}
 +
 +
 +//
 +// M_Drawer
 +// Called after the view has been rendered,
 +// but before it has been blitted.
 +//
 +void M_Drawer (void)
 +{
 +    static short	x;
 +    static short	y;
 +    unsigned int	i;
 +    unsigned int	max;
 +    char		string[80];
 +    char               *name;
 +    int			start;
 +
 +    inhelpscreens = false;
 +
 +    // Horiz. & Vertically center string and print it.
 +    if (messageToPrint)
 +    {
 +        start = 0;
 +        y = 100 - M_StringHeight(messageString) / 2;
 +        while (messageString[start] != '\0')
 +        {
 +            int foundnewline = 0;
 +
 +            for (i = 0; i < strlen(messageString + start); i++)
 +                if (messageString[start + i] == '\n')
 +                {
 +                    memset(string, 0, sizeof(string));
 +                    strncpy(string, messageString + start, i);
 +                    foundnewline = 1;
 +                    start += i + 1;
 +                    break;
 +                }
 +
 +                if (!foundnewline)
 +                {
 +                    strcpy(string, messageString + start);
 +                    start += strlen(string);
 +                }
 +
 +                x = 160 - M_StringWidth(string) / 2;
 +                M_WriteText(x, y, string);
 +                y += SHORT(hu_font[0]->height);
 +        }
 +
 +        return;
 +    }
 +
 +    if (!menuactive)
 +        return;
 +
 +    if (currentMenu->routine)
 +        currentMenu->routine();         // call Draw routine
 +
 +    // DRAW MENU
 +    x = currentMenu->x;
 +    y = currentMenu->y;
 +    max = currentMenu->numitems;
 +
 +    for (i=0;i<max;i++)
 +    {
 +        name = DEH_String(currentMenu->menuitems[i].name);
 +
 +        if (name[0])
 +        {
 +            V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE));
 +        }
 +        y += LINEHEIGHT;
 +    }
 +
 +
 +    // haleyjd 08/27/10: [STRIFE] Adjust to draw spinning Sigil
 +    // DRAW SIGIL
 +    V_DrawPatchDirect(x + CURSORXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,
 +                      W_CacheLumpName(DEH_String(cursorName[whichCursor]),
 +                                      PU_CACHE));
 +
 +}
 +
 +
 +//
 +// M_ClearMenus
 +//
 +// haleyjd 08/28/10: [STRIFE] Added an int param so this can be called by menus.
 +//         09/08/10: Added menupause.
 +//
 +void M_ClearMenus (int choice)
 +{
 +    choice = 0;     // haleyjd: for no warning; not from decompilation.
 +    menuactive = 0;
 +    menupause = 0;
 +}
 +
 +
 +
 +
 +//
 +// M_SetupNextMenu
 +//
 +void M_SetupNextMenu(menu_t *menudef)
 +{
 +    currentMenu = menudef;
 +    itemOn = currentMenu->lastOn;
 +}
 +
 +
 +//
 +// M_Ticker
 +//
 +// haleyjd 08/27/10: [STRIFE] Rewritten for Sigil cursor
 +//
 +void M_Ticker (void)
 +{
 +    if (--cursorAnimCounter <= 0)
 +    {
 +        whichCursor = (whichCursor + 1) % 8;
 +        cursorAnimCounter = 5;
 +    }
 +}
 +
 +
 +//
 +// M_Init
 +//
 +// haleyjd 08/27/10: [STRIFE] Removed DOOM gamemode stuff
 +//
 +void M_Init (void)
 +{
 +    currentMenu = &MainDef;
 +    menuactive = 0;
 +    itemOn = currentMenu->lastOn;
 +    whichCursor = 0;
 +    cursorAnimCounter = 10;
 +    screenSize = screenblocks - 3;
 +    messageToPrint = 0;
 +    messageString = NULL;
 +    messageLastMenuActive = menuactive; // STRIFE-FIXME: assigns 0 here...
 +    quickSaveSlot = -1;
 +
 +    // Here we could catch other version dependencies,
 +    //  like HELP1/2, and four episodes.
 +}
 +
diff --cc src/strife/p_saveg.c
index 6da095e5,00000000..52c6bcd5
mode 100644,000000..100644
--- a/src/strife/p_saveg.c
+++ b/src/strife/p_saveg.c
@@@ -1,1886 -1,0 +1,1904 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Archiving: SaveGame I/O.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "dstrings.h"
 +#include "deh_main.h"
 +#include "i_system.h"
 +#include "z_zone.h"
 +#include "p_local.h"
 +#include "p_saveg.h"
 +
 +// State.
 +#include "doomstat.h"
 +#include "r_state.h"
 +
 +#define SAVEGAME_EOF 0x1d
 +#define VERSIONSIZE 16
 +
 +FILE *save_stream;
 +int savegamelength;
++boolean savegame_error;
 +
 +// Get the filename of a temporary file to write the savegame to.  After
 +// the file has been successfully saved, it will be renamed to the
 +// real file.
 +
 +char *P_TempSaveGameFile(void)
 +{
 +    static char *filename = NULL;
 +
 +    if (filename == NULL)
 +    {
 +        filename = malloc(strlen(savegamedir) + 32);
 +    }
 +
 +    sprintf(filename, "%stemp.dsg", savegamedir);
 +
 +    return filename;
 +}
 +
 +// Get the filename of the save game file to use for the specified slot.
 +
 +char *P_SaveGameFile(int slot)
 +{
 +    static char *filename = NULL;
 +    char basename[32];
 +
 +    if (filename == NULL)
 +    {
 +        filename = malloc(strlen(savegamedir) + 32);
 +    }
 +
-     sprintf(basename, DEH_String(SAVEGAMENAME "%d.dsg"), slot);
++    DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot);
 +
 +    sprintf(filename, "%s%s", savegamedir, basename);
 +
 +    return filename;
 +}
 +
 +// Endian-safe integer read/write functions
 +
 +static byte saveg_read8(void)
 +{
 +    byte result;
 +
-     fread(&result, 1, 1, save_stream);
++    if (fread(&result, 1, 1, save_stream) < 1)
++    {
++        if (!savegame_error)
++        {
++            fprintf(stderr, "saveg_read8: Unexpected end of file while "
++                            "reading save game\n");
++
++            savegame_error = true;
++        }
++    }
 +
 +    return result;
 +}
 +
 +static void saveg_write8(byte value)
 +{
-     fwrite(&value, 1, 1, save_stream);
++    if (fwrite(&value, 1, 1, save_stream) < 1)
++    {
++        if (!savegame_error)
++        {
++            fprintf(stderr, "saveg_write8: Error while writing save game\n");
++
++            savegame_error = true;
++        }
++    }
 +}
 +
 +static short saveg_read16(void)
 +{
 +    int result;
 +
 +    result = saveg_read8();
 +    result |= saveg_read8() << 8;
 +
 +    return result;
 +}
 +
 +static void saveg_write16(short value)
 +{
 +    saveg_write8(value & 0xff);
 +    saveg_write8((value >> 8) & 0xff);
 +}
 +
 +static int saveg_read32(void)
 +{
 +    int result;
 +
 +    result = saveg_read8();
 +    result |= saveg_read8() << 8;
 +    result |= saveg_read8() << 16;
 +    result |= saveg_read8() << 24;
 +
 +    return result;
 +}
 +
 +static void saveg_write32(int value)
 +{
 +    saveg_write8(value & 0xff);
 +    saveg_write8((value >> 8) & 0xff);
 +    saveg_write8((value >> 16) & 0xff);
 +    saveg_write8((value >> 24) & 0xff);
 +}
 +
 +// Pad to 4-byte boundaries
 +
 +static void saveg_read_pad(void)
 +{
 +    unsigned long pos;
 +    int padding;
 +    int i;
 +
 +    pos = ftell(save_stream);
 +
 +    padding = (4 - (pos & 3)) & 3;
 +
 +    for (i=0; i<padding; ++i)
 +    {
 +        saveg_read8();
 +    }
 +}
 +
 +static void saveg_write_pad(void)
 +{
 +    unsigned long pos;
 +    int padding;
 +    int i;
 +
 +    pos = ftell(save_stream);
 +
 +    padding = (4 - (pos & 3)) & 3;
 +
 +    for (i=0; i<padding; ++i)
 +    {
 +        saveg_write8(0);
 +    }
 +}
 +
 +
 +// Pointers
 +
 +static void *saveg_readp(void)
 +{
 +    return (void *) saveg_read32();
 +}
 +
 +static void saveg_writep(void *p)
 +{
 +    saveg_write32((int) p);
 +}
 +
 +// Enum values are 32-bit integers.
 +
 +#define saveg_read_enum saveg_read32
 +#define saveg_write_enum saveg_write32
 +
 +//
 +// Structure read/write functions
 +//
 +
 +//
 +// mapthing_t
 +//
 +
 +static void saveg_read_mapthing_t(mapthing_t *str)
 +{
 +    // short x;
 +    str->x = saveg_read16();
 +
 +    // short y;
 +    str->y = saveg_read16();
 +
 +    // short angle;
 +    str->angle = saveg_read16();
 +
 +    // short type;
 +    str->type = saveg_read16();
 +
 +    // short options;
 +    str->options = saveg_read16();
 +}
 +
 +static void saveg_write_mapthing_t(mapthing_t *str)
 +{
 +    // short x;
 +    saveg_write16(str->x);
 +
 +    // short y;
 +    saveg_write16(str->y);
 +
 +    // short angle;
 +    saveg_write16(str->angle);
 +
 +    // short type;
 +    saveg_write16(str->type);
 +
 +    // short options;
 +    saveg_write16(str->options);
 +}
 +
 +//
 +// actionf_t
 +//
 +
 +static void saveg_read_actionf_t(actionf_t *str)
 +{
 +    // actionf_p1 acp1;
 +    str->acp1 = saveg_readp();
 +}
 +
 +static void saveg_write_actionf_t(actionf_t *str)
 +{
 +    // actionf_p1 acp1;
 +    saveg_writep(str->acp1);
 +}
 +
 +//
 +// think_t
 +//
 +// This is just an actionf_t.
 +//
 +
 +#define saveg_read_think_t saveg_read_actionf_t
 +#define saveg_write_think_t saveg_write_actionf_t
 +
 +//
 +// thinker_t
 +//
 +
 +static void saveg_read_thinker_t(thinker_t *str)
 +{
 +    // struct thinker_s* prev;
 +    str->prev = saveg_readp();
 +
 +    // struct thinker_s* next;
 +    str->next = saveg_readp();
 +
 +    // think_t function;
 +    saveg_read_think_t(&str->function);
 +}
 +
 +static void saveg_write_thinker_t(thinker_t *str)
 +{
 +    // struct thinker_s* prev;
 +    saveg_writep(str->prev);
 +
 +    // struct thinker_s* next;
 +    saveg_writep(str->next);
 +
 +    // think_t function;
 +    saveg_write_think_t(&str->function);
 +}
 +
 +//
 +// mobj_t
 +//
 +
 +static void saveg_read_mobj_t(mobj_t *str)
 +{
 +    int pl;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // fixed_t x;
 +    str->x = saveg_read32();
 +
 +    // fixed_t y;
 +    str->y = saveg_read32();
 +
 +    // fixed_t z;
 +    str->z = saveg_read32();
 +
 +    // struct mobj_s* snext;
 +    str->snext = saveg_readp();
 +
 +    // struct mobj_s* sprev;
 +    str->sprev = saveg_readp();
 +
 +    // angle_t angle;
 +    str->angle = saveg_read32();
 +
 +    // spritenum_t sprite;
 +    str->sprite = saveg_read_enum();
 +
 +    // int frame;
 +    str->frame = saveg_read32();
 +
 +    // struct mobj_s* bnext;
 +    str->bnext = saveg_readp();
 +
 +    // struct mobj_s* bprev;
 +    str->bprev = saveg_readp();
 +
 +    // struct subsector_s* subsector;
 +    str->subsector = saveg_readp();
 +
 +    // fixed_t floorz;
 +    str->floorz = saveg_read32();
 +
 +    // fixed_t ceilingz;
 +    str->ceilingz = saveg_read32();
 +
 +    // fixed_t radius;
 +    str->radius = saveg_read32();
 +
 +    // fixed_t height;
 +    str->height = saveg_read32();
 +
 +    // fixed_t momx;
 +    str->momx = saveg_read32();
 +
 +    // fixed_t momy;
 +    str->momy = saveg_read32();
 +
 +    // fixed_t momz;
 +    str->momz = saveg_read32();
 +
 +    // int validcount;
 +    str->validcount = saveg_read32();
 +
 +    // mobjtype_t type;
 +    str->type = saveg_read_enum();
 +
 +    // mobjinfo_t* info;
 +    str->info = saveg_readp();
 +
 +    // int tics;
 +    str->tics = saveg_read32();
 +
 +    // state_t* state;
 +    str->state = &states[saveg_read32()];
 +
 +    // int flags;
 +    str->flags = saveg_read32();
 +
 +    // int health;
 +    str->health = saveg_read32();
 +
 +    // int movedir;
 +    str->movedir = saveg_read32();
 +
 +    // int movecount;
 +    str->movecount = saveg_read32();
 +
 +    // struct mobj_s* target;
 +    str->target = saveg_readp();
 +
 +    // int reactiontime;
 +    str->reactiontime = saveg_read32();
 +
 +    // int threshold;
 +    str->threshold = saveg_read32();
 +
 +    // struct player_s* player;
 +    pl = saveg_read32();
 +
 +    if (pl > 0)
 +    {
 +        str->player = &players[pl - 1];
 +        str->player->mo = str;
 +    }
 +    else
 +    {
 +        str->player = NULL;
 +    }
 +
 +    // int lastlook;
 +    str->lastlook = saveg_read32();
 +
 +    // mapthing_t spawnpoint;
 +    saveg_read_mapthing_t(&str->spawnpoint);
 +
 +    // struct mobj_s* tracer;
 +    str->tracer = saveg_readp();
 +}
 +
 +static void saveg_write_mobj_t(mobj_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // fixed_t x;
 +    saveg_write32(str->x);
 +
 +    // fixed_t y;
 +    saveg_write32(str->y);
 +
 +    // fixed_t z;
 +    saveg_write32(str->z);
 +
 +    // struct mobj_s* snext;
 +    saveg_writep(str->snext);
 +
 +    // struct mobj_s* sprev;
 +    saveg_writep(str->sprev);
 +
 +    // angle_t angle;
 +    saveg_write32(str->angle);
 +
 +    // spritenum_t sprite;
 +    saveg_write_enum(str->sprite);
 +
 +    // int frame;
 +    saveg_write32(str->frame);
 +
 +    // struct mobj_s* bnext;
 +    saveg_writep(str->bnext);
 +
 +    // struct mobj_s* bprev;
 +    saveg_writep(str->bprev);
 +
 +    // struct subsector_s* subsector;
 +    saveg_writep(str->subsector);
 +
 +    // fixed_t floorz;
 +    saveg_write32(str->floorz);
 +
 +    // fixed_t ceilingz;
 +    saveg_write32(str->ceilingz);
 +
 +    // fixed_t radius;
 +    saveg_write32(str->radius);
 +
 +    // fixed_t height;
 +    saveg_write32(str->height);
 +
 +    // fixed_t momx;
 +    saveg_write32(str->momx);
 +
 +    // fixed_t momy;
 +    saveg_write32(str->momy);
 +
 +    // fixed_t momz;
 +    saveg_write32(str->momz);
 +
 +    // int validcount;
 +    saveg_write32(str->validcount);
 +
 +    // mobjtype_t type;
 +    saveg_write_enum(str->type);
 +
 +    // mobjinfo_t* info;
 +    saveg_writep(str->info);
 +
 +    // int tics;
 +    saveg_write32(str->tics);
 +
 +    // state_t* state;
 +    saveg_write32(str->state - states);
 +
 +    // int flags;
 +    saveg_write32(str->flags);
 +
 +    // int health;
 +    saveg_write32(str->health);
 +
 +    // int movedir;
 +    saveg_write32(str->movedir);
 +
 +    // int movecount;
 +    saveg_write32(str->movecount);
 +
 +    // struct mobj_s* target;
 +    saveg_writep(str->target);
 +
 +    // int reactiontime;
 +    saveg_write32(str->reactiontime);
 +
 +    // int threshold;
 +    saveg_write32(str->threshold);
 +
 +    // struct player_s* player;
 +    if (str->player)
 +    {
 +        saveg_write32(str->player - players + 1);
 +    }
 +    else
 +    {
 +        saveg_write32(0);
 +    }
 +
 +    // int lastlook;
 +    saveg_write32(str->lastlook);
 +
 +    // mapthing_t spawnpoint;
 +    saveg_write_mapthing_t(&str->spawnpoint);
 +
 +    // struct mobj_s* tracer;
 +    saveg_writep(str->tracer);
 +}
 +
 +
 +//
 +// ticcmd_t
 +//
 +
 +static void saveg_read_ticcmd_t(ticcmd_t *str)
 +{
 +
 +    // signed char forwardmove;
 +    str->forwardmove = saveg_read8();
 +
 +    // signed char sidemove;
 +    str->sidemove = saveg_read8();
 +
 +    // short angleturn;
 +    str->angleturn = saveg_read16();
 +
 +    // short consistancy;
 +    str->consistancy = saveg_read16();
 +
 +    // byte chatchar;
 +    str->chatchar = saveg_read8();
 +
 +    // byte buttons;
 +    str->buttons = saveg_read8();
 +}
 +
 +static void saveg_write_ticcmd_t(ticcmd_t *str)
 +{
 +
 +    // signed char forwardmove;
 +    saveg_write8(str->forwardmove);
 +
 +    // signed char sidemove;
 +    saveg_write8(str->sidemove);
 +
 +    // short angleturn;
 +    saveg_write16(str->angleturn);
 +
 +    // short consistancy;
 +    saveg_write16(str->consistancy);
 +
 +    // byte chatchar;
 +    saveg_write8(str->chatchar);
 +
 +    // byte buttons;
 +    saveg_write8(str->buttons);
 +}
 +
 +//
 +// pspdef_t
 +//
 +
 +static void saveg_read_pspdef_t(pspdef_t *str)
 +{
 +    int state;
 +
 +    // state_t* state;
 +    state = saveg_read32();
 +
 +    if (state > 0)
 +    {
 +        str->state = &states[state];
 +    }
 +    else
 +    {
 +        str->state = NULL;
 +    }
 +
 +    // int tics;
 +    str->tics = saveg_read32();
 +
 +    // fixed_t sx;
 +    str->sx = saveg_read32();
 +
 +    // fixed_t sy;
 +    str->sy = saveg_read32();
 +}
 +
 +static void saveg_write_pspdef_t(pspdef_t *str)
 +{
 +    // state_t* state;
 +    if (str->state)
 +    {
 +        saveg_write32(str->state - states);
 +    }
 +    else
 +    {
 +        saveg_write32(0);
 +    }
 +
 +    // int tics;
 +    saveg_write32(str->tics);
 +
 +    // fixed_t sx;
 +    saveg_write32(str->sx);
 +
 +    // fixed_t sy;
 +    saveg_write32(str->sy);
 +}
 +
 +//
 +// player_t
 +//
 +
 +static void saveg_read_player_t(player_t *str)
 +{
 +    int i;
 +
 +    // mobj_t* mo;
 +    str->mo = saveg_readp();
 +
 +    // playerstate_t playerstate;
 +    str->playerstate = saveg_read_enum();
 +
 +    // ticcmd_t cmd;
 +    saveg_read_ticcmd_t(&str->cmd);
 +
 +    // fixed_t viewz;
 +    str->viewz = saveg_read32();
 +
 +    // fixed_t viewheight;
 +    str->viewheight = saveg_read32();
 +
 +    // fixed_t deltaviewheight;
 +    str->deltaviewheight = saveg_read32();
 +
 +    // fixed_t bob;
 +    str->bob = saveg_read32();
 +
 +    // int health;
 +    str->health = saveg_read32();
 +
 +    // int armorpoints;
 +    str->armorpoints = saveg_read32();
 +
 +    // int armortype;
 +    str->armortype = saveg_read32();
 +
 +    // int powers[NUMPOWERS];
 +    for (i=0; i<NUMPOWERS; ++i)
 +    {
 +        str->powers[i] = saveg_read32();
 +    }
 +
 +    // boolean cards[NUMCARDS];
 +    for (i=0; i<NUMCARDS; ++i)
 +    {
 +        str->cards[i] = saveg_read32();
 +    }
 +
 +    // boolean backpack;
 +    str->backpack = saveg_read32();
 +
 +    // int frags[MAXPLAYERS];
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        str->frags[i] = saveg_read32();
 +    }
 +
 +    // weapontype_t readyweapon;
 +    str->readyweapon = saveg_read_enum();
 +
 +    // weapontype_t pendingweapon;
 +    str->pendingweapon = saveg_read_enum();
 +
 +    // boolean weaponowned[NUMWEAPONS];
 +    for (i=0; i<NUMWEAPONS; ++i)
 +    {
 +        str->weaponowned[i] = saveg_read32();
 +    }
 +
 +    // int ammo[NUMAMMO];
 +    for (i=0; i<NUMAMMO; ++i)
 +    {
 +        str->ammo[i] = saveg_read32();
 +    }
 +
 +    // int maxammo[NUMAMMO];
 +    for (i=0; i<NUMAMMO; ++i)
 +    {
 +        str->maxammo[i] = saveg_read32();
 +    }
 +
 +    // int attackdown;
 +    str->attackdown = saveg_read32();
 +
 +    // int usedown;
 +    str->usedown = saveg_read32();
 +
 +    // int cheats;
 +    str->cheats = saveg_read32();
 +
 +    // int refire;
 +    str->refire = saveg_read32();
 +
 +    // int killcount;
 +    str->killcount = saveg_read32();
 +
 +    // haleyjd 08/30/10 [STRIFE] No itemcount.
 +    // int itemcount;
 +    //str->itemcount = saveg_read32();
 +
 +    // haleyjd 08/30/10 [STRIFE] No secretcount.
 +    // int secretcount;
 +    //str->secretcount = saveg_read32();
 +
 +    // char* message;
 +    str->message = saveg_readp();
 +
 +    // int damagecount;
 +    str->damagecount = saveg_read32();
 +
 +    // int bonuscount;
 +    str->bonuscount = saveg_read32();
 +
 +    // mobj_t* attacker;
 +    str->attacker = saveg_readp();
 +
 +    // int extralight;
 +    str->extralight = saveg_read32();
 +
 +    // int fixedcolormap;
 +    str->fixedcolormap = saveg_read32();
 +
 +    // int colormap;
 +    str->colormap = saveg_read32();
 +
 +    // pspdef_t psprites[NUMPSPRITES];
 +    for (i=0; i<NUMPSPRITES; ++i)
 +    {
 +        saveg_read_pspdef_t(&str->psprites[i]);
 +    }
 +
 +    // haleyjd 08/30/10: [STRIFE] No intermission, no didsecret.
 +    // boolean didsecret;
 +    //str->didsecret = saveg_read32();
 +}
 +
 +static void saveg_write_player_t(player_t *str)
 +{
 +    int i;
 +
 +    // mobj_t* mo;
 +    saveg_writep(str->mo);
 +
 +    // playerstate_t playerstate;
 +    saveg_write_enum(str->playerstate);
 +
 +    // ticcmd_t cmd;
 +    saveg_write_ticcmd_t(&str->cmd);
 +
 +    // fixed_t viewz;
 +    saveg_write32(str->viewz);
 +
 +    // fixed_t viewheight;
 +    saveg_write32(str->viewheight);
 +
 +    // fixed_t deltaviewheight;
 +    saveg_write32(str->deltaviewheight);
 +
 +    // fixed_t bob;
 +    saveg_write32(str->bob);
 +
 +    // int health;
 +    saveg_write32(str->health);
 +
 +    // int armorpoints;
 +    saveg_write32(str->armorpoints);
 +
 +    // int armortype;
 +    saveg_write32(str->armortype);
 +
 +    // int powers[NUMPOWERS];
 +    for (i=0; i<NUMPOWERS; ++i)
 +    {
 +        saveg_write32(str->powers[i]);
 +    }
 +
 +    // boolean cards[NUMCARDS];
 +    for (i=0; i<NUMCARDS; ++i)
 +    {
 +        saveg_write32(str->cards[i]);
 +    }
 +
 +    // boolean backpack;
 +    saveg_write32(str->backpack);
 +
 +    // int frags[MAXPLAYERS];
 +    for (i=0; i<MAXPLAYERS; ++i)
 +    {
 +        saveg_write32(str->frags[i]);
 +    }
 +
 +    // weapontype_t readyweapon;
 +    saveg_write_enum(str->readyweapon);
 +
 +    // weapontype_t pendingweapon;
 +    saveg_write_enum(str->pendingweapon);
 +
 +    // boolean weaponowned[NUMWEAPONS];
 +    for (i=0; i<NUMWEAPONS; ++i)
 +    {
 +        saveg_write32(str->weaponowned[i]);
 +    }
 +
 +    // int ammo[NUMAMMO];
 +    for (i=0; i<NUMAMMO; ++i)
 +    {
 +        saveg_write32(str->ammo[i]);
 +    }
 +
 +    // int maxammo[NUMAMMO];
 +    for (i=0; i<NUMAMMO; ++i)
 +    {
 +        saveg_write32(str->maxammo[i]);
 +    }
 +
 +    // int attackdown;
 +    saveg_write32(str->attackdown);
 +
 +    // int usedown;
 +    saveg_write32(str->usedown);
 +
 +    // int cheats;
 +    saveg_write32(str->cheats);
 +
 +    // int refire;
 +    saveg_write32(str->refire);
 +
 +    // int killcount;
 +    saveg_write32(str->killcount);
 +
 +    // haleyjd 08/30/10 [STRIFE] No itemcount
 +    // int itemcount;
 +    //saveg_write32(str->itemcount);
 +
 +    // haleyjd 08/30/10 [STRIFE] No secretcount
 +    // int secretcount;
 +    //saveg_write32(str->secretcount);
 +
 +    // char* message;
 +    saveg_writep(str->message);
 +
 +    // int damagecount;
 +    saveg_write32(str->damagecount);
 +
 +    // int bonuscount;
 +    saveg_write32(str->bonuscount);
 +
 +    // mobj_t* attacker;
 +    saveg_writep(str->attacker);
 +
 +    // int extralight;
 +    saveg_write32(str->extralight);
 +
 +    // int fixedcolormap;
 +    saveg_write32(str->fixedcolormap);
 +
 +    // int colormap;
 +    saveg_write32(str->colormap);
 +
 +    // pspdef_t psprites[NUMPSPRITES];
 +    for (i=0; i<NUMPSPRITES; ++i)
 +    {
 +        saveg_write_pspdef_t(&str->psprites[i]);
 +    }
 +
 +    // haleyjd 08/30/10: [STRIFE] No intermission, no secret.
 +    // boolean didsecret;
 +    //saveg_write32(str->didsecret);
 +}
 +
 +
 +//
 +// ceiling_t
 +//
 +
 +static void saveg_read_ceiling_t(ceiling_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // ceiling_e type;
 +    str->type = saveg_read_enum();
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // fixed_t bottomheight;
 +    str->bottomheight = saveg_read32();
 +
 +    // fixed_t topheight;
 +    str->topheight = saveg_read32();
 +
 +    // fixed_t speed;
 +    str->speed = saveg_read32();
 +
 +    // boolean crush;
 +    str->crush = saveg_read32();
 +
 +    // int direction;
 +    str->direction = saveg_read32();
 +
 +    // int tag;
 +    str->tag = saveg_read32();
 +
 +    // int olddirection;
 +    str->olddirection = saveg_read32();
 +}
 +
 +static void saveg_write_ceiling_t(ceiling_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // ceiling_e type;
 +    saveg_write_enum(str->type);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // fixed_t bottomheight;
 +    saveg_write32(str->bottomheight);
 +
 +    // fixed_t topheight;
 +    saveg_write32(str->topheight);
 +
 +    // fixed_t speed;
 +    saveg_write32(str->speed);
 +
 +    // boolean crush;
 +    saveg_write32(str->crush);
 +
 +    // int direction;
 +    saveg_write32(str->direction);
 +
 +    // int tag;
 +    saveg_write32(str->tag);
 +
 +    // int olddirection;
 +    saveg_write32(str->olddirection);
 +}
 +
 +//
 +// vldoor_t
 +//
 +
 +static void saveg_read_vldoor_t(vldoor_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // vldoor_e type;
 +    str->type = saveg_read_enum();
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // fixed_t topheight;
 +    str->topheight = saveg_read32();
 +
 +    // fixed_t speed;
 +    str->speed = saveg_read32();
 +
 +    // int direction;
 +    str->direction = saveg_read32();
 +
 +    // int topwait;
 +    str->topwait = saveg_read32();
 +
 +    // int topcountdown;
 +    str->topcountdown = saveg_read32();
 +}
 +
 +static void saveg_write_vldoor_t(vldoor_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // vldoor_e type;
 +    saveg_write_enum(str->type);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // fixed_t topheight;
 +    saveg_write32(str->topheight);
 +
 +    // fixed_t speed;
 +    saveg_write32(str->speed);
 +
 +    // int direction;
 +    saveg_write32(str->direction);
 +
 +    // int topwait;
 +    saveg_write32(str->topwait);
 +
 +    // int topcountdown;
 +    saveg_write32(str->topcountdown);
 +}
 +
 +//
 +// floormove_t
 +//
 +
 +static void saveg_read_floormove_t(floormove_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // floor_e type;
 +    str->type = saveg_read_enum();
 +
 +    // boolean crush;
 +    str->crush = saveg_read32();
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // int direction;
 +    str->direction = saveg_read32();
 +
 +    // int newspecial;
 +    str->newspecial = saveg_read32();
 +
 +    // short texture;
 +    str->texture = saveg_read16();
 +
 +    // fixed_t floordestheight;
 +    str->floordestheight = saveg_read32();
 +
 +    // fixed_t speed;
 +    str->speed = saveg_read32();
 +}
 +
 +static void saveg_write_floormove_t(floormove_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // floor_e type;
 +    saveg_write_enum(str->type);
 +
 +    // boolean crush;
 +    saveg_write32(str->crush);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // int direction;
 +    saveg_write32(str->direction);
 +
 +    // int newspecial;
 +    saveg_write32(str->newspecial);
 +
 +    // short texture;
 +    saveg_write16(str->texture);
 +
 +    // fixed_t floordestheight;
 +    saveg_write32(str->floordestheight);
 +
 +    // fixed_t speed;
 +    saveg_write32(str->speed);
 +}
 +
 +//
 +// plat_t
 +//
 +
 +static void saveg_read_plat_t(plat_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // fixed_t speed;
 +    str->speed = saveg_read32();
 +
 +    // fixed_t low;
 +    str->low = saveg_read32();
 +
 +    // fixed_t high;
 +    str->high = saveg_read32();
 +
 +    // int wait;
 +    str->wait = saveg_read32();
 +
 +    // int count;
 +    str->count = saveg_read32();
 +
 +    // plat_e status;
 +    str->status = saveg_read_enum();
 +
 +    // plat_e oldstatus;
 +    str->oldstatus = saveg_read_enum();
 +
 +    // boolean crush;
 +    str->crush = saveg_read32();
 +
 +    // int tag;
 +    str->tag = saveg_read32();
 +
 +    // plattype_e type;
 +    str->type = saveg_read_enum();
 +}
 +
 +static void saveg_write_plat_t(plat_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // fixed_t speed;
 +    saveg_write32(str->speed);
 +
 +    // fixed_t low;
 +    saveg_write32(str->low);
 +
 +    // fixed_t high;
 +    saveg_write32(str->high);
 +
 +    // int wait;
 +    saveg_write32(str->wait);
 +
 +    // int count;
 +    saveg_write32(str->count);
 +
 +    // plat_e status;
 +    saveg_write_enum(str->status);
 +
 +    // plat_e oldstatus;
 +    saveg_write_enum(str->oldstatus);
 +
 +    // boolean crush;
 +    saveg_write32(str->crush);
 +
 +    // int tag;
 +    saveg_write32(str->tag);
 +
 +    // plattype_e type;
 +    saveg_write_enum(str->type);
 +}
 +
 +//
 +// lightflash_t
 +//
 +
 +static void saveg_read_lightflash_t(lightflash_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // int count;
 +    str->count = saveg_read32();
 +
 +    // int maxlight;
 +    str->maxlight = saveg_read32();
 +
 +    // int minlight;
 +    str->minlight = saveg_read32();
 +
 +    // int maxtime;
 +    str->maxtime = saveg_read32();
 +
 +    // int mintime;
 +    str->mintime = saveg_read32();
 +}
 +
 +static void saveg_write_lightflash_t(lightflash_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // int count;
 +    saveg_write32(str->count);
 +
 +    // int maxlight;
 +    saveg_write32(str->maxlight);
 +
 +    // int minlight;
 +    saveg_write32(str->minlight);
 +
 +    // int maxtime;
 +    saveg_write32(str->maxtime);
 +
 +    // int mintime;
 +    saveg_write32(str->mintime);
 +}
 +
 +//
 +// strobe_t
 +//
 +
 +static void saveg_read_strobe_t(strobe_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // int count;
 +    str->count = saveg_read32();
 +
 +    // int minlight;
 +    str->minlight = saveg_read32();
 +
 +    // int maxlight;
 +    str->maxlight = saveg_read32();
 +
 +    // int darktime;
 +    str->darktime = saveg_read32();
 +
 +    // int brighttime;
 +    str->brighttime = saveg_read32();
 +}
 +
 +static void saveg_write_strobe_t(strobe_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // int count;
 +    saveg_write32(str->count);
 +
 +    // int minlight;
 +    saveg_write32(str->minlight);
 +
 +    // int maxlight;
 +    saveg_write32(str->maxlight);
 +
 +    // int darktime;
 +    saveg_write32(str->darktime);
 +
 +    // int brighttime;
 +    saveg_write32(str->brighttime);
 +}
 +
 +//
 +// glow_t
 +//
 +
 +static void saveg_read_glow_t(glow_t *str)
 +{
 +    int sector;
 +
 +    // thinker_t thinker;
 +    saveg_read_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    sector = saveg_read32();
 +    str->sector = &sectors[sector];
 +
 +    // int minlight;
 +    str->minlight = saveg_read32();
 +
 +    // int maxlight;
 +    str->maxlight = saveg_read32();
 +
 +    // int direction;
 +    str->direction = saveg_read32();
 +}
 +
 +static void saveg_write_glow_t(glow_t *str)
 +{
 +    // thinker_t thinker;
 +    saveg_write_thinker_t(&str->thinker);
 +
 +    // sector_t* sector;
 +    saveg_write32(str->sector - sectors);
 +
 +    // int minlight;
 +    saveg_write32(str->minlight);
 +
 +    // int maxlight;
 +    saveg_write32(str->maxlight);
 +
 +    // int direction;
 +    saveg_write32(str->direction);
 +}
 +
 +//
 +// Write the header for a savegame
 +//
 +
 +void P_WriteSaveGameHeader(char *description)
 +{
 +    char name[VERSIONSIZE];
 +    int i;
 +
 +    for (i=0; description[i] != '\0'; ++i)
 +        saveg_write8(description[i]);
 +    for (; i<SAVESTRINGSIZE; ++i)
 +        saveg_write8(0);
 +
 +    memset (name,0,sizeof(name));
 +    sprintf (name,"version %i",DOOM_VERSION);
 +
 +    for (i=0; i<VERSIONSIZE; ++i)
 +        saveg_write8(name[i]);
 +
 +    saveg_write8(gameskill);
 +    saveg_write8(gameepisode);
 +    saveg_write8(gamemap);
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +        saveg_write8(playeringame[i]);
 +
 +    saveg_write8((leveltime >> 16) & 0xff);
 +    saveg_write8((leveltime >> 8) & 0xff);
 +    saveg_write8(leveltime & 0xff);
 +}
 +
 +//
 +// Read the header for a savegame
 +//
 +
 +boolean P_ReadSaveGameHeader(void)
 +{
 +    int	 i;
 +    byte a, b, c;
 +    char vcheck[VERSIONSIZE];
 +    char read_vcheck[VERSIONSIZE];
 +
 +    // skip the description field
 +
 +    for (i=0; i<SAVESTRINGSIZE; ++i)
 +        saveg_read8();
 +
 +    for (i=0; i<VERSIONSIZE; ++i)
 +        read_vcheck[i] = saveg_read8();
 +
 +    memset (vcheck,0,sizeof(vcheck));
 +    sprintf (vcheck,"version %i",DOOM_VERSION);
 +    if (strcmp(read_vcheck, vcheck) != 0)
 +	return false;				// bad version
 +
 +    gameskill = saveg_read8();
 +    gameepisode = saveg_read8();
 +    gamemap = saveg_read8();
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +	playeringame[i] = saveg_read8();
 +
 +    // get the times
 +    a = saveg_read8();
 +    b = saveg_read8();
 +    c = saveg_read8();
 +    leveltime = (a<<16) + (b<<8) + c;
 +
 +    return true;
 +}
 +
 +//
 +// Read the end of file marker.  Returns true if read successfully.
 +//
 +
 +boolean P_ReadSaveGameEOF(void)
 +{
 +    int value;
 +
 +    value = saveg_read8();
 +
 +    return value == SAVEGAME_EOF;
 +}
 +
 +//
 +// Write the end of file marker
 +//
 +
 +void P_WriteSaveGameEOF(void)
 +{
 +    saveg_write8(SAVEGAME_EOF);
 +}
 +
 +//
 +// P_ArchivePlayers
 +//
 +void P_ArchivePlayers (void)
 +{
 +    int		i;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (!playeringame[i])
 +	    continue;
 +
 +	saveg_write_pad();
 +
 +        saveg_write_player_t(&players[i]);
 +    }
 +}
 +
 +
 +
 +//
 +// P_UnArchivePlayers
 +//
 +void P_UnArchivePlayers (void)
 +{
 +    int		i;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (!playeringame[i])
 +	    continue;
 +
 +	saveg_read_pad();
 +
 +        saveg_read_player_t(&players[i]);
 +
 +	// will be set when unarc thinker
 +	players[i].mo = NULL;
 +	players[i].message = NULL;
 +	players[i].attacker = NULL;
 +    }
 +}
 +
 +
 +//
 +// P_ArchiveWorld
 +//
 +void P_ArchiveWorld (void)
 +{
 +    int			i;
 +    int			j;
 +    sector_t*		sec;
 +    line_t*		li;
 +    side_t*		si;
 +
 +    // do sectors
 +    for (i=0, sec = sectors ; i<numsectors ; i++,sec++)
 +    {
 +	saveg_write16(sec->floorheight >> FRACBITS);
 +	saveg_write16(sec->ceilingheight >> FRACBITS);
 +	saveg_write16(sec->floorpic);
 +	saveg_write16(sec->ceilingpic);
 +	saveg_write16(sec->lightlevel);
 +	saveg_write16(sec->special);		// needed?
 +	saveg_write16(sec->tag);		// needed?
 +    }
 +
 +
 +    // do lines
 +    for (i=0, li = lines ; i<numlines ; i++,li++)
 +    {
 +	saveg_write16(li->flags);
 +	saveg_write16(li->special);
 +	saveg_write16(li->tag);
 +	for (j=0 ; j<2 ; j++)
 +	{
 +	    if (li->sidenum[j] == -1)
 +		continue;
 +
 +	    si = &sides[li->sidenum[j]];
 +
 +	    saveg_write16(si->textureoffset >> FRACBITS);
 +	    saveg_write16(si->rowoffset >> FRACBITS);
 +	    saveg_write16(si->toptexture);
 +	    saveg_write16(si->bottomtexture);
 +	    saveg_write16(si->midtexture);
 +	}
 +    }
 +}
 +
 +
 +
 +//
 +// P_UnArchiveWorld
 +//
 +void P_UnArchiveWorld (void)
 +{
 +    int			i;
 +    int			j;
 +    sector_t*		sec;
 +    line_t*		li;
 +    side_t*		si;
 +
 +    // do sectors
 +    for (i=0, sec = sectors ; i<numsectors ; i++,sec++)
 +    {
 +	sec->floorheight = saveg_read16() << FRACBITS;
 +	sec->ceilingheight = saveg_read16() << FRACBITS;
 +	sec->floorpic = saveg_read16();
 +	sec->ceilingpic = saveg_read16();
 +	sec->lightlevel = saveg_read16();
 +	sec->special = saveg_read16();		// needed?
 +	sec->tag = saveg_read16();		// needed?
 +	sec->specialdata = 0;
 +	sec->soundtarget = 0;
 +    }
 +
 +    // do lines
 +    for (i=0, li = lines ; i<numlines ; i++,li++)
 +    {
 +	li->flags = saveg_read16();
 +	li->special = saveg_read16();
 +	li->tag = saveg_read16();
 +	for (j=0 ; j<2 ; j++)
 +	{
 +	    if (li->sidenum[j] == -1)
 +		continue;
 +	    si = &sides[li->sidenum[j]];
 +	    si->textureoffset = saveg_read16() << FRACBITS;
 +	    si->rowoffset = saveg_read16() << FRACBITS;
 +	    si->toptexture = saveg_read16();
 +	    si->bottomtexture = saveg_read16();
 +	    si->midtexture = saveg_read16();
 +	}
 +    }
 +}
 +
 +
 +
 +
 +
 +//
 +// Thinkers
 +//
 +typedef enum
 +{
 +    tc_end,
 +    tc_mobj
 +
 +} thinkerclass_t;
 +
 +
 +//
 +// P_ArchiveThinkers
 +//
 +void P_ArchiveThinkers (void)
 +{
 +    thinker_t*		th;
 +
 +    // save off the current thinkers
 +    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
 +    {
 +	if (th->function.acp1 == (actionf_p1)P_MobjThinker)
 +	{
 +            saveg_write8(tc_mobj);
 +	    saveg_write_pad();
 +            saveg_write_mobj_t((mobj_t *) th);
 +
 +	    continue;
 +	}
 +
 +	// I_Error ("P_ArchiveThinkers: Unknown thinker function");
 +    }
 +
 +    // add a terminating marker
 +    saveg_write8(tc_end);
 +}
 +
 +
 +
 +//
 +// P_UnArchiveThinkers
 +//
 +void P_UnArchiveThinkers (void)
 +{
 +    byte		tclass;
 +    thinker_t*		currentthinker;
 +    thinker_t*		next;
 +    mobj_t*		mobj;
 +
 +    // remove all the current thinkers
 +    currentthinker = thinkercap.next;
 +    while (currentthinker != &thinkercap)
 +    {
 +	next = currentthinker->next;
 +
 +	if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
 +	    P_RemoveMobj ((mobj_t *)currentthinker);
 +	else
 +	    Z_Free (currentthinker);
 +
 +	currentthinker = next;
 +    }
 +    P_InitThinkers ();
 +
 +    // read in saved thinkers
 +    while (1)
 +    {
 +	tclass = saveg_read8();
 +	switch (tclass)
 +	{
 +	  case tc_end:
 +	    return; 	// end of list
 +
 +	  case tc_mobj:
 +	    saveg_read_pad();
 +	    mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);
 +            saveg_read_mobj_t(mobj);
 +
 +	    mobj->target = NULL;
 +            mobj->tracer = NULL;
 +	    P_SetThingPosition (mobj);
 +	    mobj->info = &mobjinfo[mobj->type];
 +	    mobj->floorz = mobj->subsector->sector->floorheight;
 +	    mobj->ceilingz = mobj->subsector->sector->ceilingheight;
 +	    mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
 +	    P_AddThinker (&mobj->thinker);
 +	    break;
 +
 +	  default:
 +	    I_Error ("Unknown tclass %i in savegame",tclass);
 +	}
 +
 +    }
 +
 +}
 +
 +
 +//
 +// P_ArchiveSpecials
 +//
 +enum
 +{
 +    tc_ceiling,
 +    tc_door,
 +    tc_floor,
 +    tc_plat,
 +    tc_flash,
 +    tc_strobe,
 +    tc_glow,
 +    tc_endspecials
 +
 +} specials_e;
 +
 +
 +
 +//
 +// Things to handle:
 +//
 +// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list
 +// T_VerticalDoor, (vldoor_t: sector_t * swizzle),
 +// T_MoveFloor, (floormove_t: sector_t * swizzle),
 +// T_LightFlash, (lightflash_t: sector_t * swizzle),
 +// T_StrobeFlash, (strobe_t: sector_t *),
 +// T_Glow, (glow_t: sector_t *),
 +// T_PlatRaise, (plat_t: sector_t *), - active list
 +//
 +void P_ArchiveSpecials (void)
 +{
 +    thinker_t*		th;
 +    int			i;
 +
 +    // save off the current thinkers
 +    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
 +    {
 +	if (th->function.acv == (actionf_v)NULL)
 +	{
 +	    for (i = 0; i < MAXCEILINGS;i++)
 +		if (activeceilings[i] == (ceiling_t *)th)
 +		    break;
 +
 +	    if (i<MAXCEILINGS)
 +	    {
 +                saveg_write8(tc_ceiling);
 +		saveg_write_pad();
 +                saveg_write_ceiling_t((ceiling_t *) th);
 +	    }
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_MoveCeiling)
 +	{
 +            saveg_write8(tc_ceiling);
 +	    saveg_write_pad();
 +            saveg_write_ceiling_t((ceiling_t *) th);
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_VerticalDoor)
 +	{
 +            saveg_write8(tc_door);
 +	    saveg_write_pad();
 +            saveg_write_vldoor_t((vldoor_t *) th);
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_MoveFloor)
 +	{
 +            saveg_write8(tc_floor);
 +	    saveg_write_pad();
 +            saveg_write_floormove_t((floormove_t *) th);
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_PlatRaise)
 +	{
 +            saveg_write8(tc_plat);
 +	    saveg_write_pad();
 +            saveg_write_plat_t((plat_t *) th);
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_LightFlash)
 +	{
 +            saveg_write8(tc_flash);
 +	    saveg_write_pad();
 +            saveg_write_lightflash_t((lightflash_t *) th);
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_StrobeFlash)
 +	{
 +            saveg_write8(tc_strobe);
 +	    saveg_write_pad();
 +            saveg_write_strobe_t((strobe_t *) th);
 +	    continue;
 +	}
 +
 +	if (th->function.acp1 == (actionf_p1)T_Glow)
 +	{
 +            saveg_write8(tc_glow);
 +	    saveg_write_pad();
 +            saveg_write_glow_t((glow_t *) th);
 +	    continue;
 +	}
 +    }
 +
 +    // add a terminating marker
 +    saveg_write8(tc_endspecials);
 +
 +}
 +
 +
 +//
 +// P_UnArchiveSpecials
 +//
 +void P_UnArchiveSpecials (void)
 +{
 +    byte		tclass;
 +    ceiling_t*		ceiling;
 +    vldoor_t*		door;
 +    floormove_t*	floor;
 +    plat_t*		plat;
 +    lightflash_t*	flash;
 +    strobe_t*		strobe;
 +    glow_t*		glow;
 +
 +
 +    // read in saved thinkers
 +    while (1)
 +    {
 +	tclass = saveg_read8();
 +
 +	switch (tclass)
 +	{
 +	  case tc_endspecials:
 +	    return;	// end of list
 +
 +	  case tc_ceiling:
 +	    saveg_read_pad();
 +	    ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL);
 +            saveg_read_ceiling_t(ceiling);
 +	    ceiling->sector->specialdata = ceiling;
 +
 +	    if (ceiling->thinker.function.acp1)
 +		ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling;
 +
 +	    P_AddThinker (&ceiling->thinker);
 +	    P_AddActiveCeiling(ceiling);
 +	    break;
 +
 +	  case tc_door:
 +	    saveg_read_pad();
 +	    door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL);
 +            saveg_read_vldoor_t(door);
 +	    door->sector->specialdata = door;
 +	    door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor;
 +	    P_AddThinker (&door->thinker);
 +	    break;
 +
 +	  case tc_floor:
 +	    saveg_read_pad();
 +	    floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL);
 +            saveg_read_floormove_t(floor);
 +	    floor->sector->specialdata = floor;
 +	    floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor;
 +	    P_AddThinker (&floor->thinker);
 +	    break;
 +
 +	  case tc_plat:
 +	    saveg_read_pad();
 +	    plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL);
 +            saveg_read_plat_t(plat);
 +	    plat->sector->specialdata = plat;
 +
 +	    if (plat->thinker.function.acp1)
 +		plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise;
 +
 +	    P_AddThinker (&plat->thinker);
 +	    P_AddActivePlat(plat);
 +	    break;
 +
 +	  case tc_flash:
 +	    saveg_read_pad();
 +	    flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL);
 +            saveg_read_lightflash_t(flash);
 +	    flash->thinker.function.acp1 = (actionf_p1)T_LightFlash;
 +	    P_AddThinker (&flash->thinker);
 +	    break;
 +
 +	  case tc_strobe:
 +	    saveg_read_pad();
 +	    strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL);
 +            saveg_read_strobe_t(strobe);
 +	    strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash;
 +	    P_AddThinker (&strobe->thinker);
 +	    break;
 +
 +	  case tc_glow:
 +	    saveg_read_pad();
 +	    glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL);
 +            saveg_read_glow_t(glow);
 +	    glow->thinker.function.acp1 = (actionf_p1)T_Glow;
 +	    P_AddThinker (&glow->thinker);
 +	    break;
 +
 +	  default:
 +	    I_Error ("P_UnarchiveSpecials:Unknown tclass %i "
 +		     "in savegame",tclass);
 +	}
 +
 +    }
 +
 +}
 +
diff --cc src/strife/p_saveg.h
index 3a96cc3e,00000000..5488289c
mode 100644,000000..100644
--- a/src/strife/p_saveg.h
+++ b/src/strife/p_saveg.h
@@@ -1,69 -1,0 +1,70 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Savegame I/O, archiving, persistence.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +#ifndef __P_SAVEG__
 +#define __P_SAVEG__
 +
 +#include <stdio.h>
 +
 +// maximum size of a savegame description
 +
 +#define SAVESTRINGSIZE 24
 +
 +// temporary filename to use while saving.
 +
 +char *P_TempSaveGameFile(void);
 +
 +// filename to use for a savegame slot
 +
 +char *P_SaveGameFile(int slot);
 +
 +// Savegame file header read/write functions
 +
 +boolean P_ReadSaveGameHeader(void);
 +void P_WriteSaveGameHeader(char *description);
 +
 +// Savegame end-of-file read/write functions
 +
 +boolean P_ReadSaveGameEOF(void);
 +void P_WriteSaveGameEOF(void);
 +
 +// Persistent storage/archiving.
 +// These are the load / save game routines.
 +void P_ArchivePlayers (void);
 +void P_UnArchivePlayers (void);
 +void P_ArchiveWorld (void);
 +void P_UnArchiveWorld (void);
 +void P_ArchiveThinkers (void);
 +void P_UnArchiveThinkers (void);
 +void P_ArchiveSpecials (void);
 +void P_UnArchiveSpecials (void);
 +
 +extern FILE *save_stream;
++extern boolean savegame_error;
 +
 +
 +#endif
diff --cc src/strife/p_setup.c
index 652d86df,00000000..2145c5d5
mode 100644,000000..100644
--- a/src/strife/p_setup.c
+++ b/src/strife/p_setup.c
@@@ -1,793 -1,0 +1,875 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Do all the WAD I/O, get map description,
 +//	set up initial state and misc. LUTs.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +
 +#include <math.h>
 +
 +#include "z_zone.h"
 +
 +#include "deh_main.h"
 +#include "i_swap.h"
++#include "m_argv.h"
 +#include "m_bbox.h"
 +
 +#include "g_game.h"
 +
 +#include "i_system.h"
 +#include "w_wad.h"
 +
 +#include "doomdef.h"
 +#include "p_local.h"
 +
 +#include "s_sound.h"
 +
 +#include "doomstat.h"
 +
 +
 +void	P_SpawnMapThing (mapthing_t*	mthing);
 +
 +
 +//
 +// MAP related Lookup tables.
 +// Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
 +//
 +int		numvertexes;
 +vertex_t*	vertexes;
 +
 +int		numsegs;
 +seg_t*		segs;
 +
 +int		numsectors;
 +sector_t*	sectors;
 +
 +int		numsubsectors;
 +subsector_t*	subsectors;
 +
 +int		numnodes;
 +node_t*		nodes;
 +
 +int		numlines;
 +line_t*		lines;
 +
 +int		numsides;
 +side_t*		sides;
 +
++static int      totallines;
 +
 +// BLOCKMAP
 +// Created from axis aligned bounding box
 +// of the map, a rectangular array of
 +// blocks of size ...
 +// Used to speed up collision detection
 +// by spatial subdivision in 2D.
 +//
 +// Blockmap size.
 +int		bmapwidth;
 +int		bmapheight;	// size in mapblocks
 +short*		blockmap;	// int for larger maps
 +// offsets in blockmap are from here
 +short*		blockmaplump;
 +// origin of block map
 +fixed_t		bmaporgx;
 +fixed_t		bmaporgy;
 +// for thing chains
 +mobj_t**	blocklinks;
 +
 +
 +// REJECT
 +// For fast sight rejection.
 +// Speeds up enemy AI by skipping detailed
 +//  LineOf Sight calculation.
 +// Without special effect, this could be
 +//  used as a PVS lookup as well.
 +//
 +byte*		rejectmatrix;
 +
 +
 +// Maintain single and multi player starting spots.
 +#define MAX_DEATHMATCH_STARTS	10
 +
 +mapthing_t	deathmatchstarts[MAX_DEATHMATCH_STARTS];
 +mapthing_t*	deathmatch_p;
 +mapthing_t	playerstarts[MAXPLAYERS];
 +
 +// haleyjd 08/24/10: [STRIFE] rift spots for player spawning
 +mapthing_t      riftSpots[MAXRIFTSPOTS];
 +
 +
 +
 +
 +//
 +// P_LoadVertexes
 +//
 +void P_LoadVertexes (int lump)
 +{
 +    byte*		data;
 +    int			i;
 +    mapvertex_t*	ml;
 +    vertex_t*		li;
 +
 +    // Determine number of lumps:
 +    //  total lump length / vertex record length.
 +    numvertexes = W_LumpLength (lump) / sizeof(mapvertex_t);
 +
 +    // Allocate zone memory for buffer.
 +    vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0);
 +
 +    // Load data into cache.
 +    data = W_CacheLumpNum (lump, PU_STATIC);
 +
 +    ml = (mapvertex_t *)data;
 +    li = vertexes;
 +
 +    // Copy and convert vertex coordinates,
 +    // internal representation as fixed.
 +    for (i=0 ; i<numvertexes ; i++, li++, ml++)
 +    {
 +	li->x = SHORT(ml->x)<<FRACBITS;
 +	li->y = SHORT(ml->y)<<FRACBITS;
 +    }
 +
 +    // Free buffer memory.
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +
 +//
 +// P_LoadSegs
 +//
 +void P_LoadSegs (int lump)
 +{
 +    byte*		data;
 +    int			i;
 +    mapseg_t*		ml;
 +    seg_t*		li;
 +    line_t*		ldef;
 +    int			linedef;
 +    int			side;
 +    int                 sidenum;
 +
 +    numsegs = W_LumpLength (lump) / sizeof(mapseg_t);
 +    segs = Z_Malloc (numsegs*sizeof(seg_t),PU_LEVEL,0);
 +    memset (segs, 0, numsegs*sizeof(seg_t));
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +
 +    ml = (mapseg_t *)data;
 +    li = segs;
 +    for (i=0 ; i<numsegs ; i++, li++, ml++)
 +    {
 +	li->v1 = &vertexes[SHORT(ml->v1)];
 +	li->v2 = &vertexes[SHORT(ml->v2)];
 +
 +	li->angle = (SHORT(ml->angle))<<16;
 +	li->offset = (SHORT(ml->offset))<<16;
 +	linedef = SHORT(ml->linedef);
 +	ldef = &lines[linedef];
 +	li->linedef = ldef;
 +	side = SHORT(ml->side);
 +	li->sidedef = &sides[ldef->sidenum[side]];
 +	li->frontsector = sides[ldef->sidenum[side]].sector;
 +
 +        if (ldef-> flags & ML_TWOSIDED)
 +        {
 +            sidenum = ldef->sidenum[side ^ 1];
 +
 +            // If the sidenum is out of range, this may be a "glass hack"
 +            // impassible window.  Point at side #0 (this may not be
 +            // the correct Vanilla behavior; however, it seems to work for
 +            // OTTAWAU.WAD, which is the one place I've seen this trick
 +            // used).
 +
 +            if (sidenum < 0 || sidenum >= numsides)
 +            {
 +                sidenum = 0;
 +            }
 +
 +            li->backsector = sides[sidenum].sector;
 +        }
 +        else
 +        {
 +	    li->backsector = 0;
 +        }
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +//
 +// P_LoadSubsectors
 +//
 +void P_LoadSubsectors (int lump)
 +{
 +    byte*		data;
 +    int			i;
 +    mapsubsector_t*	ms;
 +    subsector_t*	ss;
 +
 +    numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t);
 +    subsectors = Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0);
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +
 +    ms = (mapsubsector_t *)data;
 +    memset (subsectors,0, numsubsectors*sizeof(subsector_t));
 +    ss = subsectors;
 +
 +    for (i=0 ; i<numsubsectors ; i++, ss++, ms++)
 +    {
 +	ss->numlines = SHORT(ms->numsegs);
 +	ss->firstline = SHORT(ms->firstseg);
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +
 +//
 +// P_LoadSectors
 +//
 +void P_LoadSectors (int lump)
 +{
 +    byte*		data;
 +    int			i;
 +    mapsector_t*	ms;
 +    sector_t*		ss;
 +
 +    numsectors = W_LumpLength (lump) / sizeof(mapsector_t);
 +    sectors = Z_Malloc (numsectors*sizeof(sector_t),PU_LEVEL,0);
 +    memset (sectors, 0, numsectors*sizeof(sector_t));
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +
 +    ms = (mapsector_t *)data;
 +    ss = sectors;
 +    for (i=0 ; i<numsectors ; i++, ss++, ms++)
 +    {
 +	ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
 +	ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
 +	ss->floorpic = R_FlatNumForName(ms->floorpic);
 +	ss->ceilingpic = R_FlatNumForName(ms->ceilingpic);
 +	ss->lightlevel = SHORT(ms->lightlevel);
 +	ss->special = SHORT(ms->special);
 +	ss->tag = SHORT(ms->tag);
 +	ss->thinglist = NULL;
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +//
 +// P_LoadNodes
 +//
 +void P_LoadNodes (int lump)
 +{
 +    byte*	data;
 +    int		i;
 +    int		j;
 +    int		k;
 +    mapnode_t*	mn;
 +    node_t*	no;
 +
 +    numnodes = W_LumpLength (lump) / sizeof(mapnode_t);
 +    nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0);
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +
 +    mn = (mapnode_t *)data;
 +    no = nodes;
 +
 +    for (i=0 ; i<numnodes ; i++, no++, mn++)
 +    {
 +	no->x = SHORT(mn->x)<<FRACBITS;
 +	no->y = SHORT(mn->y)<<FRACBITS;
 +	no->dx = SHORT(mn->dx)<<FRACBITS;
 +	no->dy = SHORT(mn->dy)<<FRACBITS;
 +	for (j=0 ; j<2 ; j++)
 +	{
 +	    no->children[j] = SHORT(mn->children[j]);
 +	    for (k=0 ; k<4 ; k++)
 +		no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
 +	}
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +//
 +// P_LoadThings
 +//
 +// haleyjd 08/24/10: [STRIFE]:
 +// * Added code to record rift spots
 +void P_LoadThings (int lump)
 +{
 +    byte               *data;
 +    int			i;
 +    mapthing_t         *mt;
 +    mapthing_t          spawnthing;
 +    int			numthings;
 +    boolean		spawn;
 +
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +    numthings = W_LumpLength (lump) / sizeof(mapthing_t);
 +
 +    mt = (mapthing_t *)data;
 +    for (i=0 ; i<numthings ; i++, mt++)
 +    {
 +        spawn = true;
 +
 +        // Do not spawn cool, new monsters if !commercial
 +        // STRIFE-TODO: replace with isregistered stuff
 +        /*
 +        if (gamemode != commercial)
 +        {
 +            switch (SHORT(mt->type))
 +            {
 +            case 68:	// Arachnotron
 +            case 64:	// Archvile
 +            case 88:	// Boss Brain
 +            case 89:	// Boss Shooter
 +            case 69:	// Hell Knight
 +            case 67:	// Mancubus
 +            case 71:	// Pain Elemental
 +            case 65:	// Former Human Commando
 +            case 66:	// Revenant
 +            case 84:	// Wolf SS
 +                spawn = false;
 +                break;
 +            }
 +        }
 +        if (spawn == false)
 +            break;
 +        */
 +
 +        // Do spawn all other stuff.
 +        spawnthing.x = SHORT(mt->x);
 +        spawnthing.y = SHORT(mt->y);
 +        spawnthing.angle = SHORT(mt->angle);
 +        spawnthing.type = SHORT(mt->type);
 +        spawnthing.options = SHORT(mt->options);
 +
 +        // haleyjd 08/24/2010: Special Strife checks
 +        if(spawnthing.type >= 118 && spawnthing.type < 128)
 +        {
 +            // initialize riftSpots
 +            int riftSpotNum = spawnthing.type - 118;
 +            riftSpots[riftSpotNum] = spawnthing;
 +            riftSpots[riftSpotNum].type = 1;
 +        }
 +        else if(spawnthing.type >= 9001 && spawnthing.type < 9011)
 +        {
 +            // STRIFE-TODO: mystery array of 90xx objects
 +        }
 +        else
 +            P_SpawnMapThing(&spawnthing);
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +//
 +// P_LoadLineDefs
 +// Also counts secret lines for intermissions.
 +//
 +void P_LoadLineDefs (int lump)
 +{
 +    byte*		data;
 +    int			i;
 +    maplinedef_t*	mld;
 +    line_t*		ld;
 +    vertex_t*		v1;
 +    vertex_t*		v2;
 +
 +    numlines = W_LumpLength (lump) / sizeof(maplinedef_t);
 +    lines = Z_Malloc (numlines*sizeof(line_t),PU_LEVEL,0);
 +    memset (lines, 0, numlines*sizeof(line_t));
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +
 +    mld = (maplinedef_t *)data;
 +    ld = lines;
 +    for (i=0 ; i<numlines ; i++, mld++, ld++)
 +    {
 +	ld->flags = SHORT(mld->flags);
 +	ld->special = SHORT(mld->special);
 +	ld->tag = SHORT(mld->tag);
 +	v1 = ld->v1 = &vertexes[SHORT(mld->v1)];
 +	v2 = ld->v2 = &vertexes[SHORT(mld->v2)];
 +	ld->dx = v2->x - v1->x;
 +	ld->dy = v2->y - v1->y;
 +
 +	if (!ld->dx)
 +	    ld->slopetype = ST_VERTICAL;
 +	else if (!ld->dy)
 +	    ld->slopetype = ST_HORIZONTAL;
 +	else
 +	{
 +	    if (FixedDiv (ld->dy , ld->dx) > 0)
 +		ld->slopetype = ST_POSITIVE;
 +	    else
 +		ld->slopetype = ST_NEGATIVE;
 +	}
 +
 +	if (v1->x < v2->x)
 +	{
 +	    ld->bbox[BOXLEFT] = v1->x;
 +	    ld->bbox[BOXRIGHT] = v2->x;
 +	}
 +	else
 +	{
 +	    ld->bbox[BOXLEFT] = v2->x;
 +	    ld->bbox[BOXRIGHT] = v1->x;
 +	}
 +
 +	if (v1->y < v2->y)
 +	{
 +	    ld->bbox[BOXBOTTOM] = v1->y;
 +	    ld->bbox[BOXTOP] = v2->y;
 +	}
 +	else
 +	{
 +	    ld->bbox[BOXBOTTOM] = v2->y;
 +	    ld->bbox[BOXTOP] = v1->y;
 +	}
 +
 +	ld->sidenum[0] = SHORT(mld->sidenum[0]);
 +	ld->sidenum[1] = SHORT(mld->sidenum[1]);
 +
 +	if (ld->sidenum[0] != -1)
 +	    ld->frontsector = sides[ld->sidenum[0]].sector;
 +	else
 +	    ld->frontsector = 0;
 +
 +	if (ld->sidenum[1] != -1)
 +	    ld->backsector = sides[ld->sidenum[1]].sector;
 +	else
 +	    ld->backsector = 0;
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +//
 +// P_LoadSideDefs
 +//
 +void P_LoadSideDefs (int lump)
 +{
 +    byte*		data;
 +    int			i;
 +    mapsidedef_t*	msd;
 +    side_t*		sd;
 +
 +    numsides = W_LumpLength (lump) / sizeof(mapsidedef_t);
 +    sides = Z_Malloc (numsides*sizeof(side_t),PU_LEVEL,0);
 +    memset (sides, 0, numsides*sizeof(side_t));
 +    data = W_CacheLumpNum (lump,PU_STATIC);
 +
 +    msd = (mapsidedef_t *)data;
 +    sd = sides;
 +    for (i=0 ; i<numsides ; i++, msd++, sd++)
 +    {
 +	sd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS;
 +	sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
 +	sd->toptexture = R_TextureNumForName(msd->toptexture);
 +	sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
 +	sd->midtexture = R_TextureNumForName(msd->midtexture);
 +	sd->sector = &sectors[SHORT(msd->sector)];
 +    }
 +
 +    W_ReleaseLumpNum(lump);
 +}
 +
 +
 +//
 +// P_LoadBlockMap
 +//
 +void P_LoadBlockMap (int lump)
 +{
 +    int i;
 +    int count;
 +    int lumplen;
 +
 +    lumplen = W_LumpLength(lump);
 +    count = lumplen / 2;
 +
 +    blockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL);
 +    W_ReadLump(lump, blockmaplump);
 +    blockmap = blockmaplump + 4;
 +
 +    // Swap all short integers to native byte ordering.
 +
 +    for (i=0; i<count; i++)
 +    {
 +	blockmaplump[i] = SHORT(blockmaplump[i]);
 +    }
 +
 +    // Read the header
 +
 +    bmaporgx = blockmaplump[0]<<FRACBITS;
 +    bmaporgy = blockmaplump[1]<<FRACBITS;
 +    bmapwidth = blockmaplump[2];
 +    bmapheight = blockmaplump[3];
 +
 +    // Clear out mobj chains
 +
 +    count = sizeof(*blocklinks) * bmapwidth * bmapheight;
 +    blocklinks = Z_Malloc(count, PU_LEVEL, 0);
 +    memset(blocklinks, 0, count);
 +}
 +
 +
 +
 +//
 +// P_GroupLines
 +// Builds sector line lists and subsector sector numbers.
 +// Finds block bounding boxes for sectors.
 +//
 +void P_GroupLines (void)
 +{
 +    line_t**		linebuffer;
 +    int			i;
 +    int			j;
-     int			total;
 +    line_t*		li;
 +    sector_t*		sector;
 +    subsector_t*	ss;
 +    seg_t*		seg;
 +    fixed_t		bbox[4];
 +    int			block;
 +
 +    // look up sector number for each subsector
 +    ss = subsectors;
 +    for (i=0 ; i<numsubsectors ; i++, ss++)
 +    {
 +	seg = &segs[ss->firstline];
 +	ss->sector = seg->sidedef->sector;
 +    }
 +
 +    // count number of lines in each sector
 +    li = lines;
-     total = 0;
++    totallines = 0;
 +    for (i=0 ; i<numlines ; i++, li++)
 +    {
- 	total++;
++	totallines++;
 +	li->frontsector->linecount++;
 +
 +	if (li->backsector && li->backsector != li->frontsector)
 +	{
 +	    li->backsector->linecount++;
- 	    total++;
++	    totallines++;
 +	}
 +    }
 +
 +    // build line tables for each sector
-     linebuffer = Z_Malloc (total*sizeof(line_t *), PU_LEVEL, 0);
++    linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0);
 +
 +    for (i=0; i<numsectors; ++i)
 +    {
 +        // Assign the line buffer for this sector
 +
 +        sectors[i].lines = linebuffer;
 +        linebuffer += sectors[i].linecount;
 +
 +        // Reset linecount to zero so in the next stage we can count
 +        // lines into the list.
 +
 +        sectors[i].linecount = 0;
 +    }
 +
 +    // Assign lines to sectors
 +
 +    for (i=0; i<numlines; ++i)
 +    {
 +        li = &lines[i];
 +
 +        if (li->frontsector != NULL)
 +        {
 +            sector = li->frontsector;
 +
 +            sector->lines[sector->linecount] = li;
 +            ++sector->linecount;
 +        }
 +
 +        if (li->backsector != NULL && li->frontsector != li->backsector)
 +        {
 +            sector = li->backsector;
 +
 +            sector->lines[sector->linecount] = li;
 +            ++sector->linecount;
 +        }
 +    }
 +
 +    // Generate bounding boxes for sectors
 +
 +    sector = sectors;
 +    for (i=0 ; i<numsectors ; i++, sector++)
 +    {
 +	M_ClearBox (bbox);
 +
 +	for (j=0 ; j<sector->linecount; j++)
 +	{
 +            li = sector->lines[j];
 +
 +            M_AddToBox (bbox, li->v1->x, li->v1->y);
 +            M_AddToBox (bbox, li->v2->x, li->v2->y);
 +	}
 +
 +	// set the degenmobj_t to the middle of the bounding box
 +	sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2;
 +	sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2;
 +
 +	// adjust bounding box to map blocks
 +	block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT;
 +	block = block >= bmapheight ? bmapheight-1 : block;
 +	sector->blockbox[BOXTOP]=block;
 +
 +	block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT;
 +	block = block < 0 ? 0 : block;
 +	sector->blockbox[BOXBOTTOM]=block;
 +
 +	block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT;
 +	block = block >= bmapwidth ? bmapwidth-1 : block;
 +	sector->blockbox[BOXRIGHT]=block;
 +
 +	block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT;
 +	block = block < 0 ? 0 : block;
 +	sector->blockbox[BOXLEFT]=block;
 +    }
 +
 +}
 +
++// Pad the REJECT lump with extra data when the lump is too small,
++// to simulate a REJECT buffer overflow in Vanilla Doom.
++
++static void PadRejectArray(byte *array, unsigned int len)
++{
++    unsigned int i;
++    unsigned int byte_num;
++    byte *dest;
++    unsigned int padvalue;
++
++    // Values to pad the REJECT array with:
++
++    unsigned int rejectpad[4] =
++    {
++        ((totallines * 4 + 3) & ~3) + 24,     // Size
++        0,                                    // Part of z_zone block header
++        50,                                   // PU_LEVEL
++        0x1d4a11                              // DOOM_CONST_ZONEID
++    };
++
++    // Copy values from rejectpad into the destination array.
++
++    dest = array;
++
++    for (i=0; i<len && i<sizeof(rejectpad); ++i)
++    {
++        byte_num = i % 4;
++        *dest = (rejectpad[i / 4] >> (byte_num * 8)) & 0xff;
++        ++dest;
++    }
++
++    // We only have a limited pad size.  Print a warning if the
++    // REJECT lump is too small.
++
++    if (len > sizeof(rejectpad))
++    {
++        fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%i > %i)\n",
++                        len, sizeof(rejectpad));
++
++        // Pad remaining space with 0 (or 0xff, if specified on command line).
++
++        if (M_CheckParm("-reject_pad_with_ff"))
++        {
++            padvalue = 0xff;
++        }
++        else
++        {
++            padvalue = 0xf00;
++        }
++
++        memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad));
++    }
++}
++
++static void P_LoadReject(int lumpnum)
++{
++    int minlength;
++    int lumplen;
++
++    // Calculate the size that the REJECT lump *should* be.
++
++    minlength = (numsectors * numsectors + 7) / 8;
++
++    // If the lump meets the minimum length, it can be loaded directly.
++    // Otherwise, we need to allocate a buffer of the correct size
++    // and pad it with appropriate data.
++
++    lumplen = W_LumpLength(lumpnum);
++
++    if (lumplen >= minlength)
++    {
++        rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL);
++    }
++    else
++    {
++        rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix);
++        W_ReadLump(lumpnum, rejectmatrix);
++
++        PadRejectArray(rejectmatrix + lumplen, minlength - lumplen);
++    }
++}
 +
 +//
 +// P_SetupLevel
 +//
 +void
 +P_SetupLevel
 +( int		episode,
 +  int		map,
 +  int		playermask,
 +  skill_t	skill)
 +{
 +    int		i;
 +    char	lumpname[9];
 +    int		lumpnum;
 +
 +    totalkills = totalitems = totalsecret = wminfo.maxfrags = 0;
 +    wminfo.partime = 180;
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +        // haleyjd 08/30/10: [STRIFE] Removed secretcount, itemcount
 +	players[i].killcount = 0;
 +    }
 +
 +    // Initial height of PointOfView
 +    // will be set by player think.
 +    players[consoleplayer].viewz = 1;
 +
 +    // 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);
 +
 +
 +    // UNUSED W_Profile ();
 +    P_InitThinkers ();
 +
 +    // find map name
 +    if ( gamemode == commercial)
 +    {
 +	if (map<10)
- 	    sprintf (lumpname, DEH_String("map0%i"), map);
++	    DEH_snprintf(lumpname, 9, "map0%i", map);
 +	else
- 	    sprintf (lumpname, DEH_String("map%i"), map);
++	    DEH_snprintf(lumpname, 9, "map%i", map);
 +    }
 +    else
 +    {
 +	lumpname[0] = 'E';
 +	lumpname[1] = '0' + episode;
 +	lumpname[2] = 'M';
 +	lumpname[3] = '0' + map;
 +	lumpname[4] = 0;
 +    }
 +
 +    lumpnum = W_GetNumForName (lumpname);
 +
 +    leveltime = 0;
 +
 +    // note: most of this ordering is important
 +    P_LoadBlockMap (lumpnum+ML_BLOCKMAP);
 +    P_LoadVertexes (lumpnum+ML_VERTEXES);
 +    P_LoadSectors (lumpnum+ML_SECTORS);
 +    P_LoadSideDefs (lumpnum+ML_SIDEDEFS);
 +
 +    P_LoadLineDefs (lumpnum+ML_LINEDEFS);
 +    P_LoadSubsectors (lumpnum+ML_SSECTORS);
 +    P_LoadNodes (lumpnum+ML_NODES);
 +    P_LoadSegs (lumpnum+ML_SEGS);
-
-     rejectmatrix = W_CacheLumpNum (lumpnum+ML_REJECT,PU_LEVEL);
++
 +    P_GroupLines ();
++    P_LoadReject (lumpnum+ML_REJECT);
 +
 +    bodyqueslot = 0;
 +    deathmatch_p = deathmatchstarts;
 +    P_LoadThings (lumpnum+ML_THINGS);
 +
 +    // if deathmatch, randomly spawn the active players
 +    if (deathmatch)
 +    {
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	    if (playeringame[i])
 +	    {
 +		players[i].mo = NULL;
 +		G_DeathMatchSpawnPlayer (i);
 +	    }
 +
 +    }
 +
 +    // clear special respawning que
 +    iquehead = iquetail = 0;
 +
 +    // set up world state
 +    P_SpawnSpecials ();
 +
 +    // build subsector connect matrix
 +    //	UNUSED P_ConnectSubsectors ();
 +
 +    // preload graphics
 +    if (precache)
 +	R_PrecacheLevel ();
 +
 +    //printf ("free memory: 0x%x\n", Z_FreeMemory());
 +
 +}
 +
 +
 +
 +//
 +// P_Init
 +//
 +void P_Init (void)
 +{
 +    P_InitSwitchList();
 +    P_InitPicAnims();
 +    P_InitTerrainTypes();   // villsa [STRIFE]
 +    R_InitSprites(sprnames);
 +}
 +
 +
 +
diff --cc src/strife/r_draw.c
index a09032c7,00000000..79c571fe
mode 100644,000000..100644
--- a/src/strife/r_draw.c
+++ b/src/strife/r_draw.c
@@@ -1,976 -1,0 +1,987 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	The actual span/column drawing functions.
 +//	Here find the main potential for optimization,
 +//	 e.g. inline assembly, different algorithms.
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +
 +
 +#include "doomdef.h"
 +#include "deh_main.h"
 +
 +#include "i_system.h"
 +#include "z_zone.h"
 +#include "w_wad.h"
 +
 +#include "r_local.h"
 +
 +// Needs access to LFB (guess what).
 +#include "v_video.h"
 +
 +// State.
 +#include "doomstat.h"
 +
 +
 +// ?
 +#define MAXWIDTH			1120
 +#define MAXHEIGHT			832
 +
 +// status bar height at bottom of screen
 +// haleyjd 08/31/10: Verified unmodified.
 +#define SBARHEIGHT              32
 +
 +//
 +// All drawing to the view buffer is accomplished in this file.
 +// The other refresh files only know about ccordinates,
 +//  not the architecture of the frame buffer.
 +// Conveniently, the frame buffer is a linear one,
 +//  and we need only the base address,
 +//  and the total size == width*height*depth/8.,
 +//
 +
 +
 +byte*		viewimage;
 +int		viewwidth;
 +int		scaledviewwidth;
 +int		viewheight;
 +int		viewwindowx;
 +int		viewwindowy;
 +byte*		ylookup[MAXHEIGHT];
 +int		columnofs[MAXWIDTH];
 +
 +// Color tables for different players,
 +//  translate a limited part to another
 +//  (color ramps used for  suit colors).
 +//
 +// [STRIFE] Unused.
 +//byte          translations[3][256];
 +
 +// Backing buffer containing the bezel drawn around the screen and
 +// surrounding background.
 +
 +static byte *background_buffer = NULL;
 +
 +// haleyjd 08/29/10: [STRIFE] Rogue added the ability to customize the view
 +// border flat by storing it in the configuration file.
 +char *back_flat = "F_PAVE01";
 +
 +//
 +// R_DrawColumn
 +// Source is the top of the column to scale.
 +//
 +lighttable_t*		dc_colormap;
 +int			dc_x;
 +int			dc_yl;
 +int			dc_yh;
 +fixed_t			dc_iscale;
 +fixed_t			dc_texturemid;
 +
 +// first pixel in a column (possibly virtual)
 +byte*			dc_source;
 +
 +// just for profiling
 +int			dccount;
 +
 +//
 +// A column is a vertical slice/span from a wall texture that,
 +//  given the DOOM style restrictions on the view orientation,
 +//  will always have constant z depth.
 +// Thus a special case loop for very fast rendering can
 +//  be used. It has also been used with Wolfenstein 3D.
 +//
 +void R_DrawColumn (void)
 +{
 +    int			count;
 +    byte*		dest;
 +    fixed_t		frac;
 +    fixed_t		fracstep;
 +
 +    count = dc_yh - dc_yl;
 +
 +    // Zero length, column does not exceed a pixel.
 +    if (count < 0)
 +	return;
 +
 +#ifdef RANGECHECK
 +    if ((unsigned)dc_x >= SCREENWIDTH
 +	|| dc_yl < 0
 +	|| dc_yh >= SCREENHEIGHT)
 +	I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
 +#endif
 +
 +    // Framebuffer destination address.
 +    // Use ylookup LUT to avoid multiply with ScreenWidth.
 +    // Use columnofs LUT for subwindows?
 +    dest = ylookup[dc_yl] + columnofs[dc_x];
 +
 +    // Determine scaling,
 +    //  which is the only mapping to be done.
 +    fracstep = dc_iscale;
 +    frac = dc_texturemid + (dc_yl-centery)*fracstep;
 +
 +    // Inner loop that does the actual texture mapping,
 +    //  e.g. a DDA-lile scaling.
 +    // This is as fast as it gets.
 +    do
 +    {
 +	// Re-map color indices from wall texture column
 +	//  using a lighting/special effects LUT.
 +	*dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]];
 +
 +	dest += SCREENWIDTH;
 +	frac += fracstep;
 +
 +    } while (count--);
 +}
 +
 +
 +
 +// UNUSED.
 +// Loop unrolled.
 +#if 0
 +void R_DrawColumn (void)
 +{
 +    int			count;
 +    byte*		source;
 +    byte*		dest;
 +    byte*		colormap;
 +
 +    unsigned		frac;
 +    unsigned		fracstep;
 +    unsigned		fracstep2;
 +    unsigned		fracstep3;
 +    unsigned		fracstep4;
 +
 +    count = dc_yh - dc_yl + 1;
 +
 +    source = dc_source;
 +    colormap = dc_colormap;
 +    dest = ylookup[dc_yl] + columnofs[dc_x];
 +
 +    fracstep = dc_iscale<<9;
 +    frac = (dc_texturemid + (dc_yl-centery)*dc_iscale)<<9;
 +
 +    fracstep2 = fracstep+fracstep;
 +    fracstep3 = fracstep2+fracstep;
 +    fracstep4 = fracstep3+fracstep;
 +
 +    while (count >= 8)
 +    {
 +	dest[0] = colormap[source[frac>>25]];
 +	dest[SCREENWIDTH] = colormap[source[(frac+fracstep)>>25]];
 +	dest[SCREENWIDTH*2] = colormap[source[(frac+fracstep2)>>25]];
 +	dest[SCREENWIDTH*3] = colormap[source[(frac+fracstep3)>>25]];
 +
 +	frac += fracstep4;
 +
 +	dest[SCREENWIDTH*4] = colormap[source[frac>>25]];
 +	dest[SCREENWIDTH*5] = colormap[source[(frac+fracstep)>>25]];
 +	dest[SCREENWIDTH*6] = colormap[source[(frac+fracstep2)>>25]];
 +	dest[SCREENWIDTH*7] = colormap[source[(frac+fracstep3)>>25]];
 +
 +	frac += fracstep4;
 +	dest += SCREENWIDTH*8;
 +	count -= 8;
 +    }
 +
 +    while (count > 0)
 +    {
 +	*dest = colormap[source[frac>>25]];
 +	dest += SCREENWIDTH;
 +	frac += fracstep;
 +	count--;
 +    }
 +}
 +#endif
 +
 +// haleyjd 09/06/10 [STRIFE] Removed low detail
 +
 +//
 +// Spectre/Invisibility.
 +//
 +
 +// haleyjd 09/06/10: ]STRIFE] replaced fuzzdraw with translucency.
 +
 +//
 +// R_DrawMVisTLColumn
 +//
 +// villsa [STRIFE] new function
 +// Replacement for R_DrawFuzzColumn
 +//
 +void R_DrawMVisTLColumn(void)
 +{
 +    int                 count;
 +    byte*               dest;
 +    fixed_t             frac;
 +    fixed_t             fracstep;
 +
 +    // Adjust borders. Low...
 +    if (!dc_yl)
 +        dc_yl = 1;
 +
 +    // .. and high.
 +    if (dc_yh == viewheight-1)
 +        dc_yh = viewheight - 2;
 +
 +    count = dc_yh - dc_yl;
 +
 +    // Zero length.
 +    if (count < 0)
 +        return;
 +
 +#ifdef RANGECHECK
 +    if ((unsigned)dc_x >= SCREENWIDTH
 +        || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
 +    {
 +        I_Error ("R_DrawFuzzColumn: %i to %i at %i",
 +                 dc_yl, dc_yh, dc_x);
 +    }
 +#endif
 +
 +    dest = ylookup[dc_yl] + columnofs[dc_x];
 +
 +    // Looks familiar.
 +    fracstep = dc_iscale;
 +    frac = dc_texturemid + (dc_yl-centery)*fracstep;
 +
 +    do
 +    {
 +        byte src = dc_colormap[dc_source[(frac>>FRACBITS)&127]];
 +        byte col = xlatab[*dest + (src << 8)];
 +        *dest = col;
 +        dest += SCREENWIDTH;
 +        frac += fracstep;
 +    } while(count--);
 +}
 +
 +//
 +// R_DrawTLColumn
 +//
 +// villsa [STRIFE] new function
 +// Achieves a second translucency level using the same lookup table,
 +// via inversion of the colors in the index computation.
 +//
 +void R_DrawTLColumn(void)
 +{
 +    int                 count;
 +    byte*               dest;
 +    fixed_t             frac;
 +    fixed_t             fracstep;
 +
 +    // Adjust borders. Low...
 +    if (!dc_yl)
 +        dc_yl = 1;
 +
 +    // .. and high.
 +    if (dc_yh == viewheight-1)
 +        dc_yh = viewheight - 2;
 +
 +    count = dc_yh - dc_yl;
 +
 +    // Zero length.
 +    if (count < 0)
 +        return;
 +
 +#ifdef RANGECHECK
 +    if ((unsigned)dc_x >= SCREENWIDTH
 +        || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
 +    {
 +        I_Error ("R_DrawFuzzColumn2: %i to %i at %i",
 +                 dc_yl, dc_yh, dc_x);
 +    }
 +#endif
 +
 +    dest = ylookup[dc_yl] + columnofs[dc_x];
 +
 +    // Looks familiar.
 +    fracstep = dc_iscale;
 +    frac = dc_texturemid + (dc_yl-centery)*fracstep;
 +
 +    do
 +    {
 +        byte src = dc_colormap[dc_source[(frac>>FRACBITS)&127]];
 +        byte col = xlatab[(*dest << 8) + src];
 +        *dest = col;
 +        dest += SCREENWIDTH;
 +        frac += fracstep;
 +    } while(count--);
 +}
 +
 +
 +
 +//
 +// R_DrawTranslatedColumn
 +// Used to draw player sprites
 +//  with the green colorramp mapped to others.
 +// Could be used with different translation
 +//  tables, e.g. the lighter colored version
 +//  of the BaronOfHell, the HellKnight, uses
 +//  identical sprites, kinda brightened up.
 +//
 +byte*	dc_translation;
 +byte*	translationtables;
 +
 +void R_DrawTranslatedColumn (void)
 +{
 +    int                 count;
 +    byte*               dest;
 +    fixed_t             frac;
 +    fixed_t             fracstep;
 +
 +    count = dc_yh - dc_yl;
 +    if (count < 0)
 +        return;
 +
 +#ifdef RANGECHECK
 +    if ((unsigned)dc_x >= SCREENWIDTH
 +        || dc_yl < 0
 +        || dc_yh >= SCREENHEIGHT)
 +    {
 +        I_Error ( "R_DrawColumn: %i to %i at %i",
 +                 dc_yl, dc_yh, dc_x);
 +    }
 +
 +#endif
 +
 +    dest = ylookup[dc_yl] + columnofs[dc_x];
 +
 +    // Looks familiar.
 +    fracstep = dc_iscale;
 +    frac = dc_texturemid + (dc_yl-centery)*fracstep;
 +
 +    // Here we do an additional index re-mapping.
 +    do
 +    {
 +        // Translation tables are used
 +        //  to map certain colorramps to other ones,
 +        //  used with PLAY sprites.
 +        // Thus the "green" ramp of the player 0 sprite
 +        //  is mapped to gray, red, black/indigo.
 +        *dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]];
 +        dest += SCREENWIDTH;
 +        frac += fracstep;
 +    } while (count--);
 +}
 +
 +// haleyjd 09/06/10 [STRIFE] Removed low detail
 +
 +//
 +// R_DrawTRTLColumn
 +//
 +// villsa [STRIFE] new function
 +// Combines translucency and color translation.
 +//
 +void R_DrawTRTLColumn(void)
 +{
 +    int                 count;
 +    byte*               dest;
 +    fixed_t             frac;
 +    fixed_t             fracstep;
 +
 +    count = dc_yh - dc_yl;
 +    if (count < 0)
 +        return;
 +
 +#ifdef RANGECHECK
 +    if ((unsigned)dc_x >= SCREENWIDTH
 +        || dc_yl < 0
 +        || dc_yh >= SCREENHEIGHT)
 +    {
 +        I_Error ( "R_DrawColumn: %i to %i at %i",
 +                 dc_yl, dc_yh, dc_x);
 +    }
 +#endif
 +
 +    dest = ylookup[dc_yl] + columnofs[dc_x];
 +
 +    // Looks familiar.
 +    fracstep = dc_iscale;
 +    frac = dc_texturemid + (dc_yl-centery)*fracstep;
 +
 +    // Here we do an additional index re-mapping.
 +    do
 +    {
 +        byte src = dc_colormap[dc_translation[dc_source[frac>>FRACBITS&127]]];
 +        byte col = xlatab[(*dest << 8) + src];
 +        *dest = col;
 +        dest += SCREENWIDTH;
 +        frac += fracstep;
 +    } while (count--);
 +}
 +
 +
 +// haleyjd 08/26/10: [STRIFE] - Rogue's translucency lookup table
 +// This is functionally equivalent to Raven's TINTTAB and BOOM's TRANMAPs.
 +byte *xlatab;
 +
 +//
 +// R_InitTranslationTables
 +// Creates the translation tables to map
 +//  the green color ramp to gray, brown, red.
 +// Assumes a given structure of the PLAYPAL.
 +// Could be read from a lump instead.
 +//
 +// haleyjd 08/26/10: [STRIFE]
 +// * Added loading of XLATAB
 +//
 +void R_InitTranslationTables (void)
 +{
 +    int i;
 +    byte col1, col2;
 +
 +    // [STRIFE] Load xlatab. Here's how Rogue did it:
 +    //   v7 = W_CacheLumpName("XLATAB", PU_CACHE); // note potential cache bug...
 +    //   HIWORD(v8) = (Z_Malloc(131072, PU_STATIC, NULL) + 65535) >> 16;
 +    //   LOWORD(v8) = 0; // aligning to a 64K boundary, as if this is Wolf3D.
 +    //   xlatab = v8;
 +    //   memcpy(v8, v7, 65536);
 +    //   memcpy(v8+65536, v7+65536, 0); // 0 bytes, does nothing!
 +    // As you can see, they copypasta'd id's unnecessary 64K boundary alignment
 +    // from the colormap code, and then evidently at some point dropped the high
 +    // side of the presumably 128KB lump (because it is redundant data), but
 +    // just set the 2nd memset call's length to 0 bytes... Terrible. Since none
 +    // of this accomplishes anything, and isn't strictly portable, all we need
 +    // to do is this:
 +    xlatab = W_CacheLumpName("XLATAB", PU_STATIC);
 +
 +    // villsa [STRIFE] allocate a larger size for translation tables
 +    translationtables = Z_Malloc (256*8, PU_STATIC, 0);
 +
 +    col1 = 0xFA;
 +    col2 = 0xE0;
 +
 +    // villsa [STRIFE] setup all translation tables
 +    for(i = 0; i < 256; i++)
 +    {
 +        if(i >= 0x80 && i <= 0x8f)
 +        {
 +            translationtables [i      ] = (i & 0x0f) + 64;
 +            translationtables [i+  256] = (i & 0x0f) - 80;
 +            translationtables [i+2*256] = (i & 0x0f) + 16;
 +            translationtables [i+3*256] = (i & 0x0f) + 48;
 +            translationtables [i+4*256] = (i & 0x0f) + 80;
 +            translationtables [i+5*256] = (i & 0x0f) + 96;
 +            translationtables [i+6*256] = (i & 0x0f) - 112;
 +        }
 +
 +        else if(i >= 0x50 && i<= 0x5f)
 +        {
 +            translationtables [i      ] = i;
 +            translationtables [i+  256] = i;
 +            translationtables [i+2*256] = i;
 +            translationtables [i+3*256] = i;
 +            translationtables [i+4*256] = (i & 0x0f) + -128;
 +            translationtables [i+5*256] = (i & 0x0f) + 16;
 +            translationtables [i+6*256] = (i & 0x0f) + 64;
 +        }
 +        else if(i >= 0xd0 && i<= 0xdf)
 +        {
 +            translationtables [i      ] = i;
 +            translationtables [i+  256] = i;
 +            translationtables [i+2*256] = i;
 +            translationtables [i+3*256] = i;
 +            translationtables [i+4*256] = (i & 0x0f) - 80;
 +            translationtables [i+5*256] = (i & 0x0f) + 48;
 +            translationtables [i+6*256] = (i & 0x0f) + 16;
 +        }
 +        else if(i >= 0xc0 && i<= 0xcf)
 +        {
 +            translationtables [i      ] = i;
 +            translationtables [i+  256] = i;
 +            translationtables [i+2*256] = i;
 +            translationtables [i+3*256] = i;
 +            translationtables [i+4*256] = (i & 0x0f) - 96;
 +            translationtables [i+5*256] = (i & 0x0f) + 32;
 +            translationtables [i+6*256] = (i & 0x0f);
 +        }
 +        else if(i >= 0xf7 && i<= 0xfb)
 +        {
 +            translationtables [i      ] = col1;
 +            translationtables [i+  256] = i;
 +            translationtables [i+2*256] = i;
 +            translationtables [i+3*256] = i;
 +            translationtables [i+4*256] = i;
 +            translationtables [i+5*256] = i;
 +            translationtables [i+6*256] = i;
 +        }
 +        else if(i >= 0xf1 && i<= 0xf6)
 +        {
 +            translationtables [i      ] = (i & 0x0f) - 33;
 +            translationtables [i+  256] = i;
 +            translationtables [i+2*256] = i;
 +            translationtables [i+3*256] = i;
 +            translationtables [i+4*256] = i;
 +            translationtables [i+5*256] = i;
 +            translationtables [i+6*256] = i;
 +        }
 +        else if(i >= 0x20 && i<= 0x40)
 +        {
 +            translationtables [i      ] = col2;
 +            translationtables [i+  256] = col2;
 +            translationtables [i+2*256] = (i & 0x0f) - 48;
 +            translationtables [i+3*256] = (i & 0x0f) - 48;
 +            translationtables [i+4*256] = col2;
 +            translationtables [i+5*256] = col2;
 +            translationtables [i+6*256] = col2;
 +        }
 +        else  // Keep all other colors as is.
 +        {
 +            translationtables[i]=
 +            translationtables[i+256]=
 +            translationtables[i+2*256]=
 +            translationtables[i+3*256]=
 +            translationtables[i+4*256]=
 +            translationtables[i+5*256]=
 +            translationtables[i+6*256]=i;
 +        }
 +
 +        ++col1;
 +        ++col2;
 +    }
 +}
 +
 +
 +
 +
 +//
 +// R_DrawSpan
 +// With DOOM style restrictions on view orientation,
 +//  the floors and ceilings consist of horizontal slices
 +//  or spans with constant z depth.
 +// However, rotation around the world z axis is possible,
 +//  thus this mapping, while simpler and faster than
 +//  perspective correct texture mapping, has to traverse
 +//  the texture at an angle in all but a few cases.
 +// In consequence, flats are not stored by column (like walls),
 +//  and the inner loop has to step in texture space u and v.
 +//
 +int			ds_y;
 +int			ds_x1;
 +int			ds_x2;
 +
 +lighttable_t*		ds_colormap;
 +
 +fixed_t			ds_xfrac;
 +fixed_t			ds_yfrac;
 +fixed_t			ds_xstep;
 +fixed_t			ds_ystep;
 +
 +// start of a 64*64 tile image
 +byte*			ds_source;
 +
 +// just for profiling
 +int			dscount;
 +
 +
 +//
 +// Draws the actual span.
 +void R_DrawSpan (void)
 +{
-     fixed_t		xfrac;
-     fixed_t		yfrac;
-     byte*		dest;
-     int			count;
-     int			spot;
-
- #ifdef RANGECHECK
++    unsigned int position, step;
++    byte *dest;
++    int count;
++    int spot;
++    unsigned int xtemp, ytemp;
++
++#ifdef RANGECHECK
 +    if (ds_x2 < ds_x1
 +	|| ds_x1<0
- 	|| ds_x2>=SCREENWIDTH
++	|| ds_x2>=SCREENWIDTH
 +	|| (unsigned)ds_y>SCREENHEIGHT)
 +    {
 +	I_Error( "R_DrawSpan: %i to %i at %i",
 +		 ds_x1,ds_x2,ds_y);
 +    }
- //	dscount++;
- #endif
++//	dscount++;
++#endif
++
++    // Pack position and step variables into a single 32-bit integer,
++    // with x in the top 16 bits and y in the bottom 16 bits.  For
++    // each 16-bit part, the top 6 bits are the integer part and the
++    // bottom 10 bits are the fractional part of the pixel position.
++
++    position = ((ds_xfrac << 10) & 0xffff0000)
++             | ((ds_yfrac >> 6)  & 0x0000ffff);
++    step = ((ds_xstep << 10) & 0xffff0000)
++         | ((ds_ystep >> 6)  & 0x0000ffff);
 +
-
-     xfrac = ds_xfrac;
-     yfrac = ds_yfrac;
-
 +    dest = ylookup[ds_y] + columnofs[ds_x1];
 +
 +    // We do not check for zero spans here?
-     count = ds_x2 - ds_x1;
++    count = ds_x2 - ds_x1;
 +
-     do
++    do
 +    {
- 	// Current texture index in u,v.
- 	spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
++	// Calculate current texture index in u,v.
++        ytemp = (position >> 4) & 0x0fc0;
++        xtemp = (position >> 26);
++        spot = xtemp | ytemp;
 +
 +	// Lookup pixel from flat texture tile,
 +	//  re-index using light/colormap.
 +	*dest++ = ds_colormap[ds_source[spot]];
 +
- 	// Next step in u,v.
- 	xfrac += ds_xstep;
- 	yfrac += ds_ystep;
-
-     } while (count--);
- }
++        position += step;
++
++    } while (count--);
++}
 +
 +
 +
 +// UNUSED.
 +// Loop unrolled by 4.
 +#if 0
 +void R_DrawSpan (void)
 +{
 +    unsigned	position, step;
 +
 +    byte*	source;
 +    byte*	colormap;
 +    byte*	dest;
 +
 +    unsigned	count;
 +    usingned	spot;
 +    unsigned	value;
 +    unsigned	temp;
 +    unsigned	xtemp;
 +    unsigned	ytemp;
 +
 +    position = ((ds_xfrac<<10)&0xffff0000) | ((ds_yfrac>>6)&0xffff);
 +    step = ((ds_xstep<<10)&0xffff0000) | ((ds_ystep>>6)&0xffff);
 +
 +    source = ds_source;
 +    colormap = ds_colormap;
 +    dest = ylookup[ds_y] + columnofs[ds_x1];
 +    count = ds_x2 - ds_x1 + 1;
 +
 +    while (count >= 4)
 +    {
 +	ytemp = position>>4;
 +	ytemp = ytemp & 4032;
 +	xtemp = position>>26;
 +	spot = xtemp | ytemp;
 +	position += step;
 +	dest[0] = colormap[source[spot]];
 +
 +	ytemp = position>>4;
 +	ytemp = ytemp & 4032;
 +	xtemp = position>>26;
 +	spot = xtemp | ytemp;
 +	position += step;
 +	dest[1] = colormap[source[spot]];
 +
 +	ytemp = position>>4;
 +	ytemp = ytemp & 4032;
 +	xtemp = position>>26;
 +	spot = xtemp | ytemp;
 +	position += step;
 +	dest[2] = colormap[source[spot]];
 +
 +	ytemp = position>>4;
 +	ytemp = ytemp & 4032;
 +	xtemp = position>>26;
 +	spot = xtemp | ytemp;
 +	position += step;
 +	dest[3] = colormap[source[spot]];
 +
 +	count -= 4;
 +	dest += 4;
 +    }
 +    while (count > 0)
 +    {
 +	ytemp = position>>4;
 +	ytemp = ytemp & 4032;
 +	xtemp = position>>26;
 +	spot = xtemp | ytemp;
 +	position += step;
 +	*dest++ = colormap[source[spot]];
 +	count--;
 +    }
 +}
 +#endif
 +
 +
 +//
 +// Again..
 +//
- void R_DrawSpanLow (void)
- {
-     fixed_t		xfrac;
-     fixed_t		yfrac;
-     byte*		dest;
-     int			count;
-     int			spot;
-
- #ifdef RANGECHECK
++void R_DrawSpanLow (void)
++{
++    unsigned int position, step;
++    unsigned int xtemp, ytemp;
++    byte *dest;
++    int count;
++    int spot;
++
++#ifdef RANGECHECK
 +    if (ds_x2 < ds_x1
 +	|| ds_x1<0
- 	|| ds_x2>=SCREENWIDTH
++	|| ds_x2>=SCREENWIDTH
 +	|| (unsigned)ds_y>SCREENHEIGHT)
 +    {
 +	I_Error( "R_DrawSpan: %i to %i at %i",
 +		 ds_x1,ds_x2,ds_y);
 +    }
 +//	dscount++;
- #endif
-
-     xfrac = ds_xfrac;
-     yfrac = ds_yfrac;
-
-     count = (ds_x2 - ds_x1);
++#endif
++
++    position = ((ds_xfrac << 10) & 0xffff0000)
++             | ((ds_yfrac >> 6)  & 0x0000ffff);
++    step = ((ds_xstep << 10) & 0xffff0000)
++         | ((ds_ystep >> 6)  & 0x0000ffff);
++
++    count = (ds_x2 - ds_x1);
 +
 +    // Blocky mode, need to multiply by 2.
 +    ds_x1 <<= 1;
 +    ds_x2 <<= 1;
-
++
 +    dest = ylookup[ds_y] + columnofs[ds_x1];
-
-     do
-     {
- 	spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
++
++    do
++    {
++	// Calculate current texture index in u,v.
++        ytemp = (position >> 4) & 0x0fc0;
++        xtemp = (position >> 26);
++        spot = xtemp | ytemp;
++
 +	// Lowres/blocky mode does it twice,
 +	//  while scale is adjusted appropriately.
- 	*dest++ = ds_colormap[ds_source[spot]];
 +	*dest++ = ds_colormap[ds_source[spot]];
-
- 	xfrac += ds_xstep;
- 	yfrac += ds_ystep;
++	*dest++ = ds_colormap[ds_source[spot]];
 +
-     } while (count--);
++	position += step;
++
++    } while (count--);
 +}
 +
 +//
 +// R_InitBuffer
 +// Creats lookup tables that avoid
 +//  multiplies and other hazzles
 +//  for getting the framebuffer address
 +//  of a pixel to draw.
 +//
 +void
 +R_InitBuffer
 +( int		width,
 +  int		height )
 +{
 +    int		i;
 +
 +    // Handle resize,
 +    //  e.g. smaller view windows
 +    //  with border and/or status bar.
 +    viewwindowx = (SCREENWIDTH-width) >> 1;
 +
 +    // Column offset. For windows.
 +    for (i=0 ; i<width ; i++)
 +	columnofs[i] = viewwindowx + i;
 +
 +    // Samw with base row offset.
 +    if (width == SCREENWIDTH)
 +	viewwindowy = 0;
 +    else
 +	viewwindowy = (SCREENHEIGHT-SBARHEIGHT-height) >> 1;
 +
 +    // Preclaculate all row offsets.
 +    for (i=0 ; i<height ; i++)
 +	ylookup[i] = I_VideoBuffer + (i+viewwindowy)*SCREENWIDTH;
 +}
 +
 +
 +
 +
 +//
 +// R_FillBackScreen
 +// Fills the back screen with a pattern
 +//  for variable screen sizes
 +// Also draws a beveled edge.
 +//
 +// haleyjd 08/29/10: [STRIFE] Added support for configurable back_flat.
 +//
 +void R_FillBackScreen (void)
 +{
 +    byte*	src;
 +    byte*	dest;
 +    int		x;
 +    int		y;
 +    patch_t*	patch;
 +
 +    char *name;
 +
 +    // If we are running full screen, there is no need to do any of this,
 +    // and the background buffer can be freed if it was previously in use.
 +
 +    if (scaledviewwidth == SCREENWIDTH)
 +    {
 +        if (background_buffer != NULL)
 +        {
 +            Z_Free(background_buffer);
 +            background_buffer = NULL;
 +        }
 +
 +	return;
 +    }
 +
 +    // Allocate the background buffer if necessary
 +
 +    if (background_buffer == NULL)
 +    {
 +        background_buffer = Z_Malloc(SCREENWIDTH * (SCREENHEIGHT - SBARHEIGHT),
 +                                     PU_STATIC, NULL);
 +    }
 +
 +    // haleyjd 08/29/10: [STRIFE] Use configurable back_flat
 +    name = back_flat;
 +
 +    src = W_CacheLumpName(name, PU_CACHE);
 +    dest = background_buffer;
 +
 +    for (y=0 ; y<SCREENHEIGHT-SBARHEIGHT ; y++)
 +    {
 +	for (x=0 ; x<SCREENWIDTH/64 ; x++)
 +	{
 +	    memcpy (dest, src+((y&63)<<6), 64);
 +	    dest += 64;
 +	}
 +
 +	if (SCREENWIDTH&63)
 +	{
 +	    memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63);
 +	    dest += (SCREENWIDTH&63);
 +	}
 +    }
 +
 +    // Draw screen and bezel; this is done to a separate screen buffer.
 +
 +    V_UseBuffer(background_buffer);
 +
 +    patch = W_CacheLumpName(DEH_String("brdr_t"),PU_CACHE);
 +
 +    for (x=0 ; x<scaledviewwidth ; x+=8)
 +	V_DrawPatch(viewwindowx+x, viewwindowy-8, patch);
 +    patch = W_CacheLumpName(DEH_String("brdr_b"),PU_CACHE);
 +
 +    for (x=0 ; x<scaledviewwidth ; x+=8)
 +	V_DrawPatch(viewwindowx+x, viewwindowy+viewheight, patch);
 +    patch = W_CacheLumpName(DEH_String("brdr_l"),PU_CACHE);
 +
 +    for (y=0 ; y<viewheight ; y+=8)
 +	V_DrawPatch(viewwindowx-8, viewwindowy+y, patch);
 +    patch = W_CacheLumpName(DEH_String("brdr_r"),PU_CACHE);
 +
 +    for (y=0 ; y<viewheight ; y+=8)
 +	V_DrawPatch(viewwindowx+scaledviewwidth, viewwindowy+y, patch);
 +
 +    // Draw beveled edge.
 +    V_DrawPatch(viewwindowx-8,
 +                viewwindowy-8,
 +                W_CacheLumpName(DEH_String("brdr_tl"),PU_CACHE));
 +
 +    V_DrawPatch(viewwindowx+scaledviewwidth,
 +                viewwindowy-8,
 +                W_CacheLumpName(DEH_String("brdr_tr"),PU_CACHE));
 +
 +    V_DrawPatch(viewwindowx-8,
 +                viewwindowy+viewheight,
 +                W_CacheLumpName(DEH_String("brdr_bl"),PU_CACHE));
 +
 +    V_DrawPatch(viewwindowx+scaledviewwidth,
 +                viewwindowy+viewheight,
 +                W_CacheLumpName(DEH_String("brdr_br"),PU_CACHE));
 +
 +    V_RestoreBuffer();
 +}
 +
 +
 +//
 +// Copy a screen buffer.
 +//
 +void
 +R_VideoErase
 +( unsigned	ofs,
 +  int		count )
 +{
 +  // LFB copy.
 +  // This might not be a good idea if memcpy
 +  //  is not optiomal, e.g. byte by byte on
 +  //  a 32bit CPU, as GNU GCC/Linux libc did
 +  //  at one point.
 +
 +    if (background_buffer != NULL)
 +    {
 +        memcpy(I_VideoBuffer + ofs, background_buffer + ofs, count);
 +    }
 +}
 +
 +
 +//
 +// R_DrawViewBorder
 +// Draws the border around the view
 +//  for different size windows?
 +//
 +void
 +V_MarkRect
 +( int		x,
 +  int		y,
 +  int		width,
 +  int		height );
 +
 +void R_DrawViewBorder (void)
 +{
 +    int		top;
 +    int		side;
 +    int		ofs;
 +    int		i;
 +
 +    if (scaledviewwidth == SCREENWIDTH)
 +	return;
 +
 +    top = ((SCREENHEIGHT-SBARHEIGHT)-viewheight)/2;
 +    side = (SCREENWIDTH-scaledviewwidth)/2;
 +
 +    // copy top and one line of left side
 +    R_VideoErase (0, top*SCREENWIDTH+side);
 +
 +    // copy one line of right side and bottom
 +    ofs = (viewheight+top)*SCREENWIDTH-side;
 +    R_VideoErase (ofs, top*SCREENWIDTH+side);
 +
 +    // copy sides using wraparound
 +    ofs = top*SCREENWIDTH + SCREENWIDTH-side;
 +    side <<= 1;
 +
 +    for (i=1 ; i<viewheight ; i++)
 +    {
 +	R_VideoErase (ofs, side);
 +	ofs += SCREENWIDTH;
 +    }
 +
 +    // ?
 +    V_MarkRect (0,0,SCREENWIDTH, SCREENHEIGHT-SBARHEIGHT);
 +}
 +
 +
diff --cc src/strife/st_stuff.c
index 3b4ff0fb,00000000..1af8a485
mode 100644,000000..100644
--- a/src/strife/st_stuff.c
+++ b/src/strife/st_stuff.c
@@@ -1,972 -1,0 +1,972 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Status bar code.
 +//	Does the face/direction indicator animatin.
 +//	Does palette indicators as well (red pain/berserk, bright pickup)
 +//
 +//-----------------------------------------------------------------------------
 +
 +
 +
 +#include <stdio.h>
 +
 +#include "i_system.h"
 +#include "i_video.h"
 +#include "z_zone.h"
 +#include "m_random.h"
 +#include "w_wad.h"
 +
 +#include "deh_main.h"
 +#include "deh_misc.h"
 +#include "doomdef.h"
 +#include "doomkeys.h"
 +
 +#include "g_game.h"
 +
 +#include "st_stuff.h"
 +#include "st_lib.h"
 +#include "r_local.h"
 +
 +#include "p_local.h"
 +#include "p_inter.h"
 +
 +#include "am_map.h"
 +#include "m_cheat.h"
 +
 +#include "s_sound.h"
 +
 +// Needs access to LFB.
 +#include "v_video.h"
 +
 +// State.
 +#include "doomstat.h"
 +
 +// Data.
 +#include "dstrings.h"
 +#include "sounds.h"
 +
 +//
 +// STATUS BAR DATA
 +//
 +
 +
 +// Palette indices.
 +// For damage/bonus red-/gold-shifts
 +#define STARTREDPALS            1
 +#define STARTBONUSPALS          9
 +#define NUMREDPALS              8
 +#define NUMBONUSPALS            4
 +// Radiation suit, green shift.
 +#define RADIATIONPAL            13
 +
 +// Location of status bar
 +#define ST_X                    0
 +
 +#define ST_FX                   143
 +
 +// Location and size of statistics,
 +//  justified according to widget type.
 +// Problem is, within which space? STbar? Screen?
 +// Note: this could be read in by a lump.
 +//       Problem is, is the stuff rendered
 +//       into a buffer,
 +//       or into the frame buffer?
 +
 +// AMMO number pos.
 +// haleyjd 09/01/10: [STRIFE] Adjusted.
 +#define ST_AMMOWIDTH            3
 +#define ST_AMMOX                311
 +#define ST_AMMOY                162
 +
 +// HEALTH number pos.
 +// haleyjd 09/01/10: [STRIFE] Adjusted.
 +#define ST_HEALTHWIDTH          3
 +#define ST_HEALTHX              79
 +#define ST_HEALTHY              162
 +
 +// Weapon pos.
 +#define ST_ARMSX		111
 +#define ST_ARMSY		172
 +#define ST_ARMSBGX		104
 +#define ST_ARMSBGY		168
 +#define ST_ARMSXSPACE		12
 +#define ST_ARMSYSPACE		10
 +
 +// Frags pos.
 +#define ST_FRAGSX			138
 +#define ST_FRAGSY			171
 +#define ST_FRAGSWIDTH		2
 +
 +// ARMOR number pos.
 +#define ST_ARMORWIDTH		3
 +#define ST_ARMORX			221
 +#define ST_ARMORY			171
 +
 +// Key icon positions.
 +#define ST_KEY0WIDTH		8
 +#define ST_KEY0HEIGHT		5
 +#define ST_KEY0X			239
 +#define ST_KEY0Y			171
 +#define ST_KEY1WIDTH		ST_KEY0WIDTH
 +#define ST_KEY1X			239
 +#define ST_KEY1Y			181
 +#define ST_KEY2WIDTH		ST_KEY0WIDTH
 +#define ST_KEY2X			239
 +#define ST_KEY2Y			191
 +
 +// Ammunition counter.
 +#define ST_AMMO0WIDTH		3
 +#define ST_AMMO0HEIGHT		6
 +#define ST_AMMO0X			288
 +#define ST_AMMO0Y			173
 +#define ST_AMMO1WIDTH		ST_AMMO0WIDTH
 +#define ST_AMMO1X			288
 +#define ST_AMMO1Y			179
 +#define ST_AMMO2WIDTH		ST_AMMO0WIDTH
 +#define ST_AMMO2X			288
 +#define ST_AMMO2Y			191
 +#define ST_AMMO3WIDTH		ST_AMMO0WIDTH
 +#define ST_AMMO3X			288
 +#define ST_AMMO3Y			185
 +
 +// Indicate maximum ammunition.
 +// Only needed because backpack exists.
 +#define ST_MAXAMMO0WIDTH		3
 +#define ST_MAXAMMO0HEIGHT		5
 +#define ST_MAXAMMO0X		314
 +#define ST_MAXAMMO0Y		173
 +#define ST_MAXAMMO1WIDTH		ST_MAXAMMO0WIDTH
 +#define ST_MAXAMMO1X		314
 +#define ST_MAXAMMO1Y		179
 +#define ST_MAXAMMO2WIDTH		ST_MAXAMMO0WIDTH
 +#define ST_MAXAMMO2X		314
 +#define ST_MAXAMMO2Y		191
 +#define ST_MAXAMMO3WIDTH		ST_MAXAMMO0WIDTH
 +#define ST_MAXAMMO3X		314
 +#define ST_MAXAMMO3Y		185
 +
 +// pistol
 +#define ST_WEAPON0X			110
 +#define ST_WEAPON0Y			172
 +
 +// shotgun
 +#define ST_WEAPON1X			122
 +#define ST_WEAPON1Y			172
 +
 +// chain gun
 +#define ST_WEAPON2X			134
 +#define ST_WEAPON2Y			172
 +
 +// missile launcher
 +#define ST_WEAPON3X			110
 +#define ST_WEAPON3Y			181
 +
 +// plasma gun
 +#define ST_WEAPON4X			122
 +#define ST_WEAPON4Y			181
 +
 + // bfg
 +#define ST_WEAPON5X			134
 +#define ST_WEAPON5Y			181
 +
 +// WPNS title
 +#define ST_WPNSX			109
 +#define ST_WPNSY			191
 +
 + // DETH title
 +#define ST_DETHX			109
 +#define ST_DETHY			191
 +
 +// Dimensions given in characters.
 +#define ST_MSGWIDTH			52
 +
 +// haleyjd 08/31/10: [STRIFE]
 +// * Removed faces.
 +// haleyjd 09/01/10:
 +// * Removed DOOM pre-beta cruft.
 +// * Removed deathmatch frags/arms-related stuff.
 +// * Removed arms panel stuff.
 +// * Removed unused widgets.
 +// * Removed more faces, keyboxes, st_randomnumber
 +
 +// graphics are drawn to a backing screen and blitted to the real screen
 +byte                   *st_backing_screen;
 +
 +// main player in game
 +static player_t*        plyr;
 +
 +// ST_Start() has just been called
 +static boolean          st_firsttime;
 +
 +// lump number for PLAYPAL
 +static int              lu_palette;
 +
 +// whether in automap or first-person
 +static st_stateenum_t   st_gamestate;
 +
 +// whether left-side main status bar is active
 +static boolean          st_statusbaron;
 +
 +// haleyjd 09/01/10: [STRIFE]
 +// Whether or not a popup is currently displayed
 +static boolean          st_displaypopup;
 +
 +// haleyjd 09/01/10: [STRIFE] sbar -> invback
 +// main inventory background and other bits
 +static patch_t*         invback;  // main bar
 +static patch_t*         invtop;   // top bit
 +static patch_t*         invpop;   // popup frame with text
 +static patch_t*         invpop2;  // plain popup frame
 +static patch_t*         invpbak;  // popup background w/details
 +static patch_t*         invpbak2; // plain popup background
 +
 +// haleyjd 09/01/10: [STRIFE] Replaced tallnum, shortnum w/inv fonts
 +// 0-9, green numbers
 +static patch_t*         invfontg[10];
 +
 +// 0-9, yellow numbers
 +static patch_t*         invfonty[10];
 +
 +// 3 key-cards, 3 skulls -- STRIFE-TODO: This is handled differently
 +static patch_t*         keys[NUMCARDS];
 +
 +// ready-weapon widget
 +static st_number_t      w_ready; // haleyjd [STRIFE]: This is still used.
 +
 +// haleyjd: [STRIFE] This is still used but was changed to a st_number_t.
 +// health widget
 +static st_number_t      w_health;
 +
 +// ammo widgets
 +static st_number_t      w_ammo[NUMAMMO];     // haleyjd [STRIFE]: Still used.
 +
 +// max ammo widgets
 +static st_number_t      w_maxammo[NUMAMMO];  // haleyjd [STRIFE]: Still used.
 +
 +// number of frags so far in deathmatch
 +static int              st_fragscount;
 +
 +
 +cheatseq_t cheat_mus = CHEAT("idmus", 2);
 +cheatseq_t cheat_god = CHEAT("iddqd", 0);
 +cheatseq_t cheat_ammo = CHEAT("idkfa", 0);
 +cheatseq_t cheat_ammonokey = CHEAT("idfa", 0);
 +cheatseq_t cheat_noclip = CHEAT("idspispopd", 0);
 +cheatseq_t cheat_commercial_noclip = CHEAT("idclip", 0);
 +
 +cheatseq_t	cheat_powerup[7] =
 +{
 +    CHEAT("idbeholdv", 0),
 +    CHEAT("idbeholds", 0),
 +    CHEAT("idbeholdi", 0),
 +    CHEAT("idbeholdr", 0),
 +    CHEAT("idbeholda", 0),
 +    CHEAT("idbeholdl", 0),
 +    CHEAT("idbehold", 0),
 +};
 +
 +cheatseq_t cheat_choppers = CHEAT("idchoppers", 0);
 +cheatseq_t cheat_clev = CHEAT("idclev", 2);
 +cheatseq_t cheat_mypos = CHEAT("idmypos", 0);
 +
 +
 +//
 +// STATUS BAR CODE
 +//
 +void ST_Stop(void);
 +
 +void ST_refreshBackground(void)
 +{
 +    if (st_statusbaron)
 +    {
 +        V_UseBuffer(st_backing_screen);
 +
 +        V_DrawPatch(ST_X, 0, invback);
 +
 +        // haleyjd 09/01/10: STRIFE-TODO: status bar stuff
 +        /*
 +        if (netgame)
 +            V_DrawPatch(ST_FX, 0, faceback);
 +        */
 +        V_RestoreBuffer();
 +
 +        V_CopyRect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y);
 +    }
 +}
 +
 +
 +// Respond to keyboard input events,
 +//  intercept cheats.
 +boolean
 +ST_Responder (event_t* ev)
 +{
 +  int		i;
 +
 +  // Filter automap on/off.
 +  if (ev->type == ev_keyup
 +      && ((ev->data1 & 0xffff0000) == AM_MSGHEADER))
 +  {
 +    switch(ev->data1)
 +    {
 +      case AM_MSGENTERED:
 +	st_gamestate = AutomapState;
 +	st_firsttime = true;
 +	break;
 +
 +      case AM_MSGEXITED:
 +	//	fprintf(stderr, "AM exited\n");
 +	st_gamestate = FirstPersonState;
 +	break;
 +    }
 +  }
 +
 +  // if a user keypress...
 +  else if (ev->type == ev_keydown)
 +  {
 +    if (!netgame && gameskill != sk_nightmare)
 +    {
 +      // 'dqd' cheat for toggleable god mode
 +      if (cht_CheckCheat(&cheat_god, ev->data2))
 +      {
 +	plyr->cheats ^= CF_GODMODE;
 +	if (plyr->cheats & CF_GODMODE)
 +	{
 +	  if (plyr->mo)
 +	    plyr->mo->health = 100;
 +
 +	  plyr->health = deh_god_mode_health;
 +	  plyr->message = DEH_String(STSTR_DQDON);
 +	}
 +	else
 +	  plyr->message = DEH_String(STSTR_DQDOFF);
 +      }
 +      // 'fa' cheat for killer fucking arsenal
 +      else if (cht_CheckCheat(&cheat_ammonokey, ev->data2))
 +      {
 +	plyr->armorpoints = deh_idfa_armor;
 +	plyr->armortype = deh_idfa_armor_class;
 +
 +	for (i=0;i<NUMWEAPONS;i++)
 +	  plyr->weaponowned[i] = true;
 +
 +	for (i=0;i<NUMAMMO;i++)
 +	  plyr->ammo[i] = plyr->maxammo[i];
 +
 +	plyr->message = DEH_String(STSTR_FAADDED);
 +      }
 +      // 'kfa' cheat for key full ammo
 +      else if (cht_CheckCheat(&cheat_ammo, ev->data2))
 +      {
 +	plyr->armorpoints = deh_idkfa_armor;
 +	plyr->armortype = deh_idkfa_armor_class;
 +
 +	for (i=0;i<NUMWEAPONS;i++)
 +	  plyr->weaponowned[i] = true;
 +
 +	for (i=0;i<NUMAMMO;i++)
 +	  plyr->ammo[i] = plyr->maxammo[i];
 +
 +	for (i=0;i<NUMCARDS;i++)
 +	  plyr->cards[i] = true;
 +
 +	plyr->message = DEH_String(STSTR_KFAADDED);
 +      }
 +      // 'mus' cheat for changing music
 +      else if (cht_CheckCheat(&cheat_mus, ev->data2))
 +      {
 +
 +	char	buf[3];
 +	int		musnum;
 +
 +	plyr->message = DEH_String(STSTR_MUS);
 +	cht_GetParam(&cheat_mus, buf);
 +
 +	if (gamemode == commercial)
 +	{
 +	  musnum = mus_logo + (buf[0]-'0')*10 + buf[1]-'0' - 1; // villsa [STRIFE] TODO - fix music
 +
 +	  if (((buf[0]-'0')*10 + buf[1]-'0') > 35)
 +	    plyr->message = DEH_String(STSTR_NOMUS);
 +	  else
 +	    S_ChangeMusic(musnum, 1);
 +	}
 +	else
 +	{
 +	  musnum = mus_logo + (buf[0]-'1')*9 + (buf[1]-'1');    // villsa [STRIFE] TODO - fix music
 +
 +	  if (((buf[0]-'1')*9 + buf[1]-'1') > 31)
 +	    plyr->message = DEH_String(STSTR_NOMUS);
 +	  else
 +	    S_ChangeMusic(musnum, 1);
 +	}
 +      }
 +      else if ( (gamemission == doom
 +                 && cht_CheckCheat(&cheat_noclip, ev->data2))
 +             || (gamemission != doom
 +                 && cht_CheckCheat(&cheat_commercial_noclip,ev->data2)))
 +      {
 +        // Noclip cheat.
 +        // For Doom 1, use the idspipsopd cheat; for all others, use
 +        // idclip
 +
 +	plyr->cheats ^= CF_NOCLIP;
 +
 +	if (plyr->cheats & CF_NOCLIP)
 +	  plyr->message = DEH_String(STSTR_NCON);
 +	else
 +	  plyr->message = DEH_String(STSTR_NCOFF);
 +      }
 +      // 'behold?' power-up cheats
 +      for (i=0;i<6;i++)
 +      {
 +	if (cht_CheckCheat(&cheat_powerup[i], ev->data2))
 +	{
 +	  if (!plyr->powers[i])
 +	    P_GivePower( plyr, i);
 +	  else if (i!=pw_strength)
 +	    plyr->powers[i] = 1;
 +	  else
 +	    plyr->powers[i] = 0;
 +
 +	  plyr->message = DEH_String(STSTR_BEHOLDX);
 +	}
 +      }
 +
 +      // 'behold' power-up menu
 +      if (cht_CheckCheat(&cheat_powerup[6], ev->data2))
 +      {
 +	plyr->message = DEH_String(STSTR_BEHOLD);
 +      }
 +      // 'choppers' invulnerability & chainsaw
 +      else if (cht_CheckCheat(&cheat_choppers, ev->data2))
 +      {
 +          // villsa [STRIFE] unused
 +	//plyr->weaponowned[wp_chainsaw] = true;
 +	plyr->powers[pw_invulnerability] = true;
 +	plyr->message = DEH_String(STSTR_CHOPPERS);
 +      }
 +      // 'mypos' for player position
 +      else if (cht_CheckCheat(&cheat_mypos, ev->data2))
 +      {
 +	static char	buf[ST_MSGWIDTH];
 +	sprintf(buf, "ang=0x%x;x,y=(0x%x,0x%x)",
 +		players[consoleplayer].mo->angle,
 +		players[consoleplayer].mo->x,
 +		players[consoleplayer].mo->y);
 +	plyr->message = buf;
 +      }
 +    }
 +
 +    // 'clev' change-level cheat
 +    if (!netgame && cht_CheckCheat(&cheat_clev, ev->data2))
 +    {
 +      char		buf[3];
 +      int		epsd;
 +      int		map;
 +
 +      cht_GetParam(&cheat_clev, buf);
 +
 +      if (gamemode == commercial)
 +      {
 +	epsd = 1;
 +	map = (buf[0] - '0')*10 + buf[1] - '0';
 +      }
 +      else
 +      {
 +	epsd = buf[0] - '0';
 +	map = buf[1] - '0';
 +      }
 +
 +      // haleyjd 09/01/10: Removed Chex Quest stuff.
 +
 +      // Catch invalid maps.
 +      if (epsd < 1)
 +	return false;
 +
 +      if (map < 1)
 +	return false;
 +
 +      // Ohmygod - this is not going to work.
 +      if ((gamemode == retail)
 +	  && ((epsd > 4) || (map > 9)))
 +	return false;
 +
 +      if ((gamemode == registered)
 +	  && ((epsd > 3) || (map > 9)))
 +	return false;
 +
 +      if ((gamemode == shareware)
 +	  && ((epsd > 1) || (map > 9)))
 +	return false;
 +
 +      if ((gamemode == commercial)
 +	&& (( epsd > 1) || (map > 34)))
 +	return false;
 +
 +      // So be it.
 +      plyr->message = DEH_String(STSTR_CLEV);
 +      G_DeferedInitNew(gameskill, epsd, map);
 +    }
 +  }
 +  return false;
 +}
 +
 +
 +/*
 +int ST_calcPainOffset(void)
 +{
 +    // haleyjd 08/31/10: [STRIFE] Removed.
 +}
 +*/
 +
 +//
 +// This is a not-very-pretty routine which handles
 +//  the face states and their timing.
 +// the precedence of expressions is:
 +//  dead > evil grin > turned head > straight ahead
 +//
 +/*
 +void ST_updateFaceWidget(void)
 +{
 +    // haleyjd 08/31/10: [STRIFE] Removed.
 +}
 +*/
 +
 +/*
 +void ST_updateWidgets(void)
 +{
 +    // haleyjd 09/01/10: [STRIFE] Rogue merged this into ST_Ticker below.
 +}
 +*/
 +
 +//
 +// ST_Ticker
 +//
 +// haleyjd 09/01/10: [STRIFE]
 +// * Removed st_clock and st_randomnumber.
 +// * Merged ST_updateWidgets here. Wasn't inlined, as doesn't exist separately
 +//   in the binary as inlined functions normally do.
 +//
 +void ST_Ticker (void)
 +{
 +    static int  largeammo = 1994; // means "n/a"
 +    int         i;
 +
 +    // must redirect the pointer if the ready weapon has changed.
 +    if (weaponinfo[plyr->readyweapon].ammo == am_noammo)
 +        w_ready.num = &largeammo;
 +    else
 +        w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo];
 +
 +    w_ready.data = plyr->readyweapon;
 +
 +    // STRIFE-TODO: Gobbledeegunk.
 +    /*
 +    v2 = dword_88490-- == 1; // no clue yet...
 +    if(v2)
 +        dword_DC7F4 = dword_DC7F0;
 +    v1 = st_popupdisplaytics;
 +    if(st_popupdisplaytics)
 +    {
 +        --st_popupdisplaytics;
 +        if(v1 == 1)
 +        {
 +            st_displaypopup = false;
 +            st_showkeys = false;
 +            dword_88484 = -1;     // unknown var
 +            if(st_dosizedisplay)
 +                M_SizeDisplay();  // mondo hack?
 +            st_dosizedisplay = false;
 +        }
 +    }
 +    */
 +
 +    // haleyjd 09/01/10: [STRIFE] Keys are handled on a popup
 +    // haleyjd 08/31/10: [STRIFE] No face widget
 +    // haleyjd 09/01/10: [STRIFE] Armor, weapons, frags, etc. handled elsewhere
 +
 +    // haleyjd: This is from the PRE-BETA! Left here because it amuses me ;)
 +    // get rid of chat window if up because of message
 +    //if (!--st_msgcounter)
 +    //    st_chat = st_oldchat;
 +}
 +
 +static int st_palette = 0;
 +
 +//
 +// ST_doPaletteStuff
 +//
 +// haleyjd 08/31/10: [STRIFE]
 +// * Changed radsuit palette handling for Strife nukagecount.
 +// * All other logic verified to be unmodified.
 +//
 +void ST_doPaletteStuff(void)
 +{
 +
 +    int		palette;
 +    byte*	pal;
 +    int		cnt;
 +    int		bzc;
 +
 +    cnt = plyr->damagecount;
 +
 +    if (plyr->powers[pw_strength])
 +    {
 +        // slowly fade the berzerk out
 +        bzc = 12 - (plyr->powers[pw_strength]>>6);
 +
 +        if (bzc > cnt)
 +            cnt = bzc;
 +    }
 +
 +    if (cnt)
 +    {
 +        palette = (cnt+7)>>3;
 +
 +        if (palette >= NUMREDPALS)
 +            palette = NUMREDPALS-1;
 +
 +        palette += STARTREDPALS;
 +    }
 +
 +    else if (plyr->bonuscount)
 +    {
 +        palette = (plyr->bonuscount+7)>>3;
 +
 +        if (palette >= NUMBONUSPALS)
 +            palette = NUMBONUSPALS-1;
 +
 +        palette += STARTBONUSPALS;
 +    }
 +    // haleyjd 08/31/10: [STRIFE] Flash green when in nukage, not when has
 +    // an environment suit (a breathing sound is played to indicate that
 +    // instead).
 +    else if ( plyr->nukagecount > 16*TICRATE ||
 +              (plyr->nukagecount & 8))
 +        palette = RADIATIONPAL;
 +    else
 +        palette = 0;
 +
 +    // haleyjd 08/31/10: Removed Chex Quest
 +
 +    if (palette != st_palette)
 +    {
 +        st_palette = palette;
 +        pal = (byte *) W_CacheLumpNum (lu_palette, PU_CACHE)+palette*768;
 +        I_SetPalette (pal);
 +    }
 +
 +}
 +
 +/*
 +void ST_drawWidgets(boolean refresh)
 +{
 +    haleyjd 09/01/10: [STRIFE] Removed
 +}
 +*/
 +
 +void ST_doRefresh(void)
 +{
 +    st_firsttime = false;
 +
 +    // draw status bar background to off-screen buff
 +    ST_refreshBackground();
 +}
 +
 +void ST_diffDraw(void)
 +{
 +    // haleyjd: STRIFE-TODO: Needed?
 +}
 +
 +void ST_Drawer (boolean fullscreen, boolean refresh)
 +{
 +    st_statusbaron = (!fullscreen) || automapactive;
 +    st_firsttime = st_firsttime || refresh;
 +
 +    // Do red-/gold-shifts from damage/items
 +    ST_doPaletteStuff();
 +
 +    // haleyjd 09/01/10: STRIFE-TODO: work out statbar details
 +
 +    // If just after ST_Start(), refresh all
 +    if (st_firsttime) ST_doRefresh();
 +    // Otherwise, update as little as possible
 +    else ST_diffDraw();
 +}
 +
 +//
 +// ST_DrawExternal
 +//
 +// haleyjd 09/01/10: [STRIFE] New function.
 +// * Draws external portions of the status bar such the top bar and popups.
 +//
 +boolean ST_DrawExternal(void)
 +{
 +    if (st_statusbaron)
 +    {
 +        V_DrawPatchDirect(0, 160, invtop);
 +        STlib_drawNumPositive(&w_health);
 +        STlib_drawNumPositive(&w_ready);
 +    }
 +    else
 +    {
 +        // STRIFE-TODO:
 +        // ST_drawNumFontY2(15, 194, plyr->health);
 +        // v5 = weaponinfo[plyr->readyweapon].ammo;
 +        // if (v5 != am_noammo)
 +        //     ST_drawNumFontY2(310, 194, plyr->ammo[v5]);
 +    }
 +
 +    if(!st_displaypopup)
 +        return false;
 +
 +    // STRIFE-TODO: Shitloads more stuff:
 +    // * showobjective shit
 +    // * keys/frags popup
 +    // * weapons/ammo/stats popup
 +    // * etc etc etc
 +
 +    return true;
 +}
 +
 +typedef void (*load_callback_t)(char *lumpname, patch_t **variable);
 +
 +// Iterates through all graphics to be loaded or unloaded, along with
 +// the variable they use, invoking the specified callback function.
 +
 +static void ST_loadUnloadGraphics(load_callback_t callback)
 +{
 +
 +    int		i;
 +    int		j;
 +    int		facenum;
 +
 +    char	namebuf[9];
 +
 +    // haleyjd 09/01/10: [STRIFE]
 +    // Load the numbers, green and yellow
 +    for (i=0;i<10;i++)
 +    {
- 	sprintf(namebuf, DEH_String("INVFONG%d"), i);
++        DEH_snprintf(namebuf, 9, "INVFONG%d", i);
 +        callback(namebuf, &invfontg[i]);
 +
- 	sprintf(namebuf, DEH_String("INVFONY%d"), i);
++        DEH_snprintf(namebuf, 9, "INVFONY%d", i);
 +        callback(namebuf, &invfonty[i]);
 +    }
 +
 +    // haleyjd 08/31/10: [STRIFE]
 +    // * No face - STRIFE-TODO: but there are similar color patches, which appear behind the armor in deathmatch...
 +    // 09/01/10:
 +    // * Removed all unused DOOM stuff (arms, numbers, %, etc).
 +
 +    // haleyjd 09/01/10: [STRIFE]: stbar -> invback, added new patches
 +    // status bar background bits
 +    callback(DEH_String("INVBACK"),  &invback);
 +    callback(DEH_String("INVTOP"),   &invtop);
 +    callback(DEH_String("INVPOP"),   &invpop);
 +    callback(DEH_String("INVPOP2"),  &invpop2);
 +    callback(DEH_String("INVPBAK"),  &invpbak);
 +    callback(DEH_String("INVPBAK2"), &invpbak2);
 +}
 +
 +static void ST_loadCallback(char *lumpname, patch_t **variable)
 +{
 +    *variable = W_CacheLumpName(lumpname, PU_STATIC);
 +}
 +
 +void ST_loadGraphics(void)
 +{
 +    ST_loadUnloadGraphics(ST_loadCallback);
 +}
 +
 +void ST_loadData(void)
 +{
 +    lu_palette = W_GetNumForName (DEH_String("PLAYPAL"));
 +    ST_loadGraphics();
 +}
 +
 +static void ST_unloadCallback(char *lumpname, patch_t **variable)
 +{
 +    W_ReleaseLumpName(lumpname);
 +    *variable = NULL;
 +}
 +
 +void ST_unloadGraphics(void)
 +{
 +    ST_loadUnloadGraphics(ST_unloadCallback);
 +}
 +
 +void ST_unloadData(void)
 +{
 +    ST_unloadGraphics();
 +}
 +
 +//
 +// ST_initData
 +//
 +// haleyjd 09/01/10: [STRIFE]
 +// * Removed prebeta cruft, face stuff, keyboxes, and oldwe
 +//
 +void ST_initData(void)
 +{
 +    st_firsttime = true;
 +    plyr = &players[consoleplayer];
 +
 +    st_gamestate = FirstPersonState;
 +
 +    st_statusbaron = true;
 +
 +    st_palette = -1;
 +
 +    STlib_init();
 +}
 +
 +
 +
 +void ST_createWidgets(void)
 +{
 +    int i;
 +
 +    // ready weapon ammo
 +    STlib_initNum(&w_ready,
 +                  ST_AMMOX,
 +                  ST_AMMOY,
 +                  invfontg,
 +                  &plyr->ammo[weaponinfo[plyr->readyweapon].ammo],
 +                  ST_AMMOWIDTH);
 +
 +    // the last weapon type
 +    w_ready.data = plyr->readyweapon;
 +
 +    // health percentage
 +    STlib_initNum(&w_health,
 +                  ST_HEALTHX,
 +                  ST_HEALTHY,
 +                  invfontg,
 +                  &plyr->health,
 +                  ST_HEALTHWIDTH);
 +
 +    // haleyjd 08/31/10: [STRIFE]
 +    // * No face.
 +    // 09/01/10:
 +    // * No arms, weaponsowned, frags, armor, keyboxes
 +
 +    // haleyjd 09/01/10: STRIFE-TODO: Ammo Widgets!!!
 +    /*
 +    // ammo count (all four kinds)
 +    STlib_initNum(&w_ammo[0],
 +		  ST_AMMO0X,
 +		  ST_AMMO0Y,
 +		  shortnum,
 +		  &plyr->ammo[0],
 +		  &st_statusbaron,
 +		  ST_AMMO0WIDTH);
 +
 +    STlib_initNum(&w_ammo[1],
 +		  ST_AMMO1X,
 +		  ST_AMMO1Y,
 +		  shortnum,
 +		  &plyr->ammo[1],
 +		  &st_statusbaron,
 +		  ST_AMMO1WIDTH);
 +
 +    STlib_initNum(&w_ammo[2],
 +		  ST_AMMO2X,
 +		  ST_AMMO2Y,
 +		  shortnum,
 +		  &plyr->ammo[2],
 +		  &st_statusbaron,
 +		  ST_AMMO2WIDTH);
 +
 +    STlib_initNum(&w_ammo[3],
 +		  ST_AMMO3X,
 +		  ST_AMMO3Y,
 +		  shortnum,
 +		  &plyr->ammo[3],
 +		  &st_statusbaron,
 +		  ST_AMMO3WIDTH);
 +
 +    // max ammo count (all four kinds)
 +    STlib_initNum(&w_maxammo[0],
 +		  ST_MAXAMMO0X,
 +		  ST_MAXAMMO0Y,
 +		  shortnum,
 +		  &plyr->maxammo[0],
 +		  &st_statusbaron,
 +		  ST_MAXAMMO0WIDTH);
 +
 +    STlib_initNum(&w_maxammo[1],
 +		  ST_MAXAMMO1X,
 +		  ST_MAXAMMO1Y,
 +		  shortnum,
 +		  &plyr->maxammo[1],
 +		  &st_statusbaron,
 +		  ST_MAXAMMO1WIDTH);
 +
 +    STlib_initNum(&w_maxammo[2],
 +		  ST_MAXAMMO2X,
 +		  ST_MAXAMMO2Y,
 +		  shortnum,
 +		  &plyr->maxammo[2],
 +		  &st_statusbaron,
 +		  ST_MAXAMMO2WIDTH);
 +
 +    STlib_initNum(&w_maxammo[3],
 +		  ST_MAXAMMO3X,
 +		  ST_MAXAMMO3Y,
 +		  shortnum,
 +		  &plyr->maxammo[3],
 +		  &st_statusbaron,
 +		  ST_MAXAMMO3WIDTH);
 +  */
 +}
 +
 +static boolean	st_stopped = true;
 +
 +
 +void ST_Start (void)
 +{
 +    if (!st_stopped)
 +        ST_Stop();
 +
 +    ST_initData();
 +    ST_createWidgets();
 +    st_stopped = false;
 +}
 +
 +void ST_Stop (void)
 +{
 +    if (st_stopped)
 +	return;
 +
 +    I_SetPalette (W_CacheLumpNum (lu_palette, PU_CACHE));
 +
 +    st_stopped = true;
 +}
 +
 +void ST_Init (void)
 +{
 +    ST_loadData();
 +    st_backing_screen = (byte *) Z_Malloc(ST_WIDTH * ST_HEIGHT, PU_STATIC, 0);
 +}
 +
diff --cc src/strife/wi_stuff.c
index 1ae8c9e2,00000000..f322e31f
mode 100644,000000..100644
--- a/src/strife/wi_stuff.c
+++ b/src/strife/wi_stuff.c
@@@ -1,1843 -1,0 +1,1844 @@@
 +// Emacs style mode select   -*- C++ -*-
 +//-----------------------------------------------------------------------------
 +//
 +// Copyright(C) 1993-1996 Id Software, Inc.
 +// Copyright(C) 2005 Simon Howard
 +//
 +// This program is free software; you can redistribute it and/or
 +// modify it under the terms of the GNU General Public License
 +// as published by the Free Software Foundation; either version 2
 +// of the License, or (at your option) any later version.
 +//
 +// This program is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with this program; if not, write to the Free Software
 +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 +// 02111-1307, USA.
 +//
 +// DESCRIPTION:
 +//	Intermission screens.
 +//
 +//-----------------------------------------------------------------------------
 +
 +// haleyjd 08/23/2010: There is no intermission in Strife
 +#if 0
 +#include <stdio.h>
 +
 +#include "z_zone.h"
 +
 +#include "m_random.h"
 +
 +#include "deh_main.h"
 +#include "i_swap.h"
 +#include "i_system.h"
 +
 +#include "w_wad.h"
 +
 +#include "g_game.h"
 +
 +#include "r_local.h"
 +#include "s_sound.h"
 +
 +#include "doomstat.h"
 +
 +// Data.
 +#include "sounds.h"
 +
 +// Needs access to LFB.
 +#include "v_video.h"
 +
 +#include "wi_stuff.h"
 +
 +//
 +// Data needed to add patches to full screen intermission pics.
 +// Patches are statistics messages, and animations.
 +// Loads of by-pixel layout and placement, offsets etc.
 +//
 +
 +
 +//
 +// Different vetween registered DOOM (1994) and
 +//  Ultimate DOOM - Final edition (retail, 1995?).
 +// This is supposedly ignored for commercial
 +//  release (aka DOOM II), which had 34 maps
 +//  in one episode. So there.
 +#define NUMEPISODES	4
 +#define NUMMAPS		9
 +
 +
 +// in tics
 +//U #define PAUSELEN		(TICRATE*2)
 +//U #define SCORESTEP		100
 +//U #define ANIMPERIOD		32
 +// pixel distance from "(YOU)" to "PLAYER N"
 +//U #define STARDIST		10
 +//U #define WK 1
 +
 +
 +// GLOBAL LOCATIONS
 +#define WI_TITLEY		2
 +#define WI_SPACINGY    		33
 +
 +// SINGPLE-PLAYER STUFF
 +#define SP_STATSX		50
 +#define SP_STATSY		50
 +
 +#define SP_TIMEX		16
 +#define SP_TIMEY		(SCREENHEIGHT-32)
 +
 +
 +// NET GAME STUFF
 +#define NG_STATSY		50
 +#define NG_STATSX		(32 + SHORT(star->width)/2 + 32*!dofrags)
 +
 +#define NG_SPACINGX    		64
 +
 +
 +// DEATHMATCH STUFF
 +#define DM_MATRIXX		42
 +#define DM_MATRIXY		68
 +
 +#define DM_SPACINGX		40
 +
 +#define DM_TOTALSX		269
 +
 +#define DM_KILLERSX		10
 +#define DM_KILLERSY		100
 +#define DM_VICTIMSX    		5
 +#define DM_VICTIMSY		50
 +
 +
 +
 +
 +typedef enum
 +{
 +    ANIM_ALWAYS,
 +    ANIM_RANDOM,
 +    ANIM_LEVEL
 +
 +} animenum_t;
 +
 +typedef struct
 +{
 +    int		x;
 +    int		y;
 +
 +} point_t;
 +
 +
 +//
 +// Animation.
 +// There is another anim_t used in p_spec.
 +//
 +typedef struct
 +{
 +    animenum_t	type;
 +
 +    // period in tics between animations
 +    int		period;
 +
 +    // number of animation frames
 +    int		nanims;
 +
 +    // location of animation
 +    point_t	loc;
 +
 +    // ALWAYS: n/a,
 +    // RANDOM: period deviation (<256),
 +    // LEVEL: level
 +    int		data1;
 +
 +    // ALWAYS: n/a,
 +    // RANDOM: random base period,
 +    // LEVEL: n/a
 +    int		data2;
 +
 +    // actual graphics for frames of animations
 +    patch_t*	p[3];
 +
 +    // following must be initialized to zero before use!
 +
 +    // next value of bcnt (used in conjunction with period)
 +    int		nexttic;
 +
 +    // last drawn animation frame
 +    int		lastdrawn;
 +
 +    // next frame number to animate
 +    int		ctr;
 +
 +    // used by RANDOM and LEVEL when animating
 +    int		state;
 +
 +} anim_t;
 +
 +
 +static point_t lnodes[NUMEPISODES][NUMMAPS] =
 +{
 +    // Episode 0 World Map
 +    {
 +	{ 185, 164 },	// location of level 0 (CJ)
 +	{ 148, 143 },	// location of level 1 (CJ)
 +	{ 69, 122 },	// location of level 2 (CJ)
 +	{ 209, 102 },	// location of level 3 (CJ)
 +	{ 116, 89 },	// location of level 4 (CJ)
 +	{ 166, 55 },	// location of level 5 (CJ)
 +	{ 71, 56 },	// location of level 6 (CJ)
 +	{ 135, 29 },	// location of level 7 (CJ)
 +	{ 71, 24 }	// location of level 8 (CJ)
 +    },
 +
 +    // Episode 1 World Map should go here
 +    {
 +	{ 254, 25 },	// location of level 0 (CJ)
 +	{ 97, 50 },	// location of level 1 (CJ)
 +	{ 188, 64 },	// location of level 2 (CJ)
 +	{ 128, 78 },	// location of level 3 (CJ)
 +	{ 214, 92 },	// location of level 4 (CJ)
 +	{ 133, 130 },	// location of level 5 (CJ)
 +	{ 208, 136 },	// location of level 6 (CJ)
 +	{ 148, 140 },	// location of level 7 (CJ)
 +	{ 235, 158 }	// location of level 8 (CJ)
 +    },
 +
 +    // Episode 2 World Map should go here
 +    {
 +	{ 156, 168 },	// location of level 0 (CJ)
 +	{ 48, 154 },	// location of level 1 (CJ)
 +	{ 174, 95 },	// location of level 2 (CJ)
 +	{ 265, 75 },	// location of level 3 (CJ)
 +	{ 130, 48 },	// location of level 4 (CJ)
 +	{ 279, 23 },	// location of level 5 (CJ)
 +	{ 198, 48 },	// location of level 6 (CJ)
 +	{ 140, 25 },	// location of level 7 (CJ)
 +	{ 281, 136 }	// location of level 8 (CJ)
 +    }
 +
 +};
 +
 +
 +//
 +// Animation locations for episode 0 (1).
 +// Using patches saves a lot of space,
 +//  as they replace 320x200 full screen frames.
 +//
 +
 +#define ANIM(type, period, nanims, x, y, nexttic)            \
 +   { (type), (period), (nanims), { (x), (y) }, (nexttic),    \
 +     0, { NULL, NULL, NULL }, 0, 0, 0, 0 }
 +
 +
 +static anim_t epsd0animinfo[] =
 +{
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 224, 104, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 184, 160, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 112, 136, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 72, 112, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 88, 96, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 48, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 192, 40, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 136, 16, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 80, 16, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 24, 0),
 +};
 +
 +static anim_t epsd1animinfo[] =
 +{
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 1),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 2),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 3),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 4),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 5),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 6),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 7),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 3, 192, 144, 8),
 +    ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 8),
 +};
 +
 +static anim_t epsd2animinfo[] =
 +{
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 168, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 40, 136, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 160, 96, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 80, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/3, 3, 120, 32, 0),
 +    ANIM(ANIM_ALWAYS, TICRATE/4, 3, 40, 0, 0),
 +};
 +
 +static int NUMANIMS[NUMEPISODES] =
 +{
 +    arrlen(epsd0animinfo),
 +    arrlen(epsd1animinfo),
 +    arrlen(epsd2animinfo),
 +};
 +
 +static anim_t *anims[NUMEPISODES] =
 +{
 +    epsd0animinfo,
 +    epsd1animinfo,
 +    epsd2animinfo
 +};
 +
 +
 +//
 +// GENERAL DATA
 +//
 +
 +//
 +// Locally used stuff.
 +//
 +
 +// States for single-player
 +#define SP_KILLS		0
 +#define SP_ITEMS		2
 +#define SP_SECRET		4
 +#define SP_FRAGS		6
 +#define SP_TIME			8
 +#define SP_PAR			ST_TIME
 +
 +#define SP_PAUSE		1
 +
 +// in seconds
 +#define SHOWNEXTLOCDELAY	4
 +//#define SHOWLASTLOCDELAY	SHOWNEXTLOCDELAY
 +
 +
 +// used to accelerate or skip a stage
 +static int		acceleratestage;
 +
 +// wbs->pnum
 +static int		me;
 +
 + // specifies current state
 +static stateenum_t	state;
 +
 +// contains information passed into intermission
 +static wbstartstruct_t*	wbs;
 +
 +static wbplayerstruct_t* plrs;  // wbs->plyr[]
 +
 +// used for general timing
 +static int 		cnt;
 +
 +// used for timing of background animation
 +static int 		bcnt;
 +
 +// signals to refresh everything for one frame
 +static int 		firstrefresh;
 +
 +static int		cnt_kills[MAXPLAYERS];
 +static int		cnt_items[MAXPLAYERS];
 +static int		cnt_secret[MAXPLAYERS];
 +static int		cnt_time;
 +static int		cnt_par;
 +static int		cnt_pause;
 +
 +// # of commercial levels
 +static int		NUMCMAPS;
 +
 +
 +//
 +//	GRAPHICS
 +//
 +
 +// You Are Here graphic
 +static patch_t*		yah[3] = { NULL, NULL, NULL };
 +
 +// splat
 +static patch_t*		splat[2] = { NULL, NULL };
 +
 +// %, : graphics
 +static patch_t*		percent;
 +static patch_t*		colon;
 +
 +// 0-9 graphic
 +static patch_t*		num[10];
 +
 +// minus sign
 +static patch_t*		wiminus;
 +
 +// "Finished!" graphics
 +static patch_t*		finished;
 +
 +// "Entering" graphic
 +static patch_t*		entering;
 +
 +// "secret"
 +static patch_t*		sp_secret;
 +
 + // "Kills", "Scrt", "Items", "Frags"
 +static patch_t*		kills;
 +static patch_t*		secret;
 +static patch_t*		items;
 +static patch_t*		frags;
 +
 +// Time sucks.
 +static patch_t*		timepatch;
 +static patch_t*		par;
 +static patch_t*		sucks;
 +
 +// "killers", "victims"
 +static patch_t*		killers;
 +static patch_t*		victims;
 +
 +// "Total", your face, your dead face
 +static patch_t*		total;
 +static patch_t*		star;
 +static patch_t*		bstar;
 +
 +// "red P[1..MAXPLAYERS]"
 +static patch_t*		p[MAXPLAYERS];
 +
 +// "gray P[1..MAXPLAYERS]"
 +static patch_t*		bp[MAXPLAYERS];
 +
 + // Name graphics of each level (centered)
 +static patch_t**	lnames;
 +
 +// Buffer storing the backdrop
 +static patch_t *background;
 +
 +//
 +// CODE
 +//
 +
 +// slam background
 +void WI_slamBackground(void)
 +{
 +    V_DrawPatch(0, 0, background);
 +}
 +
 +// The ticker is used to detect keys
 +//  because of timing issues in netgames.
 +boolean WI_Responder(event_t* ev)
 +{
 +    return false;
 +}
 +
 +
 +// Draws "<Levelname> Finished!"
 +void WI_drawLF(void)
 +{
 +    int y = WI_TITLEY;
 +
 +    if (gamemode != commercial || wbs->last < NUMCMAPS)
 +    {
 +        // draw <LevelName>
 +        V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width))/2,
 +                    y, lnames[wbs->last]);
 +
 +        // draw "Finished!"
 +        y += (5*SHORT(lnames[wbs->last]->height))/4;
 +
 +        V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished);
 +    }
 +    else if (wbs->last == NUMCMAPS)
 +    {
 +        // MAP33 - nothing is displayed!
 +    }
 +    else if (wbs->last > NUMCMAPS)
 +    {
 +        // > MAP33.  Doom bombs out here with a Bad V_DrawPatch error.
 +        // I'm pretty sure that doom2.exe is just reading into random
 +        // bits of memory at this point, but let's try to be accurate
 +        // anyway.  This deliberately triggers a V_DrawPatch error.
 +
 +        patch_t tmp = { SCREENWIDTH, SCREENHEIGHT, 1, 1,
 +                        { 0, 0, 0, 0, 0, 0, 0, 0 } };
 +
 +        V_DrawPatch(0, y, &tmp);
 +    }
 +}
 +
 +
 +
 +// Draws "Entering <LevelName>"
 +void WI_drawEL(void)
 +{
 +    int y = WI_TITLEY;
 +
 +    // draw "Entering"
 +    V_DrawPatch((SCREENWIDTH - SHORT(entering->width))/2,
 +		y,
 +                entering);
 +
 +    // draw level
 +    y += (5*SHORT(lnames[wbs->next]->height))/4;
 +
 +    V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width))/2,
 +		y,
 +                lnames[wbs->next]);
 +
 +}
 +
 +void
 +WI_drawOnLnode
 +( int		n,
 +  patch_t*	c[] )
 +{
 +
 +    int		i;
 +    int		left;
 +    int		top;
 +    int		right;
 +    int		bottom;
 +    boolean	fits = false;
 +
 +    i = 0;
 +    do
 +    {
 +	left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset);
 +	top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset);
 +	right = left + SHORT(c[i]->width);
 +	bottom = top + SHORT(c[i]->height);
 +
 +	if (left >= 0
 +	    && right < SCREENWIDTH
 +	    && top >= 0
 +	    && bottom < SCREENHEIGHT)
 +	{
 +	    fits = true;
 +	}
 +	else
 +	{
 +	    i++;
 +	}
 +    } while (!fits && i!=2 && c[i] != NULL);
 +
 +    if (fits && i<2)
 +    {
 +	V_DrawPatch(lnodes[wbs->epsd][n].x,
 +                    lnodes[wbs->epsd][n].y,
 +		    c[i]);
 +    }
 +    else
 +    {
 +	// DEBUG
 +	printf("Could not place patch on level %d", n+1);
 +    }
 +}
 +
 +
 +
 +void WI_initAnimatedBack(void)
 +{
 +    int		i;
 +    anim_t*	a;
 +
 +    if (gamemode == commercial)
 +	return;
 +
 +    if (wbs->epsd > 2)
 +	return;
 +
 +    for (i=0;i<NUMANIMS[wbs->epsd];i++)
 +    {
 +	a = &anims[wbs->epsd][i];
 +
 +	// init variables
 +	a->ctr = -1;
 +
 +	// specify the next time to draw it
 +	if (a->type == ANIM_ALWAYS)
 +	    a->nexttic = bcnt + 1 + (M_Random()%a->period);
 +	else if (a->type == ANIM_RANDOM)
 +	    a->nexttic = bcnt + 1 + a->data2+(M_Random()%a->data1);
 +	else if (a->type == ANIM_LEVEL)
 +	    a->nexttic = bcnt + 1;
 +    }
 +
 +}
 +
 +void WI_updateAnimatedBack(void)
 +{
 +    int		i;
 +    anim_t*	a;
 +
 +    if (gamemode == commercial)
 +	return;
 +
 +    if (wbs->epsd > 2)
 +	return;
 +
 +    for (i=0;i<NUMANIMS[wbs->epsd];i++)
 +    {
 +	a = &anims[wbs->epsd][i];
 +
 +	if (bcnt == a->nexttic)
 +	{
 +	    switch (a->type)
 +	    {
 +	      case ANIM_ALWAYS:
 +		if (++a->ctr >= a->nanims) a->ctr = 0;
 +		a->nexttic = bcnt + a->period;
 +		break;
 +
 +	      case ANIM_RANDOM:
 +		a->ctr++;
 +		if (a->ctr == a->nanims)
 +		{
 +		    a->ctr = -1;
 +		    a->nexttic = bcnt+a->data2+(M_Random()%a->data1);
 +		}
 +		else a->nexttic = bcnt + a->period;
 +		break;
 +
 +	      case ANIM_LEVEL:
 +		// gawd-awful hack for level anims
 +		if (!(state == StatCount && i == 7)
 +		    && wbs->next == a->data1)
 +		{
 +		    a->ctr++;
 +		    if (a->ctr == a->nanims) a->ctr--;
 +		    a->nexttic = bcnt + a->period;
 +		}
 +		break;
 +	    }
 +	}
 +
 +    }
 +
 +}
 +
 +void WI_drawAnimatedBack(void)
 +{
 +    int			i;
 +    anim_t*		a;
 +
 +    if (gamemode == commercial)
 +	return;
 +
 +    if (wbs->epsd > 2)
 +	return;
 +
 +    for (i=0 ; i<NUMANIMS[wbs->epsd] ; i++)
 +    {
 +	a = &anims[wbs->epsd][i];
 +
 +	if (a->ctr >= 0)
 +	    V_DrawPatch(a->loc.x, a->loc.y, a->p[a->ctr]);
 +    }
 +
 +}
 +
 +//
 +// Draws a number.
 +// If digits > 0, then use that many digits minimum,
 +//  otherwise only use as many as necessary.
 +// Returns new x position.
 +//
 +
 +int
 +WI_drawNum
 +( int		x,
 +  int		y,
 +  int		n,
 +  int		digits )
 +{
 +
 +    int		fontwidth = SHORT(num[0]->width);
 +    int		neg;
 +    int		temp;
 +
 +    if (digits < 0)
 +    {
 +	if (!n)
 +	{
 +	    // make variable-length zeros 1 digit long
 +	    digits = 1;
 +	}
 +	else
 +	{
 +	    // figure out # of digits in #
 +	    digits = 0;
 +	    temp = n;
 +
 +	    while (temp)
 +	    {
 +		temp /= 10;
 +		digits++;
 +	    }
 +	}
 +    }
 +
 +    neg = n < 0;
 +    if (neg)
 +	n = -n;
 +
 +    // if non-number, do not draw it
 +    if (n == 1994)
 +	return 0;
 +
 +    // draw the new number
 +    while (digits--)
 +    {
 +	x -= fontwidth;
 +	V_DrawPatch(x, y, num[ n % 10 ]);
 +	n /= 10;
 +    }
 +
 +    // draw a minus sign if necessary
 +    if (neg)
 +	V_DrawPatch(x-=8, y, wiminus);
 +
 +    return x;
 +
 +}
 +
 +void
 +WI_drawPercent
 +( int		x,
 +  int		y,
 +  int		p )
 +{
 +    if (p < 0)
 +	return;
 +
 +    V_DrawPatch(x, y, percent);
 +    WI_drawNum(x, y, p, -1);
 +}
 +
 +
 +
 +//
 +// Display level completion time and par,
 +//  or "sucks" message if overflow.
 +//
 +void
 +WI_drawTime
 +( int		x,
 +  int		y,
 +  int		t )
 +{
 +
 +    int		div;
 +    int		n;
 +
 +    if (t<0)
 +	return;
 +
 +    if (t <= 61*59)
 +    {
 +	div = 1;
 +
 +	do
 +	{
 +	    n = (t / div) % 60;
 +	    x = WI_drawNum(x, y, n, 2) - SHORT(colon->width);
 +	    div *= 60;
 +
 +	    // draw
 +	    if (div==60 || t / div)
 +		V_DrawPatch(x, y, colon);
 +
 +	} while (t / div);
 +    }
 +    else
 +    {
 +	// "sucks"
 +	V_DrawPatch(x - SHORT(sucks->width), y, sucks);
 +    }
 +}
 +
 +
 +void WI_End(void)
 +{
 +    void WI_unloadData(void);
 +    WI_unloadData();
 +}
 +
 +void WI_initNoState(void)
 +{
 +    state = NoState;
 +    acceleratestage = 0;
 +    cnt = 10;
 +}
 +
 +void WI_updateNoState(void) {
 +
 +    WI_updateAnimatedBack();
 +
 +    if (!--cnt)
 +    {
 +        // Don't call WI_End yet.  G_WorldDone doesnt immediately
 +        // change gamestate, so WI_Drawer is still going to get
 +        // run until that happens.  If we do that after WI_End
 +        // (which unloads all the graphics), we're in trouble.
 +	//WI_End();
 +	G_WorldDone();
 +    }
 +
 +}
 +
 +static boolean		snl_pointeron = false;
 +
 +
 +void WI_initShowNextLoc(void)
 +{
 +    state = ShowNextLoc;
 +    acceleratestage = 0;
 +    cnt = SHOWNEXTLOCDELAY * TICRATE;
 +
 +    WI_initAnimatedBack();
 +}
 +
 +void WI_updateShowNextLoc(void)
 +{
 +    WI_updateAnimatedBack();
 +
 +    if (!--cnt || acceleratestage)
 +	WI_initNoState();
 +    else
 +	snl_pointeron = (cnt & 31) < 20;
 +}
 +
 +void WI_drawShowNextLoc(void)
 +{
 +
 +    int		i;
 +    int		last;
 +
 +    WI_slamBackground();
 +
 +    // draw animated background
 +    WI_drawAnimatedBack();
 +
 +    if ( gamemode != commercial)
 +    {
 +  	if (wbs->epsd > 2)
 +	{
 +	    WI_drawEL();
 +	    return;
 +	}
 +
 +	last = (wbs->last == 8) ? wbs->next - 1 : wbs->last;
 +
 +	// draw a splat on taken cities.
 +	for (i=0 ; i<=last ; i++)
 +	    WI_drawOnLnode(i, splat);
 +
 +	// splat the secret level?
 +	if (wbs->didsecret)
 +	    WI_drawOnLnode(8, splat);
 +
 +	// draw flashing ptr
 +	if (snl_pointeron)
 +	    WI_drawOnLnode(wbs->next, yah);
 +    }
 +
 +    // draws which level you are entering..
 +    if ( (gamemode != commercial)
 +	 || wbs->next != 30)
 +	WI_drawEL();
 +
 +}
 +
 +void WI_drawNoState(void)
 +{
 +    snl_pointeron = true;
 +    WI_drawShowNextLoc();
 +}
 +
 +int WI_fragSum(int playernum)
 +{
 +    int		i;
 +    int		frags = 0;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (playeringame[i]
 +	    && i!=playernum)
 +	{
 +	    frags += plrs[playernum].frags[i];
 +	}
 +    }
 +
 +
 +    // JDC hack - negative frags.
 +    frags -= plrs[playernum].frags[playernum];
 +    // UNUSED if (frags < 0)
 +    // 	frags = 0;
 +
 +    return frags;
 +}
 +
 +
 +
 +static int		dm_state;
 +static int		dm_frags[MAXPLAYERS][MAXPLAYERS];
 +static int		dm_totals[MAXPLAYERS];
 +
 +
 +
 +void WI_initDeathmatchStats(void)
 +{
 +
 +    int		i;
 +    int		j;
 +
 +    state = StatCount;
 +    acceleratestage = 0;
 +    dm_state = 1;
 +
 +    cnt_pause = TICRATE;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (playeringame[i])
 +	{
 +	    for (j=0 ; j<MAXPLAYERS ; j++)
 +		if (playeringame[j])
 +		    dm_frags[i][j] = 0;
 +
 +	    dm_totals[i] = 0;
 +	}
 +    }
 +
 +    WI_initAnimatedBack();
 +}
 +
 +
 +
 +void WI_updateDeathmatchStats(void)
 +{
 +
 +    int		i;
 +    int		j;
 +
 +    boolean	stillticking;
 +
 +    WI_updateAnimatedBack();
 +
 +    if (acceleratestage && dm_state != 4)
 +    {
 +	acceleratestage = 0;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (playeringame[i])
 +	    {
 +		for (j=0 ; j<MAXPLAYERS ; j++)
 +		    if (playeringame[j])
 +			dm_frags[i][j] = plrs[i].frags[j];
 +
 +		dm_totals[i] = WI_fragSum(i);
 +	    }
 +	}
 +
 +
 +	S_StartSound(0, sfx_barexp);
 +	dm_state = 4;
 +    }
 +
 +
 +    if (dm_state == 2)
 +    {
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	stillticking = false;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (playeringame[i])
 +	    {
 +		for (j=0 ; j<MAXPLAYERS ; j++)
 +		{
 +		    if (playeringame[j]
 +			&& dm_frags[i][j] != plrs[i].frags[j])
 +		    {
 +			if (plrs[i].frags[j] < 0)
 +			    dm_frags[i][j]--;
 +			else
 +			    dm_frags[i][j]++;
 +
 +			if (dm_frags[i][j] > 99)
 +			    dm_frags[i][j] = 99;
 +
 +			if (dm_frags[i][j] < -99)
 +			    dm_frags[i][j] = -99;
 +
 +			stillticking = true;
 +		    }
 +		}
 +		dm_totals[i] = WI_fragSum(i);
 +
 +		if (dm_totals[i] > 99)
 +		    dm_totals[i] = 99;
 +
 +		if (dm_totals[i] < -99)
 +		    dm_totals[i] = -99;
 +	    }
 +
 +	}
 +	if (!stillticking)
 +	{
 +	    S_StartSound(0, sfx_barexp);
 +	    dm_state++;
 +	}
 +
 +    }
 +    else if (dm_state == 4)
 +    {
 +	if (acceleratestage)
 +	{
 +	    S_StartSound(0, sfx_slop);
 +
 +	    if ( gamemode == commercial)
 +		WI_initNoState();
 +	    else
 +		WI_initShowNextLoc();
 +	}
 +    }
 +    else if (dm_state & 1)
 +    {
 +	if (!--cnt_pause)
 +	{
 +	    dm_state++;
 +	    cnt_pause = TICRATE;
 +	}
 +    }
 +}
 +
 +
 +
 +void WI_drawDeathmatchStats(void)
 +{
 +
 +    int		i;
 +    int		j;
 +    int		x;
 +    int		y;
 +    int		w;
 +
 +    int		lh;	// line height
 +
 +    lh = WI_SPACINGY;
 +
 +    WI_slamBackground();
 +
 +    // draw animated background
 +    WI_drawAnimatedBack();
 +    WI_drawLF();
 +
 +    // draw stat titles (top line)
 +    V_DrawPatch(DM_TOTALSX-SHORT(total->width)/2,
 +		DM_MATRIXY-WI_SPACINGY+10,
 +		total);
 +
 +    V_DrawPatch(DM_KILLERSX, DM_KILLERSY, killers);
 +    V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, victims);
 +
 +    // draw P?
 +    x = DM_MATRIXX + DM_SPACINGX;
 +    y = DM_MATRIXY;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (playeringame[i])
 +	{
 +	    V_DrawPatch(x-SHORT(p[i]->width)/2,
 +			DM_MATRIXY - WI_SPACINGY,
 +			p[i]);
 +
 +	    V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2,
 +			y,
 +			p[i]);
 +
 +	    if (i == me)
 +	    {
 +		V_DrawPatch(x-SHORT(p[i]->width)/2,
 +			    DM_MATRIXY - WI_SPACINGY,
 +			    bstar);
 +
 +		V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2,
 +			    y,
 +			    star);
 +	    }
 +	}
 +	else
 +	{
 +	    // V_DrawPatch(x-SHORT(bp[i]->width)/2,
 +	    //   DM_MATRIXY - WI_SPACINGY, bp[i]);
 +	    // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2,
 +	    //   y, bp[i]);
 +	}
 +	x += DM_SPACINGX;
 +	y += WI_SPACINGY;
 +    }
 +
 +    // draw stats
 +    y = DM_MATRIXY+10;
 +    w = SHORT(num[0]->width);
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	x = DM_MATRIXX + DM_SPACINGX;
 +
 +	if (playeringame[i])
 +	{
 +	    for (j=0 ; j<MAXPLAYERS ; j++)
 +	    {
 +		if (playeringame[j])
 +		    WI_drawNum(x+w, y, dm_frags[i][j], 2);
 +
 +		x += DM_SPACINGX;
 +	    }
 +	    WI_drawNum(DM_TOTALSX+w, y, dm_totals[i], 2);
 +	}
 +	y += WI_SPACINGY;
 +    }
 +}
 +
 +static int	cnt_frags[MAXPLAYERS];
 +static int	dofrags;
 +static int	ng_state;
 +
 +void WI_initNetgameStats(void)
 +{
 +
 +    int i;
 +
 +    state = StatCount;
 +    acceleratestage = 0;
 +    ng_state = 1;
 +
 +    cnt_pause = TICRATE;
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (!playeringame[i])
 +	    continue;
 +
 +	cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0;
 +
 +	dofrags += WI_fragSum(i);
 +    }
 +
 +    dofrags = !!dofrags;
 +
 +    WI_initAnimatedBack();
 +}
 +
 +
 +
 +void WI_updateNetgameStats(void)
 +{
 +
 +    int		i;
 +    int		fsum;
 +
 +    boolean	stillticking;
 +
 +    WI_updateAnimatedBack();
 +
 +    if (acceleratestage && ng_state != 10)
 +    {
 +	acceleratestage = 0;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (!playeringame[i])
 +		continue;
 +
 +	    cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;
 +	    cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;
 +	    cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret;
 +
 +	    if (dofrags)
 +		cnt_frags[i] = WI_fragSum(i);
 +	}
 +	S_StartSound(0, sfx_barexp);
 +	ng_state = 10;
 +    }
 +
 +    if (ng_state == 2)
 +    {
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	stillticking = false;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (!playeringame[i])
 +		continue;
 +
 +	    cnt_kills[i] += 2;
 +
 +	    if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills)
 +		cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;
 +	    else
 +		stillticking = true;
 +	}
 +
 +	if (!stillticking)
 +	{
 +	    S_StartSound(0, sfx_barexp);
 +	    ng_state++;
 +	}
 +    }
 +    else if (ng_state == 4)
 +    {
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	stillticking = false;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (!playeringame[i])
 +		continue;
 +
 +	    cnt_items[i] += 2;
 +	    if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems)
 +		cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;
 +	    else
 +		stillticking = true;
 +	}
 +	if (!stillticking)
 +	{
 +	    S_StartSound(0, sfx_barexp);
 +	    ng_state++;
 +	}
 +    }
 +    else if (ng_state == 6)
 +    {
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	stillticking = false;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (!playeringame[i])
 +		continue;
 +
 +	    cnt_secret[i] += 2;
 +
 +	    if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs->maxsecret)
 +		cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret;
 +	    else
 +		stillticking = true;
 +	}
 +
 +	if (!stillticking)
 +	{
 +	    S_StartSound(0, sfx_barexp);
 +	    ng_state += 1 + 2*!dofrags;
 +	}
 +    }
 +    else if (ng_state == 8)
 +    {
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	stillticking = false;
 +
 +	for (i=0 ; i<MAXPLAYERS ; i++)
 +	{
 +	    if (!playeringame[i])
 +		continue;
 +
 +	    cnt_frags[i] += 1;
 +
 +	    if (cnt_frags[i] >= (fsum = WI_fragSum(i)))
 +		cnt_frags[i] = fsum;
 +	    else
 +		stillticking = true;
 +	}
 +
 +	if (!stillticking)
 +	{
 +	    S_StartSound(0, sfx_pldeth);
 +	    ng_state++;
 +	}
 +    }
 +    else if (ng_state == 10)
 +    {
 +	if (acceleratestage)
 +	{
 +	    S_StartSound(0, sfx_sgcock);
 +	    if ( gamemode == commercial )
 +		WI_initNoState();
 +	    else
 +		WI_initShowNextLoc();
 +	}
 +    }
 +    else if (ng_state & 1)
 +    {
 +	if (!--cnt_pause)
 +	{
 +	    ng_state++;
 +	    cnt_pause = TICRATE;
 +	}
 +    }
 +}
 +
 +
 +
 +void WI_drawNetgameStats(void)
 +{
 +    int		i;
 +    int		x;
 +    int		y;
 +    int		pwidth = SHORT(percent->width);
 +
 +    WI_slamBackground();
 +
 +    // draw animated background
 +    WI_drawAnimatedBack();
 +
 +    WI_drawLF();
 +
 +    // draw stat titles (top line)
 +    V_DrawPatch(NG_STATSX+NG_SPACINGX-SHORT(kills->width),
 +		NG_STATSY, kills);
 +
 +    V_DrawPatch(NG_STATSX+2*NG_SPACINGX-SHORT(items->width),
 +		NG_STATSY, items);
 +
 +    V_DrawPatch(NG_STATSX+3*NG_SPACINGX-SHORT(secret->width),
 +		NG_STATSY, secret);
 +
 +    if (dofrags)
 +	V_DrawPatch(NG_STATSX+4*NG_SPACINGX-SHORT(frags->width),
 +		    NG_STATSY, frags);
 +
 +    // draw stats
 +    y = NG_STATSY + SHORT(kills->height);
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	if (!playeringame[i])
 +	    continue;
 +
 +	x = NG_STATSX;
 +	V_DrawPatch(x-SHORT(p[i]->width), y, p[i]);
 +
 +	if (i == me)
 +	    V_DrawPatch(x-SHORT(p[i]->width), y, star);
 +
 +	x += NG_SPACINGX;
 +	WI_drawPercent(x-pwidth, y+10, cnt_kills[i]);	x += NG_SPACINGX;
 +	WI_drawPercent(x-pwidth, y+10, cnt_items[i]);	x += NG_SPACINGX;
 +	WI_drawPercent(x-pwidth, y+10, cnt_secret[i]);	x += NG_SPACINGX;
 +
 +	if (dofrags)
 +	    WI_drawNum(x, y+10, cnt_frags[i], -1);
 +
 +	y += WI_SPACINGY;
 +    }
 +
 +}
 +
 +static int	sp_state;
 +
 +void WI_initStats(void)
 +{
 +    state = StatCount;
 +    acceleratestage = 0;
 +    sp_state = 1;
 +    cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;
 +    cnt_time = cnt_par = -1;
 +    cnt_pause = TICRATE;
 +
 +    WI_initAnimatedBack();
 +}
 +
 +void WI_updateStats(void)
 +{
 +
 +    WI_updateAnimatedBack();
 +
 +    if (acceleratestage && sp_state != 10)
 +    {
 +	acceleratestage = 0;
 +	cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;
 +	cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;
 +	cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret;
 +	cnt_time = plrs[me].stime / TICRATE;
 +	cnt_par = wbs->partime / TICRATE;
 +	S_StartSound(0, sfx_barexp);
 +	sp_state = 10;
 +    }
 +
 +    if (sp_state == 2)
 +    {
 +	cnt_kills[0] += 2;
 +
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills)
 +	{
 +	    cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;
 +	    S_StartSound(0, sfx_barexp);
 +	    sp_state++;
 +	}
 +    }
 +    else if (sp_state == 4)
 +    {
 +	cnt_items[0] += 2;
 +
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems)
 +	{
 +	    cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;
 +	    S_StartSound(0, sfx_barexp);
 +	    sp_state++;
 +	}
 +    }
 +    else if (sp_state == 6)
 +    {
 +	cnt_secret[0] += 2;
 +
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret)
 +	{
 +	    cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret;
 +	    S_StartSound(0, sfx_barexp);
 +	    sp_state++;
 +	}
 +    }
 +
 +    else if (sp_state == 8)
 +    {
 +	if (!(bcnt&3))
 +	    S_StartSound(0, sfx_pistol);
 +
 +	cnt_time += 3;
 +
 +	if (cnt_time >= plrs[me].stime / TICRATE)
 +	    cnt_time = plrs[me].stime / TICRATE;
 +
 +	cnt_par += 3;
 +
 +	if (cnt_par >= wbs->partime / TICRATE)
 +	{
 +	    cnt_par = wbs->partime / TICRATE;
 +
 +	    if (cnt_time >= plrs[me].stime / TICRATE)
 +	    {
 +		S_StartSound(0, sfx_barexp);
 +		sp_state++;
 +	    }
 +	}
 +    }
 +    else if (sp_state == 10)
 +    {
 +	if (acceleratestage)
 +	{
 +	    S_StartSound(0, sfx_sgcock);
 +
 +	    if (gamemode == commercial)
 +		WI_initNoState();
 +	    else
 +		WI_initShowNextLoc();
 +	}
 +    }
 +    else if (sp_state & 1)
 +    {
 +	if (!--cnt_pause)
 +	{
 +	    sp_state++;
 +	    cnt_pause = TICRATE;
 +	}
 +    }
 +
 +}
 +
 +void WI_drawStats(void)
 +{
 +    // line height
 +    int lh;
 +
 +    lh = (3*SHORT(num[0]->height))/2;
 +
 +    WI_slamBackground();
 +
 +    // draw animated background
 +    WI_drawAnimatedBack();
 +
 +    WI_drawLF();
 +
 +    V_DrawPatch(SP_STATSX, SP_STATSY, kills);
 +    WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]);
 +
 +    V_DrawPatch(SP_STATSX, SP_STATSY+lh, items);
 +    WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]);
 +
 +    V_DrawPatch(SP_STATSX, SP_STATSY+2*lh, sp_secret);
 +    WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]);
 +
 +    V_DrawPatch(SP_TIMEX, SP_TIMEY, timepatch);
 +    WI_drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time);
 +
 +    if (wbs->epsd < 3)
 +    {
 +	V_DrawPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, par);
 +	WI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par);
 +    }
 +
 +}
 +
 +void WI_checkForAccelerate(void)
 +{
 +    int   i;
 +    player_t  *player;
 +
 +    // check for button presses to skip delays
 +    for (i=0, player = players ; i<MAXPLAYERS ; i++, player++)
 +    {
 +	if (playeringame[i])
 +	{
 +	    if (player->cmd.buttons & BT_ATTACK)
 +	    {
 +		if (!player->attackdown)
 +		    acceleratestage = 1;
 +		player->attackdown = true;
 +	    }
 +	    else
 +		player->attackdown = false;
 +	    if (player->cmd.buttons & BT_USE)
 +	    {
 +		if (!player->usedown)
 +		    acceleratestage = 1;
 +		player->usedown = true;
 +	    }
 +	    else
 +		player->usedown = false;
 +	}
 +    }
 +}
 +
 +
 +
 +// Updates stuff each tick
 +void WI_Ticker(void)
 +{
 +    // counter for general background animation
 +    bcnt++;
 +
 +    if (bcnt == 1)
 +    {
 +	// intermission music
 +  	if ( gamemode == commercial )
 +	  S_ChangeMusic(mus_dm2int, true);
 +	else
 +	  S_ChangeMusic(mus_inter, true);
 +    }
 +
 +    WI_checkForAccelerate();
 +
 +    switch (state)
 +    {
 +      case StatCount:
 +	if (deathmatch) WI_updateDeathmatchStats();
 +	else if (netgame) WI_updateNetgameStats();
 +	else WI_updateStats();
 +	break;
 +
 +      case ShowNextLoc:
 +	WI_updateShowNextLoc();
 +	break;
 +
 +      case NoState:
 +	WI_updateNoState();
 +	break;
 +    }
 +
 +}
 +
 +typedef void (*load_callback_t)(char *lumpname, patch_t **variable);
 +
 +// Common load/unload function.  Iterates over all the graphics
 +// lumps to be loaded/unloaded into memory.
 +
 +static void WI_loadUnloadData(load_callback_t callback)
 +{
 +    int i, j;
 +    char name[9];
 +    anim_t *a;
 +
 +    if (gamemode == commercial)
 +    {
 +	for (i=0 ; i<NUMCMAPS ; i++)
- 	{
- 	    sprintf(name, DEH_String("CWILV%2.2d"), i);
++	{
++	    DEH_snprintf(name, 9, "CWILV%2.2d", i);
 +            callback(name, &lnames[i]);
- 	}
++	}
 +    }
 +    else
 +    {
 +	for (i=0 ; i<NUMMAPS ; i++)
 +	{
- 	    sprintf(name, DEH_String("WILV%d%d"), wbs->epsd, i);
++	    DEH_snprintf(name, 9, "WILV%d%d", wbs->epsd, i);
 +            callback(name, &lnames[i]);
 +	}
 +
 +	// you are here
 +        callback(DEH_String("WIURH0"), &yah[0]);
 +
 +	// you are here (alt.)
 +        callback(DEH_String("WIURH1"), &yah[1]);
 +
 +	// splat
 +        callback(DEH_String("WISPLAT"), &splat[0]);
-
++
 +	if (wbs->epsd < 3)
 +	{
 +	    for (j=0;j<NUMANIMS[wbs->epsd];j++)
 +	    {
 +		a = &anims[wbs->epsd][j];
 +		for (i=0;i<a->nanims;i++)
 +		{
 +		    // MONDO HACK!
- 		    if (wbs->epsd != 1 || j != 8)
++		    if (wbs->epsd != 1 || j != 8)
 +		    {
 +			// animations
- 			sprintf(name, DEH_String("WIA%d%.2d%.2d"),
- 				      wbs->epsd, j, i);
++			DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i);
 +                        callback(name, &a->p[i]);
 +		    }
 +		    else
 +		    {
 +			// HACK ALERT!
- 			a->p[i] = anims[1][4].p[i];
++			a->p[i] = anims[1][4].p[i];
 +		    }
 +		}
 +	    }
 +	}
 +    }
 +
 +    // More hacks on minus sign.
 +    callback(DEH_String("WIMINUS"), &wiminus);
 +
 +    for (i=0;i<10;i++)
 +    {
 +	 // numbers 0-9
- 	sprintf(name, DEH_String("WINUM%d"), i);
++	DEH_snprintf(name, 9, "WINUM%d", i);
 +        callback(name, &num[i]);
 +    }
 +
 +    // percent sign
 +    callback(DEH_String("WIPCNT"), &percent);
 +
 +    // "finished"
 +    callback(DEH_String("WIF"), &finished);
 +
 +    // "entering"
 +    callback(DEH_String("WIENTER"), &entering);
 +
 +    // "kills"
 +    callback(DEH_String("WIOSTK"), &kills);
 +
 +    // "scrt"
 +    callback(DEH_String("WIOSTS"), &secret);
 +
 +     // "secret"
 +    callback(DEH_String("WISCRT2"), &sp_secret);
 +
 +    // french wad uses WIOBJ (?)
 +    if (W_CheckNumForName(DEH_String("WIOBJ")) >= 0)
 +    {
 +    	// "items"
 +    	if (netgame && !deathmatch)
 +            callback(DEH_String("WIOBJ"), &items);
 +    	else
 +            callback(DEH_String("WIOSTI"), &items);
 +    } else {
 +        callback(DEH_String("WIOSTI"), &items);
 +    }
 +
 +    // "frgs"
 +    callback(DEH_String("WIFRGS"), &frags);
 +
 +    // ":"
 +    callback(DEH_String("WICOLON"), &colon);
 +
 +    // "time"
-     callback(DEH_String("WITIME"), &timepatch);
++    callback(DEH_String("WITIME"), &timepatch);
 +
 +    // "sucks"
-     callback(DEH_String("WISUCKS"), &sucks);
++    callback(DEH_String("WISUCKS"), &sucks);
 +
 +    // "par"
-     callback(DEH_String("WIPAR"), &par);
++    callback(DEH_String("WIPAR"), &par);
 +
 +    // "killers" (vertical)
 +    callback(DEH_String("WIKILRS"), &killers);
 +
 +    // "victims" (horiz)
 +    callback(DEH_String("WIVCTMS"), &victims);
 +
 +    // "total"
-     callback(DEH_String("WIMSTT"), &total);
++    callback(DEH_String("WIMSTT"), &total);
 +
 +    for (i=0 ; i<MAXPLAYERS ; i++)
 +    {
 +	// "1,2,3,4"
- 	sprintf(name, DEH_String("STPB%d"), i);
++	DEH_snprintf(name, 9, "STPB%d", i);
 +        callback(name, &p[i]);
 +
 +	// "1,2,3,4"
- 	sprintf(name, DEH_String("WIBP%d"), i+1);
++	DEH_snprintf(name, 9, "WIBP%d", i+1);
 +        callback(name, &bp[i]);
 +    }
 +
 +    // Background image
 +
 +    if (gamemode == commercial)
 +    {
- 	strcpy(name, DEH_String("INTERPIC"));
++	strncpy(name, DEH_String("INTERPIC"), 9);
++        name[8] = '\0';
 +    }
 +    else if (gamemode == retail && wbs->epsd == 3)
 +    {
- 	strcpy(name, DEH_String("INTERPIC"));
++	strncpy(name, DEH_String("INTERPIC"), 9);
++        name[8] = '\0';
 +    }
-     else
++    else
 +    {
- 	sprintf(name, DEH_String("WIMAP%d"), wbs->epsd);
++	DEH_snprintf(name, 9, "WIMAP%d", wbs->epsd);
 +    }
-
++
 +    // Draw backdrop and save to a temporary buffer
-
++
 +    callback(name, &background);
 +}
 +
 +static void WI_loadCallback(char *name, patch_t **variable)
 +{
 +    *variable = W_CacheLumpName(name, PU_STATIC);
 +}
 +
 +void WI_loadData(void)
 +{
 +    if (gamemode == commercial)
 +    {
- 	NUMCMAPS = 32;
++	NUMCMAPS = 32;
 +	lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS,
 +				       PU_STATIC, NULL);
 +    }
 +    else
 +    {
 +	lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMMAPS,
 +				       PU_STATIC, NULL);
 +    }
 +
 +    WI_loadUnloadData(WI_loadCallback);
 +
 +    // These two graphics are special cased because we're sharing
 +    // them with the status bar code
 +
 +    // your face
 +    star = W_CacheLumpName(DEH_String("STFST01"), PU_STATIC);
 +
 +    // dead face
 +    bstar = W_CacheLumpName(DEH_String("STFDEAD0"), PU_STATIC);
 +}
 +
 +static void WI_unloadCallback(char *name, patch_t **variable)
 +{
 +    W_ReleaseLumpName(name);
 +    *variable = NULL;
 +}
 +
 +void WI_unloadData(void)
 +{
 +    WI_loadUnloadData(WI_unloadCallback);
 +
 +    // We do not free these lumps as they are shared with the status
 +    // bar code.
 +
 +    // W_ReleaseLumpName("STFST01");
 +    // W_ReleaseLumpName("STFDEAD0");
 +}
 +
 +void WI_Drawer (void)
 +{
 +    switch (state)
 +    {
 +      case StatCount:
 +	if (deathmatch)
 +	    WI_drawDeathmatchStats();
 +	else if (netgame)
 +	    WI_drawNetgameStats();
 +	else
 +	    WI_drawStats();
 +	break;
 +
 +      case ShowNextLoc:
 +	WI_drawShowNextLoc();
 +	break;
 +
 +      case NoState:
 +	WI_drawNoState();
 +	break;
 +    }
 +}
 +
 +
 +void WI_initVariables(wbstartstruct_t* wbstartstruct)
 +{
 +
 +    wbs = wbstartstruct;
 +
 +#ifdef RANGECHECKING
 +    if (gamemode != commercial)
 +    {
 +      if ( gamemode == retail )
 +	RNGCHECK(wbs->epsd, 0, 3);
 +      else
 +	RNGCHECK(wbs->epsd, 0, 2);
 +    }
 +    else
 +    {
 +	RNGCHECK(wbs->last, 0, 8);
 +	RNGCHECK(wbs->next, 0, 8);
 +    }
 +    RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
 +    RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
 +#endif
 +
 +    acceleratestage = 0;
 +    cnt = bcnt = 0;
 +    firstrefresh = 1;
 +    me = wbs->pnum;
 +    plrs = wbs->plyr;
 +
 +    if (!wbs->maxkills)
 +	wbs->maxkills = 1;
 +
 +    if (!wbs->maxitems)
 +	wbs->maxitems = 1;
 +
 +    if (!wbs->maxsecret)
 +	wbs->maxsecret = 1;
 +
 +    if ( gamemode != retail )
 +      if (wbs->epsd > 2)
 +	wbs->epsd -= 3;
 +}
 +
 +void WI_Start(wbstartstruct_t* wbstartstruct)
 +{
 +    WI_initVariables(wbstartstruct);
 +    WI_loadData();
 +
 +    if (deathmatch)
 +	WI_initDeathmatchStats();
 +    else if (netgame)
 +	WI_initNetgameStats();
 +    else
 +	WI_initStats();
 +}
 +#endif