]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
Initial revision
authorlordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 22 Aug 2000 02:56:41 +0000 (02:56 +0000)
committerlordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 22 Aug 2000 02:56:41 +0000 (02:56 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2 d7cf8633-e32d-0410-b094-e92efae38249

116 files changed:
anorms.h [new file with mode: 0644]
bspfile.h [new file with mode: 0644]
cd_win.c [new file with mode: 0644]
cdaudio.h [new file with mode: 0644]
chase.c [new file with mode: 0644]
cl_demo.c [new file with mode: 0644]
cl_input.c [new file with mode: 0644]
cl_main.c [new file with mode: 0644]
cl_parse.c [new file with mode: 0644]
cl_tent.c [new file with mode: 0644]
client.h [new file with mode: 0644]
cmd.c [new file with mode: 0644]
cmd.h [new file with mode: 0644]
common.c [new file with mode: 0644]
common.h [new file with mode: 0644]
conproc.c [new file with mode: 0644]
conproc.h [new file with mode: 0644]
console.c [new file with mode: 0644]
console.h [new file with mode: 0644]
crc.c [new file with mode: 0644]
crc.h [new file with mode: 0644]
cvar.c [new file with mode: 0644]
cvar.h [new file with mode: 0644]
draw.h [new file with mode: 0644]
fractalnoise.c [new file with mode: 0644]
gl_draw.c [new file with mode: 0644]
gl_poly.c [new file with mode: 0644]
gl_poly.h [new file with mode: 0644]
gl_refrag.c [new file with mode: 0644]
gl_rmain.c [new file with mode: 0644]
gl_rmisc.c [new file with mode: 0644]
gl_rsurf.c [new file with mode: 0644]
gl_screen.c [new file with mode: 0644]
gl_warp.c [new file with mode: 0644]
gl_warp_sin.h [new file with mode: 0644]
glquake.h [new file with mode: 0644]
hcompress.c [new file with mode: 0644]
host.c [new file with mode: 0644]
host_cmd.c [new file with mode: 0644]
image.c [new file with mode: 0644]
in_null.c [new file with mode: 0644]
in_win.c [new file with mode: 0644]
input.h [new file with mode: 0644]
keys.c [new file with mode: 0644]
keys.h [new file with mode: 0644]
mathlib.c [new file with mode: 0644]
mathlib.h [new file with mode: 0644]
menu.c [new file with mode: 0644]
menu.h [new file with mode: 0644]
model_alias.c [new file with mode: 0644]
model_alias.h [new file with mode: 0644]
model_brush.c [new file with mode: 0644]
model_brush.h [new file with mode: 0644]
model_shared.c [new file with mode: 0644]
model_shared.h [new file with mode: 0644]
model_sprite.c [new file with mode: 0644]
model_sprite.h [new file with mode: 0644]
modelgen.h [new file with mode: 0644]
net.h [new file with mode: 0644]
net_bsd.c [new file with mode: 0644]
net_dgrm.c [new file with mode: 0644]
net_dgrm.h [new file with mode: 0644]
net_loop.c [new file with mode: 0644]
net_loop.h [new file with mode: 0644]
net_main.c [new file with mode: 0644]
net_vcr.c [new file with mode: 0644]
net_vcr.h [new file with mode: 0644]
net_win.c [new file with mode: 0644]
net_wins.c [new file with mode: 0644]
net_wins.h [new file with mode: 0644]
net_wipx.c [new file with mode: 0644]
net_wipx.h [new file with mode: 0644]
nonintel.c [new file with mode: 0644]
pr_cmds.c [new file with mode: 0644]
pr_comp.h [new file with mode: 0644]
pr_edict.c [new file with mode: 0644]
pr_exec.c [new file with mode: 0644]
progdefs.h [new file with mode: 0644]
progs.h [new file with mode: 0644]
protocol.h [new file with mode: 0644]
quakedef.h [new file with mode: 0644]
r_light.c [new file with mode: 0644]
r_part.c [new file with mode: 0644]
render.h [new file with mode: 0644]
resource.h [new file with mode: 0644]
sbar.c [new file with mode: 0644]
sbar.h [new file with mode: 0644]
screen.h [new file with mode: 0644]
server.h [new file with mode: 0644]
snd_dma.c [new file with mode: 0644]
snd_mem.c [new file with mode: 0644]
snd_mix.c [new file with mode: 0644]
snd_win.c [new file with mode: 0644]
sound.h [new file with mode: 0644]
spritegn.h [new file with mode: 0644]
sv_main.c [new file with mode: 0644]
sv_move.c [new file with mode: 0644]
sv_phys.c [new file with mode: 0644]
sv_user.c [new file with mode: 0644]
sys.h [new file with mode: 0644]
sys_win.c [new file with mode: 0644]
sys_wind.c [new file with mode: 0644]
transform.c [new file with mode: 0644]
transform.h [new file with mode: 0644]
vid.h [new file with mode: 0644]
vid_shared.c [new file with mode: 0644]
vid_wgl.c [new file with mode: 0644]
view.c [new file with mode: 0644]
view.h [new file with mode: 0644]
wad.c [new file with mode: 0644]
wad.h [new file with mode: 0644]
winquake.h [new file with mode: 0644]
world.c [new file with mode: 0644]
world.h [new file with mode: 0644]
zone.c [new file with mode: 0644]
zone.h [new file with mode: 0644]

diff --git a/anorms.h b/anorms.h
new file mode 100644 (file)
index 0000000..11a9007
--- /dev/null
+++ b/anorms.h
@@ -0,0 +1,181 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+{-0.525731, 0.000000, 0.850651}, 
+{-0.442863, 0.238856, 0.864188}, 
+{-0.295242, 0.000000, 0.955423}, 
+{-0.309017, 0.500000, 0.809017}, 
+{-0.162460, 0.262866, 0.951056}, 
+{0.000000, 0.000000, 1.000000}, 
+{0.000000, 0.850651, 0.525731}, 
+{-0.147621, 0.716567, 0.681718}, 
+{0.147621, 0.716567, 0.681718}, 
+{0.000000, 0.525731, 0.850651}, 
+{0.309017, 0.500000, 0.809017}, 
+{0.525731, 0.000000, 0.850651}, 
+{0.295242, 0.000000, 0.955423}, 
+{0.442863, 0.238856, 0.864188}, 
+{0.162460, 0.262866, 0.951056}, 
+{-0.681718, 0.147621, 0.716567}, 
+{-0.809017, 0.309017, 0.500000}, 
+{-0.587785, 0.425325, 0.688191}, 
+{-0.850651, 0.525731, 0.000000}, 
+{-0.864188, 0.442863, 0.238856}, 
+{-0.716567, 0.681718, 0.147621}, 
+{-0.688191, 0.587785, 0.425325}, 
+{-0.500000, 0.809017, 0.309017}, 
+{-0.238856, 0.864188, 0.442863}, 
+{-0.425325, 0.688191, 0.587785}, 
+{-0.716567, 0.681718, -0.147621}, 
+{-0.500000, 0.809017, -0.309017}, 
+{-0.525731, 0.850651, 0.000000}, 
+{0.000000, 0.850651, -0.525731}, 
+{-0.238856, 0.864188, -0.442863}, 
+{0.000000, 0.955423, -0.295242}, 
+{-0.262866, 0.951056, -0.162460}, 
+{0.000000, 1.000000, 0.000000}, 
+{0.000000, 0.955423, 0.295242}, 
+{-0.262866, 0.951056, 0.162460}, 
+{0.238856, 0.864188, 0.442863}, 
+{0.262866, 0.951056, 0.162460}, 
+{0.500000, 0.809017, 0.309017}, 
+{0.238856, 0.864188, -0.442863}, 
+{0.262866, 0.951056, -0.162460}, 
+{0.500000, 0.809017, -0.309017}, 
+{0.850651, 0.525731, 0.000000}, 
+{0.716567, 0.681718, 0.147621}, 
+{0.716567, 0.681718, -0.147621}, 
+{0.525731, 0.850651, 0.000000}, 
+{0.425325, 0.688191, 0.587785}, 
+{0.864188, 0.442863, 0.238856}, 
+{0.688191, 0.587785, 0.425325}, 
+{0.809017, 0.309017, 0.500000}, 
+{0.681718, 0.147621, 0.716567}, 
+{0.587785, 0.425325, 0.688191}, 
+{0.955423, 0.295242, 0.000000}, 
+{1.000000, 0.000000, 0.000000}, 
+{0.951056, 0.162460, 0.262866}, 
+{0.850651, -0.525731, 0.000000}, 
+{0.955423, -0.295242, 0.000000}, 
+{0.864188, -0.442863, 0.238856}, 
+{0.951056, -0.162460, 0.262866}, 
+{0.809017, -0.309017, 0.500000}, 
+{0.681718, -0.147621, 0.716567}, 
+{0.850651, 0.000000, 0.525731}, 
+{0.864188, 0.442863, -0.238856}, 
+{0.809017, 0.309017, -0.500000}, 
+{0.951056, 0.162460, -0.262866}, 
+{0.525731, 0.000000, -0.850651}, 
+{0.681718, 0.147621, -0.716567}, 
+{0.681718, -0.147621, -0.716567}, 
+{0.850651, 0.000000, -0.525731}, 
+{0.809017, -0.309017, -0.500000}, 
+{0.864188, -0.442863, -0.238856}, 
+{0.951056, -0.162460, -0.262866}, 
+{0.147621, 0.716567, -0.681718}, 
+{0.309017, 0.500000, -0.809017}, 
+{0.425325, 0.688191, -0.587785}, 
+{0.442863, 0.238856, -0.864188}, 
+{0.587785, 0.425325, -0.688191}, 
+{0.688191, 0.587785, -0.425325}, 
+{-0.147621, 0.716567, -0.681718}, 
+{-0.309017, 0.500000, -0.809017}, 
+{0.000000, 0.525731, -0.850651}, 
+{-0.525731, 0.000000, -0.850651}, 
+{-0.442863, 0.238856, -0.864188}, 
+{-0.295242, 0.000000, -0.955423}, 
+{-0.162460, 0.262866, -0.951056}, 
+{0.000000, 0.000000, -1.000000}, 
+{0.295242, 0.000000, -0.955423}, 
+{0.162460, 0.262866, -0.951056}, 
+{-0.442863, -0.238856, -0.864188}, 
+{-0.309017, -0.500000, -0.809017}, 
+{-0.162460, -0.262866, -0.951056}, 
+{0.000000, -0.850651, -0.525731}, 
+{-0.147621, -0.716567, -0.681718}, 
+{0.147621, -0.716567, -0.681718}, 
+{0.000000, -0.525731, -0.850651}, 
+{0.309017, -0.500000, -0.809017}, 
+{0.442863, -0.238856, -0.864188}, 
+{0.162460, -0.262866, -0.951056}, 
+{0.238856, -0.864188, -0.442863}, 
+{0.500000, -0.809017, -0.309017}, 
+{0.425325, -0.688191, -0.587785}, 
+{0.716567, -0.681718, -0.147621}, 
+{0.688191, -0.587785, -0.425325}, 
+{0.587785, -0.425325, -0.688191}, 
+{0.000000, -0.955423, -0.295242}, 
+{0.000000, -1.000000, 0.000000}, 
+{0.262866, -0.951056, -0.162460}, 
+{0.000000, -0.850651, 0.525731}, 
+{0.000000, -0.955423, 0.295242}, 
+{0.238856, -0.864188, 0.442863}, 
+{0.262866, -0.951056, 0.162460}, 
+{0.500000, -0.809017, 0.309017}, 
+{0.716567, -0.681718, 0.147621}, 
+{0.525731, -0.850651, 0.000000}, 
+{-0.238856, -0.864188, -0.442863}, 
+{-0.500000, -0.809017, -0.309017}, 
+{-0.262866, -0.951056, -0.162460}, 
+{-0.850651, -0.525731, 0.000000}, 
+{-0.716567, -0.681718, -0.147621}, 
+{-0.716567, -0.681718, 0.147621}, 
+{-0.525731, -0.850651, 0.000000}, 
+{-0.500000, -0.809017, 0.309017}, 
+{-0.238856, -0.864188, 0.442863}, 
+{-0.262866, -0.951056, 0.162460}, 
+{-0.864188, -0.442863, 0.238856}, 
+{-0.809017, -0.309017, 0.500000}, 
+{-0.688191, -0.587785, 0.425325}, 
+{-0.681718, -0.147621, 0.716567}, 
+{-0.442863, -0.238856, 0.864188}, 
+{-0.587785, -0.425325, 0.688191}, 
+{-0.309017, -0.500000, 0.809017}, 
+{-0.147621, -0.716567, 0.681718}, 
+{-0.425325, -0.688191, 0.587785}, 
+{-0.162460, -0.262866, 0.951056}, 
+{0.442863, -0.238856, 0.864188}, 
+{0.162460, -0.262866, 0.951056}, 
+{0.309017, -0.500000, 0.809017}, 
+{0.147621, -0.716567, 0.681718}, 
+{0.000000, -0.525731, 0.850651}, 
+{0.425325, -0.688191, 0.587785}, 
+{0.587785, -0.425325, 0.688191}, 
+{0.688191, -0.587785, 0.425325}, 
+{-0.955423, 0.295242, 0.000000}, 
+{-0.951056, 0.162460, 0.262866}, 
+{-1.000000, 0.000000, 0.000000}, 
+{-0.850651, 0.000000, 0.525731}, 
+{-0.955423, -0.295242, 0.000000}, 
+{-0.951056, -0.162460, 0.262866}, 
+{-0.864188, 0.442863, -0.238856}, 
+{-0.951056, 0.162460, -0.262866}, 
+{-0.809017, 0.309017, -0.500000}, 
+{-0.864188, -0.442863, -0.238856}, 
+{-0.951056, -0.162460, -0.262866}, 
+{-0.809017, -0.309017, -0.500000}, 
+{-0.681718, 0.147621, -0.716567}, 
+{-0.681718, -0.147621, -0.716567}, 
+{-0.850651, 0.000000, -0.525731}, 
+{-0.688191, 0.587785, -0.425325}, 
+{-0.587785, 0.425325, -0.688191}, 
+{-0.425325, 0.688191, -0.587785}, 
+{-0.425325, -0.688191, -0.587785}, 
+{-0.587785, -0.425325, -0.688191}, 
+{-0.688191, -0.587785, -0.425325}, 
diff --git a/bspfile.h b/bspfile.h
new file mode 100644 (file)
index 0000000..a1268b4
--- /dev/null
+++ b/bspfile.h
@@ -0,0 +1,327 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+
+// upper design bounds
+
+#define        MAX_MAP_HULLS           4
+
+#define        MAX_MAP_MODELS          256
+#define        MAX_MAP_BRUSHES         4096
+#define        MAX_MAP_ENTITIES        1024
+#define        MAX_MAP_ENTSTRING       65536
+
+#define        MAX_MAP_PLANES          32767
+#define        MAX_MAP_NODES           32767           // because negative shorts are contents
+#define        MAX_MAP_CLIPNODES       32767           //
+#define        MAX_MAP_LEAFS           32767           // was 8192
+#define        MAX_MAP_VERTS           65535
+#define        MAX_MAP_FACES           65535
+#define        MAX_MAP_MARKSURFACES 65535
+#define        MAX_MAP_TEXINFO         4096
+#define        MAX_MAP_EDGES           256000
+#define        MAX_MAP_SURFEDGES       512000
+#define        MAX_MAP_TEXTURES        512
+#define        MAX_MAP_MIPTEX          0x200000
+#define        MAX_MAP_LIGHTING        0x100000
+#define        MAX_MAP_VISIBILITY      0x100000
+
+#define        MAX_MAP_PORTALS         65536
+
+// key / value pair sizes
+
+#define        MAX_KEY         32
+#define        MAX_VALUE       1024
+
+//=============================================================================
+
+
+#define BSPVERSION     29
+#define        TOOLVERSION     2
+
+typedef struct
+{
+       int             fileofs, filelen;
+} lump_t;
+
+#define        LUMP_ENTITIES   0
+#define        LUMP_PLANES             1
+#define        LUMP_TEXTURES   2
+#define        LUMP_VERTEXES   3
+#define        LUMP_VISIBILITY 4
+#define        LUMP_NODES              5
+#define        LUMP_TEXINFO    6
+#define        LUMP_FACES              7
+#define        LUMP_LIGHTING   8
+#define        LUMP_CLIPNODES  9
+#define        LUMP_LEAFS              10
+#define        LUMP_MARKSURFACES 11
+#define        LUMP_EDGES              12
+#define        LUMP_SURFEDGES  13
+#define        LUMP_MODELS             14
+
+#define        HEADER_LUMPS    15
+
+typedef struct
+{
+       float           mins[3], maxs[3];
+       float           origin[3];
+       int                     headnode[MAX_MAP_HULLS];
+       int                     visleafs;               // not including the solid leaf 0
+       int                     firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+       int                     version;        
+       lump_t          lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+       int                     nummiptex;
+       int                     dataofs[4];             // [nummiptex]
+} dmiptexlump_t;
+
+#define        MIPLEVELS       4
+typedef struct miptex_s
+{
+       char            name[16];
+       unsigned        width, height;
+       unsigned        offsets[MIPLEVELS];             // four mip maps stored
+} miptex_t;
+
+
+typedef struct
+{
+       float   point[3];
+} dvertex_t;
+
+
+// 0-2 are axial planes
+#define        PLANE_X                 0
+#define        PLANE_Y                 1
+#define        PLANE_Z                 2
+
+// 3-5 are non-axial planes snapped to the nearest
+#define        PLANE_ANYX              3
+#define        PLANE_ANYY              4
+#define        PLANE_ANYZ              5
+
+typedef struct
+{
+       float   normal[3];
+       float   dist;
+       int             type;           // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+
+#define        CONTENTS_EMPTY          -1
+#define        CONTENTS_SOLID          -2
+#define        CONTENTS_WATER          -3
+#define        CONTENTS_SLIME          -4
+#define        CONTENTS_LAVA           -5
+#define        CONTENTS_SKY            -6
+#define        CONTENTS_ORIGIN         -7              // removed at csg time
+#define        CONTENTS_CLIP           -8              // changed to contents_solid
+
+// LordHavoc: Q2 water
+/*
+#define        CONTENTS_CURRENT_0              -9
+#define        CONTENTS_CURRENT_90             -10
+#define        CONTENTS_CURRENT_180    -11
+#define        CONTENTS_CURRENT_270    -12
+#define        CONTENTS_CURRENT_UP             -13
+#define        CONTENTS_CURRENT_DOWN   -14
+*/
+
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+       int                     planenum;
+       short           children[2];    // negative numbers are -(leafs+1), not nodes
+       short           mins[3];                // for sphere culling
+       short           maxs[3];
+       unsigned short  firstface;
+       unsigned short  numfaces;       // counting both sides
+} dnode_t;
+
+typedef struct
+{
+       int                     planenum;
+       short           children[2];    // negative numbers are contents
+} dclipnode_t;
+
+
+typedef struct texinfo_s
+{
+       float           vecs[2][4];             // [s/t][xyz offset]
+       int                     miptex;
+       int                     flags;
+} texinfo_t;
+#define        TEX_SPECIAL             1               // sky or slime, no lightmap or 256 subdivision
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+       unsigned short  v[2];           // vertex numbers
+} dedge_t;
+
+#define        MAXLIGHTMAPS    4
+typedef struct
+{
+       short           planenum;
+       short           side;
+
+       int                     firstedge;              // we must support > 64k edges
+       short           numedges;       
+       short           texinfo;
+
+// lighting info
+       byte            styles[MAXLIGHTMAPS];
+       int                     lightofs;               // start of [numstyles*surfsize] samples
+} dface_t;
+
+
+
+#define        AMBIENT_WATER   0
+#define        AMBIENT_SKY             1
+#define        AMBIENT_SLIME   2
+#define        AMBIENT_LAVA    3
+
+#define        NUM_AMBIENTS                    4               // automatic ambient sounds
+
+// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
+// all other leafs need visibility info
+typedef struct
+{
+       int                     contents;
+       int                     visofs;                         // -1 = no visibility info
+
+       short           mins[3];                        // for frustum culling
+       short           maxs[3];
+
+       unsigned short          firstmarksurface;
+       unsigned short          nummarksurfaces;
+
+       byte            ambient_level[NUM_AMBIENTS];
+} dleaf_t;
+
+
+//============================================================================
+
+#ifndef QUAKE_GAME
+
+#define        ANGLE_UP        -1
+#define        ANGLE_DOWN      -2
+
+
+// the utilities get to be lazy and just use large static arrays
+
+extern int                     nummodels;
+extern dmodel_t        dmodels[MAX_MAP_MODELS];
+
+extern int                     visdatasize;
+extern byte            dvisdata[MAX_MAP_VISIBILITY];
+
+extern int                     lightdatasize;
+extern byte            dlightdata[MAX_MAP_LIGHTING];
+
+extern int                     texdatasize;
+extern byte            dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+extern int                     entdatasize;
+extern char            dentdata[MAX_MAP_ENTSTRING];
+
+extern int                     numleafs;
+extern dleaf_t         dleafs[MAX_MAP_LEAFS];
+
+extern int                     numplanes;
+extern dplane_t        dplanes[MAX_MAP_PLANES];
+
+extern int                     numvertexes;
+extern dvertex_t       dvertexes[MAX_MAP_VERTS];
+
+extern int                     numnodes;
+extern dnode_t         dnodes[MAX_MAP_NODES];
+
+extern int                     numtexinfo;
+extern texinfo_t       texinfo[MAX_MAP_TEXINFO];
+
+extern int                     numfaces;
+extern dface_t         dfaces[MAX_MAP_FACES];
+
+extern int                     numclipnodes;
+extern dclipnode_t     dclipnodes[MAX_MAP_CLIPNODES];
+
+extern int                     numedges;
+extern dedge_t         dedges[MAX_MAP_EDGES];
+
+extern int                     nummarksurfaces;
+extern unsigned short  dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+extern int                     numsurfedges;
+extern int                     dsurfedges[MAX_MAP_SURFEDGES];
+
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void   LoadBSPFile (char *filename);
+void   WriteBSPFile (char *filename);
+void   PrintBSPFileSizes (void);
+
+//===============
+
+
+typedef struct epair_s
+{
+       struct epair_s  *next;
+       char    *key;
+       char    *value;
+} epair_t;
+
+typedef struct
+{
+       vec3_t          origin;
+       int                     firstbrush;
+       int                     numbrushes;
+       epair_t         *epairs;
+} entity_t;
+
+extern int                     num_entities;
+extern entity_t        entities[MAX_MAP_ENTITIES];
+
+void   ParseEntities (void);
+void   UnparseEntities (void);
+
+void   SetKeyValue (entity_t *ent, char *key, char *value);
+char   *ValueForKey (entity_t *ent, char *key);
+// will return "" if not present
+
+vec_t  FloatForKey (entity_t *ent, char *key);
+void   GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+epair_t *ParseEpair (void);
+
+#endif
diff --git a/cd_win.c b/cd_win.c
new file mode 100644 (file)
index 0000000..3e249d1
--- /dev/null
+++ b/cd_win.c
@@ -0,0 +1,476 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
+// rights reserved.
+
+#include <windows.h>
+#include "quakedef.h"
+
+extern HWND    mainwindow;
+extern cvar_t  bgmvolume;
+
+static qboolean cdValid = false;
+static qboolean        playing = false;
+static qboolean        wasPlaying = false;
+static qboolean        initialized = false;
+static qboolean        enabled = false;
+static qboolean playLooping = false;
+static float   cdvolume;
+static byte    remap[100];
+static byte            cdrom;
+static byte            playTrack;
+static byte            maxTrack;
+
+UINT   wDeviceID;
+
+
+static void CDAudio_Eject(void)
+{
+       DWORD   dwReturn;
+
+    if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)NULL))
+               Con_DPrintf("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn);
+}
+
+
+static void CDAudio_CloseDoor(void)
+{
+       DWORD   dwReturn;
+
+    if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)NULL))
+               Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn);
+}
+
+
+static int CDAudio_GetAudioDiskInfo(void)
+{
+       DWORD                           dwReturn;
+       MCI_STATUS_PARMS        mciStatusParms;
+
+
+       cdValid = false;
+
+       mciStatusParms.dwItem = MCI_STATUS_READY;
+    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
+       if (dwReturn)
+       {
+               Con_DPrintf("CDAudio: drive ready test - get status failed\n");
+               return -1;
+       }
+       if (!mciStatusParms.dwReturn)
+       {
+               Con_DPrintf("CDAudio: drive not ready\n");
+               return -1;
+       }
+
+       mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
+    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
+       if (dwReturn)
+       {
+               Con_DPrintf("CDAudio: get tracks - status failed\n");
+               return -1;
+       }
+       if (mciStatusParms.dwReturn < 1)
+       {
+               Con_DPrintf("CDAudio: no music tracks\n");
+               return -1;
+       }
+
+       cdValid = true;
+       maxTrack = mciStatusParms.dwReturn;
+
+       return 0;
+}
+
+
+void CDAudio_Play(byte track, qboolean looping)
+{
+       DWORD                           dwReturn;
+    MCI_PLAY_PARMS             mciPlayParms;
+       MCI_STATUS_PARMS        mciStatusParms;
+
+       if (!enabled)
+               return;
+       
+       if (!cdValid)
+       {
+               CDAudio_GetAudioDiskInfo();
+               if (!cdValid)
+                       return;
+       }
+
+       track = remap[track];
+
+       if (track < 1 || track > maxTrack)
+       {
+               Con_DPrintf("CDAudio: Bad track number %u.\n", track);
+               return;
+       }
+
+       // don't try to play a non-audio track
+       mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
+       mciStatusParms.dwTrack = track;
+    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
+       if (dwReturn)
+       {
+               Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
+               return;
+       }
+       if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO)
+       {
+               Con_Printf("CDAudio: track %i is not audio\n", track);
+               return;
+       }
+
+       // get the length of the track to be played
+       mciStatusParms.dwItem = MCI_STATUS_LENGTH;
+       mciStatusParms.dwTrack = track;
+    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
+       if (dwReturn)
+       {
+               Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
+               return;
+       }
+
+       if (playing)
+       {
+               if (playTrack == track)
+                       return;
+               CDAudio_Stop();
+       }
+
+    mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0);
+       mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track;
+    mciPlayParms.dwCallback = (DWORD)mainwindow;
+    dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms);
+       if (dwReturn)
+       {
+               Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn);
+               return;
+       }
+
+       playLooping = looping;
+       playTrack = track;
+       playing = true;
+
+       if (cdvolume == 0.0)
+               CDAudio_Pause ();
+}
+
+
+void CDAudio_Stop(void)
+{
+       DWORD   dwReturn;
+
+       if (!enabled)
+               return;
+       
+       if (!playing)
+               return;
+
+    if (dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD)NULL))
+               Con_DPrintf("MCI_STOP failed (%i)", dwReturn);
+
+       wasPlaying = false;
+       playing = false;
+}
+
+
+void CDAudio_Pause(void)
+{
+       DWORD                           dwReturn;
+       MCI_GENERIC_PARMS       mciGenericParms;
+
+       if (!enabled)
+               return;
+
+       if (!playing)
+               return;
+
+       mciGenericParms.dwCallback = (DWORD)mainwindow;
+    if (dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &mciGenericParms))
+               Con_DPrintf("MCI_PAUSE failed (%i)", dwReturn);
+
+       wasPlaying = playing;
+       playing = false;
+}
+
+
+void CDAudio_Resume(void)
+{
+       DWORD                   dwReturn;
+    MCI_PLAY_PARMS     mciPlayParms;
+
+       if (!enabled)
+               return;
+       
+       if (!cdValid)
+               return;
+
+       if (!wasPlaying)
+               return;
+       
+    mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0);
+    mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0);
+    mciPlayParms.dwCallback = (DWORD)mainwindow;
+    dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);
+       if (dwReturn)
+       {
+               Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn);
+               return;
+       }
+       playing = true;
+}
+
+
+static void CD_f (void)
+{
+       char    *command;
+       int             ret;
+       int             n;
+
+       if (Cmd_Argc() < 2)
+               return;
+
+       command = Cmd_Argv (1);
+
+       if (Q_strcasecmp(command, "on") == 0)
+       {
+               enabled = true;
+               return;
+       }
+
+       if (Q_strcasecmp(command, "off") == 0)
+       {
+               if (playing)
+                       CDAudio_Stop();
+               enabled = false;
+               return;
+       }
+
+       if (Q_strcasecmp(command, "reset") == 0)
+       {
+               enabled = true;
+               if (playing)
+                       CDAudio_Stop();
+               for (n = 0; n < 100; n++)
+                       remap[n] = n;
+               CDAudio_GetAudioDiskInfo();
+               return;
+       }
+
+       if (Q_strcasecmp(command, "remap") == 0)
+       {
+               ret = Cmd_Argc() - 2;
+               if (ret <= 0)
+               {
+                       for (n = 1; n < 100; n++)
+                               if (remap[n] != n)
+                                       Con_Printf("  %u -> %u\n", n, remap[n]);
+                       return;
+               }
+               for (n = 1; n <= ret; n++)
+                       remap[n] = atoi(Cmd_Argv (n+1));
+               return;
+       }
+
+       if (Q_strcasecmp(command, "close") == 0)
+       {
+               CDAudio_CloseDoor();
+               return;
+       }
+
+       if (!cdValid)
+       {
+               CDAudio_GetAudioDiskInfo();
+               if (!cdValid)
+               {
+                       Con_Printf("No CD in player.\n");
+                       return;
+               }
+       }
+
+       if (Q_strcasecmp(command, "play") == 0)
+       {
+               CDAudio_Play((byte)atoi(Cmd_Argv (2)), false);
+               return;
+       }
+
+       if (Q_strcasecmp(command, "loop") == 0)
+       {
+               CDAudio_Play((byte)atoi(Cmd_Argv (2)), true);
+               return;
+       }
+
+       if (Q_strcasecmp(command, "stop") == 0)
+       {
+               CDAudio_Stop();
+               return;
+       }
+
+       if (Q_strcasecmp(command, "pause") == 0)
+       {
+               CDAudio_Pause();
+               return;
+       }
+
+       if (Q_strcasecmp(command, "resume") == 0)
+       {
+               CDAudio_Resume();
+               return;
+       }
+
+       if (Q_strcasecmp(command, "eject") == 0)
+       {
+               if (playing)
+                       CDAudio_Stop();
+               CDAudio_Eject();
+               cdValid = false;
+               return;
+       }
+
+       if (Q_strcasecmp(command, "info") == 0)
+       {
+               Con_Printf("%u tracks\n", maxTrack);
+               if (playing)
+                       Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
+               else if (wasPlaying)
+                       Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
+               Con_Printf("Volume is %f\n", cdvolume);
+               return;
+       }
+}
+
+
+LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+       if (lParam != wDeviceID)
+               return 1;
+
+       switch (wParam)
+       {
+               case MCI_NOTIFY_SUCCESSFUL:
+                       if (playing)
+                       {
+                               playing = false;
+                               if (playLooping)
+                                       CDAudio_Play(playTrack, true);
+                       }
+                       break;
+
+               case MCI_NOTIFY_ABORTED:
+               case MCI_NOTIFY_SUPERSEDED:
+                       break;
+
+               case MCI_NOTIFY_FAILURE:
+                       Con_DPrintf("MCI_NOTIFY_FAILURE\n");
+                       CDAudio_Stop ();
+                       cdValid = false;
+                       break;
+
+               default:
+                       Con_DPrintf("Unexpected MM_MCINOTIFY type (%i)\n", wParam);
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+void CDAudio_Update(void)
+{
+       if (!enabled)
+               return;
+
+       if (bgmvolume.value != cdvolume)
+       {
+               if (cdvolume)
+               {
+                       Cvar_SetValue ("bgmvolume", 0.0);
+                       cdvolume = bgmvolume.value;
+                       CDAudio_Pause ();
+               }
+               else
+               {
+                       Cvar_SetValue ("bgmvolume", 1.0);
+                       cdvolume = bgmvolume.value;
+                       CDAudio_Resume ();
+               }
+       }
+}
+
+
+int CDAudio_Init(void)
+{
+       DWORD   dwReturn;
+       MCI_OPEN_PARMS  mciOpenParms;
+    MCI_SET_PARMS      mciSetParms;
+       int                             n;
+
+       if (cls.state == ca_dedicated)
+               return -1;
+
+       if (COM_CheckParm("-nocdaudio"))
+               return -1;
+
+       mciOpenParms.lpstrDeviceType = "cdaudio";
+       if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD) (LPVOID) &mciOpenParms))
+       {
+               Con_Printf("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn);
+               return -1;
+       }
+       wDeviceID = mciOpenParms.wDeviceID;
+
+    // Set the time format to track/minute/second/frame (TMSF).
+    mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;
+    if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms))
+    {
+               Con_Printf("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn);
+        mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL);
+               return -1;
+    }
+
+       for (n = 0; n < 100; n++)
+               remap[n] = n;
+       initialized = true;
+       enabled = true;
+
+       if (CDAudio_GetAudioDiskInfo())
+       {
+               Con_Printf("CDAudio_Init: No CD in player.\n");
+               cdValid = false;
+       }
+
+       Cmd_AddCommand ("cd", CD_f);
+
+       Con_Printf("CD Audio Initialized\n");
+
+       return 0;
+}
+
+
+void CDAudio_Shutdown(void)
+{
+       if (!initialized)
+               return;
+       CDAudio_Stop();
+       if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)NULL))
+               Con_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n");
+}
diff --git a/cdaudio.h b/cdaudio.h
new file mode 100644 (file)
index 0000000..80e975b
--- /dev/null
+++ b/cdaudio.h
@@ -0,0 +1,27 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+int CDAudio_Init(void);
+void CDAudio_Play(byte track, qboolean looping);
+void CDAudio_Stop(void);
+void CDAudio_Pause(void);
+void CDAudio_Resume(void);
+void CDAudio_Shutdown(void);
+void CDAudio_Update(void);
diff --git a/chase.c b/chase.c
new file mode 100644 (file)
index 0000000..4249881
--- /dev/null
+++ b/chase.c
@@ -0,0 +1,75 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// chase.c -- chase camera code
+
+#include "quakedef.h"
+
+cvar_t chase_back = {"chase_back", "48"};
+cvar_t chase_up = {"chase_up", "22"};
+cvar_t chase_active = {"chase_active", "0"};
+
+void Chase_Init (void)
+{
+       Cvar_RegisterVariable (&chase_back);
+       Cvar_RegisterVariable (&chase_up);
+       Cvar_RegisterVariable (&chase_active);
+}
+
+void Chase_Reset (void)
+{
+       // for respawning and teleporting
+//     start position 12 units behind head
+}
+
+qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace);
+
+void TraceLine (vec3_t start, vec3_t end, vec3_t impact)
+{
+       trace_t trace;
+
+       memset (&trace, 0, sizeof(trace));
+       SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
+
+       VectorCopy (trace.endpos, impact);
+}
+
+void Chase_Update (void)
+{
+       vec3_t  forward, up, right, stop, chase_dest;
+       float   dist;
+
+       chase_back.value = bound(0, chase_back.value, 128);
+       chase_up.value = bound(-64, chase_up.value, 64);
+
+       AngleVectors (cl.viewangles, forward, right, up);
+
+       dist = -chase_back.value - 8;
+       chase_dest[0] = r_refdef.vieworg[0] + forward[0] * dist;
+       chase_dest[1] = r_refdef.vieworg[1] + forward[1] * dist;
+       chase_dest[2] = r_refdef.vieworg[2] + forward[2] * dist + chase_up.value;
+
+       TraceLine (r_refdef.vieworg, chase_dest, stop);
+       chase_dest[0] = stop[0] + forward[0] * 8;
+       chase_dest[1] = stop[1] + forward[1] * 8;
+       chase_dest[2] = stop[2] + forward[2] * 8;
+
+       VectorCopy (chase_dest, r_refdef.vieworg);
+}
+
diff --git a/cl_demo.c b/cl_demo.c
new file mode 100644 (file)
index 0000000..aaca270
--- /dev/null
+++ b/cl_demo.c
@@ -0,0 +1,374 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+void CL_FinishTimeDemo (void);
+
+/*
+==============================================================================
+
+DEMO CODE
+
+When a demo is playing back, all NET_SendMessages are skipped, and
+NET_GetMessages are read from the demo file.
+
+Whenever cl.time gets past the last received message, another message is
+read from the demo file.
+==============================================================================
+*/
+
+/*
+==============
+CL_StopPlayback
+
+Called when a demo file runs out, or the user starts a game
+==============
+*/
+void CL_StopPlayback (void)
+{
+       if (!cls.demoplayback)
+               return;
+
+       fclose (cls.demofile);
+       cls.demoplayback = false;
+       cls.demofile = NULL;
+       cls.state = ca_disconnected;
+
+       if (cls.timedemo)
+               CL_FinishTimeDemo ();
+}
+
+/*
+====================
+CL_WriteDemoMessage
+
+Dumps the current net message, prefixed by the length and view angles
+====================
+*/
+void CL_WriteDemoMessage (void)
+{
+       int             len;
+       int             i;
+       float   f;
+
+       if (cls.demopaused) // LordHavoc: pausedemo
+               return;
+
+       len = LittleLong (net_message.cursize);
+       fwrite (&len, 4, 1, cls.demofile);
+       for (i=0 ; i<3 ; i++)
+       {
+               f = LittleFloat (cl.viewangles[i]);
+               fwrite (&f, 4, 1, cls.demofile);
+       }
+       fwrite (net_message.data, net_message.cursize, 1, cls.demofile);
+       fflush (cls.demofile);
+}
+
+/*
+====================
+CL_GetMessage
+
+Handles recording and playback of demos, on top of NET_ code
+====================
+*/
+int CL_GetMessage (void)
+{
+       int             r, i;
+       float   f;
+       
+       if      (cls.demoplayback)
+       {
+               if (cls.demopaused) // LordHavoc: pausedemo
+                       return 0;
+
+       // decide if it is time to grab the next message                
+               if (cls.signon == SIGNONS)      // allways grab until fully connected
+               {
+                       if (cls.timedemo)
+                       {
+                               if (host_framecount == cls.td_lastframe)
+                                       return 0;               // allready read this frame's message
+                               cls.td_lastframe = host_framecount;
+                       // if this is the second frame, grab the real td_starttime
+                       // so the bogus time on the first frame doesn't count
+                               if (host_framecount == cls.td_startframe + 1)
+                                       cls.td_starttime = realtime;
+                       }
+                       else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0])
+                       {
+                                       return 0;               // don't need another message yet
+                       }
+               }
+               
+       // get the next message
+               fread (&net_message.cursize, 4, 1, cls.demofile);
+               VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
+               for (i=0 ; i<3 ; i++)
+               {
+                       r = fread (&f, 4, 1, cls.demofile);
+                       cl.mviewangles[0][i] = LittleFloat (f);
+               }
+               
+               net_message.cursize = LittleLong (net_message.cursize);
+               if (net_message.cursize > MAX_MSGLEN)
+                       Sys_Error ("Demo message > MAX_MSGLEN");
+               r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
+               if (r != 1)
+               {
+                       CL_StopPlayback ();
+                       return 0;
+               }
+       
+               return 1;
+       }
+
+       while (1)
+       {
+               r = NET_GetMessage (cls.netcon);
+               
+               if (r != 1 && r != 2)
+                       return r;
+       
+       // discard nop keepalive message
+               if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
+                       Con_Printf ("<-- server to client keepalive\n");
+               else
+                       break;
+       }
+
+       if (cls.demorecording)
+               CL_WriteDemoMessage ();
+       
+       return r;
+}
+
+
+/*
+====================
+CL_Stop_f
+
+stop recording a demo
+====================
+*/
+void CL_Stop_f (void)
+{
+       if (cmd_source != src_command)
+               return;
+
+       if (!cls.demorecording)
+       {
+               Con_Printf ("Not recording a demo.\n");
+               return;
+       }
+
+// write a disconnect message to the demo file
+       SZ_Clear (&net_message);
+       MSG_WriteByte (&net_message, svc_disconnect);
+       CL_WriteDemoMessage ();
+
+// finish up
+       fclose (cls.demofile);
+       cls.demofile = NULL;
+       cls.demorecording = false;
+       Con_Printf ("Completed demo\n");
+}
+
+/*
+====================
+CL_Record_f
+
+record <demoname> <map> [cd track]
+====================
+*/
+void CL_Record_f (void)
+{
+       int             c;
+       char    name[MAX_OSPATH];
+       int             track;
+
+       if (cmd_source != src_command)
+               return;
+
+       c = Cmd_Argc();
+       if (c != 2 && c != 3 && c != 4)
+       {
+               Con_Printf ("record <demoname> [<map> [cd track]]\n");
+               return;
+       }
+
+       if (strstr(Cmd_Argv(1), ".."))
+       {
+               Con_Printf ("Relative pathnames are not allowed.\n");
+               return;
+       }
+
+       if (c == 2 && cls.state == ca_connected)
+       {
+               Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
+               return;
+       }
+
+// write the forced cd track number, or -1
+       if (c == 4)
+       {
+               track = atoi(Cmd_Argv(3));
+               Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
+       }
+       else
+               track = -1;     
+
+       sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+       
+//
+// start the map up
+//
+       if (c > 2)
+               Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
+       
+//
+// open the demo file
+//
+       COM_DefaultExtension (name, ".dem");
+
+       Con_Printf ("recording to %s.\n", name);
+       cls.demofile = fopen (name, "wb");
+       if (!cls.demofile)
+       {
+               Con_Printf ("ERROR: couldn't open.\n");
+               return;
+       }
+
+       cls.forcetrack = track;
+       fprintf (cls.demofile, "%i\n", cls.forcetrack);
+       
+       cls.demorecording = true;
+}
+
+
+/*
+====================
+CL_PlayDemo_f
+
+play [demoname]
+====================
+*/
+void CL_PlayDemo_f (void)
+{
+       char    name[256];
+       int c;
+       qboolean neg = false;
+
+       if (cmd_source != src_command)
+               return;
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("play <demoname> : plays a demo\n");
+               return;
+       }
+
+//
+// disconnect from server
+//
+       CL_Disconnect ();
+       
+//
+// open the demo file
+//
+       strcpy (name, Cmd_Argv(1));
+       COM_DefaultExtension (name, ".dem");
+
+       Con_Printf ("Playing demo from %s.\n", name);
+       COM_FOpenFile (name, &cls.demofile, false);
+       if (!cls.demofile)
+       {
+               Con_Printf ("ERROR: couldn't open.\n");
+               cls.demonum = -1;               // stop demo loop
+               return;
+       }
+
+       cls.demoplayback = true;
+       cls.state = ca_connected;
+       cls.forcetrack = 0;
+
+       while ((c = getc(cls.demofile)) != '\n')
+               if (c == '-')
+                       neg = true;
+               else
+                       cls.forcetrack = cls.forcetrack * 10 + (c - '0');
+
+       if (neg)
+               cls.forcetrack = -cls.forcetrack;
+// ZOID, fscanf is evil
+//     fscanf (cls.demofile, "%i\n", &cls.forcetrack);
+}
+
+/*
+====================
+CL_FinishTimeDemo
+
+====================
+*/
+void CL_FinishTimeDemo (void)
+{
+       int             frames;
+       double  time; // LordHavoc: changed timedemo accuracy to double
+       
+       cls.timedemo = false;
+       
+// the first frame didn't count
+       frames = (host_framecount - cls.td_startframe) - 1;
+       time = realtime - cls.td_starttime;
+       if (!time)
+               time = 1;
+       // LordHavoc: timedemo now prints out 7 digits of fraction
+       Con_Printf ("%i frames %5.7f seconds %5.7f fps\n", frames, time, frames/time);
+}
+
+/*
+====================
+CL_TimeDemo_f
+
+timedemo [demoname]
+====================
+*/
+void CL_TimeDemo_f (void)
+{
+       if (cmd_source != src_command)
+               return;
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("timedemo <demoname> : gets demo speeds\n");
+               return;
+       }
+
+       CL_PlayDemo_f ();
+       
+// cls.td_starttime will be grabbed at the second frame of the demo, so
+// all the loading time doesn't get counted
+       
+       cls.timedemo = true;
+       cls.td_startframe = host_framecount;
+       cls.td_lastframe = -1;          // get a new message this frame
+}
+
diff --git a/cl_input.c b/cl_input.c
new file mode 100644 (file)
index 0000000..9fa51fa
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl.input.c  -- builds an intended movement command to send to the server
+
+// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
+// rights reserved.
+
+#include "quakedef.h"
+
+/*
+===============================================================================
+
+KEY BUTTONS
+
+Continuous button event tracking is complicated by the fact that two different
+input sources (say, mouse button 1 and the control key) can both press the
+same button, but the button should only be released when both of the
+pressing key have been released.
+
+When a key event issues a button command (+forward, +attack, etc), it appends
+its key number as a parameter to the command so it can be matched up with
+the release.
+
+state bit 0 is the current state of the key
+state bit 1 is edge triggered on the up to down transition
+state bit 2 is edge triggered on the down to up transition
+
+===============================================================================
+*/
+
+
+kbutton_t      in_mlook, in_klook;
+kbutton_t      in_left, in_right, in_forward, in_back;
+kbutton_t      in_lookup, in_lookdown, in_moveleft, in_moveright;
+kbutton_t      in_strafe, in_speed, in_use, in_jump, in_attack;
+kbutton_t      in_up, in_down;
+// LordHavoc: added 6 new buttons
+kbutton_t      in_button3, in_button4, in_button5, in_button6, in_button7, in_button8;
+
+int                    in_impulse;
+
+
+void KeyDown (kbutton_t *b)
+{
+       int             k;
+       char    *c;
+
+       c = Cmd_Argv(1);
+       if (c[0])
+               k = atoi(c);
+       else
+               k = -1;         // typed manually at the console for continuous down
+
+       if (k == b->down[0] || k == b->down[1])
+               return;         // repeating key
+       
+       if (!b->down[0])
+               b->down[0] = k;
+       else if (!b->down[1])
+               b->down[1] = k;
+       else
+       {
+               Con_Printf ("Three keys down for a button!\n");
+               return;
+       }
+       
+       if (b->state & 1)
+               return;         // still down
+       b->state |= 1 + 2;      // down + impulse down
+}
+
+void KeyUp (kbutton_t *b)
+{
+       int             k;
+       char    *c;
+       
+       c = Cmd_Argv(1);
+       if (c[0])
+               k = atoi(c);
+       else
+       { // typed manually at the console, assume for unsticking, so clear all
+               b->down[0] = b->down[1] = 0;
+               b->state = 4;   // impulse up
+               return;
+       }
+
+       if (b->down[0] == k)
+               b->down[0] = 0;
+       else if (b->down[1] == k)
+               b->down[1] = 0;
+       else
+               return;         // key up without coresponding down (menu pass through)
+       if (b->down[0] || b->down[1])
+               return;         // some other key is still holding it down
+
+       if (!(b->state & 1))
+               return;         // still up (this should not happen)
+       b->state &= ~1;         // now up
+       b->state |= 4;          // impulse up
+}
+
+void IN_KLookDown (void) {KeyDown(&in_klook);}
+void IN_KLookUp (void) {KeyUp(&in_klook);}
+void IN_MLookDown (void) {KeyDown(&in_mlook);}
+void IN_MLookUp (void) {
+KeyUp(&in_mlook);
+if ( !(in_mlook.state&1) &&  lookspring.value)
+       V_StartPitchDrift();
+}
+void IN_UpDown(void) {KeyDown(&in_up);}
+void IN_UpUp(void) {KeyUp(&in_up);}
+void IN_DownDown(void) {KeyDown(&in_down);}
+void IN_DownUp(void) {KeyUp(&in_down);}
+void IN_LeftDown(void) {KeyDown(&in_left);}
+void IN_LeftUp(void) {KeyUp(&in_left);}
+void IN_RightDown(void) {KeyDown(&in_right);}
+void IN_RightUp(void) {KeyUp(&in_right);}
+void IN_ForwardDown(void) {KeyDown(&in_forward);}
+void IN_ForwardUp(void) {KeyUp(&in_forward);}
+void IN_BackDown(void) {KeyDown(&in_back);}
+void IN_BackUp(void) {KeyUp(&in_back);}
+void IN_LookupDown(void) {KeyDown(&in_lookup);}
+void IN_LookupUp(void) {KeyUp(&in_lookup);}
+void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
+void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
+void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
+void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
+void IN_MoverightDown(void) {KeyDown(&in_moveright);}
+void IN_MoverightUp(void) {KeyUp(&in_moveright);}
+
+void IN_SpeedDown(void) {KeyDown(&in_speed);}
+void IN_SpeedUp(void) {KeyUp(&in_speed);}
+void IN_StrafeDown(void) {KeyDown(&in_strafe);}
+void IN_StrafeUp(void) {KeyUp(&in_strafe);}
+
+void IN_AttackDown(void) {KeyDown(&in_attack);}
+void IN_AttackUp(void) {KeyUp(&in_attack);}
+
+// LordHavoc: added 6 new buttons
+void IN_Button3Down(void) {KeyDown(&in_button3);} void IN_Button3Up(void) {KeyUp(&in_button3);}
+void IN_Button4Down(void) {KeyDown(&in_button4);} void IN_Button4Up(void) {KeyUp(&in_button4);}
+void IN_Button5Down(void) {KeyDown(&in_button5);} void IN_Button5Up(void) {KeyUp(&in_button5);}
+void IN_Button6Down(void) {KeyDown(&in_button6);} void IN_Button6Up(void) {KeyUp(&in_button6);}
+void IN_Button7Down(void) {KeyDown(&in_button7);} void IN_Button7Up(void) {KeyUp(&in_button7);}
+void IN_Button8Down(void) {KeyDown(&in_button8);} void IN_Button8Up(void) {KeyUp(&in_button8);}
+
+void IN_UseDown (void) {KeyDown(&in_use);}
+void IN_UseUp (void) {KeyUp(&in_use);}
+void IN_JumpDown (void) {KeyDown(&in_jump);}
+void IN_JumpUp (void) {KeyUp(&in_jump);}
+
+void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
+
+/*
+===============
+CL_KeyState
+
+Returns 0.25 if a key was pressed and released during the frame,
+0.5 if it was pressed and held
+0 if held then released, and
+1.0 if held for the entire time
+===============
+*/
+float CL_KeyState (kbutton_t *key)
+{
+       float           val;
+       qboolean        impulsedown, impulseup, down;
+       
+       impulsedown = key->state & 2;
+       impulseup = key->state & 4;
+       down = key->state & 1;
+       val = 0;
+       
+       if (impulsedown && !impulseup)
+               if (down)
+                       val = 0.5;      // pressed and held this frame
+               else
+                       val = 0;        //      I_Error ();
+       if (impulseup && !impulsedown)
+               if (down)
+                       val = 0;        //      I_Error ();
+               else
+                       val = 0;        // released this frame
+       if (!impulsedown && !impulseup)
+               if (down)
+                       val = 1.0;      // held the entire frame
+               else
+                       val = 0;        // up the entire frame
+       if (impulsedown && impulseup)
+               if (down)
+                       val = 0.75;     // released and re-pressed this frame
+               else
+                       val = 0.25;     // pressed and released this frame
+
+       key->state &= 1;                // clear impulses
+       
+       return val;
+}
+
+
+
+
+//==========================================================================
+
+cvar_t cl_upspeed = {"cl_upspeed","200"};
+cvar_t cl_forwardspeed = {"cl_forwardspeed","200", true};
+cvar_t cl_backspeed = {"cl_backspeed","200", true};
+cvar_t cl_sidespeed = {"cl_sidespeed","350"};
+
+cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0"};
+
+cvar_t cl_yawspeed = {"cl_yawspeed","140"};
+cvar_t cl_pitchspeed = {"cl_pitchspeed","150"};
+
+cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5"};
+
+
+/*
+================
+CL_AdjustAngles
+
+Moves the local angle positions
+================
+*/
+void CL_AdjustAngles (void)
+{
+       float   speed;
+       float   up, down;
+       
+       if (in_speed.state & 1)
+               speed = host_frametime * cl_anglespeedkey.value;
+       else
+               speed = host_frametime;
+
+       if (!(in_strafe.state & 1))
+       {
+               cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
+               cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
+               cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
+       }
+       if (in_klook.state & 1)
+       {
+               V_StopPitchDrift ();
+               cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
+               cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
+       }
+       
+       up = CL_KeyState (&in_lookup);
+       down = CL_KeyState(&in_lookdown);
+       
+       cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
+       cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
+
+       if (up || down)
+               V_StopPitchDrift ();
+
+       // LordHavoc: changed from 80 to 90 (straight up)
+       if (cl.viewangles[PITCH] > 90)
+               cl.viewangles[PITCH] = 90;
+       // LordHavoc: changed from -70 to -90 (straight down)
+       if (cl.viewangles[PITCH] < -90)
+               cl.viewangles[PITCH] = -90;
+
+       if (cl.viewangles[ROLL] > 50)
+               cl.viewangles[ROLL] = 50;
+       if (cl.viewangles[ROLL] < -50)
+               cl.viewangles[ROLL] = -50;
+               
+}
+
+/*
+================
+CL_BaseMove
+
+Send the intended movement message to the server
+================
+*/
+void CL_BaseMove (usercmd_t *cmd)
+{      
+       if (cls.signon != SIGNONS)
+               return;
+                       
+       CL_AdjustAngles ();
+       
+       memset (cmd, 0, sizeof(*cmd));
+       
+       if (in_strafe.state & 1)
+       {
+               cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
+               cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
+       }
+
+       cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
+       cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
+
+       cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
+       cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
+
+       if (! (in_klook.state & 1) )
+       {       
+               cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
+               cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
+       }       
+
+//
+// adjust for speed key
+//
+       if (in_speed.state & 1)
+       {
+               cmd->forwardmove *= cl_movespeedkey.value;
+               cmd->sidemove *= cl_movespeedkey.value;
+               cmd->upmove *= cl_movespeedkey.value;
+       }
+}
+
+
+
+/*
+==============
+CL_SendMove
+==============
+*/
+void CL_SendMove (usercmd_t *cmd)
+{
+       int             i;
+       int             bits;
+       sizebuf_t       buf;
+       byte    data[128];
+       
+       buf.maxsize = 128;
+       buf.cursize = 0;
+       buf.data = data;
+       
+       cl.cmd = *cmd;
+
+//
+// send the movement message
+//
+    MSG_WriteByte (&buf, clc_move);
+
+       MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
+
+       for (i=0 ; i<3 ; i++)
+               MSG_WriteAngle (&buf, cl.viewangles[i]);
+       
+    MSG_WriteShort (&buf, cmd->forwardmove);
+    MSG_WriteShort (&buf, cmd->sidemove);
+    MSG_WriteShort (&buf, cmd->upmove);
+
+//
+// send button bits
+//
+       bits = 0;
+       
+       if ( in_attack.state & 3 )
+               bits |= 1;
+       in_attack.state &= ~2;
+       
+       if (in_jump.state & 3)
+               bits |= 2;
+       in_jump.state &= ~2;
+       // LordHavoc: added 6 new buttons
+       if (in_button3.state & 3) bits |=   4;in_button3.state &= ~2;
+       if (in_button4.state & 3) bits |=   8;in_button4.state &= ~2;
+       if (in_button5.state & 3) bits |=  16;in_button5.state &= ~2;
+       if (in_button6.state & 3) bits |=  32;in_button6.state &= ~2;
+       if (in_button7.state & 3) bits |=  64;in_button7.state &= ~2;
+       if (in_button8.state & 3) bits |= 128;in_button8.state &= ~2;
+       
+    MSG_WriteByte (&buf, bits);
+
+    MSG_WriteByte (&buf, in_impulse);
+       in_impulse = 0;
+
+//
+// deliver the message
+//
+       if (cls.demoplayback)
+               return;
+
+//
+// allways dump the first two message, because it may contain leftover inputs
+// from the last level
+//
+       if (++cl.movemessages <= 2)
+               return;
+       
+       if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1)
+       {
+               Con_Printf ("CL_SendMove: lost server connection\n");
+               CL_Disconnect ();
+       }
+}
+
+/*
+============
+CL_InitInput
+============
+*/
+void CL_InitInput (void)
+{
+       Cmd_AddCommand ("+moveup",IN_UpDown);
+       Cmd_AddCommand ("-moveup",IN_UpUp);
+       Cmd_AddCommand ("+movedown",IN_DownDown);
+       Cmd_AddCommand ("-movedown",IN_DownUp);
+       Cmd_AddCommand ("+left",IN_LeftDown);
+       Cmd_AddCommand ("-left",IN_LeftUp);
+       Cmd_AddCommand ("+right",IN_RightDown);
+       Cmd_AddCommand ("-right",IN_RightUp);
+       Cmd_AddCommand ("+forward",IN_ForwardDown);
+       Cmd_AddCommand ("-forward",IN_ForwardUp);
+       Cmd_AddCommand ("+back",IN_BackDown);
+       Cmd_AddCommand ("-back",IN_BackUp);
+       Cmd_AddCommand ("+lookup", IN_LookupDown);
+       Cmd_AddCommand ("-lookup", IN_LookupUp);
+       Cmd_AddCommand ("+lookdown", IN_LookdownDown);
+       Cmd_AddCommand ("-lookdown", IN_LookdownUp);
+       Cmd_AddCommand ("+strafe", IN_StrafeDown);
+       Cmd_AddCommand ("-strafe", IN_StrafeUp);
+       Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
+       Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
+       Cmd_AddCommand ("+moveright", IN_MoverightDown);
+       Cmd_AddCommand ("-moveright", IN_MoverightUp);
+       Cmd_AddCommand ("+speed", IN_SpeedDown);
+       Cmd_AddCommand ("-speed", IN_SpeedUp);
+       Cmd_AddCommand ("+attack", IN_AttackDown);
+       Cmd_AddCommand ("-attack", IN_AttackUp);
+       Cmd_AddCommand ("+use", IN_UseDown);
+       Cmd_AddCommand ("-use", IN_UseUp);
+       Cmd_AddCommand ("+jump", IN_JumpDown);
+       Cmd_AddCommand ("-jump", IN_JumpUp);
+       Cmd_AddCommand ("impulse", IN_Impulse);
+       Cmd_AddCommand ("+klook", IN_KLookDown);
+       Cmd_AddCommand ("-klook", IN_KLookUp);
+       Cmd_AddCommand ("+mlook", IN_MLookDown);
+       Cmd_AddCommand ("-mlook", IN_MLookUp);
+
+       // LordHavoc: added 6 new buttons
+       Cmd_AddCommand ("+button3", IN_Button3Down);
+       Cmd_AddCommand ("-button3", IN_Button3Up);
+       Cmd_AddCommand ("+button4", IN_Button4Down);
+       Cmd_AddCommand ("-button4", IN_Button4Up);
+       Cmd_AddCommand ("+button5", IN_Button5Down);
+       Cmd_AddCommand ("-button5", IN_Button5Up);
+       Cmd_AddCommand ("+button6", IN_Button6Down);
+       Cmd_AddCommand ("-button6", IN_Button6Up);
+       Cmd_AddCommand ("+button7", IN_Button7Down);
+       Cmd_AddCommand ("-button7", IN_Button7Up);
+       Cmd_AddCommand ("+button8", IN_Button8Down);
+       Cmd_AddCommand ("-button8", IN_Button8Up);
+}
diff --git a/cl_main.c b/cl_main.c
new file mode 100644 (file)
index 0000000..cb2ea33
--- /dev/null
+++ b/cl_main.c
@@ -0,0 +1,832 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl_main.c  -- client main loop
+
+#include "quakedef.h"
+
+// we need to declare some mouse variables here, because the menu system
+// references them even when on a unix system.
+
+// these two are not intended to be set directly
+cvar_t cl_name = {"_cl_name", "player", true};
+cvar_t cl_color = {"_cl_color", "0", true};
+
+cvar_t cl_shownet = {"cl_shownet","0"};        // can be 0, 1, or 2
+cvar_t cl_nolerp = {"cl_nolerp","0"};
+
+cvar_t maxfps = {"maxfps", "100"};
+
+cvar_t lookspring = {"lookspring","0", true};
+cvar_t lookstrafe = {"lookstrafe","0", true};
+cvar_t sensitivity = {"sensitivity","3", true};
+
+cvar_t m_pitch = {"m_pitch","0.022", true};
+cvar_t m_yaw = {"m_yaw","0.022", true};
+cvar_t m_forward = {"m_forward","1", true};
+cvar_t m_side = {"m_side","0.8", true};
+
+
+client_static_t        cls;
+client_state_t cl;
+// FIXME: put these on hunk?
+efrag_t                        cl_efrags[MAX_EFRAGS];
+entity_t               cl_entities[MAX_EDICTS];
+entity_t               cl_static_entities[MAX_STATIC_ENTITIES];
+lightstyle_t   cl_lightstyle[MAX_LIGHTSTYLES];
+dlight_t               cl_dlights[MAX_DLIGHTS];
+
+int                            cl_numvisedicts;
+entity_t               *cl_visedicts[MAX_VISEDICTS];
+
+/*
+=====================
+CL_ClearState
+
+=====================
+*/
+void CL_ClearState (void)
+{
+       int                     i;
+
+       if (!sv.active)
+               Host_ClearMemory ();
+
+// wipe the entire cl structure
+       memset (&cl, 0, sizeof(cl));
+
+       SZ_Clear (&cls.message);
+
+// clear other arrays  
+       memset (cl_efrags, 0, sizeof(cl_efrags));
+       memset (cl_entities, 0, sizeof(cl_entities));
+       memset (cl_dlights, 0, sizeof(cl_dlights));
+       memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
+       memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
+       memset (cl_beams, 0, sizeof(cl_beams));
+       // LordHavoc: have to set up the baseline info for alpha and other stuff
+       for (i = 0;i < MAX_EDICTS;i++)
+       {
+               cl_entities[i].baseline.alpha = 255;
+               cl_entities[i].baseline.scale = 16;
+               cl_entities[i].baseline.glowsize = 0;
+               cl_entities[i].baseline.glowcolor = 254;
+               cl_entities[i].baseline.colormod = 255;
+       }
+
+//
+// allocate the efrags and chain together into a free list
+//
+       cl.free_efrags = cl_efrags;
+       for (i=0 ; i<MAX_EFRAGS-1 ; i++)
+               cl.free_efrags[i].entnext = &cl.free_efrags[i+1];
+       cl.free_efrags[i].entnext = NULL;
+}
+
+/*
+=====================
+CL_Disconnect
+
+Sends a disconnect message to the server
+This is also called on Host_Error, so it shouldn't cause any errors
+=====================
+*/
+void CL_Disconnect (void)
+{
+// stop sounds (especially looping!)
+       S_StopAllSounds (true);
+       
+// bring the console down and fade the colors back to normal
+//     SCR_BringDownConsole ();
+
+// if running a local server, shut it down
+       if (cls.demoplayback)
+               CL_StopPlayback ();
+       else if (cls.state == ca_connected)
+       {
+               if (cls.demorecording)
+                       CL_Stop_f ();
+
+               Con_DPrintf ("Sending clc_disconnect\n");
+               SZ_Clear (&cls.message);
+               MSG_WriteByte (&cls.message, clc_disconnect);
+               NET_SendUnreliableMessage (cls.netcon, &cls.message);
+               SZ_Clear (&cls.message);
+               NET_Close (cls.netcon);
+
+               cls.state = ca_disconnected;
+               if (sv.active)
+                       Host_ShutdownServer(false);
+       }
+
+       cls.demoplayback = cls.timedemo = false;
+       cls.signon = 0;
+}
+
+void CL_Disconnect_f (void)
+{
+       CL_Disconnect ();
+       if (sv.active)
+               Host_ShutdownServer (false);
+}
+
+
+
+
+/*
+=====================
+CL_EstablishConnection
+
+Host should be either "local" or a net address to be passed on
+=====================
+*/
+void CL_EstablishConnection (char *host)
+{
+       if (cls.state == ca_dedicated)
+               return;
+
+       if (cls.demoplayback)
+               return;
+
+       CL_Disconnect ();
+
+       cls.netcon = NET_Connect (host);
+       if (!cls.netcon)
+               Host_Error ("CL_Connect: connect failed\n");
+       Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
+       
+       cls.demonum = -1;                       // not in the demo loop now
+       cls.state = ca_connected;
+       cls.signon = 0;                         // need all the signon messages before playing
+}
+
+extern int numgltextures;
+extern int texels;
+
+/*
+=====================
+CL_SignonReply
+
+An svc_signonnum has been received, perform a client side setup
+=====================
+*/
+void CL_SignonReply (void)
+{
+       char    str[8192];
+
+Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
+
+       switch (cls.signon)
+       {
+       case 1:
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, "prespawn");
+               break;
+               
+       case 2:         
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
+       
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
+       
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               sprintf (str, "spawn %s", cls.spawnparms);
+               MSG_WriteString (&cls.message, str);
+               break;
+               
+       case 3: 
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, "begin");
+               Cache_Report ();                // print remaining memory
+               break;
+               
+       case 4:
+               SCR_EndLoadingPlaque ();                // allow normal screen updates
+               // LordHavoc: debugging purposes
+               Con_DPrintf("GLQuake texture slots in use: %i : %i : %i texels\n", texture_extension_number, numgltextures, texels);
+               break;
+       }
+}
+
+/*
+=====================
+CL_NextDemo
+
+Called to play the next demo in the demo loop
+=====================
+*/
+void CL_NextDemo (void)
+{
+       char    str[1024];
+
+       if (cls.demonum == -1)
+               return;         // don't play demos
+
+       SCR_BeginLoadingPlaque ();
+
+       if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
+       {
+               cls.demonum = 0;
+               if (!cls.demos[cls.demonum][0])
+               {
+                       Con_Printf ("No demos listed with startdemos\n");
+                       cls.demonum = -1;
+                       return;
+               }
+       }
+
+       sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
+       Cbuf_InsertText (str);
+       cls.demonum++;
+}
+
+/*
+==============
+CL_PrintEntities_f
+==============
+*/
+void CL_PrintEntities_f (void)
+{
+       entity_t        *ent;
+       int                     i;
+       
+       for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
+       {
+               Con_Printf ("%3i:",i);
+               if (!ent->model)
+               {
+                       Con_Printf ("EMPTY\n");
+                       continue;
+               }
+               Con_Printf ("%s:%2i  (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n"
+               ,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
+       }
+}
+
+
+/*
+===============
+SetPal
+
+Debugging tool, just flashes the screen
+===============
+*/
+void SetPal (int i)
+{
+#if 0
+       static int old;
+       byte    pal[768];
+       int             c;
+       
+       if (i == old)
+               return;
+       old = i;
+
+       if (i==0)
+               VID_SetPalette (host_basepal);
+       else if (i==1)
+       {
+               for (c=0 ; c<768 ; c+=3)
+               {
+                       pal[c] = 0;
+                       pal[c+1] = 255;
+                       pal[c+2] = 0;
+               }
+               VID_SetPalette (pal);
+       }
+       else
+       {
+               for (c=0 ; c<768 ; c+=3)
+               {
+                       pal[c] = 0;
+                       pal[c+1] = 0;
+                       pal[c+2] = 255;
+               }
+               VID_SetPalette (pal);
+       }
+#endif
+}
+
+/*
+===============
+CL_AllocDlight
+
+===============
+*/
+dlight_t *CL_AllocDlight (int key)
+{
+       int             i;
+       dlight_t        *dl;
+
+// first look for an exact key match
+       if (key)
+       {
+               dl = cl_dlights;
+               for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+               {
+                       if (dl->key == key)
+                       {
+                               memset (dl, 0, sizeof(*dl));
+                               dl->key = key;
+                               return dl;
+                       }
+               }
+       }
+
+// then look for anything else
+       dl = cl_dlights;
+       for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+       {
+               if (dl->die < cl.time)
+               {
+                       memset (dl, 0, sizeof(*dl));
+                       dl->key = key;
+                       return dl;
+               }
+       }
+
+       dl = &cl_dlights[0];
+       memset (dl, 0, sizeof(*dl));
+       dl->key = key;
+       return dl;
+}
+
+
+/*
+===============
+CL_DecayLights
+
+===============
+*/
+void CL_DecayLights (void)
+{
+       int                     i;
+       dlight_t        *dl;
+       float           time;
+       
+       time = cl.time - cl.oldtime;
+
+       dl = cl_dlights;
+       for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+       {
+               if (dl->die < cl.time || !dl->radius)
+                       continue;
+               
+               dl->radius -= time*dl->decay;
+               if (dl->radius < 0)
+                       dl->radius = 0;
+       }
+}
+
+
+/*
+===============
+CL_LerpPoint
+
+Determines the fraction between the last two messages that the objects
+should be put at.
+===============
+*/
+float  CL_LerpPoint (void)
+{
+       float   f, frac;
+
+       f = cl.mtime[0] - cl.mtime[1];
+       
+       if (!f || cl_nolerp.value || cls.timedemo || sv.active)
+       {
+               cl.time = cl.mtime[0];
+               return 1;
+       }
+               
+       if (f > 0.1)
+       {       // dropped packet, or start of demo
+               cl.mtime[1] = cl.mtime[0] - 0.1;
+               f = 0.1;
+       }
+       frac = (cl.time - cl.mtime[1]) / f;
+//Con_Printf ("frac: %f\n",frac);
+       if (frac < 0)
+       {
+               if (frac < -0.01)
+               {
+SetPal(1);
+                       cl.time = cl.mtime[1];
+//                             Con_Printf ("low frac\n");
+               }
+               frac = 0;
+       }
+       else if (frac > 1)
+       {
+               if (frac > 1.01)
+               {
+SetPal(2);
+                       cl.time = cl.mtime[0];
+//                             Con_Printf ("high frac\n");
+               }
+               frac = 1;
+       }
+       else
+               SetPal(0);
+               
+       return frac;
+}
+
+
+/*
+===============
+CL_RelinkEntities
+===============
+*/
+void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent);
+void CL_RelinkEntities (void)
+{
+       entity_t        *ent;
+       int                     i, j;
+       float           frac, f, d;
+       vec3_t          delta;
+       float           bobjrotate;
+       vec3_t          oldorg;
+       dlight_t        *dl;
+       byte            *tempcolor;
+
+// determine partial update time       
+       frac = CL_LerpPoint ();
+
+       cl_numvisedicts = 0;
+
+//
+// interpolate player info
+//
+       for (i=0 ; i<3 ; i++)
+               cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
+
+       if (cls.demoplayback)
+       {
+       // interpolate the angles       
+               for (j=0 ; j<3 ; j++)
+               {
+                       d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
+                       if (d > 180)
+                               d -= 360;
+                       else if (d < -180)
+                               d += 360;
+                       cl.viewangles[j] = cl.mviewangles[1][j] + frac*d;
+               }
+       }
+       
+       bobjrotate = anglemod(100*cl.time);
+       
+// start on the entity after the world
+       for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
+       {
+               if (!ent->model)
+               {       // empty slot
+                       if (ent->forcelink)
+                               R_RemoveEfrags (ent);   // just became empty
+                       continue;
+               }
+
+// if the object wasn't included in the last packet, remove it
+               if (ent->msgtime != cl.mtime[0])
+               {
+                       ent->model = NULL;
+                       continue;
+               }
+
+               VectorCopy (ent->origin, oldorg);
+
+               if (ent->forcelink)
+               {       // the entity was not updated in the last message
+                       // so move to the final spot
+                       VectorCopy (ent->msg_origins[0], ent->origin);
+                       VectorCopy (ent->msg_angles[0], ent->angles);
+               }
+               else
+               {       // if the delta is large, assume a teleport and don't lerp
+                       f = frac;
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
+                               // LordHavoc: increased lerp tolerance from 100 to 200
+                               if (delta[j] > 200 || delta[j] < -200)
+                                       f = 1;          // assume a teleportation, not a motion
+                       }
+
+               // interpolate the origin and angles
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
+
+                               d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
+                               if (d > 180)
+                                       d -= 360;
+                               else if (d < -180)
+                                       d += 360;
+                               ent->angles[j] = ent->msg_angles[1][j] + f*d;
+                       }
+                       
+               }
+
+               if (ent->effects & EF_BRIGHTFIELD)
+                       R_EntityParticles (ent);
+               if (ent->effects & EF_MUZZLEFLASH)
+               {
+                       vec3_t          fv, rv, uv;
+
+                       dl = CL_AllocDlight (i);
+                       VectorCopy (ent->origin,  dl->origin);
+                       dl->origin[2] += 16;
+                       AngleVectors (ent->angles, fv, rv, uv);
+                        
+                       VectorMA (dl->origin, 18, fv, dl->origin);
+                       dl->radius = 200 + (rand()&31);
+                       dl->minlight = 32;
+                       dl->die = cl.time + 0.1;
+                       dl->color[0] = 1.0;dl->color[1] = 1.0;dl->color[2] = 1.0;
+               }
+               if (ent->effects & EF_BRIGHTLIGHT)
+               {                       
+                       dl = CL_AllocDlight (i);
+                       VectorCopy (ent->origin,  dl->origin);
+                       dl->origin[2] += 16;
+                       dl->radius = 400 + (rand()&31);
+                       dl->die = cl.time + 0.001;
+                       dl->color[0] = 1.0;dl->color[1] = 1.0;dl->color[2] = 1.0;
+               }
+               if (ent->effects & EF_DIMLIGHT)
+               {                       
+                       dl = CL_AllocDlight (i);
+                       VectorCopy (ent->origin,  dl->origin);
+                       dl->radius = 200 + (rand()&31);
+                       dl->die = cl.time + 0.001;
+                       dl->color[0] = 1.0;dl->color[1] = 1.0;dl->color[2] = 1.0;
+               }
+               // LordHavoc: added EF_RED and EF_BLUE
+               if (ent->effects & EF_RED) // red
+               {                       
+                       if (ent->effects & EF_BLUE) // magenta
+                       {
+                               dl = CL_AllocDlight (i);
+                               VectorCopy (ent->origin,  dl->origin);
+                               dl->radius = 200 + (rand()&31);
+                               dl->die = cl.time + 0.001;
+                               dl->color[0] = 0.7;dl->color[1] = 0.07;dl->color[2] = 0.7;
+                       }
+                       else // red
+                       {
+                               dl = CL_AllocDlight (i);
+                               VectorCopy (ent->origin,  dl->origin);
+                               dl->radius = 200 + (rand()&31);
+                               dl->die = cl.time + 0.001;
+                               dl->color[0] = 0.8;dl->color[1] = 0.05;dl->color[2] = 0.05;
+                       }
+               }
+               else if (ent->effects & EF_BLUE) // blue
+               {
+                       dl = CL_AllocDlight (i);
+                       VectorCopy (ent->origin,  dl->origin);
+                       dl->radius = 200 + (rand()&31);
+                       dl->die = cl.time + 0.001;
+                       dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
+               }
+
+               if (ent->model->flags) // LordHavoc: if the model has no flags, don't check each
+               {
+               // rotate binary objects locally
+                       if (ent->model->flags & EF_ROTATE)
+                               ent->angles[1] = bobjrotate;
+                       if (ent->model->flags & EF_GIB)
+                               R_RocketTrail (oldorg, ent->origin, 2, ent);
+                       else if (ent->model->flags & EF_ZOMGIB)
+                               R_RocketTrail (oldorg, ent->origin, 4, ent);
+                       else if (ent->model->flags & EF_TRACER)
+                               R_RocketTrail (oldorg, ent->origin, 3, ent);
+                       else if (ent->model->flags & EF_TRACER2)
+                               R_RocketTrail (oldorg, ent->origin, 5, ent);
+                       else if (ent->model->flags & EF_ROCKET)
+                       {
+                               R_RocketTrail (oldorg, ent->origin, 0, ent);
+                               dl = CL_AllocDlight (i);
+                               VectorCopy (ent->origin, dl->origin);
+                               dl->radius = 200;
+                               dl->die = cl.time + 0.001;
+                               dl->color[0] = 1.0;dl->color[1] = 0.8;dl->color[2] = 0.4;
+                       }
+                       else if (ent->model->flags & EF_GRENADE)
+                       {
+                               if (ent->alpha == -1) // LordHavoc: Nehahra dem compatibility
+                                       R_RocketTrail (oldorg, ent->origin, 7, ent);
+                               else
+                                       R_RocketTrail (oldorg, ent->origin, 1, ent);
+                       }
+                       else if (ent->model->flags & EF_TRACER3)
+                               R_RocketTrail (oldorg, ent->origin, 6, ent);
+               }
+               if (ent->glowsize) // LordHavoc: customizable glow
+               {
+                       dl = CL_AllocDlight (i);
+                       VectorCopy (ent->origin, dl->origin);
+                       dl->dark = ent->glowsize < 0; // darklight
+                       dl->radius = ent->glowsize;
+                       if (dl->dark)
+                       {
+                               if (ent->glowtrail) // LordHavoc: all darklights leave black trails
+                                       R_RocketTrail2 (oldorg, ent->origin, 0, ent);
+                               dl->radius = -ent->glowsize;
+                       }
+                       else if (ent->glowtrail) // LordHavoc: customizable glow and trail
+                               R_RocketTrail2 (oldorg, ent->origin, ent->glowcolor, ent);
+                       dl->die = cl.time + 0.001;
+                       tempcolor = (byte *)&d_8to24table[ent->glowcolor];
+                       dl->color[0] = tempcolor[0]*(1.0/255.0);dl->color[1] = tempcolor[1]*(1.0/255.0);dl->color[2] = tempcolor[2]*(1.0/255.0);
+               }
+               else if (ent->glowtrail) // LordHavoc: customizable glow and trail
+                       R_RocketTrail2 (oldorg, ent->origin, ent->glowcolor, ent);
+
+               ent->forcelink = false;
+
+               if (i == cl.viewentity && !chase_active.value)
+                       continue;
+
+// LordHavoc: enabled EF_NODRAW
+               if (!ent->model || ent->effects & EF_NODRAW)
+                       continue;
+               if (cl_numvisedicts < MAX_VISEDICTS)
+               {
+                       cl_visedicts[cl_numvisedicts] = ent;
+                       cl_numvisedicts++;
+               }
+       }
+
+}
+
+
+/*
+===============
+CL_ReadFromServer
+
+Read all incoming data from the server
+===============
+*/
+int CL_ReadFromServer (void)
+{
+       int             ret;
+
+       cl.oldtime = cl.time;
+       cl.time += host_frametime;
+       
+       do
+       {
+               ret = CL_GetMessage ();
+               if (ret == -1)
+                       Host_Error ("CL_ReadFromServer: lost server connection");
+               if (!ret)
+                       break;
+               
+               cl.last_received_message = realtime;
+               CL_ParseServerMessage ();
+       } while (ret && cls.state == ca_connected);
+       
+       if (cl_shownet.value)
+               Con_Printf ("\n");
+
+       CL_RelinkEntities ();
+       CL_UpdateTEnts ();
+
+//
+// bring the links up to date
+//
+       return 0;
+}
+
+/*
+=================
+CL_SendCmd
+=================
+*/
+void CL_SendCmd (void)
+{
+       usercmd_t               cmd;
+
+       if (cls.state != ca_connected)
+               return;
+
+       if (cls.signon == SIGNONS)
+       {
+       // get basic movement from keyboard
+               CL_BaseMove (&cmd);
+       
+       // allow mice or other external controllers to add to the move
+               IN_Move (&cmd);
+       
+       // send the unreliable message
+               CL_SendMove (&cmd);
+       
+       }
+
+       if (cls.demoplayback)
+       {
+               SZ_Clear (&cls.message);
+               return;
+       }
+       
+// send the reliable message
+       if (!cls.message.cursize)
+               return;         // no message at all
+       
+       if (!NET_CanSendMessage (cls.netcon))
+       {
+               Con_DPrintf ("CL_WriteToServer: can't send\n");
+               return;
+       }
+
+       if (NET_SendMessage (cls.netcon, &cls.message) == -1)
+               Host_Error ("CL_WriteToServer: lost server connection");
+
+       SZ_Clear (&cls.message);
+}
+
+// LordHavoc: pausedemo command
+void CL_PauseDemo_f (void)
+{
+       cls.demopaused = !cls.demopaused;
+       if (cls.demopaused)
+               Con_Printf("Demo paused\n");
+       else
+               Con_Printf("Demo unpaused\n");
+}
+
+cvar_t demo_nehahra = {"demo_nehahra", "0"};
+
+/*
+=================
+CL_Init
+=================
+*/
+void CL_Init (void)
+{      
+       SZ_Alloc (&cls.message, 1024);
+
+       CL_InitInput ();
+       CL_InitTEnts ();
+       
+//
+// register our commands
+//
+       Cvar_RegisterVariable (&cl_name);
+       Cvar_RegisterVariable (&cl_color);
+       Cvar_RegisterVariable (&cl_upspeed);
+       Cvar_RegisterVariable (&cl_forwardspeed);
+       Cvar_RegisterVariable (&cl_backspeed);
+       Cvar_RegisterVariable (&cl_sidespeed);
+       Cvar_RegisterVariable (&cl_movespeedkey);
+       Cvar_RegisterVariable (&cl_yawspeed);
+       Cvar_RegisterVariable (&cl_pitchspeed);
+       Cvar_RegisterVariable (&cl_anglespeedkey);
+       Cvar_RegisterVariable (&cl_shownet);
+       Cvar_RegisterVariable (&cl_nolerp);
+       Cvar_RegisterVariable (&maxfps);
+       Cvar_RegisterVariable (&lookspring);
+       Cvar_RegisterVariable (&lookstrafe);
+       Cvar_RegisterVariable (&sensitivity);
+
+       Cvar_RegisterVariable (&m_pitch);
+       Cvar_RegisterVariable (&m_yaw);
+       Cvar_RegisterVariable (&m_forward);
+       Cvar_RegisterVariable (&m_side);
+
+//     Cvar_RegisterVariable (&cl_autofire);
+       
+       Cmd_AddCommand ("entities", CL_PrintEntities_f);
+       Cmd_AddCommand ("disconnect", CL_Disconnect_f);
+       Cmd_AddCommand ("record", CL_Record_f);
+       Cmd_AddCommand ("stop", CL_Stop_f);
+       Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
+       Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
+
+       // LordHavoc: added pausedemo
+       Cmd_AddCommand ("pausedemo", CL_PauseDemo_f);
+       // LordHavoc: added demo_nehahra cvar
+       Cvar_RegisterVariable (&demo_nehahra);
+       if (nehahra)
+               Cvar_SetValue("demo_nehahra", 1);
+}
+
diff --git a/cl_parse.c b/cl_parse.c
new file mode 100644 (file)
index 0000000..d7520d5
--- /dev/null
@@ -0,0 +1,1121 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl_parse.c  -- parse a message received from the server
+
+#include "quakedef.h"
+
+char *svc_strings[] =
+{
+       "svc_bad",
+       "svc_nop",
+       "svc_disconnect",
+       "svc_updatestat",
+       "svc_version",          // [long] server version
+       "svc_setview",          // [short] entity number
+       "svc_sound",                    // <see code>
+       "svc_time",                     // [float] server time
+       "svc_print",                    // [string] null terminated string
+       "svc_stufftext",                // [string] stuffed into client's console buffer
+                                               // the string should be \n terminated
+       "svc_setangle",         // [vec3] set the view angle to this absolute value
+       
+       "svc_serverinfo",               // [long] version
+                                               // [string] signon string
+                                               // [string]..[0]model cache [string]...[0]sounds cache
+                                               // [string]..[0]item cache
+       "svc_lightstyle",               // [byte] [string]
+       "svc_updatename",               // [byte] [string]
+       "svc_updatefrags",      // [byte] [short]
+       "svc_clientdata",               // <shortbits + data>
+       "svc_stopsound",                // <see code>
+       "svc_updatecolors",     // [byte] [byte]
+       "svc_particle",         // [vec3] <variable>
+       "svc_damage",                   // [byte] impact [byte] blood [vec3] from
+       
+       "svc_spawnstatic",
+       "OBSOLETE svc_spawnbinary",
+       "svc_spawnbaseline",
+       
+       "svc_temp_entity",              // <variable>
+       "svc_setpause",
+       "svc_signonnum",
+       "svc_centerprint",
+       "svc_killedmonster",
+       "svc_foundsecret",
+       "svc_spawnstaticsound",
+       "svc_intermission",
+       "svc_finale",                   // [string] music [string] text
+       "svc_cdtrack",                  // [byte] track [byte] looptrack
+       "svc_sellscreen",
+       "svc_cutscene",
+       "svc_showlmp",  // [string] iconlabel [string] lmpfile [byte] x [byte] y
+       "svc_hidelmp",  // [string] iconlabel
+       "svc_skybox", // [string] skyname
+       "?", // 38
+       "?", // 39
+       "?", // 40
+       "?", // 41
+       "?", // 42
+       "?", // 43
+       "?", // 44
+       "?", // 45
+       "?", // 46
+       "?", // 47
+       "?", // 48
+       "?", // 49
+       "svc_skyboxsize", // [coord] size
+       "svc_fog" // [byte] enable <optional past this point, only included if enable is true> [float] density [byte] red [byte] green [byte] blue
+};
+
+//=============================================================================
+
+int Nehahrademcompatibility; // LordHavoc: to allow playback of the early Nehahra movie segments
+
+/*
+===============
+CL_EntityNum
+
+This error checks and tracks the total number of entities
+===============
+*/
+entity_t       *CL_EntityNum (int num)
+{
+       if (num >= cl.num_entities)
+       {
+               if (num >= MAX_EDICTS)
+                       Host_Error ("CL_EntityNum: %i is an invalid number",num);
+               while (cl.num_entities<=num)
+               {
+                       cl_entities[cl.num_entities].colormap = vid.colormap;
+                       cl.num_entities++;
+               }
+       }
+               
+       return &cl_entities[num];
+}
+
+
+/*
+==================
+CL_ParseStartSoundPacket
+==================
+*/
+void CL_ParseStartSoundPacket(void)
+{
+    vec3_t  pos;
+    int        channel, ent;
+    int        sound_num;
+    int        volume;
+    int        field_mask;
+    float      attenuation;  
+       int             i;
+                  
+    field_mask = MSG_ReadByte(); 
+
+    if (field_mask & SND_VOLUME)
+               volume = MSG_ReadByte ();
+       else
+               volume = DEFAULT_SOUND_PACKET_VOLUME;
+       
+    if (field_mask & SND_ATTENUATION)
+               attenuation = MSG_ReadByte () / 64.0;
+       else
+               attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+       
+       channel = MSG_ReadShort ();
+       sound_num = MSG_ReadByte ();
+
+       ent = channel >> 3;
+       channel &= 7;
+
+       if (ent > MAX_EDICTS)
+               Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent);
+       
+       for (i=0 ; i<3 ; i++)
+               pos[i] = MSG_ReadCoord ();
+    S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
+}       
+
+/*
+==================
+CL_KeepaliveMessage
+
+When the client is taking a long time to load stuff, send keepalive messages
+so the server doesn't disconnect.
+==================
+*/
+void CL_KeepaliveMessage (void)
+{
+       float   time;
+       static float lastmsg;
+       int             ret;
+       sizebuf_t       old;
+       byte            olddata[8192];
+       
+       if (sv.active)
+               return;         // no need if server is local
+       if (cls.demoplayback)
+               return;
+
+// read messages from server, should just be nops
+       old = net_message;
+       memcpy (olddata, net_message.data, net_message.cursize);
+       
+       do
+       {
+               ret = CL_GetMessage ();
+               switch (ret)
+               {
+               default:
+                       Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed");               
+               case 0:
+                       break;  // nothing waiting
+               case 1:
+                       Host_Error ("CL_KeepaliveMessage: received a message");
+                       break;
+               case 2:
+                       if (MSG_ReadByte() != svc_nop)
+                               Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop");
+                       break;
+               }
+       } while (ret);
+
+       net_message = old;
+       memcpy (net_message.data, olddata, net_message.cursize);
+
+// check time
+       time = Sys_FloatTime ();
+       if (time - lastmsg < 5)
+               return;
+       lastmsg = time;
+
+// write out a nop
+       Con_Printf ("--> client to server keepalive\n");
+
+       MSG_WriteByte (&cls.message, clc_nop);
+       NET_SendMessage (cls.netcon, &cls.message);
+       SZ_Clear (&cls.message);
+}
+
+extern qboolean isworldmodel;
+extern char skyname[];
+extern cvar_t r_fogdensity;
+extern cvar_t r_fogred;
+extern cvar_t r_foggreen;
+extern cvar_t r_fogblue;
+extern void R_SetSkyBox (char *sky);
+extern void FOG_clear();
+
+void CL_ParseEntityLump(char *entdata)
+{
+       char *data;
+       char key[128], value[1024];
+       char wadname[128];
+       int i, j, k;
+       FOG_clear(); // LordHavoc: no fog until set
+       skyname[0] = 0; // LordHavoc: no enviroment mapped sky until set
+//     r_skyboxsize.value = 4096; // LordHavoc: default skyboxsize
+       data = entdata;
+       if (!data)
+               return;
+       data = COM_Parse(data);
+       if (!data)
+               return; // valid exit
+       if (com_token[0] != '{')
+               return; // error
+       while (1)
+       {
+               data = COM_Parse(data);
+               if (!data)
+                       return; // error
+               if (com_token[0] == '}')
+                       return; // since we're just parsing the first ent (worldspawn), exit
+               strcpy(key, com_token);
+               while (key[strlen(key)-1] == ' ') // remove trailing spaces
+                       key[strlen(key)-1] = 0;
+               data = COM_Parse(data);
+               if (!data)
+                       return; // error
+               strcpy(value, com_token);
+               if (!strcmp("sky", key))
+                       R_SetSkyBox(value);
+               else if (!strcmp("skyname", key)) // non-standard, introduced by QuakeForge... sigh.
+                       R_SetSkyBox(value);
+               else if (!strcmp("qlsky", key)) // non-standard, introduced by QuakeLives (EEK)
+                       R_SetSkyBox(value);
+//             else if (!strcmp("skyboxsize", key))
+//             {
+//                     r_skyboxsize.value = atof(value);
+//                     if (r_skyboxsize.value < 64)
+//                             r_skyboxsize.value = 64;
+//             }
+               else if (!strcmp("fog_density", key))
+                       r_fogdensity.value = atof(value);
+               else if (!strcmp("fog_red", key))
+                       r_fogred.value = atof(value);
+               else if (!strcmp("fog_green", key))
+                       r_foggreen.value = atof(value);
+               else if (!strcmp("fog_blue", key))
+                       r_fogblue.value = atof(value);
+               else if (!strcmp("wad", key)) // for HalfLife maps
+               {
+                       j = 0;
+                       for (i = 0;i < 128;i++)
+                               if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
+                                       break;
+                       if (value[i])
+                       {
+                               for (;i < 128;i++)
+                               {
+                                       // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
+                                       if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
+                                               j = i+1;
+                                       else if (value[i] == ';' || value[i] == 0)
+                                       {
+                                               k = value[i];
+                                               value[i] = 0;
+                                               strcpy(wadname, "textures/");
+                                               strcat(wadname, &value[j]);
+                                               W_LoadTextureWadFile (wadname, FALSE);
+                                               j = i+1;
+                                               if (!k)
+                                                       break;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/*
+==================
+CL_ParseServerInfo
+==================
+*/
+extern cvar_t demo_nehahra;
+void CL_ParseServerInfo (void)
+{
+       char    *str;
+       int             i;
+       int             nummodels, numsounds;
+       char    model_precache[MAX_MODELS][MAX_QPATH];
+       char    sound_precache[MAX_SOUNDS][MAX_QPATH];
+       
+       Con_DPrintf ("Serverinfo packet received.\n");
+//
+// wipe the client_state_t struct
+//
+       CL_ClearState ();
+
+// parse protocol version number
+       i = MSG_ReadLong ();
+       if (i != PROTOCOL_VERSION && i != 250)
+       {
+               Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION);
+               return;
+       }
+       Nehahrademcompatibility = false;
+       if (i == 250)
+               Nehahrademcompatibility = true;
+       if (cls.demoplayback && demo_nehahra.value)
+               Nehahrademcompatibility = true;
+
+// parse maxclients
+       cl.maxclients = MSG_ReadByte ();
+       if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
+       {
+               Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients);
+               return;
+       }
+       cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores");
+
+// parse gametype
+       cl.gametype = MSG_ReadByte ();
+
+// parse signon message
+       str = MSG_ReadString ();
+       strncpy (cl.levelname, str, sizeof(cl.levelname)-1);
+
+// seperate the printfs so the server message can have a color
+       if (!Nehahrademcompatibility) // no messages when playing the Nehahra movie
+       {
+               Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+               Con_Printf ("%c%s\n", 2, str);
+       }
+
+//
+// first we go through and touch all of the precache data that still
+// happens to be in the cache, so precaching something else doesn't
+// needlessly purge it
+//
+
+// precache models
+       memset (cl.model_precache, 0, sizeof(cl.model_precache));
+       for (nummodels=1 ; ; nummodels++)
+       {
+               str = MSG_ReadString ();
+               if (!str[0])
+                       break;
+               if (nummodels==MAX_MODELS)
+               {
+                       Con_Printf ("Server sent too many model precaches\n");
+                       return;
+               }
+               strcpy (model_precache[nummodels], str);
+               Mod_TouchModel (str);
+       }
+
+// precache sounds
+       memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
+       for (numsounds=1 ; ; numsounds++)
+       {
+               str = MSG_ReadString ();
+               if (!str[0])
+                       break;
+               if (numsounds==MAX_SOUNDS)
+               {
+                       Con_Printf ("Server sent too many sound precaches\n");
+                       return;
+               }
+               strcpy (sound_precache[numsounds], str);
+               S_TouchSound (str);
+       }
+
+//
+// now we try to load everything else until a cache allocation fails
+//
+
+       for (i=1 ; i<nummodels ; i++)
+       {
+               isworldmodel = i == 1; // LordHavoc: first model is the world model
+               cl.model_precache[i] = Mod_ForName (model_precache[i], false);
+               if (cl.model_precache[i] == NULL)
+               {
+                       Con_Printf("Model %s not found\n", model_precache[i]);
+                       return;
+               }
+               CL_KeepaliveMessage ();
+       }
+
+       S_BeginPrecaching ();
+       for (i=1 ; i<numsounds ; i++)
+       {
+               cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]);
+               CL_KeepaliveMessage ();
+       }
+       S_EndPrecaching ();
+
+
+// local state
+       cl_entities[0].model = cl.worldmodel = cl.model_precache[1];
+       
+       R_NewMap ();
+
+       Hunk_Check ();          // make sure nothing is hurt
+       
+       noclip_anglehack = false;               // noclip is turned off at start        
+}
+
+
+/*
+==================
+CL_ParseUpdate
+
+Parse an entity update message from the server
+If an entities model or origin changes from frame to frame, it must be
+relinked.  Other attributes can change without relinking.
+==================
+*/
+//int  bitcounts[16];
+
+void CL_ParseUpdate (int bits)
+{
+       int                     i, modnum, num, skin, alpha, scale, glowsize, glowcolor, colormod;
+       model_t         *model;
+       qboolean        forcelink;
+       entity_t        *ent;
+       entity_state_t *baseline;
+
+       if (cls.signon == SIGNONS - 1)
+       {       // first update is the final signon stage
+               cls.signon = SIGNONS;
+               CL_SignonReply ();
+       }
+
+       if (bits & U_MOREBITS)
+       {
+               i = MSG_ReadByte ();
+               bits |= (i<<8);
+       }
+       if (bits & U_EXTEND1 && !Nehahrademcompatibility)
+       {
+               bits |= MSG_ReadByte() << 16;
+               if (bits & U_EXTEND2)
+                       bits |= MSG_ReadByte() << 24;
+       }
+
+       if (bits & U_LONGENTITY)        
+               num = MSG_ReadShort ();
+       else
+               num = MSG_ReadByte ();
+
+       ent = CL_EntityNum (num);
+
+//for (i=0 ; i<16 ; i++)
+//if (bits&(1<<i))
+//     bitcounts[i]++;
+
+       forcelink = ent->msgtime != cl.mtime[1]; // no previous frame to lerp from
+
+       ent->msgtime = cl.mtime[0];
+       
+       // LordHavoc: new protocol stuff
+       baseline = &ent->baseline;
+       if (bits & U_DELTA)
+               baseline = &ent->deltabaseline;
+
+       modnum = bits & U_MODEL ? MSG_ReadByte() : baseline->modelindex;
+       if (modnum >= MAX_MODELS)
+               Host_Error ("CL_ParseModel: bad modnum");
+       ent->deltabaseline.modelindex = modnum;
+               
+       model = cl.model_precache[modnum];
+       if (model != ent->model)
+       {
+               ent->model = model;
+       // automatic animation (torches, etc) can be either all together
+       // or randomized
+               if (model)
+                       ent->syncbase = model->synctype == ST_RAND ? (float)(rand()&0x7fff) / 0x7fff : 0.0;
+               else
+                       forcelink = true;       // hack to make null model players work
+               if (num > 0 && num <= cl.maxclients)
+                       R_TranslatePlayerSkin (num - 1);
+       }
+
+       ent->frame = ((bits & U_FRAME) ? MSG_ReadByte() : (baseline->frame & 0xFF));
+
+       i = bits & U_COLORMAP ? MSG_ReadByte() : baseline->colormap;
+       ent->deltabaseline.colormap = i;
+       if (!i)
+               ent->colormap = vid.colormap;
+       else
+       {
+               if (i > cl.maxclients)
+                       Sys_Error ("i >= cl.maxclients");
+               ent->colormap = cl.scores[i-1].translations;
+       }
+
+       skin = bits & U_SKIN ? MSG_ReadByte() : baseline->skin;
+       if (skin != ent->skinnum)
+       {
+               ent->skinnum = skin;
+               if (num > 0 && num <= cl.maxclients)
+                       R_TranslatePlayerSkin (num - 1);
+       }
+       ent->deltabaseline.skin = skin;
+
+       ent->effects = ((bits & U_EFFECTS) ? MSG_ReadByte() : (baseline->effects & 0xFF));
+
+// shift the known values for interpolation
+       VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
+       VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
+       VectorCopy (baseline->origin, ent->msg_origins[0]);
+       VectorCopy (baseline->angles, ent->msg_angles[0]);
+
+       if (bits & U_ORIGIN1) ent->msg_origins[0][0] = MSG_ReadCoord ();
+       if (bits & U_ANGLE1) ent->msg_angles[0][0] = MSG_ReadAngle();
+       if (bits & U_ORIGIN2) ent->msg_origins[0][1] = MSG_ReadCoord ();
+       if (bits & U_ANGLE2) ent->msg_angles[0][1] = MSG_ReadAngle();
+       if (bits & U_ORIGIN3) ent->msg_origins[0][2] = MSG_ReadCoord ();
+       if (bits & U_ANGLE3) ent->msg_angles[0][2] = MSG_ReadAngle();
+
+       VectorCopy(ent->msg_origins[0], ent->deltabaseline.origin);
+       VectorCopy(ent->msg_angles[0], ent->deltabaseline.angles);
+
+       alpha = bits & U_ALPHA ? MSG_ReadByte() : baseline->alpha;
+       scale = bits & U_SCALE ? MSG_ReadByte() : baseline->scale;
+       ent->effects |= ((bits & U_EFFECTS2) ? (MSG_ReadByte() << 8) : (baseline->effects & 0xFF00));
+       glowsize = bits & U_GLOWSIZE ? MSG_ReadByte() : baseline->glowsize;
+       glowcolor = bits & U_GLOWCOLOR ? MSG_ReadByte() : baseline->glowcolor;
+       colormod = bits & U_COLORMOD ? MSG_ReadByte() : baseline->colormod;
+       ent->frame |= ((bits & U_FRAME2) ? (MSG_ReadByte() << 8) : (baseline->frame & 0xFF00));
+       ent->deltabaseline.alpha = alpha;
+       ent->deltabaseline.scale = scale;
+       ent->deltabaseline.effects = ent->effects;
+       ent->deltabaseline.glowsize = glowsize;
+       ent->deltabaseline.glowcolor = glowcolor;
+       ent->deltabaseline.colormod = colormod;
+       ent->deltabaseline.frame = ent->frame;
+       ent->alpha = (float) alpha * (1.0 / 255.0);
+       ent->scale = (float) scale * (1.0 / 16.0);
+       ent->glowsize = glowsize < 128 ? glowsize * 8.0 : (glowsize - 256) * 8.0;
+       ent->glowcolor = glowcolor;
+       ent->colormod[0] = (float) ((colormod >> 5) & 7) * (1.0 / 7.0);
+       ent->colormod[1] = (float) ((colormod >> 2) & 7) * (1.0 / 7.0);
+       ent->colormod[2] = (float) (colormod & 3) * (1.0 / 3.0);
+       if (bits & U_EXTEND1 && Nehahrademcompatibility) // LordHavoc: to allow playback of the early Nehahra movie segments
+       {
+               i = MSG_ReadFloat();
+               ent->alpha = MSG_ReadFloat();
+               if (i == 2 && MSG_ReadFloat() != 0.0)
+                       ent->effects |= EF_FULLBRIGHT;
+               if (ent->alpha == 0)
+                       ent->alpha = 1;
+       }
+
+       //if ( bits & U_NOLERP )
+       //      ent->forcelink = true;
+       //if (bits & U_STEP) // FIXME: implement clientside interpolation of monsters
+
+       if ( forcelink )
+       {       // didn't have an update last message
+               VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
+               VectorCopy (ent->msg_origins[0], ent->origin);
+               VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
+               VectorCopy (ent->msg_angles[0], ent->angles);
+               ent->forcelink = true;
+       }
+}
+
+/*
+==================
+CL_ParseBaseline
+==================
+*/
+void CL_ParseBaseline (entity_t *ent)
+{
+       int                     i;
+       
+       ent->baseline.modelindex = MSG_ReadByte ();
+       ent->baseline.frame = MSG_ReadByte ();
+       ent->baseline.colormap = MSG_ReadByte();
+       ent->baseline.skin = MSG_ReadByte();
+       for (i=0 ; i<3 ; i++)
+       {
+               ent->baseline.origin[i] = MSG_ReadCoord ();
+               ent->baseline.angles[i] = MSG_ReadAngle ();
+       }
+       ent->baseline.alpha = 255;
+       ent->baseline.scale = 16;
+       ent->baseline.glowsize = 0;
+       ent->baseline.glowcolor = 254;
+       ent->baseline.colormod = 255;
+}
+
+
+/*
+==================
+CL_ParseClientdata
+
+Server information pertaining to this client only
+==================
+*/
+void CL_ParseClientdata (int bits)
+{
+       int             i, j;
+       
+       if (bits & SU_VIEWHEIGHT)
+               cl.viewheight = MSG_ReadChar ();
+       else
+               cl.viewheight = DEFAULT_VIEWHEIGHT;
+
+       if (bits & SU_IDEALPITCH)
+               cl.idealpitch = MSG_ReadChar ();
+       else
+               cl.idealpitch = 0;
+       
+       VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
+       for (i=0 ; i<3 ; i++)
+       {
+               if (bits & (SU_PUNCH1<<i) )
+                       cl.punchangle[i] = MSG_ReadChar();
+               else
+                       cl.punchangle[i] = 0;
+               if (bits & (SU_VELOCITY1<<i) )
+                       cl.mvelocity[0][i] = MSG_ReadChar()*16;
+               else
+                       cl.mvelocity[0][i] = 0;
+       }
+
+// [always sent]       if (bits & SU_ITEMS)
+               i = MSG_ReadLong ();
+
+       if (cl.items != i)
+       {       // set flash times
+//             Sbar_Changed ();
+               for (j=0 ; j<32 ; j++)
+                       if ( (i & (1<<j)) && !(cl.items & (1<<j)))
+                               cl.item_gettime[j] = cl.time;
+               cl.items = i;
+       }
+               
+       cl.onground = (bits & SU_ONGROUND) != 0;
+       cl.inwater = (bits & SU_INWATER) != 0;
+
+       if (bits & SU_WEAPONFRAME)
+               cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte ();
+       else
+               cl.stats[STAT_WEAPONFRAME] = 0;
+
+       if (bits & SU_ARMOR)
+               i = MSG_ReadByte ();
+       else
+               i = 0;
+       if (cl.stats[STAT_ARMOR] != i)
+       {
+               cl.stats[STAT_ARMOR] = i;
+//             Sbar_Changed ();
+       }
+
+       if (bits & SU_WEAPON)
+               i = MSG_ReadByte ();
+       else
+               i = 0;
+       if (cl.stats[STAT_WEAPON] != i)
+       {
+               cl.stats[STAT_WEAPON] = i;
+//             Sbar_Changed ();
+       }
+       
+       i = MSG_ReadShort ();
+       if (cl.stats[STAT_HEALTH] != i)
+       {
+               cl.stats[STAT_HEALTH] = i;
+//             Sbar_Changed ();
+       }
+
+       i = MSG_ReadByte ();
+       if (cl.stats[STAT_AMMO] != i)
+       {
+               cl.stats[STAT_AMMO] = i;
+//             Sbar_Changed ();
+       }
+
+       for (i=0 ; i<4 ; i++)
+       {
+               j = MSG_ReadByte ();
+               if (cl.stats[STAT_SHELLS+i] != j)
+               {
+                       cl.stats[STAT_SHELLS+i] = j;
+//                     Sbar_Changed ();
+               }
+       }
+
+       i = MSG_ReadByte ();
+
+       if (standard_quake)
+       {
+               if (cl.stats[STAT_ACTIVEWEAPON] != i)
+               {
+                       cl.stats[STAT_ACTIVEWEAPON] = i;
+//                     Sbar_Changed ();
+               }
+       }
+       else
+       {
+               if (cl.stats[STAT_ACTIVEWEAPON] != (1<<i))
+               {
+                       cl.stats[STAT_ACTIVEWEAPON] = (1<<i);
+//                     Sbar_Changed ();
+               }
+       }
+}
+
+/*
+=====================
+CL_NewTranslation
+=====================
+*/
+void CL_NewTranslation (int slot)
+{
+       int             i, j;
+       int             top, bottom;
+       byte    *dest, *source;
+       
+       if (slot > cl.maxclients)
+               Sys_Error ("CL_NewTranslation: slot > cl.maxclients");
+       dest = cl.scores[slot].translations;
+       source = vid.colormap;
+       memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations));
+       top = cl.scores[slot].colors & 0xf0;
+       bottom = (cl.scores[slot].colors &15)<<4;
+       R_TranslatePlayerSkin (slot);
+
+       for (i=0 ; i<VID_GRADES ; i++, dest += 256, source+=256)
+       {
+               // LordHavoc: corrected color ranges
+               if (top < 128 || (top >= 224 && top < 240))     // the artists made some backwards ranges.  sigh.
+                       memcpy (dest + TOP_RANGE, source + top, 16);
+               else
+                       for (j=0 ; j<16 ; j++)
+                               dest[TOP_RANGE+j] = source[top+15-j];
+                               
+               // LordHavoc: corrected color ranges
+               if (bottom < 128 || (bottom >= 224 && bottom < 240))
+                       memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
+               else
+                       for (j=0 ; j<16 ; j++)
+                               dest[BOTTOM_RANGE+j] = source[bottom+15-j];             
+       }
+}
+
+/*
+=====================
+CL_ParseStatic
+=====================
+*/
+void CL_ParseStatic (void)
+{
+       entity_t *ent;
+       int             i;
+               
+       i = cl.num_statics;
+       if (i >= MAX_STATIC_ENTITIES)
+               Host_Error ("Too many static entities");
+       ent = &cl_static_entities[i];
+       cl.num_statics++;
+       CL_ParseBaseline (ent);
+
+// copy it to the current state
+       ent->model = cl.model_precache[ent->baseline.modelindex];
+       ent->frame = ent->baseline.frame;
+       ent->colormap = vid.colormap;
+       ent->skinnum = ent->baseline.skin;
+       ent->effects = ent->baseline.effects;
+       ent->alpha = 1;
+       ent->scale = 1;
+       ent->alpha = 1;
+       ent->glowsize = 0;
+       ent->glowcolor = 254;
+       ent->colormod[0] = ent->colormod[1] = ent->colormod[2] = 1;
+
+       VectorCopy (ent->baseline.origin, ent->origin);
+       VectorCopy (ent->baseline.angles, ent->angles); 
+       R_AddEfrags (ent);
+}
+
+/*
+===================
+CL_ParseStaticSound
+===================
+*/
+void CL_ParseStaticSound (void)
+{
+       vec3_t          org;
+       int                     sound_num, vol, atten;
+       int                     i;
+       
+       for (i=0 ; i<3 ; i++)
+               org[i] = MSG_ReadCoord ();
+       sound_num = MSG_ReadByte ();
+       vol = MSG_ReadByte ();
+       atten = MSG_ReadByte ();
+       
+       S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
+}
+
+
+#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
+
+extern void SHOWLMP_decodehide();
+extern void SHOWLMP_decodeshow();
+extern void R_SetSkyBox(char* sky);
+
+extern cvar_t r_fogdensity;
+extern cvar_t r_fogred;
+extern cvar_t r_foggreen;
+extern cvar_t r_fogblue;
+
+/*
+=====================
+CL_ParseServerMessage
+=====================
+*/
+void CL_ParseServerMessage (void)
+{
+       int                     cmd;
+       int                     i;
+       
+//
+// if recording demos, copy the message out
+//
+       if (cl_shownet.value == 1)
+               Con_Printf ("%i ",net_message.cursize);
+       else if (cl_shownet.value == 2)
+               Con_Printf ("------------------\n");
+       
+       cl.onground = false;    // unless the server says otherwise     
+//
+// parse the message
+//
+       MSG_BeginReading ();
+       
+       while (1)
+       {
+               if (msg_badread)
+                       Host_Error ("CL_ParseServerMessage: Bad server message");
+
+               cmd = MSG_ReadByte ();
+
+               if (cmd == -1)
+               {
+                       SHOWNET("END OF MESSAGE");
+                       return;         // end of message
+               }
+
+       // if the high bit of the command byte is set, it is a fast update
+               if (cmd & 128)
+               {
+                       SHOWNET("fast update");
+                       CL_ParseUpdate (cmd&127);
+                       continue;
+               }
+
+               SHOWNET(svc_strings[cmd]);
+       
+       // other commands
+               switch (cmd)
+               {
+               default:
+                       Host_Error ("CL_ParseServerMessage: Illegible server message\n");
+                       break;
+                       
+               case svc_nop:
+//                     Con_Printf ("svc_nop\n");
+                       break;
+                       
+               case svc_time:
+                       cl.mtime[1] = cl.mtime[0];
+                       cl.mtime[0] = MSG_ReadFloat ();                 
+                       break;
+                       
+               case svc_clientdata:
+                       i = MSG_ReadShort ();
+                       CL_ParseClientdata (i);
+                       break;
+               
+               case svc_version:
+                       i = MSG_ReadLong ();
+                       if (i != PROTOCOL_VERSION && i != 250)
+                               Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION);
+                       Nehahrademcompatibility = i == 250;
+                       break;
+                       
+               case svc_disconnect:
+                       Host_EndGame ("Server disconnected\n");
+
+               case svc_print:
+                       Con_Printf ("%s", MSG_ReadString ());
+                       break;
+                       
+               case svc_centerprint:
+                       SCR_CenterPrint (MSG_ReadString ());
+                       break;
+                       
+               case svc_stufftext:
+                       Cbuf_AddText (MSG_ReadString ());
+                       break;
+                       
+               case svc_damage:
+                       V_ParseDamage ();
+                       break;
+                       
+               case svc_serverinfo:
+                       CL_ParseServerInfo ();
+                       vid.recalc_refdef = true;       // leave intermission full screen
+                       break;
+                       
+               case svc_setangle:
+                       for (i=0 ; i<3 ; i++)
+                               cl.viewangles[i] = MSG_ReadAngle ();
+                       break;
+                       
+               case svc_setview:
+                       cl.viewentity = MSG_ReadShort ();
+                       break;
+                                       
+               case svc_lightstyle:
+                       i = MSG_ReadByte ();
+                       if (i >= MAX_LIGHTSTYLES)
+                               Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES");
+                       strcpy (cl_lightstyle[i].map,  MSG_ReadString());
+                       cl_lightstyle[i].length = strlen(cl_lightstyle[i].map);
+                       break;
+                       
+               case svc_sound:
+                       CL_ParseStartSoundPacket();
+                       break;
+                       
+               case svc_stopsound:
+                       i = MSG_ReadShort();
+                       S_StopSound(i>>3, i&7);
+                       break;
+               
+               case svc_updatename:
+//                     Sbar_Changed ();
+                       i = MSG_ReadByte ();
+                       if (i >= cl.maxclients)
+                               Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD");
+                       strcpy (cl.scores[i].name, MSG_ReadString ());
+                       break;
+                       
+               case svc_updatefrags:
+//                     Sbar_Changed ();
+                       i = MSG_ReadByte ();
+                       if (i >= cl.maxclients)
+                               Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
+                       cl.scores[i].frags = MSG_ReadShort ();
+                       break;                  
+
+               case svc_updatecolors:
+//                     Sbar_Changed ();
+                       i = MSG_ReadByte ();
+                       if (i >= cl.maxclients)
+                               Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD");
+                       cl.scores[i].colors = MSG_ReadByte ();
+                       CL_NewTranslation (i);
+                       break;
+                       
+               case svc_particle:
+                       R_ParseParticleEffect ();
+                       break;
+
+               case svc_spawnbaseline:
+                       i = MSG_ReadShort ();
+                       // must use CL_EntityNum() to force cl.num_entities up
+                       CL_ParseBaseline (CL_EntityNum(i));
+                       break;
+               case svc_spawnstatic:
+                       CL_ParseStatic ();
+                       break;                  
+               case svc_temp_entity:
+                       CL_ParseTEnt ();
+                       break;
+
+               case svc_setpause:
+                       {
+                               cl.paused = MSG_ReadByte ();
+
+                               if (cl.paused)
+                               {
+                                       CDAudio_Pause ();
+#ifdef _WIN32
+                                       VID_HandlePause (true);
+#endif
+                               }
+                               else
+                               {
+                                       CDAudio_Resume ();
+#ifdef _WIN32
+                                       VID_HandlePause (false);
+#endif
+                               }
+                       }
+                       break;
+                       
+               case svc_signonnum:
+                       i = MSG_ReadByte ();
+                       if (i <= cls.signon)
+                               Host_Error ("Received signon %i when at %i", i, cls.signon);
+                       cls.signon = i;
+                       CL_SignonReply ();
+                       break;
+
+               case svc_killedmonster:
+                       cl.stats[STAT_MONSTERS]++;
+                       break;
+
+               case svc_foundsecret:
+                       cl.stats[STAT_SECRETS]++;
+                       break;
+
+               case svc_updatestat:
+                       i = MSG_ReadByte ();
+                       if (i < 0 || i >= MAX_CL_STATS)
+                               Sys_Error ("svc_updatestat: %i is invalid", i);
+                       cl.stats[i] = MSG_ReadLong ();;
+                       break;
+                       
+               case svc_spawnstaticsound:
+                       CL_ParseStaticSound ();
+                       break;
+
+               case svc_cdtrack:
+                       cl.cdtrack = MSG_ReadByte ();
+                       cl.looptrack = MSG_ReadByte ();
+                       if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
+                               CDAudio_Play ((byte)cls.forcetrack, true);
+                       else
+                               CDAudio_Play ((byte)cl.cdtrack, true);
+                       break;
+
+               case svc_intermission:
+                       cl.intermission = 1;
+                       cl.completed_time = cl.time;
+                       vid.recalc_refdef = true;       // go to full screen
+                       break;
+
+               case svc_finale:
+                       cl.intermission = 2;
+                       cl.completed_time = cl.time;
+                       vid.recalc_refdef = true;       // go to full screen
+                       SCR_CenterPrint (MSG_ReadString ());                    
+                       break;
+
+               case svc_cutscene:
+                       cl.intermission = 3;
+                       cl.completed_time = cl.time;
+                       vid.recalc_refdef = true;       // go to full screen
+                       SCR_CenterPrint (MSG_ReadString ());                    
+                       break;
+
+               case svc_sellscreen:
+                       Cmd_ExecuteString ("help", src_command);
+                       break;
+               case svc_hidelmp:
+                       SHOWLMP_decodehide();
+                       break;
+               case svc_showlmp:
+                       SHOWLMP_decodeshow();
+                       break;
+       // LordHavoc: extra worldspawn fields (fog, sky, skyboxsize)
+               case svc_skybox:
+                       R_SetSkyBox(MSG_ReadString());
+                       break;
+               case svc_skyboxsize:
+                       /*r_skyboxsize.value = */MSG_ReadCoord();
+                       break;
+               case svc_fog:
+                       if (MSG_ReadByte())
+                       {
+                               r_fogdensity.value = MSG_ReadFloat();
+                               r_fogred.value = MSG_ReadByte() * (1.0 / 255.0);
+                               r_foggreen.value = MSG_ReadByte() * (1.0 / 255.0);
+                               r_fogblue.value = MSG_ReadByte() * (1.0 / 255.0);
+                       }
+                       else
+                               r_fogdensity.value = 0.0f;
+                       break;
+               }
+       }
+}
+
diff --git a/cl_tent.c b/cl_tent.c
new file mode 100644 (file)
index 0000000..bbb4812
--- /dev/null
+++ b/cl_tent.c
@@ -0,0 +1,587 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl_tent.c -- client side temporary entities
+
+#include "quakedef.h"
+
+int                    num_temp_entities;
+entity_t       cl_temp_entities[MAX_TEMP_ENTITIES];
+beam_t         cl_beams[MAX_BEAMS];
+
+sfx_t                  *cl_sfx_wizhit;
+sfx_t                  *cl_sfx_knighthit;
+sfx_t                  *cl_sfx_tink1;
+sfx_t                  *cl_sfx_ric1;
+sfx_t                  *cl_sfx_ric2;
+sfx_t                  *cl_sfx_ric3;
+sfx_t                  *cl_sfx_r_exp3;
+
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+void CL_InitTEnts (void)
+{
+       cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
+       cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
+       cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
+       cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
+       cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
+       cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
+       cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
+}
+
+/*
+=================
+CL_ParseBeam
+=================
+*/
+void CL_ParseBeam (model_t *m)
+{
+       int             ent;
+       vec3_t  start, end;
+       beam_t  *b;
+       int             i;
+       
+       ent = MSG_ReadShort ();
+       
+       start[0] = MSG_ReadCoord ();
+       start[1] = MSG_ReadCoord ();
+       start[2] = MSG_ReadCoord ();
+       
+       end[0] = MSG_ReadCoord ();
+       end[1] = MSG_ReadCoord ();
+       end[2] = MSG_ReadCoord ();
+
+// override any beam with the same entity
+       for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+               if (b->entity == ent)
+               {
+                       b->entity = ent;
+                       b->model = m;
+                       b->endtime = cl.time + 0.2;
+                       VectorCopy (start, b->start);
+                       VectorCopy (end, b->end);
+                       return;
+               }
+
+// find a free beam
+       for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+       {
+               if (!b->model || b->endtime < cl.time)
+               {
+                       b->entity = ent;
+                       b->model = m;
+                       b->endtime = cl.time + 0.2;
+                       VectorCopy (start, b->start);
+                       VectorCopy (end, b->end);
+                       return;
+               }
+       }
+       Con_Printf ("beam list overflow!\n");   
+}
+
+void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count);
+void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel);
+void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type);
+
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+void CL_ParseTEnt (void)
+{
+       int             type;
+       vec3_t  pos;
+       vec3_t  dir;
+       vec3_t  pos2;
+       dlight_t        *dl;
+       int             rnd;
+       int             colorStart, colorLength, count;
+       float   velspeed;
+       byte *tempcolor;
+
+       type = MSG_ReadByte ();
+       switch (type)
+       {
+       case TE_WIZSPIKE:                       // spike hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_RunParticleEffect (pos, vec3_origin, 20, 30);
+               S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
+               break;
+               
+       case TE_KNIGHTSPIKE:                    // spike hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_RunParticleEffect (pos, vec3_origin, 226, 20);
+               S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
+               break;
+               
+       case TE_SPIKE:                  // spike hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               // LordHavoc: changed to spark shower
+               R_SparkShower(pos, vec3_origin, 15, 0);
+               //R_RunParticleEffect (pos, vec3_origin, 0, 10);
+               if ( rand() % 5 )
+                       S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+               else
+               {
+                       rnd = rand() & 3;
+                       if (rnd == 1)
+                               S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
+                       else if (rnd == 2)
+                               S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+                       else
+                               S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+               }
+               break;
+       case TE_SPIKEQUAD:                      // quad spike hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               // LordHavoc: changed to spark shower
+               R_SparkShower(pos, vec3_origin, 15, 0);
+               //R_RunParticleEffect (pos, vec3_origin, 0, 10);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 200;
+               dl->die = cl.time + 0.2;
+               dl->decay = 1000;
+               dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               if ( rand() % 5 )
+                       S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+               else
+               {
+                       rnd = rand() & 3;
+                       if (rnd == 1)
+                               S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
+                       else if (rnd == 2)
+                               S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+                       else
+                               S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+               }
+               break;
+       case TE_SUPERSPIKE:                     // super spike hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               // LordHavoc: changed to dust shower
+               R_SparkShower(pos, vec3_origin, 30, 0);
+               //R_RunParticleEffect (pos, vec3_origin, 0, 20);
+               if ( rand() % 5 )
+                       S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+               else
+               {
+                       rnd = rand() & 3;
+                       if (rnd == 1)
+                               S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
+                       else if (rnd == 2)
+                               S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+                       else
+                               S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+               }
+               break;
+       case TE_SUPERSPIKEQUAD:                 // quad super spike hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               // LordHavoc: changed to dust shower
+               R_SparkShower(pos, vec3_origin, 30, 0);
+               //R_RunParticleEffect (pos, vec3_origin, 0, 20);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 200;
+               dl->die = cl.time + 0.2;
+               dl->decay = 1000;
+               dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
+               if ( rand() % 5 )
+                       S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+               else
+               {
+                       rnd = rand() & 3;
+                       if (rnd == 1)
+                               S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
+                       else if (rnd == 2)
+                               S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+                       else
+                               S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+               }
+               break;
+               // LordHavoc: added for improved blood splatters
+       case TE_BLOOD:  // blood puff
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               dir[0] = MSG_ReadChar ();
+               dir[1] = MSG_ReadChar ();
+               dir[2] = MSG_ReadChar ();
+               count = MSG_ReadByte (); // amount of particles
+               R_SparkShower(pos, dir, count, 1);
+               break;
+       case TE_SPARK:  // spark shower
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               dir[0] = MSG_ReadChar ();
+               dir[1] = MSG_ReadChar ();
+               dir[2] = MSG_ReadChar ();
+               count = MSG_ReadByte (); // amount of particles
+               R_SparkShower(pos, dir, count, 0);
+               break;
+               // LordHavoc: added for improved gore
+       case TE_BLOODSHOWER:    // vaporized body
+               pos[0] = MSG_ReadCoord (); // mins
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               dir[0] = MSG_ReadCoord (); // maxs
+               dir[1] = MSG_ReadCoord ();
+               dir[2] = MSG_ReadCoord ();
+               velspeed = MSG_ReadCoord (); // speed
+               count = MSG_ReadShort (); // number of particles
+               R_BloodShower(pos, dir, velspeed, count);
+               break;
+       case TE_PARTICLECUBE:   // general purpose particle effect
+               pos[0] = MSG_ReadCoord (); // mins
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               pos2[0] = MSG_ReadCoord (); // maxs
+               pos2[1] = MSG_ReadCoord ();
+               pos2[2] = MSG_ReadCoord ();
+               dir[0] = MSG_ReadCoord (); // dir
+               dir[1] = MSG_ReadCoord ();
+               dir[2] = MSG_ReadCoord ();
+               count = MSG_ReadShort (); // number of particles
+               colorStart = MSG_ReadByte (); // color
+               colorLength = MSG_ReadByte (); // gravity (1 or 0)
+               velspeed = MSG_ReadCoord (); // randomvel
+               R_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
+               break;
+
+       case TE_PARTICLERAIN:   // general purpose particle effect
+               pos[0] = MSG_ReadCoord (); // mins
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               pos2[0] = MSG_ReadCoord (); // maxs
+               pos2[1] = MSG_ReadCoord ();
+               pos2[2] = MSG_ReadCoord ();
+               dir[0] = MSG_ReadCoord (); // dir
+               dir[1] = MSG_ReadCoord ();
+               dir[2] = MSG_ReadCoord ();
+               count = MSG_ReadShort (); // number of particles
+               colorStart = MSG_ReadByte (); // color
+               R_ParticleRain(pos, pos2, dir, count, colorStart, 0);
+               break;
+
+       case TE_PARTICLESNOW:   // general purpose particle effect
+               pos[0] = MSG_ReadCoord (); // mins
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               pos2[0] = MSG_ReadCoord (); // maxs
+               pos2[1] = MSG_ReadCoord ();
+               pos2[2] = MSG_ReadCoord ();
+               dir[0] = MSG_ReadCoord (); // dir
+               dir[1] = MSG_ReadCoord ();
+               dir[2] = MSG_ReadCoord ();
+               count = MSG_ReadShort (); // number of particles
+               colorStart = MSG_ReadByte (); // color
+               R_ParticleRain(pos, pos2, dir, count, colorStart, 1);
+               break;
+
+       case TE_GUNSHOT:                        // bullet hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               // LordHavoc: changed to dust shower
+               R_SparkShower(pos, vec3_origin, 15, 0);
+               //R_RunParticleEffect (pos, vec3_origin, 0, 20);
+               break;
+
+       case TE_GUNSHOTQUAD:                    // quad bullet hitting wall
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_SparkShower(pos, vec3_origin, 15, 0);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 200;
+               dl->die = cl.time + 0.2;
+               dl->decay = 1000;
+               dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
+               break;
+
+       case TE_EXPLOSION:                      // rocket explosion
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_ParticleExplosion (pos, false);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 350;
+               dl->die = cl.time + 0.5;
+               dl->decay = 700;
+               dl->color[0] = 1.0;dl->color[1] = 0.8;dl->color[2] = 0.4;
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+
+       case TE_EXPLOSIONQUAD:                  // quad rocket explosion
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_ParticleExplosion (pos, false);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 600;
+               dl->die = cl.time + 0.5;
+               dl->decay = 1200;
+               dl->color[0] = 0.5;dl->color[1] = 0.4;dl->color[2] = 1.0;
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+
+               /*
+       case TE_SMOKEEXPLOSION:                 // rocket explosion with a cloud of smoke
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_ParticleExplosion (pos, true);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 350;
+               dl->die = cl.time + 0.5;
+               dl->decay = 300;
+               dl->color[0] = 1.0;dl->color[1] = 0.8;dl->color[2] = 0.4;
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+               */
+
+       case TE_EXPLOSION3:                             // Nehahra movie colored lighting explosion
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_ParticleExplosion (pos, false);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 350;
+               dl->die = cl.time + 0.5;
+               dl->decay = 700;
+               dl->color[0] = MSG_ReadCoord();dl->color[1] = MSG_ReadCoord();dl->color[2] = MSG_ReadCoord();
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+
+       case TE_EXPLOSIONRGB:                   // colored lighting explosion
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_ParticleExplosion (pos, false);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 350;
+               dl->die = cl.time + 0.5;
+               dl->decay = 700;
+               dl->color[0] = MSG_ReadByte() * (1.0 / 255.0);dl->color[1] = MSG_ReadByte() * (1.0 / 255.0);dl->color[2] = MSG_ReadByte() * (1.0 / 255.0);
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+
+       case TE_TAREXPLOSION:                   // tarbaby explosion
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_BlobExplosion (pos);
+
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+
+       case TE_LIGHTNING1:                             // lightning bolts
+               CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
+               break;
+       
+       case TE_LIGHTNING2:                             // lightning bolts
+               CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
+               break;
+       
+       case TE_LIGHTNING3:                             // lightning bolts
+               CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
+               break;
+
+// PGM 01/21/97 
+       case TE_BEAM:                           // grappling hook beam
+               CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
+               break;
+// PGM 01/21/97
+
+// LordHavoc: ONLY for compatibility with the Nehahra movie... hack hack hack
+       case TE_LIGHTNING4NEH:
+               CL_ParseBeam (Mod_ForName(MSG_ReadString(), true));
+               break;
+
+       case TE_LAVASPLASH:     
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_LavaSplash (pos);
+               break;
+       
+       case TE_TELEPORT:
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               R_TeleportSplash (pos);
+               break;
+               
+       case TE_EXPLOSION2:                             // color mapped explosion
+               pos[0] = MSG_ReadCoord ();
+               pos[1] = MSG_ReadCoord ();
+               pos[2] = MSG_ReadCoord ();
+               colorStart = MSG_ReadByte ();
+               colorLength = MSG_ReadByte ();
+               R_ParticleExplosion2 (pos, colorStart, colorLength);
+               dl = CL_AllocDlight (0);
+               VectorCopy (pos, dl->origin);
+               dl->radius = 350;
+               dl->die = cl.time + 0.5;
+               dl->decay = 700;
+               tempcolor = (byte *)&d_8to24table[colorStart+(colorLength >> 1)];
+               dl->color[0] = tempcolor[0];dl->color[1] = tempcolor[1];dl->color[2] = tempcolor[2];
+               S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+               break;
+               
+       default:
+               Sys_Error ("CL_ParseTEnt: bad type");
+       }
+}
+
+
+/*
+=================
+CL_NewTempEntity
+=================
+*/
+entity_t *CL_NewTempEntity (void)
+{
+       entity_t        *ent;
+
+       if (cl_numvisedicts == MAX_VISEDICTS)
+               return NULL;
+       if (num_temp_entities == MAX_TEMP_ENTITIES)
+               return NULL;
+       ent = &cl_temp_entities[num_temp_entities];
+       memset (ent, 0, sizeof(*ent));
+       num_temp_entities++;
+       cl_visedicts[cl_numvisedicts] = ent;
+       cl_numvisedicts++;
+
+       ent->colormap = vid.colormap;
+       ent->scale = 1;
+       ent->alpha = 1;
+       ent->colormod[0] = ent->colormod[1] = ent->colormod[2] = 1;
+       return ent;
+}
+
+
+/*
+=================
+CL_UpdateTEnts
+=================
+*/
+void CL_UpdateTEnts (void)
+{
+       int                     i;
+       beam_t          *b;
+       vec3_t          dist, org;
+       float           d;
+       entity_t        *ent;
+       float           yaw, pitch;
+       float           forward;
+       dlight_t        *dl;
+
+       num_temp_entities = 0;
+
+// update lightning
+       for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+       {
+               if (!b->model || b->endtime < cl.time)
+                       continue;
+
+       // if coming from the player, update the start position
+               if (b->entity == cl.viewentity)
+               {
+                       VectorCopy (cl_entities[cl.viewentity].origin, b->start);
+               }
+
+       // calculate pitch and yaw
+               VectorSubtract (b->end, b->start, dist);
+
+               if (dist[1] == 0 && dist[0] == 0)
+               {
+                       yaw = 0;
+                       if (dist[2] > 0)
+                               pitch = 90;
+                       else
+                               pitch = 270;
+               }
+               else
+               {
+                       yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
+                       if (yaw < 0)
+                               yaw += 360;
+       
+                       forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
+                       pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
+                       if (pitch < 0)
+                               pitch += 360;
+               }
+
+       // add new entities for the lightning
+               VectorCopy (b->start, org);
+               d = VectorNormalizeLength(dist);
+               while (d > 0)
+               {
+                       ent = CL_NewTempEntity ();
+                       if (!ent)
+                               return;
+                       VectorCopy (org, ent->origin);
+                       ent->model = b->model;
+                       ent->effects = EF_FULLBRIGHT;
+                       ent->angles[0] = pitch;
+                       ent->angles[1] = yaw;
+                       ent->angles[2] = rand()%360;
+
+                       dl = CL_AllocDlight (0);
+                       VectorCopy (ent->origin,  dl->origin);
+                       dl->radius = 100 + (rand()&31);
+                       dl->die = cl.time + 0.001;
+                       dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 1;
+
+                       for (i=0 ; i<3 ; i++)
+                               org[i] += dist[i]*30;
+                       d -= 30;
+               }
+       }
+       
+}
+
+
diff --git a/client.h b/client.h
new file mode 100644 (file)
index 0000000..c5cbffd
--- /dev/null
+++ b/client.h
@@ -0,0 +1,367 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// client.h
+
+typedef struct
+{
+       vec3_t  viewangles;
+
+// intended velocities
+       float   forwardmove;
+       float   sidemove;
+       float   upmove;
+} usercmd_t;
+
+typedef struct
+{
+       int             length;
+       char    map[MAX_STYLESTRING];
+} lightstyle_t;
+
+typedef struct
+{
+       char    name[MAX_SCOREBOARDNAME];
+       float   entertime;
+       int             frags;
+       int             colors;                 // two 4 bit fields
+       byte    translations[VID_GRADES*256];
+} scoreboard_t;
+
+typedef struct
+{
+       int             destcolor[3];
+       int             percent;                // 0-256
+} cshift_t;
+
+#define        CSHIFT_CONTENTS 0
+#define        CSHIFT_DAMAGE   1
+#define        CSHIFT_BONUS    2
+#define        CSHIFT_POWERUP  3
+#define        NUM_CSHIFTS             4
+
+#define        NAME_LENGTH     64
+
+
+//
+// client_state_t should hold all pieces of the client state
+//
+
+#define        SIGNONS         4                       // signon messages to receive before connected
+
+// LordHavoc: 256 dynamic lights
+#define        MAX_DLIGHTS             256
+typedef struct
+{
+       vec3_t  origin;
+       float   radius;
+       float   die;                            // stop lighting after this time
+       float   decay;                          // drop this each second
+       float   minlight;                       // don't add when contributing less
+       int             key;
+       vec3_t  color;                          // LordHavoc: colored lighting
+       qboolean        dark;                   // subtracts light instead of adding
+} dlight_t;
+
+
+#define        MAX_BEAMS       24
+typedef struct
+{
+       int             entity;
+       struct model_s  *model;
+       float   endtime;
+       vec3_t  start, end;
+} beam_t;
+
+// LordHavoc: increased MAX_EFRAGS from 640 to 2048
+#define        MAX_EFRAGS              2048
+
+#define        MAX_MAPSTRING   2048
+#define        MAX_DEMOS               8
+#define        MAX_DEMONAME    16
+
+typedef enum {
+ca_dedicated,          // a dedicated server with no ability to start a client
+ca_disconnected,       // full screen console with no connection
+ca_connected           // valid netcon, talking to a server
+} cactive_t;
+
+//
+// the client_static_t structure is persistant through an arbitrary number
+// of server connections
+//
+typedef struct
+{
+       cactive_t       state;
+
+// personalization data sent to server 
+       char            mapstring[MAX_QPATH];
+       char            spawnparms[MAX_MAPSTRING];      // to restart a level
+
+// demo loop control
+       int                     demonum;                // -1 = don't play demos
+       char            demos[MAX_DEMOS][MAX_DEMONAME];         // when not playing
+
+// demo recording info must be here, because record is started before
+// entering a map (and clearing client_state_t)
+       qboolean        demorecording;
+       qboolean        demoplayback;
+       qboolean        timedemo;
+       int                     forcetrack;                     // -1 = use normal cd track
+       FILE            *demofile;
+       int                     td_lastframe;           // to meter out one message a frame
+       int                     td_startframe;          // host_framecount at start
+       double          td_starttime;           // realtime at second frame of timedemo (LordHavoc: changed to double)
+       qboolean        demopaused;                     // LordHavoc: pausedemo
+
+
+// connection information
+       int                     signon;                 // 0 to SIGNONS
+       struct qsocket_s        *netcon;
+       sizebuf_t       message;                // writing buffer to send to server
+       
+} client_static_t;
+
+extern client_static_t cls;
+
+//
+// the client_state_t structure is wiped completely at every
+// server signon
+//
+typedef struct
+{
+       int                     movemessages;   // since connecting to this server
+                                                               // throw out the first couple, so the player
+                                                               // doesn't accidentally do something the 
+                                                               // first frame
+       usercmd_t       cmd;                    // last command sent to the server
+
+// information for local display
+       int                     stats[MAX_CL_STATS];    // health, etc
+       int                     items;                  // inventory bit flags
+       float   item_gettime[32];       // cl.time of aquiring item, for blinking
+       float           faceanimtime;   // use anim frame if cl.time < this
+
+       cshift_t        cshifts[NUM_CSHIFTS];   // color shifts for damage, powerups
+       cshift_t        prev_cshifts[NUM_CSHIFTS];      // and content types
+
+// the client maintains its own idea of view angles, which are
+// sent to the server each frame.  The server sets punchangle when
+// the view is temporarliy offset, and an angle reset commands at the start
+// of each level and after teleporting.
+       vec3_t          mviewangles[2]; // during demo playback viewangles is lerped
+                                                               // between these
+       vec3_t          viewangles;
+       
+       vec3_t          mvelocity[2];   // update by server, used for lean+bob
+                                                               // (0 is newest)
+       vec3_t          velocity;               // lerped between mvelocity[0] and [1]
+
+       vec3_t          punchangle;             // temporary offset
+       
+// pitch drifting vars
+       float           idealpitch;
+       float           pitchvel;
+       qboolean        nodrift;
+       float           driftmove;
+       double          laststop;
+
+       float           viewheight;
+       float           crouch;                 // local amount for smoothing stepups
+
+       qboolean        paused;                 // send over by server
+       qboolean        onground;
+       qboolean        inwater;
+       
+       int                     intermission;   // don't change view angle, full screen, etc
+       int                     completed_time; // latched at intermission start
+       
+       double          mtime[2];               // the timestamp of last two messages   
+       double          time;                   // clients view of time, should be between
+                                                               // servertime and oldservertime to generate
+                                                               // a lerp point for other data
+       double          oldtime;                // previous cl.time, time-oldtime is used
+                                                               // to decay light values and smooth step ups
+       
+
+       float           last_received_message;  // (realtime) for net trouble icon
+
+//
+// information that is static for the entire time connected to a server
+//
+       struct model_s          *model_precache[MAX_MODELS];
+       struct sfx_s            *sound_precache[MAX_SOUNDS];
+
+       char            levelname[40];  // for display on solo scoreboard
+       int                     viewentity;             // cl_entitites[cl.viewentity] = player
+       int                     maxclients;
+       int                     gametype;
+
+// refresh related state
+       struct model_s  *worldmodel;    // cl_entitites[0].model
+       struct efrag_s  *free_efrags;
+       int                     num_entities;   // held in cl_entities array
+       int                     num_statics;    // held in cl_staticentities array
+       entity_t        viewent;                        // the gun model
+
+       int                     cdtrack, looptrack;     // cd audio
+
+// frag scoreboard
+       scoreboard_t    *scores;                // [cl.maxclients]
+} client_state_t;
+
+
+//
+// cvars
+//
+extern cvar_t  cl_name;
+extern cvar_t  cl_color;
+
+extern cvar_t  cl_upspeed;
+extern cvar_t  cl_forwardspeed;
+extern cvar_t  cl_backspeed;
+extern cvar_t  cl_sidespeed;
+
+extern cvar_t  cl_movespeedkey;
+
+extern cvar_t  cl_yawspeed;
+extern cvar_t  cl_pitchspeed;
+
+extern cvar_t  cl_anglespeedkey;
+
+extern cvar_t  cl_autofire;
+
+extern cvar_t  cl_shownet;
+extern cvar_t  cl_nolerp;
+
+extern cvar_t  cl_pitchdriftspeed;
+extern cvar_t  lookspring;
+extern cvar_t  lookstrafe;
+extern cvar_t  sensitivity;
+
+extern cvar_t  m_pitch;
+extern cvar_t  m_yaw;
+extern cvar_t  m_forward;
+extern cvar_t  m_side;
+
+
+#define        MAX_TEMP_ENTITIES       64                      // lightning bolts, etc
+#define        MAX_STATIC_ENTITIES     128                     // torches, etc
+
+extern client_state_t  cl;
+
+// FIXME, allocate dynamically
+extern efrag_t                 cl_efrags[MAX_EFRAGS];
+extern entity_t                cl_entities[MAX_EDICTS];
+extern entity_t                cl_static_entities[MAX_STATIC_ENTITIES];
+extern lightstyle_t    cl_lightstyle[MAX_LIGHTSTYLES];
+extern dlight_t                cl_dlights[MAX_DLIGHTS];
+extern entity_t                cl_temp_entities[MAX_TEMP_ENTITIES];
+extern beam_t                  cl_beams[MAX_BEAMS];
+
+//=============================================================================
+
+//
+// cl_main
+//
+dlight_t *CL_AllocDlight (int key);
+void   CL_DecayLights (void);
+
+void CL_Init (void);
+
+void CL_EstablishConnection (char *host);
+void CL_Signon1 (void);
+void CL_Signon2 (void);
+void CL_Signon3 (void);
+void CL_Signon4 (void);
+
+void CL_Disconnect (void);
+void CL_Disconnect_f (void);
+void CL_NextDemo (void);
+
+#define                        MAX_VISEDICTS   256
+extern int                             cl_numvisedicts;
+extern entity_t                *cl_visedicts[MAX_VISEDICTS];
+
+//
+// cl_input
+//
+typedef struct
+{
+       int             down[2];                // key nums holding it down
+       int             state;                  // low bit is down state
+} kbutton_t;
+
+extern kbutton_t       in_mlook, in_klook;
+extern         kbutton_t       in_strafe;
+extern         kbutton_t       in_speed;
+
+void CL_InitInput (void);
+void CL_SendCmd (void);
+void CL_SendMove (usercmd_t *cmd);
+
+void CL_ParseTEnt (void);
+void CL_UpdateTEnts (void);
+
+void CL_ClearState (void);
+
+
+int  CL_ReadFromServer (void);
+void CL_WriteToServer (usercmd_t *cmd);
+void CL_BaseMove (usercmd_t *cmd);
+
+
+float CL_KeyState (kbutton_t *key);
+char *Key_KeynumToString (int keynum);
+
+//
+// cl_demo.c
+//
+void CL_StopPlayback (void);
+int CL_GetMessage (void);
+
+void CL_Stop_f (void);
+void CL_Record_f (void);
+void CL_PlayDemo_f (void);
+void CL_TimeDemo_f (void);
+
+//
+// cl_parse.c
+//
+void CL_ParseServerMessage (void);
+void CL_NewTranslation (int slot);
+
+//
+// view
+//
+void V_StartPitchDrift (void);
+void V_StopPitchDrift (void);
+
+void V_RenderView (void);
+void V_UpdatePalette (void);
+void V_Register (void);
+void V_ParseDamage (void);
+void V_SetContentsColor (int contents);
+
+
+//
+// cl_tent
+//
+void CL_InitTEnts (void);
+void CL_SignonReply (void);
diff --git a/cmd.c b/cmd.c
new file mode 100644 (file)
index 0000000..bf4579b
--- /dev/null
+++ b/cmd.c
@@ -0,0 +1,705 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cmd.c -- Quake script command processing module
+
+#include "quakedef.h"
+
+void Cmd_ForwardToServer (void);
+
+#define        MAX_ALIAS_NAME  32
+
+typedef struct cmdalias_s
+{
+       struct cmdalias_s       *next;
+       char    name[MAX_ALIAS_NAME];
+       char    *value;
+} cmdalias_t;
+
+cmdalias_t     *cmd_alias;
+
+int trashtest;
+int *trashspot;
+
+qboolean       cmd_wait;
+
+//=============================================================================
+
+/*
+============
+Cmd_Wait_f
+
+Causes execution of the remainder of the command buffer to be delayed until
+next frame.  This allows commands like:
+bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
+============
+*/
+void Cmd_Wait_f (void)
+{
+       cmd_wait = true;
+}
+
+/*
+=============================================================================
+
+                                               COMMAND BUFFER
+
+=============================================================================
+*/
+
+sizebuf_t      cmd_text;
+
+/*
+============
+Cbuf_Init
+============
+*/
+void Cbuf_Init (void)
+{
+       SZ_Alloc (&cmd_text, 8192);             // space for commands and script files
+}
+
+
+/*
+============
+Cbuf_AddText
+
+Adds command text at the end of the buffer
+============
+*/
+void Cbuf_AddText (char *text)
+{
+       int             l;
+       
+       l = strlen (text);
+
+       if (cmd_text.cursize + l >= cmd_text.maxsize)
+       {
+               Con_Printf ("Cbuf_AddText: overflow\n");
+               return;
+       }
+
+       SZ_Write (&cmd_text, text, strlen (text));
+}
+
+
+/*
+============
+Cbuf_InsertText
+
+Adds command text immediately after the current command
+Adds a \n to the text
+FIXME: actually change the command buffer to do less copying
+============
+*/
+void Cbuf_InsertText (char *text)
+{
+       char    *temp;
+       int             templen;
+
+// copy off any commands still remaining in the exec buffer
+       templen = cmd_text.cursize;
+       if (templen)
+       {
+               temp = Z_Malloc (templen);
+               memcpy (temp, cmd_text.data, templen);
+               SZ_Clear (&cmd_text);
+       }
+       else
+               temp = NULL;    // shut up compiler
+               
+// add the entire text of the file
+       Cbuf_AddText (text);
+       
+// add the copied off data
+       if (templen)
+       {
+               SZ_Write (&cmd_text, temp, templen);
+               Z_Free (temp);
+       }
+}
+
+/*
+============
+Cbuf_Execute
+============
+*/
+void Cbuf_Execute (void)
+{
+       int             i;
+       char    *text;
+       char    line[1024];
+       int             quotes;
+       
+       while (cmd_text.cursize)
+       {
+// find a \n or ; line break
+               text = (char *)cmd_text.data;
+
+               quotes = 0;
+               for (i=0 ; i< cmd_text.cursize ; i++)
+               {
+                       if (text[i] == '"')
+                               quotes++;
+                       if ( !(quotes&1) &&  text[i] == ';')
+                               break;  // don't break if inside a quoted string
+                       if (text[i] == '\n')
+                               break;
+               }
+                       
+                               
+               memcpy (line, text, i);
+               line[i] = 0;
+               
+// delete the text from the command buffer and move remaining commands down
+// this is necessary because commands (exec, alias) can insert data at the
+// beginning of the text buffer
+
+               if (i == cmd_text.cursize)
+                       cmd_text.cursize = 0;
+               else
+               {
+                       i++;
+                       cmd_text.cursize -= i;
+                       memcpy (text, text+i, cmd_text.cursize);
+               }
+
+// execute the command line
+               Cmd_ExecuteString (line, src_command);
+               
+               if (cmd_wait)
+               {       // skip out while text still remains in buffer, leaving it
+                       // for next frame
+                       cmd_wait = false;
+                       break;
+               }
+       }
+}
+
+/*
+==============================================================================
+
+                                               SCRIPT COMMANDS
+
+==============================================================================
+*/
+
+/*
+===============
+Cmd_StuffCmds_f
+
+Adds command line parameters as script statements
+Commands lead with a +, and continue until a - or another +
+quake +prog jctest.qp +cmd amlev1
+quake -nosound +cmd amlev1
+===============
+*/
+void Cmd_StuffCmds_f (void)
+{
+       int             i, j;
+       int             s;
+       char    *text, *build, c;
+               
+       if (Cmd_Argc () != 1)
+       {
+               Con_Printf ("stuffcmds : execute command line parameters\n");
+               return;
+       }
+
+// build the combined string to parse from
+       s = 0;
+       for (i=1 ; i<com_argc ; i++)
+       {
+               if (!com_argv[i])
+                       continue;               // NEXTSTEP nulls out -NXHost
+               s += strlen (com_argv[i]) + 1;
+       }
+       if (!s)
+               return;
+               
+       text = Z_Malloc (s+1);
+       text[0] = 0;
+       for (i=1 ; i<com_argc ; i++)
+       {
+               if (!com_argv[i])
+                       continue;               // NEXTSTEP nulls out -NXHost
+               strcat (text,com_argv[i]);
+               if (i != com_argc-1)
+                       strcat (text, " ");
+       }
+       
+// pull out the commands
+       build = Z_Malloc (s+1);
+       build[0] = 0;
+       
+       for (i=0 ; i<s-1 ; i++)
+       {
+               if (text[i] == '+')
+               {
+                       i++;
+
+                       for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
+                               ;
+
+                       c = text[j];
+                       text[j] = 0;
+                       
+                       strcat (build, text+i);
+                       strcat (build, "\n");
+                       text[j] = c;
+                       i = j-1;
+               }
+       }
+       
+       if (build[0])
+               Cbuf_InsertText (build);
+       
+       Z_Free (text);
+       Z_Free (build);
+}
+
+
+/*
+===============
+Cmd_Exec_f
+===============
+*/
+void Cmd_Exec_f (void)
+{
+       char    *f;
+       int             mark;
+
+       if (Cmd_Argc () != 2)
+       {
+               Con_Printf ("exec <filename> : execute a script file\n");
+               return;
+       }
+
+       mark = Hunk_LowMark ();
+       f = (char *)COM_LoadHunkFile (Cmd_Argv(1), false);
+       if (!f)
+       {
+               Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
+               return;
+       }
+       Con_Printf ("execing %s\n",Cmd_Argv(1));
+       
+       Cbuf_InsertText (f);
+       Hunk_FreeToLowMark (mark);
+}
+
+
+/*
+===============
+Cmd_Echo_f
+
+Just prints the rest of the line to the console
+===============
+*/
+void Cmd_Echo_f (void)
+{
+       int             i;
+       
+       for (i=1 ; i<Cmd_Argc() ; i++)
+               Con_Printf ("%s ",Cmd_Argv(i));
+       Con_Printf ("\n");
+}
+
+/*
+===============
+Cmd_Alias_f
+
+Creates a new command that executes a command string (possibly ; seperated)
+===============
+*/
+
+char *CopyString (char *in)
+{
+       char    *out;
+       
+       out = Z_Malloc (strlen(in)+1);
+       strcpy (out, in);
+       return out;
+}
+
+void Cmd_Alias_f (void)
+{
+       cmdalias_t      *a;
+       char            cmd[1024];
+       int                     i, c;
+       char            *s;
+
+       if (Cmd_Argc() == 1)
+       {
+               Con_Printf ("Current alias commands:\n");
+               for (a = cmd_alias ; a ; a=a->next)
+                       Con_Printf ("%s : %s\n", a->name, a->value);
+               return;
+       }
+
+       s = Cmd_Argv(1);
+       if (strlen(s) >= MAX_ALIAS_NAME)
+       {
+               Con_Printf ("Alias name is too long\n");
+               return;
+       }
+
+       // if the alias allready exists, reuse it
+       for (a = cmd_alias ; a ; a=a->next)
+       {
+               if (!strcmp(s, a->name))
+               {
+                       Z_Free (a->value);
+                       break;
+               }
+       }
+
+       if (!a)
+       {
+               a = Z_Malloc (sizeof(cmdalias_t));
+               a->next = cmd_alias;
+               cmd_alias = a;
+       }
+       strcpy (a->name, s);    
+
+// copy the rest of the command line
+       cmd[0] = 0;             // start out with a null string
+       c = Cmd_Argc();
+       for (i=2 ; i< c ; i++)
+       {
+               strcat (cmd, Cmd_Argv(i));
+               if (i != c)
+                       strcat (cmd, " ");
+       }
+       strcat (cmd, "\n");
+       
+       a->value = CopyString (cmd);
+}
+
+/*
+=============================================================================
+
+                                       COMMAND EXECUTION
+
+=============================================================================
+*/
+
+typedef struct cmd_function_s
+{
+       struct cmd_function_s   *next;
+       char                                    *name;
+       xcommand_t                              function;
+} cmd_function_t;
+
+
+#define        MAX_ARGS                80
+
+static int                     cmd_argc;
+static char            *cmd_argv[MAX_ARGS];
+static char            *cmd_null_string = "";
+static char            *cmd_args = NULL;
+
+cmd_source_t   cmd_source;
+
+
+static cmd_function_t  *cmd_functions;         // possible commands to execute
+
+/*
+============
+Cmd_Init
+============
+*/
+void Cmd_Init (void)
+{
+//
+// register our commands
+//
+       Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
+       Cmd_AddCommand ("exec",Cmd_Exec_f);
+       Cmd_AddCommand ("echo",Cmd_Echo_f);
+       Cmd_AddCommand ("alias",Cmd_Alias_f);
+       Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
+       Cmd_AddCommand ("wait", Cmd_Wait_f);
+}
+
+/*
+============
+Cmd_Argc
+============
+*/
+int            Cmd_Argc (void)
+{
+       return cmd_argc;
+}
+
+/*
+============
+Cmd_Argv
+============
+*/
+char   *Cmd_Argv (int arg)
+{
+       if (arg >= cmd_argc )
+               return cmd_null_string;
+       return cmd_argv[arg];   
+}
+
+/*
+============
+Cmd_Args
+============
+*/
+char           *Cmd_Args (void)
+{
+       return cmd_args;
+}
+
+
+/*
+============
+Cmd_TokenizeString
+
+Parses the given string into command line tokens.
+============
+*/
+void Cmd_TokenizeString (char *text)
+{
+       int             i;
+       
+// clear the args from the last string
+       for (i=0 ; i<cmd_argc ; i++)
+               Z_Free (cmd_argv[i]);
+               
+       cmd_argc = 0;
+       cmd_args = NULL;
+       
+       while (1)
+       {
+// skip whitespace up to a /n
+               while (*text && *text <= ' ' && *text != '\n')
+               {
+                       text++;
+               }
+               
+               if (*text == '\n')
+               {       // a newline seperates commands in the buffer
+                       text++;
+                       break;
+               }
+
+               if (!*text)
+                       return;
+       
+               if (cmd_argc == 1)
+                        cmd_args = text;
+                       
+               text = COM_Parse (text);
+               if (!text)
+                       return;
+
+               if (cmd_argc < MAX_ARGS)
+               {
+                       cmd_argv[cmd_argc] = Z_Malloc (strlen(com_token)+1);
+                       strcpy (cmd_argv[cmd_argc], com_token);
+                       cmd_argc++;
+               }
+       }
+       
+}
+
+
+/*
+============
+Cmd_AddCommand
+============
+*/
+void   Cmd_AddCommand (char *cmd_name, xcommand_t function)
+{
+       cmd_function_t  *cmd;
+       
+       if (host_initialized)   // because hunk allocation would get stomped
+               Sys_Error ("Cmd_AddCommand after host_initialized");
+               
+// fail if the command is a variable name
+       if (Cvar_VariableString(cmd_name)[0])
+       {
+               Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
+               return;
+       }
+       
+// fail if the command already exists
+       for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+       {
+               if (!strcmp (cmd_name, cmd->name))
+               {
+                       Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
+                       return;
+               }
+       }
+
+       cmd = Hunk_Alloc (sizeof(cmd_function_t));
+       cmd->name = cmd_name;
+       cmd->function = function;
+       cmd->next = cmd_functions;
+       cmd_functions = cmd;
+}
+
+/*
+============
+Cmd_Exists
+============
+*/
+qboolean       Cmd_Exists (char *cmd_name)
+{
+       cmd_function_t  *cmd;
+
+       for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+       {
+               if (!strcmp (cmd_name,cmd->name))
+                       return true;
+       }
+
+       return false;
+}
+
+
+
+/*
+============
+Cmd_CompleteCommand
+============
+*/
+char *Cmd_CompleteCommand (char *partial)
+{
+       cmd_function_t  *cmd;
+       int                             len;
+       
+       len = strlen(partial);
+       
+       if (!len)
+               return NULL;
+               
+// check functions
+       for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+               if (!strncmp (partial,cmd->name, len))
+                       return cmd->name;
+
+       return NULL;
+}
+
+/*
+============
+Cmd_ExecuteString
+
+A complete command line has been parsed, so try to execute it
+FIXME: lookupnoadd the token to speed search?
+============
+*/
+void   Cmd_ExecuteString (char *text, cmd_source_t src)
+{      
+       cmd_function_t  *cmd;
+       cmdalias_t              *a;
+
+       cmd_source = src;
+       Cmd_TokenizeString (text);
+                       
+// execute the command line
+       if (!Cmd_Argc())
+               return;         // no tokens
+
+// check functions
+       for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+       {
+               if (!Q_strcasecmp (cmd_argv[0],cmd->name))
+               {
+                       cmd->function ();
+                       return;
+               }
+       }
+
+// check alias
+       for (a=cmd_alias ; a ; a=a->next)
+       {
+               if (!Q_strcasecmp (cmd_argv[0], a->name))
+               {
+                       Cbuf_InsertText (a->value);
+                       return;
+               }
+       }
+       
+// check cvars
+       if (!Cvar_Command ())
+               Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
+       
+}
+
+
+/*
+===================
+Cmd_ForwardToServer
+
+Sends the entire command line over to the server
+===================
+*/
+void Cmd_ForwardToServer (void)
+{
+       if (cls.state != ca_connected)
+       {
+               Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
+               return;
+       }
+       
+       if (cls.demoplayback)
+               return;         // not really connected
+
+       MSG_WriteByte (&cls.message, clc_stringcmd);
+       if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
+       {
+               SZ_Print (&cls.message, Cmd_Argv(0));
+               SZ_Print (&cls.message, " ");
+       }
+       if (Cmd_Argc() > 1)
+               SZ_Print (&cls.message, Cmd_Args());
+       else
+               SZ_Print (&cls.message, "\n");
+}
+
+
+/*
+================
+Cmd_CheckParm
+
+Returns the position (1 to argc-1) in the command's argument list
+where the given parameter apears, or 0 if not present
+================
+*/
+
+int Cmd_CheckParm (char *parm)
+{
+       int i;
+       
+       if (!parm)
+               Sys_Error ("Cmd_CheckParm: NULL");
+
+       for (i = 1; i < Cmd_Argc (); i++)
+               if (!Q_strcasecmp (parm, Cmd_Argv (i)))
+                       return i;
+                       
+       return 0;
+}
diff --git a/cmd.h b/cmd.h
new file mode 100644 (file)
index 0000000..f9eab24
--- /dev/null
+++ b/cmd.h
@@ -0,0 +1,121 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// cmd.h -- Command buffer and command execution
+
+//===========================================================================
+
+/*
+
+Any number of commands can be added in a frame, from several different sources.
+Most commands come from either keybindings or console line input, but remote
+servers can also send across commands and entire text files can be execed.
+
+The + command line options are also added to the command buffer.
+
+The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
+
+*/
+
+
+void Cbuf_Init (void);
+// allocates an initial text buffer that will grow as needed
+
+void Cbuf_AddText (char *text);
+// as new commands are generated from the console or keybindings,
+// the text is added to the end of the command buffer.
+
+void Cbuf_InsertText (char *text);
+// when a command wants to issue other commands immediately, the text is
+// inserted at the beginning of the buffer, before any remaining unexecuted
+// commands.
+
+void Cbuf_Execute (void);
+// Pulls off \n terminated lines of text from the command buffer and sends
+// them through Cmd_ExecuteString.  Stops when the buffer is empty.
+// Normally called once per frame, but may be explicitly invoked.
+// Do not call inside a command function!
+
+//===========================================================================
+
+/*
+
+Command execution takes a null terminated string, breaks it into tokens,
+then searches for a command or variable that matches the first token.
+
+Commands can come from three sources, but the handler functions may choose
+to dissallow the action or forward it to a remote server if the source is
+not apropriate.
+
+*/
+
+typedef void (*xcommand_t) (void);
+
+typedef enum
+{
+       src_client,             // came in over a net connection as a clc_stringcmd
+                                       // host_client will be valid during this state.
+       src_command             // from the command buffer
+} cmd_source_t;
+
+extern cmd_source_t    cmd_source;
+
+void   Cmd_Init (void);
+
+void   Cmd_AddCommand (char *cmd_name, xcommand_t function);
+// called by the init functions of other parts of the program to
+// register commands and functions to call for them.
+// The cmd_name is referenced later, so it should not be in temp memory
+
+qboolean Cmd_Exists (char *cmd_name);
+// used by the cvar code to check for cvar / command name overlap
+
+char   *Cmd_CompleteCommand (char *partial);
+// attempts to match a partial command for automatic command line completion
+// returns NULL if nothing fits
+
+int            Cmd_Argc (void);
+char   *Cmd_Argv (int arg);
+char   *Cmd_Args (void);
+// The functions that execute commands get their parameters with these
+// functions. Cmd_Argv () will return an empty string, not a NULL
+// if arg > argc, so string operations are allways safe.
+
+int Cmd_CheckParm (char *parm);
+// Returns the position (1 to argc-1) in the command's argument list
+// where the given parameter apears, or 0 if not present
+
+void Cmd_TokenizeString (char *text);
+// Takes a null terminated string.  Does not need to be /n terminated.
+// breaks the string up into arg tokens.
+
+void   Cmd_ExecuteString (char *text, cmd_source_t src);
+// Parses a single line of text into arguments and tries to execute it.
+// The text can come from the command buffer, a remote client, or stdin.
+
+void   Cmd_ForwardToServer (void);
+// adds the current command line as a clc_stringcmd to the client message.
+// things like godmode, noclip, etc, are commands directed to the server,
+// so when they are typed in at the console, they will need to be forwarded.
+
+void   Cmd_Print (char *text);
+// used by command functions to send output to either the graphics console or
+// passed as a print message to the client
+
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..11778fa
--- /dev/null
+++ b/common.c
@@ -0,0 +1,1891 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// common.c -- misc functions used in client and server
+
+#include "quakedef.h"
+
+#define NUM_SAFE_ARGVS  7
+
+static char     *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
+static char     *argvdummy = " ";
+
+static char     *safeargvs[NUM_SAFE_ARGVS] =
+       {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
+
+cvar_t  registered = {"registered","0"};
+cvar_t  cmdline = {"cmdline","0", false, true};
+
+qboolean        com_modified;   // set true if using non-id files
+
+qboolean               proghack;
+
+int             static_registered = 1;  // only for startup check, then set
+
+qboolean               msg_suppress_1 = 0;
+
+void COM_InitFilesystem (void);
+
+// if a packfile directory differs from this, it is assumed to be hacked
+#define PAK0_COUNT              339
+#define PAK0_CRC                32981
+
+char   com_token[1024];
+int            com_argc;
+char   **com_argv;
+
+#define CMDLINE_LENGTH 256
+char   com_cmdline[CMDLINE_LENGTH];
+
+qboolean               standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
+
+// this graphic needs to be in the pak file to use registered features
+unsigned short pop[] =
+{
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
+,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
+,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
+,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
+,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
+,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
+,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
+,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
+,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
+,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
+,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
+};
+
+/*
+
+
+All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
+
+The "base directory" is the path to the directory holding the quake.exe and all game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.  This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory.  The base directory is
+only used during filesystem initialization.
+
+The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to.  This can be overridden with the "-game" command line parameter.  The game directory can never be changed while quake is executing.  This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
+
+The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory
+specified, when a file is found by the normal search path, it will be mirrored
+into the cache directory, then opened there.
+
+
+
+FIXME:
+The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently.  This could be used to add a "-sspeed 22050" for the high quality sound edition.  Because they are added at the end, they will not override an explicit setting on the original command line.
+       
+*/
+
+//============================================================================
+
+
+// ClearLink is used for new headnodes
+void ClearLink (link_t *l)
+{
+       l->prev = l->next = l;
+}
+
+void RemoveLink (link_t *l)
+{
+       l->next->prev = l->prev;
+       l->prev->next = l->next;
+}
+
+void InsertLinkBefore (link_t *l, link_t *before)
+{
+       l->next = before;
+       l->prev = before->prev;
+       l->prev->next = l;
+       l->next->prev = l;
+}
+void InsertLinkAfter (link_t *l, link_t *after)
+{
+       l->next = after->next;
+       l->prev = after;
+       l->prev->next = l;
+       l->next->prev = l;
+}
+
+/*
+============================================================================
+
+                                       LIBRARY REPLACEMENT FUNCTIONS
+
+============================================================================
+*/
+
+/*
+void Q_memset (void *dest, int fill, int count)
+{
+       int             i;
+       
+       if ( (((long)dest | count) & 3) == 0)
+       {
+               count >>= 2;
+               fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
+               for (i=0 ; i<count ; i++)
+                       ((int *)dest)[i] = fill;
+       }
+       else
+               for (i=0 ; i<count ; i++)
+                       ((byte *)dest)[i] = fill;
+}
+
+void Q_memcpy (void *dest, void *src, int count)
+{
+       int             i;
+       
+       if (( ( (long)dest | (long)src | count) & 3) == 0 )
+       {
+               count>>=2;
+               for (i=0 ; i<count ; i++)
+                       ((int *)dest)[i] = ((int *)src)[i];
+       }
+       else
+               for (i=0 ; i<count ; i++)
+                       ((byte *)dest)[i] = ((byte *)src)[i];
+}
+
+int Q_memcmp (void *m1, void *m2, int count)
+{
+       while(count)
+       {
+               count--;
+               if (((byte *)m1)[count] != ((byte *)m2)[count])
+                       return -1;
+       }
+       return 0;
+}
+
+void Q_strcpy (char *dest, char *src)
+{
+       while (*src)
+       {
+               *dest++ = *src++;
+       }
+       *dest++ = 0;
+}
+
+void Q_strncpy (char *dest, char *src, int count)
+{
+       while (*src && count--)
+       {
+               *dest++ = *src++;
+       }
+       if (count)
+               *dest++ = 0;
+}
+
+int Q_strlen (char *str)
+{
+       int             count;
+       
+       count = 0;
+       while (str[count])
+               count++;
+
+       return count;
+}
+
+char *Q_strrchr(char *s, char c)
+{
+    int len = Q_strlen(s);
+    s += len;
+    while (len--)
+       if (*--s == c) return s;
+    return 0;
+}
+
+void Q_strcat (char *dest, char *src)
+{
+       dest += Q_strlen(dest);
+       Q_strcpy (dest, src);
+}
+
+int Q_strcmp (char *s1, char *s2)
+{
+       while (1)
+       {
+               if (*s1 != *s2)
+                       return -1;              // strings not equal    
+               if (!*s1)
+                       return 0;               // strings are equal
+               s1++;
+               s2++;
+       }
+       
+       return -1;
+}
+
+int Q_strncmp (char *s1, char *s2, int count)
+{
+       while (1)
+       {
+               if (!count--)
+                       return 0;
+               if (*s1 != *s2)
+                       return -1;              // strings not equal    
+               if (!*s1)
+                       return 0;               // strings are equal
+               s1++;
+               s2++;
+       }
+       
+       return -1;
+}
+*/
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+       int             c1, c2;
+       
+       while (1)
+       {
+               c1 = *s1++;
+               c2 = *s2++;
+
+               if (!n--)
+                       return 0;               // strings are equal until end point
+               
+               if (c1 != c2)
+               {
+                       if (c1 >= 'a' && c1 <= 'z')
+                               c1 -= ('a' - 'A');
+                       if (c2 >= 'a' && c2 <= 'z')
+                               c2 -= ('a' - 'A');
+                       if (c1 != c2)
+                               return -1;              // strings not equal
+               }
+               if (!c1)
+                       return 0;               // strings are equal
+//              s1++;
+//              s2++;
+       }
+       
+       return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+       return Q_strncasecmp (s1, s2, 99999);
+}
+/*
+int Q_atoi (char *str)
+{
+       int             val;
+       int             sign;
+       int             c;
+       
+       if (*str == '-')
+       {
+               sign = -1;
+               str++;
+       }
+       else
+               sign = 1;
+               
+       val = 0;
+
+//
+// check for hex
+//
+       if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+       {
+               str += 2;
+               while (1)
+               {
+                       c = *str++;
+                       if (c >= '0' && c <= '9')
+                               val = (val<<4) + c - '0';
+                       else if (c >= 'a' && c <= 'f')
+                               val = (val<<4) + c - 'a' + 10;
+                       else if (c >= 'A' && c <= 'F')
+                               val = (val<<4) + c - 'A' + 10;
+                       else
+                               return val*sign;
+               }
+       }
+       
+//
+// check for character
+//
+       if (str[0] == '\'')
+       {
+               return sign * str[1];
+       }
+       
+//
+// assume decimal
+//
+       while (1)
+       {
+               c = *str++;
+               if (c <'0' || c > '9')
+                       return val*sign;
+               val = val*10 + c - '0';
+       }
+       
+       return 0;
+}
+
+
+float Q_atof (char *str)
+{
+       double                  val;
+       int             sign;
+       int             c;
+       int             decimal, total;
+       
+       if (*str == '-')
+       {
+               sign = -1;
+               str++;
+       }
+       else
+               sign = 1;
+               
+       val = 0;
+
+//
+// check for hex
+//
+       if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+       {
+               str += 2;
+               while (1)
+               {
+                       c = *str++;
+                       if (c >= '0' && c <= '9')
+                               val = (val*16) + c - '0';
+                       else if (c >= 'a' && c <= 'f')
+                               val = (val*16) + c - 'a' + 10;
+                       else if (c >= 'A' && c <= 'F')
+                               val = (val*16) + c - 'A' + 10;
+                       else
+                               return val*sign;
+               }
+       }
+       
+//
+// check for character
+//
+       if (str[0] == '\'')
+       {
+               return sign * str[1];
+       }
+       
+//
+// assume decimal
+//
+       decimal = -1;
+       total = 0;
+       while (1)
+       {
+               c = *str++;
+               if (c == '.')
+               {
+                       decimal = total;
+                       continue;
+               }
+               if (c <'0' || c > '9')
+                       break;
+               val = val*10 + c - '0';
+               total++;
+       }
+
+       if (decimal == -1)
+               return val*sign;
+       while (total > decimal)
+       {
+               val /= 10;
+               total--;
+       }
+       
+       return val*sign;
+}
+*/
+
+/*
+============================================================================
+
+                                       BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifndef WIN32
+short   (*BigShort) (short l);
+short   (*LittleShort) (short l);
+int     (*BigLong) (int l);
+int     (*LittleLong) (int l);
+float   (*BigFloat) (float l);
+float   (*LittleFloat) (float l);
+#endif
+
+short   ShortSwap (short l)
+{
+       byte    b1,b2;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+
+       return (b1<<8) + b2;
+}
+
+short   ShortNoSwap (short l)
+{
+       return l;
+}
+
+int    LongSwap (int l)
+{
+       byte    b1,b2,b3,b4;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+       b3 = (l>>16)&255;
+       b4 = (l>>24)&255;
+
+       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int     LongNoSwap (int l)
+{
+       return l;
+}
+
+float FloatSwap (float f)
+{
+       union
+       {
+               float   f;
+               byte    b[4];
+       } dat1, dat2;
+       
+       
+       dat1.f = f;
+       dat2.b[0] = dat1.b[3];
+       dat2.b[1] = dat1.b[2];
+       dat2.b[2] = dat1.b[1];
+       dat2.b[3] = dat1.b[0];
+       return dat2.f;
+}
+
+float FloatNoSwap (float f)
+{
+       return f;
+}
+
+/*
+==============================================================================
+
+                       MESSAGE IO FUNCTIONS
+
+Handles byte ordering and avoids alignment errors
+==============================================================================
+*/
+
+//
+// writing functions
+//
+
+void MSG_WriteChar (sizebuf_t *sb, int c)
+{
+       byte    *buf;
+       
+//#ifdef PARANOID
+//     if (c < -128 || c > 127)
+//             Sys_Error ("MSG_WriteChar: range error");
+//#endif
+
+       buf = SZ_GetSpace (sb, 1);
+       buf[0] = c;
+}
+
+void MSG_WriteByte (sizebuf_t *sb, int c)
+{
+       byte    *buf;
+       
+//#ifdef PARANOID
+//     if (c < 0 || c > 255)
+//             Sys_Error ("MSG_WriteByte: range error");
+//#endif
+
+       buf = SZ_GetSpace (sb, 1);
+       buf[0] = c;
+}
+
+void MSG_WriteShort (sizebuf_t *sb, int c)
+{
+       byte    *buf;
+       
+//#ifdef PARANOID
+//     if (c < ((short)0x8000) || c > (short)0x7fff)
+//             Sys_Error ("MSG_WriteShort: range error");
+//#endif
+
+       buf = SZ_GetSpace (sb, 2);
+       buf[0] = c&0xff;
+       buf[1] = c>>8;
+}
+
+void MSG_WriteLong (sizebuf_t *sb, int c)
+{
+       byte    *buf;
+       
+       buf = SZ_GetSpace (sb, 4);
+       buf[0] = c&0xff;
+       buf[1] = (c>>8)&0xff;
+       buf[2] = (c>>16)&0xff;
+       buf[3] = c>>24;
+}
+
+void MSG_WriteFloat (sizebuf_t *sb, float f)
+{
+       union
+       {
+               float   f;
+               int     l;
+       } dat;
+       
+       
+       dat.f = f;
+       dat.l = LittleLong (dat.l);
+       
+       SZ_Write (sb, &dat.l, 4);
+}
+
+void MSG_WriteString (sizebuf_t *sb, char *s)
+{
+       if (!s)
+               SZ_Write (sb, "", 1);
+       else
+               SZ_Write (sb, s, strlen(s)+1);
+}
+
+void MSG_WriteCoord (sizebuf_t *sb, float f)
+{
+       MSG_WriteShort (sb, (int)(f*8));
+}
+
+void MSG_WriteAngle (sizebuf_t *sb, float f)
+{
+       MSG_WriteByte (sb, ((int)f*256/360) & 255);
+}
+
+//
+// reading functions
+//
+int                     msg_readcount;
+qboolean        msg_badread;
+
+void MSG_BeginReading (void)
+{
+       msg_readcount = 0;
+       msg_badread = false;
+}
+
+/*
+// returns -1 and sets msg_badread if no more characters are available
+int MSG_ReadChar (void)
+{
+       int     c;
+       
+       // LordHavoc: minor optimization
+       if (msg_readcount >= net_message.cursize)
+//     if (msg_readcount+1 > net_message.cursize)
+       {
+               msg_badread = true;
+               return -1;
+       }
+               
+       c = (signed char)net_message.data[msg_readcount];
+       msg_readcount++;
+       
+       return c;
+}
+
+int MSG_ReadByte (void)
+{
+       int     c;
+       
+       // LordHavoc: minor optimization
+       if (msg_readcount >= net_message.cursize)
+//     if (msg_readcount+1 > net_message.cursize)
+       {
+               msg_badread = true;
+               return -1;
+       }
+               
+       c = (unsigned char)net_message.data[msg_readcount];
+       msg_readcount++;
+       
+       return c;
+}
+*/
+
+int MSG_ReadShort (void)
+{
+       int     c;
+       
+       if (msg_readcount+2 > net_message.cursize)
+       {
+               msg_badread = true;
+               return -1;
+       }
+               
+       c = (short)(net_message.data[msg_readcount]
+       + (net_message.data[msg_readcount+1]<<8));
+       
+       msg_readcount += 2;
+       
+       return c;
+}
+
+int MSG_ReadLong (void)
+{
+       int     c;
+       
+       if (msg_readcount+4 > net_message.cursize)
+       {
+               msg_badread = true;
+               return -1;
+       }
+               
+       c = net_message.data[msg_readcount]
+       + (net_message.data[msg_readcount+1]<<8)
+       + (net_message.data[msg_readcount+2]<<16)
+       + (net_message.data[msg_readcount+3]<<24);
+       
+       msg_readcount += 4;
+       
+       return c;
+}
+
+float MSG_ReadFloat (void)
+{
+       union
+       {
+               byte    b[4];
+               float   f;
+               int     l;
+       } dat;
+       
+       dat.b[0] =      net_message.data[msg_readcount];
+       dat.b[1] =      net_message.data[msg_readcount+1];
+       dat.b[2] =      net_message.data[msg_readcount+2];
+       dat.b[3] =      net_message.data[msg_readcount+3];
+       msg_readcount += 4;
+       
+       dat.l = LittleLong (dat.l);
+
+       return dat.f;   
+}
+
+char *MSG_ReadString (void)
+{
+       static char     string[2048];
+       int             l,c;
+       
+       l = 0;
+       do
+       {
+               c = MSG_ReadChar ();
+               if (c == -1 || c == 0)
+                       break;
+               string[l] = c;
+               l++;
+       } while (l < sizeof(string)-1);
+       
+       string[l] = 0;
+       
+       return string;
+}
+
+/*
+float MSG_ReadCoord (void)
+{
+       return MSG_ReadShort() * (1.0f/8.0f);
+}
+
+float MSG_ReadAngle (void)
+{
+       return MSG_ReadChar() * (360.0f/256.0f);
+}
+*/
+
+
+
+//===========================================================================
+
+void SZ_Alloc (sizebuf_t *buf, int startsize)
+{
+       if (startsize < 256)
+               startsize = 256;
+       buf->data = Hunk_AllocName (startsize, "sizebuf");
+       buf->maxsize = startsize;
+       buf->cursize = 0;
+}
+
+
+void SZ_Free (sizebuf_t *buf)
+{
+//      Z_Free (buf->data);
+//      buf->data = NULL;
+//      buf->maxsize = 0;
+       buf->cursize = 0;
+}
+
+void SZ_Clear (sizebuf_t *buf)
+{
+       buf->cursize = 0;
+}
+
+void *SZ_GetSpace (sizebuf_t *buf, int length)
+{
+       void    *data;
+       
+       if (buf->cursize + length > buf->maxsize)
+       {
+               if (!buf->allowoverflow)
+                       Sys_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 512k (quake original default was 48k)");
+               
+               if (length > buf->maxsize)
+                       Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
+                       
+               buf->overflowed = true;
+               Con_Printf ("SZ_GetSpace: overflow");
+               SZ_Clear (buf); 
+       }
+
+       data = buf->data + buf->cursize;
+       buf->cursize += length;
+       
+       return data;
+}
+
+void SZ_Write (sizebuf_t *buf, void *data, int length)
+{
+       memcpy (SZ_GetSpace(buf,length),data,length);         
+}
+
+void SZ_Print (sizebuf_t *buf, char *data)
+{
+       int             len;
+       
+       len = strlen(data)+1;
+
+// byte * cast to keep VC++ happy
+       if (buf->data[buf->cursize-1])
+               memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
+       else
+               memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
+}
+
+
+//============================================================================
+
+
+/*
+============
+COM_SkipPath
+============
+*/
+char *COM_SkipPath (char *pathname)
+{
+       char    *last;
+       
+       last = pathname;
+       while (*pathname)
+       {
+               if (*pathname=='/')
+                       last = pathname+1;
+               pathname++;
+       }
+       return last;
+}
+
+/*
+============
+COM_StripExtension
+============
+*/
+void COM_StripExtension (char *in, char *out)
+{
+       while (*in && *in != '.')
+               *out++ = *in++;
+       *out = 0;
+}
+
+/*
+============
+COM_FileExtension
+============
+*/
+char *COM_FileExtension (char *in)
+{
+       static char exten[8];
+       int             i;
+
+       while (*in && *in != '.')
+               in++;
+       if (!*in)
+               return "";
+       in++;
+       for (i=0 ; i<7 && *in ; i++,in++)
+               exten[i] = *in;
+       exten[i] = 0;
+       return exten;
+}
+
+/*
+============
+COM_FileBase
+============
+*/
+void COM_FileBase (char *in, char *out)
+{
+       char *s, *s2;
+       
+       s = in + strlen(in) - 1;
+       
+       while (s != in && *s != '.')
+               s--;
+       
+       for (s2 = s ; *s2 && *s2 != '/' ; s2--)
+       ;
+       
+       if (s-s2 < 2)
+               strcpy (out,"?model?");
+       else
+       {
+               s--;
+               strncpy (out,s2+1, s-s2);
+               out[s-s2] = 0;
+       }
+}
+
+
+/*
+==================
+COM_DefaultExtension
+==================
+*/
+void COM_DefaultExtension (char *path, char *extension)
+{
+       char    *src;
+//
+// if path doesn't have a .EXT, append extension
+// (extension should include the .)
+//
+       src = path + strlen(path) - 1;
+
+       while (*src != '/' && src != path)
+       {
+               if (*src == '.')
+                       return;                 // it has an extension
+               src--;
+       }
+
+       strcat (path, extension);
+}
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+       int             c;
+       int             len;
+       
+       len = 0;
+       com_token[0] = 0;
+       
+       if (!data)
+               return NULL;
+               
+// skip whitespace
+skipwhite:
+       while ( (c = *data) <= ' ')
+       {
+               if (c == 0)
+                       return NULL;                    // end of file;
+               data++;
+       }
+       
+// skip // comments
+       if (c=='/' && data[1] == '/')
+       {
+               while (*data && *data != '\n')
+                       data++;
+               goto skipwhite;
+       }
+       
+
+// handle quoted strings specially
+       if (c == '\"')
+       {
+               data++;
+               while (1)
+               {
+                       c = *data++;
+                       if (c=='\"' || !c)
+                       {
+                               com_token[len] = 0;
+                               return data;
+                       }
+                       com_token[len] = c;
+                       len++;
+               }
+       }
+
+// parse single characters
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+       {
+               com_token[len] = c;
+               len++;
+               com_token[len] = 0;
+               return data+1;
+       }
+
+// parse a regular word
+       do
+       {
+               com_token[len] = c;
+               data++;
+               len++;
+               c = *data;
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+                       break;
+       } while (c>32);
+       
+       com_token[len] = 0;
+       return data;
+}
+
+
+/*
+================
+COM_CheckParm
+
+Returns the position (1 to argc-1) in the program's argument list
+where the given parameter apears, or 0 if not present
+================
+*/
+int COM_CheckParm (char *parm)
+{
+       int             i;
+       
+       for (i=1 ; i<com_argc ; i++)
+       {
+               if (!com_argv[i])
+                       continue;               // NEXTSTEP sometimes clears appkit vars.
+               if (!strcmp (parm,com_argv[i]))
+                       return i;
+       }
+               
+       return 0;
+}
+
+/*
+================
+COM_CheckRegistered
+
+Looks for the pop.txt file and verifies it.
+Sets the "registered" cvar.
+Immediately exits out if an alternate game was attempted to be started without
+being registered.
+================
+*/
+void COM_CheckRegistered (void)
+{
+       int             h;
+       unsigned short  check[128];
+       int                     i;
+
+       Cvar_Set ("cmdline", com_cmdline);
+
+       COM_OpenFile("gfx/pop.lmp", &h, false);
+       static_registered = 0;
+
+       if (h == -1)
+       {
+               if (com_modified)
+                       Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
+               else
+                       Con_Printf ("Playing shareware version.\n");
+//#if WINDED
+//     Sys_Error ("This dedicated server requires a full registered copy of Quake");
+//#endif
+//             Con_Printf ("Playing shareware version.\n");
+//             if (com_modified)
+//                     Sys_Error ("You must have the registered version to use modified games");
+               return;
+       }
+
+       Sys_FileRead (h, check, sizeof(check));
+       COM_CloseFile (h);
+       
+       for (i=0 ; i<128 ; i++)
+               if (pop[i] != (unsigned short)BigShort (check[i]))
+                       Sys_Error ("Corrupted data file.");
+       
+//     Cvar_Set ("cmdline", com_cmdline);
+       Cvar_Set ("registered", "1");
+       static_registered = 1;
+       Con_Printf ("Playing registered version.\n");
+}
+
+
+void COM_Path_f (void);
+
+
+/*
+================
+COM_InitArgv
+================
+*/
+void COM_InitArgv (int argc, char **argv)
+{
+       qboolean        safe;
+       int             i, j, n;
+
+// reconstitute the command line for the cmdline externally visible cvar
+       n = 0;
+
+       for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
+       {
+               i = 0;
+
+               while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
+               {
+                       com_cmdline[n++] = argv[j][i++];
+               }
+
+               if (n < (CMDLINE_LENGTH - 1))
+                       com_cmdline[n++] = ' ';
+               else
+                       break;
+       }
+
+       com_cmdline[n] = 0;
+
+       safe = false;
+
+       for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
+                com_argc++)
+       {
+               largv[com_argc] = argv[com_argc];
+               if (!strcmp ("-safe", argv[com_argc]))
+                       safe = true;
+       }
+
+       if (safe)
+       {
+       // force all the safe-mode switches. Note that we reserved extra space in
+       // case we need to add these, so we don't need an overflow check
+               for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
+               {
+                       largv[com_argc] = safeargvs[i];
+                       com_argc++;
+               }
+       }
+
+       largv[com_argc] = argvdummy;
+       com_argv = largv;
+
+#ifdef NEHAHRA
+       nehahra = true;
+       standard_quake = false;
+#else
+       if (COM_CheckParm ("-rogue"))
+       {
+               rogue = true;
+               standard_quake = false;
+       }
+
+       if (COM_CheckParm ("-hipnotic"))
+       {
+               hipnotic = true;
+               standard_quake = false;
+       }
+
+       if (COM_CheckParm ("-nehahra"))
+       {
+               nehahra = true;
+               standard_quake = false;
+       }
+#endif
+}
+
+
+/*
+================
+COM_Init
+================
+*/
+void COM_Init (char *basedir)
+{
+#ifndef WIN32
+       byte    swaptest[2] = {1,0};
+
+// set the byte swapping variables in a portable manner 
+       if ( *(short *)swaptest == 1)
+       {
+               BigShort = ShortSwap;
+               LittleShort = ShortNoSwap;
+               BigLong = LongSwap;
+               LittleLong = LongNoSwap;
+               BigFloat = FloatSwap;
+               LittleFloat = FloatNoSwap;
+       }
+       else
+       {
+               BigShort = ShortNoSwap;
+               LittleShort = ShortSwap;
+               BigLong = LongNoSwap;
+               LittleLong = LongSwap;
+               BigFloat = FloatNoSwap;
+               LittleFloat = FloatSwap;
+       }
+#endif
+
+       Cvar_RegisterVariable (&registered);
+       Cvar_RegisterVariable (&cmdline);
+       Cmd_AddCommand ("path", COM_Path_f);
+
+       COM_InitFilesystem ();
+       COM_CheckRegistered ();
+}
+
+
+/*
+============
+va
+
+does a varargs printf into a temp buffer, so I don't need to have
+varargs versions of all text functions.
+FIXME: make this buffer size safe someday
+============
+*/
+char    *va(char *format, ...)
+{
+       va_list         argptr;
+       static char             string[1024];
+       
+       va_start (argptr, format);
+       vsprintf (string, format,argptr);
+       va_end (argptr);
+
+       return string;  
+}
+
+
+/// just for debugging
+int     memsearch (byte *start, int count, int search)
+{
+       int             i;
+       
+       for (i=0 ; i<count ; i++)
+               if (start[i] == search)
+                       return i;
+       return -1;
+}
+
+/*
+=============================================================================
+
+QUAKE FILESYSTEM
+
+=============================================================================
+*/
+
+int     com_filesize;
+
+
+//
+// in memory
+//
+
+typedef struct
+{
+       char    name[MAX_QPATH];
+       int             filepos, filelen;
+} packfile_t;
+
+typedef struct pack_s
+{
+       char    filename[MAX_OSPATH];
+       int             handle;
+       int             numfiles;
+       packfile_t      *files;
+} pack_t;
+
+//
+// on disk
+//
+typedef struct
+{
+       char    name[56];
+       int             filepos, filelen;
+} dpackfile_t;
+
+typedef struct
+{
+       char    id[4];
+       int             dirofs;
+       int             dirlen;
+} dpackheader_t;
+
+#define MAX_FILES_IN_PACK       2048
+
+char    com_cachedir[MAX_OSPATH];
+char    com_gamedir[MAX_OSPATH];
+
+typedef struct searchpath_s
+{
+       char    filename[MAX_OSPATH];
+       pack_t  *pack;          // only one of filename / pack will be used
+       struct searchpath_s *next;
+} searchpath_t;
+
+searchpath_t    *com_searchpaths;
+
+/*
+============
+COM_Path_f
+
+============
+*/
+void COM_Path_f (void)
+{
+       searchpath_t    *s;
+       
+       Con_Printf ("Current search path:\n");
+       for (s=com_searchpaths ; s ; s=s->next)
+       {
+               if (s->pack)
+               {
+                       Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+               }
+               else
+                       Con_Printf ("%s\n", s->filename);
+       }
+}
+
+/*
+============
+COM_WriteFile
+
+The filename will be prefixed by the current game directory
+============
+*/
+void COM_WriteFile (char *filename, void *data, int len)
+{
+       int             handle;
+       char    name[MAX_OSPATH];
+       
+       sprintf (name, "%s/%s", com_gamedir, filename);
+
+       handle = Sys_FileOpenWrite (name);
+       if (handle == -1)
+       {
+               Sys_Printf ("COM_WriteFile: failed on %s\n", name);
+               return;
+       }
+       
+       Sys_Printf ("COM_WriteFile: %s\n", name);
+       Sys_FileWrite (handle, data, len);
+       Sys_FileClose (handle);
+}
+
+
+/*
+============
+COM_CreatePath
+
+Only used for CopyFile
+============
+*/
+void    COM_CreatePath (char *path)
+{
+       char    *ofs;
+       
+       for (ofs = path+1 ; *ofs ; ofs++)
+       {
+               if (*ofs == '/')
+               {       // create the directory
+                       *ofs = 0;
+                       Sys_mkdir (path);
+                       *ofs = '/';
+               }
+       }
+}
+
+
+/*
+===========
+COM_CopyFile
+
+Copies a file over from the net to the local cache, creating any directories
+needed.  This is for the convenience of developers using ISDN from home.
+===========
+*/
+void COM_CopyFile (char *netpath, char *cachepath)
+{
+       int             in, out;
+       int             remaining, count;
+       char    buf[4096];
+       
+       remaining = Sys_FileOpenRead (netpath, &in);            
+       COM_CreatePath (cachepath);     // create directories up to the cache file
+       out = Sys_FileOpenWrite (cachepath);
+       
+       while (remaining)
+       {
+               if (remaining < sizeof(buf))
+                       count = remaining;
+               else
+                       count = sizeof(buf);
+               Sys_FileRead (in, buf, count);
+               Sys_FileWrite (out, buf, count);
+               remaining -= count;
+       }
+
+       Sys_FileClose (in);
+       Sys_FileClose (out);    
+}
+
+/*
+===========
+COM_FindFile
+
+Finds the file in the search path.
+Sets com_filesize and one of handle or file
+===========
+*/
+int COM_FindFile (char *filename, int *handle, FILE **file, qboolean quiet)
+{
+       searchpath_t    *search;
+       char            netpath[MAX_OSPATH];
+       char            cachepath[MAX_OSPATH];
+       pack_t          *pak;
+       int                     i;
+       int                     findtime, cachetime;
+
+       if (file && handle)
+               Sys_Error ("COM_FindFile: both handle and file set");
+       if (!file && !handle)
+               Sys_Error ("COM_FindFile: neither handle or file set");
+               
+//
+// search through the path, one element at a time
+//
+       search = com_searchpaths;
+       if (proghack)
+       {       // gross hack to use quake 1 progs with quake 2 maps
+               if (!strcmp(filename, "progs.dat"))
+                       search = search->next;
+       }
+
+       for ( ; search ; search = search->next)
+       {
+       // is the element a pak file?
+               if (search->pack)
+               {
+               // look through all the pak file elements
+                       pak = search->pack;
+                       for (i=0 ; i<pak->numfiles ; i++)
+                               if (!strcmp (pak->files[i].name, filename))
+                               {       // found it!
+                                       if (!quiet)
+                                               Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
+                                       if (handle)
+                                       {
+                                               *handle = pak->handle;
+                                               Sys_FileSeek (pak->handle, pak->files[i].filepos);
+                                       }
+                                       else
+                                       {       // open a new file on the pakfile
+                                               *file = fopen (pak->filename, "rb");
+                                               if (*file)
+                                                       fseek (*file, pak->files[i].filepos, SEEK_SET);
+                                       }
+                                       com_filesize = pak->files[i].filelen;
+                                       return com_filesize;
+                               }
+               }
+               else
+               {               
+       // check a file in the directory tree
+//                     if (!static_registered)
+//                     {       // if not a registered version, don't ever go beyond base
+//                             if ( strchr (filename, '/') || strchr (filename,'\\'))
+//                                     continue;
+//                     }
+                       
+                       sprintf (netpath, "%s/%s",search->filename, filename);
+                       
+                       findtime = Sys_FileTime (netpath);
+                       if (findtime == -1)
+                               continue;
+                               
+               // see if the file needs to be updated in the cache
+                       if (!com_cachedir[0])
+                               strcpy (cachepath, netpath);
+                       else
+                       {       
+#if defined(_WIN32)
+                               if ((strlen(netpath) < 2) || (netpath[1] != ':'))
+                                       sprintf (cachepath,"%s%s", com_cachedir, netpath);
+                               else
+                                       sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
+#else
+                               sprintf (cachepath,"%s%s", com_cachedir, netpath);
+#endif
+
+                               cachetime = Sys_FileTime (cachepath);
+                       
+                               if (cachetime < findtime)
+                                       COM_CopyFile (netpath, cachepath);
+                               strcpy (netpath, cachepath);
+                       }       
+
+                       if (!quiet)
+                               Sys_Printf ("FindFile: %s\n",netpath);
+                       com_filesize = Sys_FileOpenRead (netpath, &i);
+                       if (handle)
+                               *handle = i;
+                       else
+                       {
+                               Sys_FileClose (i);
+                               *file = fopen (netpath, "rb");
+                       }
+                       return com_filesize;
+               }
+               
+       }
+       
+       if (!quiet)
+               Sys_Printf ("FindFile: can't find %s\n", filename);
+       
+       if (handle)
+               *handle = -1;
+       else
+               *file = NULL;
+       com_filesize = -1;
+       return -1;
+}
+
+
+/*
+===========
+COM_OpenFile
+
+filename never has a leading slash, but may contain directory walks
+returns a handle and a length
+it may actually be inside a pak file
+===========
+*/
+int COM_OpenFile (char *filename, int *handle, qboolean quiet)
+{
+       return COM_FindFile (filename, handle, NULL, quiet);
+}
+
+/*
+===========
+COM_FOpenFile
+
+If the requested file is inside a packfile, a new FILE * will be opened
+into the file.
+===========
+*/
+int COM_FOpenFile (char *filename, FILE **file, qboolean quiet)
+{
+       return COM_FindFile (filename, NULL, file, quiet);
+}
+
+/*
+============
+COM_CloseFile
+
+If it is a pak file handle, don't really close it
+============
+*/
+void COM_CloseFile (int h)
+{
+       searchpath_t    *s;
+       
+       for (s = com_searchpaths ; s ; s=s->next)
+               if (s->pack && s->pack->handle == h)
+                       return;
+                       
+       Sys_FileClose (h);
+}
+
+
+/*
+============
+COM_LoadFile
+
+Filename are reletive to the quake directory.
+Allways appends a 0 byte.
+============
+*/
+cache_user_t *loadcache;
+byte    *loadbuf;
+int             loadsize;
+byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
+{
+       int             h;
+       byte    *buf;
+       char    base[32];
+       int             len;
+
+       buf = NULL;     // quiet compiler warning
+
+// look for it in the filesystem or pack files
+       len = COM_OpenFile (path, &h, quiet);
+       if (h == -1)
+               return NULL;
+       
+// extract the filename base name for hunk tag
+       COM_FileBase (path, base);
+       
+       if (usehunk == 1)
+               buf = Hunk_AllocName (len+1, base);
+       else if (usehunk == 2)
+               buf = Hunk_TempAlloc (len+1);
+       else if (usehunk == 0)
+               buf = Z_Malloc (len+1);
+       else if (usehunk == 3)
+               buf = Cache_Alloc (loadcache, len+1, base);
+       else if (usehunk == 4)
+       {
+               if (len+1 > loadsize)
+                       buf = Hunk_TempAlloc (len+1);
+               else
+                       buf = loadbuf;
+       }
+       else
+               Sys_Error ("COM_LoadFile: bad usehunk");
+
+       if (!buf)
+               Sys_Error ("COM_LoadFile: not enough space for %s", path);
+               
+       ((byte *)buf)[len] = 0;
+
+       Sys_FileRead (h, buf, len);                     
+       COM_CloseFile (h);
+
+       return buf;
+}
+
+byte *COM_LoadHunkFile (char *path, qboolean quiet)
+{
+       return COM_LoadFile (path, 1, quiet);
+}
+
+byte *COM_LoadTempFile (char *path, qboolean quiet)
+{
+       return COM_LoadFile (path, 2, quiet);
+}
+
+void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
+{
+       loadcache = cu;
+       COM_LoadFile (path, 3, quiet);
+}
+
+// uses temp hunk if larger than bufsize
+byte *COM_LoadStackFile (char *path, void *buffer, int bufsize, qboolean quiet)
+{
+       byte    *buf;
+       
+       loadbuf = (byte *)buffer;
+       loadsize = bufsize;
+       buf = COM_LoadFile (path, 4, quiet);
+       
+       return buf;
+}
+
+/*
+=================
+COM_LoadPackFile
+
+Takes an explicit (not game tree related) path to a pak file.
+
+Loads the header and directory, adding the files at the beginning
+of the list so they override previous pack files.
+=================
+*/
+pack_t *COM_LoadPackFile (char *packfile)
+{
+       dpackheader_t   header;
+       int                             i;
+       packfile_t              *newfiles;
+       int                             numpackfiles;
+       pack_t                  *pack;
+       int                             packhandle;
+       dpackfile_t             info[MAX_FILES_IN_PACK];
+       unsigned short          crc;
+
+       if (Sys_FileOpenRead (packfile, &packhandle) == -1)
+       {
+//              Con_Printf ("Couldn't open %s\n", packfile);
+               return NULL;
+       }
+       Sys_FileRead (packhandle, (void *)&header, sizeof(header));
+       if (header.id[0] != 'P' || header.id[1] != 'A'
+       || header.id[2] != 'C' || header.id[3] != 'K')
+               Sys_Error ("%s is not a packfile", packfile);
+       header.dirofs = LittleLong (header.dirofs);
+       header.dirlen = LittleLong (header.dirlen);
+
+       numpackfiles = header.dirlen / sizeof(dpackfile_t);
+
+       if (numpackfiles > MAX_FILES_IN_PACK)
+               Sys_Error ("%s has %i files", packfile, numpackfiles);
+
+       if (numpackfiles != PAK0_COUNT)
+               com_modified = true;    // not the original file
+
+       newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
+
+       Sys_FileSeek (packhandle, header.dirofs);
+       Sys_FileRead (packhandle, (void *)info, header.dirlen);
+
+// crc the directory to check for modifications
+       CRC_Init (&crc);
+       // LordHavoc: speedup
+       CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
+//     for (i=0 ; i<header.dirlen ; i++)
+//             CRC_ProcessByte (&crc, ((byte *)info)[i]);
+       if (crc != PAK0_CRC)
+               com_modified = true;
+
+// parse the directory
+       for (i=0 ; i<numpackfiles ; i++)
+       {
+               strcpy (newfiles[i].name, info[i].name);
+               newfiles[i].filepos = LittleLong(info[i].filepos);
+               newfiles[i].filelen = LittleLong(info[i].filelen);
+       }
+
+       pack = Hunk_Alloc (sizeof (pack_t));
+       strcpy (pack->filename, packfile);
+       pack->handle = packhandle;
+       pack->numfiles = numpackfiles;
+       pack->files = newfiles;
+       
+       Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
+       return pack;
+}
+
+
+/*
+================
+COM_AddGameDirectory
+
+Sets com_gamedir, adds the directory to the head of the path,
+then loads and adds pak1.pak pak2.pak ... 
+================
+*/
+void COM_AddGameDirectory (char *dir)
+{
+       int                             i;
+       searchpath_t    *search;
+       pack_t                  *pak;
+       char                    pakfile[MAX_OSPATH];
+
+       strcpy (com_gamedir, dir);
+
+//
+// add the directory to the search path
+//
+       search = Hunk_Alloc (sizeof(searchpath_t));
+       strcpy (search->filename, dir);
+       search->next = com_searchpaths;
+       com_searchpaths = search;
+
+//
+// add any pak files in the format pak0.pak pak1.pak, ...
+//
+       for (i=0 ; ; i++)
+       {
+               sprintf (pakfile, "%s/pak%i.pak", dir, i);
+               pak = COM_LoadPackFile (pakfile);
+               if (!pak)
+                       break;
+               search = Hunk_Alloc (sizeof(searchpath_t));
+               search->pack = pak;
+               search->next = com_searchpaths;
+               com_searchpaths = search;               
+       }
+
+//
+// add the contents of the parms.txt file to the end of the command line
+//
+
+}
+
+/*
+================
+COM_InitFilesystem
+================
+*/
+void COM_InitFilesystem (void)
+{
+       int             i, j;
+       char    basedir[MAX_OSPATH];
+       searchpath_t    *search;
+
+//
+// -basedir <path>
+// Overrides the system supplied base directory (under GAMENAME)
+//
+       i = COM_CheckParm ("-basedir");
+       if (i && i < com_argc-1)
+               strcpy (basedir, com_argv[i+1]);
+       else
+               strcpy (basedir, host_parms.basedir);
+
+       j = strlen (basedir);
+
+       if (j > 0)
+       {
+               if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
+                       basedir[j-1] = 0;
+       }
+
+//
+// -cachedir <path>
+// Overrides the system supplied cache directory (NULL or /qcache)
+// -cachedir - will disable caching.
+//
+       i = COM_CheckParm ("-cachedir");
+       if (i && i < com_argc-1)
+       {
+               if (com_argv[i+1][0] == '-')
+                       com_cachedir[0] = 0;
+               else
+                       strcpy (com_cachedir, com_argv[i+1]);
+       }
+       else if (host_parms.cachedir)
+               strcpy (com_cachedir, host_parms.cachedir);
+       else
+               com_cachedir[0] = 0;
+
+//
+// start up with GAMENAME by default (id1)
+//
+       COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
+
+#ifdef NEHAHRA
+       COM_AddGameDirectory (va("%s/nehahra", basedir) );
+#else
+       if (COM_CheckParm ("-rogue"))
+               COM_AddGameDirectory (va("%s/rogue", basedir) );
+       if (COM_CheckParm ("-hipnotic"))
+               COM_AddGameDirectory (va("%s/hipnotic", basedir) );
+       if (COM_CheckParm ("-nehahra"))
+               COM_AddGameDirectory (va("%s/nehahra", basedir) );
+#endif
+
+//
+// -game <gamedir>
+// Adds basedir/gamedir as an override game
+//
+       i = COM_CheckParm ("-game");
+       if (i && i < com_argc-1)
+       {
+               com_modified = true;
+               COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
+       }
+
+//
+// -path <dir or packfile> [<dir or packfile>] ...
+// Fully specifies the exact serach path, overriding the generated one
+//
+       i = COM_CheckParm ("-path");
+       if (i)
+       {
+               com_modified = true;
+               com_searchpaths = NULL;
+               while (++i < com_argc)
+               {
+                       if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
+                               break;
+                       
+                       search = Hunk_Alloc (sizeof(searchpath_t));
+                       if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
+                       {
+                               search->pack = COM_LoadPackFile (com_argv[i]);
+                               if (!search->pack)
+                                       Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
+                       }
+                       else
+                               strcpy (search->filename, com_argv[i]);
+                       search->next = com_searchpaths;
+                       com_searchpaths = search;
+               }
+       }
+
+       if (COM_CheckParm ("-proghack"))
+               proghack = true;
+}
+
+int COM_FileExists(char *filename)
+{
+       searchpath_t    *search;
+       char                    netpath[MAX_OSPATH];
+       pack_t                  *pak;
+       int                             i;
+       int                             findtime;
+
+       for (search = com_searchpaths;search;search = search->next)
+       {
+               if (search->pack)
+               {
+                       pak = search->pack;
+                       for (i = 0;i < pak->numfiles;i++)
+                               if (!strcmp (pak->files[i].name, filename))
+                                       return true;
+               }
+               else
+               {
+                       sprintf (netpath, "%s/%s",search->filename, filename);               
+                       findtime = Sys_FileTime (netpath);
+                       if (findtime != -1)
+                               return true;
+               }               
+       }
+
+       return false;
+}
+
diff --git a/common.h b/common.h
new file mode 100644 (file)
index 0000000..eb2175c
--- /dev/null
+++ b/common.h
@@ -0,0 +1,207 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// comndef.h  -- general definitions
+
+#if !defined BYTE_DEFINED
+typedef unsigned char          byte;
+#define BYTE_DEFINED 1
+#endif
+
+#undef true
+#undef false
+
+typedef enum {false, true}     qboolean;
+
+//============================================================================
+
+typedef struct sizebuf_s
+{
+       qboolean        allowoverflow;  // if false, do a Sys_Error
+       qboolean        overflowed;             // set to true if the buffer size failed
+       byte    *data;
+       int             maxsize;
+       int             cursize;
+} sizebuf_t;
+
+void SZ_Alloc (sizebuf_t *buf, int startsize);
+void SZ_Free (sizebuf_t *buf);
+void SZ_Clear (sizebuf_t *buf);
+void *SZ_GetSpace (sizebuf_t *buf, int length);
+void SZ_Write (sizebuf_t *buf, void *data, int length);
+void SZ_Print (sizebuf_t *buf, char *data);    // strcats onto the sizebuf
+
+//============================================================================
+
+typedef struct link_s
+{
+       struct link_s   *prev, *next;
+} link_t;
+
+
+void ClearLink (link_t *l);
+void RemoveLink (link_t *l);
+void InsertLinkBefore (link_t *l, link_t *before);
+void InsertLinkAfter (link_t *l, link_t *after);
+
+// (type *)STRUCT_FROM_LINK(link_t *link, type, member)
+// ent = STRUCT_FROM_LINK(link,entity_t,order)
+// FIXME: remove this mess!
+#define        STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m)))
+
+//============================================================================
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define Q_MAXCHAR ((char)0x7f)
+#define Q_MAXSHORT ((short)0x7fff)
+#define Q_MAXINT       ((int)0x7fffffff)
+#define Q_MAXLONG ((int)0x7fffffff)
+#define Q_MAXFLOAT ((int)0x7fffffff)
+
+#define Q_MINCHAR ((char)0x80)
+#define Q_MINSHORT ((short)0x8000)
+#define Q_MININT       ((int)0x80000000)
+#define Q_MINLONG ((int)0x80000000)
+#define Q_MINFLOAT ((int)0x7fffffff)
+
+//============================================================================
+#ifdef WIN32
+short   ShortSwap (short l);
+int    LongSwap (int l);
+float FloatSwap (float f);
+#define BigShort(l) ShortSwap(l)
+#define LittleShort(l) (l)
+#define BigLong(l) LongSwap(l)
+#define LittleLong(l) (l)
+#define BigFloat(l) FloatSwap(l)
+#define LittleFloat(l) (l)
+#else
+extern short   (*BigShort) (short l);
+extern short   (*LittleShort) (short l);
+extern int     (*BigLong) (int l);
+extern int     (*LittleLong) (int l);
+extern float   (*BigFloat) (float l);
+extern float   (*LittleFloat) (float l);
+#endif
+
+//============================================================================
+
+void MSG_WriteChar (sizebuf_t *sb, int c);
+void MSG_WriteByte (sizebuf_t *sb, int c);
+void MSG_WriteShort (sizebuf_t *sb, int c);
+void MSG_WriteLong (sizebuf_t *sb, int c);
+void MSG_WriteFloat (sizebuf_t *sb, float f);
+void MSG_WriteString (sizebuf_t *sb, char *s);
+void MSG_WriteCoord (sizebuf_t *sb, float f);
+void MSG_WriteAngle (sizebuf_t *sb, float f);
+
+extern int                     msg_readcount;
+extern qboolean        msg_badread;            // set if a read goes beyond end of message
+
+void MSG_BeginReading (void);
+//int MSG_ReadChar (void);
+//int MSG_ReadByte (void);
+int MSG_ReadShort (void);
+int MSG_ReadLong (void);
+float MSG_ReadFloat (void);
+char *MSG_ReadString (void);
+
+#define MSG_ReadChar() (msg_readcount >= net_message.cursize ? (msg_badread = true, -1) : (signed char)net_message.data[msg_readcount++])
+#define MSG_ReadByte() (msg_readcount >= net_message.cursize ? (msg_badread = true, -1) : (unsigned char)net_message.data[msg_readcount++])
+//#define MSG_ReadShort() ((msg_readcount + 2) > net_message.cursize ? (msg_badread = true, -1) : (short)net_message.data[msg_readcount+=2, msg_readcount-2] | (net_message.data[msg_readcount-1] << 8))
+//#define MSG_ReadLong() ((msg_readcount + 4) > net_message.cursize ? (msg_badread = true, -1) : (int)net_message.data[msg_readcount+=4, msg_readcount-4] | (net_message.data[msg_readcount-3] << 8) | (net_message.data[msg_readcount-2] << 16) | (net_message.data[msg_readcount-1] << 24))
+
+//float MSG_ReadCoord (void);
+//float MSG_ReadAngle (void);
+
+#define MSG_ReadAngle() (MSG_ReadByte() * (360.0f / 256.0f))
+#define MSG_ReadCoord() (MSG_ReadShort() * 0.125f)
+
+//============================================================================
+
+/*
+void Q_memset (void *dest, int fill, int count);
+void Q_memcpy (void *dest, void *src, int count);
+int Q_memcmp (void *m1, void *m2, int count);
+void Q_strcpy (char *dest, char *src);
+void Q_strncpy (char *dest, char *src, int count);
+int Q_strlen (char *str);
+char *Q_strrchr (char *s, char c);
+void Q_strcat (char *dest, char *src);
+int Q_strcmp (char *s1, char *s2);
+int Q_strncmp (char *s1, char *s2, int count);
+*/
+int Q_strcasecmp (char *s1, char *s2);
+int Q_strncasecmp (char *s1, char *s2, int n);
+/*
+int    Q_atoi (char *str);
+float Q_atof (char *str);
+*/
+
+//============================================================================
+
+extern char            com_token[1024];
+extern qboolean        com_eof;
+
+char *COM_Parse (char *data);
+
+
+extern int             com_argc;
+extern char    **com_argv;
+
+int COM_CheckParm (char *parm);
+void COM_Init (char *path);
+void COM_InitArgv (int argc, char **argv);
+
+char *COM_SkipPath (char *pathname);
+void COM_StripExtension (char *in, char *out);
+void COM_FileBase (char *in, char *out);
+void COM_DefaultExtension (char *path, char *extension);
+
+char   *va(char *format, ...);
+// does a varargs printf into a temp buffer
+
+
+//============================================================================
+
+extern int com_filesize;
+struct cache_user_s;
+
+extern char    com_gamedir[MAX_OSPATH];
+
+void COM_WriteFile (char *filename, void *data, int len);
+int COM_OpenFile (char *filename, int *hndl, qboolean quiet);
+int COM_FOpenFile (char *filename, FILE **file, qboolean quiet);
+void COM_CloseFile (int h);
+
+byte *COM_LoadStackFile (char *path, void *buffer, int bufsize, qboolean quiet);
+byte *COM_LoadTempFile (char *path, qboolean quiet);
+byte *COM_LoadHunkFile (char *path, qboolean quiet);
+void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet);
+
+byte *COM_LoadFile (char *path, int usehunk, qboolean quiet);
+
+int COM_FileExists(char *filename);
+
+extern struct cvar_s   registered;
+
+extern qboolean                standard_quake, rogue, hipnotic, nehahra;
diff --git a/conproc.c b/conproc.c
new file mode 100644 (file)
index 0000000..38e031b
--- /dev/null
+++ b/conproc.c
@@ -0,0 +1,363 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// conproc.c
+
+#include <windows.h>
+#include "conproc.h"
+#include "quakedef.h"
+
+HANDLE heventDone;
+HANDLE hfileBuffer;
+HANDLE heventChildSend;
+HANDLE heventParentSend;
+HANDLE hStdout;
+HANDLE hStdin;
+
+DWORD RequestProc (DWORD dwNichts);
+LPVOID GetMappedBuffer (HANDLE hfileBuffer);
+void ReleaseMappedBuffer (LPVOID pBuffer);
+BOOL GetScreenBufferLines (int *piLines);
+BOOL SetScreenBufferLines (int iLines);
+BOOL ReadText (LPTSTR pszText, int iBeginLine, int iEndLine);
+BOOL WriteText (LPCTSTR szText);
+int CharToCode (char c);
+BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy);
+
+
+void InitConProc (HANDLE hFile, HANDLE heventParent, HANDLE heventChild)
+{
+       DWORD   dwID;
+
+// ignore if we don't have all the events.
+       if (!hFile || !heventParent || !heventChild)
+               return;
+
+       hfileBuffer = hFile;
+       heventParentSend = heventParent;
+       heventChildSend = heventChild;
+
+// so we'll know when to go away.
+       heventDone = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+       if (!heventDone)
+       {
+               Con_SafePrintf ("Couldn't create heventDone\n");
+               return;
+       }
+
+       if (!CreateThread (NULL,
+                                          0,
+                                          (LPTHREAD_START_ROUTINE) RequestProc,
+                                          0,
+                                          0,
+                                          &dwID))
+       {
+               CloseHandle (heventDone);
+               Con_SafePrintf ("Couldn't create QHOST thread\n");
+               return;
+       }
+
+// save off the input/output handles.
+       hStdout = GetStdHandle (STD_OUTPUT_HANDLE);
+       hStdin = GetStdHandle (STD_INPUT_HANDLE);
+
+// force 80 character width, at least 25 character height
+       SetConsoleCXCY (hStdout, 80, 25);
+}
+
+
+void DeinitConProc (void)
+{
+       if (heventDone)
+               SetEvent (heventDone);
+}
+
+
+DWORD RequestProc (DWORD dwNichts)
+{
+       int             *pBuffer;
+       DWORD   dwRet;
+       HANDLE  heventWait[2];
+       int             iBeginLine, iEndLine;
+       
+       heventWait[0] = heventParentSend;
+       heventWait[1] = heventDone;
+
+       while (1)
+       {
+               dwRet = WaitForMultipleObjects (2, heventWait, FALSE, INFINITE);
+
+       // heventDone fired, so we're exiting.
+               if (dwRet == WAIT_OBJECT_0 + 1) 
+                       break;
+
+               pBuffer = (int *) GetMappedBuffer (hfileBuffer);
+               
+       // hfileBuffer is invalid.  Just leave.
+               if (!pBuffer)
+               {
+                       Con_SafePrintf ("Invalid hfileBuffer\n");
+                       break;
+               }
+
+               switch (pBuffer[0])
+               {
+                       case CCOM_WRITE_TEXT:
+                       // Param1 : Text
+                               pBuffer[0] = WriteText ((LPCTSTR) (pBuffer + 1));
+                               break;
+
+                       case CCOM_GET_TEXT:
+                       // Param1 : Begin line
+                       // Param2 : End line
+                               iBeginLine = pBuffer[1];
+                               iEndLine = pBuffer[2];
+                               pBuffer[0] = ReadText ((LPTSTR) (pBuffer + 1), iBeginLine, 
+                                                                          iEndLine);
+                               break;
+
+                       case CCOM_GET_SCR_LINES:
+                       // No params
+                               pBuffer[0] = GetScreenBufferLines (&pBuffer[1]);
+                               break;
+
+                       case CCOM_SET_SCR_LINES:
+                       // Param1 : Number of lines
+                               pBuffer[0] = SetScreenBufferLines (pBuffer[1]);
+                               break;
+               }
+
+               ReleaseMappedBuffer (pBuffer);
+               SetEvent (heventChildSend);
+       }
+
+       return 0;
+}
+
+
+LPVOID GetMappedBuffer (HANDLE hfileBuffer)
+{
+       LPVOID pBuffer;
+
+       pBuffer = MapViewOfFile (hfileBuffer,
+                                                       FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
+
+       return pBuffer;
+}
+
+
+void ReleaseMappedBuffer (LPVOID pBuffer)
+{
+       UnmapViewOfFile (pBuffer);
+}
+
+
+BOOL GetScreenBufferLines (int *piLines)
+{
+       CONSOLE_SCREEN_BUFFER_INFO      info;                                                     
+       BOOL                                            bRet;
+
+       bRet = GetConsoleScreenBufferInfo (hStdout, &info);
+               
+       if (bRet)
+               *piLines = info.dwSize.Y;
+
+       return bRet;
+}
+
+
+BOOL SetScreenBufferLines (int iLines)
+{
+
+       return SetConsoleCXCY (hStdout, 80, iLines);
+}
+
+
+BOOL ReadText (LPTSTR pszText, int iBeginLine, int iEndLine)
+{
+       COORD   coord;
+       DWORD   dwRead;
+       BOOL    bRet;
+
+       coord.X = 0;
+       coord.Y = iBeginLine;
+
+       bRet = ReadConsoleOutputCharacter(
+               hStdout,
+               pszText,
+               80 * (iEndLine - iBeginLine + 1),
+               coord,
+               &dwRead);
+
+       // Make sure it's null terminated.
+       if (bRet)
+               pszText[dwRead] = '\0';
+
+       return bRet;
+}
+
+
+BOOL WriteText (LPCTSTR szText)
+{
+       DWORD                   dwWritten;
+       INPUT_RECORD    rec;
+       char                    upper, *sz;
+
+       sz = (LPTSTR) szText;
+
+       while (*sz)
+       {
+       // 13 is the code for a carriage return (\n) instead of 10.
+               if (*sz == 10)
+                       *sz = 13;
+
+               upper = toupper(*sz);
+
+               rec.EventType = KEY_EVENT;
+               rec.Event.KeyEvent.bKeyDown = TRUE;
+               rec.Event.KeyEvent.wRepeatCount = 1;
+               rec.Event.KeyEvent.wVirtualKeyCode = upper;
+               rec.Event.KeyEvent.wVirtualScanCode = CharToCode (*sz);
+               rec.Event.KeyEvent.uChar.AsciiChar = *sz;
+               rec.Event.KeyEvent.uChar.UnicodeChar = *sz;
+               rec.Event.KeyEvent.dwControlKeyState = isupper(*sz) ? 0x80 : 0x0; 
+
+               WriteConsoleInput(
+                       hStdin,
+                       &rec,
+                       1,
+                       &dwWritten);
+
+               rec.Event.KeyEvent.bKeyDown = FALSE;
+
+               WriteConsoleInput(
+                       hStdin,
+                       &rec,
+                       1,
+                       &dwWritten);
+
+               sz++;
+       }
+
+       return TRUE;
+}
+
+
+int CharToCode (char c)
+{
+       char upper;
+               
+       upper = toupper(c);
+
+       switch (c)
+       {
+               case 13:
+                       return 28;
+
+               default:
+                       break;
+       }
+
+       if (isalpha(c))
+               return (30 + upper - 65); 
+
+       if (isdigit(c))
+               return (1 + upper - 47);
+
+       return c;
+}
+
+
+BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy)
+{
+       CONSOLE_SCREEN_BUFFER_INFO      info;
+       COORD                                           coordMax;
+       coordMax = GetLargestConsoleWindowSize(hStdout);
+
+       if (cy > coordMax.Y)
+               cy = coordMax.Y;
+
+       if (cx > coordMax.X)
+               cx = coordMax.X;
+       if (!GetConsoleScreenBufferInfo(hStdout, &info))
+               return FALSE;
+// height
+    info.srWindow.Left = 0;         
+    info.srWindow.Right = info.dwSize.X - 1;                
+    info.srWindow.Top = 0;
+    info.srWindow.Bottom = cy - 1;          
+       if (cy < info.dwSize.Y)
+       {
+               if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
+                       return FALSE;
+               info.dwSize.Y = cy;
+               if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
+                       return FALSE;
+    }
+    else if (cy > info.dwSize.Y)
+    {
+               info.dwSize.Y = cy;
+               if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
+                       return FALSE;
+               if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
+                       return FALSE;
+    }
+       if (!GetConsoleScreenBufferInfo(hStdout, &info))
+               return FALSE;
+// width
+       info.srWindow.Left = 0;         
+       info.srWindow.Right = cx - 1;
+       info.srWindow.Top = 0;
+       info.srWindow.Bottom = info.dwSize.Y - 1;               
+       if (cx < info.dwSize.X)
+       {
+               if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
+                       return FALSE;
+               info.dwSize.X = cx;
+    
+               if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
+                       return FALSE;
+       }
+       else if (cx > info.dwSize.X)
+       {
+               info.dwSize.X = cx;
+               if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
+                       return FALSE;
+               if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
+                       return FALSE;
+       }
+       return TRUE;
+}
+     
diff --git a/conproc.h b/conproc.h
new file mode 100644 (file)
index 0000000..743526f
--- /dev/null
+++ b/conproc.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// conproc.h
+
+#define CCOM_WRITE_TEXT                0x2
+// Param1 : Text
+
+#define CCOM_GET_TEXT          0x3
+// Param1 : Begin line
+// Param2 : End line
+
+#define CCOM_GET_SCR_LINES     0x4
+// No params
+
+#define CCOM_SET_SCR_LINES     0x5
+// Param1 : Number of lines
+
+void InitConProc (HANDLE hFile, HANDLE heventParent, HANDLE heventChild);
+void DeinitConProc (void);
+
diff --git a/console.c b/console.c
new file mode 100644 (file)
index 0000000..a134633
--- /dev/null
+++ b/console.c
@@ -0,0 +1,681 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// console.c
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+#ifdef WIN32
+#include <io.h>
+#endif
+#include <fcntl.h>
+#include "quakedef.h"
+
+int            con_linewidth;
+
+float          con_cursorspeed = 4;
+
+#define                CON_TEXTSIZE    16384
+
+qboolean       con_forcedup;           // because no entities to refresh
+
+int                    con_totallines;         // total lines in console scrollback
+int                    con_backscroll;         // lines up from bottom to display
+int                    con_current;            // where next message will be printed
+int                    con_x;                          // offset in current line for next print
+char           *con_text=0;
+
+cvar_t         con_notifytime = {"con_notifytime","3"};                //seconds
+cvar_t         logfile = {"logfile","0"};
+
+#define        NUM_CON_TIMES 4
+float          con_times[NUM_CON_TIMES];       // realtime time the line was generated
+                                                               // for transparent notify lines
+
+int                    con_vislines;
+
+qboolean       con_debuglog;
+
+#define                MAXCMDLINE      256
+extern char    key_lines[32][MAXCMDLINE];
+extern int             edit_line;
+extern int             key_linepos;
+               
+
+qboolean       con_initialized;
+
+int                    con_notifylines;                // scan lines to clear for notify lines
+
+extern void M_Menu_Main_f (void);
+
+/*
+================
+Con_ToggleConsole_f
+================
+*/
+void Con_ToggleConsole_f (void)
+{
+       if (key_dest == key_console)
+       {
+               if (cls.state == ca_connected)
+               {
+                       key_dest = key_game;
+                       key_lines[edit_line][1] = 0;    // clear any typing
+                       key_linepos = 1;
+               }
+               else
+               {
+                       M_Menu_Main_f ();
+               }
+       }
+       else
+               key_dest = key_console;
+       
+       SCR_EndLoadingPlaque ();
+       memset (con_times, 0, sizeof(con_times));
+}
+
+/*
+================
+Con_Clear_f
+================
+*/
+void Con_Clear_f (void)
+{
+       if (con_text)
+               memset (con_text, ' ', CON_TEXTSIZE);
+}
+
+                                               
+/*
+================
+Con_ClearNotify
+================
+*/
+void Con_ClearNotify (void)
+{
+       int             i;
+       
+       for (i=0 ; i<NUM_CON_TIMES ; i++)
+               con_times[i] = 0;
+}
+
+                                               
+/*
+================
+Con_MessageMode_f
+================
+*/
+extern qboolean team_message;
+
+void Con_MessageMode_f (void)
+{
+       key_dest = key_message;
+       team_message = false;
+}
+
+                                               
+/*
+================
+Con_MessageMode2_f
+================
+*/
+void Con_MessageMode2_f (void)
+{
+       key_dest = key_message;
+       team_message = true;
+}
+
+                                               
+/*
+================
+Con_CheckResize
+
+If the line width has changed, reformat the buffer.
+================
+*/
+void Con_CheckResize (void)
+{
+       int             i, j, width, oldwidth, oldtotallines, numlines, numchars;
+       char    tbuf[CON_TEXTSIZE];
+
+       width = (vid.width >> 3) - 2;
+
+       if (width == con_linewidth)
+               return;
+
+       if (width < 1)                  // video hasn't been initialized yet
+       {
+               width = 38;
+               con_linewidth = width;
+               con_totallines = CON_TEXTSIZE / con_linewidth;
+               memset (con_text, ' ', CON_TEXTSIZE);
+       }
+       else
+       {
+               oldwidth = con_linewidth;
+               con_linewidth = width;
+               oldtotallines = con_totallines;
+               con_totallines = CON_TEXTSIZE / con_linewidth;
+               numlines = oldtotallines;
+
+               if (con_totallines < numlines)
+                       numlines = con_totallines;
+
+               numchars = oldwidth;
+       
+               if (con_linewidth < numchars)
+                       numchars = con_linewidth;
+
+               memcpy (tbuf, con_text, CON_TEXTSIZE);
+               memset (con_text, ' ', CON_TEXTSIZE);
+
+               for (i=0 ; i<numlines ; i++)
+               {
+                       for (j=0 ; j<numchars ; j++)
+                       {
+                               con_text[(con_totallines - 1 - i) * con_linewidth + j] =
+                                               tbuf[((con_current - i + oldtotallines) %
+                                                         oldtotallines) * oldwidth + j];
+                       }
+               }
+
+               Con_ClearNotify ();
+       }
+
+       con_backscroll = 0;
+       con_current = con_totallines - 1;
+}
+
+
+/*
+================
+Con_Init
+================
+*/
+void Con_Init (void)
+{
+#define MAXGAMEDIRLEN  1000
+       char    temp[MAXGAMEDIRLEN+1];
+       char    *t2 = "/qconsole.log";
+
+       Cvar_RegisterVariable(&logfile);
+       con_debuglog = COM_CheckParm("-condebug");
+
+       if (con_debuglog)
+       {
+               if (strlen (com_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
+               {
+                       sprintf (temp, "%s%s", com_gamedir, t2);
+                       unlink (temp);
+               }
+               logfile.value = 1;
+       }
+
+       con_text = Hunk_AllocName (CON_TEXTSIZE, "context");
+       memset (con_text, ' ', CON_TEXTSIZE);
+       con_linewidth = -1;
+       Con_CheckResize ();
+       
+       Con_Printf ("Console initialized.\n");
+
+//
+// register our commands
+//
+       Cvar_RegisterVariable (&con_notifytime);
+
+       Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
+       Cmd_AddCommand ("messagemode", Con_MessageMode_f);
+       Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
+       Cmd_AddCommand ("clear", Con_Clear_f);
+       con_initialized = true;
+}
+
+
+/*
+===============
+Con_Linefeed
+===============
+*/
+void Con_Linefeed (void)
+{
+       con_x = 0;
+       con_current++;
+       memset (&con_text[(con_current%con_totallines)*con_linewidth]
+       , ' ', con_linewidth);
+}
+
+/*
+================
+Con_Print
+
+Handles cursor positioning, line wrapping, etc
+All console printing must go through this in order to be logged to disk
+If no console is visible, the notify window will pop up.
+================
+*/
+void Con_Print (char *txt)
+{
+       int             y;
+       int             c, l;
+       static int      cr;
+       int             mask;
+       
+       con_backscroll = 0;
+
+       if (txt[0] == 1)
+       {
+               mask = 128;             // go to colored text
+               S_LocalSound ("misc/talk.wav");
+       // play talk wav
+               txt++;
+       }
+       else if (txt[0] == 2)
+       {
+               mask = 128;             // go to colored text
+               txt++;
+       }
+       else
+               mask = 0;
+
+
+       while ( (c = *txt) )
+       {
+       // count word length
+               for (l=0 ; l< con_linewidth ; l++)
+                       if ( txt[l] <= ' ')
+                               break;
+
+       // word wrap
+               if (l != con_linewidth && (con_x + l > con_linewidth) )
+                       con_x = 0;
+
+               txt++;
+
+               if (cr)
+               {
+                       con_current--;
+                       cr = false;
+               }
+
+               
+               if (!con_x)
+               {
+                       Con_Linefeed ();
+               // mark time for transparent overlay
+                       if (con_current >= 0)
+                               con_times[con_current % NUM_CON_TIMES] = realtime;
+               }
+
+               switch (c)
+               {
+               case '\n':
+                       con_x = 0;
+                       break;
+
+               case '\r':
+                       con_x = 0;
+                       cr = 1;
+                       break;
+
+               default:        // display character and advance
+                       y = con_current % con_totallines;
+                       con_text[y*con_linewidth+con_x] = c | mask;
+                       con_x++;
+                       if (con_x >= con_linewidth)
+                               con_x = 0;
+                       break;
+               }
+               
+       }
+}
+
+
+/*
+================
+Con_DebugLog
+================
+*/
+void Con_DebugLog(char *file, char *fmt, ...)
+{
+    va_list argptr; 
+    static char data[1024];
+    int fd;
+    
+    va_start(argptr, fmt);
+    vsprintf(data, fmt, argptr);
+    va_end(argptr);
+    fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
+    write(fd, data, strlen(data));
+    close(fd);
+}
+
+
+/*
+================
+Con_Printf
+
+Handles cursor positioning, line wrapping, etc
+================
+*/
+// LordHavoc: increased from 4096 to 16384
+#define        MAXPRINTMSG     16384
+// FIXME: make a buffer size safe vsprintf?
+void Con_Printf (char *fmt, ...)
+{
+       va_list         argptr;
+       char            msg[MAXPRINTMSG];
+       static qboolean inupdate;
+       
+       va_start (argptr,fmt);
+       vsprintf (msg,fmt,argptr);
+       va_end (argptr);
+       
+// also echo to debugging console
+       Sys_Printf ("%s", msg); // also echo to debugging console
+
+// log all messages to file
+       if (con_debuglog)
+               Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg);
+
+       if (!con_initialized)
+               return;
+               
+       if (cls.state == ca_dedicated)
+               return;         // no graphics mode
+
+// write it to the scrollable buffer
+       Con_Print (msg);
+       
+// update the screen if the console is displayed
+       // LordHavoc: don't print text while loading scripts
+       if (cls.state != ca_disconnected)
+       if (cls.signon != SIGNONS && !scr_disabled_for_loading )
+       {
+       // protect against infinite loop if something in SCR_UpdateScreen calls
+       // Con_Printf
+               if (!inupdate)
+               {
+                       inupdate = true;
+                       SCR_UpdateScreen ();
+                       inupdate = false;
+               }
+       }
+}
+
+/*
+================
+Con_DPrintf
+
+A Con_Printf that only shows up if the "developer" cvar is set
+================
+*/
+void Con_DPrintf (char *fmt, ...)
+{
+       va_list         argptr;
+       char            msg[MAXPRINTMSG];
+               
+       if (!developer.value)
+               return;                 // don't confuse non-developers with techie stuff...
+
+       va_start (argptr,fmt);
+       vsprintf (msg,fmt,argptr);
+       va_end (argptr);
+       
+       Con_Printf ("%s", msg);
+}
+
+
+/*
+==================
+Con_SafePrintf
+
+Okay to call even when the screen can't be updated
+==================
+*/
+void Con_SafePrintf (char *fmt, ...)
+{
+       va_list         argptr;
+       char            msg[1024];
+       int                     temp;
+               
+       va_start (argptr,fmt);
+       vsprintf (msg,fmt,argptr);
+       va_end (argptr);
+
+       temp = scr_disabled_for_loading;
+       scr_disabled_for_loading = true;
+       Con_Printf ("%s", msg);
+       scr_disabled_for_loading = temp;
+}
+
+
+/*
+==============================================================================
+
+DRAWING
+
+==============================================================================
+*/
+
+
+/*
+================
+Con_DrawInput
+
+The input line scrolls horizontally if typing goes beyond the right edge
+================
+*/
+void Con_DrawInput (void)
+{
+       int             y;
+       char    *text;
+
+       if (key_dest != key_console && !con_forcedup)
+               return;         // don't draw anything
+
+       text = key_lines[edit_line];
+       
+// add the cursor frame
+       text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
+       
+       text[key_linepos+1] = 0; // LordHavoc: null terminate, rather than padding with spaces
+// fill out remainder with spaces
+//     for (i=key_linepos+1 ; i< con_linewidth ; i++)
+//             text[i] = ' ';
+               
+//     prestep if horizontally scrolling
+       if (key_linepos >= con_linewidth)
+               text += 1 + key_linepos - con_linewidth;
+               
+// draw it
+       y = con_vislines-16;
+
+//     for (i=0 ; i<con_linewidth ; i++)
+//             Draw_Character ( (i+1)<<3, con_vislines - 16, text[i]);
+       // LordHavoc: speedup
+       Draw_String(8, con_vislines - 16, text, con_linewidth);
+
+// remove cursor
+       key_lines[edit_line][key_linepos] = 0;
+}
+
+
+/*
+================
+Con_DrawNotify
+
+Draws the last few lines of output transparently over the game top
+================
+*/
+void Con_DrawNotify (void)
+{
+       int             x, v;
+       char    *text;
+       int             i;
+       float   time;
+       extern char chat_buffer[];
+       char    temptext[256];
+
+       v = 0;
+       for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
+       {
+               if (i < 0)
+                       continue;
+               time = con_times[i % NUM_CON_TIMES];
+               if (time == 0)
+                       continue;
+               time = realtime - time;
+               if (time > con_notifytime.value)
+                       continue;
+               text = con_text + (i % con_totallines)*con_linewidth;
+               
+               clearnotify = 0;
+               scr_copytop = 1;
+
+//             for (x = 0 ; x < con_linewidth ; x++)
+//                     Draw_Character ( (x+1)<<3, v, text[x]);
+               // LordHavoc: speedup
+               Draw_String(8, v, text, con_linewidth);
+
+               v += 8;
+       }
+
+
+       if (key_dest == key_message)
+       {
+               clearnotify = 0;
+               scr_copytop = 1;
+       
+               x = 0;
+               
+               // LordHavoc: speedup, and other improvements
+               if (team_message)
+                       sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
+               else
+                       sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
+               while (strlen(temptext) >= con_linewidth)
+               {
+                       Draw_String (8, v, temptext, con_linewidth);
+                       strcpy(temptext, &temptext[con_linewidth]);
+                       v += 8;
+               }
+               if (strlen(temptext) > 0)
+               {
+                       Draw_String (8, v, temptext, 0);
+                       v += 8;
+               }
+//             Draw_String (8, v, "say:", 0);
+//             while(chat_buffer[x])
+//             {
+//                     Draw_Character ( (x+5)<<3, v, chat_buffer[x]);
+//                     x++;
+//             }
+//             Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
+//             v += 8;
+       }
+       
+       if (v > con_notifylines)
+               con_notifylines = v;
+}
+
+/*
+================
+Con_DrawConsole
+
+Draws the console with the solid background
+The typing input line at the bottom should only be drawn if typing is allowed
+================
+*/
+void Con_DrawConsole (int lines, qboolean drawinput)
+{
+       int                             i, y;
+       int                             rows;
+       char                    *text;
+       int                             j;
+       
+       if (lines <= 0)
+               return;
+
+// draw the background
+       Draw_ConsoleBackground (lines);
+
+// draw the text
+       con_vislines = lines;
+
+       rows = (lines-16)>>3;           // rows of text to draw
+       y = lines - 16 - (rows<<3);     // may start slightly negative
+
+       for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 )
+       {
+               j = i - con_backscroll;
+               if (j<0)
+                       j = 0;
+               text = con_text + (j % con_totallines)*con_linewidth;
+
+//             for (x=0 ; x<con_linewidth ; x++)
+//                     Draw_Character ( (x+1)<<3, y, text[x]);
+               // LordHavoc: speedup
+               Draw_String(8, y, text, con_linewidth);
+       }
+
+// draw the input prompt, user text, and cursor if desired
+       if (drawinput)
+               Con_DrawInput ();
+}
+
+
+/*
+==================
+Con_NotifyBox
+==================
+*/
+void Con_NotifyBox (char *text)
+{
+       double          t1, t2;
+
+// during startup for sound / cd warnings
+       Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
+
+       Con_Printf (text);
+
+       Con_Printf ("Press a key.\n");
+       Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
+
+       key_count = -2;         // wait for a key down and up
+       key_dest = key_console;
+
+       do
+       {
+               t1 = Sys_FloatTime ();
+               SCR_UpdateScreen ();
+               Sys_SendKeyEvents ();
+               t2 = Sys_FloatTime ();
+               realtime += t2-t1;              // make the cursor blink
+       } while (key_count < 0);
+
+       Con_Printf ("\n");
+       key_dest = key_game;
+       realtime = 0;                           // put the cursor back to invisible
+}
+
diff --git a/console.h b/console.h
new file mode 100644 (file)
index 0000000..ef01f00
--- /dev/null
+++ b/console.h
@@ -0,0 +1,46 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// console
+//
+extern int con_totallines;
+extern int con_backscroll;
+extern qboolean con_forcedup;  // because no entities to refresh
+extern qboolean con_initialized;
+extern byte *con_chars;
+extern int     con_notifylines;                // scan lines to clear for notify lines
+
+void Con_DrawCharacter (int cx, int line, int num);
+
+void Con_CheckResize (void);
+void Con_Init (void);
+void Con_DrawConsole (int lines, qboolean drawinput);
+void Con_Print (char *txt);
+void Con_Printf (char *fmt, ...);
+void Con_DPrintf (char *fmt, ...);
+void Con_SafePrintf (char *fmt, ...);
+void Con_Clear_f (void);
+void Con_DrawNotify (void);
+void Con_ClearNotify (void);
+void Con_ToggleConsole_f (void);
+
+void Con_NotifyBox (char *text);       // during startup for sound / cd warnings
+
diff --git a/crc.c b/crc.c
new file mode 100644 (file)
index 0000000..84dd855
--- /dev/null
+++ b/crc.c
@@ -0,0 +1,100 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+/* crc.c */
+
+#include "quakedef.h"
+#include "crc.h"
+
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below...  in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE 0xffff
+#define CRC_XOR_VALUE  0x0000
+
+static unsigned short crctable[256] =
+{
+       0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+       0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+       0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+       0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+       0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+       0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+       0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+       0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+       0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+       0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+       0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+       0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+       0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+       0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+       0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+       0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+       0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+       0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+       0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+       0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+       0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+       0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+       0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+       0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+       0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+       0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+       0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+       0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+       0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+       0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+       0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+       0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+void CRC_Init(unsigned short *crcvalue)
+{
+       *crcvalue = CRC_INIT_VALUE;
+}
+
+void CRC_ProcessByte(unsigned short *crcvalue, byte data)
+{
+       *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
+}
+
+// LordHavoc: added for speed reasons
+void CRC_ProcessBytes(unsigned short *crcvalue, byte *data, int size)
+{
+       unsigned short crc;
+       crc = *crcvalue;
+       while (size--)
+               crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
+       *crcvalue = crc;
+}
+
+unsigned short CRC_Value(unsigned short crcvalue)
+{
+       return crcvalue ^ CRC_XOR_VALUE;
+}
+
+// LordHavoc: further speed and usability improvement
+unsigned short CRC_Block(byte *data, int size)
+{
+       unsigned short crc = CRC_INIT_VALUE;
+       while (size--)
+               crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
+       return crc ^ CRC_XOR_VALUE;
+}
diff --git a/crc.h b/crc.h
new file mode 100644 (file)
index 0000000..c613ebc
--- /dev/null
+++ b/crc.h
@@ -0,0 +1,28 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+/* crc.h */
+
+void CRC_Init(unsigned short *crcvalue);
+void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+// LordHavoc: added this for speed reasons
+void CRC_ProcessBytes(unsigned short *crcvalue, byte *data, int size);
+unsigned short CRC_Value(unsigned short crcvalue);
+// LordHavoc: further speed and usability improvement
+unsigned short CRC_Block(byte *data, int size);
diff --git a/cvar.c b/cvar.c
new file mode 100644 (file)
index 0000000..48d2814
--- /dev/null
+++ b/cvar.c
@@ -0,0 +1,225 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cvar.c -- dynamic variable tracking
+
+#include "quakedef.h"
+
+cvar_t *cvar_vars;
+char   *cvar_null_string = "";
+
+/*
+============
+Cvar_FindVar
+============
+*/
+cvar_t *Cvar_FindVar (char *var_name)
+{
+       cvar_t  *var;
+       
+       for (var=cvar_vars ; var ; var=var->next)
+               if (!strcmp (var_name, var->name))
+                       return var;
+
+       return NULL;
+}
+
+/*
+============
+Cvar_VariableValue
+============
+*/
+float  Cvar_VariableValue (char *var_name)
+{
+       cvar_t  *var;
+       
+       var = Cvar_FindVar (var_name);
+       if (!var)
+               return 0;
+       return atof (var->string);
+}
+
+
+/*
+============
+Cvar_VariableString
+============
+*/
+char *Cvar_VariableString (char *var_name)
+{
+       cvar_t *var;
+       
+       var = Cvar_FindVar (var_name);
+       if (!var)
+               return cvar_null_string;
+       return var->string;
+}
+
+
+/*
+============
+Cvar_CompleteVariable
+============
+*/
+char *Cvar_CompleteVariable (char *partial)
+{
+       cvar_t          *cvar;
+       int                     len;
+       
+       len = strlen(partial);
+       
+       if (!len)
+               return NULL;
+               
+// check functions
+       for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
+               if (!strncmp (partial,cvar->name, len))
+                       return cvar->name;
+
+       return NULL;
+}
+
+
+/*
+============
+Cvar_Set
+============
+*/
+void Cvar_Set (char *var_name, char *value)
+{
+       cvar_t  *var;
+       qboolean changed;
+       
+       var = Cvar_FindVar (var_name);
+       if (!var)
+       {       // there is an error in C code if this happens
+               Con_Printf ("Cvar_Set: variable %s not found\n", var_name);
+               return;
+       }
+
+       changed = strcmp(var->string, value);
+       
+       Z_Free (var->string);   // free the old value string
+       
+       var->string = Z_Malloc (strlen(value)+1);
+       strcpy (var->string, value);
+       var->value = atof (var->string);
+       if (var->server && changed)
+       {
+               if (sv.active)
+                       SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string);
+       }
+}
+
+/*
+============
+Cvar_SetValue
+============
+*/
+void Cvar_SetValue (char *var_name, float value)
+{
+       char    val[32];
+       
+       // LordHavoc: changed from %f to %g to use shortest representation
+       sprintf (val, "%g",value);
+       Cvar_Set (var_name, val);
+}
+
+
+/*
+============
+Cvar_RegisterVariable
+
+Adds a freestanding variable to the variable list.
+============
+*/
+void Cvar_RegisterVariable (cvar_t *variable)
+{
+       char    *oldstr;
+       
+// first check to see if it has allready been defined
+       if (Cvar_FindVar (variable->name))
+       {
+               Con_Printf ("Can't register variable %s, allready defined\n", variable->name);
+               return;
+       }
+       
+// check for overlap with a command
+       if (Cmd_Exists (variable->name))
+       {
+               Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name);
+               return;
+       }
+               
+// copy the value off, because future sets will Z_Free it
+       oldstr = variable->string;
+       variable->string = Z_Malloc (strlen(variable->string)+1);       
+       strcpy (variable->string, oldstr);
+       variable->value = atof (variable->string);
+       
+// link the variable in
+       variable->next = cvar_vars;
+       cvar_vars = variable;
+}
+
+/*
+============
+Cvar_Command
+
+Handles variable inspection and changing from the console
+============
+*/
+qboolean       Cvar_Command (void)
+{
+       cvar_t                  *v;
+
+// check variables
+       v = Cvar_FindVar (Cmd_Argv(0));
+       if (!v)
+               return false;
+               
+// perform a variable print or set
+       if (Cmd_Argc() == 1)
+       {
+               Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string);
+               return true;
+       }
+
+       Cvar_Set (v->name, Cmd_Argv(1));
+       return true;
+}
+
+
+/*
+============
+Cvar_WriteVariables
+
+Writes lines containing "set variable value" for all variables
+with the archive flag set to true.
+============
+*/
+void Cvar_WriteVariables (FILE *f)
+{
+       cvar_t  *var;
+       
+       for (var = cvar_vars ; var ; var = var->next)
+               if (var->archive)
+                       fprintf (f, "%s \"%s\"\n", var->name, var->string);
+}
+
diff --git a/cvar.h b/cvar.h
new file mode 100644 (file)
index 0000000..009b747
--- /dev/null
+++ b/cvar.h
@@ -0,0 +1,97 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cvar.h
+
+/*
+
+cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly
+in C code.
+
+it is sufficient to initialize a cvar_t with just the first two fields, or
+you can add a ,true flag for variables that you want saved to the configuration
+file when the game is quit:
+
+cvar_t r_draworder = {"r_draworder","1"};
+cvar_t scr_screensize = {"screensize","1",true};
+
+Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string.  Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed:
+Cvar_RegisterVariable (&host_framerate);
+
+
+C code usually just references a cvar in place:
+if ( r_draworder.value )
+
+It could optionally ask for the value to be looked up for a string name:
+if (Cvar_VariableValue ("r_draworder"))
+
+Interpreted prog code can access cvars with the cvar(name) or
+cvar_set (name, value) internal functions:
+teamplay = cvar("teamplay");
+cvar_set ("registered", "1");
+
+The user can access cvars from the console in two ways:
+r_draworder                    prints the current value
+r_draworder 0          sets the current value to 0
+Cvars are restricted from having the same names as commands to keep this
+interface from being ambiguous.
+*/
+
+typedef struct cvar_s
+{
+       char    *name;
+       char    *string;
+       qboolean archive;               // set to true to cause it to be saved to vars.rc
+       qboolean server;                // notifies players when changed
+       float   value;
+       struct cvar_s *next;
+} cvar_t;
+
+void   Cvar_RegisterVariable (cvar_t *variable);
+// registers a cvar that allready has the name, string, and optionally the
+// archive elements set.
+
+void   Cvar_Set (char *var_name, char *value);
+// equivelant to "<name> <variable>" typed at the console
+
+void   Cvar_SetValue (char *var_name, float value);
+// expands value to a string and calls Cvar_Set
+
+float  Cvar_VariableValue (char *var_name);
+// returns 0 if not defined or non numeric
+
+char   *Cvar_VariableString (char *var_name);
+// returns an empty string if not defined
+
+char   *Cvar_CompleteVariable (char *partial);
+// attempts to match a partial variable name for command line completion
+// returns NULL if nothing fits
+
+qboolean Cvar_Command (void);
+// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known
+// command.  Returns true if the command was a variable reference that
+// was handled. (print or change)
+
+void   Cvar_WriteVariables (FILE *f);
+// Writes lines containing "set variable value" for all variables
+// with the archive flag set to true.
+
+cvar_t *Cvar_FindVar (char *var_name);
+
+extern cvar_t  *cvar_vars;
diff --git a/draw.h b/draw.h
new file mode 100644 (file)
index 0000000..db8a860
--- /dev/null
+++ b/draw.h
@@ -0,0 +1,35 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// draw.h -- these are the only functions outside the refresh allowed
+// to touch the vid buffer
+
+extern qpic_t          *draw_disc;     // also used on sbar
+
+void Draw_Init (void);
+void Draw_Character (int x, int y, int num);
+void Draw_Pic (int x, int y, qpic_t *pic);
+void Draw_TransPic (int x, int y, qpic_t *pic);
+void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation);
+void Draw_ConsoleBackground (int lines);
+void Draw_Fill (int x, int y, int w, int h, int c);
+void Draw_String (int x, int y, char *str, int maxlen); // LordHavoc: added maxlen
+qpic_t *Draw_PicFromWad (char *name);
+qpic_t *Draw_CachePic (char *path);
diff --git a/fractalnoise.c b/fractalnoise.c
new file mode 100644 (file)
index 0000000..b663027
--- /dev/null
@@ -0,0 +1,50 @@
+
+#include <stdlib.h>
+
+void fractalnoise(unsigned char *noise, int size)
+{
+       int x, y, g, g2, amplitude, amplitude2, min, max, size1 = size - 1;
+       int *noisebuf;
+#define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
+       noisebuf = calloc(size*size, sizeof(int));
+
+       amplitude = 32767;
+       g2 = size;
+       n(0,0) = 0;
+       for (;(g = g2 >> 1) >= 1;g2 >>= 1)
+       {
+               // subdivide, diamond-square algorythm (really this has little to do with squares)
+               // diamond
+               for (y = 0;y < size;y += g2)
+                       for (x = 0;x < size;x += g2)
+                               n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
+               // square
+               for (y = 0;y < size;y += g2)
+                       for (x = 0;x < size;x += g2)
+                       {
+                               n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
+                               n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
+                       }
+               // brownian motion theory
+               amplitude >>= 1;
+               amplitude2 = amplitude >> 1;
+               for (y = 0;y < size;y += g)
+                       for (x = 0;x < size;x += g)
+                               n(x,y) += (rand()&amplitude) - amplitude2;
+       }
+       // find range of noise values
+       min = max = 0;
+       for (y = 0;y < size;y++)
+               for (x = 0;x < size;x++)
+               {
+                       if (n(x,y) < min) min = n(x,y);
+                       if (n(x,y) > max) max = n(x,y);
+               }
+       max -= min;
+       // normalize noise and copy to output
+       for (y = 0;y < size;y++)
+               for (x = 0;x < size;x++)
+                       *noise++ = (n(x,y) - min) * 255 / max;
+       free(noisebuf);
+#undef n
+}
\ No newline at end of file
diff --git a/gl_draw.c b/gl_draw.c
new file mode 100644 (file)
index 0000000..61aaf7a
--- /dev/null
+++ b/gl_draw.c
@@ -0,0 +1,1413 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// draw.c -- this is the only file outside the refresh that touches the
+// vid buffer
+
+#include "quakedef.h"
+
+#define GL_COLOR_INDEX8_EXT     0x80E5
+
+extern unsigned char d_15to8table[65536];
+
+cvar_t         qsg_version = {"qsg_version", "1"};
+cvar_t         gl_max_size = {"gl_max_size", "1024"};
+cvar_t         gl_picmip = {"gl_picmip", "0"};
+cvar_t         gl_conalpha = {"gl_conalpha", "1"};
+cvar_t         gl_lerpimages = {"gl_lerpimages", "1"};
+
+byte           *draw_chars;                            // 8*8 graphic characters
+qpic_t         *draw_disc;
+
+int                    translate_texture;
+int                    char_texture;
+
+typedef struct
+{
+       int             texnum;
+       float   sl, tl, sh, th;
+} glpic_t;
+
+byte           conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)];
+qpic_t         *conback = (qpic_t *)&conback_buffer;
+
+int            gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
+int            gl_filter_max = GL_LINEAR;
+
+
+int            texels;
+
+typedef struct
+{
+       int             texnum;
+       char    identifier[64];
+       int             width, height;
+       qboolean        mipmap;
+// LordHavoc: 32bit textures
+       int             bytesperpixel;
+// LordHavoc: CRC to identify cache mismatchs
+       int             crc;
+       int             lerped; // whether this texture was uploaded with or without interpolation
+} gltexture_t;
+
+#define        MAX_GLTEXTURES  4096
+gltexture_t    gltextures[MAX_GLTEXTURES];
+int                    numgltextures;
+
+/*
+=============================================================================
+
+  scrap allocation
+
+  Allocate all the little status bar obejcts into a single texture
+  to crutch up stupid hardware / drivers
+
+=============================================================================
+*/
+
+#define        MAX_SCRAPS              2
+#define        BLOCK_WIDTH             256
+#define        BLOCK_HEIGHT    256
+
+int                    scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
+byte           scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4];
+qboolean       scrap_dirty;
+int                    scrap_texnum;
+
+// returns a texture number and the position inside it
+int Scrap_AllocBlock (int w, int h, int *x, int *y)
+{
+       int             i, j;
+       int             best, best2;
+       int             texnum;
+
+       for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
+       {
+               best = BLOCK_HEIGHT;
+
+               for (i=0 ; i<BLOCK_WIDTH-w ; i++)
+               {
+                       best2 = 0;
+
+                       for (j=0 ; j<w ; j++)
+                       {
+                               if (scrap_allocated[texnum][i+j] >= best)
+                                       break;
+                               if (scrap_allocated[texnum][i+j] > best2)
+                                       best2 = scrap_allocated[texnum][i+j];
+                       }
+                       if (j == w)
+                       {       // this is a valid spot
+                               *x = i;
+                               *y = best = best2;
+                       }
+               }
+
+               if (best + h > BLOCK_HEIGHT)
+                       continue;
+
+               for (i=0 ; i<w ; i++)
+                       scrap_allocated[texnum][*x + i] = best + h;
+
+               return texnum;
+       }
+
+       Sys_Error ("Scrap_AllocBlock: full");
+}
+
+int    scrap_uploads;
+
+void Scrap_Upload (void)
+{
+       int             texnum;
+
+       scrap_uploads++;
+
+       for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
+       {
+               glBindTexture(GL_TEXTURE_2D, scrap_texnum + texnum);
+               GL_Upload8 (scrap_texels[texnum], BLOCK_WIDTH, BLOCK_HEIGHT, false, true);
+       }
+       scrap_dirty = false;
+}
+
+//=============================================================================
+/* Support Routines */
+
+typedef struct cachepic_s
+{
+       char            name[MAX_QPATH];
+       qpic_t          pic;
+       byte            padding[32];    // for appended glpic
+} cachepic_t;
+
+#define        MAX_CACHED_PICS         128
+cachepic_t     menu_cachepics[MAX_CACHED_PICS];
+int                    menu_numcachepics;
+
+byte           menuplyr_pixels[4096];
+
+int            pic_texels;
+int            pic_count;
+
+int GL_LoadPicTexture (qpic_t *pic);
+
+qpic_t *Draw_PicFromWad (char *name)
+{
+       qpic_t  *p;
+       glpic_t *gl;
+
+       p = W_GetLumpName (name);
+       gl = (glpic_t *)p->data;
+
+       // load little ones into the scrap
+       if (p->width < 64 && p->height < 64)
+       {
+               int             x, y;
+               int             i, j, k;
+               int             texnum;
+
+               texnum = Scrap_AllocBlock (p->width, p->height, &x, &y);
+               scrap_dirty = true;
+               k = 0;
+               for (i=0 ; i<p->height ; i++)
+                       for (j=0 ; j<p->width ; j++, k++)
+                               scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k];
+               texnum += scrap_texnum;
+               gl->texnum = texnum;
+               gl->sl = (x+0.01)/(float)BLOCK_WIDTH;
+               gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH;
+               gl->tl = (y+0.01)/(float)BLOCK_WIDTH;
+               gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH;
+
+               pic_count++;
+               pic_texels += p->width*p->height;
+               // LordHavoc: LINEAR interpolation
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       }
+       else
+       {
+               gl->texnum = GL_LoadPicTexture (p);
+               gl->sl = 0;
+               gl->sh = 1;
+               gl->tl = 0;
+               gl->th = 1;
+               // LordHavoc: LINEAR interpolation
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //NEAREST);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //NEAREST);
+       }
+       return p;
+}
+
+
+/*
+================
+Draw_CachePic
+================
+*/
+qpic_t *Draw_CachePic (char *path)
+{
+       cachepic_t      *pic;
+       int                     i;
+       qpic_t          *dat;
+       glpic_t         *gl;
+
+       for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
+               if (!strcmp (path, pic->name))
+                       return &pic->pic;
+
+       if (menu_numcachepics == MAX_CACHED_PICS)
+               Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
+       menu_numcachepics++;
+       strcpy (pic->name, path);
+
+//
+// load the pic from disk
+//
+       dat = (qpic_t *)COM_LoadTempFile (path, false);
+       if (!dat)
+               Sys_Error ("Draw_CachePic: failed to load %s", path);
+       SwapPic (dat);
+
+       // HACK HACK HACK --- we need to keep the bytes for
+       // the translatable player picture just for the menu
+       // configuration dialog
+       if (!strcmp (path, "gfx/menuplyr.lmp"))
+               memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
+
+       pic->pic.width = dat->width;
+       pic->pic.height = dat->height;
+
+       gl = (glpic_t *)pic->pic.data;
+       gl->texnum = GL_LoadPicTexture (dat);
+       gl->sl = 0;
+       gl->sh = 1;
+       gl->tl = 0;
+       gl->th = 1;
+
+       return &pic->pic;
+}
+
+
+void Draw_CharToConback (int num, byte *dest)
+{
+       int             row, col;
+       byte    *source;
+       int             drawline;
+       int             x;
+
+       row = num>>4;
+       col = num&15;
+       source = draw_chars + (row<<10) + (col<<3);
+
+       drawline = 8;
+
+       while (drawline--)
+       {
+               for (x=0 ; x<8 ; x++)
+                       if (source[x] != 255)
+                               dest[x] = 0x60 + source[x];
+               source += 128;
+               dest += 320;
+       }
+
+}
+
+typedef struct
+{
+       char *name;
+       int     minimize, maximize;
+} glmode_t;
+
+glmode_t modes[] = {
+       {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
+       {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
+       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
+       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
+       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
+       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
+};
+
+/*
+===============
+Draw_TextureMode_f
+===============
+*/
+void Draw_TextureMode_f (void)
+{
+       int             i;
+       gltexture_t     *glt;
+
+       if (Cmd_Argc() == 1)
+       {
+               for (i=0 ; i< 6 ; i++)
+                       if (gl_filter_min == modes[i].minimize)
+                       {
+                               Con_Printf ("%s\n", modes[i].name);
+                               return;
+                       }
+               Con_Printf ("current filter is unknown???\n");
+               return;
+       }
+
+       for (i=0 ; i< 6 ; i++)
+       {
+               if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
+                       break;
+       }
+       if (i == 6)
+       {
+               Con_Printf ("bad filter name\n");
+               return;
+       }
+
+       gl_filter_min = modes[i].minimize;
+       gl_filter_max = modes[i].maximize;
+
+       // change all the existing mipmap texture objects
+       for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
+       {
+               if (glt->mipmap)
+               {
+                       glBindTexture(GL_TEXTURE_2D, glt->texnum);
+                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
+                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
+               }
+       }
+}
+
+extern void LoadSky_f(void);
+
+extern char *QSG_EXTENSIONS;
+
+/*
+===============
+Draw_Init
+===============
+*/
+void rmain_registercvars();
+void Draw_Init (void)
+{
+       int             i;
+       qpic_t  *cb;
+       byte    *dest;
+       int             x, y;
+       char    ver[40];
+       glpic_t *gl;
+       int             start;
+
+       Cvar_RegisterVariable (&qsg_version);
+       Cvar_RegisterVariable (&gl_max_size);
+       Cvar_RegisterVariable (&gl_picmip);
+       Cvar_RegisterVariable (&gl_conalpha);
+       Cvar_RegisterVariable (&gl_lerpimages);
+
+       // 3dfx can only handle 256 wide textures
+       if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) ||
+               strstr((char *)gl_renderer, "Glide"))
+               Cvar_Set ("gl_max_size", "256");
+
+       Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
+
+       Cmd_AddCommand ("loadsky", &LoadSky_f);
+
+       // load the console background and the charset
+       // by hand, because we need to write the version
+       // string into the background before turning
+       // it into a texture
+       draw_chars = W_GetLumpName ("conchars");
+       for (i=0 ; i<256*64 ; i++)
+               if (draw_chars[i] == 0)
+                       draw_chars[i] = 255;    // proper transparent color
+
+       // now turn them into textures
+       char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1);
+
+       start = Hunk_LowMark();
+
+       cb = (qpic_t *)COM_LoadTempFile ("gfx/conback.lmp", false);
+       if (!cb)
+               Sys_Error ("Couldn't load gfx/conback.lmp");
+       SwapPic (cb);
+
+       // hack the version number directly into the pic
+#ifdef NEHAHRA
+#if defined(__linux__)
+       sprintf (ver, "(DPNehahra %.2f, Linux %2.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)LINUX_VERSION, (float)GLQUAKE_VERSION, (float)VERSION);
+#else
+       sprintf (ver, "(DPNehahra %.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)GLQUAKE_VERSION, (float)VERSION);
+#endif
+#else
+#if defined(__linux__)
+       sprintf (ver, "(DarkPlaces %.2f, Linux %2.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)LINUX_VERSION, (float)GLQUAKE_VERSION, (float)VERSION);
+#else
+       sprintf (ver, "(DarkPlaces %.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)GLQUAKE_VERSION, (float)VERSION);
+#endif
+#endif
+       dest = cb->data + 320*186 + 320 - 11 - 8*strlen(ver);
+       y = strlen(ver);
+       for (x=0 ; x<y ; x++)
+               Draw_CharToConback (ver[x], dest+(x<<3));
+
+       gl = (glpic_t *)conback->data;
+       gl->texnum = GL_LoadTexture ("conback", cb->width, cb->height, cb->data, false, false, 1);
+       gl->sl = 0;
+       gl->sh = 1;
+       gl->tl = 0;
+       gl->th = 1;
+       conback->width = vid.width;
+       conback->height = vid.height;
+
+       // free loaded console
+       Hunk_FreeToLowMark(start);
+
+       // save a texture slot for translated picture
+       translate_texture = texture_extension_number++;
+
+       // save slots for scraps
+       scrap_texnum = texture_extension_number;
+       texture_extension_number += MAX_SCRAPS;
+
+       //
+       // get the other pics we need
+       //
+       draw_disc = Draw_PicFromWad ("disc");
+
+       rmain_registercvars();
+}
+
+
+/*
+================
+Draw_Character
+
+Draws one 8*8 graphics character with 0 being transparent.
+It can be clipped to the top of the screen to allow the console to be
+smoothly scrolled off.
+================
+*/
+void Draw_Character (int x, int y, int num)
+{
+       int                             row, col;
+       float                   frow, fcol, size;
+
+       if (num == 32)
+               return;         // space
+
+       num &= 255;
+       
+       if (y <= -8)
+               return;                 // totally off screen
+
+       row = num>>4;
+       col = num&15;
+
+       frow = row*0.0625;
+       fcol = col*0.0625;
+       size = 0.0625;
+
+       glBindTexture(GL_TEXTURE_2D, char_texture);
+       // LordHavoc: NEAREST mode on text if not scaling up
+       if ((int) vid.width < glwidth)
+       {
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       }
+
+       glColor3f(1,1,1);
+       glBegin (GL_QUADS);
+       glTexCoord2f (fcol, frow);
+       glVertex2f (x, y);
+       glTexCoord2f (fcol + size, frow);
+       glVertex2f (x+8, y);
+       glTexCoord2f (fcol + size, frow + size);
+       glVertex2f (x+8, y+8);
+       glTexCoord2f (fcol, frow + size);
+       glVertex2f (x, y+8);
+       glEnd ();
+
+       // LordHavoc: revert to LINEAR mode
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+/*
+================
+Draw_String
+================
+*/
+// LordHavoc: sped this up a lot, and added maxlen
+void Draw_String (int x, int y, char *str, int maxlen)
+{
+       int num;
+       float frow, fcol;
+       if (y <= -8 || y >= (int) vid.height || x >= (int) vid.width || *str == 0) // completely offscreen or no text to print
+               return;
+       if (maxlen < 1)
+               maxlen = strlen(str);
+       else if (maxlen > (int) strlen(str))
+               maxlen = strlen(str);
+       glBindTexture(GL_TEXTURE_2D, char_texture);
+
+       // LordHavoc: NEAREST mode on text if not scaling up
+       if ((int) vid.width < glwidth)
+       {
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       }
+
+       glColor3f(1,1,1);
+       glBegin (GL_QUADS);
+       while (maxlen-- && x < (int) vid.width) // stop rendering when out of characters or room
+       {
+               if ((num = *str++) != 32) // skip spaces
+               {
+                       frow = (float) ((int) num >> 4)*0.0625;
+                       fcol = (float) ((int) num & 15)*0.0625;
+                       glTexCoord2f (fcol, frow);
+                       glVertex2f (x, y);
+                       glTexCoord2f (fcol + 0.0625, frow);
+                       glVertex2f (x+8, y);
+                       glTexCoord2f (fcol + 0.0625, frow + 0.0625);
+                       glVertex2f (x+8, y+8);
+                       glTexCoord2f (fcol, frow + 0.0625);
+                       glVertex2f (x, y+8);
+               }
+               x += 8;
+       }
+       glEnd ();
+
+       // LordHavoc: revert to LINEAR mode
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+/*
+=============
+Draw_AlphaPic
+=============
+*/
+void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
+{
+       glpic_t                 *gl;
+
+       if (scrap_dirty)
+               Scrap_Upload ();
+       gl = (glpic_t *)pic->data;
+//     glDisable(GL_ALPHA_TEST);
+//     glEnable (GL_BLEND);
+//     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+//     glCullFace(GL_FRONT);
+       glColor4f(0.8,0.8,0.8,alpha);
+       glBindTexture(GL_TEXTURE_2D, gl->texnum);
+//     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glBegin (GL_QUADS);
+       glTexCoord2f (gl->sl, gl->tl);
+       glVertex2f (x, y);
+       glTexCoord2f (gl->sh, gl->tl);
+       glVertex2f (x+pic->width, y);
+       glTexCoord2f (gl->sh, gl->th);
+       glVertex2f (x+pic->width, y+pic->height);
+       glTexCoord2f (gl->sl, gl->th);
+       glVertex2f (x, y+pic->height);
+       glEnd ();
+       glColor3f(1,1,1);
+//     glEnable(GL_ALPHA_TEST);
+//     glDisable (GL_BLEND);
+}
+
+
+/*
+=============
+Draw_Pic
+=============
+*/
+void Draw_Pic (int x, int y, qpic_t *pic)
+{
+       glpic_t                 *gl;
+
+       if (scrap_dirty)
+               Scrap_Upload ();
+       gl = (glpic_t *)pic->data;
+       glColor3f(0.8,0.8,0.8);
+       glBindTexture(GL_TEXTURE_2D, gl->texnum);
+       glBegin (GL_QUADS);
+       glTexCoord2f (gl->sl, gl->tl);
+       glVertex2f (x, y);
+       glTexCoord2f (gl->sh, gl->tl);
+       glVertex2f (x+pic->width, y);
+       glTexCoord2f (gl->sh, gl->th);
+       glVertex2f (x+pic->width, y+pic->height);
+       glTexCoord2f (gl->sl, gl->th);
+       glVertex2f (x, y+pic->height);
+       glEnd ();
+}
+
+
+/*
+=============
+Draw_TransPic
+=============
+*/
+void Draw_TransPic (int x, int y, qpic_t *pic)
+{
+       if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || (unsigned)(y + pic->height) > vid.height)
+               Sys_Error ("Draw_TransPic: bad coordinates");
+
+//     glEnable(GL_BLEND);
+//     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+//     glDisable(GL_ALPHA_TEST);
+       Draw_Pic (x, y, pic);
+//     glDisable(GL_BLEND);
+}
+
+
+/*
+=============
+Draw_TransPicTranslate
+
+Only used for the player color selection menu
+=============
+*/
+void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation)
+{
+       int                             v, u, c;
+       unsigned                trans[64*64], *dest;
+       byte                    *src;
+       int                             p;
+
+       glBindTexture(GL_TEXTURE_2D, translate_texture);
+
+       c = pic->width * pic->height;
+
+       dest = trans;
+       for (v=0 ; v<64 ; v++, dest += 64)
+       {
+               src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width];
+               for (u=0 ; u<64 ; u++)
+               {
+                       p = src[(u*pic->width)>>6];
+                       if (p == 255)
+                               dest[u] = p;
+                       else
+                               dest[u] =  d_8to24table[translation[p]];
+               }
+       }
+
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       glColor3f(0.8,0.8,0.8);
+       glBegin (GL_QUADS);
+       glTexCoord2f (0, 0);
+       glVertex2f (x, y);
+       glTexCoord2f (1, 0);
+       glVertex2f (x+pic->width, y);
+       glTexCoord2f (1, 1);
+       glVertex2f (x+pic->width, y+pic->height);
+       glTexCoord2f (0, 1);
+       glVertex2f (x, y+pic->height);
+       glEnd ();
+}
+
+
+/*
+================
+Draw_ConsoleBackground
+
+================
+*/
+void Draw_ConsoleBackground (int lines)
+{
+       // LordHavoc: changed alpha
+       //int y = (vid.height >> 1);
+
+       if (lines >= (int) vid.height)
+               Draw_Pic(0, lines - vid.height, conback);
+       else
+               Draw_AlphaPic (0, lines - vid.height, conback, gl_conalpha.value*lines/vid.height);
+       //      Draw_AlphaPic (0, lines - vid.height, conback, (float)(1.2 * lines)/y);
+}
+
+/*
+=============
+Draw_Fill
+
+Fills a box of pixels with a single color
+=============
+*/
+void Draw_Fill (int x, int y, int w, int h, int c)
+{
+       glDisable (GL_TEXTURE_2D);
+       glColor3f (host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0);
+
+       glBegin (GL_QUADS);
+
+       glVertex2f (x,y);
+       glVertex2f (x+w, y);
+       glVertex2f (x+w, y+h);
+       glVertex2f (x, y+h);
+
+       glEnd ();
+       glColor3f(1,1,1);
+       glEnable (GL_TEXTURE_2D);
+}
+//=============================================================================
+
+//=============================================================================
+
+/*
+================
+GL_Set2D
+
+Setup as if the screen was 320*200
+================
+*/
+void GL_Set2D (void)
+{
+       glViewport (glx, gly, glwidth, glheight);
+
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+       glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
+
+       glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity ();
+
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       glEnable (GL_BLEND); // was Disable
+//     glEnable (GL_ALPHA_TEST);
+       glDisable (GL_ALPHA_TEST);
+       glEnable(GL_TEXTURE_2D);
+
+       // LordHavoc: added this
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       glColor3f(1,1,1);
+}
+
+// LordHavoc: SHOWLMP stuff
+#define SHOWLMP_MAXLABELS 256
+typedef struct showlmp_s
+{
+       qboolean        isactive;
+       float           x;
+       float           y;
+       char            label[32];
+       char            pic[128];
+} showlmp_t;
+
+showlmp_t showlmp[SHOWLMP_MAXLABELS];
+
+void SHOWLMP_decodehide()
+{
+       int i;
+       byte *lmplabel;
+       lmplabel = MSG_ReadString();
+       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
+               if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
+               {
+                       showlmp[i].isactive = false;
+                       return;
+               }
+}
+
+void SHOWLMP_decodeshow()
+{
+       int i, k;
+       byte lmplabel[256], picname[256];
+       float x, y;
+       strcpy(lmplabel,MSG_ReadString());
+       strcpy(picname, MSG_ReadString());
+       x = MSG_ReadByte();
+       y = MSG_ReadByte();
+       k = -1;
+       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
+               if (showlmp[i].isactive)
+               {
+                       if (strcmp(showlmp[i].label, lmplabel) == 0)
+                       {
+                               k = i;
+                               break; // drop out to replace it
+                       }
+               }
+               else if (k < 0) // find first empty one to replace
+                       k = i;
+       if (k < 0)
+               return; // none found to replace
+       // change existing one
+       showlmp[k].isactive = true;
+       strcpy(showlmp[k].label, lmplabel);
+       strcpy(showlmp[k].pic, picname);
+       showlmp[k].x = x;
+       showlmp[k].y = y;
+}
+
+void SHOWLMP_drawall()
+{
+       int i;
+       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
+               if (showlmp[i].isactive)
+                       Draw_TransPic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic));
+}
+
+void SHOWLMP_clear()
+{
+       int i;
+       for (i = 0;i < SHOWLMP_MAXLABELS;i++)
+               showlmp[i].isactive = false;
+}
+
+//====================================================================
+
+/*
+================
+GL_FindTexture
+================
+*/
+int GL_FindTexture (char *identifier)
+{
+       int             i;
+       gltexture_t     *glt;
+
+       for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
+       {
+               if (!strcmp (identifier, glt->identifier))
+                       return gltextures[i].texnum;
+       }
+
+       return -1;
+}
+
+extern byte gamma[];
+
+// LordHavoc: gamma correction and improved resampling
+void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
+{
+       int             j, xi, oldx = 0;
+       float   f, fstep, l1, l2;
+       fstep = (float) inwidth/outwidth;
+       for (j = 0,f = 0;j < outwidth;j++, f += fstep)
+       {
+               xi = (int) f;
+               if (xi != oldx)
+               {
+                       in += (xi - oldx) * 4;
+                       oldx = xi;
+               }
+               if (xi < (inwidth-1))
+               {
+                       l2 = f - xi;
+                       l1 = 1 - l2;
+                       *out++ = gamma[(byte) (in[0] * l1 + in[4] * l2)];
+                       *out++ = gamma[(byte) (in[1] * l1 + in[5] * l2)];
+                       *out++ = gamma[(byte) (in[2] * l1 + in[6] * l2)];
+                       *out++ =       (byte) (in[3] * l1 + in[7] * l2) ;
+               }
+               else // last pixel of the line has no pixel to lerp to
+               {
+                       *out++ = gamma[in[0]];
+                       *out++ = gamma[in[1]];
+                       *out++ = gamma[in[2]];
+                       *out++ =       in[3] ;
+               }
+       }
+}
+
+/*
+================
+GL_ResampleTexture
+================
+*/
+void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata,  int outwidth, int outheight)
+{
+       // LordHavoc: gamma correction and greatly improved resampling
+       if (gl_lerpimages.value)
+       {
+               int             i, j, yi, oldy;
+               byte    *inrow, *out, *row1, *row2;
+               float   f, fstep, l1, l2;
+               out = outdata;
+               fstep = (float) inheight/outheight;
+
+               row1 = malloc(outwidth*4);
+               row2 = malloc(outwidth*4);
+               inrow = indata;
+               oldy = 0;
+               GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
+               GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
+               for (i = 0, f = 0;i < outheight;i++,f += fstep)
+               {
+                       yi = (int) f;
+                       if (yi != oldy)
+                       {
+                               inrow = (byte *)((int)indata + inwidth*4*yi);
+                               if (yi == oldy+1)
+                                       memcpy(row1, row2, outwidth*4);
+                               else
+                                       GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
+                               if (yi < (inheight-1))
+                                       GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
+                               else
+                                       memcpy(row2, row1, outwidth*4);
+                               oldy = yi;
+                       }
+                       if (yi < (inheight-1))
+                       {
+                               l2 = f - yi;
+                               l1 = 1 - l2;
+                               for (j = 0;j < outwidth;j++)
+                               {
+                                       *out++ = (byte) (*row1++ * l1 + *row2++ * l2);
+                                       *out++ = (byte) (*row1++ * l1 + *row2++ * l2);
+                                       *out++ = (byte) (*row1++ * l1 + *row2++ * l2);
+                                       *out++ = (byte) (*row1++ * l1 + *row2++ * l2);
+                               }
+                               row1 -= outwidth*4;
+                               row2 -= outwidth*4;
+                       }
+                       else // last line has no pixels to lerp to
+                       {
+                               for (j = 0;j < outwidth;j++)
+                               {
+                                       *out++ = *row1++;
+                                       *out++ = *row1++;
+                                       *out++ = *row1++;
+                                       *out++ = *row1++;
+                               }
+                               row1 -= outwidth*4;
+                       }
+               }
+               free(row1);
+               free(row2);
+       }
+       else
+       {
+               int             i, j;
+               unsigned        frac, fracstep;
+               byte    *inrow, *out, *inpix;
+               out = outdata;
+
+               fracstep = inwidth*0x10000/outwidth;
+               for (i=0 ; i<outheight ; i++)
+               {
+                       inrow = (byte *)indata + inwidth*(i*inheight/outheight)*4;
+                       frac = fracstep >> 1;
+                       for (j=0 ; j<outwidth ; j+=4)
+                       {
+                               inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
+                               inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
+                               inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
+                               inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
+                       }
+               }
+       }
+}
+
+/*
+================
+GL_Resample8BitTexture -- JACK
+================
+*/
+void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out,  int outwidth, int outheight)
+{
+       int             i, j;
+       unsigned        char *inrow;
+       unsigned        frac, fracstep;
+
+       fracstep = inwidth*0x10000/outwidth;
+       for (i=0 ; i<outheight ; i++, out += outwidth)
+       {
+               inrow = in + inwidth*(i*inheight/outheight);
+               frac = fracstep >> 1;
+               for (j=0 ; j<outwidth ; j+=4)
+               {
+                       out[j] = inrow[frac>>16];
+                       frac += fracstep;
+                       out[j+1] = inrow[frac>>16];
+                       frac += fracstep;
+                       out[j+2] = inrow[frac>>16];
+                       frac += fracstep;
+                       out[j+3] = inrow[frac>>16];
+                       frac += fracstep;
+               }
+       }
+}
+
+
+/*
+================
+GL_MipMap
+
+Operates in place, quartering the size of the texture
+================
+*/
+void GL_MipMap (byte *in, int width, int height)
+{
+       int             i, j;
+       byte    *out;
+
+       width <<=2;
+       height >>= 1;
+       out = in;
+       for (i=0 ; i<height ; i++, in+=width)
+       {
+               for (j=0 ; j<width ; j+=8, out+=4, in+=8)
+               {
+                       out[0] = (in[0] + in[4] + in[width+0] + in[width+4])>>2;
+                       out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2;
+                       out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2;
+                       out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2;
+               }
+       }
+}
+
+/*
+================
+GL_MipMap8Bit
+
+Mipping for 8 bit textures
+================
+*/
+void GL_MipMap8Bit (byte *in, int width, int height)
+{
+       int             i, j;
+       unsigned short     r,g,b;
+       byte    *out, *at1, *at2, *at3, *at4;
+
+       height >>= 1;
+       out = in;
+       for (i=0 ; i<height ; i++, in+=width)
+       {
+               for (j=0 ; j<width ; j+=2, out+=1, in+=2)
+               {
+                       at1 = (byte *) (d_8to24table + in[0]);
+                       at2 = (byte *) (d_8to24table + in[1]);
+                       at3 = (byte *) (d_8to24table + in[width+0]);
+                       at4 = (byte *) (d_8to24table + in[width+1]);
+
+                       r = (at1[0]+at2[0]+at3[0]+at4[0]); r>>=5;
+                       g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5;
+                       b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5;
+
+                       out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)];
+               }
+       }
+}
+
+/*
+===============
+GL_Upload32
+===============
+*/
+void GL_Upload32 (void *data, int width, int height,  qboolean mipmap, qboolean alpha)
+{
+       int samples, scaled_width, scaled_height, i;
+       byte *in, *out, *scaled;
+
+       for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
+               ;
+       for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
+               ;
+
+       scaled_width >>= (int)gl_picmip.value;
+       scaled_height >>= (int)gl_picmip.value;
+
+       if (scaled_width > gl_max_size.value)
+               scaled_width = gl_max_size.value;
+       if (scaled_height > gl_max_size.value)
+               scaled_height = gl_max_size.value;
+
+       samples = alpha ? gl_alpha_format : gl_solid_format;
+
+#if 0
+       if (mipmap)
+               gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
+       else if (scaled_width == width && scaled_height == height)
+               glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
+       else
+       {
+               gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, scaled, scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled);
+               glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
+       }
+#else
+texels += scaled_width * scaled_height;
+
+       scaled = malloc(scaled_width*scaled_height*4);
+       if (scaled_width == width && scaled_height == height)
+       {
+               // LordHavoc: gamma correct while copying
+               in = (byte *)data;
+               out = (byte *)scaled;
+               for (i = 0;i < width*height;i++)
+               {
+                       *out++ = gamma[*in++];
+                       *out++ = gamma[*in++];
+                       *out++ = gamma[*in++];
+                       *out++ = *in++;
+               }
+       }
+       else
+               GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
+
+       glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
+       if (mipmap)
+       {
+               int             miplevel;
+
+               miplevel = 0;
+               while (scaled_width > 1 || scaled_height > 1)
+               {
+                       GL_MipMap ((byte *)scaled, scaled_width, scaled_height);
+                       scaled_width >>= 1;
+                       scaled_height >>= 1;
+                       if (scaled_width < 1)
+                               scaled_width = 1;
+                       if (scaled_height < 1)
+                               scaled_height = 1;
+                       miplevel++;
+                       glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
+               }
+       }
+#endif
+
+
+       if (mipmap)
+       {
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
+       }
+       else
+       {
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
+       }
+       free(scaled);
+}
+
+void GL_Upload8_EXT (byte *data, int width, int height,  qboolean mipmap)
+{
+       int             scaled_width, scaled_height;
+       byte    *scaled;
+
+       for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
+               ;
+       for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
+               ;
+
+       scaled_width >>= (int)gl_picmip.value;
+       scaled_height >>= (int)gl_picmip.value;
+
+       if (scaled_width > gl_max_size.value)
+               scaled_width = gl_max_size.value;
+       if (scaled_height > gl_max_size.value)
+               scaled_height = gl_max_size.value;
+
+       texels += scaled_width * scaled_height;
+
+       if (scaled_width == width && scaled_height == height)
+       {
+               if (!mipmap)
+               {
+                       glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data);
+                       goto done;
+               }
+               scaled = malloc(scaled_width*scaled_height*4);
+               memcpy (scaled, data, width*height);
+       }
+       else
+       {
+               scaled = malloc(scaled_width*scaled_height*4);
+               GL_Resample8BitTexture (data, width, height, (void*) &scaled, scaled_width, scaled_height);
+       }
+
+       glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
+       if (mipmap)
+       {
+               int             miplevel;
+
+               miplevel = 0;
+               while (scaled_width > 1 || scaled_height > 1)
+               {
+                       GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height);
+                       scaled_width >>= 1;
+                       scaled_height >>= 1;
+                       if (scaled_width < 1)
+                               scaled_width = 1;
+                       if (scaled_height < 1)
+                               scaled_height = 1;
+                       miplevel++;
+                       glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
+               }
+       }
+done: ;
+
+
+       if (mipmap)
+       {
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
+       }
+       else
+       {
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
+       }
+       free(scaled);
+}
+
+qboolean VID_Is8bit();
+
+/*
+===============
+GL_Upload8
+===============
+*/
+void GL_Upload8 (byte *data, int width, int height,  qboolean mipmap, qboolean alpha)
+{
+       static  unsigned *trans;
+       int                     i, s;
+       qboolean        noalpha;
+       int                     p;
+       byte    *indata;
+       int             *outdata;
+
+       s = width*height;
+       trans = malloc(s*4);
+       // if there are no transparent pixels, make it a 3 component
+       // texture even if it was specified as otherwise
+       if (alpha)
+       {
+               noalpha = true;
+               for (i=0 ; i<s ; i++)
+               {
+                       p = data[i];
+                       if (p != 255)
+                               trans[i] = d_8to24table[p];
+                       else
+                       {
+                               trans[i] = 0; // force to black
+                               noalpha = false;
+                       }
+               }
+
+               if (noalpha)
+               {
+                       if (VID_Is8bit() && (data!=scrap_texels[0]))
+                       {
+                               GL_Upload8_EXT (data, width, height, mipmap);
+                               free(trans);
+                               return;
+                       }
+                       alpha = false;
+               }
+       }
+       else
+       {
+               // LordHavoc: dodge the copy if it will be uploaded as 8bit
+               if (VID_Is8bit() && (data!=scrap_texels[0]))
+               {
+                       GL_Upload8_EXT (data, width, height, mipmap);
+                       free(trans);
+                       return;
+               }
+               //if (s&3)
+               //      Sys_Error ("GL_Upload8: s&3");
+               indata = data;
+               outdata = trans;
+               if (s&1)
+                       *outdata++ = d_8to24table[*indata++];
+               if (s&2)
+               {
+                       *outdata++ = d_8to24table[*indata++];
+                       *outdata++ = d_8to24table[*indata++];
+               }
+               for (i = 0;i < s;i+=4)
+               {
+                       *outdata++ = d_8to24table[*indata++];
+                       *outdata++ = d_8to24table[*indata++];
+                       *outdata++ = d_8to24table[*indata++];
+                       *outdata++ = d_8to24table[*indata++];
+               }
+       }
+
+       GL_Upload32 (trans, width, height, mipmap, alpha);
+       free(trans);
+}
+
+/*
+================
+GL_LoadTexture
+================
+*/
+int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel)
+{
+       unsigned short  crc;
+       int                             i;
+       gltexture_t             *glt;
+
+       if (isDedicated)
+               return 1;
+
+       // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
+       crc = CRC_Block(data, width*height*bytesperpixel);
+       // see if the texture is already present
+       if (identifier[0])
+       {
+               for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
+               {
+                       if (!strcmp (identifier, glt->identifier))
+                       {
+                               // LordHavoc: everyone hates cache mismatchs, so I fixed it
+                               if (crc != glt->crc || width != glt->width || height != glt->height)
+                               {
+                                       Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
+                                       goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
+                                       //Sys_Error ("GL_LoadTexture: cache mismatch");
+                               }
+                               if ((gl_lerpimages.value != 0) != glt->lerped)
+                                       goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
+                               return glt->texnum;
+                       }
+               }
+       }
+       // LordHavoc: although this could be an else condition as it was in the original id code,
+       //            it is more clear this way
+       // LordHavoc: check if there are still slots available
+       if (numgltextures >= MAX_GLTEXTURES)
+               Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
+       glt = &gltextures[numgltextures++];
+
+       strcpy (glt->identifier, identifier);
+       glt->texnum = texture_extension_number;
+       texture_extension_number++;
+// LordHavoc: label to drop out of the loop into the setup code
+GL_LoadTexture_setup:
+       glt->crc = crc; // LordHavoc: used to verify textures are identical
+       glt->width = width;
+       glt->height = height;
+       glt->mipmap = mipmap;
+       glt->bytesperpixel = bytesperpixel;
+       glt->lerped = gl_lerpimages.value != 0;
+
+       glBindTexture(GL_TEXTURE_2D, glt->texnum);
+
+       if (bytesperpixel == 1) // 8bit
+               GL_Upload8 (data, width, height, mipmap, alpha);
+       else // 32bit
+               GL_Upload32 (data, width, height, mipmap, true);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       return glt->texnum;
+}
+
+/*
+================
+GL_LoadPicTexture
+================
+*/
+int GL_LoadPicTexture (qpic_t *pic)
+{
+       return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true, 1);
+}
+
+int GL_GetTextureSlots (int count)
+{
+       gltexture_t             *glt, *first;
+
+       first = glt = &gltextures[numgltextures];
+       while (count--)
+       {
+               glt->identifier[0] = 0;
+               glt->texnum = texture_extension_number++;
+               glt->crc = 0;
+               glt->width = 0;
+               glt->height = 0;
+               glt->bytesperpixel = 0;
+               glt++;
+               numgltextures++;
+       }
+       return first->texnum;
+}
diff --git a/gl_poly.c b/gl_poly.c
new file mode 100644 (file)
index 0000000..78b6f5c
--- /dev/null
+++ b/gl_poly.c
@@ -0,0 +1,667 @@
+#include "quakedef.h"
+
+transvert_t *transvert;
+transpoly_t *transpoly;
+unsigned short *transpolyindex;
+wallvert_t *wallvert;
+wallpoly_t *wallpoly;
+skyvert_t *skyvert;
+skypoly_t *skypoly;
+
+unsigned short currenttranspoly;
+unsigned short currenttransvert;
+unsigned short currentwallpoly;
+unsigned short currentwallvert;
+unsigned short currentskypoly;
+unsigned short currentskyvert;
+
+cvar_t gl_multitexture = {"gl_multitexture", "1"};
+cvar_t gl_vertexarrays = {"gl_vertexarrays", "1"};
+
+void glpoly_init()
+{
+       Cvar_RegisterVariable (&gl_multitexture);
+       Cvar_RegisterVariable (&gl_vertexarrays);
+       transvert = malloc(MAX_TRANSVERTS * sizeof(transvert_t));
+       transpoly = malloc(MAX_TRANSPOLYS * sizeof(transpoly_t));
+       transpolyindex = malloc(MAX_TRANSPOLYS * sizeof(unsigned short));
+       wallvert = malloc(MAX_WALLVERTS * sizeof(wallvert_t));
+       wallpoly = malloc(MAX_WALLPOLYS * sizeof(wallpoly_t));
+       skyvert = malloc(MAX_SKYVERTS * sizeof(skyvert_t));
+       skypoly = malloc(MAX_SKYPOLYS * sizeof(skypoly_t));
+}
+
+void transpolyclear()
+{
+       currenttranspoly = currenttransvert = 0;
+}
+
+void transpolybegin(int texnum, int glowtexnum, int fogtexnum, int transpolytype)
+{
+       if (currenttranspoly >= MAX_TRANSPOLYS || currenttransvert >= MAX_TRANSVERTS)
+               return;
+       transpoly[currenttranspoly].texnum = (unsigned short) texnum;
+       transpoly[currenttranspoly].glowtexnum = (unsigned short) glowtexnum;
+       transpoly[currenttranspoly].fogtexnum = (unsigned short) fogtexnum;
+       transpoly[currenttranspoly].transpolytype = (unsigned short) transpolytype;
+       transpoly[currenttranspoly].firstvert = currenttransvert;
+       transpoly[currenttranspoly].verts = 0;
+//     transpoly[currenttranspoly].ndist = 0; // clear the normal
+}
+
+// turned into a #define
+/*
+void transpolyvert(float x, float y, float z, float s, float t, int r, int g, int b, int a)
+{
+       int i;
+       if (currenttranspoly >= MAX_TRANSPOLYS || currenttransvert >= MAX_TRANSVERTS)
+               return;
+       transvert[currenttransvert].s = s;
+       transvert[currenttransvert].t = t;
+       transvert[currenttransvert].r = bound(0, r, 255);
+       transvert[currenttransvert].g = bound(0, g, 255);
+       transvert[currenttransvert].b = bound(0, b, 255);
+       transvert[currenttransvert].a = bound(0, a, 255);
+       transvert[currenttransvert].v[0] = x;
+       transvert[currenttransvert].v[1] = y;
+       transvert[currenttransvert].v[2] = z;
+       currenttransvert++;
+       transpoly[currenttranspoly].verts++;
+}
+*/
+
+void transpolyend()
+{
+       if (currenttranspoly >= MAX_TRANSPOLYS)
+               return;
+       if (transpoly[currenttranspoly].verts < 3) // skip invalid polygons
+       {
+               currenttransvert = transpoly[currenttranspoly].firstvert; // reset vert pointer
+               return;
+       }
+       if (currenttransvert >= MAX_TRANSVERTS)
+               return;
+       currenttranspoly++;
+}
+
+int transpolyindices;
+void transpolyrenderminmax()
+{
+       int i, j, k, lastvert;
+       vec_t d, min, max, viewdist, s, average;
+       //vec_t ndist;
+       //vec3_t v1, v2, n;
+       transpolyindices = 0;
+       viewdist = DotProduct(r_refdef.vieworg, vpn);
+       for (i = 0;i < currenttranspoly;i++)
+       {
+               if (transpoly[i].verts < 3) // only process valid polygons
+                       continue;
+               min = 1000000;max = -1000000;
+               s = 1.0f / transpoly[i].verts;
+               lastvert = transpoly[i].firstvert + transpoly[i].verts;
+               average = 0;
+               for (j = transpoly[i].firstvert;j < lastvert;j++)
+               {
+                       d = DotProduct(transvert[j].v, vpn)-viewdist;
+                       if (d < min) min = d;
+                       if (d > max) max = d;
+                       average += d * s;
+               }
+               if (max < 4) // free to check here, so skip polys behind the view
+                       continue;
+               transpoly[i].distance = average;
+               /*
+               transpoly[i].mindistance = min;
+               transpoly[i].maxdistance = max;
+               // calculate normal (eek)
+               VectorSubtract(transvert[transpoly[i].firstvert  ].v, transvert[transpoly[i].firstvert+1].v, v1);
+               VectorSubtract(transvert[transpoly[i].firstvert+2].v, transvert[transpoly[i].firstvert+1].v, v2);
+               VectorNormalize(v1);
+               VectorNormalize(v2);
+               if (transpoly[i].verts > 3 && fabs(DotProduct(v1, v2)) >= (1.0f - (1.0f / 256.0f))) // colinear edges, find a better triple
+               {
+                       VectorSubtract(transvert[transpoly[i].firstvert + transpoly[i].verts - 1].v, transvert[transpoly[i].firstvert].v, v1);
+                       VectorSubtract(transvert[transpoly[i].firstvert + 1].v, transvert[transpoly[i].firstvert].v, v2);
+                       VectorNormalize(v1);
+                       VectorNormalize(v2);
+                       if (fabs(DotProduct(v1, v2)) < (1.0f - (1.0f / 256.0f))) // found a good triple
+                               goto foundtriple;
+                       for (k = transpoly[i].firstvert + 2;k < (transpoly[i].firstvert + transpoly[i].verts - 1);k++)
+                       {
+                               VectorSubtract(transvert[k-1].v, transvert[k].v, v1);
+                               VectorSubtract(transvert[k+1].v, transvert[k].v, v2);
+                               VectorNormalize(v1);
+                               VectorNormalize(v2);
+                               if (fabs(DotProduct(v1, v2)) < (1.0f - (1.0f / 256.0f))) // found a good triple
+                                       goto foundtriple;
+                       }
+                       VectorSubtract(transvert[k-1].v, transvert[k].v, v1);
+                       VectorSubtract(transvert[transpoly[i].firstvert].v, transvert[k].v, v2);
+                       VectorNormalize(v1);
+                       VectorNormalize(v2);
+                       if (fabs(DotProduct(v1, v2)) >= (1.0f - (1.0f / 256.0f))) // no good triples; the polygon is a line, skip it
+                               continue;
+               }
+foundtriple:
+               CrossProduct(v1, v2, n);
+               VectorNormalize(n);
+               ndist = DotProduct(transvert[transpoly[i].firstvert+1].v, n);
+               // sorted insert
+               for (j = 0;j < transpolyindices;j++)
+               {
+                       // easy cases
+                       if (transpoly[transpolyindex[j]].mindistance > max)
+                               continue;
+                       if (transpoly[transpolyindex[j]].maxdistance < min)
+                               break;
+                       // hard case, check side
+                       for (k = transpoly[transpolyindex[j]].firstvert;k < (transpoly[transpolyindex[j]].firstvert + transpoly[transpolyindex[j]].verts);k++)
+                               if (DotProduct(transvert[k].v, n) < ndist)
+                                       goto skip;
+                       break;
+skip:
+                       ;
+               }
+               */
+               // sorted insert
+               for (j = 0;j < transpolyindices;j++)
+                       if (transpoly[transpolyindex[j]].distance < average)
+                               break;
+               for (k = transpolyindices;k > j;k--)
+                       transpolyindex[k] = transpolyindex[k-1];
+               transpolyindices++;
+               transpolyindex[j] = i;
+       }
+}
+
+// LordHavoc: qsort compare function
+/*
+int transpolyqsort(const void *ia, const void *ib)
+{
+       transpoly_t *a, *b;
+       int i, j;
+       a = &transpoly[*((unsigned short *)ia)];
+       b = &transpoly[*((unsigned short *)ib)];
+       // easy cases
+       if (a->mindistance > b->mindistance && a->maxdistance > b->maxdistance)
+               return -1; // behind
+       if (a->mindistance < b->mindistance && a->maxdistance < b->maxdistance)
+               return 1; // infront
+       // hard case
+       if (!a->ndist)
+       {
+               // calculate normal (eek)
+               vec3_t v1, v2;
+               VectorSubtract(transvert[a->firstvert  ].v, transvert[a->firstvert+1].v, v1);
+               VectorSubtract(transvert[a->firstvert+2].v, transvert[a->firstvert+1].v, v2);
+               CrossProduct(v1, v2, a->n);
+               VectorNormalize(a->n);
+               a->ndist = DotProduct(transvert[a->firstvert  ].v, a->n);
+       }
+       // check side
+       for (i = b->firstvert, j = 0;i < (b->firstvert + b->verts);i++)
+               j += DotProduct(transvert[i].v, a->n) < a->ndist; // (1) b is infront of a
+       if (j == 0)
+               return -1; // (-1) a is behind b
+       return j == b->verts; // (1) a is infront of b    (0) a and b intersect
+//     return (transpoly[*((unsigned short *)ib)].mindistance + transpoly[*((unsigned short *)ib)].maxdistance) - (transpoly[*((unsigned short *)ia)].mindistance + transpoly[*((unsigned short *)ia)].maxdistance);
+}
+*/
+
+extern qboolean isG200;
+
+/*
+void transpolysort()
+{
+       int i, j, a;
+//     qsort(&transpolyindex[0], transpolyindices, sizeof(unsigned short), transpolyqsort);
+       a = true;
+       while(a)
+       {
+               a = false;
+               for (i = 1;i < transpolyindices;i++)
+               {
+                       // easy cases
+                       if (transpoly[transpolyindex[i - 1]].mindistance > transpoly[transpolyindex[i]].mindistance && transpoly[transpolyindex[i - 1]].maxdistance > transpoly[transpolyindex[i]].maxdistance)
+                               continue; // previous is behind (no swap)
+                       if (transpoly[transpolyindex[i - 1]].mindistance < transpoly[transpolyindex[i]].mindistance && transpoly[transpolyindex[i - 1]].maxdistance < transpoly[transpolyindex[i]].maxdistance)
+                               goto swap; // previous is infront (swap)
+                       // hard case
+*/
+                       /*
+                       if (!transpoly[transpolyindex[i - 1]].ndist)
+                       {
+                               // calculate normal (eek)
+                               vec3_t v1, v2;
+                               VectorSubtract(transvert[transpoly[transpolyindex[i - 1]].firstvert  ].v, transvert[transpoly[transpolyindex[i - 1]].firstvert+1].v, v1);
+                               VectorSubtract(transvert[transpoly[transpolyindex[i - 1]].firstvert+2].v, transvert[transpoly[transpolyindex[i - 1]].firstvert+1].v, v2);
+                               CrossProduct(v1, v2, transpoly[transpolyindex[i - 1]].n);
+                               VectorNormalize(transpoly[transpolyindex[i - 1]].n);
+                               transpoly[transpolyindex[i - 1]].ndist = DotProduct(transvert[transpoly[transpolyindex[i - 1]].firstvert  ].v, transpoly[transpolyindex[i - 1]].n);
+                       }
+                       if (DotProduct(transpoly[transpolyindex[i - 1]].n, vpn) >= 0.0f) // backface
+                               continue;
+                       */
+/*
+                       // check side
+                       for (i = transpoly[transpolyindex[i]].firstvert;i < (transpoly[transpolyindex[i]].firstvert + transpoly[transpolyindex[i]].verts);i++)
+                               if (DotProduct(transvert[i].v, transpoly[transpolyindex[i - 1]].n) >= transpoly[transpolyindex[i - 1]].ndist)
+                                       goto noswap; // previous is behind or they intersect
+swap:
+                       // previous is infront (swap)
+                       j = transpolyindex[i];
+                       transpolyindex[i] = transpolyindex[i - 1];
+                       transpolyindex[i - 1] = j;
+                       a = true;
+noswap:
+                       ;
+               }
+       }
+}
+*/
+
+void transpolyrender()
+{
+       int i, j, tpolytype, texnum;
+       transpoly_t *p;
+       if (currenttranspoly < 1)
+               return;
+       transpolyrenderminmax();
+       if (transpolyindices < 1)
+               return;
+       // testing
+//     Con_DPrintf("transpolyrender: %i polys %i infront %i vertices\n", currenttranspoly, transpolyindices, currenttransvert);
+//     if (transpolyindices >= 2)
+//             transpolysort();
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glEnable(GL_BLEND);
+       glShadeModel(GL_SMOOTH);
+       glDepthMask(0); // disable zbuffer updates
+       if (isG200) // Matrox G200 cards can't handle per pixel alpha
+               glEnable(GL_ALPHA_TEST);
+       else
+               glDisable(GL_ALPHA_TEST);
+       // later note: wasn't working on my TNT drivers...  strangely...  used a cheaper hack in transpolyvert
+       //// offset by 16 depth units so decal sprites appear infront of walls
+       //glPolygonOffset(1, -16);
+       //glEnable(GL_POLYGON_OFFSET_FILL);
+       tpolytype = -1;
+       texnum = -1;
+       /*
+       if (gl_vertexarrays.value)
+       {
+               // set up the vertex array
+               qglInterleavedArrays(GL_T2F_C4UB_V3F, 0, transvert);
+               for (i = 0;i < transpolyindices;i++)
+               {
+                       p = &transpoly[transpolyindex[i]];
+                       if (p->texnum != texnum || p->transpolytype != tpolytype)
+                       {
+                               if (p->texnum != texnum)
+                               {
+                                       texnum = p->texnum;
+                                       glBindTexture(GL_TEXTURE_2D, texnum);
+                               }
+                               if (p->transpolytype != tpolytype)
+                               {
+                                       tpolytype = p->transpolytype;
+                                       if (tpolytype == TPOLYTYPE_ADD) // additive
+                                               glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+                                       else // alpha
+                                               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                               }
+                       }
+                       qglDrawArrays(GL_TRIANGLE_FAN, p->firstvert, p->verts);
+                       if (p->glowtexnum)
+                       {
+                               texnum = p->glowtexnum; // highly unlikely to match next poly, but...
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                               tpolytype = TPOLYTYPE_ADD; // might match next poly
+                               glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+                               qglDrawArrays(GL_TRIANGLE_FAN, p->firstvert, p->verts);
+                       }
+               }
+               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+               glDisableClientState(GL_COLOR_ARRAY);
+               glDisableClientState(GL_VERTEX_ARRAY);
+       }
+       else
+       */
+       {
+               int points = -1;
+               transvert_t *vert;
+               for (i = 0;i < transpolyindices;i++)
+               {
+                       p = &transpoly[transpolyindex[i]];
+                       if (p->texnum != texnum || p->verts != points || p->transpolytype != tpolytype)
+                       {
+                               glEnd();
+                               if (isG200)
+                               {
+                                       if (p->fogtexnum) // alpha
+                                               glEnable(GL_ALPHA_TEST);
+                                       else
+                                               glDisable(GL_ALPHA_TEST);
+                               }
+                               if (p->texnum != texnum)
+                               {
+                                       texnum = p->texnum;
+                                       glBindTexture(GL_TEXTURE_2D, texnum);
+                               }
+                               if (p->transpolytype != tpolytype)
+                               {
+                                       tpolytype = p->transpolytype;
+                                       if (tpolytype == TPOLYTYPE_ADD) // additive
+                                               glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+                                       else // alpha
+                                               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                               }
+                               points = p->verts;
+                               switch (points)
+                               {
+                               case 3:
+                                       glBegin(GL_TRIANGLES);
+                                       break;
+                               case 4:
+                                       glBegin(GL_QUADS);
+                                       break;
+                               default:
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       points = -1; // to force a reinit on the next poly
+                                       break;
+                               }
+                       }
+                       for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
+                       {
+                               // would be 2fv, but windoze Matrox G200 and probably G400 drivers don't support that (dumb...)
+                               glTexCoord2f(vert->s, vert->t);
+                               // again, vector version isn't supported I think
+                               glColor4ub(vert->r, vert->g, vert->b, vert->a);
+                               glVertex3fv(vert->v);
+                       }
+                       if (p->glowtexnum)
+                       {
+                               glEnd();
+                               texnum = p->glowtexnum; // highly unlikely to match next poly, but...
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                               if (tpolytype != TPOLYTYPE_ADD)
+                               {
+                                       tpolytype = TPOLYTYPE_ADD; // might match next poly
+                                       glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+                               }
+                               points = -1;
+                               glBegin(GL_TRIANGLE_FAN);
+                               for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
+                               {
+                                       glColor4ub(255,255,255,vert->a);
+                                       // would be 2fv, but windoze Matrox G200 and probably G400 drivers don't support that (dumb...)
+                                       glTexCoord2f(vert->s, vert->t);
+                                       glVertex3fv(vert->v);
+                               }
+                               glEnd();
+                       }
+                       if (fogenabled && p->transpolytype == TPOLYTYPE_ALPHA)
+                       {
+                               vec3_t diff;
+                               glEnd();
+                               points = -1; // to force a reinit on the next poly
+                               if (tpolytype != TPOLYTYPE_ALPHA)
+                               {
+                                       tpolytype = TPOLYTYPE_ALPHA; // probably matchs next poly
+                                       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                               }
+                               if (p->fogtexnum)
+                               {
+                                       if (texnum != p->fogtexnum) // highly unlikely to match next poly, but...
+                                       {
+                                               texnum = p->fogtexnum;
+                                               glBindTexture(GL_TEXTURE_2D, texnum);
+                                       }
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
+                                       {
+                                               VectorSubtract(vert->v, r_refdef.vieworg,diff);
+                                               glTexCoord2f(vert->s, vert->t);
+                                               glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], vert->a*(1.0f/255.0f)*exp(fogdensity/DotProduct(diff,diff)));
+                                               glVertex3fv(vert->v);
+                                       }
+                                       glEnd ();
+                               }
+                               else
+                               {
+                                       glDisable(GL_TEXTURE_2D);
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
+                                       {
+                                               VectorSubtract(vert->v, r_refdef.vieworg,diff);
+                                               glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], vert->a*(1.0f/255.0f)*exp(fogdensity/DotProduct(diff,diff)));
+                                               glVertex3fv(vert->v);
+                                       }
+                                       glEnd ();
+                                       glEnable(GL_TEXTURE_2D);
+                               }
+                       }
+               }
+               glEnd();
+       }
+
+       //glDisable(GL_POLYGON_OFFSET_FILL);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glDepthMask(1); // enable zbuffer updates
+       glDisable(GL_ALPHA_TEST);
+}
+
+void wallpolyclear()
+{
+       currentwallpoly = currentwallvert = 0;
+}
+
+extern qboolean lighthalf;
+void wallpolyrender()
+{
+       int i, j, texnum, lighttexnum;
+       wallpoly_t *p;
+       wallvert_t *vert;
+       if (currentwallpoly < 1)
+               return;
+       // testing
+       //Con_DPrintf("wallpolyrender: %i polys %i vertices\n", currentwallpoly, currentwallvert);
+       if (!gl_mtexable)
+               gl_multitexture.value = 0;
+       glDisable(GL_BLEND);
+       glShadeModel(GL_FLAT);
+       // make sure zbuffer is enabled
+       glEnable(GL_DEPTH_TEST);
+       glDisable(GL_ALPHA_TEST);
+       glDepthMask(1);
+       glColor3f(1,1,1);
+       if (r_fullbright.value) // LordHavoc: easy to do fullbright...
+       {
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+               texnum = -1;
+               for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
+               {
+                       if (p->texnum != texnum)
+                       {
+                               texnum = p->texnum;
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                       }
+                       vert = &wallvert[p->firstvert];
+                       glBegin(GL_POLYGON);
+                       for (j=0 ; j<p->verts ; j++, vert++)
+                       {
+                               glTexCoord2f (vert->s, vert->t);
+                               glVertex3fv (vert->vert);
+                       }
+                       glEnd ();
+               }
+       }
+       else if (gl_multitexture.value)
+       {
+               qglSelectTexture(gl_mtex_enum+0);
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+               glEnable(GL_TEXTURE_2D);
+               qglSelectTexture(gl_mtex_enum+1);
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+               glEnable(GL_TEXTURE_2D);
+               texnum = -1;
+               lighttexnum = -1;
+               for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
+               {
+//                     if (p->texnum != texnum || p->lighttexnum != lighttexnum)
+//                     {
+                               texnum = p->texnum;
+                               lighttexnum = p->lighttexnum;
+                               qglSelectTexture(gl_mtex_enum+0);
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                               qglSelectTexture(gl_mtex_enum+1);
+                               glBindTexture(GL_TEXTURE_2D, lighttexnum);
+//                     }
+                       vert = &wallvert[p->firstvert];
+                       glBegin(GL_POLYGON);
+                       for (j=0 ; j<p->verts ; j++, vert++)
+                       {
+                               qglMTexCoord2f(gl_mtex_enum, vert->s, vert->t); // texture
+                               qglMTexCoord2f((gl_mtex_enum+1), vert->u, vert->v); // lightmap
+                               glVertex3fv (vert->vert);
+                       }
+                       glEnd ();
+               }
+
+               qglSelectTexture(gl_mtex_enum+1);
+               glDisable(GL_TEXTURE_2D);
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+               qglSelectTexture(gl_mtex_enum+0);
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       }
+       else
+       {
+               // first do the textures
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+               texnum = -1;
+               for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
+               {
+                       if (p->texnum != texnum)
+                       {
+                               texnum = p->texnum;
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                       }
+                       vert = &wallvert[p->firstvert];
+                       glBegin(GL_POLYGON);
+                       for (j=0 ; j<p->verts ; j++, vert++)
+                       {
+                               glTexCoord2f (vert->s, vert->t);
+                               glVertex3fv (vert->vert);
+                       }
+                       glEnd ();
+               }
+               // then modulate using the lightmaps
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+               glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+               glEnable(GL_BLEND);
+               texnum = -1;
+               for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
+               {
+                       if (p->lighttexnum != texnum)
+                       {
+                               texnum = p->lighttexnum;
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                       }
+                       vert = &wallvert[p->firstvert];
+                       glBegin(GL_POLYGON);
+                       for (j=0 ; j<p->verts ; j++, vert++)
+                       {
+                               glTexCoord2f (vert->u, vert->v);
+                               glVertex3fv (vert->vert);
+                       }
+                       glEnd ();
+               }
+       }
+       // render glow textures
+       glDepthMask(0);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glBlendFunc(GL_ONE, GL_ONE);
+       glEnable(GL_BLEND);
+       if (lighthalf)
+               glColor3f(0.5,0.5,0.5);
+       else
+               glColor3f(1,1,1);
+       texnum = -1;
+       for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
+       {
+               if (!p->glowtexnum)
+                       continue;
+               if (p->glowtexnum != texnum)
+               {
+                       texnum = p->glowtexnum;
+                       glBindTexture(GL_TEXTURE_2D, texnum);
+               }
+               vert = &wallvert[p->firstvert];
+               glBegin(GL_POLYGON);
+               for (j=0 ; j<p->verts ; j++, vert++)
+               {
+                       glTexCoord2f (vert->s, vert->t);
+                       glVertex3fv (vert->vert);
+               }
+               glEnd ();
+       }
+       glColor3f(1,1,1);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glShadeModel(GL_SMOOTH);
+       if (fogenabled)
+       {
+               vec3_t diff;
+               glDisable(GL_TEXTURE_2D);
+               for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
+               {
+                       vert = &wallvert[p->firstvert];
+                       glBegin(GL_POLYGON);
+                       for (j=0 ; j<p->verts ; j++, vert++)
+                       {
+                               VectorSubtract(vert->vert, r_refdef.vieworg,diff);
+                               glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], exp(fogdensity/DotProduct(diff,diff)));
+                               glVertex3fv (vert->vert);
+                       }
+                       glEnd ();
+               }
+               glEnable(GL_TEXTURE_2D);
+       }
+       glDisable(GL_BLEND);
+       glDepthMask(1);
+}
+
+void skypolyclear()
+{
+       currentskypoly = currentskyvert = 0;
+}
+
+extern qboolean isATI;
+void skypolyrender()
+{
+       int i, j;
+       skypoly_t *p;
+       skyvert_t *vert;
+       if (currentskypoly < 1)
+               return;
+       // testing
+//     Con_DPrintf("skypolyrender: %i polys %i vertices\n", currentskypoly, currentskyvert);
+       glDisable(GL_TEXTURE_2D);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glDisable(GL_ALPHA_TEST);
+       glDisable(GL_BLEND);
+       // make sure zbuffer is enabled
+       glEnable(GL_DEPTH_TEST);
+       glDepthMask(1);
+       glColor3fv(fogcolor); // note: gets rendered over by sky if fog is not enabled
+       for (i = 0,p = &skypoly[0];i < currentskypoly;i++, p++)
+       {
+               vert = &skyvert[p->firstvert];
+               glBegin(GL_POLYGON);
+               for (j=0 ; j<p->verts ; j++, vert++)
+                       glVertex3fv (vert->v);
+               glEnd ();
+       }
+       glColor3f(1,1,1);
+       glEnable(GL_TEXTURE_2D);
+}
diff --git a/gl_poly.h b/gl_poly.h
new file mode 100644 (file)
index 0000000..b25a1b3
--- /dev/null
+++ b/gl_poly.h
@@ -0,0 +1,102 @@
+
+#define TPOLYTYPE_ALPHA 0
+#define TPOLYTYPE_ADD 1
+
+extern void transpolyclear();
+extern void transpolyrender();
+extern void transpolybegin(int texnum, int glowtexnum, int fogtexnum, int transpolytype);
+extern void transpolyend();
+
+extern void wallpolyclear();
+extern void wallpolyrender();
+
+extern void skypolyclear();
+extern void skypolyrender();
+extern void skypolybegin();
+extern void skypolyvert(float x, float y, float z);
+extern void skypolyend();
+
+#define MAX_TRANSPOLYS 8192
+#define MAX_TRANSVERTS (MAX_TRANSPOLYS*4)
+#define MAX_WALLPOLYS 16384
+#define MAX_WALLVERTS (MAX_WALLPOLYS*4)
+#define MAX_SKYPOLYS 2048
+#define MAX_SKYVERTS (MAX_SKYPOLYS*4)
+
+typedef struct
+{
+       vec_t s, t;
+       byte r,g,b,a;
+       vec3_t v;
+} transvert_t;
+
+typedef struct
+{
+       vec_t mindistance, maxdistance; // closest and farthest distance along v_forward
+       vec_t distance; // distance to center
+//     vec3_t n; // normal
+//     vec_t ndist; // distance from origin along that normal
+       unsigned short texnum;
+       unsigned short glowtexnum;
+       unsigned short fogtexnum;
+       unsigned short firstvert;
+       unsigned short verts;
+       unsigned short transpolytype;
+} transpoly_t;
+
+typedef struct
+{
+       vec3_t vert;
+       vec_t s, t, u, v;
+} wallvert_t;
+
+typedef struct
+{
+       unsigned short texnum, lighttexnum, glowtexnum;
+       unsigned short firstvert;
+       unsigned short verts;
+} wallpoly_t;
+
+typedef struct
+{
+       vec3_t v;
+} skyvert_t;
+
+typedef struct
+{
+       unsigned short firstvert;
+       unsigned short verts;
+} skypoly_t;
+
+extern transvert_t *transvert;
+extern transpoly_t *transpoly;
+extern unsigned short *transpolyindex;
+extern wallvert_t *wallvert;
+extern wallpoly_t *wallpoly;
+extern skyvert_t *skyvert;
+extern skypoly_t *skypoly;
+
+extern unsigned short currenttranspoly;
+extern unsigned short currenttransvert;
+extern unsigned short currentwallpoly;
+extern unsigned short currentwallvert;
+extern unsigned short currentskypoly;
+extern unsigned short currentskyvert;
+
+#define transpolyvert(vx,vy,vz,vs,vt,vr,vg,vb,va) \
+{\
+       if (currenttranspoly < MAX_TRANSPOLYS && currenttransvert < MAX_TRANSVERTS)\
+       {\
+               transvert[currenttransvert].s = (vs);\
+               transvert[currenttransvert].t = (vt);\
+               transvert[currenttransvert].r = (byte) (bound(0, (int) (vr), 255));\
+               transvert[currenttransvert].g = (byte) (bound(0, (int) (vg), 255));\
+               transvert[currenttransvert].b = (byte) (bound(0, (int) (vb), 255));\
+               transvert[currenttransvert].a = (byte) (bound(0, (int) (va), 255));\
+               transvert[currenttransvert].v[0] = (vx);\
+               transvert[currenttransvert].v[1] = (vy);\
+               transvert[currenttransvert].v[2] = (vz);\
+               currenttransvert++;\
+               transpoly[currenttranspoly].verts++;\
+       }\
+}
diff --git a/gl_refrag.c b/gl_refrag.c
new file mode 100644 (file)
index 0000000..1a85e7c
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_efrag.c
+
+#include "quakedef.h"
+
+mnode_t        *r_pefragtopnode;
+
+
+//===========================================================================
+
+/*
+===============================================================================
+
+                                       ENTITY FRAGMENT FUNCTIONS
+
+===============================================================================
+*/
+
+efrag_t                **lastlink;
+
+vec3_t         r_emins, r_emaxs;
+
+entity_t       *r_addent;
+
+
+/*
+================
+R_RemoveEfrags
+
+Call when removing an object from the world or moving it to another position
+================
+*/
+void R_RemoveEfrags (entity_t *ent)
+{
+       efrag_t         *ef, *old, *walk, **prev;
+       
+       ef = ent->efrag;
+       
+       while (ef)
+       {
+               prev = &ef->leaf->efrags;
+               while (1)
+               {
+                       walk = *prev;
+                       if (!walk)
+                               break;
+                       if (walk == ef)
+                       {       // remove this fragment
+                               *prev = ef->leafnext;
+                               break;
+                       }
+                       else
+                               prev = &walk->leafnext;
+               }
+                               
+               old = ef;
+               ef = ef->entnext;
+               
+       // put it on the free list
+               old->entnext = cl.free_efrags;
+               cl.free_efrags = old;
+       }
+       
+       ent->efrag = NULL; 
+}
+
+/*
+===================
+R_SplitEntityOnNode
+===================
+*/
+void R_SplitEntityOnNode (mnode_t *node)
+{
+       efrag_t         *ef;
+       mplane_t        *splitplane;
+       mleaf_t         *leaf;
+       int                     sides;
+
+loc0:
+       if (node->contents == CONTENTS_SOLID)
+       {
+               return;
+       }
+       
+// add an efrag if the node is a leaf
+
+       if ( node->contents < 0)
+       {
+               if (!r_pefragtopnode)
+                       r_pefragtopnode = node;
+
+               leaf = (mleaf_t *)node;
+
+// grab an efrag off the free list
+               ef = cl.free_efrags;
+               if (!ef)
+               {
+                       Con_Printf ("Too many efrags!\n");
+                       return;         // no free fragments...
+               }
+               cl.free_efrags = cl.free_efrags->entnext;
+
+               ef->entity = r_addent;
+               
+// add the entity link 
+               *lastlink = ef;
+               lastlink = &ef->entnext;
+               ef->entnext = NULL;
+               
+// set the leaf links
+               ef->leaf = leaf;
+               ef->leafnext = leaf->efrags;
+               leaf->efrags = ef;
+                       
+               return;
+       }
+       
+// NODE_MIXED
+
+       splitplane = node->plane;
+       sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
+       
+       if (sides == 3)
+       {
+       // split on this plane
+       // if this is the first splitter of this bmodel, remember it
+               if (!r_pefragtopnode)
+                       r_pefragtopnode = node;
+       }
+       
+// recurse down the contacted sides
+       // LordHavoc: optimized recursion
+//     if (sides & 1) R_SplitEntityOnNode (node->children[0]);
+//     if (sides & 2) R_SplitEntityOnNode (node->children[1]);
+       if (sides & 1)
+       {
+               if (sides & 2) // 3
+               {
+                       R_SplitEntityOnNode (node->children[0]);
+                       node = node->children[1];
+                       goto loc0;
+               }
+               else // 1
+               {
+                       node = node->children[0];
+                       goto loc0;
+               }
+       }
+       // 2
+       node = node->children[1];
+       goto loc0;
+}
+
+
+
+/*
+===========
+R_AddEfrags
+===========
+*/
+void R_AddEfrags (entity_t *ent)
+{
+       model_t         *entmodel;
+       int                     i;
+               
+       if (!ent->model)
+               return;
+
+       r_addent = ent;
+                       
+       lastlink = &ent->efrag;
+       r_pefragtopnode = NULL;
+       
+       entmodel = ent->model;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               r_emins[i] = ent->origin[i] + entmodel->mins[i];
+               r_emaxs[i] = ent->origin[i] + entmodel->maxs[i];
+       }
+
+       R_SplitEntityOnNode (cl.worldmodel->nodes);
+
+       ent->topnode = r_pefragtopnode;
+}
+
+
+/*
+================
+R_StoreEfrags
+
+// FIXME: a lot of this goes away with edge-based
+================
+*/
+void R_StoreEfrags (efrag_t **ppefrag)
+{
+       entity_t        *pent;
+       model_t         *clmodel;
+       efrag_t         *pefrag;
+
+
+       while ((pefrag = *ppefrag) != NULL)
+       {
+               pent = pefrag->entity;
+               clmodel = pent->model;
+
+               switch (clmodel->type)
+               {
+               case mod_alias:
+               case mod_brush:
+               case mod_sprite:
+                       pent = pefrag->entity;
+
+                       if ((pent->visframe != r_framecount) &&
+                               (cl_numvisedicts < MAX_VISEDICTS))
+                       {
+                               cl_visedicts[cl_numvisedicts++] = pent;
+
+                       // mark that we've recorded this entity for this frame
+                               pent->visframe = r_framecount;
+                       }
+
+                       ppefrag = &pefrag->leafnext;
+                       break;
+
+               default:        
+                       Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type);
+               }
+       }
+}
+
+
diff --git a/gl_rmain.c b/gl_rmain.c
new file mode 100644 (file)
index 0000000..dd27928
--- /dev/null
@@ -0,0 +1,1599 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_main.c
+
+#include "quakedef.h"
+
+entity_t       r_worldentity;
+
+qboolean       r_cache_thrash;         // compatability
+
+vec3_t         modelorg, r_entorigin;
+entity_t       *currententity;
+
+int                    r_visframecount;        // bumped when going to a new PVS
+int                    r_framecount;           // used for dlight push checking
+
+mplane_t       frustum[4];
+
+int                    c_brush_polys, c_alias_polys;
+
+qboolean       envmap;                         // true during envmap command capture 
+
+// LordHavoc: moved all code related to particles into r_part.c
+//int                  particletexture;        // little dot for particles
+int                    playertextures;         // up to 16 color translated skins
+
+extern qboolean isG200, isRagePro; // LordHavoc: special card hacks
+
+//
+// view origin
+//
+vec3_t vup;
+vec3_t vpn;
+vec3_t vright;
+vec3_t r_origin;
+
+float  r_world_matrix[16];
+float  r_base_world_matrix[16];
+
+//
+// screen size info
+//
+refdef_t       r_refdef;
+
+mleaf_t                *r_viewleaf, *r_oldviewleaf;
+
+texture_t      *r_notexture_mip;
+
+int            d_lightstylevalue[256]; // 8.8 fraction of base light value
+
+
+void R_MarkLeaves (void);
+
+//cvar_t       r_norefresh = {"r_norefresh","0"};
+cvar_t r_drawentities = {"r_drawentities","1"};
+cvar_t r_drawviewmodel = {"r_drawviewmodel","1"};
+cvar_t r_speeds = {"r_speeds","0"};
+cvar_t r_speeds2 = {"r_speeds2","0"};
+cvar_t r_fullbright = {"r_fullbright","0"};
+//cvar_t       r_lightmap = {"r_lightmap","0"};
+//cvar_t       r_shadows = {"r_shadows","0"};
+cvar_t r_wateralpha = {"r_wateralpha","1"};
+//cvar_t       r_dynamic = {"r_dynamic","1"};
+cvar_t r_novis = {"r_novis","0"};
+cvar_t r_waterripple = {"r_waterripple","0"};
+cvar_t r_fullbrights = {"r_fullbrights", "1"};
+
+//cvar_t       gl_cull = {"gl_cull","1"};
+//cvar_t       gl_affinemodels = {"gl_affinemodels","0"};
+//cvar_t       gl_polyblend = {"gl_polyblend","1"};
+//cvar_t       gl_flashblend = {"gl_flashblend","0"};
+cvar_t gl_playermip = {"gl_playermip","0"};
+//cvar_t       gl_nocolors = {"gl_nocolors","0"};
+//cvar_t       gl_keeptjunctions = {"gl_keeptjunctions","1"};
+//cvar_t       gl_reporttjunctions = {"gl_reporttjunctions","0"};
+cvar_t contrast = {"contrast", "1.0", TRUE}; // LordHavoc: a method of operating system independent color correction
+cvar_t brightness = {"brightness", "1.0", TRUE}; // LordHavoc: a method of operating system independent color correction
+cvar_t gl_lightmode = {"gl_lightmode", "1", TRUE}; // LordHavoc: overbright lighting
+//cvar_t       r_particles = {"r_particles", "1"};
+//cvar_t       r_dynamicwater = {"r_dynamicwater", "1"};
+//cvar_t       r_smokealpha = {"r_smokealpha", "0.25"};
+//cvar_t       r_dynamicbothsides = {"r_dynamicbothsides", "1"}; // LordHavoc: can disable dynamic lighting of backfaces, but quake maps are weird so it doesn't always work right...
+
+cvar_t r_fogdensity = {"r_fogdensity", "0"};
+cvar_t r_fogred = {"r_fogred","0.3"};
+cvar_t r_foggreen = {"r_foggreen","0.3"};
+cvar_t r_fogblue = {"r_fogblue","0.3"};
+
+cvar_t gl_fogenable = {"gl_fogenable", "0"};
+cvar_t gl_fogdensity = {"gl_fogdensity", "0.25"};
+cvar_t gl_fogred = {"gl_fogred","0.3"};
+cvar_t gl_foggreen = {"gl_foggreen","0.3"};
+cvar_t gl_fogblue = {"gl_fogblue","0.3"};
+cvar_t gl_fogstart = {"gl_fogstart", "0"};
+cvar_t gl_fogend = {"gl_fogend","0"};
+
+int chrometexture;
+
+void makechrometextures()
+{
+       int x, y, g, g2, amplitude, noise[64][64], min, max;
+       byte data[64][64][4];
+       //
+       // particle texture
+       //
+       chrometexture = texture_extension_number++;
+    glBindTexture(GL_TEXTURE_2D, chrometexture);
+
+#define n(x,y) noise[(y)&63][(x)&63]
+
+       amplitude = 16777215;
+       g2 = 64;
+       noise[0][0] = 0;
+       for (;(g = g2 >> 1) >= 1;g2 >>= 1)
+       {
+               // subdivide, diamond-square algorythm (really this has little to do with squares)
+               // diamond
+               for (y = 0;y < 64;y += g2)
+                       for (x = 0;x < 64;x += g2)
+                               n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
+               // square
+               for (y = 0;y < 64;y += g2)
+                       for (x = 0;x < 64;x += g2)
+                       {
+                               n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
+                               n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
+                       }
+               // brownian motion theory
+               amplitude >>= 1;
+               for (y = 0;y < 64;y += g)
+                       for (x = 0;x < 64;x += g)
+                               noise[y][x] += rand()&amplitude;
+       }
+       // normalize the noise range
+       min = max = 0;
+       for (y = 0;y < 64;y++)
+               for (x = 0;x < 64;x++)
+               {
+                       if (n(x,y) < min) min = n(x,y);
+                       if (n(x,y) > max) max = n(x,y);
+               }
+       max -= min;
+       for (y = 0;y < 64;y++)
+               for (x = 0;x < 64;x++)
+                       n(x,y) = (n(x,y) - min) * 255 / max;
+
+#undef n
+
+       // convert to RGBA data
+       for (y = 0;y < 64;y++)
+               for (x = 0;x < 64;x++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = (byte) noise[y][x];
+                       data[y][x][3] = 255;
+               }
+
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+extern qboolean isRagePro;
+
+qboolean lighthalf;
+
+vec3_t fogcolor;
+vec_t fogdensity;
+qboolean fogenabled;
+qboolean oldgl_fogenable;
+void FOG_framebegin()
+{
+       if (gl_fogenable.value)
+       {
+               oldgl_fogenable = true;
+               r_fogdensity.value = gl_fogdensity.value;
+               r_fogred.value = gl_fogred.value;
+               r_foggreen.value = gl_foggreen.value;
+               r_fogblue.value = gl_fogblue.value;
+       }
+       else if (oldgl_fogenable)
+       {
+               oldgl_fogenable = false;
+               r_fogdensity.value = 0;
+               r_fogred.value = 0.3;
+               r_foggreen.value = 0.3;
+               r_fogblue.value = 0.3;
+       }
+       /*
+       if(gl_fogdensity.value)
+       {
+               // LordHavoc: Borland C++ 5.0 was choking on this line, stupid compiler...
+               //GLfloat colors[4] = {(GLfloat) gl_fogred.value, (GLfloat) gl_foggreen.value, (GLfloat) gl_fogblue.value, (GLfloat) 1};
+               GLfloat colors[4];
+               colors[0] = gl_fogred.value;
+               colors[1] = gl_foggreen.value;
+               colors[2] = gl_fogblue.value;
+               colors[3] = 1;
+
+               glFogi (GL_FOG_MODE, GL_EXP2);
+               glFogf (GL_FOG_DENSITY, (GLfloat) gl_fogdensity.value / 100); 
+               glFogfv (GL_FOG_COLOR, colors);
+               glEnable (GL_FOG);
+       }
+       else
+               glDisable(GL_FOG);
+       */
+       if (r_fogdensity.value)
+       {
+               fogenabled = true;
+               fogdensity = -6144.0f / (r_fogdensity.value * r_fogdensity.value);
+               fogcolor[0] = bound(0.0f, r_fogred.value, 1.0f);
+               fogcolor[1] = bound(0.0f, r_foggreen.value, 1.0f);
+               fogcolor[2] = bound(0.0f, r_fogblue.value, 1.0f);
+               if (lighthalf)
+               {
+                       fogcolor[0] *= 0.5f;
+                       fogcolor[1] *= 0.5f;
+                       fogcolor[2] *= 0.5f;
+               }
+       }
+       else
+               fogenabled = false;
+}
+
+void FOG_frameend()
+{
+//     glDisable(GL_FOG);
+}
+
+void FOG_clear()
+{
+       gl_fogenable.value = 0;
+       r_fogdensity.value = 0;
+       r_fogred.value = 0.3;
+       r_fogblue.value = 0.3;
+       r_foggreen.value = 0.3;
+}
+
+void FOG_registercvars()
+{
+       Cvar_RegisterVariable (&r_fogdensity);
+       Cvar_RegisterVariable (&r_fogred);
+       Cvar_RegisterVariable (&r_foggreen); 
+       Cvar_RegisterVariable (&r_fogblue);
+       Cvar_RegisterVariable (&gl_fogenable);
+       Cvar_RegisterVariable (&gl_fogdensity);
+       Cvar_RegisterVariable (&gl_fogred);
+       Cvar_RegisterVariable (&gl_foggreen); 
+       Cvar_RegisterVariable (&gl_fogblue);
+       Cvar_RegisterVariable (&gl_fogstart);
+       Cvar_RegisterVariable (&gl_fogend);
+}
+
+void glpoly_init();
+void glrsurf_init();
+void rlight_init();
+
+// LordHavoc: vertex array
+float *aliasvert;
+float *aliasvertnorm;
+byte *aliasvertcolor;
+
+void rmain_registercvars()
+{
+       // allocate vertex processing arrays
+       aliasvert = malloc(sizeof(float[MD2MAX_VERTS][3]));
+       aliasvertnorm = malloc(sizeof(float[MD2MAX_VERTS][3]));
+       aliasvertcolor = malloc(sizeof(byte[MD2MAX_VERTS][4]));
+
+       FOG_registercvars();
+       Cvar_RegisterVariable (&r_speeds2);
+       Cvar_RegisterVariable (&contrast);
+       Cvar_RegisterVariable (&brightness);
+       Cvar_RegisterVariable (&gl_lightmode);
+//     Cvar_RegisterVariable (&r_dynamicwater);
+//     Cvar_RegisterVariable (&r_particles);
+//     Cvar_RegisterVariable (&r_smokealpha);
+//     Cvar_RegisterVariable (&r_dynamicbothsides);
+       Cvar_RegisterVariable (&r_fullbrights);
+       if (nehahra)
+               Cvar_SetValue("r_fullbrights", 0);
+//     if (gl_vendor && strstr(gl_vendor, "3Dfx"))
+//             gl_lightmode.value = 0;
+       Cvar_RegisterVariable (&r_fullbright);
+       makechrometextures();
+       glpoly_init();
+       glrsurf_init();
+       rlight_init();
+}
+
+/*
+void R_RotateForEntity (entity_t *e)
+{
+       glTranslatef (e->origin[0],  e->origin[1],  e->origin[2]);
+
+       glRotatef (e->angles[1],  0, 0, 1);
+       glRotatef (-e->angles[0],  0, 1, 0);
+       glRotatef (e->angles[2],  1, 0, 0);
+
+       glScalef (e->scale, e->scale, e->scale); // LordHavoc: model scale
+}
+*/
+
+// LordHavoc: if not for the fact BRIGHTFIELD particles require this, it would be removed...
+#define NUMVERTEXNORMALS       162
+
+float  r_avertexnormals[NUMVERTEXNORMALS][3] = {
+#include "anorms.h"
+};
+
+// LordHavoc: moved this shading stuff up because the sprites need shading stuff
+vec3_t shadevector;
+vec3_t shadecolor;
+
+float  modelalpha;
+
+void R_LightPoint (vec3_t color, vec3_t p);
+void R_DynamicLightPoint(vec3_t color, vec3_t org, int *dlightbits);
+void R_DynamicLightPointNoMask(vec3_t color, vec3_t org);
+
+float R_CalcAnimLerp(int pose, float lerpscale)
+{
+       if (currententity->draw_lastmodel == currententity->model && currententity->draw_lerpstart <= cl.time)
+       {
+               if (pose != currententity->draw_pose)
+               {
+                       currententity->draw_lastpose = currententity->draw_pose;
+                       currententity->draw_pose = pose;
+                       currententity->draw_lerpstart = cl.time;
+                       return 0;
+               }
+               else
+                       return ((cl.time - currententity->draw_lerpstart) * lerpscale);
+       }
+       else // uninitialized
+       {
+               currententity->draw_lastmodel = currententity->model;
+               currententity->draw_lastpose = currententity->draw_pose = pose;
+               currententity->draw_lerpstart = cl.time;
+               return 0;
+       }
+}
+
+/*
+=============================================================
+
+  SPRITE MODELS
+
+=============================================================
+*/
+
+/*
+================
+R_GetSpriteFrame
+================
+*/
+void R_GetSpriteFrame (entity_t *currententity, mspriteframe_t **oldframe, mspriteframe_t **newframe, float *framelerp)
+{
+       msprite_t               *psprite;
+       mspritegroup_t  *pspritegroup;
+       int                             i, j, numframes, frame;
+       float                   *pintervals, fullinterval, targettime, time, jtime, jinterval;
+
+       psprite = currententity->model->cache.data;
+       frame = currententity->frame;
+
+       if ((frame >= psprite->numframes) || (frame < 0))
+       {
+               Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
+               frame = 0;
+       }
+
+       if (psprite->frames[frame].type == SPR_SINGLE)
+       {
+               if (currententity->draw_lastmodel == currententity->model && currententity->draw_lerpstart < cl.time)
+               {
+                       if (frame != currententity->draw_pose)
+                       {
+                               currententity->draw_lastpose = currententity->draw_pose;
+                               currententity->draw_pose = frame;
+                               currententity->draw_lerpstart = cl.time;
+                               *framelerp = 0;
+                       }
+                       else
+                               *framelerp = (cl.time - currententity->draw_lerpstart) * 10.0;
+               }
+               else // uninitialized
+               {
+                       currententity->draw_lastmodel = currententity->model;
+                       currententity->draw_lastpose = currententity->draw_pose = frame;
+                       currententity->draw_lerpstart = cl.time;
+                       *framelerp = 0;
+               }
+               *oldframe = psprite->frames[currententity->draw_lastpose].frameptr;
+               *newframe = psprite->frames[frame].frameptr;
+       }
+       else
+       {
+               pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
+               pintervals = pspritegroup->intervals;
+               numframes = pspritegroup->numframes;
+               fullinterval = pintervals[numframes-1];
+
+               time = cl.time + currententity->syncbase;
+
+       // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
+       // are positive, so we don't have to worry about division by 0
+               targettime = time - ((int)(time / fullinterval)) * fullinterval;
+
+               // LordHavoc: since I can't measure the time properly when it loops from numframes-1 to 0,
+               //            I instead measure the time of the first frame, hoping it is consistent
+               j = numframes-1;jtime = 0;jinterval = pintervals[1] - pintervals[0];
+               for (i=0 ; i<(numframes-1) ; i++)
+               {
+                       if (pintervals[i] > targettime)
+                               break;
+                       j = i;jinterval = pintervals[i] - jtime;jtime = pintervals[i];
+               }
+               *framelerp = (targettime - jtime) / jinterval;
+
+               *oldframe = pspritegroup->frames[j];
+               *newframe = pspritegroup->frames[i];
+       }
+}
+
+void GL_DrawSpriteImage (mspriteframe_t *frame, vec3_t origin, vec3_t up, vec3_t right, int red, int green, int blue, int alpha)
+{
+       // LordHavoc: rewrote this to use the transparent poly system
+       // FIXME: need to use uncolored fog sprite
+       transpolybegin(frame->gl_texturenum, 0, frame->gl_fogtexturenum, currententity->effects & EF_ADDITIVE ? TPOLYTYPE_ADD : TPOLYTYPE_ALPHA);
+       transpolyvert(origin[0] + frame->down * up[0] + frame->left * right[0], origin[1] + frame->down * up[1] + frame->left * right[1], origin[2] + frame->down * up[2] + frame->left * right[2], 0, 1, red, green, blue, alpha);
+       transpolyvert(origin[0] + frame->up * up[0] + frame->left * right[0], origin[1] + frame->up * up[1] + frame->left * right[1], origin[2] + frame->up * up[2] + frame->left * right[2], 0, 0, red, green, blue, alpha);
+       transpolyvert(origin[0] + frame->up * up[0] + frame->right * right[0], origin[1] + frame->up * up[1] + frame->right * right[1], origin[2] + frame->up * up[2] + frame->right * right[2], 1, 0, red, green, blue, alpha);
+       transpolyvert(origin[0] + frame->down * up[0] + frame->right * right[0], origin[1] + frame->down * up[1] + frame->right * right[1], origin[2] + frame->down * up[2] + frame->right * right[2], 1, 1, red, green, blue, alpha);
+       transpolyend();
+}
+
+extern qboolean isG200, isRagePro, lighthalf;
+
+/*
+=================
+R_DrawSpriteModel
+
+=================
+*/
+void R_DrawSpriteModel (entity_t *e)
+{
+       mspriteframe_t  *oldframe, *newframe;
+       float           *up, *right, lerp, ilerp;
+       vec3_t          v_forward, v_right, v_up, org;
+       msprite_t               *psprite;
+
+       // don't even bother culling, because it's just a single
+       // polygon without a surface cache
+       R_GetSpriteFrame (e, &oldframe, &newframe, &lerp);
+       if (lerp < 0) lerp = 0;
+       if (lerp > 1) lerp = 1;
+       if (isRagePro) // LordHavoc: no alpha scaling supported on per pixel alpha images on ATI Rage Pro... ACK!
+               lerp = 1;
+       ilerp = 1.0 - lerp;
+       psprite = e->model->cache.data;
+
+       if (psprite->type == SPR_ORIENTED)
+       {       // bullet marks on walls
+               AngleVectors (e->angles, v_forward, v_right, v_up);
+               up = v_up;
+               right = v_right;
+               VectorSubtract(e->origin, vpn, org);
+       }
+       else
+       {       // normal sprite
+               up = vup;
+               right = vright;
+               VectorCopy(e->origin, org);
+       }
+       if (e->scale != 1)
+       {
+               VectorScale(up, e->scale, up);
+               VectorScale(right, e->scale, right);
+       }
+
+       if (e->model->flags & EF_FULLBRIGHT || e->effects & EF_FULLBRIGHT)
+       {
+               if (lighthalf)
+               {
+                       shadecolor[0] = e->colormod[0] * 128;
+                       shadecolor[1] = e->colormod[1] * 128;
+                       shadecolor[2] = e->colormod[2] * 128;
+               }
+               else
+               {
+                       shadecolor[0] = e->colormod[0] * 255;
+                       shadecolor[1] = e->colormod[1] * 255;
+                       shadecolor[2] = e->colormod[2] * 255;
+               }
+       }
+       else
+       {
+               R_LightPoint (shadecolor, e->origin);
+               R_DynamicLightPointNoMask(shadecolor, e->origin);
+               if (lighthalf)
+               {
+                       shadecolor[0] *= e->colormod[0] * 0.5;
+                       shadecolor[1] *= e->colormod[1] * 0.5;
+                       shadecolor[2] *= e->colormod[2] * 0.5;
+               }
+       }
+
+       // LordHavoc: interpolated sprite rendering
+       if (ilerp != 0)
+               GL_DrawSpriteImage(oldframe, org, up, right, shadecolor[0],shadecolor[1],shadecolor[2],e->alpha*255*ilerp);
+       if (lerp != 0)
+               GL_DrawSpriteImage(newframe, org, up, right, shadecolor[0],shadecolor[1],shadecolor[2],e->alpha*255*lerp);
+}
+
+/*
+=============================================================
+
+  ALIAS MODELS
+
+=============================================================
+*/
+
+extern vec3_t softwaretransform_x;
+extern vec3_t softwaretransform_y;
+extern vec3_t softwaretransform_z;
+extern vec_t softwaretransform_scale;
+extern vec3_t softwaretransform_offset;
+void R_AliasLerpVerts(int vertcount, float lerp, trivert2 *verts1, vec3_t scale1, vec3_t translate1, trivert2 *verts2, vec3_t scale2, vec3_t translate2)
+{
+       int i;
+       vec3_t point, matrix_x, matrix_y, matrix_z;
+       float *av, *avn;
+       av = aliasvert;
+       avn = aliasvertnorm;
+       if (lerp < 0) lerp = 0;
+       if (lerp > 1) lerp = 1;
+       if (lerp != 0)
+       {
+               float ilerp, ilerp127, lerp127, scalex1, scalex2, translatex, scaley1, scaley2, translatey, scalez1, scalez2, translatez;
+               if (lerp < 0) lerp = 0;
+               if (lerp > 1) lerp = 1;
+               ilerp = 1 - lerp;
+               ilerp127 = ilerp * (1.0 / 127.0);
+               lerp127 = lerp * (1.0 / 127.0);
+               VectorScale(softwaretransform_x, softwaretransform_scale, matrix_x);
+               VectorScale(softwaretransform_y, softwaretransform_scale, matrix_y);
+               VectorScale(softwaretransform_z, softwaretransform_scale, matrix_z);
+               // calculate combined interpolation variables
+               scalex1 = scale1[0] * ilerp;scalex2 = scale2[0] *  lerp;translatex = translate1[0] * ilerp + translate2[0] *  lerp;
+               scaley1 = scale1[1] * ilerp;scaley2 = scale2[1] *  lerp;translatey = translate1[1] * ilerp + translate2[1] *  lerp;
+               scalez1 = scale1[2] * ilerp;scalez2 = scale2[2] *  lerp;translatez = translate1[2] * ilerp + translate2[2] *  lerp;
+               // generate vertices
+               for (i = 0;i < vertcount;i++)
+               {
+                       // rotate, scale, and translate the vertex locations
+                       point[0] = verts1->v[0] * scalex1 + verts2->v[0] * scalex2 + translatex;
+                       point[1] = verts1->v[1] * scaley1 + verts2->v[1] * scaley2 + translatey;
+                       point[2] = verts1->v[2] * scalez1 + verts2->v[2] * scalez2 + translatez;
+                       *av++ = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
+                       *av++ = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
+                       *av++ = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
+                       // rotate the normals
+                       point[0] = verts1->n[0] * ilerp127 + verts2->n[0] * lerp127;
+                       point[1] = verts1->n[1] * ilerp127 + verts2->n[1] * lerp127;
+                       point[2] = verts1->n[2] * ilerp127 + verts2->n[2] * lerp127;
+                       *avn++ = point[0] * softwaretransform_x[0] + point[1] * softwaretransform_y[0] + point[2] * softwaretransform_z[0];
+                       *avn++ = point[0] * softwaretransform_x[1] + point[1] * softwaretransform_y[1] + point[2] * softwaretransform_z[1];
+                       *avn++ = point[0] * softwaretransform_x[2] + point[1] * softwaretransform_y[2] + point[2] * softwaretransform_z[2];
+                       verts1++;verts2++;
+               }
+       }
+       else
+       {
+               float i127;
+               i127 = 1.0f / 127.0f;
+               VectorScale(softwaretransform_x, softwaretransform_scale, matrix_x);
+               VectorScale(softwaretransform_y, softwaretransform_scale, matrix_y);
+               VectorScale(softwaretransform_z, softwaretransform_scale, matrix_z);
+               // generate vertices
+               for (i = 0;i < vertcount;i++)
+               {
+                       // rotate, scale, and translate the vertex locations
+                       point[0] = verts1->v[0] * scale1[0] + translate1[0];
+                       point[1] = verts1->v[1] * scale1[1] + translate1[1];
+                       point[2] = verts1->v[2] * scale1[2] + translate1[2];
+                       *av++ = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
+                       *av++ = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
+                       *av++ = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
+                       // rotate the normals
+                       point[0] = verts1->n[0] * i127;
+                       point[1] = verts1->n[1] * i127;
+                       point[2] = verts1->n[2] * i127;
+                       *avn++ = point[0] * softwaretransform_x[0] + point[1] * softwaretransform_y[0] + point[2] * softwaretransform_z[0];
+                       *avn++ = point[0] * softwaretransform_x[1] + point[1] * softwaretransform_y[1] + point[2] * softwaretransform_z[1];
+                       *avn++ = point[0] * softwaretransform_x[2] + point[1] * softwaretransform_y[2] + point[2] * softwaretransform_z[2];
+                       verts1++;
+               }
+       }
+}
+
+/*
+=================
+R_DrawAliasFrame
+
+=================
+*/
+extern vec3_t lightspot;
+void R_LightModel(int numverts, vec3_t center);
+extern cvar_t gl_vertexarrays;
+void R_DrawAliasFrame (aliashdr_t *paliashdr)
+{
+       int                             i, pose, frame = currententity->frame;
+       float                   lerpscale, lerp;
+
+       softwaretransformforentity(currententity);
+
+       if ((frame >= paliashdr->numframes) || (frame < 0))
+       {
+               Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame);
+               frame = 0;
+       }
+
+       pose = paliashdr->frames[frame].firstpose;
+
+       if (paliashdr->frames[frame].numposes > 1)
+       {
+               lerpscale = 1.0 / paliashdr->frames[frame].interval;
+               pose += (int)(cl.time * lerpscale) % paliashdr->frames[frame].numposes;
+       }
+       else
+               lerpscale = 10.0;
+
+       lerp = R_CalcAnimLerp(pose, lerpscale);
+
+       R_AliasLerpVerts(paliashdr->numverts, lerp, (trivert2 *)((byte *)paliashdr + paliashdr->posedata) + currententity->draw_lastpose * paliashdr->numverts, paliashdr->scale, paliashdr->scale_origin, (trivert2 *)((byte *)paliashdr + paliashdr->posedata) + currententity->draw_pose * paliashdr->numverts, paliashdr->scale, paliashdr->scale_origin);
+
+       R_LightModel(paliashdr->numverts, currententity->origin);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glShadeModel(GL_SMOOTH);
+       if (currententity->effects & EF_ADDITIVE)
+       {
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE); // additive rendering
+               glEnable(GL_BLEND);
+               glDepthMask(0);
+       }
+       else if (modelalpha != 1.0)
+       {
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               glEnable(GL_BLEND);
+               glDepthMask(0);
+       }
+       else
+       {
+               glDisable(GL_BLEND);
+               glDepthMask(1);
+       }
+
+       if (gl_vertexarrays.value)
+       {
+               // LordHavoc: I would use InterleavedArrays here,
+               // but the texture coordinates are a seperate array,
+               // and it would be wasteful to copy them into the main array...
+       //      glColor4f(shadecolor[0], shadecolor[1], shadecolor[2], modelalpha);
+               qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+               qglColorPointer(4, GL_UNSIGNED_BYTE, 0, aliasvertcolor);
+               glEnableClientState(GL_VERTEX_ARRAY);
+               glEnableClientState(GL_COLOR_ARRAY);
+
+               // draw the front faces
+               qglTexCoordPointer(2, GL_FLOAT, 0, (void *)((int) paliashdr->texcoords + (int) paliashdr));
+               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+               qglDrawElements(GL_TRIANGLES, paliashdr->frontfaces * 3, GL_UNSIGNED_SHORT, (void *)((int) paliashdr->vertindices + (int) paliashdr));
+               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+               // draw the back faces
+               qglTexCoordPointer(2, GL_FLOAT, 0, (void *)((int) paliashdr->texcoords + sizeof(float[2]) * paliashdr->numverts + (int) paliashdr));
+               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+               qglDrawElements(GL_TRIANGLES, paliashdr->backfaces * 3, GL_UNSIGNED_SHORT, (void *)((int) paliashdr->vertindices + sizeof(unsigned short[3]) * paliashdr->frontfaces + (int) paliashdr));
+               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+               glDisableClientState(GL_COLOR_ARRAY);
+               glDisableClientState(GL_VERTEX_ARRAY);
+       }
+       else
+       {
+               unsigned short *in, index;
+               float *tex;
+               in = (void *)((int) paliashdr->vertindices + (int) paliashdr);
+               glBegin(GL_TRIANGLES);
+               // draw the front faces
+               tex = (void *)((int) paliashdr->texcoords + (int) paliashdr);
+               //if (isG200)
+               //{
+                       for (i = 0;i < paliashdr->frontfaces * 3;i++)
+                       {
+                               index = *in++;
+                               glTexCoord2f(tex[index*2], tex[index*2+1]);
+                               glColor4f(aliasvertcolor[index*4] * (1.0f / 255.0f), aliasvertcolor[index*4+1] * (1.0f / 255.0f), aliasvertcolor[index*4+2] * (1.0f / 255.0f), aliasvertcolor[index*4+3] * (1.0f / 255.0f));
+                               glVertex3fv(&aliasvert[index*3]);
+                       }
+               /*
+               }
+               else
+               {
+                       for (i = 0;i < paliashdr->frontfaces * 3;i++)
+                       {
+                               index = *in++;
+                               glTexCoord2f(tex[index*2], tex[index*2+1]);
+                               glColor4ub(aliasvertcolor[index*4], aliasvertcolor[index*4+1], aliasvertcolor[index*4+2], aliasvertcolor[index*4+3]);
+                               glVertex3fv(&aliasvert[index*3]);
+                       }
+               }
+               */
+               // draw the back faces
+               tex += 2 * paliashdr->numverts;
+               //if (isG200)
+               //{
+                       for (i = 0;i < paliashdr->backfaces * 3;i++)
+                       {
+                               index = *in++;
+                               glTexCoord2f(tex[index*2], tex[index*2+1]);
+                               glColor4f(aliasvertcolor[index*4] * (1.0f / 255.0f), aliasvertcolor[index*4+1] * (1.0f / 255.0f), aliasvertcolor[index*4+2] * (1.0f / 255.0f), aliasvertcolor[index*4+3] * (1.0f / 255.0f));
+                               glVertex3fv(&aliasvert[index*3]);
+                       }
+               /*
+               }
+               else
+               {
+                       for (i = 0;i < paliashdr->backfaces * 3;i++)
+                       {
+                               index = *in++;
+                               glTexCoord2f(tex[index*2], tex[index*2+1]);
+                               glColor4ub(aliasvertcolor[index*4], aliasvertcolor[index*4+1], aliasvertcolor[index*4+2], aliasvertcolor[index*4+3]);
+                               glVertex3fv(&aliasvert[index*3]);
+                       }
+               }
+               */
+               glEnd();
+       }
+
+       if (fogenabled)
+       {
+               vec3_t diff;
+               glDisable (GL_TEXTURE_2D);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               glEnable (GL_BLEND);
+               glDepthMask(0); // disable zbuffer updates
+
+               VectorSubtract(currententity->origin, r_refdef.vieworg, diff);
+               glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], exp(fogdensity/DotProduct(diff,diff)));
+
+               if (gl_vertexarrays.value)
+               {
+                       qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+                       glEnableClientState(GL_VERTEX_ARRAY);
+                       qglDrawElements(GL_TRIANGLES, paliashdr->numtris * 3, GL_UNSIGNED_SHORT, (void *)((int) paliashdr->vertindices + (int) paliashdr));
+                       glDisableClientState(GL_VERTEX_ARRAY);
+               }
+               else
+               {
+                       unsigned short *in;
+                       in = (void *)((int) paliashdr->vertindices + (int) paliashdr);
+                       glBegin(GL_TRIANGLES);
+                       for (i = 0;i < paliashdr->numtris * 3;i++)
+                               glVertex3fv(&aliasvert[*in++ * 3]);
+                       glEnd();
+               }
+
+               glEnable (GL_TEXTURE_2D);
+               glColor3f (1,1,1);
+       }
+
+       /*
+       if (r_shadows.value && !(currententity->effects & EF_ADDITIVE) && currententity != &cl.viewent)
+       {
+               // flatten it to make a shadow
+               float *av = aliasvert + 2, l = lightspot[2] + 0.125;
+               av = aliasvert + 2;
+               for (i = 0;i < paliashdr->numverts;i++, av+=3)
+                       if (*av > l)
+                               *av = l;
+               glDisable (GL_TEXTURE_2D);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               glEnable (GL_BLEND);
+               glDepthMask(0); // disable zbuffer updates
+               glColor4f (0,0,0,0.5 * modelalpha);
+
+               if (gl_vertexarrays.value)
+               {
+                       qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+                       glEnableClientState(GL_VERTEX_ARRAY);
+                       qglDrawElements(GL_TRIANGLES, paliashdr->numtris * 3, GL_UNSIGNED_SHORT, (void *)((int) paliashdr->vertindices + (int) paliashdr));
+                       glDisableClientState(GL_VERTEX_ARRAY);
+               }
+               else
+               {
+                       unsigned short *in;
+                       in = (void *)((int) paliashdr->vertindices + (int) paliashdr);
+                       glBegin(GL_TRIANGLES);
+                       for (i = 0;i < paliashdr->numtris * 3;i++)
+                               glVertex3fv(&aliasvert[*in++ * 3]);
+                       glEnd();
+               }
+
+               glEnable (GL_TEXTURE_2D);
+               glColor3f (1,1,1);
+       }
+       */
+
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glEnable (GL_BLEND);
+       glDepthMask(1);
+}
+
+/*
+=================
+R_DrawQ2AliasFrame
+
+=================
+*/
+void R_DrawQ2AliasFrame (md2mem_t *pheader)
+{
+       int *order, count, frame = currententity->frame;
+       float lerp;
+       md2memframe_t *frame1, *frame2;
+
+       softwaretransformforentity(currententity);
+
+       if ((frame >= pheader->num_frames) || (frame < 0))
+       {
+               Con_DPrintf ("R_SetupQ2AliasFrame: no such frame %d\n", frame);
+               frame = 0;
+       }
+
+       lerp = R_CalcAnimLerp(frame, 10);
+
+       frame1 = (void *)((int) pheader + pheader->ofs_frames + (pheader->framesize * currententity->draw_lastpose));
+       frame2 = (void *)((int) pheader + pheader->ofs_frames + (pheader->framesize * currententity->draw_pose));
+       R_AliasLerpVerts(pheader->num_xyz, lerp, frame1->verts, frame1->scale, frame1->translate, frame2->verts, frame2->scale, frame2->translate);
+
+       R_LightModel(pheader->num_xyz, currententity->origin);
+
+       if (gl_vertexarrays.value)
+       {
+               // LordHavoc: big mess...
+               // using arrays only slightly, although it is enough to prevent duplicates
+               // (saving half the transforms)
+               //glColor4f(shadecolor[0], shadecolor[1], shadecolor[2], modelalpha);
+               qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+               qglColorPointer(4, GL_UNSIGNED_BYTE, 0, aliasvertcolor);
+               glEnableClientState(GL_VERTEX_ARRAY);
+               glEnableClientState(GL_COLOR_ARRAY);
+
+               order = (int *)((int)pheader + pheader->ofs_glcmds);
+               while(1)
+               {
+                       if (!(count = *order++))
+                               break;
+                       if (count > 0)
+                               glBegin(GL_TRIANGLE_STRIP);
+                       else
+                       {
+                               glBegin(GL_TRIANGLE_FAN);
+                               count = -count;
+                       }
+                       do
+                       {
+                               glTexCoord2f(((float *)order)[0], ((float *)order)[1]);
+                               qglArrayElement(order[2]);
+                               order += 3;
+                       }
+                       while (count--);
+               }
+
+               glDisableClientState(GL_COLOR_ARRAY);
+               glDisableClientState(GL_VERTEX_ARRAY);
+       }
+       else
+       {
+               order = (int *)((int)pheader + pheader->ofs_glcmds);
+               while(1)
+               {
+                       if (!(count = *order++))
+                               break;
+                       if (count > 0)
+                               glBegin(GL_TRIANGLE_STRIP);
+                       else
+                       {
+                               glBegin(GL_TRIANGLE_FAN);
+                               count = -count;
+                       }
+                       //if (isG200)
+                       //{
+                               do
+                               {
+                                       glTexCoord2f(((float *)order)[0], ((float *)order)[1]);
+                                       glColor4f(aliasvertcolor[order[2] * 4] * (1.0f / 255.0f), aliasvertcolor[order[2] * 4 + 1] * (1.0f / 255.0f), aliasvertcolor[order[2] * 4 + 2] * (1.0f / 255.0f), aliasvertcolor[order[2] * 4 + 3] * (1.0f / 255.0f));
+                                       glVertex3fv(&aliasvert[order[2] * 3]);
+                                       order += 3;
+                               }
+                               while (count--);
+                       /*
+                       }
+                       else
+                       {
+                               do
+                               {
+                                       glTexCoord2f(((float *)order)[0], ((float *)order)[1]);
+                                       glColor4ub(aliasvertcolor[order[2] * 4], aliasvertcolor[order[2] * 4 + 1], aliasvertcolor[order[2] * 4 + 2], aliasvertcolor[order[2] * 4 + 3]);
+                                       glVertex3fv(&aliasvert[order[2] * 3]);
+                                       order += 3;
+                               }
+                               while (count--);
+                       }
+                       */
+               }
+       }
+
+       if (fogenabled)
+       {
+               glDisable (GL_TEXTURE_2D);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               glEnable (GL_BLEND);
+               glDepthMask(0); // disable zbuffer updates
+               {
+                       vec3_t diff;
+                       VectorSubtract(currententity->origin, r_refdef.vieworg, diff);
+                       glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], exp(fogdensity/DotProduct(diff,diff)));
+               }
+
+               if (gl_vertexarrays.value)
+               {
+                       // LordHavoc: big mess...
+                       // using arrays only slightly, although it is enough to prevent duplicates
+                       // (saving half the transforms)
+                       //glColor4f(shadecolor[0], shadecolor[1], shadecolor[2], modelalpha);
+                       qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+                       glEnableClientState(GL_VERTEX_ARRAY);
+
+                       order = (int *)((int)pheader + pheader->ofs_glcmds);
+                       while(1)
+                       {
+                               if (!(count = *order++))
+                                       break;
+                               if (count > 0)
+                                       glBegin(GL_TRIANGLE_STRIP);
+                               else
+                               {
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       count = -count;
+                               }
+                               do
+                               {
+                                       qglArrayElement(order[2]);
+                                       order += 3;
+                               }
+                               while (count--);
+                       }
+
+                       glDisableClientState(GL_VERTEX_ARRAY);
+               }
+               else
+               {
+                       order = (int *)((int)pheader + pheader->ofs_glcmds);
+                       while(1)
+                       {
+                               if (!(count = *order++))
+                                       break;
+                               if (count > 0)
+                                       glBegin(GL_TRIANGLE_STRIP);
+                               else
+                               {
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       count = -count;
+                               }
+                               do
+                               {
+                                       glVertex3fv(&aliasvert[order[2] * 3]);
+                                       order += 3;
+                               }
+                               while (count--);
+                       }
+               }
+
+               glEnable (GL_TEXTURE_2D);
+               glColor3f (1,1,1);
+       }
+
+       /*
+       if (r_shadows.value && !(currententity->effects & EF_ADDITIVE) && currententity != &cl.viewent)
+       {
+               int i;
+               float *av = aliasvert + 2, l = lightspot[2] + 0.125;
+               av = aliasvert + 2;
+               for (i = 0;i < pheader->num_xyz;i++, av+=3)
+                       if (*av > l)
+                               *av = l;
+               glDisable (GL_TEXTURE_2D);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               glEnable (GL_BLEND);
+               glDepthMask(0); // disable zbuffer updates
+               glColor4f (0,0,0,0.5 * modelalpha);
+
+               if (gl_vertexarrays.value)
+               {
+                       qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+                       glEnableClientState(GL_VERTEX_ARRAY);
+                                               
+                       while(1)
+                       {
+                               if (!(count = *order++))
+                                       break;
+                               if (count > 0)
+                                       glBegin(GL_TRIANGLE_STRIP);
+                               else
+                               {
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       count = -count;
+                               }
+                               do
+                               {
+                                       qglArrayElement(order[2]);
+                                       order += 3;
+                               }
+                               while (count--);
+                       }
+
+                       glDisableClientState(GL_VERTEX_ARRAY);
+               }
+               else
+               {
+                       while(1)
+                       {
+                               if (!(count = *order++))
+                                       break;
+                               if (count > 0)
+                                       glBegin(GL_TRIANGLE_STRIP);
+                               else
+                               {
+                                       glBegin(GL_TRIANGLE_FAN);
+                                       count = -count;
+                               }
+                               do
+                               {
+                                       glVertex3fv(&aliasvert[order[2] * 3]);
+                                       order += 3;
+                               }
+                               while (count--);
+                       }
+               }
+
+               glEnable (GL_TEXTURE_2D);
+               glColor3f (1,1,1);
+       }
+       */
+
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glEnable (GL_BLEND);
+       glDepthMask(1);
+}
+
+/*
+=================
+R_DrawAliasModel
+
+=================
+*/
+void R_DrawAliasModel (entity_t *e, int cull)
+{
+       int                     i;
+       model_t         *clmodel;
+       vec3_t          mins, maxs;
+       aliashdr_t      *paliashdr;
+       md2mem_t                *pheader;
+       int                     anim;
+
+       if (modelalpha < (1.0 / 64.0))
+               return; // basically completely transparent
+
+       clmodel = currententity->model;
+
+       VectorAdd (currententity->origin, clmodel->mins, mins);
+       VectorAdd (currententity->origin, clmodel->maxs, maxs);
+
+       if (cull && R_CullBox (mins, maxs))
+               return;
+
+       VectorCopy (currententity->origin, r_entorigin);
+       VectorSubtract (r_origin, r_entorigin, modelorg);
+
+       // get lighting information
+
+       if (currententity->model->flags & EF_FULLBRIGHT || currententity->effects & EF_FULLBRIGHT)
+       {
+               shadecolor[0] = currententity->colormod[0] * 256;
+               shadecolor[1] = currententity->colormod[1] * 256;
+               shadecolor[2] = currententity->colormod[2] * 256;
+       }
+       else
+       {
+               R_LightPoint (shadecolor, currententity->origin);
+
+               // HACK HACK HACK -- no fullbright colors, so make torches full light
+               if (!strcmp (currententity->model->name, "progs/flame2.mdl") || !strcmp (currententity->model->name, "progs/flame.mdl") )
+                       shadecolor[0] = shadecolor[1] = shadecolor[2] = 128;
+
+               shadecolor[0] *= currententity->colormod[0];
+               shadecolor[1] *= currententity->colormod[1];
+               shadecolor[2] *= currententity->colormod[2];
+       }
+
+       // locate the proper data
+       if (clmodel->aliastype == ALIASTYPE_MD2)
+       {
+               pheader = (void *)Mod_Extradata (currententity->model);
+               c_alias_polys += pheader->num_tris;
+       }
+       else
+       {
+               paliashdr = (void *)Mod_Extradata (currententity->model);
+               c_alias_polys += paliashdr->numtris;
+       }
+
+       // draw all the triangles
+
+       if (clmodel->aliastype == ALIASTYPE_MD2)
+       {
+               if (currententity->skinnum < 0 || currententity->skinnum >= pheader->num_skins)
+               {
+                       currententity->skinnum = 0;
+                       Con_DPrintf("invalid skin number %d for model %s\n", currententity->skinnum, clmodel->name);
+               }
+               glBindTexture(GL_TEXTURE_2D, pheader->gl_texturenum[currententity->skinnum]);
+       }
+       else
+       {
+               if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->numskins)
+               {
+                       currententity->skinnum = 0;
+                       Con_DPrintf("invalid skin number %d for model %s\n", currententity->skinnum, clmodel->name);
+               }
+               anim = (int)(cl.time*10) & 3;
+           glBindTexture(GL_TEXTURE_2D, paliashdr->gl_texturenum[currententity->skinnum][anim]);
+       }
+       glDisable(GL_ALPHA_TEST);
+       glEnable (GL_TEXTURE_2D);
+
+       // we can't dynamically colormap textures, so they are cached
+       // seperately for the players.  Heads are just uncolored.
+       if (currententity->colormap != vid.colormap/* && !gl_nocolors.value*/)
+       {
+               i = currententity - cl_entities;
+               if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
+                   glBindTexture(GL_TEXTURE_2D, playertextures - 1 + i);
+       }
+
+//     if (gl_affinemodels.value)
+//             glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+       if (clmodel->aliastype == ALIASTYPE_MD2)
+               R_DrawQ2AliasFrame (pheader);
+       else
+               R_DrawAliasFrame (paliashdr);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+}
+
+//==================================================================================
+
+void R_DrawBrushModel (entity_t *e);
+
+/*
+=============
+R_DrawEntitiesOnList
+=============
+*/
+// LordHavoc: split so bmodels are rendered before any other objects
+void R_DrawEntitiesOnList1 (void)
+{
+       int             i;
+
+       if (!r_drawentities.value)
+               return;
+
+       for (i=0 ; i<cl_numvisedicts ; i++)
+       {
+               if (cl_visedicts[i]->model->type != mod_brush)
+                       continue;
+               currententity = cl_visedicts[i];
+               modelalpha = currententity->alpha;
+
+               R_DrawBrushModel (currententity);
+       }
+}
+
+void R_DrawEntitiesOnList2 (void)
+{
+       int             i;
+
+       if (!r_drawentities.value)
+               return;
+
+       for (i=0 ; i<cl_numvisedicts ; i++)
+       {
+               currententity = cl_visedicts[i];
+               modelalpha = currententity->alpha;
+
+               switch (currententity->model->type)
+               {
+               case mod_alias:
+                       R_DrawAliasModel (currententity, true);
+                       break;
+
+               case mod_sprite:
+                       R_DrawSpriteModel (currententity);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
+/*
+=============
+R_DrawViewModel
+=============
+*/
+void R_DrawViewModel (void)
+{
+       if (!r_drawviewmodel.value || chase_active.value || envmap || !r_drawentities.value || cl.items & IT_INVISIBILITY || cl.stats[STAT_HEALTH] <= 0 || !cl.viewent.model)
+               return;
+
+       currententity = &cl.viewent;
+       currententity->alpha = modelalpha = cl_entities[cl.viewentity].alpha; // LordHavoc: if the player is transparent, so is his gun
+       currententity->effects = cl_entities[cl.viewentity].effects;
+       currententity->scale = 1;
+       VectorCopy(cl_entities[cl.viewentity].colormod, currententity->colormod);
+
+       // hack the depth range to prevent view model from poking into walls
+       glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
+       R_DrawAliasModel (currententity, FALSE);
+       glDepthRange (gldepthmin, gldepthmax);
+}
+
+void R_DrawBrushModel (entity_t *e);
+
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees );
+
+void R_SetFrustum (void)
+{
+       int             i;
+
+       if (r_refdef.fov_x == 90) 
+       {
+               // front side is visible
+
+               VectorAdd (vpn, vright, frustum[0].normal);
+               VectorSubtract (vpn, vright, frustum[1].normal);
+
+               VectorAdd (vpn, vup, frustum[2].normal);
+               VectorSubtract (vpn, vup, frustum[3].normal);
+       }
+       else
+       {
+               // rotate VPN right by FOV_X/2 degrees
+               RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) );
+               // rotate VPN left by FOV_X/2 degrees
+               RotatePointAroundVector( frustum[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 );
+               // rotate VPN up by FOV_X/2 degrees
+               RotatePointAroundVector( frustum[2].normal, vright, vpn, 90-r_refdef.fov_y / 2 );
+               // rotate VPN down by FOV_X/2 degrees
+               RotatePointAroundVector( frustum[3].normal, vright, vpn, -( 90 - r_refdef.fov_y / 2 ) );
+       }
+
+       for (i=0 ; i<4 ; i++)
+       {
+               frustum[i].type = PLANE_ANYZ;
+               frustum[i].dist = DotProduct (r_origin, frustum[i].normal);
+//             frustum[i].signbits = SignbitsForPlane (&frustum[i]);
+               BoxOnPlaneSideClassify(&frustum[i]);
+       }
+}
+
+void R_AnimateLight (void);
+void V_CalcBlend (void);
+
+/*
+===============
+R_SetupFrame
+===============
+*/
+void R_SetupFrame (void)
+{
+// don't allow cheats in multiplayer
+       if (cl.maxclients > 1)
+               Cvar_Set ("r_fullbright", "0");
+
+       R_AnimateLight ();
+
+       r_framecount++;
+
+// build the transformation matrix for the given view angles
+       VectorCopy (r_refdef.vieworg, r_origin);
+
+       AngleVectors (r_refdef.viewangles, vpn, vright, vup);
+
+// current viewleaf
+       r_oldviewleaf = r_viewleaf;
+       r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel);
+
+       V_SetContentsColor (r_viewleaf->contents);
+       V_CalcBlend ();
+
+       r_cache_thrash = false;
+
+       c_brush_polys = 0;
+       c_alias_polys = 0;
+
+}
+
+
+void MYgluPerspective( GLdouble fovy, GLdouble aspect,
+                    GLdouble zNear, GLdouble zFar )
+{
+   GLdouble xmin, xmax, ymin, ymax;
+
+   ymax = zNear * tan( fovy * M_PI / 360.0 );
+   ymin = -ymax;
+
+   xmin = ymin * aspect;
+   xmax = ymax * aspect;
+
+   glFrustum( xmin, xmax, ymin, ymax, zNear, zFar );
+}
+
+
+extern char skyname[];
+
+/*
+=============
+R_SetupGL
+=============
+*/
+void R_SetupGL (void)
+{
+       float   screenaspect;
+       extern  int glwidth, glheight;
+       int             x, x2, y2, y, w, h;
+
+       //
+       // set up viewpoint
+       //
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+       x = r_refdef.vrect.x * glwidth/vid.width;
+       x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width;
+       y = (vid.height-r_refdef.vrect.y) * glheight/vid.height;
+       y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height;
+
+       // fudge around because of frac screen scale
+       if (x > 0)
+               x--;
+       if (x2 < glwidth)
+               x2++;
+       if (y2 < 0)
+               y2--;
+       if (y < glheight)
+               y++;
+
+       w = x2 - x;
+       h = y - y2;
+
+       if (envmap)
+       {
+               x = y2 = 0;
+               w = h = 256;
+       }
+
+       glViewport (glx + x, gly + y2, w, h);
+    screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;
+//     yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI;
+//     if (skyname[0]) // skybox enabled?
+//             MYgluPerspective (r_refdef.fov_y,  screenaspect,  4,  r_skyboxsize.value*1.732050807569 + 256); // this is size*sqrt(3) + 256
+//     else
+               MYgluPerspective (r_refdef.fov_y,  screenaspect,  4,  6144);
+
+       glCullFace(GL_FRONT);
+
+       glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity ();
+
+    glRotatef (-90,  1, 0, 0);     // put Z going up
+    glRotatef (90,  0, 0, 1);      // put Z going up
+    glRotatef (-r_refdef.viewangles[2],  1, 0, 0);
+    glRotatef (-r_refdef.viewangles[0],  0, 1, 0);
+    glRotatef (-r_refdef.viewangles[1],  0, 0, 1);
+    glTranslatef (-r_refdef.vieworg[0],  -r_refdef.vieworg[1],  -r_refdef.vieworg[2]);
+
+       glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix);
+
+       //
+       // set drawing parms
+       //
+//     if (gl_cull.value)
+               glEnable(GL_CULL_FACE);
+//     else
+//             glDisable(GL_CULL_FACE);
+
+       glEnable(GL_BLEND); // was Disable
+       glDisable(GL_ALPHA_TEST);
+       glAlphaFunc(GL_GREATER, 0.5);
+       glEnable(GL_DEPTH_TEST);
+       glDepthMask(1);
+       glShadeModel(GL_SMOOTH);
+}
+
+void R_DrawWorld (void);
+//void R_RenderDlights (void);
+void R_DrawParticles (void);
+
+/*
+=============
+R_Clear
+=============
+*/
+void R_Clear (void)
+{
+//     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // LordHavoc: moved to SCR_UpdateScreen
+       gldepthmin = 0;
+       gldepthmax = 1;
+       glDepthFunc (GL_LEQUAL);
+
+       glDepthRange (gldepthmin, gldepthmax);
+}
+
+// LordHavoc: my trick to *FIX* GLQuake lighting once and for all :)
+void GL_Brighten()
+{
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+       glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
+       glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity ();
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       glDisable(GL_TEXTURE_2D);
+       glEnable(GL_BLEND);
+       glBlendFunc (GL_DST_COLOR, GL_ONE);
+       glBegin (GL_TRIANGLES);
+       glColor3f (1, 1, 1);
+       glVertex2f (-5000, -5000);
+       glVertex2f (10000, -5000);
+       glVertex2f (-5000, 10000);
+       glEnd ();
+       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glDisable(GL_BLEND);
+       glEnable(GL_TEXTURE_2D);
+       glEnable (GL_DEPTH_TEST);
+       glEnable (GL_CULL_FACE);
+}
+
+extern cvar_t contrast;
+extern cvar_t brightness;
+extern cvar_t gl_lightmode;
+
+void GL_BlendView()
+{
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+       glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
+       glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity ();
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       glDisable(GL_TEXTURE_2D);
+       glEnable(GL_BLEND);
+       if (lighthalf)
+       {
+               glBlendFunc (GL_DST_COLOR, GL_ONE);
+               glBegin (GL_TRIANGLES);
+               glColor3f (1, 1, 1);
+               glVertex2f (-5000, -5000);
+               glVertex2f (10000, -5000);
+               glVertex2f (-5000, 10000);
+               glEnd ();
+       }
+       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       contrast.value = bound(0.2, contrast.value, 1.0);
+       if (/*gl_polyblend.value && */v_blend[3])
+       {
+               glBegin (GL_TRIANGLES);
+               glColor4fv (v_blend);
+               glVertex2f (-5000, -5000);
+               glVertex2f (10000, -5000);
+               glVertex2f (-5000, 10000);
+               glEnd ();
+       }
+
+       glEnable (GL_CULL_FACE);
+       glEnable (GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+       glEnable(GL_TEXTURE_2D);
+}
+
+#define TIMEREPORT(DESC) \
+       if (r_speeds2.value)\
+       {\
+               temptime = -currtime;\
+               currtime = Sys_FloatTime();\
+               temptime += currtime;\
+               Con_Printf(DESC " %.4fms ", temptime * 1000.0);\
+       }
+
+/*
+================
+R_RenderView
+
+r_refdef must be set before the first call
+================
+*/
+void R_RenderView (void)
+{
+       double currtime, temptime;
+//     if (r_norefresh.value)
+//             return;
+
+       if (!r_worldentity.model || !cl.worldmodel)
+               Sys_Error ("R_RenderView: NULL worldmodel");
+
+       lighthalf = gl_lightmode.value;
+
+       FOG_framebegin();
+       transpolyclear();
+       wallpolyclear();
+
+       if (r_speeds2.value)
+       {
+               currtime = Sys_FloatTime();
+               Con_Printf("render time: ");
+       }
+       R_Clear();
+       TIMEREPORT("R_Clear")
+
+       // render normal view
+
+       R_SetupFrame ();
+       TIMEREPORT("R_SetupFrame")
+       R_SetFrustum ();
+       TIMEREPORT("R_SetFrustum")
+       R_SetupGL ();
+       TIMEREPORT("R_SetupGL")
+       R_MarkLeaves ();        // done here so we know if we're in water
+       TIMEREPORT("R_MarkLeaves")
+       R_DrawWorld ();         // adds static entities to the list
+       TIMEREPORT("R_DrawWorld")
+       S_ExtraUpdate ();       // don't let sound get messed up if going slow
+       TIMEREPORT("S_ExtraUpdate")
+       R_DrawEntitiesOnList1 (); // BSP models
+       TIMEREPORT("R_DrawEntitiesOnList1")
+       wallpolyrender();
+       TIMEREPORT("wallpolyrender")
+       R_DrawEntitiesOnList2 (); // other models
+       TIMEREPORT("R_DrawEntitiesOnList2")
+//     R_RenderDlights ();
+       R_DrawViewModel ();
+       TIMEREPORT("R_DrawViewModel")
+       R_DrawParticles ();
+       TIMEREPORT("R_DrawParticles")
+       transpolyrender();
+       TIMEREPORT("transpolyrender")
+
+       FOG_frameend();
+       GL_BlendView();
+       TIMEREPORT("GL_BlendView")
+       if (r_speeds2.value)
+               Con_Printf("\n");
+}
diff --git a/gl_rmisc.c b/gl_rmisc.c
new file mode 100644 (file)
index 0000000..68c782a
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_misc.c
+
+#include "quakedef.h"
+
+
+
+/*
+==================
+R_InitTextures
+==================
+*/
+void   R_InitTextures (void)
+{
+       int             x,y, m;
+       byte    *dest;
+
+// create a simple checkerboard texture for the default
+       r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture");
+       
+       r_notexture_mip->width = r_notexture_mip->height = 16;
+       r_notexture_mip->offsets[0] = sizeof(texture_t);
+       r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16;
+       r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8;
+       r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4;
+       r_notexture_mip->transparent = FALSE;
+       
+       for (m=0 ; m<4 ; m++)
+       {
+               dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
+               for (y=0 ; y< (16>>m) ; y++)
+                       for (x=0 ; x< (16>>m) ; x++)
+                       {
+                               if (  (y< (8>>m) ) ^ (x< (8>>m) ) )
+                                       *dest++ = 0;
+                               else
+                                       *dest++ = 0xff;
+                       }
+       }       
+}
+
+/*
+===============
+R_Envmap_f
+
+Grab six views for environment mapping tests
+===============
+*/
+void R_Envmap_f (void)
+{
+       byte    buffer[256*256*4];
+
+       glDrawBuffer  (GL_FRONT);
+       glReadBuffer  (GL_FRONT);
+       envmap = true;
+
+       r_refdef.vrect.x = 0;
+       r_refdef.vrect.y = 0;
+       r_refdef.vrect.width = 256;
+       r_refdef.vrect.height = 256;
+
+       r_refdef.viewangles[0] = 0;
+       r_refdef.viewangles[1] = 0;
+       r_refdef.viewangles[2] = 0;
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       R_RenderView ();
+       glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+       COM_WriteFile ("env0.rgb", buffer, sizeof(buffer));             
+
+       r_refdef.viewangles[1] = 90;
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       R_RenderView ();
+       glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+       COM_WriteFile ("env1.rgb", buffer, sizeof(buffer));             
+
+       r_refdef.viewangles[1] = 180;
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       R_RenderView ();
+       glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+       COM_WriteFile ("env2.rgb", buffer, sizeof(buffer));             
+
+       r_refdef.viewangles[1] = 270;
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       R_RenderView ();
+       glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+       COM_WriteFile ("env3.rgb", buffer, sizeof(buffer));             
+
+       r_refdef.viewangles[0] = -90;
+       r_refdef.viewangles[1] = 0;
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       R_RenderView ();
+       glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+       COM_WriteFile ("env4.rgb", buffer, sizeof(buffer));             
+
+       r_refdef.viewangles[0] = 90;
+       r_refdef.viewangles[1] = 0;
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       R_RenderView ();
+       glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+       COM_WriteFile ("env5.rgb", buffer, sizeof(buffer));             
+
+       envmap = false;
+       glDrawBuffer  (GL_BACK);
+       glReadBuffer  (GL_BACK);
+       GL_EndRendering ();
+}
+
+void R_InitParticles (void);
+
+/*
+===============
+R_Init
+===============
+*/
+void R_Init (void)
+{      
+       extern byte *hunk_base;
+//     extern cvar_t gl_finish;
+
+       Cmd_AddCommand ("timerefresh", R_TimeRefresh_f);        
+       Cmd_AddCommand ("envmap", R_Envmap_f);  
+       Cmd_AddCommand ("pointfile", R_ReadPointFile_f);        
+
+//     Cvar_RegisterVariable (&r_norefresh);
+//     Cvar_RegisterVariable (&r_lightmap);
+       Cvar_RegisterVariable (&r_drawentities);
+       Cvar_RegisterVariable (&r_drawviewmodel);
+//     Cvar_RegisterVariable (&r_shadows);
+       Cvar_RegisterVariable (&r_wateralpha);
+//     Cvar_RegisterVariable (&r_dynamic);
+       Cvar_RegisterVariable (&r_novis);
+       Cvar_RegisterVariable (&r_speeds);
+       Cvar_RegisterVariable (&r_waterripple); // LordHavoc: added waterripple
+
+//     Cvar_RegisterVariable (&gl_cull);
+//     Cvar_RegisterVariable (&gl_affinemodels);
+//     Cvar_RegisterVariable (&gl_polyblend);
+//     Cvar_RegisterVariable (&gl_flashblend);
+       Cvar_RegisterVariable (&gl_playermip);
+//     Cvar_RegisterVariable (&gl_nocolors);
+
+//     Cvar_RegisterVariable (&gl_keeptjunctions);
+//     Cvar_RegisterVariable (&gl_reporttjunctions);
+
+       R_InitParticles ();
+
+       playertextures = texture_extension_number;
+       texture_extension_number += 64; // LordHavoc: increased number of players from 16 to 64
+}
+
+qboolean VID_Is8bit(void);
+void GL_Upload8_EXT (byte *data, int width, int height,  qboolean mipmap, qboolean alpha);
+
+/*
+===============
+R_TranslatePlayerSkin
+
+Translates a skin texture by the per-player color lookup
+===============
+*/
+void R_TranslatePlayerSkin (int playernum)
+{
+       int             top, bottom;
+       byte    translate[256];
+       unsigned        translate32[256];
+       int             i, j, s;
+       model_t *model;
+       aliashdr_t *paliashdr;
+       byte    *original;
+       unsigned        pixels[512*256], *out;
+       unsigned        scaled_width, scaled_height;
+       int                     inwidth, inheight;
+       byte            *inrow;
+       unsigned        frac, fracstep;
+       extern  byte            **player_8bit_texels_tbl;
+
+       top = cl.scores[playernum].colors & 0xf0;
+       bottom = (cl.scores[playernum].colors &15)<<4;
+
+       for (i=0 ; i<256 ; i++)
+               translate[i] = i;
+
+       for (i=0 ; i<16 ; i++)
+       {
+               // LordHavoc: corrected color ranges
+               if (top < 128 || (top >= 224 && top < 240))     // the artists made some backwards ranges.  sigh.
+                       translate[TOP_RANGE+i] = top+i;
+               else
+                       translate[TOP_RANGE+i] = top+15-i;
+
+               // LordHavoc: corrected color ranges
+               if (bottom < 128 || (bottom >= 224 && bottom < 240))
+                       translate[BOTTOM_RANGE+i] = bottom+i;
+               else
+                       translate[BOTTOM_RANGE+i] = bottom+15-i;
+       }
+
+       //
+       // locate the original skin pixels
+       //
+       currententity = &cl_entities[1+playernum];
+       model = currententity->model;
+       if (!model)
+               return;         // player doesn't have a model yet
+       if (model->type != mod_alias)
+               return; // only translate skins on alias models
+
+       paliashdr = (aliashdr_t *)Mod_Extradata (model);
+       s = paliashdr->skinwidth * paliashdr->skinheight;
+       if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->numskins)
+       {
+               Con_Printf("(%d): Invalid player skin #%d\n", playernum, currententity->skinnum);
+               original = (byte *)paliashdr + paliashdr->texels[0];
+       }
+       else
+               original = (byte *)paliashdr + paliashdr->texels[currententity->skinnum];
+       if (s & 3)
+               Sys_Error ("R_TranslateSkin: s&3");
+
+       inwidth = paliashdr->skinwidth;
+       inheight = paliashdr->skinheight;
+
+       // because this happens during gameplay, do it fast
+       // instead of sending it through gl_upload 8
+    glBindTexture(GL_TEXTURE_2D, playertextures + playernum);
+
+#if 0
+       byte    translated[320*200];
+
+       for (i=0 ; i<s ; i+=4)
+       {
+               translated[i] = translate[original[i]];
+               translated[i+1] = translate[original[i+1]];
+               translated[i+2] = translate[original[i+2]];
+               translated[i+3] = translate[original[i+3]];
+       }
+
+
+       // don't mipmap these, because it takes too long
+       GL_Upload8 (translated, paliashdr->skinwidth, paliashdr->skinheight, false, false, true);
+#else
+       scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512;
+       scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256;
+
+       // allow users to crunch sizes down even more if they want
+       scaled_width >>= (int)gl_playermip.value;
+       scaled_height >>= (int)gl_playermip.value;
+
+       if (VID_Is8bit())
+       { // 8bit texture upload
+               byte *out2;
+
+               out2 = (byte *)pixels;
+               memset(pixels, 0, sizeof(pixels));
+               fracstep = inwidth*0x10000/scaled_width;
+               for (i=0 ; i<scaled_height ; i++, out2 += scaled_width)
+               {
+                       inrow = original + inwidth*(i*inheight/scaled_height);
+                       frac = fracstep >> 1;
+                       for (j=0 ; j<scaled_width ; j+=4)
+                       {
+                               out2[j] = translate[inrow[frac>>16]];
+                               frac += fracstep;
+                               out2[j+1] = translate[inrow[frac>>16]];
+                               frac += fracstep;
+                               out2[j+2] = translate[inrow[frac>>16]];
+                               frac += fracstep;
+                               out2[j+3] = translate[inrow[frac>>16]];
+                               frac += fracstep;
+                       }
+               }
+
+               GL_Upload8_EXT ((byte *)pixels, scaled_width, scaled_height, false, false);
+               return;
+       }
+
+       for (i=0 ; i<256 ; i++)
+               translate32[i] = d_8to24table[translate[i]];
+
+       out = pixels;
+       fracstep = inwidth*0x10000/scaled_width;
+       for (i=0 ; i<scaled_height ; i++, out += scaled_width)
+       {
+               inrow = original + inwidth*(i*inheight/scaled_height);
+               frac = fracstep >> 1;
+               for (j=0 ; j<scaled_width ; j+=4)
+               {
+                       out[j] = translate32[inrow[frac>>16]];
+                       frac += fracstep;
+                       out[j+1] = translate32[inrow[frac>>16]];
+                       frac += fracstep;
+                       out[j+2] = translate32[inrow[frac>>16]];
+                       frac += fracstep;
+                       out[j+3] = translate32[inrow[frac>>16]];
+                       frac += fracstep;
+               }
+       }
+       glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+#endif
+
+}
+
+void R_ClearParticles (void);
+void GL_BuildLightmaps (void);
+
+/*
+===============
+R_NewMap
+===============
+*/
+void SHOWLMP_clear();
+void R_NewMap (void)
+{
+       int             i;
+       
+       for (i=0 ; i<256 ; i++)
+               d_lightstylevalue[i] = 264;             // normal light value
+
+       memset (&r_worldentity, 0, sizeof(r_worldentity));
+       r_worldentity.model = cl.worldmodel;
+       currententity = &r_worldentity;
+
+// clear out efrags in case the level hasn't been reloaded
+// FIXME: is this one short?
+       for (i=0 ; i<cl.worldmodel->numleafs ; i++)
+               cl.worldmodel->leafs[i].efrags = NULL;
+                       
+       r_viewleaf = NULL;
+       R_ClearParticles ();
+
+       GL_BuildLightmaps ();
+
+       // identify sky texture
+       skytexturenum = -1;
+       for (i=0 ; i<cl.worldmodel->numtextures ; i++)
+       {
+               if (!cl.worldmodel->textures[i])
+                       continue;
+               if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) )
+                       skytexturenum = i;
+               cl.worldmodel->textures[i]->texturechain = NULL;
+       }
+       SHOWLMP_clear();
+}
+
+
+/*
+====================
+R_TimeRefresh_f
+
+For program optimization
+====================
+*/
+void R_TimeRefresh_f (void)
+{
+       int                     i;
+       float           start, stop, time;
+
+       start = Sys_FloatTime ();
+       for (i=0 ; i<128 ; i++)
+       {
+               r_refdef.viewangles[1] = i/128.0*360.0;
+               SCR_UpdateScreen();
+       }
+
+       stop = Sys_FloatTime ();
+       time = stop-start;
+       Con_Printf ("%f seconds (%f fps)\n", time, 128/time);
+}
+
+void D_FlushCaches (void)
+{
+}
+
+
diff --git a/gl_rsurf.c b/gl_rsurf.c
new file mode 100644 (file)
index 0000000..2af75cd
--- /dev/null
@@ -0,0 +1,1586 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_surf.c: surface-related refresh code
+
+#include "quakedef.h"
+
+int                    skytexturenum;
+
+int            lightmap_textures;
+
+signed blocklights[18*18*3]; // LordHavoc: *3 for colored lighting
+
+// LordHavoc: skinny but tall lightmaps for quicker subimage uploads
+#define        BLOCK_WIDTH             128
+#define        BLOCK_HEIGHT    128
+// LordHavoc: increased lightmap limit from 64 to 1024
+#define        MAX_LIGHTMAPS   1024
+#define LIGHTMAPSIZE   (BLOCK_WIDTH*BLOCK_HEIGHT*3)
+
+int                    active_lightmaps;
+
+short allocated[MAX_LIGHTMAPS][BLOCK_WIDTH];
+
+byte *lightmaps[MAX_LIGHTMAPS];
+
+int lightmapalign, lightmapalignmask; // LordHavoc: NVIDIA's broken subimage fix, see BuildLightmaps for notes
+cvar_t gl_lightmapalign = {"gl_lightmapalign", "4"};
+cvar_t gl_lightmaprgba = {"gl_lightmaprgba", "1"};
+cvar_t gl_nosubimagefragments = {"gl_nosubimagefragments", "0"};
+
+qboolean lightmaprgba, nosubimagefragments;
+int lightmapbytes;
+
+qboolean skyisvisible;
+extern qboolean gl_arrays;
+
+void glrsurf_init()
+{
+       int i;
+       for (i = 0;i < MAX_LIGHTMAPS;i++)
+               lightmaps[i] = (byte *) NULL;
+       Cvar_RegisterVariable(&gl_lightmapalign);
+       Cvar_RegisterVariable(&gl_lightmaprgba);
+       Cvar_RegisterVariable(&gl_nosubimagefragments);
+       // check if it's the glquake minigl driver
+       if (strnicmp(gl_vendor,"3Dfx",4)==0)
+       if (!gl_arrays)
+       {
+               Cvar_SetValue("gl_nosubimagefragments", 1);
+               Cvar_SetValue("gl_lightmode", 0);
+       }
+}
+
+int dlightdivtable[8192];
+int dlightdivtableinitialized = 0;
+
+/*
+===============
+R_AddDynamicLights
+===============
+*/
+void R_AddDynamicLights (msurface_t *surf)
+{
+       int                     sdtable[18], lnum, td, maxdist, maxdist2, maxdist3, i, s, t, smax, tmax, red, green, blue, j;
+       unsigned        *bl;
+       float           dist, f;
+       vec3_t          impact, local;
+       // use 64bit integer...  shame it's not very standardized...
+#if _MSC_VER
+       __int64         k; // MSVC
+#else
+       long long       k; // GCC
+#endif
+
+       if (!dlightdivtableinitialized)
+       {
+               dlightdivtable[0] = 1048576 >> 7;
+               for (s = 1;s < 8192;s++)
+                       dlightdivtable[s] = 1048576 / (s << 7);
+               dlightdivtableinitialized = 1;
+       }
+
+       smax = (surf->extents[0]>>4)+1;
+       tmax = (surf->extents[1]>>4)+1;
+
+       for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
+       {
+               if ( !(surf->dlightbits[lnum >> 5] & (1<<(lnum&31)) ) )
+                       continue;               // not lit by this light
+
+               VectorSubtract(cl_dlights[lnum].origin, currententity->origin, local);
+               dist = DotProduct (local, surf->plane->normal) - surf->plane->dist;
+               for (i=0 ; i<3 ; i++)
+                       impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist;
+
+               f = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
+               i = f;
+
+               // reduce calculations
+               t = dist*dist;
+               for (s = 0;s < smax;s++, i -= 16)
+                       sdtable[s] = i*i + t;
+
+               f = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
+               i = f;
+
+               maxdist = (int) (cl_dlights[lnum].radius*cl_dlights[lnum].radius); // for comparisons to minimum acceptable light
+               // clamp radius to avoid exceeding 8192 entry division table
+               if (maxdist > 1048576)
+                       maxdist = 1048576;
+               maxdist3 = maxdist - (int) (dist*dist);
+               // convert to 8.8 blocklights format
+               if (!cl_dlights[lnum].dark)
+               {
+                       f = cl_dlights[lnum].color[0] * maxdist;red = f;
+                       f = cl_dlights[lnum].color[1] * maxdist;green = f;
+                       f = cl_dlights[lnum].color[2] * maxdist;blue = f;
+               }
+               else // negate for darklight
+               {
+                       f = cl_dlights[lnum].color[0] * -maxdist;red = f;
+                       f = cl_dlights[lnum].color[1] * -maxdist;green = f;
+                       f = cl_dlights[lnum].color[2] * -maxdist;blue = f;
+               }
+               bl = blocklights;
+               for (t = 0;t < tmax;t++,i -= 16)
+               {
+                       td = i*i;
+                       if (td < maxdist3) // make sure some part of it is visible on this line
+                       {
+                               maxdist2 = maxdist - td;
+                               for (s = 0;s < smax;s++)
+                               {
+                                       if (sdtable[s] < maxdist2)
+                                       {
+                                               j = dlightdivtable[(sdtable[s]+td) >> 7];
+                                               k = (red   * j) >> 8;bl[0] += k;
+                                               k = (green * j) >> 8;bl[1] += k;
+                                               k = (blue  * j) >> 8;bl[2] += k;
+                                       }
+                                       bl += 3;
+                               }
+                       }
+                       else
+                               bl+=smax*3; // skip line
+               }
+       }
+}
+
+extern qboolean lighthalf;
+/*
+===============
+R_BuildLightMap
+
+Combine and scale multiple lightmaps into the 8.8 format in blocklights
+===============
+*/
+void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
+{
+       int                     smax, tmax;
+       int                     t;
+       int                     i, j, size;
+       byte            *lightmap;
+       int                     scale;
+       int                     maps;
+       int                     *bl;
+
+       surf->cached_dlight = (surf->dlightframe == r_framecount);
+       surf->cached_lighthalf = lighthalf;
+
+       smax = (surf->extents[0]>>4)+1;
+       tmax = (surf->extents[1]>>4)+1;
+       size = smax*tmax;
+       lightmap = surf->samples;
+
+// set to full bright if no light data
+       if (currententity->effects & EF_FULLBRIGHT || !cl.worldmodel->lightdata)
+       {
+               bl = blocklights;
+               for (i=0 ; i<size ; i++)
+               {
+                       *bl++ = 255*256;
+                       *bl++ = 255*256;
+                       *bl++ = 255*256;
+               }
+       }
+       else
+       {
+// clear to no light
+               bl = blocklights;
+               for (i=0 ; i<size ; i++)
+               {
+                       *bl++ = 0;
+                       *bl++ = 0;
+                       *bl++ = 0;
+               }
+
+// add all the lightmaps
+               if (lightmap)
+                       for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
+                       {
+                               scale = d_lightstylevalue[surf->styles[maps]];
+                               surf->cached_light[maps] = scale;       // 8.8 fraction
+                               bl = blocklights;
+                               for (i=0 ; i<size ; i++)
+                               {
+                                       *bl++ += *lightmap++ * scale;
+                                       *bl++ += *lightmap++ * scale;
+                                       *bl++ += *lightmap++ * scale;
+                               }
+                       }
+
+// add all the dynamic lights
+               if (surf->dlightframe == r_framecount)
+                       R_AddDynamicLights (surf);
+       }
+       stride -= (smax*lightmapbytes);
+       bl = blocklights;
+       if (lighthalf)
+       {
+               // LordHavoc: I shift down by 8 unlike GLQuake's 7,
+               // the image is brightened as a processing pass
+               if (lightmaprgba)
+               {
+                       for (i=0 ; i<tmax ; i++, dest += stride)
+                       {
+                               for (j=0 ; j<smax ; j++)
+                               {
+                                       t = *bl++ >> 8;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 8;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 8;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       *dest++ = 255;
+                               }
+                       }
+               }
+               else
+               {
+                       for (i=0 ; i<tmax ; i++, dest += stride)
+                       {
+                               for (j=0 ; j<smax ; j++)
+                               {
+                                       t = *bl++ >> 8;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 8;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 8;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                               }
+                       }
+               }
+       }
+       else
+       {
+               if (lightmaprgba)
+               {
+                       for (i=0 ; i<tmax ; i++, dest += stride)
+                       {
+                               for (j=0 ; j<smax ; j++)
+                               {
+                                       t = *bl++ >> 7;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 7;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 7;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       *dest++ = 255;
+                               }
+                       }
+               }
+               else
+               {
+                       for (i=0 ; i<tmax ; i++, dest += stride)
+                       {
+                               for (j=0 ; j<smax ; j++)
+                               {
+                                       t = *bl++ >> 7;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 7;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                                       t = *bl++ >> 7;if (t > 255) t = 255;else if (t < 0) t = 0;*dest++ = t;
+                               }
+                       }
+               }
+       }
+}
+
+byte templight[32*32*4];
+
+void R_UpdateLightmap(msurface_t *s, int lnum)
+{
+       int smax, tmax;
+       // upload the new lightmap texture fragment
+       glBindTexture(GL_TEXTURE_2D, lightmap_textures + lnum);
+       if (nosubimagefragments)
+       {
+               smax = (s->extents[0]>>4)+1;
+               tmax = (s->extents[1]>>4)+1;
+               if (lightmaprgba)
+               {
+                       R_BuildLightMap (s, lightmaps[s->lightmaptexturenum] + (s->light_t * BLOCK_WIDTH + s->light_s) * 4, BLOCK_WIDTH * 4);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, s->light_t, BLOCK_WIDTH, tmax, GL_RGBA, GL_UNSIGNED_BYTE, lightmaps[s->lightmaptexturenum] + s->light_t * (BLOCK_WIDTH * 4));
+               }
+               else
+               {
+                       R_BuildLightMap (s, lightmaps[s->lightmaptexturenum] + (s->light_t * BLOCK_WIDTH + s->light_s) * 3, BLOCK_WIDTH * 3);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, s->light_t, BLOCK_WIDTH, tmax, GL_RGB , GL_UNSIGNED_BYTE, lightmaps[s->lightmaptexturenum] + s->light_t * (BLOCK_WIDTH * 3));
+               }
+       }
+       else
+       {
+               smax = ((s->extents[0]>>4)+lightmapalign) & lightmapalignmask;
+               tmax = (s->extents[1]>>4)+1;
+               if (lightmaprgba)
+               {
+                       R_BuildLightMap (s, templight, smax * 4);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, s->light_s, s->light_t, smax, tmax, GL_RGBA, GL_UNSIGNED_BYTE, templight);
+               }
+               else
+               {
+                       R_BuildLightMap (s, templight, smax * 3);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, s->light_s, s->light_t, smax, tmax, GL_RGB , GL_UNSIGNED_BYTE, templight);
+               }
+       }
+}
+
+
+/*
+===============
+R_TextureAnimation
+
+Returns the proper texture for a given time and base texture
+===============
+*/
+texture_t *R_TextureAnimation (texture_t *base)
+{
+       texture_t *original;
+       int             relative;
+       int             count;
+
+       if (currententity->frame)
+       {
+               if (base->alternate_anims)
+                       base = base->alternate_anims;
+       }
+       
+       if (!base->anim_total)
+               return base;
+
+       original = base;
+
+       relative = (int)(cl.time*10) % base->anim_total;
+
+       count = 0;      
+       while (base->anim_min > relative || base->anim_max <= relative)
+       {
+               base = base->anim_next;
+               if (!base)
+               {
+                       Con_Printf("R_TextureAnimation: broken cycle");
+                       return original;
+               }
+               if (++count > 100)
+               {
+                       Con_Printf("R_TextureAnimation: infinite cycle");
+                       return original;
+               }
+       }
+
+       return base;
+}
+
+
+/*
+=============================================================
+
+       BRUSH MODELS
+
+=============================================================
+*/
+
+
+extern int             solidskytexture;
+extern int             alphaskytexture;
+extern float   speedscale;             // for top sky and bottom sky
+
+qboolean mtexenabled = false;
+
+extern char skyname[];
+
+void R_DynamicLightPoint(vec3_t color, vec3_t org, int *dlightbits);
+//extern cvar_t r_dynamicwater;
+extern int r_dlightframecount;
+float  turbsin[256] =
+{
+       #include "gl_warp_sin.h"
+};
+#define TURBSCALE (256.0 / (2 * M_PI))
+
+
+/*
+================
+DrawTextureChains
+================
+*/
+extern qboolean hlbsp;
+void DrawTextureChains (void)
+{
+       int             i, j, maps;
+       msurface_t      *s;
+       texture_t       *t;
+       glpoly_t        *p;
+       float           *v;
+       float           os = turbsin[(int)(realtime * TURBSCALE) & 255], ot = turbsin[(int)(realtime * TURBSCALE + 96.0) & 255];
+
+       for (j = 0;j < cl.worldmodel->numtextures;j++)
+       {
+               if (!cl.worldmodel->textures[j] || !(s = cl.worldmodel->textures[j]->texturechain))
+                       continue;
+               cl.worldmodel->textures[j]->texturechain = NULL;
+               t = R_TextureAnimation (cl.worldmodel->textures[j]);
+               // LordHavoc: decide the render type only once, because the surface properties were determined by texture anyway
+               // subdivided water surface warp
+               if (s->flags & SURF_DRAWTURB)
+               {
+                       int light, alpha, r, g, b;
+                       vec3_t nv, shadecolor;
+                       alpha = s->flags & SURF_DRAWNOALPHA ? 255 : r_wateralpha.value*255.0f;
+                       light = false;
+                       if (s->flags & SURF_DRAWFULLBRIGHT)
+                               r = g = b = lighthalf ? 128 : 255;
+                       else if (s->dlightframe == r_dlightframecount/* && r_dynamicwater.value*/)
+                               light = true;
+                       else
+                               r = g = b = lighthalf ? 64 : 128;
+                       if (r_waterripple.value)
+                       {
+                               if (lighthalf)
+                               {
+                                       if (light)
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       nv[0] = v[0];
+                                                                       nv[1] = v[1];
+                                                                       nv[2] = v[2] + r_waterripple.value * turbsin[(int)((v[3]*0.125f+realtime) * TURBSCALE) & 255] * turbsin[(int)((v[4]*0.125f+realtime) * TURBSCALE) & 255] * (1.0f / 64.0f);
+                                                                       shadecolor[0] = shadecolor[1] = shadecolor[2] = 128;
+                                                                       R_DynamicLightPoint(shadecolor, nv, s->dlightbits);
+                                                                       transpolyvert(nv[0], nv[1], nv[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), (int) shadecolor[0] >> 1,(int) shadecolor[1] >> 1,(int) shadecolor[2] >> 1,alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       nv[0] = v[0];
+                                                                       nv[1] = v[1];
+                                                                       nv[2] = v[2] + r_waterripple.value * turbsin[(int)((v[3]*0.125f+realtime) * TURBSCALE) & 255] * turbsin[(int)((v[4]*0.125f+realtime) * TURBSCALE) & 255] * (1.0f / 64.0f);
+                                                                       transpolyvert(nv[0], nv[1], nv[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), r,g,b,alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (light)
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       nv[0] = v[0];
+                                                                       nv[1] = v[1];
+                                                                       nv[2] = v[2] + r_waterripple.value * turbsin[(int)((v[3]*0.125f+realtime) * TURBSCALE) & 255] * turbsin[(int)((v[4]*0.125f+realtime) * TURBSCALE) & 255] * (1.0f / 64.0f);
+                                                                       shadecolor[0] = shadecolor[1] = shadecolor[2] = 128;
+                                                                       R_DynamicLightPoint(shadecolor, nv, s->dlightbits);
+                                                                       transpolyvert(nv[0], nv[1], nv[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), shadecolor[0],shadecolor[1],shadecolor[2],alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       nv[0] = v[0];
+                                                                       nv[1] = v[1];
+                                                                       nv[2] = v[2] + r_waterripple.value * turbsin[(int)((v[3]*0.125f+realtime) * TURBSCALE) & 255] * turbsin[(int)((v[4]*0.125f+realtime) * TURBSCALE) & 255] * (1.0f / 64.0f);
+                                                                       transpolyvert(nv[0], nv[1], nv[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), r,g,b,alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (lighthalf)
+                               {
+                                       if (light)
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       shadecolor[0] = shadecolor[1] = shadecolor[2] = 128;
+                                                                       R_DynamicLightPoint(shadecolor, v, s->dlightbits);
+                                                                       transpolyvert(v[0], v[1], v[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), (int) shadecolor[0] >> 1,(int) shadecolor[1] >> 1,(int) shadecolor[2] >> 1,alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       transpolyvert(v[0], v[1], v[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), r,g,b,alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (light)
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       shadecolor[0] = shadecolor[1] = shadecolor[2] = 128;
+                                                                       R_DynamicLightPoint(shadecolor, v, s->dlightbits);
+                                                                       transpolyvert(v[0], v[1], v[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), shadecolor[0],shadecolor[1],shadecolor[2],alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               for (;s;s = s->texturechain)
+                                               {
+                                                       for (p=s->polys ; p ; p=p->next)
+                                                       {
+                                                               // FIXME: could be a transparent water texture
+                                                               transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, TPOLYTYPE_ALPHA);
+                                                               for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                               {
+                                                                       transpolyvert(v[0], v[1], v[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), r,g,b,alpha);
+                                                               }
+                                                               transpolyend();
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else if (s->flags & SURF_DRAWSKY)
+               {
+                       skyisvisible = true;
+                       if (!hlbsp) // LordHavoc: HalfLife maps have freaky skypolys...
+                       {
+                               for (;s;s = s->texturechain)
+                               {
+                                       for (p=s->polys ; p ; p=p->next)
+                                       {
+                                               if (currentskypoly < MAX_SKYPOLYS && currentskyvert + p->numverts <= MAX_SKYVERTS)
+                                               {
+                                                       skypoly[currentskypoly].firstvert = currentskyvert;
+                                                       skypoly[currentskypoly++].verts = p->numverts;
+                                                       for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                                       {
+                                                               skyvert[currentskyvert].v[0] = v[0];
+                                                               skyvert[currentskyvert].v[1] = v[1];
+                                                               skyvert[currentskyvert++].v[2] = v[2];
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else // normal wall
+               {
+                       c_brush_polys++;
+                       for (;s;s = s->texturechain)
+                       {
+                               if (currentwallpoly < MAX_WALLPOLYS && currentwallvert < MAX_WALLVERTS && (currentwallvert + s->polys->numverts) <= MAX_WALLVERTS)
+                               {
+                                       // check for lightmap modification
+//                                     if (r_dynamic.value)
+//                                     {
+                                               if (s->dlightframe == r_framecount || s->cached_dlight || lighthalf != s->cached_lighthalf) // dynamic this frame or previously, or lighthalf changed
+                                                       R_UpdateLightmap(s, s->lightmaptexturenum);
+                                               else
+                                                       for (maps = 0 ; maps < MAXLIGHTMAPS && s->styles[maps] != 255 ; maps++)
+                                                               if (d_lightstylevalue[s->styles[maps]] != s->cached_light[maps])
+                                                               {
+                                                                       R_UpdateLightmap(s, s->lightmaptexturenum);
+                                                                       break;
+                                                               }
+//                                     }
+                                       wallpoly[currentwallpoly].texnum = (unsigned short) t->gl_texturenum;
+                                       wallpoly[currentwallpoly].lighttexnum = (unsigned short) lightmap_textures + s->lightmaptexturenum;
+                                       wallpoly[currentwallpoly].glowtexnum = (unsigned short) t->gl_glowtexturenum;
+                                       wallpoly[currentwallpoly].firstvert = currentwallvert;
+                                       wallpoly[currentwallpoly++].verts = s->polys->numverts;
+                                       for (i = 0,v = s->polys->verts[0];i<s->polys->numverts;i++, v += VERTEXSIZE)
+                                       {
+                                               wallvert[currentwallvert].vert[0] = v[0];
+                                               wallvert[currentwallvert].vert[1] = v[1];
+                                               wallvert[currentwallvert].vert[2] = v[2];
+                                               wallvert[currentwallvert].s = v[3];
+                                               wallvert[currentwallvert].t = v[4];
+                                               wallvert[currentwallvert].u = v[5];
+                                               wallvert[currentwallvert++].v = v[6];
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+// LordHavoc: transparent brush models
+extern int r_dlightframecount;
+extern float modelalpha;
+extern vec3_t shadecolor;
+//qboolean R_CullBox (vec3_t mins, vec3_t maxs);
+void R_DynamicLightPoint(vec3_t color, vec3_t org, int *dlightbits);
+void R_DynamicLightPointNoMask(vec3_t color, vec3_t org);
+void EmitWaterPolys (msurface_t *fa);
+void R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex, mnode_t *node);
+
+/*
+=================
+R_DrawBrushModel
+=================
+*/
+void R_DrawBrushModel (entity_t *e)
+{
+       int                     i, j, k, smax, tmax, size3, maps;
+       vec3_t          mins, maxs, nv;
+       msurface_t      *s;
+       mplane_t        *pplane;
+       model_t         *clmodel;
+       qboolean        rotated, vertexlit = false;
+       float           dot, *v, scale;
+       texture_t       *t;
+       byte            *lm;
+       float           os = turbsin[(int)(realtime * TURBSCALE) & 255], ot = turbsin[(int)(realtime * TURBSCALE + 96.0) & 255];
+
+       currententity = e;
+
+       clmodel = e->model;
+
+       if (e->angles[0] || e->angles[1] || e->angles[2])
+       {
+               rotated = true;
+               for (i=0 ; i<3 ; i++)
+               {
+                       mins[i] = e->origin[i] - clmodel->radius;
+                       maxs[i] = e->origin[i] + clmodel->radius;
+               }
+       }
+       else
+       {
+               rotated = false;
+               VectorAdd (e->origin, clmodel->mins, mins);
+               VectorAdd (e->origin, clmodel->maxs, maxs);
+       }
+
+       if (R_CullBox (mins, maxs))
+               return;
+
+       VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
+       if (rotated)
+       {
+               vec3_t  temp;
+               vec3_t  forward, right, up;
+
+               VectorCopy (modelorg, temp);
+               AngleVectors (e->angles, forward, right, up);
+               modelorg[0] = DotProduct (temp, forward);
+               modelorg[1] = -DotProduct (temp, right);
+               modelorg[2] = DotProduct (temp, up);
+       }
+
+       s = &clmodel->surfaces[clmodel->firstmodelsurface];
+
+// calculate dynamic lighting for bmodel if it's not an
+// instanced model
+       if (modelalpha == 1 && clmodel->firstmodelsurface != 0 && !(currententity->effects & EF_FULLBRIGHT) && currententity->colormod[0] == 1 && currententity->colormod[2] == 1 && currententity->colormod[2] == 1)
+       {
+//             if (!gl_flashblend.value)
+//             {
+                       vec3_t org;
+                       for (k=0 ; k<MAX_DLIGHTS ; k++)
+                       {
+                               if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius))
+                                       continue;
+
+                               VectorSubtract(cl_dlights[k].origin, currententity->origin, org);
+                               R_MarkLights (org, &cl_dlights[k], 1<<(k&31), k >> 5, clmodel->nodes + clmodel->hulls[0].firstclipnode);
+                       }
+//             }
+       }
+       else
+               vertexlit = true;
+
+e->angles[0] = -e->angles[0];  // stupid quake bug
+       softwaretransformforentity (e);
+e->angles[0] = -e->angles[0];  // stupid quake bug
+
+       // draw texture
+       for (j = 0;j < clmodel->nummodelsurfaces;j++, s++)
+       {
+       // find which side of the node we are on
+               pplane = s->plane;
+
+               dot = DotProduct (modelorg, pplane->normal) - pplane->dist;
+
+       // draw the polygon
+               if (((s->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
+                       (!(s->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
+               {
+                       if (s->flags & SURF_DRAWSKY)
+                               continue;
+                       if (s->flags & SURF_DRAWTURB)
+                       {
+                               glpoly_t        *p;
+                               int                     light, alpha, r, g, b;
+                               vec3_t          shadecolor;
+
+                               if (s->flags & SURF_DRAWNOALPHA)
+                                       alpha = modelalpha*255.0f;
+                               else
+                                       alpha = r_wateralpha.value*modelalpha*255.0f;
+                               light = false;
+                               if (s->flags & SURF_DRAWFULLBRIGHT || currententity->effects & EF_FULLBRIGHT)
+                               {
+                                       if (lighthalf)
+                                       {
+                                               r = 128.0f * currententity->colormod[0];
+                                               g = 128.0f * currententity->colormod[1];
+                                               b = 128.0f * currententity->colormod[2];
+                                       }
+                                       else
+                                       {
+                                               r = 255.0f * currententity->colormod[0];
+                                               g = 255.0f * currententity->colormod[1];
+                                               b = 255.0f * currententity->colormod[2];
+                                       }
+                               }
+                               else if (s->dlightframe == r_dlightframecount/* && r_dynamicwater.value*/)
+                                       light = true;
+                               else
+                               {
+                                       if (lighthalf)
+                                       {
+                                               r = 64.0f * currententity->colormod[0];
+                                               g = 64.0f * currententity->colormod[1];
+                                               b = 64.0f * currententity->colormod[2];
+                                       }
+                                       else
+                                       {
+                                               r = 128.0f * currententity->colormod[0];
+                                               g = 128.0f * currententity->colormod[1];
+                                               b = 128.0f * currententity->colormod[2];
+                                       }
+                               }
+                               for (p=s->polys ; p ; p=p->next)
+                               {
+                                       // FIXME: could be a transparent water texture
+                                       transpolybegin(s->texinfo->texture->gl_texturenum, s->texinfo->texture->gl_glowtexturenum, 0, currententity->effects & EF_ADDITIVE ? TPOLYTYPE_ADD : TPOLYTYPE_ALPHA);
+                                       for (i = 0,v = p->verts[0];i < p->numverts;i++, v += VERTEXSIZE)
+                                       {
+                                               softwaretransform(v, nv);
+                                               if (r_waterripple.value)
+                                                       nv[2] += r_waterripple.value * turbsin[(int)((v[3]*0.125f+realtime) * TURBSCALE) & 255] * turbsin[(int)((v[4]*0.125f+realtime) * TURBSCALE) & 255] * (1.0f / 64.0f);
+                                               if (light)
+                                               {
+                                                       shadecolor[0] = shadecolor[1] = shadecolor[2] = 128;
+                                                       R_DynamicLightPoint(shadecolor, nv, s->dlightbits);
+                                                       if (lighthalf)
+                                                       {
+                                                               r = (int) ((float) (shadecolor[0] * currententity->colormod[0])) >> 1;
+                                                               g = (int) ((float) (shadecolor[1] * currententity->colormod[1])) >> 1;
+                                                               b = (int) ((float) (shadecolor[2] * currententity->colormod[2])) >> 1;
+                                                       }
+                                                       else
+                                                       {
+                                                               r = (int) ((float) (shadecolor[0] * currententity->colormod[0]));
+                                                               g = (int) ((float) (shadecolor[1] * currententity->colormod[1]));
+                                                               b = (int) ((float) (shadecolor[2] * currententity->colormod[2]));
+                                                       }
+                                               }
+                                               transpolyvert(nv[0], nv[1], nv[2], (v[3] + os) * (1.0f/64.0f), (v[4] + ot) * (1.0f/64.0f), r,g,b,alpha);
+                                       }
+                                       transpolyend();
+                               }
+                               continue;
+                       }
+                       c_brush_polys++;
+                       t = R_TextureAnimation (s->texinfo->texture);
+                       v = s->polys->verts[0];
+                       if (vertexlit || s->texinfo->texture->transparent)
+                       {
+                               // FIXME: could be a transparent water texture
+                               transpolybegin(t->gl_texturenum, t->gl_glowtexturenum, 0, currententity->effects & EF_ADDITIVE ? TPOLYTYPE_ADD : TPOLYTYPE_ALPHA);
+                               if ((currententity->effects & EF_FULLBRIGHT) || !s->samples)
+                               {
+                                       for (i = 0;i < s->polys->numverts;i++, v += VERTEXSIZE)
+                                       {
+                                               softwaretransform(v, nv);
+                                               transpolyvert(nv[0], nv[1], nv[2], v[3], v[4], 255,255,255,modelalpha*255.0f);
+                                       }
+                               }
+                               else
+                               {
+                                       smax = (s->extents[0]>>4)+1;
+                                       tmax = (s->extents[1]>>4)+1;
+                                       size3 = smax*tmax*3; // *3 for colored lighting
+                                       for (i = 0;i < s->polys->numverts;i++, v += VERTEXSIZE)
+                                       {
+                                               shadecolor[0] = shadecolor[1] = shadecolor[2] = 0;
+                                               lm = (byte *)((long) s->samples + ((int) v[8] * smax + (int) v[7]) * 3); // LordHavoc: *3 for colored lighting
+                                               for (maps = 0;maps < MAXLIGHTMAPS && s->styles[maps] != 255;maps++)
+                                               {
+                                                       scale = d_lightstylevalue[s->styles[maps]] * (1.0 / 128.0);
+                                                       shadecolor[0] += lm[0] * scale;
+                                                       shadecolor[1] += lm[1] * scale;
+                                                       shadecolor[2] += lm[2] * scale;
+                                                       lm += size3; // LordHavoc: *3 for colored lighting
+                                               }
+                                               softwaretransform(v, nv);
+                                               R_DynamicLightPointNoMask(shadecolor, nv); // LordHavoc: dynamic lighting
+                                               if (lighthalf)
+                                               {
+                                                       transpolyvert(nv[0], nv[1], nv[2], v[3], v[4], (int) shadecolor[0] >> 1, (int) shadecolor[1] >> 1, (int) shadecolor[2] >> 1, modelalpha*255.0f);
+                                               }
+                                               else
+                                               {
+                                                       transpolyvert(nv[0], nv[1], nv[2], v[3], v[4], shadecolor[0], shadecolor[1], shadecolor[2], modelalpha*255.0f);
+                                               }
+                                       }
+                               }
+                               transpolyend();
+                       }
+                       else
+                       {
+                               // check for lightmap modification
+//                             if (r_dynamic.value)
+//                             {
+                                       if (s->dlightframe == r_framecount || s->cached_dlight || lighthalf != s->cached_lighthalf) // dynamic this frame or previously, or lighthalf changed
+                                               R_UpdateLightmap(s, s->lightmaptexturenum);
+                                       else
+                                               for (maps = 0 ; maps < MAXLIGHTMAPS && s->styles[maps] != 255 ; maps++)
+                                                       if (d_lightstylevalue[s->styles[maps]] != s->cached_light[maps])
+                                                       {
+                                                               R_UpdateLightmap(s, s->lightmaptexturenum);
+                                                               break;
+                                                       }
+//                             }
+                               if (currentwallpoly < MAX_WALLPOLYS && (currentwallvert + s->polys->numverts) <= MAX_WALLVERTS)
+                               {
+                                       wallpoly[currentwallpoly].texnum = (unsigned short) t->gl_texturenum;
+                                       wallpoly[currentwallpoly].lighttexnum = (unsigned short) lightmap_textures + s->lightmaptexturenum;
+                                       wallpoly[currentwallpoly].glowtexnum = (unsigned short) t->gl_glowtexturenum;
+                                       wallpoly[currentwallpoly].firstvert = currentwallvert;
+                                       wallpoly[currentwallpoly++].verts = s->polys->numverts;
+                                       for (i = 0;i<s->polys->numverts;i++, v += VERTEXSIZE)
+                                       {
+                                               softwaretransform(v, wallvert[currentwallvert].vert);
+                                               wallvert[currentwallvert].s = v[3];
+                                               wallvert[currentwallvert].t = v[4];
+                                               wallvert[currentwallvert].u = v[5];
+                                               wallvert[currentwallvert++].v = v[6];
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/*
+=============================================================
+
+       WORLD MODEL
+
+=============================================================
+*/
+
+void R_StoreEfrags (efrag_t **ppefrag);
+
+/*
+================
+R_RecursiveWorldNode
+================
+*/
+//extern qboolean R_CullBox (vec3_t mins, vec3_t maxs);
+/*
+void R_RecursiveWorldNode (mnode_t *node)
+{
+       int                     c, side;
+       double          dot;
+
+loc0:
+// if a leaf node, draw stuff
+       if (node->contents < 0)
+       {
+               mleaf_t         *pleaf;
+               pleaf = (mleaf_t *)node;
+
+               if (c = pleaf->nummarksurfaces)
+               {
+                       msurface_t      **mark;
+                       mark = pleaf->firstmarksurface;
+                       do
+                       {
+                               (*mark)->visframe = r_framecount;
+                               mark++;
+                       } while (--c);
+               }
+
+       // deal with model fragments in this leaf
+               if (pleaf->efrags)
+                       R_StoreEfrags (&pleaf->efrags);
+
+               return;
+       }
+
+// node is just a decision point, so go down the apropriate sides
+
+// find which side of the node we are on
+       dot = (node->plane->type < 3 ? modelorg[node->plane->type] : DotProduct (modelorg, node->plane->normal)) - node->plane->dist;
+
+// recurse down the children, front side first
+       side = dot < 0;
+       // LordHavoc: save a stack frame by avoiding a call
+//     if (node->children[side]->contents != CONTENTS_SOLID && node->children[side]->visframe == r_visframecount && !R_CullBox (node->children[side]->minmaxs, node->children[side]->minmaxs+3))
+       // LordHavoc: inlined further to reduce conditions
+       if (node->children[side]->contents != CONTENTS_SOLID
+        && node->children[side]->visframe == r_visframecount
+        && frustum[0].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[0]) != 2
+        && frustum[1].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[1]) != 2
+        && frustum[2].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[2]) != 2
+        && frustum[3].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[3]) != 2)
+               R_RecursiveWorldNode (node->children[side]);
+
+       // backside
+       side = dot >= 0;
+// draw stuff
+       if (c = node->numsurfaces)
+       {
+               msurface_t      *surf;
+               surf = cl.worldmodel->surfaces + node->firstsurface;
+
+               // LordHavoc: caused a crash due to texsort (it could render twice...)
+               // back side
+               //side = dot >= -BACKFACE_EPSILON;
+               if (dot < 0)
+               {
+                       for (;c;c--, surf++)
+                       {
+                               if (surf->visframe == r_framecount && (surf->flags & SURF_PLANEBACK))
+                               {
+                                       surf->texturechain = surf->texinfo->texture->texturechain;
+                                       surf->texinfo->texture->texturechain = surf;
+                               }
+                       }
+               }
+               else
+               {
+                       for (;c;c--, surf++)
+                       {
+                               if (surf->visframe == r_framecount && !(surf->flags & SURF_PLANEBACK))
+                               {
+                                       surf->texturechain = surf->texinfo->texture->texturechain;
+                                       surf->texinfo->texture->texturechain = surf;
+                               }
+                       }
+               }
+       }
+
+// recurse down the back side
+       // LordHavoc: save a stack frame by avoiding a call
+//     if (node->children[side]->contents != CONTENTS_SOLID && node->children[side]->visframe == r_visframecount && !R_CullBox (node->children[side]->minmaxs, node->children[side]->minmaxs+3))
+       // LordHavoc: inlined further to reduce conditions
+       if (node->children[side]->contents != CONTENTS_SOLID
+        && node->children[side]->visframe == r_visframecount
+        && frustum[0].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[0]) != 2
+        && frustum[1].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[1]) != 2
+        && frustum[2].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[2]) != 2
+        && frustum[3].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[3]) != 2)
+       {
+               node = node->children[side];
+               goto loc0;
+       }
+//             R_RecursiveWorldNode (node->children[side]);
+}
+*/
+
+extern int c_nodes;
+void R_WorldNode ()
+{
+       int             c, side;
+       double  dot;
+       struct
+       {
+               double dot;
+               mnode_t *node;
+       } nodestack[1024];
+       int             s = 0;
+       mnode_t *node;
+
+       if (!(node = cl.worldmodel->nodes))
+               return;
+
+       while(1)
+       {
+       // if a leaf node, draw stuff
+               c_nodes++;
+               if (node->contents < 0)
+               {
+                       if (node->contents != CONTENTS_SOLID)
+                       {
+                               mleaf_t         *pleaf;
+                               pleaf = (mleaf_t *)node;
+
+                               if (c = pleaf->nummarksurfaces)
+                               {
+                                       msurface_t      **mark;
+                                       mark = pleaf->firstmarksurface;
+                                       do
+                                       {
+                                               (*mark)->visframe = r_framecount;
+                                               mark++;
+                                       } while (--c);
+                               }
+
+                               // deal with model fragments in this leaf
+                               if (pleaf->efrags)
+                                       R_StoreEfrags (&pleaf->efrags);
+                       }
+
+                       if (!s)
+                               break;
+                       node = nodestack[--s].node;
+                       dot = nodestack[s].dot;
+                       goto loc0;
+               }
+
+       // node is just a decision point, so go down the apropriate sides
+
+       // find which side of the node we are on
+               dot = (node->plane->type < 3 ? modelorg[node->plane->type] : DotProduct (modelorg, node->plane->normal)) - node->plane->dist;
+
+       // recurse down the children, front side first
+               side = dot < 0;
+               if (node->children[side]->visframe == r_visframecount
+                && frustum[0].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[0]) != 2
+                && frustum[1].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[1]) != 2
+                && frustum[2].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[2]) != 2
+                && frustum[3].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[3]) != 2)
+               {
+                       nodestack[s].node = node;
+                       nodestack[s++].dot = dot;
+                       node = node->children[side];
+                       continue;
+               }
+loc0:
+
+               // backside
+               side = dot >= 0;
+       // draw stuff
+               if (c = node->numsurfaces)
+               {
+                       msurface_t      *surf;
+                       surf = cl.worldmodel->surfaces + node->firstsurface;
+
+                       if (side)
+                       {
+                               for (;c;c--, surf++)
+                               {
+                                       if (surf->visframe == r_framecount && !(surf->flags & SURF_PLANEBACK))
+                                       {
+                                               surf->texturechain = surf->texinfo->texture->texturechain;
+                                               surf->texinfo->texture->texturechain = surf;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               for (;c;c--, surf++)
+                               {
+                                       if (surf->visframe == r_framecount && (surf->flags & SURF_PLANEBACK))
+                                       {
+                                               surf->texturechain = surf->texinfo->texture->texturechain;
+                                               surf->texinfo->texture->texturechain = surf;
+                                       }
+                               }
+                       }
+               }
+
+       // recurse down the back side
+               if (node->children[side]->visframe == r_visframecount
+                && frustum[0].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[0]) != 2
+                && frustum[1].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[1]) != 2
+                && frustum[2].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[2]) != 2
+                && frustum[3].BoxOnPlaneSideFunc(node->children[side]->minmaxs, node->children[side]->minmaxs+3, &frustum[3]) != 2)
+               {
+                       node = node->children[side];
+                       continue;
+               }
+
+               if (!s)
+                       break;
+               node = nodestack[--s].node;
+               dot = nodestack[s].dot;
+               goto loc0;
+       }
+}
+
+
+/*
+=============
+R_DrawWorld
+=============
+*/
+extern void R_Sky();
+void R_DrawWorld (void)
+{
+       entity_t        ent;
+
+       memset (&ent, 0, sizeof(ent));
+       ent.model = cl.worldmodel;
+       ent.colormod[0] = ent.colormod[1] = ent.colormod[2] = 1;
+       modelalpha = ent.alpha = 1;
+       ent.scale = 1;
+
+       VectorCopy (r_refdef.vieworg, modelorg);
+
+       currententity = &ent;
+
+       softwaretransformidentity(); // LordHavoc: clear transform
+       skypolyclear();
+       skyisvisible = false;
+
+       if (cl.worldmodel)
+               R_WorldNode ();
+
+       DrawTextureChains ();
+
+       glClear (GL_DEPTH_BUFFER_BIT);
+
+       skypolyrender(); // fogged sky polys, affects depth
+
+       if (skyisvisible && !fogenabled)
+               R_Sky(); // does not affect depth, draws over the sky polys
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+
+/*
+===============
+R_MarkLeaves
+===============
+*/
+void R_MarkLeaves (void)
+{
+       byte    *vis;
+       mnode_t *node;
+       int             i;
+
+       if (r_oldviewleaf == r_viewleaf && !r_novis.value)
+               return;
+       
+       r_visframecount++;
+       r_oldviewleaf = r_viewleaf;
+
+       if (r_novis.value)
+       {
+               for (i=0 ; i<cl.worldmodel->numleafs ; i++)
+               {
+                       node = (mnode_t *)&cl.worldmodel->leafs[i+1];
+                       do
+                       {
+                               if (node->visframe == r_visframecount)
+                                       break;
+                               node->visframe = r_visframecount;
+                               node = node->parent;
+                       } while (node);
+               }
+       }
+       else
+       {
+               vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel);
+               
+               for (i=0 ; i<cl.worldmodel->numleafs ; i++)
+               {
+                       if (vis[i>>3] & (1<<(i&7)))
+                       {
+                               node = (mnode_t *)&cl.worldmodel->leafs[i+1];
+                               do
+                               {
+                                       if (node->visframe == r_visframecount)
+                                               break;
+                                       node->visframe = r_visframecount;
+                                       node = node->parent;
+                               } while (node);
+                       }
+               }
+       }
+}
+
+
+
+/*
+=============================================================================
+
+  LIGHTMAP ALLOCATION
+
+=============================================================================
+*/
+
+// returns a texture number and the position inside it
+int AllocBlock (int w, int h, int *x, int *y)
+{
+       int             i, j;
+       int             best, best2;
+       int             texnum;
+
+       for (texnum=0 ; texnum<MAX_LIGHTMAPS ; texnum++)
+       {
+               best = BLOCK_HEIGHT;
+
+               for (i=0 ; i<BLOCK_WIDTH-w ; i+=lightmapalign) // LordHavoc: NVIDIA has broken subimage, so align the lightmaps
+               {
+                       best2 = 0;
+
+                       for (j=0 ; j<w ; j++)
+                       {
+                               if (allocated[texnum][i+j] >= best)
+                                       break;
+                               if (allocated[texnum][i+j] > best2)
+                                       best2 = allocated[texnum][i+j];
+                       }
+                       if (j == w)
+                       {       // this is a valid spot
+                               *x = i;
+                               *y = best = best2;
+                       }
+               }
+
+               if (best + h > BLOCK_HEIGHT)
+                       continue;
+
+               if (gl_nosubimagefragments.value)
+                       if (!lightmaps[texnum])
+                               lightmaps[texnum] = calloc(BLOCK_WIDTH*BLOCK_HEIGHT*4, 1);
+               // LordHavoc: clear texture to blank image, fragments are uploaded using subimage
+               if (!allocated[texnum][0])
+               {
+                       byte blank[BLOCK_WIDTH*BLOCK_HEIGHT*3];
+                       memset(blank, 0, sizeof(blank));
+                       glBindTexture(GL_TEXTURE_2D, lightmap_textures + texnum);
+                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+                       if (lightmaprgba)
+                               glTexImage2D (GL_TEXTURE_2D, 0, 4, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, blank);
+                       else
+                               glTexImage2D (GL_TEXTURE_2D, 0, 3, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, blank);
+               }
+
+               for (i=0 ; i<w ; i++)
+                       allocated[texnum][*x + i] = best + h;
+
+               return texnum;
+       }
+
+       Sys_Error ("AllocBlock: full");
+}
+
+
+mvertex_t      *r_pcurrentvertbase;
+model_t                *currentmodel;
+
+int    nColinElim;
+
+/*
+================
+BuildSurfaceDisplayList
+================
+*/
+void BuildSurfaceDisplayList (msurface_t *fa)
+{
+       int                     i, lindex, lnumverts;
+       medge_t         *pedges, *r_pedge;
+       int                     vertpage;
+       float           *vec;
+       float           s, t;
+       glpoly_t        *poly;
+
+// reconstruct the polygon
+       pedges = currentmodel->edges;
+       lnumverts = fa->numedges;
+       vertpage = 0;
+
+       //
+       // draw texture
+       //
+       poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float));
+       poly->next = fa->polys;
+       poly->flags = fa->flags;
+       fa->polys = poly;
+       poly->numverts = lnumverts;
+
+       for (i=0 ; i<lnumverts ; i++)
+       {
+               lindex = currentmodel->surfedges[fa->firstedge + i];
+
+               if (lindex > 0)
+               {
+                       r_pedge = &pedges[lindex];
+                       vec = r_pcurrentvertbase[r_pedge->v[0]].position;
+               }
+               else
+               {
+                       r_pedge = &pedges[-lindex];
+                       vec = r_pcurrentvertbase[r_pedge->v[1]].position;
+               }
+               s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
+               s /= fa->texinfo->texture->width;
+
+               t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
+               t /= fa->texinfo->texture->height;
+
+               VectorCopy (vec, poly->verts[i]);
+               poly->verts[i][3] = s;
+               poly->verts[i][4] = t;
+
+               //
+               // lightmap texture coordinates
+               //
+               s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
+               s -= fa->texturemins[0];
+               poly->verts[i][7] = bound(0l, ((int)s>>4), (fa->extents[0]>>4)); // LordHavoc: raw lightmap coordinates
+               s += fa->light_s*16;
+               s += 8;
+               s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width;
+
+               t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
+               t -= fa->texturemins[1];
+               poly->verts[i][8] = bound(0l, ((int)t>>4), (fa->extents[1]>>4)); // LordHavoc: raw lightmap coordinates
+               t += fa->light_t*16;
+               t += 8;
+               t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height;
+
+               poly->verts[i][5] = s;
+               poly->verts[i][6] = t;
+       }
+
+       //
+       // remove co-linear points - Ed
+       //
+       /*
+       if (!gl_keeptjunctions.value)
+       {
+               for (i = 0 ; i < lnumverts ; ++i)
+               {
+                       vec3_t v1, v2;
+                       float *prev, *this, *next;
+
+                       prev = poly->verts[(i + lnumverts - 1) % lnumverts];
+                       this = poly->verts[i];
+                       next = poly->verts[(i + 1) % lnumverts];
+
+                       VectorSubtract( this, prev, v1 );
+                       VectorNormalize( v1 );
+                       VectorSubtract( next, prev, v2 );
+                       VectorNormalize( v2 );
+
+                       // skip co-linear points
+                       #define COLINEAR_EPSILON 0.001
+                       if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) &&
+                               (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && 
+                               (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON))
+                       {
+                               int j;
+                               for (j = i + 1; j < lnumverts; ++j)
+                               {
+                                       int k;
+                                       for (k = 0; k < VERTEXSIZE; ++k)
+                                               poly->verts[j - 1][k] = poly->verts[j][k];
+                               }
+                               --lnumverts;
+                               ++nColinElim;
+                               // retry next vertex next time, which is now current vertex
+                               --i;
+                       }
+               }
+       }
+       */
+       poly->numverts = lnumverts;
+
+}
+
+/*
+========================
+GL_CreateSurfaceLightmap
+========================
+*/
+void GL_CreateSurfaceLightmap (msurface_t *surf)
+{
+       int             smax, tmax;
+
+       if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))
+               return;
+
+       smax = (surf->extents[0]>>4)+1;
+       tmax = (surf->extents[1]>>4)+1;
+
+       surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t);
+       glBindTexture(GL_TEXTURE_2D, lightmap_textures + surf->lightmaptexturenum);
+       if (nosubimagefragments)
+       {
+               if (lightmaprgba)
+               {
+                       R_BuildLightMap (surf, lightmaps[surf->lightmaptexturenum] + (surf->light_t * BLOCK_WIDTH + surf->light_s) * 4, BLOCK_WIDTH * 4);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, surf->light_t, BLOCK_WIDTH, tmax, GL_RGBA, GL_UNSIGNED_BYTE, lightmaps[surf->lightmaptexturenum] + surf->light_t * (BLOCK_WIDTH * 4));
+               }
+               else
+               {
+                       R_BuildLightMap (surf, lightmaps[surf->lightmaptexturenum] + (surf->light_t * BLOCK_WIDTH + surf->light_s) * 3, BLOCK_WIDTH * 3);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, surf->light_t, BLOCK_WIDTH, tmax, GL_RGB , GL_UNSIGNED_BYTE, lightmaps[surf->lightmaptexturenum] + surf->light_t * (BLOCK_WIDTH * 3));
+               }
+       }
+       else
+       {
+               smax = ((surf->extents[0]>>4)+lightmapalign) & lightmapalignmask;
+               if (lightmaprgba)
+               {
+                       R_BuildLightMap (surf, templight, smax * 4);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, tmax, GL_RGBA, GL_UNSIGNED_BYTE, templight);
+               }
+               else
+               {
+                       R_BuildLightMap (surf, templight, smax * 3);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, tmax, GL_RGB , GL_UNSIGNED_BYTE, templight);
+               }
+       }
+}
+
+
+/*
+==================
+GL_BuildLightmaps
+
+Builds the lightmap texture
+with all the surfaces from all brush models
+==================
+*/
+void GL_BuildLightmaps (void)
+{
+       int             i, j;
+       model_t *m;
+
+       memset (allocated, 0, sizeof(allocated));
+
+       r_framecount = 1;               // no dlightcache
+
+       if (gl_nosubimagefragments.value)
+               nosubimagefragments = 1;
+       else
+               nosubimagefragments = 0;
+
+       if (gl_lightmaprgba.value)
+       {
+               lightmaprgba = true;
+               lightmapbytes = 4;
+       }
+       else
+       {
+               lightmaprgba = false;
+               lightmapbytes = 3;
+       }
+
+       // LordHavoc: NVIDIA seems to have a broken glTexSubImage2D,
+       //            it needs to be aligned on 4 pixel boundaries...
+       //            so I implemented an adjustable lightmap alignment
+       if (gl_lightmapalign.value < 1)
+               gl_lightmapalign.value = 1;
+       if (gl_lightmapalign.value > 16)
+               gl_lightmapalign.value = 16;
+       lightmapalign = 1;
+       while (lightmapalign < gl_lightmapalign.value)
+               lightmapalign <<= 1;
+       gl_lightmapalign.value = lightmapalign;
+       lightmapalignmask = ~(lightmapalign - 1);
+       if (nosubimagefragments)
+       {
+               lightmapalign = 1;
+               lightmapalignmask = ~0;
+       }
+
+       if (!lightmap_textures)
+       {
+               lightmap_textures = texture_extension_number;
+               texture_extension_number += MAX_LIGHTMAPS;
+       }
+
+       for (j=1 ; j<MAX_MODELS ; j++)
+       {
+               m = cl.model_precache[j];
+               if (!m)
+                       break;
+               if (m->name[0] == '*')
+                       continue;
+               r_pcurrentvertbase = m->vertexes;
+               currentmodel = m;
+               for (i=0 ; i<m->numsurfaces ; i++)
+               {
+                       if ( m->surfaces[i].flags & SURF_DRAWTURB )
+                               continue;
+                       if ( m->surfaces[i].flags & SURF_DRAWSKY )
+                               continue;
+                       GL_CreateSurfaceLightmap (m->surfaces + i);
+                       BuildSurfaceDisplayList (m->surfaces + i);
+               }
+       }
+}
+
diff --git a/gl_screen.c b/gl_screen.c
new file mode 100644 (file)
index 0000000..a7a03d4
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// screen.c -- master for refresh, status bar, console, chat, notify, etc
+
+#include "quakedef.h"
+
+/*
+
+background clear
+rendering
+turtle/net/ram icons
+sbar
+centerprint / slow centerprint
+notify lines
+intermission / finale overlay
+loading plaque
+console
+menu
+
+required background clears
+required update regions
+
+
+syncronous draw mode or async
+One off screen buffer, with updates either copied or xblited
+Need to double buffer?
+
+
+async draw will require the refresh area to be cleared, because it will be
+xblited, but sync draw can just ignore it.
+
+sync
+draw
+
+CenterPrint ()
+SlowPrint ()
+Screen_Update ();
+Con_Printf ();
+
+net 
+turn off messages option
+
+the refresh is allways rendered, unless the console is full screen
+
+
+console is:
+       notify lines
+       half
+       full
+       
+
+*/
+
+
+int                    glx, gly, glwidth, glheight;
+
+// only the refresh window will be updated unless these variables are flagged 
+int                    scr_copytop;
+int                    scr_copyeverything;
+
+float          scr_con_current;
+float          scr_conlines;           // lines of console to display
+
+float          oldscreensize, oldfov;
+cvar_t         scr_viewsize = {"viewsize","100", true};
+cvar_t         scr_fov = {"fov","90"}; // 10 - 170
+cvar_t         scr_conspeed = {"scr_conspeed","300"};
+cvar_t         scr_centertime = {"scr_centertime","2"};
+cvar_t         scr_showram = {"showram","1"};
+cvar_t         scr_showturtle = {"showturtle","0"};
+cvar_t         scr_showpause = {"showpause","1"};
+cvar_t         scr_printspeed = {"scr_printspeed","8"};
+cvar_t         gl_triplebuffer = {"gl_triplebuffer", "1", true };
+
+extern cvar_t  crosshair;
+
+qboolean       scr_initialized;                // ready to draw
+
+qpic_t         *scr_ram;
+qpic_t         *scr_net;
+qpic_t         *scr_turtle;
+
+int                    scr_fullupdate;
+
+int                    clearconsole;
+int                    clearnotify;
+
+int                    sb_lines;
+
+viddef_t       vid;                            // global video state
+
+vrect_t                scr_vrect;
+
+qboolean       scr_disabled_for_loading;
+qboolean       scr_drawloading;
+float          scr_disabled_time;
+
+void SCR_ScreenShot_f (void);
+
+/*
+===============================================================================
+
+CENTER PRINTING
+
+===============================================================================
+*/
+
+char           scr_centerstring[1024];
+float          scr_centertime_start;   // for slow victory printing
+float          scr_centertime_off;
+int                    scr_center_lines;
+int                    scr_erase_lines;
+int                    scr_erase_center;
+
+/*
+==============
+SCR_CenterPrint
+
+Called for important messages that should stay in the center of the screen
+for a few moments
+==============
+*/
+void SCR_CenterPrint (char *str)
+{
+       strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
+       scr_centertime_off = scr_centertime.value;
+       scr_centertime_start = cl.time;
+
+// count the number of lines for centering
+       scr_center_lines = 1;
+       while (*str)
+       {
+               if (*str == '\n')
+                       scr_center_lines++;
+               str++;
+       }
+}
+
+
+void SCR_DrawCenterString (void)
+{
+       char    *start;
+       int             l;
+       int             x, y;
+       int             remaining;
+
+// the finale prints the characters one at a time
+       if (cl.intermission)
+               remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
+       else
+               remaining = 9999;
+
+       scr_erase_center = 0;
+       start = scr_centerstring;
+
+       if (scr_center_lines <= 4)
+               y = vid.height*0.35;
+       else
+               y = 48;
+
+       do      
+       {
+       // scan the width of the line
+               for (l=0 ; l<40 ; l++)
+                       if (start[l] == '\n' || !start[l])
+                               break;
+               x = (vid.width - l*8)/2;
+               // LordHavoc: speedup
+               if (l > 0)
+               {
+                       if (remaining < l)
+                               l = remaining;
+                       Draw_String(x, y, start, l);
+                       remaining -= l;
+                       if (remaining <= 0)
+                               return;
+               }
+               /*
+               for (j=0 ; j<l ; j++, x+=8)
+               {
+                       Draw_Character (x, y, start[j]);        
+                       if (!remaining--)
+                               return;
+               }
+               */
+                       
+               y += 8;
+
+               while (*start && *start != '\n')
+                       start++;
+
+               if (!*start)
+                       break;
+               start++;                // skip the \n
+       } while (1);
+}
+
+void SCR_CheckDrawCenterString (void)
+{
+       scr_copytop = 1;
+       if (scr_center_lines > scr_erase_lines)
+               scr_erase_lines = scr_center_lines;
+
+       scr_centertime_off -= host_frametime;
+       
+       if (scr_centertime_off <= 0 && !cl.intermission)
+               return;
+       if (key_dest != key_game)
+               return;
+
+       SCR_DrawCenterString ();
+}
+
+//=============================================================================
+
+/*
+====================
+CalcFov
+====================
+*/
+float CalcFov (float fov_x, float width, float height)
+{
+        float   a;
+        float   x;
+
+        if (fov_x < 1 || fov_x > 179)
+                Sys_Error ("Bad fov: %f", fov_x);
+
+        x = width/tan(fov_x/360*M_PI);
+
+        a = atan (height/x);
+
+        a = a*360/M_PI;
+
+        return a;
+}
+
+/*
+=================
+SCR_CalcRefdef
+
+Must be called whenever vid changes
+Internal use only
+=================
+*/
+static void SCR_CalcRefdef (void)
+{
+       float           size;
+       int             h;
+       qboolean                full = false;
+
+
+       scr_fullupdate = 0;             // force a background redraw
+       vid.recalc_refdef = 0;
+
+// force the status bar to redraw
+//     Sbar_Changed ();
+
+//========================================
+       
+// bound viewsize
+       if (scr_viewsize.value < 30)
+               Cvar_Set ("viewsize","30");
+       if (scr_viewsize.value > 120)
+               Cvar_Set ("viewsize","120");
+
+// bound field of view
+       if (scr_fov.value < 10)
+               Cvar_Set ("fov","10");
+       if (scr_fov.value > 170)
+               Cvar_Set ("fov","170");
+
+// intermission is always full screen  
+       if (cl.intermission)
+               size = 120;
+       else
+               size = scr_viewsize.value;
+
+       if (size >= 120)
+               sb_lines = 0;           // no status bar at all
+       else if (size >= 110)
+               sb_lines = 24;          // no inventory
+       else
+               sb_lines = 24+16+8;
+
+       if (scr_viewsize.value >= 100.0)
+       {
+               full = true;
+               size = 100.0;
+       }
+       else
+               size = scr_viewsize.value;
+       if (cl.intermission)
+       {
+               full = true;
+               size = 100;
+               sb_lines = 0;
+       }
+       size /= 100.0;
+
+       // LordHavoc: always fullyscreen rendering
+       h = vid.height/* - sb_lines*/;
+
+       r_refdef.vrect.width = vid.width * size;
+       if (r_refdef.vrect.width < 96)
+       {
+               size = 96.0 / r_refdef.vrect.width;
+               r_refdef.vrect.width = 96;      // min for icons
+       }
+
+       r_refdef.vrect.height = vid.height * size;
+       //if (r_refdef.vrect.height > vid.height - sb_lines)
+       //      r_refdef.vrect.height = vid.height - sb_lines;
+       if (r_refdef.vrect.height > (int) vid.height)
+                       r_refdef.vrect.height = vid.height;
+       r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2;
+       if (full)
+               r_refdef.vrect.y = 0;
+       else 
+               r_refdef.vrect.y = (h - r_refdef.vrect.height)/2;
+
+       r_refdef.fov_x = scr_fov.value;
+       r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
+
+       scr_vrect = r_refdef.vrect;
+}
+
+
+/*
+=================
+SCR_SizeUp_f
+
+Keybinding command
+=================
+*/
+void SCR_SizeUp_f (void)
+{
+       Cvar_SetValue ("viewsize",scr_viewsize.value+10);
+       vid.recalc_refdef = 1;
+}
+
+
+/*
+=================
+SCR_SizeDown_f
+
+Keybinding command
+=================
+*/
+void SCR_SizeDown_f (void)
+{
+       Cvar_SetValue ("viewsize",scr_viewsize.value-10);
+       vid.recalc_refdef = 1;
+}
+
+//============================================================================
+
+/*
+==================
+SCR_Init
+==================
+*/
+void SCR_Init (void)
+{
+
+       Cvar_RegisterVariable (&scr_fov);
+       Cvar_RegisterVariable (&scr_viewsize);
+       Cvar_RegisterVariable (&scr_conspeed);
+       Cvar_RegisterVariable (&scr_showram);
+       Cvar_RegisterVariable (&scr_showturtle);
+       Cvar_RegisterVariable (&scr_showpause);
+       Cvar_RegisterVariable (&scr_centertime);
+       Cvar_RegisterVariable (&scr_printspeed);
+       Cvar_RegisterVariable (&gl_triplebuffer);
+
+//
+// register our commands
+//
+       Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
+       Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
+       Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
+
+       scr_ram = Draw_PicFromWad ("ram");
+       scr_net = Draw_PicFromWad ("net");
+       scr_turtle = Draw_PicFromWad ("turtle");
+
+       scr_initialized = true;
+}
+
+
+
+/*
+==============
+SCR_DrawRam
+==============
+*/
+void SCR_DrawRam (void)
+{
+       if (!scr_showram.value)
+               return;
+
+       if (!r_cache_thrash)
+               return;
+
+       Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram);
+}
+
+/*
+==============
+SCR_DrawTurtle
+==============
+*/
+void SCR_DrawTurtle (void)
+{
+       static int      count;
+       
+       if (!scr_showturtle.value)
+               return;
+
+       if (host_frametime < 0.1)
+       {
+               count = 0;
+               return;
+       }
+
+       count++;
+       if (count < 3)
+               return;
+
+       Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle);
+}
+
+/*
+==============
+SCR_DrawNet
+==============
+*/
+void SCR_DrawNet (void)
+{
+       if (realtime - cl.last_received_message < 0.3)
+               return;
+       if (cls.demoplayback)
+               return;
+
+       Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net);
+}
+
+/*
+==============
+DrawPause
+==============
+*/
+void SCR_DrawPause (void)
+{
+       qpic_t  *pic;
+
+       if (!scr_showpause.value)               // turn off for screenshots
+               return;
+
+       if (!cl.paused)
+               return;
+
+       pic = Draw_CachePic ("gfx/pause.lmp");
+       Draw_Pic ( (vid.width - pic->width)/2, 
+               (vid.height - 48 - pic->height)/2, pic);
+}
+
+
+
+/*
+==============
+SCR_DrawLoading
+==============
+*/
+void SCR_DrawLoading (void)
+{
+       qpic_t  *pic;
+
+       if (!scr_drawloading)
+               return;
+               
+       pic = Draw_CachePic ("gfx/loading.lmp");
+       Draw_Pic ( (vid.width - pic->width)/2, 
+               (vid.height - 48 - pic->height)/2, pic);
+}
+
+
+
+//=============================================================================
+
+
+/*
+==================
+SCR_SetUpToDrawConsole
+==================
+*/
+void SCR_SetUpToDrawConsole (void)
+{
+       Con_CheckResize ();
+       
+       //if (scr_drawloading)
+       //      return;         // never a console with loading plaque
+
+// decide on the height of the console
+       con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
+
+       if (con_forcedup)
+       {
+               scr_conlines = vid.height;              // full screen
+               scr_con_current = scr_conlines;
+       }
+       else if (key_dest == key_console)
+               scr_conlines = vid.height/2;    // half screen
+       else
+               scr_conlines = 0;                               // none visible
+       
+       if (scr_conlines < scr_con_current)
+       {
+               scr_con_current -= scr_conspeed.value*host_frametime;
+               if (scr_conlines > scr_con_current)
+                       scr_con_current = scr_conlines;
+
+       }
+       else if (scr_conlines > scr_con_current)
+       {
+               scr_con_current += scr_conspeed.value*host_frametime;
+               if (scr_conlines < scr_con_current)
+                       scr_con_current = scr_conlines;
+       }
+
+       /*
+       if (clearconsole++ < vid.numpages)
+       {
+               Sbar_Changed ();
+       }
+       else if (clearnotify++ < vid.numpages)
+       {
+       }
+       else
+               con_notifylines = 0;
+       */
+}
+       
+/*
+==================
+SCR_DrawConsole
+==================
+*/
+void SCR_DrawConsole (void)
+{
+       if (scr_con_current)
+       {
+               scr_copyeverything = 1;
+               Con_DrawConsole (scr_con_current, true);
+               clearconsole = 0;
+       }
+       else
+       {
+               if (key_dest == key_game || key_dest == key_message)
+                       Con_DrawNotify ();      // only draw notify in game
+       }
+}
+
+
+/* 
+============================================================================== 
+                                               SCREEN SHOTS 
+============================================================================== 
+*/ 
+
+typedef struct _TargaHeader {
+       unsigned char   id_length, colormap_type, image_type;
+       unsigned short  colormap_index, colormap_length;
+       unsigned char   colormap_size;
+       unsigned short  x_origin, y_origin, width, height;
+       unsigned char   pixel_size, attributes;
+} TargaHeader;
+
+
+/* 
+================== 
+SCR_ScreenShot_f
+================== 
+*/  
+void SCR_ScreenShot_f (void) 
+{
+       byte            *buffer;
+       char            pcxname[80]; 
+       char            checkname[MAX_OSPATH];
+       int                     i, c, temp;
+// 
+// find a file name to save it to 
+// 
+       strcpy(pcxname,"dp0000.tga");
+               
+       for (i=0 ; i<=9999 ; i++) 
+       { 
+               pcxname[2] = (i/1000)%10 + '0'; 
+               pcxname[3] = (i/ 100)%10 + '0'; 
+               pcxname[4] = (i/  10)%10 + '0'; 
+               pcxname[5] = (i/   1)%10 + '0'; 
+               sprintf (checkname, "%s/%s", com_gamedir, pcxname);
+               if (Sys_FileTime(checkname) == -1)
+                       break;  // file doesn't exist
+       } 
+       if (i==10000)
+       {
+               Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n"); 
+               return;
+       }
+
+
+       buffer = malloc(glwidth*glheight*3 + 18);
+       memset (buffer, 0, 18);
+       buffer[2] = 2;          // uncompressed type
+       buffer[12] = glwidth&255;
+       buffer[13] = glwidth>>8;
+       buffer[14] = glheight&255;
+       buffer[15] = glheight>>8;
+       buffer[16] = 24;        // pixel size
+
+       glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); 
+
+       // swap rgb to bgr
+       c = 18+glwidth*glheight*3;
+       for (i=18 ; i<c ; i+=3)
+       {
+               temp = buffer[i];
+               buffer[i] = buffer[i+2];
+               buffer[i+2] = temp;
+       }
+       COM_WriteFile (pcxname, buffer, glwidth*glheight*3 + 18 );
+
+       free (buffer);
+       Con_Printf ("Wrote %s\n", pcxname);
+} 
+
+
+//=============================================================================
+
+
+/*
+===============
+SCR_BeginLoadingPlaque
+
+================
+*/
+void SCR_BeginLoadingPlaque (void)
+{
+       S_StopAllSounds (true);
+
+       if (cls.state != ca_connected)
+               return;
+       if (cls.signon != SIGNONS)
+               return;
+       
+// redraw with no console and the loading plaque
+       Con_ClearNotify ();
+       scr_centertime_off = 0;
+       scr_con_current = 0;
+
+       scr_drawloading = true;
+       scr_fullupdate = 0;
+//     Sbar_Changed ();
+       SCR_UpdateScreen ();
+       scr_drawloading = false;
+
+       scr_disabled_for_loading = true;
+       scr_disabled_time = realtime;
+       scr_fullupdate = 0;
+}
+
+/*
+===============
+SCR_EndLoadingPlaque
+
+================
+*/
+void SCR_EndLoadingPlaque (void)
+{
+       scr_disabled_for_loading = false;
+       scr_fullupdate = 0;
+       Con_ClearNotify ();
+}
+
+//=============================================================================
+
+char   *scr_notifystring;
+qboolean       scr_drawdialog;
+
+void SCR_DrawNotifyString (void)
+{
+       char    *start;
+       int             l;
+       int             x, y;
+
+       start = scr_notifystring;
+
+       y = vid.height*0.35;
+
+       do      
+       {
+       // scan the width of the line
+               for (l=0 ; l<40 ; l++)
+                       if (start[l] == '\n' || !start[l])
+                               break;
+               x = (vid.width - l*8)/2;
+               // LordHavoc: speedup
+//             for (j=0 ; j<l ; j++, x+=8)
+//                     Draw_Character (x, y, start[j]);        
+               Draw_String (x, y, start, l);
+                       
+               y += 8;
+
+               while (*start && *start != '\n')
+                       start++;
+
+               if (!*start)
+                       break;
+               start++;                // skip the \n
+       } while (1);
+}
+
+/*
+==================
+SCR_ModalMessage
+
+Displays a text string in the center of the screen and waits for a Y or N
+keypress.  
+==================
+*/
+int SCR_ModalMessage (char *text)
+{
+       if (cls.state == ca_dedicated)
+               return true;
+
+       scr_notifystring = text;
+// draw a fresh screen
+       scr_fullupdate = 0;
+       scr_drawdialog = true;
+       SCR_UpdateScreen ();
+       scr_drawdialog = false;
+       
+       S_ClearBuffer ();               // so dma doesn't loop current sound
+
+       do
+       {
+               key_count = -1;         // wait for a key down and up
+               Sys_SendKeyEvents ();
+       } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE);
+
+       scr_fullupdate = 0;
+       SCR_UpdateScreen ();
+
+       return key_lastpress == 'y';
+}
+
+
+//=============================================================================
+
+/*
+===============
+SCR_BringDownConsole
+
+Brings the console down and fades the palettes back to normal
+================
+*/
+void SCR_BringDownConsole (void)
+{
+       int             i;
+       
+       scr_centertime_off = 0;
+       
+       for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
+               SCR_UpdateScreen ();
+
+       cl.cshifts[0].percent = 0;              // no area contents palette on next frame
+       VID_SetPalette (host_basepal);
+}
+
+void GL_Set2D (void);
+
+extern void SHOWLMP_drawall();
+extern cvar_t contrast;
+extern cvar_t brightness;
+extern cvar_t gl_lightmode;
+
+void GL_BrightenScreen()
+{
+       float f;
+       glDisable(GL_TEXTURE_2D);
+       glEnable(GL_BLEND);
+       f = brightness.value = bound(1.0f, brightness.value, 5.0f);
+       if (f > 1)
+       {
+               glBlendFunc (GL_DST_COLOR, GL_ONE);
+               glBegin (GL_TRIANGLES);
+               while (f > 1)
+               {
+                       if (f >= 2)
+                               glColor3f (1, 1, 1);
+                       else
+                               glColor3f (f-1, f-1, f-1);
+                       glVertex2f (-5000, -5000);
+                       glVertex2f (10000, -5000);
+                       glVertex2f (-5000, 10000);
+                       f *= 0.5;
+               }
+               glEnd ();
+       }
+       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       contrast.value = bound(0.2, contrast.value, 1.0);
+       if (contrast.value < 1.0f)
+       {
+               glBegin (GL_TRIANGLES);
+               glColor4f (1, 1, 1, 1-contrast.value);
+               glVertex2f (-5000, -5000);
+               glVertex2f (10000, -5000);
+               glVertex2f (-5000, 10000);
+               glEnd ();
+       }
+
+       glEnable (GL_CULL_FACE);
+       glEnable (GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+       glEnable(GL_TEXTURE_2D);
+}
+
+/*
+==================
+SCR_UpdateScreen
+
+This is called every frame, and can also be called explicitly to flush
+text to the screen.
+
+WARNING: be very careful calling this from elsewhere, because the refresh
+needs almost the entire 256k of stack space!
+==================
+*/
+extern cvar_t gl_vertexarrays;
+extern qboolean gl_arrays;
+void GL_Finish();
+int c_nodes;
+void SCR_UpdateScreen (void)
+{
+       static float    oldscr_viewsize;
+       double  time1, time2;
+
+       if (r_speeds.value)
+       {
+               time1 = Sys_FloatTime ();
+               c_brush_polys = 0;
+               c_alias_polys = 0;
+               c_nodes = 0;
+       }
+
+       if (!gl_arrays)
+               gl_vertexarrays.value = 0;
+
+       vid.numpages = 2 + gl_triplebuffer.value;
+
+       scr_copytop = 0;
+       scr_copyeverything = 0;
+
+       if (scr_disabled_for_loading)
+       {
+               if (realtime - scr_disabled_time > 60)
+               {
+                       scr_disabled_for_loading = false;
+                       Con_Printf ("load failed.\n");
+               }
+               else
+                       return;
+       }
+
+       if (!scr_initialized || !con_initialized)
+               return;                         // not initialized yet
+
+
+       GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
+       
+       //
+       // determine size of refresh window
+       //
+       if (oldfov != scr_fov.value)
+       {
+               oldfov = scr_fov.value;
+               vid.recalc_refdef = true;
+       }
+
+       if (oldscreensize != scr_viewsize.value)
+       {
+               oldscreensize = scr_viewsize.value;
+               vid.recalc_refdef = true;
+       }
+
+       if (vid.recalc_refdef)
+               SCR_CalcRefdef ();
+
+       glClearColor(0,0,0,0);
+       glClear (GL_COLOR_BUFFER_BIT); // LordHavoc: clear the screen (around the view as well)
+
+//
+// do 3D refresh drawing, and then update the screen
+//
+       SCR_SetUpToDrawConsole ();
+
+       V_RenderView ();
+
+       GL_Set2D ();
+
+       if (scr_drawdialog)
+       {
+               Sbar_Draw ();
+//             Draw_FadeScreen ();
+               SCR_DrawNotifyString ();
+               scr_copyeverything = true;
+       }
+       else if (scr_drawloading)
+       {
+               SCR_DrawLoading ();
+               Sbar_Draw ();
+       }
+       else if (cl.intermission == 1 && key_dest == key_game)
+       {
+               Sbar_IntermissionOverlay ();
+       }
+       else if (cl.intermission == 2 && key_dest == key_game)
+       {
+               Sbar_FinaleOverlay ();
+               SCR_CheckDrawCenterString ();
+       }
+       else
+       {
+               if (crosshair.value)
+                       Draw_Character (scr_vrect.x + scr_vrect.width/2, scr_vrect.y + scr_vrect.height/2, '+');
+               
+               SCR_DrawRam ();
+               SCR_DrawNet ();
+               SCR_DrawTurtle ();
+               SCR_DrawPause ();
+               SCR_CheckDrawCenterString ();
+               Sbar_Draw ();
+               SHOWLMP_drawall();
+               SCR_DrawConsole ();     
+               M_Draw ();
+       }
+
+       V_UpdatePalette ();
+
+       GL_BrightenScreen();
+
+       GL_Finish();
+
+       if (r_speeds.value)
+       {
+               time2 = Sys_FloatTime ();
+               Con_Printf ("%3i ms  %4i wpoly %4i epoly %4i BSPnodes\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys, c_nodes); 
+       }
+       GL_EndRendering ();
+}
+
+// for profiling, this is seperated
+void GL_Finish()
+{
+       glFinish ();
+}
+
diff --git a/gl_warp.c b/gl_warp.c
new file mode 100644 (file)
index 0000000..1dd2b28
--- /dev/null
+++ b/gl_warp.c
@@ -0,0 +1,510 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// gl_warp.c -- sky and water polygons
+
+#include "quakedef.h"
+
+extern model_t *loadmodel;
+
+int            skytexturenum;
+
+int            solidskytexture;
+int            alphaskytexture;
+float  speedscale;             // for top sky and bottom sky
+
+msurface_t     *warpface;
+
+extern cvar_t gl_subdivide_size;
+
+void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
+{
+       int             i, j;
+       float   *v;
+
+       mins[0] = mins[1] = mins[2] = 9999;
+       maxs[0] = maxs[1] = maxs[2] = -9999;
+       v = verts;
+       for (i=0 ; i<numverts ; i++)
+               for (j=0 ; j<3 ; j++, v++)
+               {
+                       if (*v < mins[j])
+                               mins[j] = *v;
+                       if (*v > maxs[j])
+                               maxs[j] = *v;
+               }
+}
+
+void SubdividePolygon (int numverts, float *verts)
+{
+       int             i, j, k;
+       vec3_t  mins, maxs;
+       float   m;
+       float   *v;
+       vec3_t  front[64], back[64];
+       int             f, b;
+       float   dist[64];
+       float   frac;
+       glpoly_t        *poly;
+       float   s, t;
+
+       if (numverts > 60)
+               Sys_Error ("numverts = %i", numverts);
+
+       BoundPoly (numverts, verts, mins, maxs);
+
+       for (i=0 ; i<3 ; i++)
+       {
+               m = (mins[i] + maxs[i]) * 0.5;
+               m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5);
+               if (maxs[i] - m < 8)
+                       continue;
+               if (m - mins[i] < 8)
+                       continue;
+
+               // cut it
+               v = verts + i;
+               for (j=0 ; j<numverts ; j++, v+= 3)
+                       dist[j] = *v - m;
+
+               // wrap cases
+               dist[j] = dist[0];
+               v-=i;
+               VectorCopy (verts, v);
+
+               f = b = 0;
+               v = verts;
+               for (j=0 ; j<numverts ; j++, v+= 3)
+               {
+                       if (dist[j] >= 0)
+                       {
+                               VectorCopy (v, front[f]);
+                               f++;
+                       }
+                       if (dist[j] <= 0)
+                       {
+                               VectorCopy (v, back[b]);
+                               b++;
+                       }
+                       if (dist[j] == 0 || dist[j+1] == 0)
+                               continue;
+                       if ( (dist[j] > 0) != (dist[j+1] > 0) )
+                       {
+                               // clip point
+                               frac = dist[j] / (dist[j] - dist[j+1]);
+                               for (k=0 ; k<3 ; k++)
+                                       front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]);
+                               f++;
+                               b++;
+                       }
+               }
+
+               SubdividePolygon (f, front[0]);
+               SubdividePolygon (b, back[0]);
+               return;
+       }
+
+       poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float));
+       poly->next = warpface->polys;
+       warpface->polys = poly;
+       poly->numverts = numverts;
+       for (i=0 ; i<numverts ; i++, verts+= 3)
+       {
+               VectorCopy (verts, poly->verts[i]);
+               s = DotProduct (verts, warpface->texinfo->vecs[0]);
+               t = DotProduct (verts, warpface->texinfo->vecs[1]);
+               poly->verts[i][3] = s;
+               poly->verts[i][4] = t;
+       }
+}
+
+/*
+================
+GL_SubdivideSurface
+
+Breaks a polygon up along axial 64 unit
+boundaries so that turbulent and sky warps
+can be done reasonably.
+================
+*/
+void GL_SubdivideSurface (msurface_t *fa)
+{
+       vec3_t          verts[64];
+       int                     numverts;
+       int                     i;
+       int                     lindex;
+       float           *vec;
+
+       warpface = fa;
+
+       //
+       // convert edges back to a normal polygon
+       //
+       numverts = 0;
+       for (i=0 ; i<fa->numedges ; i++)
+       {
+               lindex = loadmodel->surfedges[fa->firstedge + i];
+
+               if (lindex > 0)
+                       vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
+               else
+                       vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
+               VectorCopy (vec, verts[numverts]);
+               numverts++;
+       }
+
+       SubdividePolygon (numverts, verts[0]);
+}
+
+//=========================================================
+
+
+
+extern qboolean lighthalf;
+
+#define        SKY_TEX         4000
+
+char skyname[256];
+
+// LordHavoc: moved LoadTGA and LoadPCX to gl_draw.c
+
+extern int image_width, image_height;
+
+byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight);
+/*
+==================
+R_LoadSkyBox
+==================
+*/
+char   *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
+void R_LoadSkyBox (void)
+{
+       int             i;
+       char    name[64];
+       byte*   image_rgba;
+
+       for (i=0 ; i<6 ; i++)
+       {
+               glBindTexture(GL_TEXTURE_2D, SKY_TEX + i);
+               sprintf (name, "env/%s%s", skyname, suf[i]);
+               if (!(image_rgba = loadimagepixels(name, FALSE, 0, 0)))
+               {
+                       sprintf (name, "gfx/env/%s%s", skyname, suf[i]);
+                       if (!(image_rgba = loadimagepixels(name, FALSE, 0, 0)))
+                       {
+                               Con_Printf ("Couldn't load %s\n", name);
+                               continue;
+                       }
+               }
+               glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_rgba);
+               free (image_rgba);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       }
+}
+
+void R_SetSkyBox (char *sky)
+{
+       strcpy(skyname, sky);
+       R_LoadSkyBox ();
+}
+
+// LordHavoc: added LoadSky console command
+void LoadSky_f (void)
+{
+       switch (Cmd_Argc())
+       {
+       case 1:
+               if (skyname[0])
+                       Con_Printf("current sky: %s\n", skyname);
+               else
+                       Con_Printf("no skybox has been set\n", skyname);
+               break;
+       case 2:
+               R_SetSkyBox(Cmd_Argv(1));
+               Con_Printf("skybox set to %s\n", skyname);
+               break;
+       default:
+               Con_Printf("usage: loadsky skyname\n");
+               break;
+       }
+}
+
+extern cvar_t r_skyboxsize;
+
+#define R_SkyBoxPolyVec(s,t,x,y,z) \
+       glTexCoord2f((s) * (254.0f/256.0f) + (1.0f/256.0f), (t) * (254.0f/256.0f) + (1.0f/256.0f));\
+       glVertex3f((x) * 1024.0 + r_refdef.vieworg[0], (y) * 1024.0 + r_refdef.vieworg[1], (z) * 1024.0 + r_refdef.vieworg[2]);
+
+void R_SkyBox()
+{
+       glDisable (GL_BLEND);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       if (lighthalf)
+               glColor3f(0.5,0.5,0.5);
+       else
+               glColor3f(1,1,1);
+       glBindTexture(GL_TEXTURE_2D, SKY_TEX + 3); // front
+       glBegin(GL_QUADS);
+       R_SkyBoxPolyVec(1, 0,  1, -1,  1);
+       R_SkyBoxPolyVec(1, 1,  1, -1, -1);
+       R_SkyBoxPolyVec(0, 1,  1,  1, -1);
+       R_SkyBoxPolyVec(0, 0,  1,  1,  1);
+       glEnd();
+       glBindTexture(GL_TEXTURE_2D, SKY_TEX + 1); // back
+       glBegin(GL_QUADS);
+       R_SkyBoxPolyVec(1, 0, -1,  1,  1);
+       R_SkyBoxPolyVec(1, 1, -1,  1, -1);
+       R_SkyBoxPolyVec(0, 1, -1, -1, -1);
+       R_SkyBoxPolyVec(0, 0, -1, -1,  1);
+       glEnd();
+       glBindTexture(GL_TEXTURE_2D, SKY_TEX + 0); // right
+       glBegin(GL_QUADS);
+       R_SkyBoxPolyVec(1, 0,  1,  1,  1);
+       R_SkyBoxPolyVec(1, 1,  1,  1, -1);
+       R_SkyBoxPolyVec(0, 1, -1,  1, -1);
+       R_SkyBoxPolyVec(0, 0, -1,  1,  1);
+       glEnd();
+       glBindTexture(GL_TEXTURE_2D, SKY_TEX + 2); // left
+       glBegin(GL_QUADS);
+       R_SkyBoxPolyVec(1, 0, -1, -1,  1);
+       R_SkyBoxPolyVec(1, 1, -1, -1, -1);
+       R_SkyBoxPolyVec(0, 1,  1, -1, -1);
+       R_SkyBoxPolyVec(0, 0,  1, -1,  1);
+       glEnd();
+       glBindTexture(GL_TEXTURE_2D, SKY_TEX + 4); // up
+       glBegin(GL_QUADS);
+       R_SkyBoxPolyVec(1, 0,  1, -1,  1);
+       R_SkyBoxPolyVec(1, 1,  1,  1,  1);
+       R_SkyBoxPolyVec(0, 1, -1,  1,  1);
+       R_SkyBoxPolyVec(0, 0, -1, -1,  1);
+       glEnd();
+       glBindTexture(GL_TEXTURE_2D, SKY_TEX + 5); // down
+       glBegin(GL_QUADS);
+       R_SkyBoxPolyVec(1, 0,  1,  1, -1);
+       R_SkyBoxPolyVec(1, 1,  1, -1, -1);
+       R_SkyBoxPolyVec(0, 1, -1, -1, -1);
+       R_SkyBoxPolyVec(0, 0, -1,  1, -1);
+       glEnd();
+}
+
+float skydomeouter[33*33*3];
+float skydomeinner[33*33*3];
+unsigned short skydomeindices[32*66];
+qboolean skydomeinitialized = 0;
+void skydomecalc(float *dome, float dx, float dy, float dz)
+{
+       float a, b, x, ax, ay;
+       int i;
+       unsigned short *index;
+       for (a = 0;a <= 1;a += (1.0 / 32.0))
+       {
+               ax = cos(a * M_PI * 2);
+               ay = -sin(a * M_PI * 2);
+               for (b = 0;b <= 1;b += (1.0 / 32.0))
+               {
+                       x = cos(b * M_PI * 2);
+                       *dome++ = ax*x * dx;
+                       *dome++ = ay*x * dy;
+                       *dome++ = -sin(b * M_PI * 2) * dz;
+               }
+       }
+       index = skydomeindices;
+       for (i = 0;i < (32*33);i++)
+       {
+               *index++ = i;
+               *index++ = i + 33;
+       }
+}
+
+extern cvar_t gl_vertexarrays;
+void skydome(float *source, float s, float texscale)
+{
+       vec_t vert[33*33][3], tex[33*33][2], *v, *t;
+       int i, j;
+       unsigned short *index;
+       v = &vert[0][0];t = &tex[0][0];
+       for (i = 0;i < (33*33);i++)
+       {
+               *t++ = source[0] * texscale + s;
+               *t++ = source[1] * texscale + s;
+               *v++ = *source++ + r_refdef.vieworg[0];
+               *v++ = *source++ + r_refdef.vieworg[1];
+               *v++ = *source++ + r_refdef.vieworg[2];
+       }
+       if (gl_vertexarrays.value)
+       {
+               qglTexCoordPointer(2, GL_FLOAT, 0, tex);
+               qglVertexPointer(3, GL_FLOAT, 0, vert);
+               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+               glEnableClientState(GL_VERTEX_ARRAY);
+//             qglInterleavedArrays(GL_T2F_V3F, 0, vert);
+               for (i = 0;i < (32*66);i+=66)
+                       qglDrawElements(GL_TRIANGLE_STRIP, 66, GL_UNSIGNED_SHORT, &skydomeindices[i]);
+               glDisableClientState(GL_VERTEX_ARRAY);
+               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       }
+       else
+       {
+               index = skydomeindices;
+               for (i = 0;i < (32*66);i+=66)
+               {
+                       glBegin(GL_TRIANGLE_STRIP);
+                       for (j = 0;j < 66;j++)
+                       {
+                               // Matrox G200 (and possibly G400) drivers don't support TexCoord2fv...
+                               glTexCoord2f(tex[*index][0], tex[*index][1]);
+                               glVertex3fv(&vert[*index++][0]);
+                       }
+                       glEnd();
+               }
+       }
+}
+
+void R_SkyDome()
+{
+       glDisable (GL_BLEND);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       if (lighthalf)
+               glColor3f(0.5,0.5,0.5);
+       else
+               glColor3f(1,1,1);
+       glBindTexture(GL_TEXTURE_2D, solidskytexture); // upper clouds
+       if (!skydomeinitialized)
+       {
+               skydomeinitialized = true;
+               skydomecalc(skydomeouter, 1024, 1024, 256);
+               skydomecalc(skydomeinner, 512, 512, 128);
+       }
+       speedscale = realtime*8.0/256.0;
+       speedscale -= (int)speedscale;
+       skydome(skydomeouter, speedscale, 1.0 / 256.0);
+       glEnable (GL_BLEND);
+       glBindTexture(GL_TEXTURE_2D, alphaskytexture); // lower clouds
+       speedscale = realtime*8.0/128.0;
+       speedscale -= (int)speedscale;
+       skydome(skydomeinner, speedscale, 1.0 / 128.0);
+       glDisable (GL_BLEND);
+}
+
+void R_Sky()
+{
+       glDisable(GL_DEPTH_TEST);
+       glDepthMask(0);
+       if (skyname[0])
+               R_SkyBox();
+       else // classic quake sky
+               R_SkyDome();
+       glDepthMask(1);
+       glEnable (GL_DEPTH_TEST);
+       glColor3f (1,1,1);
+}
+
+//===============================================================
+
+/*
+=============
+R_InitSky
+
+A sky texture is 256*128, with the right side being a masked overlay
+==============
+*/
+// LordHavoc: changed this for GLQuake
+void R_InitSky (byte *src, int bytesperpixel) //texture_t *mt)
+{
+       int                     i, j, p;
+//     byte            *src;
+       unsigned        trans[128*128];
+       unsigned        transpix;
+       int                     r, g, b;
+       unsigned        *rgba;
+       extern  int                     skytexturenum;
+
+//     src = (byte *)mt + mt->offsets[0];
+
+       if (bytesperpixel == 4)
+       {
+               for (i = 0;i < 128;i++)
+                       for (j = 0;j < 128;j++)
+                               trans[(i*128) + j] = src[i*256+j+128];
+       }
+       else
+       {
+               // make an average value for the back to avoid
+               // a fringe on the top level
+               r = g = b = 0;
+               for (i=0 ; i<128 ; i++)
+                       for (j=0 ; j<128 ; j++)
+                       {
+                               p = src[i*256 + j + 128];
+                               rgba = &d_8to24table[p];
+                               trans[(i*128) + j] = *rgba;
+                               r += ((byte *)rgba)[0];
+                               g += ((byte *)rgba)[1];
+                               b += ((byte *)rgba)[2];
+                       }
+
+               ((byte *)&transpix)[0] = r/(128*128);
+               ((byte *)&transpix)[1] = g/(128*128);
+               ((byte *)&transpix)[2] = b/(128*128);
+               ((byte *)&transpix)[3] = 0;
+       }
+
+       if (!solidskytexture)
+               solidskytexture = texture_extension_number++;
+       if (!isDedicated)
+       {
+               glBindTexture(GL_TEXTURE_2D, solidskytexture );
+               glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       }
+
+
+       if (bytesperpixel == 4)
+       {
+               for (i = 0;i < 128;i++)
+                       for (j = 0;j < 128;j++)
+                               trans[(i*128) + j] = src[i*256+j];
+       }
+       else
+       {
+               for (i=0 ; i<128 ; i++)
+                       for (j=0 ; j<128 ; j++)
+                       {
+                               p = src[i*256 + j];
+                               if (p == 0)
+                                       trans[(i*128) + j] = transpix;
+                               else
+                                       trans[(i*128) + j] = d_8to24table[p];
+                       }
+       }
+
+       if (!alphaskytexture)
+               alphaskytexture = texture_extension_number++;
+       if (!isDedicated)
+       {
+               glBindTexture(GL_TEXTURE_2D, alphaskytexture);
+               glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       }
+}
+
diff --git a/gl_warp_sin.h b/gl_warp_sin.h
new file mode 100644 (file)
index 0000000..22976a7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+ 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677,
+ 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916,
+ 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998,
+ 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632,
+ 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068,
+ 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368,
+ 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562,
+ 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759,
+ 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222,
+ 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394,
+ 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883,
+ 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398,
+ 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647,
+ 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193,
+ 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281,
+ 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633,
+ 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677,
+ -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916,
+ -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998,
+ -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632,
+ -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068,
+ -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368,
+ -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562,
+ -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759,
+ -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222,
+ -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394,
+ -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883,
+ -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398,
+ -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647,
+ -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193,
+ -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281,
+ -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633,
diff --git a/glquake.h b/glquake.h
new file mode 100644 (file)
index 0000000..d77b358
--- /dev/null
+++ b/glquake.h
@@ -0,0 +1,332 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// disable data conversion warnings
+
+#pragma warning(disable : 4244)     // MIPS
+#pragma warning(disable : 4136)     // X86
+#pragma warning(disable : 4051)     // ALPHA
+#pragma warning(disable : 4305)                // LordHavoc: MSVC++ 6 x86, double/float
+#pragma warning(disable : 4018)                // LordHavoc: MSVC++ 4, signed/unsigned mismatch
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+void GL_BeginRendering (int *x, int *y, int *width, int *height);
+void GL_EndRendering (void);
+
+
+#ifdef _WIN32
+// Function prototypes for the Texture Object Extension routines
+typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *,
+                    const GLboolean *);
+typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);
+typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *);
+typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *);
+typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint);
+typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *,
+                    const GLclampf *);
+typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *);
+
+extern BINDTEXFUNCPTR bindTexFunc;
+extern DELTEXFUNCPTR delTexFunc;
+extern TEXSUBIMAGEPTR TexSubImage2DFunc;
+#endif
+
+extern int texture_extension_number;
+
+extern float   gldepthmin, gldepthmax;
+
+void GL_Upload32 (void *data, int width, int height,  qboolean mipmap, qboolean alpha);
+void GL_Upload8 (byte *data, int width, int height,  qboolean mipmap, qboolean alpha);
+int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel);
+int GL_FindTexture (char *identifier);
+
+typedef struct
+{
+       float   x, y, z;
+       float   s, t;
+       float   r, g, b;
+} glvert_t;
+
+extern glvert_t glv;
+
+extern int glx, gly, glwidth, glheight;
+
+#ifdef _WIN32
+extern PROC glArrayElementEXT;
+extern PROC glColorPointerEXT;
+extern PROC glTexturePointerEXT;
+extern PROC glVertexPointerEXT;
+#endif
+
+// r_local.h -- private refresh defs
+
+#define ALIAS_BASE_SIZE_RATIO          (1.0 / 11.0)
+                                       // normalizing factor so player model works out to about
+                                       //  1 pixel per triangle
+#define        MAX_LBM_HEIGHT          480
+
+#define BACKFACE_EPSILON       0.01
+
+
+void R_TimeRefresh_f (void);
+void R_ReadPointFile_f (void);
+
+typedef struct surfcache_s
+{
+       struct surfcache_s      *next;
+       struct surfcache_s      **owner;                // NULL is an empty chunk of memory
+       int                                     lightadj[MAXLIGHTMAPS]; // checked for strobe flush
+       int                                     dlight;
+       int                                     size;           // including header
+       unsigned                        width;
+       unsigned                        height;         // DEBUG only needed for debug
+       float                           mipscale;
+       struct texture_s        *texture;       // checked for animating textures
+       byte                            data[4];        // width*height elements
+} surfcache_t;
+
+
+typedef struct
+{
+       pixel_t         *surfdat;       // destination for generated surface
+       msurface_t      *surf;          // description for surface to generate
+       fixed8_t        lightadj[MAXLIGHTMAPS];
+                                                       // adjust for lightmap levels for dynamic lighting
+       texture_t       *texture;       // corrected for animating textures
+       int                     surfmip;        // mipmapped ratio of surface texels / world pixels
+       int                     surfwidth;      // in mipmapped texels
+       int                     surfheight;     // in mipmapped texels
+} drawsurf_t;
+
+// LordHavoc: added dust, smoke, snow, bloodcloud, and many others
+typedef enum {
+       pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2, pt_dust, pt_smoke, pt_snow, pt_bulletpuff, pt_bloodcloud, pt_fadespark, pt_fadespark2, pt_fallfadespark, pt_fallfadespark2, pt_bubble
+} ptype_t;
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct particle_s
+{
+// driver-usable fields
+       vec3_t          org;
+       float           color;
+// drivers never touch the following fields
+       struct particle_s       *next;
+       vec3_t          vel;
+       float           ramp;
+       float           die;
+       ptype_t         type;
+       // LordHavoc: added for improved particle effects
+       float           scale;
+       int                     texnum;
+       float           alpha; // 0-255
+       float           time2; // used for various things (snow fluttering, for example)
+} particle_t;
+
+
+//====================================================
+
+
+extern entity_t        r_worldentity;
+extern qboolean        r_cache_thrash;         // compatability
+extern vec3_t          modelorg, r_entorigin;
+extern entity_t        *currententity;
+extern int                     r_visframecount;        // ??? what difs?
+extern int                     r_framecount;
+extern mplane_t        frustum[4];
+extern int             c_brush_polys, c_alias_polys;
+
+
+//
+// view origin
+//
+extern vec3_t  vup;
+extern vec3_t  vpn;
+extern vec3_t  vright;
+extern vec3_t  r_origin;
+
+//
+// screen size info
+//
+extern refdef_t        r_refdef;
+extern mleaf_t         *r_viewleaf, *r_oldviewleaf;
+extern texture_t       *r_notexture_mip;
+extern int             d_lightstylevalue[256]; // 8.8 fraction of base light value
+
+extern qboolean        envmap;
+extern int     cnttextures[2];
+// LordHavoc: moved all code relating to particles into r_part.c
+//extern       int     particletexture;
+extern int     playertextures;
+
+extern int     skytexturenum;          // index in cl.loadmodel, not gl texture object
+
+//extern       cvar_t  r_norefresh;
+extern cvar_t  r_drawentities;
+//extern       cvar_t  r_drawworld;
+extern cvar_t  r_drawviewmodel;
+extern cvar_t  r_speeds;
+//extern       cvar_t  r_waterwarp;
+extern cvar_t  r_fullbright;
+//extern       cvar_t  r_lightmap;
+//extern       cvar_t  r_shadows;
+extern cvar_t  r_wateralpha;
+//extern       cvar_t  r_dynamic;
+extern cvar_t  r_novis;
+extern cvar_t  r_waterripple;
+
+//extern       cvar_t  gl_cull;
+//extern       cvar_t  gl_poly;
+//extern       cvar_t  gl_smoothmodels;
+//extern       cvar_t  gl_affinemodels;
+//extern       cvar_t  gl_polyblend;
+//extern       cvar_t  gl_keeptjunctions;
+//extern       cvar_t  gl_reporttjunctions;
+//extern       cvar_t  gl_flashblend;
+//extern       cvar_t  gl_nocolors;
+//extern       cvar_t  gl_doubleeyes;
+
+extern cvar_t  gl_max_size;
+extern cvar_t  gl_playermip;
+
+extern float   r_world_matrix[16];
+
+extern const char *gl_vendor;
+extern const char *gl_renderer;
+extern const char *gl_version;
+extern const char *gl_extensions;
+
+void R_TranslatePlayerSkin (int playernum);
+
+// Multitexture
+#define    TEXTURE0_SGIS                               0x835E
+#define    TEXTURE1_SGIS                               0x835F
+
+#ifndef _WIN32
+#define APIENTRY /* */
+#endif
+
+extern void (APIENTRY *qglMTexCoord2f) (GLenum, GLfloat, GLfloat);
+extern void (APIENTRY *qglSelectTexture) (GLenum);
+
+extern qboolean gl_mtexable;
+
+// LordHavoc: ARB multitexure support
+extern int             gl_mtex_enum;
+// Micro$oft dropped GL support beyond 1.1, so...
+#ifdef WIN32
+
+//#define GL_POLYGON_OFFSET_POINT                      0x2A01
+//#define GL_POLYGON_OFFSET_LINE                       0x2A02
+//#define GL_POLYGON_OFFSET_FILL                       0x8037
+
+#define GL_ACTIVE_TEXTURE_ARB                  0x84E0
+#define GL_CLIENT_ACTIVE_TEXTURE_ARB   0x84E1
+#define GL_MAX_TEXTURES_UNITS_ARB              0x84E2
+#define GL_TEXTURE0_ARB                                        0x84C0
+#define GL_TEXTURE1_ARB                                        0x84C1
+#define GL_TEXTURE2_ARB                                        0x84C2
+#define GL_TEXTURE3_ARB                                        0x84C3
+// LordHavoc: ARB supports 32+ texture units, but hey I only use 2 anyway...
+
+// LordHavoc: vertex array defines
+#define GL_VERTEX_ARRAY                                        0x8074
+//#define GL_NORMAL_ARRAY                                      0x8075
+#define GL_COLOR_ARRAY                                 0x8076
+//#define GL_INDEX_ARRAY                                       0x8077
+#define GL_TEXTURE_COORD_ARRAY                 0x8078
+//#define GL_EDGE_FLAG_ARRAY                           0x8079
+/*
+#define GL_V2F                                                 0x2A20
+#define GL_V3F                                                 0x2A21
+#define GL_C4UB_V2F                                            0x2A22
+#define GL_C4UB_V3F                                            0x2A23
+#define GL_C3F_V3F                                             0x2A24
+#define GL_N3F_V3F                                             0x2A25
+#define GL_C4F_N3F_V3F                                 0x2A26
+#define GL_T2F_V3F                                             0x2A27
+#define GL_T4F_V4F                                             0x2A28
+#define GL_T2F_C4UB_V3F                                        0x2A29
+#define GL_T2F_C3F_V3F                                 0x2A2A
+#define GL_T2F_N3F_V3F                                 0x2A2B
+#define GL_T2F_C4F_N3F_V3F                             0x2A2C
+#define GL_T4F_C4F_N3F_V4F                             0x2A2D
+*/
+
+//void (APIENTRY *qglPolygonOffset)(GLfloat factor, GLfloat units);
+void (APIENTRY *qglVertexPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr);
+//void (APIENTRY *qglNormalPointer)(GLenum type, GLsizei stride, const GLvoid *ptr);
+void (APIENTRY *qglColorPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr);
+//void (APIENTRY *qglIndexPointer)(GLenum type, GLsizei stride, const GLvoid *ptr);
+void (APIENTRY *qglTexCoordPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr);
+//void (APIENTRY *qglEdgeFlagPointer)(GLsizei stride, const GLvoid *ptr);
+//void (APIENTRY *qglGetPointerv)(GLenum pname, void **params);
+void (APIENTRY *qglArrayElement)(GLint i);
+//void (APIENTRY *qglDrawArrays)(GLenum mode, GLint first, GLsizei count);
+void (APIENTRY *qglDrawElements)(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+//void (APIENTRY *qglInterleavedArrays)(GLenum format, GLsizei stride, const GLvoid *pointer);
+
+#else
+
+//#define qglPolygonOffset glPolygonOffset
+#define qglVertexPointer glVertexPointer
+//#define qglNormalPointer glNormalPointer
+#define qglColorPointer glColorPointer
+//#define qglIndexPointer glIndexPointer
+#define qglTexCoordPointer glTexCoordPointer
+//#define qglEdgeFlagPointer glEdgeFlagPointer
+//#define qglGetPointerv glGetPointerv
+#define qglArrayElement glArrayElement
+//#define qglDrawArrays glDrawArrays
+#define qglDrawElements glDrawElements
+//#define qglInterleavedArrays glInterleavedArrays
+
+#endif
+
+void (APIENTRY *qglMTexCoord2f) (GLenum, GLfloat, GLfloat);
+void (APIENTRY *qglSelectTexture) (GLenum);
+
+void (APIENTRY *glColorTableEXT)(int, int, int, int, int, const void*);
+
+
+// LordHavoc: vertex transform
+#include "transform.h"
+
+// LordHavoc: transparent polygon system
+#include "gl_poly.h"
+
+#define gl_solid_format 3
+#define gl_alpha_format 4
+
+//#define PARANOID
+
+// LordHavoc: was a major time waster
+#define R_CullBox(mins,maxs) (frustum[0].BoxOnPlaneSideFunc(mins, maxs, &frustum[0]) == 2 || frustum[1].BoxOnPlaneSideFunc(mins, maxs, &frustum[1]) == 2 || frustum[2].BoxOnPlaneSideFunc(mins, maxs, &frustum[2]) == 2 || frustum[3].BoxOnPlaneSideFunc(mins, maxs, &frustum[3]) == 2)
+
+extern qboolean fogenabled;
+extern vec3_t fogcolor;
+extern vec_t fogdensity;
+//#define calcfog(v) (exp(-(fogdensity*fogdensity*(((v)[0] - r_refdef.vieworg[0]) * vpn[0] + ((v)[1] - r_refdef.vieworg[1]) * vpn[1] + ((v)[2] - r_refdef.vieworg[2]) * vpn[2])*(((v)[0] - r_refdef.vieworg[0]) * vpn[0] + ((v)[1] - r_refdef.vieworg[1]) * vpn[1] + ((v)[2] - r_refdef.vieworg[2]) * vpn[2]))))
+#define calcfog(v) (exp(-(fogdensity*fogdensity*(((v)[0] - r_refdef.vieworg[0])*((v)[0] - r_refdef.vieworg[0])+((v)[1] - r_refdef.vieworg[1])*((v)[1] - r_refdef.vieworg[1])+((v)[2] - r_refdef.vieworg[2])*((v)[2] - r_refdef.vieworg[2])))))
+#define calcfogbyte(v) ((byte) (bound(0, ((int) ((float) (calcfog((v)) * 255.0f))), 255)))
diff --git a/hcompress.c b/hcompress.c
new file mode 100644 (file)
index 0000000..1db33de
--- /dev/null
@@ -0,0 +1,336 @@
+
+// LordHavoc: my little compression library
+
+#if 0
+#include <stdlib.h>
+
+#ifndef byte
+typedef unsigned char byte;
+#endif
+
+#define HCOMPRESS_CORRUPT -1
+
+typedef struct
+{
+       int position;
+       int size;
+       byte *data;
+} hcblock;
+
+typedef struct
+{
+       int identifer;
+       int compressedsize;
+       int decompressedsize;
+       int compressedcrc;
+       int decompressedcrc;
+       byte data[0];
+} storagehcblock;
+
+int hc_readimmediate(hcblock *b)
+{
+       return b->data[b->position++];
+}
+
+int hc_readsize(hcblock *b)
+{
+       b->position += 2;
+       return b->data[b->position - 2];
+}
+
+int hc_readoffset(hcblock *b)
+{
+       b->position += 2;
+       return b->data[b->position - 2];
+}
+
+int hc_readbit(hcblock *b)
+{
+       return b->data[b->position++];
+}
+
+void hc_writeimmediate(hcblock *b, int num)
+{
+       b->data[b->size++] = num;
+}
+
+void hc_writesize(hcblock *b, int num)
+{
+       b->data[b->size] = num;
+       b->size += 2;
+}
+
+void hc_writeoffset(hcblock *b, int num)
+{
+       b->data[b->size] = num;
+       b->size += 2;
+}
+
+void hc_writebit(hcblock *b, int num)
+{
+       b->data[b->size++] = num;
+}
+
+int hcompress_decompress(void *inaddr, void *outaddr, int insize, int outsize)
+{
+       /*
+       byte *in, *out;
+       hcblock b;
+       b.position = 0;
+       b.size = 0;
+       b.
+       b = inaddr;
+       if (
+       int commandbits, commandbyte, count, temp;
+       in = inaddr;
+       out = outaddr;
+       while (outsize && insize)
+       {
+               hc_readbit(
+               if (!commandbits)
+               {
+                       if (!insize)
+                               return HCOMPRESS_CORRUPT;
+                       commandbyte = *in++;
+                       commandbits = 8;
+                       insize--;
+                       if (!insize)
+                               return HCOMPRESS_CORRUPT;
+               }
+               if (commandbyte)
+               {
+                       for (;commandbits && outsize;commandbits--,commandbyte >>= 1)
+                       {
+                               if (commandbyte & 1) // reference
+                               {
+                                       if (insize < 2)
+                                               return HCOMPRESS_CORRUPT;
+                                       size = (in[0] >> 4) + 3;
+                                       if (size > outsize)
+                                               return HCOMPRESS_CORRUPT;
+                                       insize -= 2;
+                                       outsize -= size;
+                                       tempout = out - ((((in[0] << 8) | in[1]) & 0xFFF) + 1);
+                                       if ((int) tempout < (int) outaddr)
+                                               return HCOMPRESS_CORRUPT;
+                                       while (size--)
+                                               *out++ = *tempout++;
+                               }
+                               else
+                               {
+                                       if (!insize || !outsize)
+                                               return HCOMPRESS_CORRUPT;
+                                       *out++ = *in++;
+                                       insize--;
+                                       outsize--;
+                               }
+                       }
+               }
+               else // copy 8 bytes straight
+               {
+                       if (insize < 8 || outsize < 8)
+                               return HCOMPRESS_CORRUPT;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       *out++ = *in++;
+                       insize -= 8;
+                       outsize -= 8;
+               }
+       }
+       if (insize || outsize)
+               return HCOMPRESS_CORRUPT;
+       return ((int) out - (int) outaddr);
+       */
+       return HCOMPRESS_CORRUPT;
+}
+
+int hcompress_compress(void *indata, void *outdata, int size)
+{
+       byte *in, *out;
+       struct hctoken
+       {
+               unsigned short size; // if size == 0, offset holds the immediate value
+               unsigned short offset;
+       } *token;
+       int offsetcount[65536];
+       int sizecount[256];
+       int tokens = 0;
+       int position = 0;
+       int c, i, j, l, bestsize, bestposition, maxlen;
+       int *h;
+       byte *c1, *c2;
+       struct
+       {
+               int start; // start of the chain
+               int length; // length of the chain
+       } hashindex[256][256];
+       int *hashtable;
+       token = malloc(size*sizeof(struct hctoken));
+       hashtable = malloc(size*sizeof(int));
+       in = indata;
+       memset(&hashindex, 0, sizeof(hashindex));
+       // count the chain lengths
+       for (i = 0;i < size-1;i++)
+               hashindex[in[i]][in[i+1]].length++;
+       hashindex[in[i]][0].length++;
+       // assign starting positions for each chain
+       c = 0;
+       for (i = 0;i < 256;i++)
+       {
+               for (j = 0;j < 256;j++)
+               {
+                       hashindex[i][j].start = c;
+                       c += hashindex[i][j].length;
+               }
+       }
+       // enter the data into the chains
+       for (i = 0;i < size-1;i++)
+               hashtable[hashindex[in[i]][in[i+1]].start++] = i;
+       hashtable[hashindex[in[i]][0].start++] = i;
+       // adjust start positions back to what they should be
+       for (i = 0;i < 256;i++)
+               for (j = 0;j < 256;j++)
+                       hashindex[i][j].start -= hashindex[i][j].length;
+       // now the real work
+       out = outdata;
+       while (position < size)
+       {
+               c = *in++;
+               if (position + 1 == size) // this is the last byte
+               {
+                       h = &hashtable[hashindex[c][0].start];
+                       l = hashindex[c][0].length;
+               }
+               else
+               {
+                       h = &hashtable[hashindex[c][*in].start];
+                       l = hashindex[c][0].length;
+               }
+               if (l)
+               {
+                       if (*h < position - 65535) // too old, nudge up the chain to avoid finding this one again
+                       {
+                               if (position + 1 == size)
+                               {
+                                       hashindex[c][0].start++;
+                                       hashindex[c][0].length--;
+                               }
+                               else
+                               {
+                                       hashindex[c][*in].start++;
+                                       hashindex[c][*in].length--;
+                               }
+                               h++;
+                               l--;
+                       }
+                       if (l)
+                       {
+                               bestsize = 0;
+                               bestposition = 0;
+                               while (l--)
+                               {
+                                       c1 = &in[*h];
+                                       c2 = &in[position];
+                                       maxlen = size - position;
+                                       if (maxlen > 258)
+                                               maxlen = 258;
+                                       for (i = 0;i < maxlen;i++)
+                                               if (*c1++ != *c2++)
+                                                       break;
+                                       if (i > bestsize)
+                                       {
+                                               bestsize = i;
+                                               bestposition = *h;
+                                       }
+                                       h++;
+                               }
+                               if (bestsize >= 3)
+                               {
+                                       // write a reference
+                                       token[tokens].size = bestsize;
+                                       token[tokens++].offset = position - bestposition; // offset backward
+                                       sizecount[bestsize - 3]++;
+                                       offsetcount[position - bestposition]++;
+                               }
+                               else
+                               {
+                                       // write an immediate
+                                       token[tokens].size = 0;
+                                       token[tokens++].offset = c;
+                               }
+                       }
+                       else
+                       {
+                               // no remaining occurances, write an immediate
+                               token[tokens].size = 0;
+                               token[tokens++].offset = c;
+                       }
+               }
+               else
+               {
+                       // no remaining occurances, write an immediate
+                       token[tokens].size = 0;
+                       token[tokens++].offset = c;
+               }
+       }
+       return HCOMPRESS_CORRUPT;
+
+       /*
+       int i, index, insize = size, outsize = 0, commandbyte = 0, commandbits = 0;
+       struct hcompress_hashchain
+       {
+               short prev, next;
+               int key;
+       } hashchain[4096];
+       short hashindex[65536];
+       int hashvalue[4096];
+       struct
+       {
+               byte type;
+               unsigned short data;
+       } ref[8];
+       for (i = 0;i < 65536;i++)
+               hashindex[i] = -1;
+       for (i = 0;i < 4096;i++)
+       {
+               hashchain[i].next = -1;
+               hashchain[i].key = -1;
+       }
+       in = indata;
+       out = outdata;
+       while(insize)
+       {
+               if (insize >= 3) // enough data left to compress
+               {
+                       key = in[0] | (in[1] << 8);
+                       if (hashindex[
+                       for (
+                       index = ((int) in + 1) & 0xFFF;
+                       if (hash[index].key >= 0)
+                       {
+                               if (hashindex[hash[index].key] == index)
+                                       hashindex[hash[index].key] = -1;
+                               if (hash[index].prev >= 0)
+                                       hash[hash[index].prev].next = hash[index].next;
+                       }
+                       hash[index].key = key;
+                       hash[index].next = hashindex[key];
+                       hashindex[key] = index;
+               }
+               else
+               {
+                       while (insize--)
+                       {
+                               ref[commandbits].type = 0;
+                               ref[commandbits++].data = *in++;
+                       }
+               }
+       }
+       */
+}
+#endif
\ No newline at end of file
diff --git a/host.c b/host.c
new file mode 100644 (file)
index 0000000..292f522
--- /dev/null
+++ b/host.c
@@ -0,0 +1,978 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// host.c -- coordinates spawning and killing of local servers
+
+#include "quakedef.h"
+
+/*
+
+A server can allways be started, even if the system started out as a client
+to a remote system.
+
+A client can NOT be started if the system started as a dedicated server.
+
+Memory is cleared / released when a server or client begins, not when they end.
+
+*/
+
+quakeparms_t host_parms;
+
+qboolean       host_initialized;               // true if into command execution
+
+double         host_frametime;
+double         host_time;
+double         realtime;                               // without any filtering or bounding
+double         oldrealtime;                    // last frame run
+int                    host_framecount;
+
+int                    host_hunklevel;
+
+int                    minimum_memory;
+
+client_t       *host_client;                   // current client
+
+jmp_buf        host_abortserver;
+
+byte           *host_basepal;
+byte           *host_colormap;
+
+cvar_t host_framerate = {"host_framerate","0"};        // set for slow motion
+cvar_t host_speeds = {"host_speeds","0"};                      // set for running times
+cvar_t slowmo = {"slowmo", "1.0"};                                     // LordHavoc: framerate independent slowmo
+
+cvar_t sys_ticrate = {"sys_ticrate","0.05"};
+cvar_t serverprofile = {"serverprofile","0"};
+
+cvar_t fraglimit = {"fraglimit","0",false,true};
+cvar_t timelimit = {"timelimit","0",false,true};
+cvar_t teamplay = {"teamplay","0",false,true};
+
+cvar_t samelevel = {"samelevel","0"};
+cvar_t noexit = {"noexit","0",false,true};
+
+cvar_t developer = {"developer","0"};
+
+cvar_t skill = {"skill","1"};                                          // 0 - 3
+cvar_t deathmatch = {"deathmatch","0"};                        // 0, 1, or 2
+cvar_t coop = {"coop","0"};                    // 0 or 1
+
+cvar_t pausable = {"pausable","1"};
+
+cvar_t temp1 = {"temp1","0"};
+
+
+/*
+================
+Host_EndGame
+================
+*/
+void Host_EndGame (char *message, ...)
+{
+       va_list         argptr;
+       char            string[1024];
+       
+       va_start (argptr,message);
+       vsprintf (string,message,argptr);
+       va_end (argptr);
+       Con_DPrintf ("Host_EndGame: %s\n",string);
+       
+       if (sv.active)
+               Host_ShutdownServer (false);
+
+       if (cls.state == ca_dedicated)
+               Sys_Error ("Host_EndGame: %s\n",string);        // dedicated servers exit
+       
+       if (cls.demonum != -1)
+               CL_NextDemo ();
+       else
+               CL_Disconnect ();
+
+       longjmp (host_abortserver, 1);
+}
+
+/*
+================
+Host_Error
+
+This shuts down both the client and server
+================
+*/
+void Host_Error (char *error, ...)
+{
+       va_list         argptr;
+       char            string[1024];
+       static  qboolean inerror = false;
+       
+       if (inerror)
+               Sys_Error ("Host_Error: recursively entered");
+       inerror = true;
+       
+       SCR_EndLoadingPlaque ();                // reenable screen updates
+
+       va_start (argptr,error);
+       vsprintf (string,error,argptr);
+       va_end (argptr);
+       Con_Printf ("Host_Error: %s\n",string);
+       
+       if (sv.active)
+               Host_ShutdownServer (false);
+
+       if (cls.state == ca_dedicated)
+               Sys_Error ("Host_Error: %s\n",string);  // dedicated servers exit
+
+       CL_Disconnect ();
+       cls.demonum = -1;
+
+       inerror = false;
+
+       longjmp (host_abortserver, 1);
+}
+
+/*
+================
+Host_FindMaxClients
+================
+*/
+void   Host_FindMaxClients (void)
+{
+       int             i;
+
+       svs.maxclients = 1;
+               
+       i = COM_CheckParm ("-dedicated");
+       if (i)
+       {
+               cls.state = ca_dedicated;
+               if (i != (com_argc - 1))
+               {
+                       svs.maxclients = atoi (com_argv[i+1]);
+               }
+               else
+                       svs.maxclients = 8;
+       }
+       else
+               cls.state = ca_disconnected;
+
+       i = COM_CheckParm ("-listen");
+       if (i)
+       {
+               if (cls.state == ca_dedicated)
+                       Sys_Error ("Only one of -dedicated or -listen can be specified");
+               if (i != (com_argc - 1))
+                       svs.maxclients = atoi (com_argv[i+1]);
+               else
+                       svs.maxclients = 8;
+       }
+       if (svs.maxclients < 1)
+               svs.maxclients = 8;
+       else if (svs.maxclients > MAX_SCOREBOARD)
+               svs.maxclients = MAX_SCOREBOARD;
+
+       svs.maxclientslimit = svs.maxclients;
+       if (svs.maxclientslimit < MAX_SCOREBOARD) // LordHavoc: upped listen mode limit from 4 to MAX_SCOREBOARD
+               svs.maxclientslimit = MAX_SCOREBOARD;
+       svs.clients = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
+
+       if (svs.maxclients > 1)
+               Cvar_SetValue ("deathmatch", 1.0);
+       else
+               Cvar_SetValue ("deathmatch", 0.0);
+}
+
+
+/*
+=======================
+Host_InitLocal
+======================
+*/
+void Host_InitLocal (void)
+{
+       Host_InitCommands ();
+       
+       Cvar_RegisterVariable (&host_framerate);
+       Cvar_RegisterVariable (&host_speeds);
+       Cvar_RegisterVariable (&slowmo);
+
+       Cvar_RegisterVariable (&sys_ticrate);
+       Cvar_RegisterVariable (&serverprofile);
+
+       Cvar_RegisterVariable (&fraglimit);
+       Cvar_RegisterVariable (&timelimit);
+       Cvar_RegisterVariable (&teamplay);
+       Cvar_RegisterVariable (&samelevel);
+       Cvar_RegisterVariable (&noexit);
+       Cvar_RegisterVariable (&skill);
+       Cvar_RegisterVariable (&developer);
+       Cvar_RegisterVariable (&deathmatch);
+       Cvar_RegisterVariable (&coop);
+
+       Cvar_RegisterVariable (&pausable);
+
+       Cvar_RegisterVariable (&temp1);
+
+       Host_FindMaxClients ();
+       
+       host_time = 1.0;                // so a think at time 0 won't get called
+}
+
+
+/*
+===============
+Host_WriteConfiguration
+
+Writes key bindings and archived cvars to config.cfg
+===============
+*/
+void Host_WriteConfiguration (void)
+{
+       FILE    *f;
+
+// dedicated servers initialize the host but don't parse and set the
+// config.cfg cvars
+       if (host_initialized & !isDedicated)
+       {
+               f = fopen (va("%s/config.cfg",com_gamedir), "w");
+               if (!f)
+               {
+                       Con_Printf ("Couldn't write config.cfg.\n");
+                       return;
+               }
+               
+               Key_WriteBindings (f);
+               Cvar_WriteVariables (f);
+
+               fclose (f);
+       }
+}
+
+
+/*
+=================
+SV_ClientPrintf
+
+Sends text across to be displayed 
+FIXME: make this just a stuffed echo?
+=================
+*/
+void SV_ClientPrintf (char *fmt, ...)
+{
+       va_list         argptr;
+       char            string[1024];
+       
+       va_start (argptr,fmt);
+       vsprintf (string, fmt,argptr);
+       va_end (argptr);
+       
+       MSG_WriteByte (&host_client->message, svc_print);
+       MSG_WriteString (&host_client->message, string);
+}
+
+/*
+=================
+SV_BroadcastPrintf
+
+Sends text to all active clients
+=================
+*/
+void SV_BroadcastPrintf (char *fmt, ...)
+{
+       va_list         argptr;
+       char            string[1024];
+       int                     i;
+       
+       va_start (argptr,fmt);
+       vsprintf (string, fmt,argptr);
+       va_end (argptr);
+       
+       for (i=0 ; i<svs.maxclients ; i++)
+               if (svs.clients[i].active && svs.clients[i].spawned)
+               {
+                       MSG_WriteByte (&svs.clients[i].message, svc_print);
+                       MSG_WriteString (&svs.clients[i].message, string);
+               }
+}
+
+/*
+=================
+Host_ClientCommands
+
+Send text over to the client to be executed
+=================
+*/
+void Host_ClientCommands (char *fmt, ...)
+{
+       va_list         argptr;
+       char            string[1024];
+       
+       va_start (argptr,fmt);
+       vsprintf (string, fmt,argptr);
+       va_end (argptr);
+       
+       MSG_WriteByte (&host_client->message, svc_stufftext);
+       MSG_WriteString (&host_client->message, string);
+}
+
+/*
+=====================
+SV_DropClient
+
+Called when the player is getting totally kicked off the host
+if (crash = true), don't bother sending signofs
+=====================
+*/
+void SV_DropClient (qboolean crash)
+{
+       int             saveSelf;
+       int             i;
+       client_t *client;
+
+       if (!crash)
+       {
+               // send any final messages (don't check for errors)
+               if (NET_CanSendMessage (host_client->netconnection))
+               {
+                       MSG_WriteByte (&host_client->message, svc_disconnect);
+                       NET_SendMessage (host_client->netconnection, &host_client->message);
+               }
+       
+               if (host_client->edict && host_client->spawned)
+               {
+               // call the prog function for removing a client
+               // this will set the body to a dead frame, among other things
+                       saveSelf = pr_global_struct->self;
+                       pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
+                       PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
+                       pr_global_struct->self = saveSelf;
+               }
+
+               Sys_Printf ("Client %s removed\n",host_client->name);
+       }
+
+// break the net connection
+       NET_Close (host_client->netconnection);
+       host_client->netconnection = NULL;
+
+// free the client (the body stays around)
+       host_client->active = false;
+       host_client->name[0] = 0;
+       host_client->old_frags = -999999;
+       net_activeconnections--;
+
+// send notification to all clients
+       for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+       {
+               if (!client->active)
+                       continue;
+               MSG_WriteByte (&client->message, svc_updatename);
+               MSG_WriteByte (&client->message, host_client - svs.clients);
+               MSG_WriteString (&client->message, "");
+               MSG_WriteByte (&client->message, svc_updatefrags);
+               MSG_WriteByte (&client->message, host_client - svs.clients);
+               MSG_WriteShort (&client->message, 0);
+               MSG_WriteByte (&client->message, svc_updatecolors);
+               MSG_WriteByte (&client->message, host_client - svs.clients);
+               MSG_WriteByte (&client->message, 0);
+       }
+}
+
+/*
+==================
+Host_ShutdownServer
+
+This only happens at the end of a game, not between levels
+==================
+*/
+void Host_ShutdownServer(qboolean crash)
+{
+       int             i;
+       int             count;
+       sizebuf_t       buf;
+       char            message[4];
+       double  start;
+
+       if (!sv.active)
+               return;
+
+       sv.active = false;
+
+// stop all client sounds immediately
+       if (cls.state == ca_connected)
+               CL_Disconnect ();
+
+// flush any pending messages - like the score!!!
+       start = Sys_FloatTime();
+       do
+       {
+               count = 0;
+               for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+               {
+                       if (host_client->active && host_client->message.cursize)
+                       {
+                               if (NET_CanSendMessage (host_client->netconnection))
+                               {
+                                       NET_SendMessage(host_client->netconnection, &host_client->message);
+                                       SZ_Clear (&host_client->message);
+                               }
+                               else
+                               {
+                                       NET_GetMessage(host_client->netconnection);
+                                       count++;
+                               }
+                       }
+               }
+               if ((Sys_FloatTime() - start) > 3.0)
+                       break;
+       }
+       while (count);
+
+// make sure all the clients know we're disconnecting
+       buf.data = message;
+       buf.maxsize = 4;
+       buf.cursize = 0;
+       MSG_WriteByte(&buf, svc_disconnect);
+       count = NET_SendToAll(&buf, 5);
+       if (count)
+               Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
+
+       for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+               if (host_client->active)
+                       SV_DropClient(crash);
+
+//
+// clear structures
+//
+       memset (&sv, 0, sizeof(sv));
+       memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
+}
+
+
+/*
+================
+Host_ClearMemory
+
+This clears all the memory used by both the client and server, but does
+not reinitialize anything.
+================
+*/
+void Host_ClearMemory (void)
+{
+       Con_DPrintf ("Clearing memory\n");
+       D_FlushCaches ();
+       Mod_ClearAll ();
+       if (host_hunklevel)
+               Hunk_FreeToLowMark (host_hunklevel);
+
+       cls.signon = 0;
+       memset (&sv, 0, sizeof(sv));
+       memset (&cl, 0, sizeof(cl));
+}
+
+
+//============================================================================
+
+extern cvar_t maxfps;
+
+/*
+===================
+Host_FilterTime
+
+Returns false if the time is too short to run a frame
+===================
+*/
+qboolean Host_FilterTime (float time)
+{
+       realtime += time;
+
+       if (maxfps.value < 5) // LordHavoc: sanity checking
+               maxfps.value = 5;
+       if (maxfps.value > 1000) // LordHavoc: sanity checking
+               maxfps.value = 1000;
+       if (!cls.timedemo && realtime - oldrealtime < (1.0 / maxfps.value))
+               return false;           // framerate is too high
+
+       host_frametime = (realtime - oldrealtime) * slowmo.value; // LordHavoc: slowmo cvar
+       oldrealtime = realtime;
+
+       if (host_framerate.value > 0)
+               host_frametime = host_framerate.value;
+       else
+       {       // don't allow really long or short frames
+               if (host_frametime > 0.1)
+                       host_frametime = 0.1;
+               if (host_frametime < 0.001)
+                       host_frametime = 0.001;
+       }
+       
+       return true;
+}
+
+
+/*
+===================
+Host_GetConsoleCommands
+
+Add them exactly as if they had been typed at the console
+===================
+*/
+void Host_GetConsoleCommands (void)
+{
+       char    *cmd;
+
+       while (1)
+       {
+               cmd = Sys_ConsoleInput ();
+               if (!cmd)
+                       break;
+               Cbuf_AddText (cmd);
+       }
+}
+
+
+/*
+==================
+Host_ServerFrame
+
+==================
+*/
+#ifdef FPS_20
+
+void _Host_ServerFrame (void)
+{
+// run the world state 
+       pr_global_struct->frametime = host_frametime;
+
+// read client messages
+       SV_RunClients ();
+       
+// move things around and think
+// always pause in single player if in console or menus
+       if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+               SV_Physics ();
+}
+
+void Host_ServerFrame (void)
+{
+       float   save_host_frametime;
+       float   temp_host_frametime;
+       static float    host_serverframe_timevalue;
+
+// run the world state 
+       pr_global_struct->frametime = host_frametime;
+
+// set the time and clear the general datagram
+       SV_ClearDatagram ();
+               
+// check for new clients
+       SV_CheckForNewClients ();
+
+       temp_host_frametime = save_host_frametime = host_frametime;
+       // LordHavoc: the results of my attempts to mangle this code to process no more than sys_ticrate, 
+       // when I found that was too choppy, I changed it back to processing at least 20fps,
+       // I consider it a bit of a failure...  because I felt a little out of control in singleplayer
+       // (sliding around)
+       //if (host_serverframe_timevalue < -0.2) // don't let it get way out of range
+       //      host_serverframe_timevalue = -0.2;
+       //host_serverframe_timevalue += host_frametime;
+       // process frames (several if rendering is too slow to run well as a server)
+       while(temp_host_frametime > 0.0)
+       {
+               host_frametime = temp_host_frametime;
+               if (host_frametime > 0.05)
+                       host_frametime = 0.05;
+               temp_host_frametime -= host_frametime;
+       //      host_serverframe_timevalue -= host_frametime;
+               _Host_ServerFrame ();
+       }
+       host_frametime = save_host_frametime;
+
+// send all messages to the clients
+       SV_SendClientMessages ();
+// LordHavoc: sadly, this didn't look good to the person running the server in listen mode
+       /*
+// wait until enough time has built up when the framerate exceeds sys_ticrate
+       if (host_serverframe_timevalue >= sys_ticrate.value)
+       //{
+       //      while(host_serverframe_timevalue >= sys_ticrate.value)
+       //              host_serverframe_timevalue -= sys_ticrate.value;
+// send all messages to the clients
+               SV_SendClientMessages ();
+       }
+       */
+}
+
+#else
+
+void Host_ServerFrame (void)
+{
+// run the world state 
+       pr_global_struct->frametime = host_frametime;
+
+// set the time and clear the general datagram
+       SV_ClearDatagram ();
+       
+// check for new clients
+       SV_CheckForNewClients ();
+
+// read client messages
+       SV_RunClients ();
+       
+// move things around and think
+// always pause in single player if in console or menus
+       if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+               SV_Physics ();
+
+// send all messages to the clients
+       SV_SendClientMessages ();
+}
+
+#endif
+
+
+/*
+==================
+Host_Frame
+
+Runs all active servers
+==================
+*/
+void _Host_Frame (float time)
+{
+       static double           time1 = 0;
+       static double           time2 = 0;
+       static double           time3 = 0;
+       int                     pass1, pass2, pass3;
+
+       if (setjmp (host_abortserver) )
+               return;                 // something bad happened, or the server disconnected
+
+// keep the random time dependent
+       rand ();
+       
+// decide the simulation time
+       if (!Host_FilterTime (time))
+               return;                 // don't run too fast, or packets will flood out
+               
+// get new key events
+       Sys_SendKeyEvents ();
+
+// allow mice or other external controllers to add commands
+       IN_Commands ();
+
+// process console commands
+       Cbuf_Execute ();
+
+       NET_Poll();
+
+// if running the server locally, make intentions now
+       if (sv.active)
+               CL_SendCmd ();
+       
+//-------------------
+//
+// server operations
+//
+//-------------------
+
+// check for commands typed to the host
+       Host_GetConsoleCommands ();
+       
+       if (sv.active)
+               Host_ServerFrame ();
+
+//-------------------
+//
+// client operations
+//
+//-------------------
+
+// if running the server remotely, send intentions now after
+// the incoming messages have been read
+       if (!sv.active)
+               CL_SendCmd ();
+
+       host_time += host_frametime;
+
+// fetch results from server
+       if (cls.state == ca_connected)
+       {
+               CL_ReadFromServer ();
+       }
+
+// update video
+       if (host_speeds.value)
+               time1 = Sys_FloatTime ();
+               
+       SCR_UpdateScreen ();
+
+       if (host_speeds.value)
+               time2 = Sys_FloatTime ();
+               
+// update audio
+       if (cls.signon == SIGNONS)
+       {
+               S_Update (r_origin, vpn, vright, vup);
+               CL_DecayLights ();
+       }
+       else
+               S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
+       
+       CDAudio_Update();
+
+       if (host_speeds.value)
+       {
+               pass1 = (time1 - time3)*1000;
+               time3 = Sys_FloatTime ();
+               pass2 = (time2 - time1)*1000;
+               pass3 = (time3 - time2)*1000;
+               Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
+                                       pass1+pass2+pass3, pass1, pass2, pass3);
+       }
+       
+       host_framecount++;
+}
+
+void Host_Frame (float time)
+{
+       double  time1, time2;
+       static double   timetotal;
+       static int              timecount;
+       int             i, c, m;
+
+       if (!serverprofile.value)
+       {
+               _Host_Frame (time);
+               return;
+       }
+       
+       time1 = Sys_FloatTime ();
+       _Host_Frame (time);
+       time2 = Sys_FloatTime ();       
+       
+       timetotal += time2 - time1;
+       timecount++;
+       
+       if (timecount < 1000)
+               return;
+
+       m = timetotal*1000/timecount;
+       timecount = 0;
+       timetotal = 0;
+       c = 0;
+       for (i=0 ; i<svs.maxclients ; i++)
+       {
+               if (svs.clients[i].active)
+                       c++;
+       }
+
+       Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m);
+}
+
+//============================================================================
+
+
+extern int vcrFile;
+#define        VCR_SIGNATURE   0x56435231
+// "VCR1"
+
+void Host_InitVCR (quakeparms_t *parms)
+{
+       int             i, len, n;
+       char    *p;
+       
+       if (COM_CheckParm("-playback"))
+       {
+               if (com_argc != 2)
+                       Sys_Error("No other parameters allowed with -playback\n");
+
+               Sys_FileOpenRead("quake.vcr", &vcrFile);
+               if (vcrFile == -1)
+                       Sys_Error("playback file not found\n");
+
+               Sys_FileRead (vcrFile, &i, sizeof(int));
+               if (i != VCR_SIGNATURE)
+                       Sys_Error("Invalid signature in vcr file\n");
+
+               Sys_FileRead (vcrFile, &com_argc, sizeof(int));
+               com_argv = malloc(com_argc * sizeof(char *));
+               com_argv[0] = parms->argv[0];
+               for (i = 0; i < com_argc; i++)
+               {
+                       Sys_FileRead (vcrFile, &len, sizeof(int));
+                       p = malloc(len);
+                       Sys_FileRead (vcrFile, p, len);
+                       com_argv[i+1] = p;
+               }
+               com_argc++; /* add one for arg[0] */
+               parms->argc = com_argc;
+               parms->argv = com_argv;
+       }
+
+       if ( (n = COM_CheckParm("-record")) != 0)
+       {
+               vcrFile = Sys_FileOpenWrite("quake.vcr");
+
+               i = VCR_SIGNATURE;
+               Sys_FileWrite(vcrFile, &i, sizeof(int));
+               i = com_argc - 1;
+               Sys_FileWrite(vcrFile, &i, sizeof(int));
+               for (i = 1; i < com_argc; i++)
+               {
+                       if (i == n)
+                       {
+                               len = 10;
+                               Sys_FileWrite(vcrFile, &len, sizeof(int));
+                               Sys_FileWrite(vcrFile, "-playback", len);
+                               continue;
+                       }
+                       len = strlen(com_argv[i]) + 1;
+                       Sys_FileWrite(vcrFile, &len, sizeof(int));
+                       Sys_FileWrite(vcrFile, com_argv[i], len);
+               }
+       }
+       
+}
+
+/*
+====================
+Host_Init
+====================
+*/
+void Host_Init (quakeparms_t *parms)
+{
+
+       if (standard_quake)
+               minimum_memory = MINIMUM_MEMORY;
+       else
+               minimum_memory = MINIMUM_MEMORY_LEVELPAK;
+
+       if (COM_CheckParm ("-minmemory"))
+               parms->memsize = minimum_memory;
+
+       host_parms = *parms;
+
+       if (parms->memsize < minimum_memory)
+               Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000);
+
+       com_argc = parms->argc;
+       com_argv = parms->argv;
+
+       Memory_Init (parms->membase, parms->memsize);
+       Cbuf_Init ();
+       Cmd_Init ();    
+       V_Init ();
+       Chase_Init ();
+       Host_InitVCR (parms);
+       COM_Init (parms->basedir);
+       Host_InitLocal ();
+       W_LoadWadFile ("gfx.wad");
+       Key_Init ();
+       Con_Init ();    
+       M_Init ();      
+       PR_Init ();
+       Mod_Init ();
+       NET_Init ();
+       SV_Init ();
+
+       Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+       Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
+       
+       R_InitTextures ();              // needed even for dedicated servers
+       if (cls.state != ca_dedicated)
+       {
+               host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp", false);
+               if (!host_basepal)
+                       Sys_Error ("Couldn't load gfx/palette.lmp");
+               host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp", false);
+               if (!host_colormap)
+                       Sys_Error ("Couldn't load gfx/colormap.lmp");
+
+#ifndef _WIN32 // on non win32, mouse comes before video for security reasons
+               IN_Init ();
+#endif
+               VID_Init (host_basepal);
+
+               Draw_Init ();
+               SCR_Init ();
+               R_Init ();
+#ifndef        _WIN32
+       // on Win32, sound initialization has to come before video initialization, so we
+       // can put up a popup if the sound hardware is in use
+               S_Init ();
+#else
+
+       // FIXME: doesn't use the new one-window approach yet
+               S_Init ();
+
+#endif // _WIN32
+               CDAudio_Init ();
+               Sbar_Init ();
+               CL_Init ();
+#ifdef _WIN32 // on non win32, mouse comes before video for security reasons
+               IN_Init ();
+#endif
+       }
+
+       Cbuf_InsertText ("exec quake.rc\n");
+
+       Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
+       host_hunklevel = Hunk_LowMark ();
+
+       host_initialized = true;
+       
+       Sys_Printf ("========Quake Initialized=========\n");    
+}
+
+
+/*
+===============
+Host_Shutdown
+
+FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
+to run quit through here before the final handoff to the sys code.
+===============
+*/
+void Host_Shutdown(void)
+{
+       static qboolean isdown = false;
+       
+       if (isdown)
+       {
+               printf ("recursive shutdown\n");
+               return;
+       }
+       isdown = true;
+
+// keep Con_Printf from trying to update the screen
+       scr_disabled_for_loading = true;
+
+       Host_WriteConfiguration (); 
+
+       CDAudio_Shutdown ();
+       NET_Shutdown ();
+       S_Shutdown();
+       IN_Shutdown ();
+
+       if (cls.state != ca_dedicated)
+       {
+               VID_Shutdown();
+       }
+}
+
diff --git a/host_cmd.c b/host_cmd.c
new file mode 100644 (file)
index 0000000..f23dc03
--- /dev/null
@@ -0,0 +1,1701 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+extern cvar_t  pausable;
+
+int    current_skill;
+
+void Mod_Print (void);
+
+/*
+==================
+Host_Quit_f
+==================
+*/
+
+extern void M_Menu_Quit_f (void);
+
+void Host_Quit_f (void)
+{
+       if (key_dest != key_console && cls.state != ca_dedicated)
+       {
+               M_Menu_Quit_f ();
+               return;
+       }
+       CL_Disconnect ();
+       Host_ShutdownServer(false);             
+
+       Sys_Quit ();
+}
+
+
+/*
+==================
+Host_Status_f
+==================
+*/
+void Host_Status_f (void)
+{
+       client_t        *client;
+       int                     seconds;
+       int                     minutes;
+       int                     hours = 0;
+       int                     j;
+       void            (*print) (char *fmt, ...);
+       
+       if (cmd_source == src_command)
+       {
+               if (!sv.active)
+               {
+                       Cmd_ForwardToServer ();
+                       return;
+               }
+               print = Con_Printf;
+       }
+       else
+               print = SV_ClientPrintf;
+
+       print ("host:    %s\n", Cvar_VariableString ("hostname"));
+       print ("version: %4.2f\n", VERSION);
+       if (tcpipAvailable)
+               print ("tcp/ip:  %s\n", my_tcpip_address);
+       if (ipxAvailable)
+               print ("ipx:     %s\n", my_ipx_address);
+       print ("map:     %s\n", sv.name);
+       print ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
+       for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+       {
+               if (!client->active)
+                       continue;
+               seconds = (int)(net_time - client->netconnection->connecttime);
+               minutes = seconds / 60;
+               if (minutes)
+               {
+                       seconds -= (minutes * 60);
+                       hours = minutes / 60;
+                       if (hours)
+                               minutes -= (hours * 60);
+               }
+               else
+                       hours = 0;
+               print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds);
+               print ("   %s\n", client->netconnection->address);
+       }
+}
+
+
+/*
+==================
+Host_God_f
+
+Sets client to godmode
+==================
+*/
+void Host_God_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (pr_global_struct->deathmatch && !host_client->privileged)
+               return;
+
+       sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
+       if (!((int)sv_player->v.flags & FL_GODMODE) )
+               SV_ClientPrintf ("godmode OFF\n");
+       else
+               SV_ClientPrintf ("godmode ON\n");
+}
+
+void Host_Notarget_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (pr_global_struct->deathmatch && !host_client->privileged)
+               return;
+
+       sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET;
+       if (!((int)sv_player->v.flags & FL_NOTARGET) )
+               SV_ClientPrintf ("notarget OFF\n");
+       else
+               SV_ClientPrintf ("notarget ON\n");
+}
+
+qboolean noclip_anglehack;
+
+void Host_Noclip_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (pr_global_struct->deathmatch && !host_client->privileged)
+               return;
+
+       if (sv_player->v.movetype != MOVETYPE_NOCLIP)
+       {
+               noclip_anglehack = true;
+               sv_player->v.movetype = MOVETYPE_NOCLIP;
+               SV_ClientPrintf ("noclip ON\n");
+       }
+       else
+       {
+               noclip_anglehack = false;
+               sv_player->v.movetype = MOVETYPE_WALK;
+               SV_ClientPrintf ("noclip OFF\n");
+       }
+}
+
+/*
+==================
+Host_Fly_f
+
+Sets client to flymode
+==================
+*/
+void Host_Fly_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (pr_global_struct->deathmatch && !host_client->privileged)
+               return;
+
+       if (sv_player->v.movetype != MOVETYPE_FLY)
+       {
+               sv_player->v.movetype = MOVETYPE_FLY;
+               SV_ClientPrintf ("flymode ON\n");
+       }
+       else
+       {
+               sv_player->v.movetype = MOVETYPE_WALK;
+               SV_ClientPrintf ("flymode OFF\n");
+       }
+}
+
+
+/*
+==================
+Host_Ping_f
+
+==================
+*/
+void Host_Ping_f (void)
+{
+       int             i, j;
+       float   total;
+       client_t        *client;
+       
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       SV_ClientPrintf ("Client ping times:\n");
+       for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+       {
+               if (!client->active)
+                       continue;
+               total = 0;
+               for (j=0 ; j<NUM_PING_TIMES ; j++)
+                       total+=client->ping_times[j];
+               total /= NUM_PING_TIMES;
+               SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name);
+       }
+}
+
+/*
+===============================================================================
+
+SERVER TRANSITIONS
+
+===============================================================================
+*/
+
+
+/*
+======================
+Host_Map_f
+
+handle a 
+map <servername>
+command from the console.  Active clients are kicked off.
+======================
+*/
+void Host_Map_f (void)
+{
+       int             i;
+       char    name[MAX_QPATH];
+
+       if (cmd_source != src_command)
+               return;
+
+       cls.demonum = -1;               // stop demo loop in case this fails
+
+       CL_Disconnect ();
+       Host_ShutdownServer(false);             
+
+       key_dest = key_game;                    // remove console or menu
+       SCR_BeginLoadingPlaque ();
+
+       cls.mapstring[0] = 0;
+       for (i=0 ; i<Cmd_Argc() ; i++)
+       {
+               strcat (cls.mapstring, Cmd_Argv(i));
+               strcat (cls.mapstring, " ");
+       }
+       strcat (cls.mapstring, "\n");
+
+       svs.serverflags = 0;                    // haven't completed an episode yet
+       strcpy (name, Cmd_Argv(1));
+       SV_SpawnServer (name);
+       if (!sv.active)
+               return;
+       
+       if (cls.state != ca_dedicated)
+       {
+               strcpy (cls.spawnparms, "");
+
+               for (i=2 ; i<Cmd_Argc() ; i++)
+               {
+                       strcat (cls.spawnparms, Cmd_Argv(i));
+                       strcat (cls.spawnparms, " ");
+               }
+               
+               Cmd_ExecuteString ("connect local", src_command);
+       }       
+}
+
+/*
+==================
+Host_Changelevel_f
+
+Goes to a new map, taking all clients along
+==================
+*/
+void Host_Changelevel_f (void)
+{
+       char    level[MAX_QPATH];
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("changelevel <levelname> : continue game on a new level\n");
+               return;
+       }
+       if (!sv.active || cls.demoplayback)
+       {
+               Con_Printf ("Only the server may changelevel\n");
+               return;
+       }
+       SV_SaveSpawnparms ();
+       strcpy (level, Cmd_Argv(1));
+       SV_SpawnServer (level);
+}
+
+/*
+==================
+Host_Restart_f
+
+Restarts the current server for a dead player
+==================
+*/
+void Host_Restart_f (void)
+{
+       char    mapname[MAX_QPATH];
+
+       if (cls.demoplayback || !sv.active)
+               return;
+
+       if (cmd_source != src_command)
+               return;
+       strcpy (mapname, sv.name);      // must copy out, because it gets cleared
+                                                               // in sv_spawnserver
+       SV_SpawnServer (mapname);
+}
+
+/*
+==================
+Host_Reconnect_f
+
+This command causes the client to wait for the signon messages again.
+This is sent just before a server changes levels
+==================
+*/
+void Host_Reconnect_f (void)
+{
+       SCR_BeginLoadingPlaque ();
+       cls.signon = 0;         // need new connection messages
+}
+
+/*
+=====================
+Host_Connect_f
+
+User command to connect to server
+=====================
+*/
+void Host_Connect_f (void)
+{
+       char    name[MAX_QPATH];
+       
+       cls.demonum = -1;               // stop demo loop in case this fails
+       if (cls.demoplayback)
+       {
+               CL_StopPlayback ();
+               CL_Disconnect ();
+       }
+       strcpy (name, Cmd_Argv(1));
+       CL_EstablishConnection (name);
+       Host_Reconnect_f ();
+}
+
+
+/*
+===============================================================================
+
+LOAD / SAVE GAME
+
+===============================================================================
+*/
+
+#define        SAVEGAME_VERSION        5
+
+/*
+===============
+Host_SavegameComment
+
+Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current 
+===============
+*/
+void Host_SavegameComment (char *text)
+{
+       int             i;
+       char    kills[20];
+
+       for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+               text[i] = ' ';
+       memcpy (text, cl.levelname, strlen(cl.levelname));
+       sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
+       memcpy (text+22, kills, strlen(kills));
+// convert space to _ to make stdio happy
+       for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+               if (text[i] == ' ')
+                       text[i] = '_';
+       text[SAVEGAME_COMMENT_LENGTH] = '\0';
+}
+
+
+/*
+===============
+Host_Savegame_f
+===============
+*/
+void Host_Savegame_f (void)
+{
+       char    name[256];
+       FILE    *f;
+       int             i;
+       char    comment[SAVEGAME_COMMENT_LENGTH+1];
+
+       if (cmd_source != src_command)
+               return;
+
+       if (!sv.active)
+       {
+               Con_Printf ("Not playing a local game.\n");
+               return;
+       }
+
+       if (cl.intermission)
+       {
+               Con_Printf ("Can't save in intermission.\n");
+               return;
+       }
+
+       if (svs.maxclients != 1)
+       {
+               Con_Printf ("Can't save multiplayer games.\n");
+               return;
+       }
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("save <savename> : save a game\n");
+               return;
+       }
+
+       if (strstr(Cmd_Argv(1), ".."))
+       {
+               Con_Printf ("Relative pathnames are not allowed.\n");
+               return;
+       }
+               
+       for (i=0 ; i<svs.maxclients ; i++)
+       {
+               if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) )
+               {
+                       Con_Printf ("Can't savegame with a dead player\n");
+                       return;
+               }
+       }
+
+       sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+       COM_DefaultExtension (name, ".sav");
+       
+       Con_Printf ("Saving game to %s...\n", name);
+       f = fopen (name, "w");
+       if (!f)
+       {
+               Con_Printf ("ERROR: couldn't open.\n");
+               return;
+       }
+       
+       fprintf (f, "%i\n", SAVEGAME_VERSION);
+       Host_SavegameComment (comment);
+       fprintf (f, "%s\n", comment);
+       for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+               fprintf (f, "%f\n", svs.clients->spawn_parms[i]);
+       fprintf (f, "%d\n", current_skill);
+       fprintf (f, "%s\n", sv.name);
+       fprintf (f, "%f\n",sv.time);
+
+// write the light styles
+
+       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+       {
+               if (sv.lightstyles[i])
+                       fprintf (f, "%s\n", sv.lightstyles[i]);
+               else
+                       fprintf (f,"m\n");
+       }
+
+
+       ED_WriteGlobals (f);
+       for (i=0 ; i<sv.num_edicts ; i++)
+       {
+               ED_Write (f, EDICT_NUM(i));
+               fflush (f);
+       }
+       fclose (f);
+       Con_Printf ("done.\n");
+}
+
+
+/*
+===============
+Host_Loadgame_f
+===============
+*/
+void Host_Loadgame_f (void)
+{
+       char    name[MAX_OSPATH];
+       FILE    *f;
+       char    mapname[MAX_QPATH];
+       float   time, tfloat;
+       char    str[32768], *start;
+       int             i, r;
+       edict_t *ent;
+       int             entnum;
+       int             version;
+       float                   spawn_parms[NUM_SPAWN_PARMS];
+
+       if (cmd_source != src_command)
+               return;
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("load <savename> : load a game\n");
+               return;
+       }
+
+       cls.demonum = -1;               // stop demo loop in case this fails
+
+       sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+       COM_DefaultExtension (name, ".sav");
+       
+// we can't call SCR_BeginLoadingPlaque, because too much stack space has
+// been used.  The menu calls it before stuffing loadgame command
+//     SCR_BeginLoadingPlaque ();
+
+       Con_Printf ("Loading game from %s...\n", name);
+       f = fopen (name, "r");
+       if (!f)
+       {
+               Con_Printf ("ERROR: couldn't open.\n");
+               return;
+       }
+
+       fscanf (f, "%i\n", &version);
+       if (version != SAVEGAME_VERSION)
+       {
+               fclose (f);
+               Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+               return;
+       }
+       fscanf (f, "%s\n", str);
+       for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+               fscanf (f, "%f\n", &spawn_parms[i]);
+// this silliness is so we can load 1.06 save files, which have float skill values
+       fscanf (f, "%f\n", &tfloat);
+       current_skill = (int)(tfloat + 0.1);
+       Cvar_SetValue ("skill", (float)current_skill);
+
+       fscanf (f, "%s\n",mapname);
+       fscanf (f, "%f\n",&time);
+
+       CL_Disconnect_f ();
+       
+       SV_SpawnServer (mapname);
+       if (!sv.active)
+       {
+               Con_Printf ("Couldn't load map\n");
+               return;
+       }
+       sv.paused = true;               // pause until all clients connect
+       sv.loadgame = true;
+
+// load the light styles
+
+       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+       {
+               fscanf (f, "%s\n", str);
+               sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1);
+               strcpy (sv.lightstyles[i], str);
+       }
+
+// load the edicts out of the savegame file
+       entnum = -1;            // -1 is the globals
+       while (!feof(f))
+       {
+               for (i=0 ; i<sizeof(str)-1 ; i++)
+               {
+                       r = fgetc (f);
+                       if (r == EOF || !r)
+                               break;
+                       str[i] = r;
+                       if (r == '}')
+                       {
+                               i++;
+                               break;
+                       }
+               }
+               if (i == sizeof(str)-1)
+                       Sys_Error ("Loadgame buffer overflow");
+               str[i] = 0;
+               start = str;
+               start = COM_Parse(str);
+               if (!com_token[0])
+                       break;          // end of file
+               if (strcmp(com_token,"{"))
+                       Sys_Error ("First token isn't a brace");
+                       
+               if (entnum == -1)
+               {       // parse the global vars
+                       ED_ParseGlobals (start);
+               }
+               else
+               {       // parse an edict
+
+                       ent = EDICT_NUM(entnum);
+                       memset (&ent->v, 0, progs->entityfields * 4);
+                       ent->free = false;
+                       ED_ParseEdict (start, ent);
+       
+               // link it into the bsp tree
+                       if (!ent->free)
+                               SV_LinkEdict (ent, false);
+               }
+
+               entnum++;
+       }
+       
+       sv.num_edicts = entnum;
+       sv.time = time;
+
+       fclose (f);
+
+       for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+               svs.clients->spawn_parms[i] = spawn_parms[i];
+
+       if (cls.state != ca_dedicated)
+       {
+               CL_EstablishConnection ("local");
+               Host_Reconnect_f ();
+       }
+}
+
+//============================================================================
+
+/*
+======================
+Host_Name_f
+======================
+*/
+void Host_Name_f (void)
+{
+       char    *newName;
+
+       if (Cmd_Argc () == 1)
+       {
+               Con_Printf ("\"name\" is \"%s\"\n", cl_name.string);
+               return;
+       }
+       if (Cmd_Argc () == 2)
+               newName = Cmd_Argv(1);  
+       else
+               newName = Cmd_Args();
+       newName[15] = 0;
+
+       if (cmd_source == src_command)
+       {
+               if (strcmp(cl_name.string, newName) == 0)
+                       return;
+               Cvar_Set ("_cl_name", newName);
+               if (cls.state == ca_connected)
+                       Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (host_client->name[0] && strcmp(host_client->name, "unconnected") )
+               if (strcmp(host_client->name, newName) != 0)
+                       Con_Printf ("%s renamed to %s\n", host_client->name, newName);
+       strcpy (host_client->name, newName);
+       host_client->edict->v.netname = host_client->name - pr_strings;
+       
+// send notification to all clients
+       
+       MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
+       MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+       MSG_WriteString (&sv.reliable_datagram, host_client->name);
+}
+
+       
+void Host_Version_f (void)
+{
+       Con_Printf ("Version %4.2f\n", VERSION);
+       Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+}
+
+#ifdef IDGODS
+void Host_Please_f (void)
+{
+       client_t *cl;
+       int                     j;
+       
+       if (cmd_source != src_command)
+               return;
+
+       if ((Cmd_Argc () == 3) && strcmp(Cmd_Argv(1), "#") == 0)
+       {
+               j = atof(Cmd_Argv(2)) - 1;
+               if (j < 0 || j >= svs.maxclients)
+                       return;
+               if (!svs.clients[j].active)
+                       return;
+               cl = &svs.clients[j];
+               if (cl->privileged)
+               {
+                       cl->privileged = false;
+                       cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET);
+                       cl->edict->v.movetype = MOVETYPE_WALK;
+                       noclip_anglehack = false;
+               }
+               else
+                       cl->privileged = true;
+       }
+
+       if (Cmd_Argc () != 2)
+               return;
+
+       for (j=0, cl = svs.clients ; j<svs.maxclients ; j++, cl++)
+       {
+               if (!cl->active)
+                       continue;
+               if (strcasecmp(cl->name, Cmd_Argv(1)) == 0)
+               {
+                       if (cl->privileged)
+                       {
+                               cl->privileged = false;
+                               cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET);
+                               cl->edict->v.movetype = MOVETYPE_WALK;
+                               noclip_anglehack = false;
+                       }
+                       else
+                               cl->privileged = true;
+                       break;
+               }
+       }
+}
+#endif
+
+
+void Host_Say(qboolean teamonly)
+{
+       client_t *client;
+       client_t *save;
+       int             j;
+       char    *p;
+       // LordHavoc: 256 char say messages
+       unsigned char   text[256];
+       qboolean        fromServer = false;
+
+       if (cmd_source == src_command)
+       {
+               if (cls.state == ca_dedicated)
+               {
+                       fromServer = true;
+                       teamonly = false;
+               }
+               else
+               {
+                       Cmd_ForwardToServer ();
+                       return;
+               }
+       }
+
+       if (Cmd_Argc () < 2)
+               return;
+
+       save = host_client;
+
+       p = Cmd_Args();
+// remove quotes if present
+       if (*p == '"')
+       {
+               p++;
+               p[strlen(p)-1] = 0;
+       }
+
+// turn on color set 1
+       if (!fromServer)
+               sprintf (text, "%c%s: ", 1, save->name);
+       else
+               sprintf (text, "%c<%s> ", 1, hostname.string);
+
+       j = sizeof(text) - 2 - strlen(text);  // -2 for /n and null terminator
+       if (strlen(p) > j)
+               p[j] = 0;
+
+       strcat (text, p);
+       strcat (text, "\n");
+
+       for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+       {
+               if (!client || !client->active || !client->spawned)
+                       continue;
+               if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team)
+                       continue;
+               host_client = client;
+               SV_ClientPrintf("%s", text);
+       }
+       host_client = save;
+
+       Sys_Printf("%s", &text[1]);
+}
+
+
+void Host_Say_f(void)
+{
+       Host_Say(false);
+}
+
+
+void Host_Say_Team_f(void)
+{
+       Host_Say(true);
+}
+
+
+void Host_Tell_f(void)
+{
+       client_t *client;
+       client_t *save;
+       int             j;
+       char    *p;
+       char    text[64];
+
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (Cmd_Argc () < 3)
+               return;
+
+       strcpy(text, host_client->name);
+       strcat(text, ": ");
+
+       p = Cmd_Args();
+
+// remove quotes if present
+       if (*p == '"')
+       {
+               p++;
+               p[strlen(p)-1] = 0;
+       }
+
+// check length & truncate if necessary
+       j = sizeof(text) - 2 - strlen(text);  // -2 for /n and null terminator
+       if (strlen(p) > j)
+               p[j] = 0;
+
+       strcat (text, p);
+       strcat (text, "\n");
+
+       save = host_client;
+       for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+       {
+               if (!client->active || !client->spawned)
+                       continue;
+               if (Q_strcasecmp(client->name, Cmd_Argv(1)))
+                       continue;
+               host_client = client;
+               SV_ClientPrintf("%s", text);
+               break;
+       }
+       host_client = save;
+}
+
+
+/*
+==================
+Host_Color_f
+==================
+*/
+void Host_Color_f(void)
+{
+       int             top, bottom;
+       int             playercolor;
+       
+       if (Cmd_Argc() == 1)
+       {
+               Con_Printf ("\"color\" is \"%i %i\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f);
+               Con_Printf ("color <0-13> [0-13]\n");
+               return;
+       }
+
+       if (Cmd_Argc() == 2)
+               top = bottom = atoi(Cmd_Argv(1));
+       else
+       {
+               top = atoi(Cmd_Argv(1));
+               bottom = atoi(Cmd_Argv(2));
+       }
+       
+       top &= 15;
+       // LordHavoc: allow skin colormaps 14 and 15 (was 13)
+       if (top > 15)
+               top = 15;
+       bottom &= 15;
+       // LordHavoc: allow skin colormaps 14 and 15 (was 13)
+       if (bottom > 15)
+               bottom = 15;
+       
+       playercolor = top*16 + bottom;
+
+       if (cmd_source == src_command)
+       {
+               Cvar_SetValue ("_cl_color", playercolor);
+               if (cls.state == ca_connected)
+                       Cmd_ForwardToServer ();
+               return;
+       }
+
+       host_client->colors = playercolor;
+       host_client->edict->v.team = bottom + 1;
+
+// send notification to all clients
+       MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
+       MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+       MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
+}
+
+/*
+==================
+Host_Kill_f
+==================
+*/
+void Host_Kill_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (sv_player->v.health <= 0)
+       {
+               SV_ClientPrintf ("Can't suicide -- allready dead!\n");
+               return;
+       }
+       
+       pr_global_struct->time = sv.time;
+       pr_global_struct->self = EDICT_TO_PROG(sv_player);
+       PR_ExecuteProgram (pr_global_struct->ClientKill);
+}
+
+
+/*
+==================
+Host_Pause_f
+==================
+*/
+void Host_Pause_f (void)
+{
+       
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+       if (!pausable.value)
+               SV_ClientPrintf ("Pause not allowed.\n");
+       else
+       {
+               sv.paused ^= 1;
+
+               if (sv.paused)
+               {
+                       SV_BroadcastPrintf ("%s paused the game\n", pr_strings + sv_player->v.netname);
+               }
+               else
+               {
+                       SV_BroadcastPrintf ("%s unpaused the game\n",pr_strings + sv_player->v.netname);
+               }
+
+       // send notification to all clients
+               MSG_WriteByte (&sv.reliable_datagram, svc_setpause);
+               MSG_WriteByte (&sv.reliable_datagram, sv.paused);
+       }
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_PreSpawn_f
+==================
+*/
+void Host_PreSpawn_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Con_Printf ("prespawn is not valid from the console\n");
+               return;
+       }
+
+       if (host_client->spawned)
+       {
+               Con_Printf ("prespawn not valid -- allready spawned\n");
+               return;
+       }
+       
+       SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
+       MSG_WriteByte (&host_client->message, svc_signonnum);
+       MSG_WriteByte (&host_client->message, 2);
+       host_client->sendsignon = true;
+}
+
+dfunction_t *ED_FindFunction (char *name);
+
+/*
+==================
+Host_Spawn_f
+==================
+*/
+void Host_Spawn_f (void)
+{
+       int             i;
+       client_t        *client;
+       edict_t *ent;
+       func_t RestoreGame;
+       dfunction_t *f;
+
+       if (cmd_source == src_command)
+       {
+               Con_Printf ("spawn is not valid from the console\n");
+               return;
+       }
+
+       if (host_client->spawned)
+       {
+               Con_Printf ("Spawn not valid -- allready spawned\n");
+               return;
+       }
+
+// run the entrance script
+       if (sv.loadgame)
+       {       // loaded games are fully inited allready
+               // if this is the last client to be connected, unpause
+               sv.paused = false;
+
+               if (f = ED_FindFunction ("RestoreGame"))
+               if (RestoreGame = (func_t)(f - pr_functions))
+               {
+                       Con_DPrintf("Calling RestoreGame\n");
+                       pr_global_struct->time = sv.time;
+                       pr_global_struct->self = EDICT_TO_PROG(sv_player);
+                       PR_ExecuteProgram (RestoreGame);
+               }
+       }
+       else
+       {
+               // set up the edict
+               ent = host_client->edict;
+
+               memset (&ent->v, 0, progs->entityfields * 4);
+               ent->v.colormap = NUM_FOR_EDICT(ent);
+               ent->v.team = (host_client->colors & 15) + 1;
+               ent->v.netname = host_client->name - pr_strings;
+
+               // copy spawn parms out of the client_t
+
+               for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+                       (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
+
+               // call the spawn function
+
+               pr_global_struct->time = sv.time;
+               pr_global_struct->self = EDICT_TO_PROG(sv_player);
+               PR_ExecuteProgram (pr_global_struct->ClientConnect);
+
+               if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
+                       Sys_Printf ("%s entered the game\n", host_client->name);
+
+               PR_ExecuteProgram (pr_global_struct->PutClientInServer);        
+       }
+
+
+// send all current names, colors, and frag counts
+       SZ_Clear (&host_client->message);
+
+// send time of update
+       MSG_WriteByte (&host_client->message, svc_time);
+       MSG_WriteFloat (&host_client->message, sv.time);
+
+       for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+       {
+               MSG_WriteByte (&host_client->message, svc_updatename);
+               MSG_WriteByte (&host_client->message, i);
+               MSG_WriteString (&host_client->message, client->name);
+               MSG_WriteByte (&host_client->message, svc_updatefrags);
+               MSG_WriteByte (&host_client->message, i);
+               MSG_WriteShort (&host_client->message, client->old_frags);
+               MSG_WriteByte (&host_client->message, svc_updatecolors);
+               MSG_WriteByte (&host_client->message, i);
+               MSG_WriteByte (&host_client->message, client->colors);
+       }
+       
+// send all current light styles
+       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+       {
+               MSG_WriteByte (&host_client->message, svc_lightstyle);
+               MSG_WriteByte (&host_client->message, (char)i);
+               MSG_WriteString (&host_client->message, sv.lightstyles[i]);
+       }
+
+//
+// send some stats
+//
+       MSG_WriteByte (&host_client->message, svc_updatestat);
+       MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
+       MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
+
+       MSG_WriteByte (&host_client->message, svc_updatestat);
+       MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
+       MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
+
+       MSG_WriteByte (&host_client->message, svc_updatestat);
+       MSG_WriteByte (&host_client->message, STAT_SECRETS);
+       MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
+
+       MSG_WriteByte (&host_client->message, svc_updatestat);
+       MSG_WriteByte (&host_client->message, STAT_MONSTERS);
+       MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
+
+//
+// send a fixangle
+// Never send a roll angle, because savegames can catch the server
+// in a state where it is expecting the client to correct the angle
+// and it won't happen if the game was just loaded, so you wind up
+// with a permanent head tilt
+       ent = EDICT_NUM( 1 + (host_client - svs.clients) );
+       MSG_WriteByte (&host_client->message, svc_setangle);
+       for (i=0 ; i < 2 ; i++)
+               MSG_WriteAngle (&host_client->message, ent->v.angles[i] );
+       MSG_WriteAngle (&host_client->message, 0 );
+
+       SV_WriteClientdataToMessage (sv_player, &host_client->message);
+
+       MSG_WriteByte (&host_client->message, svc_signonnum);
+       MSG_WriteByte (&host_client->message, 3);
+       host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Begin_f
+==================
+*/
+void Host_Begin_f (void)
+{
+       if (cmd_source == src_command)
+       {
+               Con_Printf ("begin is not valid from the console\n");
+               return;
+       }
+
+       host_client->spawned = true;
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_Kick_f
+
+Kicks a user off of the server
+==================
+*/
+void Host_Kick_f (void)
+{
+       char            *who;
+       char            *message = NULL;
+       client_t        *save;
+       int                     i;
+       qboolean        byNumber = false;
+
+       if (cmd_source == src_command)
+       {
+               if (!sv.active)
+               {
+                       Cmd_ForwardToServer ();
+                       return;
+               }
+       }
+       else if (pr_global_struct->deathmatch && !host_client->privileged)
+               return;
+
+       save = host_client;
+
+       if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
+       {
+               i = atof(Cmd_Argv(2)) - 1;
+               if (i < 0 || i >= svs.maxclients)
+                       return;
+               if (!svs.clients[i].active)
+                       return;
+               host_client = &svs.clients[i];
+               byNumber = true;
+       }
+       else
+       {
+               for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
+               {
+                       if (!host_client->active)
+                               continue;
+                       if (Q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
+                               break;
+               }
+       }
+
+       if (i < svs.maxclients)
+       {
+               if (cmd_source == src_command)
+                       if (cls.state == ca_dedicated)
+                               who = "Console";
+                       else
+                               who = cl_name.string;
+               else
+                       who = save->name;
+
+               // can't kick yourself!
+               if (host_client == save)
+                       return;
+
+               if (Cmd_Argc() > 2)
+               {
+                       message = COM_Parse(Cmd_Args());
+                       if (byNumber)
+                       {
+                               message++;                                                      // skip the #
+                               while (*message == ' ')                         // skip white space
+                                       message++;
+                               message += strlen(Cmd_Argv(2)); // skip the number
+                       }
+                       while (*message && *message == ' ')
+                               message++;
+               }
+               if (message)
+                       SV_ClientPrintf ("Kicked by %s: %s\n", who, message);
+               else
+                       SV_ClientPrintf ("Kicked by %s\n", who);
+               SV_DropClient (false);
+       }
+
+       host_client = save;
+}
+
+/*
+===============================================================================
+
+DEBUGGING TOOLS
+
+===============================================================================
+*/
+
+/*
+==================
+Host_Give_f
+==================
+*/
+void Host_Give_f (void)
+{
+       char    *t;
+       int             v;
+       eval_t  *val;
+
+       if (cmd_source == src_command)
+       {
+               Cmd_ForwardToServer ();
+               return;
+       }
+
+       if (pr_global_struct->deathmatch && !host_client->privileged)
+               return;
+
+       t = Cmd_Argv(1);
+       v = atoi (Cmd_Argv(2));
+       
+       switch (t[0])
+       {
+   case '0':
+   case '1':
+   case '2':
+   case '3':
+   case '4':
+   case '5':
+   case '6':
+   case '7':
+   case '8':
+   case '9':
+      // MED 01/04/97 added hipnotic give stuff
+      if (hipnotic)
+      {
+         if (t[0] == '6')
+         {
+            if (t[1] == 'a')
+               sv_player->v.items = (int)sv_player->v.items | HIT_PROXIMITY_GUN;
+            else
+               sv_player->v.items = (int)sv_player->v.items | IT_GRENADE_LAUNCHER;
+         }
+         else if (t[0] == '9')
+            sv_player->v.items = (int)sv_player->v.items | HIT_LASER_CANNON;
+         else if (t[0] == '0')
+            sv_player->v.items = (int)sv_player->v.items | HIT_MJOLNIR;
+         else if (t[0] >= '2')
+            sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
+      }
+      else
+      {
+         if (t[0] >= '2')
+            sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
+      }
+               break;
+       
+    case 's':
+               if (rogue)
+               {
+                   if (val = GETEDICTFIELDVALUE(sv_player, eval_ammo_shells1))
+                           val->_float = v;
+               }
+
+        sv_player->v.ammo_shells = v;
+        break;         
+    case 'n':
+               if (rogue)
+               {
+                   if (val = GETEDICTFIELDVALUE(sv_player, eval_ammo_nails1))
+                       {
+                           val->_float = v;
+                               if (sv_player->v.weapon <= IT_LIGHTNING)
+                                       sv_player->v.ammo_nails = v;
+                       }
+               }
+               else
+               {
+                       sv_player->v.ammo_nails = v;
+               }
+        break;         
+    case 'l':
+               if (rogue)
+               {
+                       val = GETEDICTFIELDVALUE(sv_player, eval_ammo_lava_nails);
+                       if (val)
+                       {
+                               val->_float = v;
+                               if (sv_player->v.weapon > IT_LIGHTNING)
+                                       sv_player->v.ammo_nails = v;
+                       }
+               }
+        break;
+    case 'r':
+               if (rogue)
+               {
+                       val = GETEDICTFIELDVALUE(sv_player, eval_ammo_rockets1);
+                       if (val)
+                       {
+                               val->_float = v;
+                               if (sv_player->v.weapon <= IT_LIGHTNING)
+                                       sv_player->v.ammo_rockets = v;
+                       }
+               }
+               else
+               {
+                       sv_player->v.ammo_rockets = v;
+               }
+        break;         
+    case 'm':
+               if (rogue)
+               {
+                       val = GETEDICTFIELDVALUE(sv_player, eval_ammo_multi_rockets);
+                       if (val)
+                       {
+                               val->_float = v;
+                               if (sv_player->v.weapon > IT_LIGHTNING)
+                                       sv_player->v.ammo_rockets = v;
+                       }
+               }
+        break;         
+    case 'h':
+        sv_player->v.health = v;
+        break;         
+    case 'c':
+               if (rogue)
+               {
+                       val = GETEDICTFIELDVALUE(sv_player, eval_ammo_cells1);
+                       if (val)
+                       {
+                               val->_float = v;
+                               if (sv_player->v.weapon <= IT_LIGHTNING)
+                                       sv_player->v.ammo_cells = v;
+                       }
+               }
+               else
+               {
+                       sv_player->v.ammo_cells = v;
+               }
+        break;         
+    case 'p':
+               if (rogue)
+               {
+                       val = GETEDICTFIELDVALUE(sv_player, eval_ammo_plasma);
+                       if (val)
+                       {
+                               val->_float = v;
+                               if (sv_player->v.weapon > IT_LIGHTNING)
+                                       sv_player->v.ammo_cells = v;
+                       }
+               }
+        break;         
+    }
+}
+
+edict_t        *FindViewthing (void)
+{
+       int             i;
+       edict_t *e;
+       
+       for (i=0 ; i<sv.num_edicts ; i++)
+       {
+               e = EDICT_NUM(i);
+               if ( !strcmp (pr_strings + e->v.classname, "viewthing") )
+                       return e;
+       }
+       Con_Printf ("No viewthing on map\n");
+       return NULL;
+}
+
+/*
+==================
+Host_Viewmodel_f
+==================
+*/
+void Host_Viewmodel_f (void)
+{
+       edict_t *e;
+       model_t *m;
+
+       e = FindViewthing ();
+       if (!e)
+               return;
+
+       m = Mod_ForName (Cmd_Argv(1), false);
+       if (!m)
+       {
+               Con_Printf ("Can't load %s\n", Cmd_Argv(1));
+               return;
+       }
+       
+       e->v.frame = 0;
+       cl.model_precache[(int)e->v.modelindex] = m;
+}
+
+/*
+==================
+Host_Viewframe_f
+==================
+*/
+void Host_Viewframe_f (void)
+{
+       edict_t *e;
+       int             f;
+       model_t *m;
+
+       e = FindViewthing ();
+       if (!e)
+               return;
+       m = cl.model_precache[(int)e->v.modelindex];
+
+       f = atoi(Cmd_Argv(1));
+       if (f >= m->numframes)
+               f = m->numframes-1;
+
+       e->v.frame = f;         
+}
+
+
+void PrintFrameName (model_t *m, int frame)
+{
+       aliashdr_t                      *hdr;
+       maliasframedesc_t       *pframedesc;
+
+       hdr = (aliashdr_t *)Mod_Extradata (m);
+       if (!hdr)
+               return;
+       pframedesc = &hdr->frames[frame];
+       
+       Con_Printf ("frame %i: %s\n", frame, pframedesc->name);
+}
+
+/*
+==================
+Host_Viewnext_f
+==================
+*/
+void Host_Viewnext_f (void)
+{
+       edict_t *e;
+       model_t *m;
+       
+       e = FindViewthing ();
+       if (!e)
+               return;
+       m = cl.model_precache[(int)e->v.modelindex];
+
+       e->v.frame = e->v.frame + 1;
+       if (e->v.frame >= m->numframes)
+               e->v.frame = m->numframes - 1;
+
+       PrintFrameName (m, e->v.frame);         
+}
+
+/*
+==================
+Host_Viewprev_f
+==================
+*/
+void Host_Viewprev_f (void)
+{
+       edict_t *e;
+       model_t *m;
+
+       e = FindViewthing ();
+       if (!e)
+               return;
+
+       m = cl.model_precache[(int)e->v.modelindex];
+
+       e->v.frame = e->v.frame - 1;
+       if (e->v.frame < 0)
+               e->v.frame = 0;
+
+       PrintFrameName (m, e->v.frame);         
+}
+
+/*
+===============================================================================
+
+DEMO LOOP CONTROL
+
+===============================================================================
+*/
+
+
+/*
+==================
+Host_Startdemos_f
+==================
+*/
+void Host_Startdemos_f (void)
+{
+       int             i, c;
+
+       if (cls.state == ca_dedicated)
+       {
+               if (!sv.active)
+                       Cbuf_AddText ("map start\n");
+               return;
+       }
+
+       c = Cmd_Argc() - 1;
+       if (c > MAX_DEMOS)
+       {
+               Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
+               c = MAX_DEMOS;
+       }
+       Con_Printf ("%i demo(s) in loop\n", c);
+
+       for (i=1 ; i<c+1 ; i++)
+               strncpy (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0])-1);
+
+       if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
+       {
+               cls.demonum = 0;
+               CL_NextDemo ();
+       }
+       else
+               cls.demonum = -1;
+}
+
+
+/*
+==================
+Host_Demos_f
+
+Return to looping demos
+==================
+*/
+void Host_Demos_f (void)
+{
+       if (cls.state == ca_dedicated)
+               return;
+       if (cls.demonum == -1)
+               cls.demonum = 1;
+       CL_Disconnect_f ();
+       CL_NextDemo ();
+}
+
+/*
+==================
+Host_Stopdemo_f
+
+Return to looping demos
+==================
+*/
+void Host_Stopdemo_f (void)
+{
+       if (cls.state == ca_dedicated)
+               return;
+       if (!cls.demoplayback)
+               return;
+       CL_StopPlayback ();
+       CL_Disconnect ();
+}
+
+//=============================================================================
+
+/*
+==================
+Host_InitCommands
+==================
+*/
+void Host_InitCommands (void)
+{
+       Cmd_AddCommand ("status", Host_Status_f);
+       Cmd_AddCommand ("quit", Host_Quit_f);
+       if (nehahra)
+       {
+               Cmd_AddCommand ("max", Host_God_f);
+               Cmd_AddCommand ("monster", Host_Notarget_f);
+               Cmd_AddCommand ("scrag", Host_Fly_f);
+               Cmd_AddCommand ("wraith", Host_Noclip_f);
+               Cmd_AddCommand ("gimme", Host_Give_f);
+       }
+       else
+       {
+               Cmd_AddCommand ("god", Host_God_f);
+               Cmd_AddCommand ("notarget", Host_Notarget_f);
+               Cmd_AddCommand ("fly", Host_Fly_f);
+               Cmd_AddCommand ("noclip", Host_Noclip_f);
+               Cmd_AddCommand ("give", Host_Give_f);
+       }
+       Cmd_AddCommand ("map", Host_Map_f);
+       Cmd_AddCommand ("restart", Host_Restart_f);
+       Cmd_AddCommand ("changelevel", Host_Changelevel_f);
+       Cmd_AddCommand ("connect", Host_Connect_f);
+       Cmd_AddCommand ("reconnect", Host_Reconnect_f);
+       Cmd_AddCommand ("name", Host_Name_f);
+       Cmd_AddCommand ("version", Host_Version_f);
+#ifdef IDGODS
+       Cmd_AddCommand ("please", Host_Please_f);
+#endif
+       Cmd_AddCommand ("say", Host_Say_f);
+       Cmd_AddCommand ("say_team", Host_Say_Team_f);
+       Cmd_AddCommand ("tell", Host_Tell_f);
+       Cmd_AddCommand ("color", Host_Color_f);
+       Cmd_AddCommand ("kill", Host_Kill_f);
+       Cmd_AddCommand ("pause", Host_Pause_f);
+       Cmd_AddCommand ("spawn", Host_Spawn_f);
+       Cmd_AddCommand ("begin", Host_Begin_f);
+       Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
+       Cmd_AddCommand ("kick", Host_Kick_f);
+       Cmd_AddCommand ("ping", Host_Ping_f);
+       Cmd_AddCommand ("load", Host_Loadgame_f);
+       Cmd_AddCommand ("save", Host_Savegame_f);
+
+       Cmd_AddCommand ("startdemos", Host_Startdemos_f);
+       Cmd_AddCommand ("demos", Host_Demos_f);
+       Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
+
+       Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
+       Cmd_AddCommand ("viewframe", Host_Viewframe_f);
+       Cmd_AddCommand ("viewnext", Host_Viewnext_f);
+       Cmd_AddCommand ("viewprev", Host_Viewprev_f);
+
+       Cmd_AddCommand ("mcache", Mod_Print);
+}
diff --git a/image.c b/image.c
new file mode 100644 (file)
index 0000000..a32d58a
--- /dev/null
+++ b/image.c
@@ -0,0 +1,359 @@
+
+#include "quakedef.h"
+
+int            image_width;
+int            image_height;
+
+/*
+=================================================================
+
+  PCX Loading
+
+=================================================================
+*/
+
+typedef struct
+{
+    char       manufacturer;
+    char       version;
+    char       encoding;
+    char       bits_per_pixel;
+    unsigned short     xmin,ymin,xmax,ymax;
+    unsigned short     hres,vres;
+    unsigned char      palette[48];
+    char       reserved;
+    char       color_planes;
+    unsigned short     bytes_per_line;
+    unsigned short     palette_type;
+    char       filler[58];
+    unsigned   data;                   // unbounded
+} pcx_t;
+
+/*
+============
+LoadPCX
+============
+*/
+byte* LoadPCX (FILE *f, int matchwidth, int matchheight)
+{
+       pcx_t   *pcx, pcxbuf;
+       byte    palette[768];
+       byte    *pix, *image_rgba;
+       int             x, y;
+       int             dataByte, runLength;
+       int             count;
+
+//
+// parse the PCX file
+//
+       fread (&pcxbuf, 1, sizeof(pcxbuf), f);
+
+       pcx = &pcxbuf;
+
+       if (pcx->manufacturer != 0x0a
+               || pcx->version != 5
+               || pcx->encoding != 1
+               || pcx->bits_per_pixel != 8
+               || pcx->xmax >= 320
+               || pcx->ymax >= 256)
+       {
+               Con_Printf ("Bad pcx file\n");
+               return NULL;
+       }
+
+       if (matchwidth && (pcx->xmax+1) != matchwidth)
+               return NULL;
+       if (matchheight && (pcx->ymax+1) != matchheight)
+               return NULL;
+
+       // seek to palette
+       fseek (f, -768, SEEK_END);
+       fread (palette, 1, 768, f);
+
+       fseek (f, sizeof(pcxbuf) - 4, SEEK_SET);
+
+       count = (pcx->xmax+1) * (pcx->ymax+1);
+       image_rgba = malloc( count * 4);
+
+       for (y=0 ; y<=pcx->ymax ; y++)
+       {
+               pix = image_rgba + 4*y*(pcx->xmax+1);
+               for (x=0 ; x<=pcx->xmax ; )
+               {
+                       dataByte = fgetc(f);
+
+                       if((dataByte & 0xC0) == 0xC0)
+                       {
+                               runLength = dataByte & 0x3F;
+                               dataByte = fgetc(f);
+                       }
+                       else
+                               runLength = 1;
+
+                       while(runLength-- > 0)
+                       {
+                               pix[0] = palette[dataByte*3];
+                               pix[1] = palette[dataByte*3+1];
+                               pix[2] = palette[dataByte*3+2];
+                               pix[3] = 255;
+                               pix += 4;
+                               x++;
+                       }
+               }
+       }
+       image_width = pcx->xmax+1;
+       image_height = pcx->ymax+1;
+       return image_rgba;
+}
+
+/*
+=========================================================
+
+TARGA LOADING
+
+=========================================================
+*/
+
+typedef struct _TargaHeader {
+       unsigned char   id_length, colormap_type, image_type;
+       unsigned short  colormap_index, colormap_length;
+       unsigned char   colormap_size;
+       unsigned short  x_origin, y_origin, width, height;
+       unsigned char   pixel_size, attributes;
+} TargaHeader;
+
+
+TargaHeader            targa_header;
+
+int fgetLittleShort (FILE *f)
+{
+       byte    b1, b2;
+
+       b1 = fgetc(f);
+       b2 = fgetc(f);
+
+       return (short)(b1 + b2*256);
+}
+
+int fgetLittleLong (FILE *f)
+{
+       byte    b1, b2, b3, b4;
+
+       b1 = fgetc(f);
+       b2 = fgetc(f);
+       b3 = fgetc(f);
+       b4 = fgetc(f);
+
+       return b1 + (b2<<8) + (b3<<16) + (b4<<24);
+}
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+byte* LoadTGA (FILE *fin, int matchwidth, int matchheight)
+{
+       int                             columns, rows, numPixels;
+       byte                    *pixbuf;
+       int                             row, column;
+       byte                    *image_rgba;
+
+       targa_header.id_length = fgetc(fin);
+       targa_header.colormap_type = fgetc(fin);
+       targa_header.image_type = fgetc(fin);
+       
+       targa_header.colormap_index = fgetLittleShort(fin);
+       targa_header.colormap_length = fgetLittleShort(fin);
+       targa_header.colormap_size = fgetc(fin);
+       targa_header.x_origin = fgetLittleShort(fin);
+       targa_header.y_origin = fgetLittleShort(fin);
+       targa_header.width = fgetLittleShort(fin);
+       targa_header.height = fgetLittleShort(fin);
+       if (matchwidth && targa_header.width != matchwidth)
+               return NULL;
+       if (matchheight && targa_header.height != matchheight)
+               return NULL;
+       targa_header.pixel_size = fgetc(fin);
+       targa_header.attributes = fgetc(fin);
+
+       if (targa_header.image_type!=2 
+               && targa_header.image_type!=10) 
+               Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+       if (targa_header.colormap_type !=0 
+               || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
+               Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+       columns = targa_header.width;
+       rows = targa_header.height;
+       numPixels = columns * rows;
+
+       image_rgba = malloc (numPixels*4);
+       
+       if (targa_header.id_length != 0)
+               fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
+       
+       if (targa_header.image_type==2) {  // Uncompressed, RGB images
+               for(row=rows-1; row>=0; row--) {
+                       pixbuf = image_rgba + row*columns*4;
+                       for(column=0; column<columns; column++) {
+                               unsigned char red,green,blue,alphabyte;
+                               switch (targa_header.pixel_size) {
+                                       case 24:
+                                                       
+                                                       blue = getc(fin);
+                                                       green = getc(fin);
+                                                       red = getc(fin);
+                                                       *pixbuf++ = red;
+                                                       *pixbuf++ = green;
+                                                       *pixbuf++ = blue;
+                                                       *pixbuf++ = 255;
+                                                       break;
+                                       case 32:
+                                                       blue = getc(fin);
+                                                       green = getc(fin);
+                                                       red = getc(fin);
+                                                       alphabyte = getc(fin);
+                                                       *pixbuf++ = red;
+                                                       *pixbuf++ = green;
+                                                       *pixbuf++ = blue;
+                                                       *pixbuf++ = alphabyte;
+                                                       break;
+                               }
+                       }
+               }
+       }
+       else if (targa_header.image_type==10) {   // Runlength encoded RGB images
+               unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+               for(row=rows-1; row>=0; row--) {
+                       pixbuf = image_rgba + row*columns*4;
+                       for(column=0; column<columns; ) {
+                               packetHeader=getc(fin);
+                               packetSize = 1 + (packetHeader & 0x7f);
+                               if (packetHeader & 0x80) {        // run-length packet
+                                       switch (targa_header.pixel_size) {
+                                               case 24:
+                                                               blue = getc(fin);
+                                                               green = getc(fin);
+                                                               red = getc(fin);
+                                                               alphabyte = 255;
+                                                               break;
+                                               case 32:
+                                                               blue = getc(fin);
+                                                               green = getc(fin);
+                                                               red = getc(fin);
+                                                               alphabyte = getc(fin);
+                                                               break;
+                                       }
+       
+                                       for(j=0;j<packetSize;j++) {
+                                               *pixbuf++=red;
+                                               *pixbuf++=green;
+                                               *pixbuf++=blue;
+                                               *pixbuf++=alphabyte;
+                                               column++;
+                                               if (column==columns) { // run spans across rows
+                                                       column=0;
+                                                       if (row>0)
+                                                               row--;
+                                                       else
+                                                               goto breakOut;
+                                                       pixbuf = image_rgba + row*columns*4;
+                                               }
+                                       }
+                               }
+                               else {                            // non run-length packet
+                                       for(j=0;j<packetSize;j++) {
+                                               switch (targa_header.pixel_size) {
+                                                       case 24:
+                                                                       blue = getc(fin);
+                                                                       green = getc(fin);
+                                                                       red = getc(fin);
+                                                                       *pixbuf++ = red;
+                                                                       *pixbuf++ = green;
+                                                                       *pixbuf++ = blue;
+                                                                       *pixbuf++ = 255;
+                                                                       break;
+                                                       case 32:
+                                                                       blue = getc(fin);
+                                                                       green = getc(fin);
+                                                                       red = getc(fin);
+                                                                       alphabyte = getc(fin);
+                                                                       *pixbuf++ = red;
+                                                                       *pixbuf++ = green;
+                                                                       *pixbuf++ = blue;
+                                                                       *pixbuf++ = alphabyte;
+                                                                       break;
+                                               }
+                                               column++;
+                                               if (column==columns) { // pixel packet run spans across rows
+                                                       column=0;
+                                                       if (row>0)
+                                                               row--;
+                                                       else
+                                                               goto breakOut;
+                                                       pixbuf = image_rgba + row*columns*4;
+                                               }                                               
+                                       }
+                               }
+                       }
+                       breakOut:;
+               }
+       }
+       
+       fclose(fin);
+       image_width = columns;
+       image_height = rows;
+       return image_rgba;
+}
+
+byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight)
+{
+       FILE    *f;
+       char    basename[128], name[128];
+       byte    *image_rgba;
+       COM_StripExtension(filename, basename); // strip the extension to allow TGA skins on Q2 models despite the .pcx in the skin name
+       sprintf (name, "textures/%s.tga", basename);
+       COM_FOpenFile (name, &f, true);
+       if (f)
+               return LoadTGA (f, matchwidth, matchheight);
+       sprintf (name, "textures/%s.pcx", basename);
+       COM_FOpenFile (name, &f, true);
+       if (f)
+               return LoadPCX (f, matchwidth, matchheight);
+       sprintf (name, "%s.tga", basename);
+       COM_FOpenFile (name, &f, true);
+       if (f)
+               return LoadTGA (f, matchwidth, matchheight);
+       sprintf (name, "%s.pcx", basename);
+       COM_FOpenFile (name, &f, true);
+       if (f)
+               return LoadPCX (f, matchwidth, matchheight);
+       if (image_rgba = W_GetTexture(basename, matchwidth, matchheight))
+               return image_rgba;
+       if (complain)
+               Con_Printf ("Couldn't load %s.tga or .pcx\n", filename);
+       return NULL;
+}
+
+int loadtextureimage (int texnum, char* filename, qboolean complain, int matchwidth, int matchheight)
+{
+       byte *data;
+       if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
+               return 0;
+       if (texnum >= 0) // specific texnum, not cached
+       {
+               glBindTexture(GL_TEXTURE_2D, texnum);
+               GL_Upload32 (data, image_width, image_height, true, true);
+               free(data);
+               return texnum;
+       }
+       else // any texnum, cached
+       {
+               texnum = GL_LoadTexture (filename, image_width, image_height, data, true, true, 4);
+               free(data);
+               return texnum;
+       }
+}
diff --git a/in_null.c b/in_null.c
new file mode 100644 (file)
index 0000000..1c54ee4
--- /dev/null
+++ b/in_null.c
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// in_null.c -- for systems without a mouse
+
+#include "quakedef.h"
+
+void IN_Init (void)
+{
+}
+
+void IN_Shutdown (void)
+{
+}
+
+void IN_Commands (void)
+{
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+}
+
diff --git a/in_win.c b/in_win.c
new file mode 100644 (file)
index 0000000..2641749
--- /dev/null
+++ b/in_win.c
@@ -0,0 +1,1233 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// in_win.c -- windows 95 mouse and joystick code
+// 02/21/97 JCB Added extended DirectInput code to support external controllers.
+
+#include <dinput.h>
+#include "quakedef.h"
+#include "winquake.h"
+//#include "dosisms.h"
+
+#define DINPUT_BUFFERSIZE           16
+#define iDirectInputCreate(a,b,c,d)    pDirectInputCreate(a,b,c,d)
+
+HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
+       LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
+
+// mouse variables
+cvar_t m_filter = {"m_filter","0"};
+
+int                    mouse_buttons;
+int                    mouse_oldbuttonstate;
+POINT          current_pos;
+int                    mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
+
+static qboolean        restore_spi;
+static int             originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
+
+unsigned int uiWheelMessage;
+qboolean       mouseactive;
+qboolean               mouseinitialized;
+static qboolean        mouseparmsvalid, mouseactivatetoggle;
+static qboolean        mouseshowtoggle = 1;
+static qboolean        dinput_acquired;
+
+static unsigned int            mstate_di;
+
+// joystick defines and variables
+// where should defines be moved?
+#define JOY_ABSOLUTE_AXIS      0x00000000              // control like a joystick
+#define JOY_RELATIVE_AXIS      0x00000010              // control like a mouse, spinner, trackball
+#define        JOY_MAX_AXES            6                               // X, Y, Z, R, U, V
+#define JOY_AXIS_X                     0
+#define JOY_AXIS_Y                     1
+#define JOY_AXIS_Z                     2
+#define JOY_AXIS_R                     3
+#define JOY_AXIS_U                     4
+#define JOY_AXIS_V                     5
+
+enum _ControlList
+{
+       AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn
+};
+
+DWORD  dwAxisFlags[JOY_MAX_AXES] =
+{
+       JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
+};
+
+DWORD  dwAxisMap[JOY_MAX_AXES];
+DWORD  dwControlMap[JOY_MAX_AXES];
+PDWORD pdwRawValue[JOY_MAX_AXES];
+
+// none of these cvars are saved over a session
+// this means that advanced controller configuration needs to be executed
+// each time.  this avoids any problems with getting back to a default usage
+// or when changing from one controller to another.  this way at least something
+// works.
+cvar_t in_joystick = {"joystick","0", true};
+cvar_t joy_name = {"joyname", "joystick"};
+cvar_t joy_advanced = {"joyadvanced", "0"};
+cvar_t joy_advaxisx = {"joyadvaxisx", "0"};
+cvar_t joy_advaxisy = {"joyadvaxisy", "0"};
+cvar_t joy_advaxisz = {"joyadvaxisz", "0"};
+cvar_t joy_advaxisr = {"joyadvaxisr", "0"};
+cvar_t joy_advaxisu = {"joyadvaxisu", "0"};
+cvar_t joy_advaxisv = {"joyadvaxisv", "0"};
+cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15"};
+cvar_t joy_sidethreshold = {"joysidethreshold", "0.15"};
+cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15"};
+cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15"};
+cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"};
+cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0"};
+cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0"};
+cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0"};
+cvar_t joy_wwhack1 = {"joywwhack1", "0.0"};
+cvar_t joy_wwhack2 = {"joywwhack2", "0.0"};
+
+qboolean       joy_avail, joy_advancedinit, joy_haspov;
+DWORD          joy_oldbuttonstate, joy_oldpovstate;
+
+int                    joy_id;
+DWORD          joy_flags;
+DWORD          joy_numbuttons;
+
+static LPDIRECTINPUT           g_pdi;
+static LPDIRECTINPUTDEVICE     g_pMouse;
+
+static JOYINFOEX       ji;
+
+static HINSTANCE hInstDI;
+
+static qboolean        dinput;
+
+typedef struct MYDATA {
+       LONG  lX;                   // X axis goes here
+       LONG  lY;                   // Y axis goes here
+       LONG  lZ;                   // Z axis goes here
+       BYTE  bButtonA;             // One button goes here
+       BYTE  bButtonB;             // Another button goes here
+       BYTE  bButtonC;             // Another button goes here
+       BYTE  bButtonD;             // Another button goes here
+} MYDATA;
+
+static DIOBJECTDATAFORMAT rgodf[] = {
+  { &GUID_XAxis,    FIELD_OFFSET(MYDATA, lX),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
+  { &GUID_YAxis,    FIELD_OFFSET(MYDATA, lY),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
+  { &GUID_ZAxis,    FIELD_OFFSET(MYDATA, lZ),       0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
+  { 0,              FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+  { 0,              FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+  { 0,              FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+  { 0,              FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
+};
+
+#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
+
+static DIDATAFORMAT    df = {
+       sizeof(DIDATAFORMAT),       // this structure
+       sizeof(DIOBJECTDATAFORMAT), // size of object data format
+       DIDF_RELAXIS,               // absolute axis coordinates
+       sizeof(MYDATA),             // device data size
+       NUM_OBJECTS,                // number of objects
+       rgodf,                      // and here they are
+};
+
+// forward-referenced functions
+void IN_StartupJoystick (void);
+void Joy_AdvancedUpdate_f (void);
+void IN_JoyMove (usercmd_t *cmd);
+
+
+/*
+===========
+Force_CenterView_f
+===========
+*/
+void Force_CenterView_f (void)
+{
+       cl.viewangles[PITCH] = 0;
+}
+
+
+/*
+===========
+IN_UpdateClipCursor
+===========
+*/
+void IN_UpdateClipCursor (void)
+{
+
+       if (mouseinitialized && mouseactive && !dinput)
+       {
+               ClipCursor (&window_rect);
+       }
+}
+
+
+/*
+===========
+IN_ShowMouse
+===========
+*/
+void IN_ShowMouse (void)
+{
+
+       if (!mouseshowtoggle)
+       {
+               ShowCursor (TRUE);
+               mouseshowtoggle = 1;
+       }
+}
+
+
+/*
+===========
+IN_HideMouse
+===========
+*/
+void IN_HideMouse (void)
+{
+
+       if (mouseshowtoggle)
+       {
+               ShowCursor (FALSE);
+               mouseshowtoggle = 0;
+       }
+}
+
+
+/*
+===========
+IN_ActivateMouse
+===========
+*/
+void IN_ActivateMouse (void)
+{
+
+       mouseactivatetoggle = true;
+
+       if (mouseinitialized)
+       {
+               if (dinput)
+               {
+                       if (g_pMouse)
+                       {
+                               if (!dinput_acquired)
+                               {
+                                       IDirectInputDevice_Acquire(g_pMouse);
+                                       dinput_acquired = true;
+                               }
+                       }
+                       else
+                       {
+                               return;
+                       }
+               }
+               else
+               {
+                       if (mouseparmsvalid)
+                               restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
+
+                       SetCursorPos (window_center_x, window_center_y);
+                       SetCapture (mainwindow);
+                       ClipCursor (&window_rect);
+               }
+
+               mouseactive = true;
+       }
+}
+
+
+/*
+===========
+IN_SetQuakeMouseState
+===========
+*/
+void IN_SetQuakeMouseState (void)
+{
+       if (mouseactivatetoggle)
+               IN_ActivateMouse ();
+}
+
+
+/*
+===========
+IN_DeactivateMouse
+===========
+*/
+void IN_DeactivateMouse (void)
+{
+
+       mouseactivatetoggle = false;
+
+       if (mouseinitialized)
+       {
+               if (dinput)
+               {
+                       if (g_pMouse)
+                       {
+                               if (dinput_acquired)
+                               {
+                                       IDirectInputDevice_Unacquire(g_pMouse);
+                                       dinput_acquired = false;
+                               }
+                       }
+               }
+               else
+               {
+                       if (restore_spi)
+                               SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
+
+                       ClipCursor (NULL);
+                       ReleaseCapture ();
+               }
+
+               mouseactive = false;
+       }
+}
+
+
+/*
+===========
+IN_RestoreOriginalMouseState
+===========
+*/
+void IN_RestoreOriginalMouseState (void)
+{
+       if (mouseactivatetoggle)
+       {
+               IN_DeactivateMouse ();
+               mouseactivatetoggle = true;
+       }
+
+// try to redraw the cursor so it gets reinitialized, because sometimes it
+// has garbage after the mode switch
+       ShowCursor (TRUE);
+       ShowCursor (FALSE);
+}
+
+
+/*
+===========
+IN_InitDInput
+===========
+*/
+qboolean IN_InitDInput (void)
+{
+    HRESULT            hr;
+       DIPROPDWORD     dipdw = {
+               {
+                       sizeof(DIPROPDWORD),        // diph.dwSize
+                       sizeof(DIPROPHEADER),       // diph.dwHeaderSize
+                       0,                          // diph.dwObj
+                       DIPH_DEVICE,                // diph.dwHow
+               },
+               DINPUT_BUFFERSIZE,              // dwData
+       };
+
+       if (!hInstDI)
+       {
+               hInstDI = LoadLibrary("dinput.dll");
+               
+               if (hInstDI == NULL)
+               {
+                       Con_SafePrintf ("Couldn't load dinput.dll\n");
+                       return false;
+               }
+       }
+
+       if (!pDirectInputCreate)
+       {
+               pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA");
+
+               if (!pDirectInputCreate)
+               {
+                       Con_SafePrintf ("Couldn't get DI proc addr\n");
+                       return false;
+               }
+       }
+
+// register with DirectInput and get an IDirectInput to play with.
+       hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
+
+       if (FAILED(hr))
+       {
+               return false;
+       }
+
+// obtain an interface to the system mouse device.
+       hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
+
+       if (FAILED(hr))
+       {
+               Con_SafePrintf ("Couldn't open DI mouse device\n");
+               return false;
+       }
+
+// set the data format to "mouse format".
+       hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
+
+       if (FAILED(hr))
+       {
+               Con_SafePrintf ("Couldn't set DI mouse format\n");
+               return false;
+       }
+
+// set the cooperativity level.
+       hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
+                       DISCL_EXCLUSIVE | DISCL_FOREGROUND);
+
+       if (FAILED(hr))
+       {
+               Con_SafePrintf ("Couldn't set DI coop level\n");
+               return false;
+       }
+
+
+// set the buffer size to DINPUT_BUFFERSIZE elements.
+// the buffer size is a DWORD property associated with the device
+       hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
+
+       if (FAILED(hr))
+       {
+               Con_SafePrintf ("Couldn't set DI buffersize\n");
+               return false;
+       }
+
+       return true;
+}
+
+
+/*
+===========
+IN_StartupMouse
+===========
+*/
+void IN_StartupMouse (void)
+{
+       if ( COM_CheckParm ("-nomouse") ) 
+               return; 
+
+       mouseinitialized = true;
+
+       if (COM_CheckParm ("-dinput"))
+       {
+               dinput = IN_InitDInput ();
+
+               if (dinput)
+               {
+                       Con_SafePrintf ("DirectInput initialized\n");
+               }
+               else
+               {
+                       Con_SafePrintf ("DirectInput not initialized\n");
+               }
+       }
+
+       if (!dinput)
+       {
+               mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);
+
+               if (mouseparmsvalid)
+               {
+                       if ( COM_CheckParm ("-noforcemspd") ) 
+                               newmouseparms[2] = originalmouseparms[2];
+
+                       if ( COM_CheckParm ("-noforcemaccel") ) 
+                       {
+                               newmouseparms[0] = originalmouseparms[0];
+                               newmouseparms[1] = originalmouseparms[1];
+                       }
+
+                       if ( COM_CheckParm ("-noforcemparms") ) 
+                       {
+                               newmouseparms[0] = originalmouseparms[0];
+                               newmouseparms[1] = originalmouseparms[1];
+                               newmouseparms[2] = originalmouseparms[2];
+                       }
+               }
+       }
+
+       mouse_buttons = 3;
+
+// if a fullscreen video mode was set before the mouse was initialized,
+// set the mouse state appropriately
+       if (mouseactivatetoggle)
+               IN_ActivateMouse ();
+}
+
+
+/*
+===========
+IN_Init
+===========
+*/
+void IN_Init (void)
+{
+       // mouse variables
+       Cvar_RegisterVariable (&m_filter);
+
+       // joystick variables
+       Cvar_RegisterVariable (&in_joystick);
+       Cvar_RegisterVariable (&joy_name);
+       Cvar_RegisterVariable (&joy_advanced);
+       Cvar_RegisterVariable (&joy_advaxisx);
+       Cvar_RegisterVariable (&joy_advaxisy);
+       Cvar_RegisterVariable (&joy_advaxisz);
+       Cvar_RegisterVariable (&joy_advaxisr);
+       Cvar_RegisterVariable (&joy_advaxisu);
+       Cvar_RegisterVariable (&joy_advaxisv);
+       Cvar_RegisterVariable (&joy_forwardthreshold);
+       Cvar_RegisterVariable (&joy_sidethreshold);
+       Cvar_RegisterVariable (&joy_pitchthreshold);
+       Cvar_RegisterVariable (&joy_yawthreshold);
+       Cvar_RegisterVariable (&joy_forwardsensitivity);
+       Cvar_RegisterVariable (&joy_sidesensitivity);
+       Cvar_RegisterVariable (&joy_pitchsensitivity);
+       Cvar_RegisterVariable (&joy_yawsensitivity);
+       Cvar_RegisterVariable (&joy_wwhack1);
+       Cvar_RegisterVariable (&joy_wwhack2);
+
+       Cmd_AddCommand ("force_centerview", Force_CenterView_f);
+       Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f);
+
+       uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
+
+       IN_StartupMouse ();
+       IN_StartupJoystick ();
+}
+
+/*
+===========
+IN_Shutdown
+===========
+*/
+void IN_Shutdown (void)
+{
+
+       IN_DeactivateMouse ();
+       IN_ShowMouse ();
+
+    if (g_pMouse)
+       {
+               IDirectInputDevice_Release(g_pMouse);
+               g_pMouse = NULL;
+       }
+
+    if (g_pdi)
+       {
+               IDirectInput_Release(g_pdi);
+               g_pdi = NULL;
+       }
+}
+
+
+/*
+===========
+IN_MouseEvent
+===========
+*/
+void IN_MouseEvent (int mstate)
+{
+       int     i;
+
+       if (mouseactive && !dinput)
+       {
+       // perform button actions
+               for (i=0 ; i<mouse_buttons ; i++)
+               {
+                       if ( (mstate & (1<<i)) &&
+                               !(mouse_oldbuttonstate & (1<<i)) )
+                       {
+                               Key_Event (K_MOUSE1 + i, true);
+                       }
+
+                       if ( !(mstate & (1<<i)) &&
+                               (mouse_oldbuttonstate & (1<<i)) )
+                       {
+                               Key_Event (K_MOUSE1 + i, false);
+                       }
+               }       
+                       
+               mouse_oldbuttonstate = mstate;
+       }
+}
+
+
+/*
+===========
+IN_MouseMove
+===========
+*/
+void IN_MouseMove (usercmd_t *cmd)
+{
+       int                                     mx, my;
+       int                                     i;
+       DIDEVICEOBJECTDATA      od;
+       DWORD                           dwElements;
+       HRESULT                         hr;
+
+       if (!mouseactive)
+               return;
+
+       if (dinput)
+       {
+               mx = 0;
+               my = 0;
+
+               for (;;)
+               {
+                       dwElements = 1;
+
+                       hr = IDirectInputDevice_GetDeviceData(g_pMouse,
+                                       sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
+
+                       if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
+                       {
+                               dinput_acquired = true;
+                               IDirectInputDevice_Acquire(g_pMouse);
+                               break;
+                       }
+
+                       /* Unable to read data or no data available */
+                       if (FAILED(hr) || dwElements == 0)
+                       {
+                               break;
+                       }
+
+                       /* Look at the element to see what happened */
+
+                       switch (od.dwOfs)
+                       {
+                               case DIMOFS_X:
+                                       mx += od.dwData;
+                                       break;
+
+                               case DIMOFS_Y:
+                                       my += od.dwData;
+                                       break;
+
+                               case DIMOFS_BUTTON0:
+                                       if (od.dwData & 0x80)
+                                               mstate_di |= 1;
+                                       else
+                                               mstate_di &= ~1;
+                                       break;
+
+                               case DIMOFS_BUTTON1:
+                                       if (od.dwData & 0x80)
+                                               mstate_di |= (1<<1);
+                                       else
+                                               mstate_di &= ~(1<<1);
+                                       break;
+                                       
+                               case DIMOFS_BUTTON2:
+                                       if (od.dwData & 0x80)
+                                               mstate_di |= (1<<2);
+                                       else
+                                               mstate_di &= ~(1<<2);
+                                       break;
+                       }
+               }
+
+       // perform button actions
+               for (i=0 ; i<mouse_buttons ; i++)
+               {
+                       if ( (mstate_di & (1<<i)) &&
+                               !(mouse_oldbuttonstate & (1<<i)) )
+                       {
+                               Key_Event (K_MOUSE1 + i, true);
+                       }
+
+                       if ( !(mstate_di & (1<<i)) &&
+                               (mouse_oldbuttonstate & (1<<i)) )
+                       {
+                               Key_Event (K_MOUSE1 + i, false);
+                       }
+               }       
+                       
+               mouse_oldbuttonstate = mstate_di;
+       }
+       else
+       {
+               GetCursorPos (&current_pos);
+               mx = current_pos.x - window_center_x + mx_accum;
+               my = current_pos.y - window_center_y + my_accum;
+               mx_accum = 0;
+               my_accum = 0;
+       }
+
+//if (mx ||  my)
+//     Con_DPrintf("mx=%d, my=%d\n", mx, my);
+
+       if (m_filter.value)
+       {
+               mouse_x = (mx + old_mouse_x) * 0.5;
+               mouse_y = (my + old_mouse_y) * 0.5;
+       }
+       else
+       {
+               mouse_x = mx;
+               mouse_y = my;
+       }
+
+       old_mouse_x = mx;
+       old_mouse_y = my;
+
+       mouse_x *= sensitivity.value;
+       mouse_y *= sensitivity.value;
+
+// add mouse X/Y movement to cmd
+       if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
+               cmd->sidemove += m_side.value * mouse_x;
+       else
+               cl.viewangles[YAW] -= m_yaw.value * mouse_x;
+
+       if (in_mlook.state & 1)
+               V_StopPitchDrift ();
+               
+       if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
+       {
+               cl.viewangles[PITCH] += m_pitch.value * mouse_y;
+               if (cl.viewangles[PITCH] > 80)
+                       cl.viewangles[PITCH] = 80;
+               if (cl.viewangles[PITCH] < -70)
+                       cl.viewangles[PITCH] = -70;
+       }
+       else
+       {
+               if ((in_strafe.state & 1) && noclip_anglehack)
+                       cmd->upmove -= m_forward.value * mouse_y;
+               else
+                       cmd->forwardmove -= m_forward.value * mouse_y;
+       }
+
+// if the mouse has moved, force it to the center, so there's room to move
+       if (mx || my)
+       {
+               SetCursorPos (window_center_x, window_center_y);
+       }
+}
+
+
+/*
+===========
+IN_Move
+===========
+*/
+void IN_Move (usercmd_t *cmd)
+{
+
+       if (ActiveApp && !Minimized)
+       {
+               IN_MouseMove (cmd);
+               IN_JoyMove (cmd);
+       }
+}
+
+
+/*
+===========
+IN_Accumulate
+===========
+*/
+void IN_Accumulate (void)
+{
+       if (mouseactive)
+       {
+               if (!dinput)
+               {
+                       GetCursorPos (&current_pos);
+
+                       mx_accum += current_pos.x - window_center_x;
+                       my_accum += current_pos.y - window_center_y;
+
+               // force the mouse to the center, so there's room to move
+                       SetCursorPos (window_center_x, window_center_y);
+               }
+       }
+}
+
+
+/*
+===================
+IN_ClearStates
+===================
+*/
+void IN_ClearStates (void)
+{
+
+       if (mouseactive)
+       {
+               mx_accum = 0;
+               my_accum = 0;
+               mouse_oldbuttonstate = 0;
+       }
+}
+
+
+/* 
+=============== 
+IN_StartupJoystick 
+=============== 
+*/  
+void IN_StartupJoystick (void) 
+{ 
+       int                     numdevs;
+       JOYCAPS         jc;
+       MMRESULT        mmr;
+       // assume no joystick
+       joy_avail = false; 
+
+       // abort startup if user requests no joystick
+       if ( COM_CheckParm ("-nojoy") ) 
+               return; 
+       // verify joystick driver is present
+       if ((numdevs = joyGetNumDevs ()) == 0)
+       {
+               Con_Printf ("\njoystick not found -- driver not present\n\n");
+               return;
+       }
+
+       // cycle through the joystick ids for the first valid one
+       for (joy_id=0 ; joy_id<numdevs ; joy_id++)
+       {
+               memset (&ji, 0, sizeof(ji));
+               ji.dwSize = sizeof(ji);
+               ji.dwFlags = JOY_RETURNCENTERED;
+
+               if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
+                       break;
+       } 
+
+       // abort startup if we didn't find a valid joystick
+       if (mmr != JOYERR_NOERROR)
+       {
+               Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
+               return;
+       }
+
+       // get the capabilities of the selected joystick
+       // abort startup if command fails
+       memset (&jc, 0, sizeof(jc));
+       if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
+       {
+               Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); 
+               return;
+       }
+
+       // save the joystick's number of buttons and POV status
+       joy_numbuttons = jc.wNumButtons;
+       joy_haspov = jc.wCaps & JOYCAPS_HASPOV;
+
+       // old button and POV states default to no buttons pressed
+       joy_oldbuttonstate = joy_oldpovstate = 0;
+
+       // mark the joystick as available and advanced initialization not completed
+       // this is needed as cvars are not available during initialization
+
+       joy_avail = true; 
+       joy_advancedinit = false;
+
+       Con_Printf ("\njoystick detected\n\n"); 
+}
+
+
+/*
+===========
+RawValuePointer
+===========
+*/
+PDWORD RawValuePointer (int axis)
+{
+       switch (axis)
+       {
+       case JOY_AXIS_X:
+               return &ji.dwXpos;
+       case JOY_AXIS_Y:
+               return &ji.dwYpos;
+       case JOY_AXIS_Z:
+               return &ji.dwZpos;
+       case JOY_AXIS_R:
+               return &ji.dwRpos;
+       case JOY_AXIS_U:
+               return &ji.dwUpos;
+       case JOY_AXIS_V:
+               return &ji.dwVpos;
+       }
+}
+
+
+/*
+===========
+Joy_AdvancedUpdate_f
+===========
+*/
+void Joy_AdvancedUpdate_f (void)
+{
+
+       // called once by IN_ReadJoystick and by user whenever an update is needed
+       // cvars are now available
+       int     i;
+       DWORD dwTemp;
+
+       // initialize all the maps
+       for (i = 0; i < JOY_MAX_AXES; i++)
+       {
+               dwAxisMap[i] = AxisNada;
+               dwControlMap[i] = JOY_ABSOLUTE_AXIS;
+               pdwRawValue[i] = RawValuePointer(i);
+       }
+
+       if( joy_advanced.value == 0.0)
+       {
+               // default joystick initialization
+               // 2 axes only with joystick control
+               dwAxisMap[JOY_AXIS_X] = AxisTurn;
+               // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
+               dwAxisMap[JOY_AXIS_Y] = AxisForward;
+               // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
+       }
+       else
+       {
+               if (strcmp (joy_name.string, "joystick") != 0)
+               {
+                       // notify user of advanced controller
+                       Con_Printf ("\n%s configured\n\n", joy_name.string);
+               }
+
+               // advanced initialization here
+               // data supplied by user via joy_axisn cvars
+               dwTemp = (DWORD) joy_advaxisx.value;
+               dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
+               dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
+               dwTemp = (DWORD) joy_advaxisy.value;
+               dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
+               dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
+               dwTemp = (DWORD) joy_advaxisz.value;
+               dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
+               dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
+               dwTemp = (DWORD) joy_advaxisr.value;
+               dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
+               dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
+               dwTemp = (DWORD) joy_advaxisu.value;
+               dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
+               dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
+               dwTemp = (DWORD) joy_advaxisv.value;
+               dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
+               dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
+       }
+
+       // compute the axes to collect from DirectInput
+       joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
+       for (i = 0; i < JOY_MAX_AXES; i++)
+       {
+               if (dwAxisMap[i] != AxisNada)
+               {
+                       joy_flags |= dwAxisFlags[i];
+               }
+       }
+}
+
+
+/*
+===========
+IN_Commands
+===========
+*/
+void IN_Commands (void)
+{
+       int             i, key_index;
+       DWORD   buttonstate, povstate;
+
+       if (!joy_avail)
+       {
+               return;
+       }
+
+       
+       // loop through the joystick buttons
+       // key a joystick event or auxillary event for higher number buttons for each state change
+       buttonstate = ji.dwButtons;
+       for (i=0 ; i < (int) joy_numbuttons ; i++)
+       {
+               if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
+               {
+                       key_index = (i < 4) ? K_JOY1 : K_AUX1;
+                       Key_Event (key_index + i, true);
+               }
+
+               if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
+               {
+                       key_index = (i < 4) ? K_JOY1 : K_AUX1;
+                       Key_Event (key_index + i, false);
+               }
+       }
+       joy_oldbuttonstate = buttonstate;
+
+       if (joy_haspov)
+       {
+               // convert POV information into 4 bits of state information
+               // this avoids any potential problems related to moving from one
+               // direction to another without going through the center position
+               povstate = 0;
+               if(ji.dwPOV != JOY_POVCENTERED)
+               {
+                       if (ji.dwPOV == JOY_POVFORWARD)
+                               povstate |= 0x01;
+                       if (ji.dwPOV == JOY_POVRIGHT)
+                               povstate |= 0x02;
+                       if (ji.dwPOV == JOY_POVBACKWARD)
+                               povstate |= 0x04;
+                       if (ji.dwPOV == JOY_POVLEFT)
+                               povstate |= 0x08;
+               }
+               // determine which bits have changed and key an auxillary event for each change
+               for (i=0 ; i < 4 ; i++)
+               {
+                       if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
+                       {
+                               Key_Event (K_AUX29 + i, true);
+                       }
+
+                       if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
+                       {
+                               Key_Event (K_AUX29 + i, false);
+                       }
+               }
+               joy_oldpovstate = povstate;
+       }
+}
+
+
+/* 
+=============== 
+IN_ReadJoystick
+=============== 
+*/  
+qboolean IN_ReadJoystick (void)
+{
+
+       memset (&ji, 0, sizeof(ji));
+       ji.dwSize = sizeof(ji);
+       ji.dwFlags = joy_flags;
+
+       if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
+       {
+               // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
+               // rather than having 32768 be the zero point, they have the zero point at 32668
+               // go figure -- anyway, now we get the full resolution out of the device
+               if (joy_wwhack1.value != 0.0)
+               {
+                       ji.dwUpos += 100;
+               }
+               return true;
+       }
+       else
+       {
+               // read error occurred
+               // turning off the joystick seems too harsh for 1 read error,\
+               // but what should be done?
+               // Con_Printf ("IN_ReadJoystick: no response\n");
+               // joy_avail = false;
+               return false;
+       }
+}
+
+
+/*
+===========
+IN_JoyMove
+===========
+*/
+void IN_JoyMove (usercmd_t *cmd)
+{
+       float   speed, aspeed;
+       float   fAxisValue, fTemp;
+       int             i;
+
+       // complete initialization if first time in
+       // this is needed as cvars are not available at initialization time
+       if( joy_advancedinit != true )
+       {
+               Joy_AdvancedUpdate_f();
+               joy_advancedinit = true;
+       }
+
+       // verify joystick is available and that the user wants to use it
+       if (!joy_avail || !in_joystick.value)
+       {
+               return; 
+       }
+       // collect the joystick data, if possible
+       if (IN_ReadJoystick () != true)
+       {
+               return;
+       }
+
+       if (in_speed.state & 1)
+               speed = cl_movespeedkey.value;
+       else
+               speed = 1;
+       aspeed = speed * host_frametime;
+
+       // loop through the axes
+       for (i = 0; i < JOY_MAX_AXES; i++)
+       {
+               // get the floating point zero-centered, potentially-inverted data for the current axis
+               fAxisValue = (float) *pdwRawValue[i];
+               // move centerpoint to zero
+               fAxisValue -= 32768.0;
+
+               if (joy_wwhack2.value != 0.0)
+               {
+                       if (dwAxisMap[i] == AxisTurn)
+                       {
+                               // this is a special formula for the Logitech WingMan Warrior
+                               // y=ax^b; where a = 300 and b = 1.3
+                               // also x values are in increments of 800 (so this is factored out)
+                               // then bounds check result to level out excessively high spin rates
+                               fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
+                               if (fTemp > 14000.0)
+                                       fTemp = 14000.0;
+                               // restore direction information
+                               fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
+                       }
+               }
+
+               // convert range from -32768..32767 to -1..1 
+               fAxisValue /= 32768.0;
+
+               switch (dwAxisMap[i])
+               {
+               case AxisForward:
+                       if ((joy_advanced.value == 0.0) && (in_mlook.state & 1))
+                       {
+                               // user wants forward control to become look control
+                               if (fabs(fAxisValue) > joy_pitchthreshold.value)
+                               {               
+                                       // if mouse invert is on, invert the joystick pitch value
+                                       // only absolute control support here (joy_advanced is false)
+                                       if (m_pitch.value < 0.0)
+                                       {
+                                               cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
+                                       }
+                                       else
+                                       {
+                                               cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
+                                       }
+                                       V_StopPitchDrift();
+                               }
+                               else
+                               {
+                                       // no pitch movement
+                                       // disable pitch return-to-center unless requested by user
+                                       // *** this code can be removed when the lookspring bug is fixed
+                                       // *** the bug always has the lookspring feature on
+                                       if(lookspring.value == 0.0)
+                                               V_StopPitchDrift();
+                               }
+                       }
+                       else
+                       {
+                               // user wants forward control to be forward control
+                               if (fabs(fAxisValue) > joy_forwardthreshold.value)
+                               {
+                                       cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value;
+                               }
+                       }
+                       break;
+
+               case AxisSide:
+                       if (fabs(fAxisValue) > joy_sidethreshold.value)
+                       {
+                               cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
+                       }
+                       break;
+
+               case AxisTurn:
+                       if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1)))
+                       {
+                               // user wants turn control to become side control
+                               if (fabs(fAxisValue) > joy_sidethreshold.value)
+                               {
+                                       cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
+                               }
+                       }
+                       else
+                       {
+                               // user wants turn control to be turn control
+                               if (fabs(fAxisValue) > joy_yawthreshold.value)
+                               {
+                                       if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
+                                       {
+                                               cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value;
+                                       }
+                                       else
+                                       {
+                                               cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0;
+                                       }
+
+                               }
+                       }
+                       break;
+
+               case AxisLook:
+                       if (in_mlook.state & 1)
+                       {
+                               if (fabs(fAxisValue) > joy_pitchthreshold.value)
+                               {
+                                       // pitch movement detected and pitch movement desired by user
+                                       if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
+                                       {
+                                               cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
+                                       }
+                                       else
+                                       {
+                                               cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0;
+                                       }
+                                       V_StopPitchDrift();
+                               }
+                               else
+                               {
+                                       // no pitch movement
+                                       // disable pitch return-to-center unless requested by user
+                                       // *** this code can be removed when the lookspring bug is fixed
+                                       // *** the bug always has the lookspring feature on
+                                       if(lookspring.value == 0.0)
+                                               V_StopPitchDrift();
+                               }
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       // bounds check pitch
+       if (cl.viewangles[PITCH] > 80.0)
+               cl.viewangles[PITCH] = 80.0;
+       if (cl.viewangles[PITCH] < -70.0)
+               cl.viewangles[PITCH] = -70.0;
+}
diff --git a/input.h b/input.h
new file mode 100644 (file)
index 0000000..c3daa17
--- /dev/null
+++ b/input.h
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// input.h -- external (non-keyboard) input devices
+
+void IN_Init (void);
+
+void IN_Shutdown (void);
+
+void IN_Commands (void);
+// oportunity for devices to stick commands on the script buffer
+
+void IN_Move (usercmd_t *cmd);
+// add additional movement on top of the keyboard move cmd
+
+void IN_ClearStates (void);
+// restores all button and position states to defaults
+
diff --git a/keys.c b/keys.c
new file mode 100644 (file)
index 0000000..968cae6
--- /dev/null
+++ b/keys.c
@@ -0,0 +1,761 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+/*
+
+key up events are sent even if in console mode
+
+*/
+
+
+#define                MAXCMDLINE      256
+char   key_lines[32][MAXCMDLINE];
+int            key_linepos;
+int            shift_down=false;
+int            key_lastpress;
+
+int            edit_line=0;
+int            history_line=0;
+
+keydest_t      key_dest;
+
+int            key_count;                      // incremented every key event
+
+char   *keybindings[256];
+qboolean       consolekeys[256];       // if true, can't be rebound while in console
+qboolean       menubound[256]; // if true, can't be rebound while in menu
+int            keyshift[256];          // key to map to if shift held down in console
+int            key_repeats[256];       // if > 1, it is autorepeating
+qboolean       keydown[256];
+
+typedef struct
+{
+       char    *name;
+       int             keynum;
+} keyname_t;
+
+keyname_t keynames[] =
+{
+       {"TAB", K_TAB},
+       {"ENTER", K_ENTER},
+       {"ESCAPE", K_ESCAPE},
+       {"SPACE", K_SPACE},
+       {"BACKSPACE", K_BACKSPACE},
+       {"UPARROW", K_UPARROW},
+       {"DOWNARROW", K_DOWNARROW},
+       {"LEFTARROW", K_LEFTARROW},
+       {"RIGHTARROW", K_RIGHTARROW},
+
+       {"ALT", K_ALT},
+       {"CTRL", K_CTRL},
+       {"SHIFT", K_SHIFT},
+       
+       {"F1", K_F1},
+       {"F2", K_F2},
+       {"F3", K_F3},
+       {"F4", K_F4},
+       {"F5", K_F5},
+       {"F6", K_F6},
+       {"F7", K_F7},
+       {"F8", K_F8},
+       {"F9", K_F9},
+       {"F10", K_F10},
+       {"F11", K_F11},
+       {"F12", K_F12},
+
+       {"INS", K_INS},
+       {"DEL", K_DEL},
+       {"PGDN", K_PGDN},
+       {"PGUP", K_PGUP},
+       {"HOME", K_HOME},
+       {"END", K_END},
+
+       {"MOUSE1", K_MOUSE1},
+       {"MOUSE2", K_MOUSE2},
+       {"MOUSE3", K_MOUSE3},
+
+       {"JOY1", K_JOY1},
+       {"JOY2", K_JOY2},
+       {"JOY3", K_JOY3},
+       {"JOY4", K_JOY4},
+
+       {"AUX1", K_AUX1},
+       {"AUX2", K_AUX2},
+       {"AUX3", K_AUX3},
+       {"AUX4", K_AUX4},
+       {"AUX5", K_AUX5},
+       {"AUX6", K_AUX6},
+       {"AUX7", K_AUX7},
+       {"AUX8", K_AUX8},
+       {"AUX9", K_AUX9},
+       {"AUX10", K_AUX10},
+       {"AUX11", K_AUX11},
+       {"AUX12", K_AUX12},
+       {"AUX13", K_AUX13},
+       {"AUX14", K_AUX14},
+       {"AUX15", K_AUX15},
+       {"AUX16", K_AUX16},
+       {"AUX17", K_AUX17},
+       {"AUX18", K_AUX18},
+       {"AUX19", K_AUX19},
+       {"AUX20", K_AUX20},
+       {"AUX21", K_AUX21},
+       {"AUX22", K_AUX22},
+       {"AUX23", K_AUX23},
+       {"AUX24", K_AUX24},
+       {"AUX25", K_AUX25},
+       {"AUX26", K_AUX26},
+       {"AUX27", K_AUX27},
+       {"AUX28", K_AUX28},
+       {"AUX29", K_AUX29},
+       {"AUX30", K_AUX30},
+       {"AUX31", K_AUX31},
+       {"AUX32", K_AUX32},
+
+       {"PAUSE", K_PAUSE},
+
+       {"MWHEELUP", K_MWHEELUP},
+       {"MWHEELDOWN", K_MWHEELDOWN},
+
+       {"SEMICOLON", ';'},     // because a raw semicolon seperates commands
+
+       {NULL,0}
+};
+
+/*
+==============================================================================
+
+                       LINE TYPING INTO THE CONSOLE
+
+==============================================================================
+*/
+
+
+/*
+====================
+Key_Console
+
+Interactive line editing and console scrollback
+====================
+*/
+void Key_Console (int key)
+{
+       char    *cmd;
+       
+       if (key == K_ENTER)
+       {
+               Cbuf_AddText (key_lines[edit_line]+1);  // skip the >
+               Cbuf_AddText ("\n");
+               Con_Printf ("%s\n",key_lines[edit_line]);
+               edit_line = (edit_line + 1) & 31;
+               history_line = edit_line;
+               key_lines[edit_line][0] = ']';
+               key_linepos = 1;
+               if (cls.state == ca_disconnected)
+                       SCR_UpdateScreen ();    // force an update, because the command
+                                                                       // may take some time
+               return;
+       }
+
+       if (key == K_TAB)
+       {       // command completion
+               cmd = Cmd_CompleteCommand (key_lines[edit_line]+1);
+               if (!cmd)
+                       cmd = Cvar_CompleteVariable (key_lines[edit_line]+1);
+               if (cmd)
+               {
+                       strcpy (key_lines[edit_line]+1, cmd);
+                       key_linepos = strlen(cmd)+1;
+                       key_lines[edit_line][key_linepos] = ' ';
+                       key_linepos++;
+                       key_lines[edit_line][key_linepos] = 0;
+                       return;
+               }
+       }
+       
+       if (key == K_BACKSPACE || key == K_LEFTARROW)
+       {
+               if (key_linepos > 1)
+                       key_linepos--;
+               return;
+       }
+
+       if (key == K_UPARROW)
+       {
+               do
+               {
+                       history_line = (history_line - 1) & 31;
+               } while (history_line != edit_line
+                               && !key_lines[history_line][1]);
+               if (history_line == edit_line)
+                       history_line = (edit_line+1)&31;
+               strcpy(key_lines[edit_line], key_lines[history_line]);
+               key_linepos = strlen(key_lines[edit_line]);
+               return;
+       }
+
+       if (key == K_DOWNARROW)
+       {
+               if (history_line == edit_line) return;
+               do
+               {
+                       history_line = (history_line + 1) & 31;
+               }
+               while (history_line != edit_line
+                       && !key_lines[history_line][1]);
+               if (history_line == edit_line)
+               {
+                       key_lines[edit_line][0] = ']';
+                       key_linepos = 1;
+               }
+               else
+               {
+                       strcpy(key_lines[edit_line], key_lines[history_line]);
+                       key_linepos = strlen(key_lines[edit_line]);
+               }
+               return;
+       }
+
+       if (key == K_PGUP || key==K_MWHEELUP)
+       {
+               con_backscroll += 2;
+               if (con_backscroll > con_totallines - (vid.height>>3) - 1)
+                       con_backscroll = con_totallines - (vid.height>>3) - 1;
+               return;
+       }
+
+       if (key == K_PGDN || key==K_MWHEELDOWN)
+       {
+               con_backscroll -= 2;
+               if (con_backscroll < 0)
+                       con_backscroll = 0;
+               return;
+       }
+
+       if (key == K_HOME)
+       {
+               con_backscroll = con_totallines - (vid.height>>3) - 1;
+               return;
+       }
+
+       if (key == K_END)
+       {
+               con_backscroll = 0;
+               return;
+       }
+       
+       if (key < 32 || key > 127)
+               return; // non printable
+               
+       if (key_linepos < MAXCMDLINE-1)
+       {
+               key_lines[edit_line][key_linepos] = key;
+               key_linepos++;
+               key_lines[edit_line][key_linepos] = 0;
+       }
+
+}
+
+//============================================================================
+
+// LordHavoc: increased messagemode length (was 32)
+char chat_buffer[256];
+qboolean team_message = false;
+
+void Key_Message (int key)
+{
+       static int chat_bufferlen = 0;
+
+       if (key == K_ENTER)
+       {
+               if (team_message)
+                       Cbuf_AddText ("say_team \"");
+               else
+                       Cbuf_AddText ("say \"");
+               Cbuf_AddText(chat_buffer);
+               Cbuf_AddText("\"\n");
+
+               key_dest = key_game;
+               chat_bufferlen = 0;
+               chat_buffer[0] = 0;
+               return;
+       }
+
+       if (key == K_ESCAPE)
+       {
+               key_dest = key_game;
+               chat_bufferlen = 0;
+               chat_buffer[0] = 0;
+               return;
+       }
+
+       if (key < 32 || key > 127)
+               return; // non printable
+
+       if (key == K_BACKSPACE)
+       {
+               if (chat_bufferlen)
+               {
+                       chat_bufferlen--;
+                       chat_buffer[chat_bufferlen] = 0;
+               }
+               return;
+       }
+
+       // LordHavoc: increased messagemode length (was 31)
+       if (chat_bufferlen == 255)
+               return; // all full
+
+       chat_buffer[chat_bufferlen++] = key;
+       chat_buffer[chat_bufferlen] = 0;
+}
+
+//============================================================================
+
+
+/*
+===================
+Key_StringToKeynum
+
+Returns a key number to be used to index keybindings[] by looking at
+the given string.  Single ascii characters return themselves, while
+the K_* names are matched up.
+===================
+*/
+int Key_StringToKeynum (char *str)
+{
+       keyname_t       *kn;
+       
+       if (!str || !str[0])
+               return -1;
+       if (!str[1])
+               return str[0];
+
+       for (kn=keynames ; kn->name ; kn++)
+       {
+               if (!Q_strcasecmp(str,kn->name))
+                       return kn->keynum;
+       }
+       return -1;
+}
+
+/*
+===================
+Key_KeynumToString
+
+Returns a string (either a single ascii char, or a K_* name) for the
+given keynum.
+FIXME: handle quote special (general escape sequence?)
+===================
+*/
+char *Key_KeynumToString (int keynum)
+{
+       keyname_t       *kn;    
+       static  char    tinystr[2];
+       
+       if (keynum == -1)
+               return "<KEY NOT FOUND>";
+       if (keynum > 32 && keynum < 127)
+       {       // printable ascii
+               tinystr[0] = keynum;
+               tinystr[1] = 0;
+               return tinystr;
+       }
+       
+       for (kn=keynames ; kn->name ; kn++)
+               if (keynum == kn->keynum)
+                       return kn->name;
+
+       return "<UNKNOWN KEYNUM>";
+}
+
+
+/*
+===================
+Key_SetBinding
+===================
+*/
+void Key_SetBinding (int keynum, char *binding)
+{
+       char    *new;
+       int             l;
+                       
+       if (keynum == -1)
+               return;
+
+// free old bindings
+       if (keybindings[keynum])
+       {
+               Z_Free (keybindings[keynum]);
+               keybindings[keynum] = NULL;
+       }
+                       
+// allocate memory for new binding
+       l = strlen (binding);   
+       new = Z_Malloc (l+1);
+       strcpy (new, binding);
+       new[l] = 0;
+       keybindings[keynum] = new;      
+}
+
+/*
+===================
+Key_Unbind_f
+===================
+*/
+void Key_Unbind_f (void)
+{
+       int             b;
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf ("unbind <key> : remove commands from a key\n");
+               return;
+       }
+       
+       b = Key_StringToKeynum (Cmd_Argv(1));
+       if (b==-1)
+       {
+               Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+               return;
+       }
+
+       Key_SetBinding (b, "");
+}
+
+void Key_Unbindall_f (void)
+{
+       int             i;
+       
+       for (i=0 ; i<256 ; i++)
+               if (keybindings[i])
+                       Key_SetBinding (i, "");
+}
+
+
+/*
+===================
+Key_Bind_f
+===================
+*/
+void Key_Bind_f (void)
+{
+       int                     i, c, b;
+       char            cmd[1024];
+       
+       c = Cmd_Argc();
+
+       if (c != 2 && c != 3)
+       {
+               Con_Printf ("bind <key> [command] : attach a command to a key\n");
+               return;
+       }
+       b = Key_StringToKeynum (Cmd_Argv(1));
+       if (b==-1)
+       {
+               Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+               return;
+       }
+
+       if (c == 2)
+       {
+               if (keybindings[b])
+                       Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
+               else
+                       Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
+               return;
+       }
+       
+// copy the rest of the command line
+       cmd[0] = 0;             // start out with a null string
+       for (i=2 ; i< c ; i++)
+       {
+               if (i > 2)
+                       strcat (cmd, " ");
+               strcat (cmd, Cmd_Argv(i));
+       }
+
+       Key_SetBinding (b, cmd);
+}
+
+/*
+============
+Key_WriteBindings
+
+Writes lines containing "bind key value"
+============
+*/
+void Key_WriteBindings (FILE *f)
+{
+       int             i;
+
+       for (i=0 ; i<256 ; i++)
+               if (keybindings[i])
+                       if (*keybindings[i])
+                               fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
+}
+
+
+/*
+===================
+Key_Init
+===================
+*/
+void Key_Init (void)
+{
+       int             i;
+
+       for (i=0 ; i<32 ; i++)
+       {
+               key_lines[i][0] = ']';
+               key_lines[i][1] = 0;
+       }
+       key_linepos = 1;
+       
+//
+// init ascii characters in console mode
+//
+       for (i=32 ; i<128 ; i++)
+               consolekeys[i] = true;
+       consolekeys[K_ENTER] = true;
+       consolekeys[K_TAB] = true;
+       consolekeys[K_LEFTARROW] = true;
+       consolekeys[K_RIGHTARROW] = true;
+       consolekeys[K_UPARROW] = true;
+       consolekeys[K_DOWNARROW] = true;
+       consolekeys[K_BACKSPACE] = true;
+       consolekeys[K_PGUP] = true;
+       consolekeys[K_PGDN] = true;
+       consolekeys[K_SHIFT] = true;
+       consolekeys[K_MWHEELUP] = true;
+       consolekeys[K_MWHEELDOWN] = true;
+       consolekeys['`'] = false;
+       consolekeys['~'] = false;
+
+       for (i=0 ; i<256 ; i++)
+               keyshift[i] = i;
+       for (i='a' ; i<='z' ; i++)
+               keyshift[i] = i - 'a' + 'A';
+       keyshift['1'] = '!';
+       keyshift['2'] = '@';
+       keyshift['3'] = '#';
+       keyshift['4'] = '$';
+       keyshift['5'] = '%';
+       keyshift['6'] = '^';
+       keyshift['7'] = '&';
+       keyshift['8'] = '*';
+       keyshift['9'] = '(';
+       keyshift['0'] = ')';
+       keyshift['-'] = '_';
+       keyshift['='] = '+';
+       keyshift[','] = '<';
+       keyshift['.'] = '>';
+       keyshift['/'] = '?';
+       keyshift[';'] = ':';
+       keyshift['\''] = '"';
+       keyshift['['] = '{';
+       keyshift[']'] = '}';
+       keyshift['`'] = '~';
+       keyshift['\\'] = '|';
+
+       menubound[K_ESCAPE] = true;
+       for (i=0 ; i<12 ; i++)
+               menubound[K_F1+i] = true;
+
+//
+// register our functions
+//
+       Cmd_AddCommand ("bind",Key_Bind_f);
+       Cmd_AddCommand ("unbind",Key_Unbind_f);
+       Cmd_AddCommand ("unbindall",Key_Unbindall_f);
+
+
+}
+
+/*
+===================
+Key_Event
+
+Called by the system between frames for both key up and key down events
+Should NOT be called during an interrupt!
+===================
+*/
+void Key_Event (int key, qboolean down)
+{
+       char    *kb;
+       char    cmd[1024];
+
+       keydown[key] = down;
+
+       if (!down)
+               key_repeats[key] = 0;
+
+       key_lastpress = key;
+       key_count++;
+       if (key_count <= 0)
+       {
+               return;         // just catching keys for Con_NotifyBox
+       }
+
+// update auto-repeat status
+       if (down)
+       {
+               key_repeats[key]++;
+               if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1)
+               {
+                       return; // ignore most autorepeats
+               }
+                       
+               if (key >= 200 && !keybindings[key])
+                       Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
+       }
+
+       if (key == K_SHIFT)
+               shift_down = down;
+
+//
+// handle escape specialy, so the user can never unbind it
+//
+       if (key == K_ESCAPE)
+       {
+               if (!down)
+                       return;
+               switch (key_dest)
+               {
+               case key_message:
+                       Key_Message (key);
+                       break;
+               case key_menu:
+                       M_Keydown (key);
+                       break;
+               case key_game:
+               case key_console:
+                       M_ToggleMenu_f ();
+                       break;
+               default:
+                       Sys_Error ("Bad key_dest");
+               }
+               return;
+       }
+
+//
+// key up events only generate commands if the game key binding is
+// a button command (leading + sign).  These will occur even in console mode,
+// to keep the character from continuing an action started before a console
+// switch.  Button commands include the kenum as a parameter, so multiple
+// downs can be matched with ups
+//
+       if (!down)
+       {
+               kb = keybindings[key];
+               if (kb && kb[0] == '+')
+               {
+                       sprintf (cmd, "-%s %i\n", kb+1, key);
+                       Cbuf_AddText (cmd);
+               }
+               if (keyshift[key] != key)
+               {
+                       kb = keybindings[keyshift[key]];
+                       if (kb && kb[0] == '+')
+                       {
+                               sprintf (cmd, "-%s %i\n", kb+1, key);
+                               Cbuf_AddText (cmd);
+                       }
+               }
+               return;
+       }
+
+//
+// during demo playback, most keys bring up the main menu
+//
+       if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game)
+       {
+               M_ToggleMenu_f ();
+               return;
+       }
+
+//
+// if not a consolekey, send to the interpreter no matter what mode is
+//
+       if ( (key_dest == key_menu && menubound[key])
+       || (key_dest == key_console && !consolekeys[key])
+       || (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) )
+       {
+               kb = keybindings[key];
+               if (kb)
+               {
+                       if (kb[0] == '+')
+                       {       // button commands add keynum as a parm
+                               sprintf (cmd, "%s %i\n", kb, key);
+                               Cbuf_AddText (cmd);
+                       }
+                       else
+                       {
+                               Cbuf_AddText (kb);
+                               Cbuf_AddText ("\n");
+                       }
+               }
+               return;
+       }
+
+       if (!down)
+               return;         // other systems only care about key down events
+
+       if (shift_down)
+       {
+               key = keyshift[key];
+       }
+
+       switch (key_dest)
+       {
+       case key_message:
+               Key_Message (key);
+               break;
+       case key_menu:
+               M_Keydown (key);
+               break;
+
+       case key_game:
+       case key_console:
+               Key_Console (key);
+               break;
+       default:
+               Sys_Error ("Bad key_dest");
+       }
+}
+
+
+/*
+===================
+Key_ClearStates
+===================
+*/
+void Key_ClearStates (void)
+{
+       int             i;
+
+       for (i=0 ; i<256 ; i++)
+       {
+               keydown[i] = false;
+               key_repeats[i] = 0;
+       }
+}
+
diff --git a/keys.h b/keys.h
new file mode 100644 (file)
index 0000000..3fbefd0
--- /dev/null
+++ b/keys.h
@@ -0,0 +1,133 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// these are the key numbers that should be passed to Key_Event
+//
+#define        K_TAB                   9
+#define        K_ENTER                 13
+#define        K_ESCAPE                27
+#define        K_SPACE                 32
+
+// normal keys should be passed as lowercased ascii
+
+#define        K_BACKSPACE             127
+#define        K_UPARROW               128
+#define        K_DOWNARROW             129
+#define        K_LEFTARROW             130
+#define        K_RIGHTARROW    131
+
+#define        K_ALT                   132
+#define        K_CTRL                  133
+#define        K_SHIFT                 134
+#define        K_F1                    135
+#define        K_F2                    136
+#define        K_F3                    137
+#define        K_F4                    138
+#define        K_F5                    139
+#define        K_F6                    140
+#define        K_F7                    141
+#define        K_F8                    142
+#define        K_F9                    143
+#define        K_F10                   144
+#define        K_F11                   145
+#define        K_F12                   146
+#define        K_INS                   147
+#define        K_DEL                   148
+#define        K_PGDN                  149
+#define        K_PGUP                  150
+#define        K_HOME                  151
+#define        K_END                   152
+
+#define K_PAUSE                        255
+
+//
+// mouse buttons generate virtual keys
+//
+#define        K_MOUSE1                200
+#define        K_MOUSE2                201
+#define        K_MOUSE3                202
+
+//
+// joystick buttons
+//
+#define        K_JOY1                  203
+#define        K_JOY2                  204
+#define        K_JOY3                  205
+#define        K_JOY4                  206
+
+//
+// aux keys are for multi-buttoned joysticks to generate so they can use
+// the normal binding process
+//
+#define        K_AUX1                  207
+#define        K_AUX2                  208
+#define        K_AUX3                  209
+#define        K_AUX4                  210
+#define        K_AUX5                  211
+#define        K_AUX6                  212
+#define        K_AUX7                  213
+#define        K_AUX8                  214
+#define        K_AUX9                  215
+#define        K_AUX10                 216
+#define        K_AUX11                 217
+#define        K_AUX12                 218
+#define        K_AUX13                 219
+#define        K_AUX14                 220
+#define        K_AUX15                 221
+#define        K_AUX16                 222
+#define        K_AUX17                 223
+#define        K_AUX18                 224
+#define        K_AUX19                 225
+#define        K_AUX20                 226
+#define        K_AUX21                 227
+#define        K_AUX22                 228
+#define        K_AUX23                 229
+#define        K_AUX24                 230
+#define        K_AUX25                 231
+#define        K_AUX26                 232
+#define        K_AUX27                 233
+#define        K_AUX28                 234
+#define        K_AUX29                 235
+#define        K_AUX30                 236
+#define        K_AUX31                 237
+#define        K_AUX32                 238
+
+// JACK: Intellimouse(c) Mouse Wheel Support
+
+#define K_MWHEELUP             239
+#define K_MWHEELDOWN   240
+
+
+
+typedef enum {key_game, key_console, key_message, key_menu} keydest_t;
+
+extern keydest_t       key_dest;
+extern char *keybindings[256];
+extern int             key_repeats[256];
+extern int             key_count;                      // incremented every key event
+extern int             key_lastpress;
+
+void Key_Event (int key, qboolean down);
+void Key_Init (void);
+void Key_WriteBindings (FILE *f);
+void Key_SetBinding (int keynum, char *binding);
+void Key_ClearStates (void);
+
diff --git a/mathlib.c b/mathlib.c
new file mode 100644 (file)
index 0000000..f1bbacf
--- /dev/null
+++ b/mathlib.c
@@ -0,0 +1,639 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// mathlib.c -- math primitives
+
+#include <math.h>
+#include "quakedef.h"
+
+void Sys_Error (char *error, ...);
+
+vec3_t vec3_origin = {0,0,0};
+int nanmask = 255<<23;
+
+/*-----------------------------------------------------------------*/
+
+#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
+
+void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
+{
+       float d;
+       vec3_t n;
+       float inv_denom;
+
+       inv_denom = 1.0F / DotProduct( normal, normal );
+
+       d = DotProduct( normal, p ) * inv_denom;
+
+       n[0] = normal[0] * inv_denom;
+       n[1] = normal[1] * inv_denom;
+       n[2] = normal[2] * inv_denom;
+
+       dst[0] = p[0] - d * n[0];
+       dst[1] = p[1] - d * n[1];
+       dst[2] = p[2] - d * n[2];
+}
+
+/*
+** assumes "src" is normalized
+*/
+void PerpendicularVector( vec3_t dst, const vec3_t src )
+{
+       int     pos;
+       int i;
+       float minelem = 1.0F;
+       vec3_t tempvec;
+
+       /*
+       ** find the smallest magnitude axially aligned vector
+       */
+       for ( pos = 0, i = 0; i < 3; i++ )
+       {
+               if ( fabs( src[i] ) < minelem )
+               {
+                       pos = i;
+                       minelem = fabs( src[i] );
+               }
+       }
+       tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
+       tempvec[pos] = 1.0F;
+
+       /*
+       ** project the point onto the plane defined by src
+       */
+       ProjectPointOnPlane( dst, tempvec, src );
+
+       /*
+       ** normalize the result
+       */
+       VectorNormalize( dst );
+}
+
+#ifdef _WIN32
+#pragma optimize( "", off )
+#endif
+
+
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees )
+{
+       float   m[3][3];
+       float   im[3][3];
+       float   zrot[3][3];
+       float   tmpmat[3][3];
+       float   rot[3][3];
+       int     i;
+       vec3_t vr, vup, vf;
+
+       vf[0] = dir[0];
+       vf[1] = dir[1];
+       vf[2] = dir[2];
+
+       PerpendicularVector( vr, dir );
+       CrossProduct( vr, vf, vup );
+
+       m[0][0] = vr[0];
+       m[1][0] = vr[1];
+       m[2][0] = vr[2];
+
+       m[0][1] = vup[0];
+       m[1][1] = vup[1];
+       m[2][1] = vup[2];
+
+       m[0][2] = vf[0];
+       m[1][2] = vf[1];
+       m[2][2] = vf[2];
+
+       memcpy( im, m, sizeof( im ) );
+
+       im[0][1] = m[1][0];
+       im[0][2] = m[2][0];
+       im[1][0] = m[0][1];
+       im[1][2] = m[2][1];
+       im[2][0] = m[0][2];
+       im[2][1] = m[1][2];
+
+       memset( zrot, 0, sizeof( zrot ) );
+       zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
+
+       zrot[0][0] = cos( DEG2RAD( degrees ) );
+       zrot[0][1] = sin( DEG2RAD( degrees ) );
+       zrot[1][0] = -sin( DEG2RAD( degrees ) );
+       zrot[1][1] = cos( DEG2RAD( degrees ) );
+
+       R_ConcatRotations( m, zrot, tmpmat );
+       R_ConcatRotations( tmpmat, im, rot );
+
+       for ( i = 0; i < 3; i++ )
+       {
+               dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
+       }
+}
+
+#ifdef _WIN32
+#pragma optimize( "", on )
+#endif
+
+/*-----------------------------------------------------------------*/
+
+
+float  anglemod(float a)
+{
+#if 0
+       if (a >= 0)
+               a -= 360*(int)(a/360);
+       else
+               a += 360*( 1 + (int)(-a/360) );
+#endif
+       a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
+       return a;
+}
+
+/*
+==================
+BOPS_Error
+
+Split out like this for ASM to call.
+==================
+*/
+/*
+void BOPS_Error (void)
+{
+       Sys_Error ("BoxOnPlaneSide:  Bad signbits");
+}
+
+
+#if    !id386
+
+*/
+/*
+==================
+BoxOnPlaneSide
+
+Returns 1, 2, or 1 + 2
+==================
+*/
+/*
+int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p)
+{
+       float   dist1, dist2;
+       int             sides;
+
+#if 0  // this is done by the BOX_ON_PLANE_SIDE macro before calling this
+               // function
+// fast axial cases
+       if (p->type < 3)
+       {
+               if (p->dist <= emins[p->type])
+                       return 1;
+               if (p->dist >= emaxs[p->type])
+                       return 2;
+               return 3;
+       }
+#endif
+       
+// general case
+       switch (p->signbits)
+       {
+       case 0:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+               break;
+       case 1:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+               break;
+       case 2:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+               break;
+       case 3:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+               break;
+       case 4:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+               break;
+       case 5:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+               break;
+       case 6:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+               break;
+       case 7:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+               break;
+       default:
+               dist1 = dist2 = 0;              // shut up compiler
+               BOPS_Error ();
+               break;
+       }
+
+#if 0
+       int             i;
+       vec3_t  corners[2];
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (plane->normal[i] < 0)
+               {
+                       corners[0][i] = emins[i];
+                       corners[1][i] = emaxs[i];
+               }
+               else
+               {
+                       corners[1][i] = emins[i];
+                       corners[0][i] = emaxs[i];
+               }
+       }
+       dist = DotProduct (plane->normal, corners[0]) - plane->dist;
+       dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+       sides = 0;
+       if (dist1 >= 0)
+               sides = 1;
+       if (dist2 < 0)
+               sides |= 2;
+
+#endif
+
+       sides = 0;
+       if (dist1 >= p->dist)
+               sides = 1;
+       if (dist2 < p->dist)
+               sides |= 2;
+
+#ifdef PARANOID
+if (sides == 0)
+       Sys_Error ("BoxOnPlaneSide: sides==0");
+#endif
+
+       return sides;
+}
+
+#endif
+*/
+
+int BoxOnPlaneSide0 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]) >= p->dist) | (((p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]) < p->dist) << 1));}
+int BoxOnPlaneSide1 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]) >= p->dist) | (((p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]) < p->dist) << 1));}
+int BoxOnPlaneSide2 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]) >= p->dist) | (((p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]) < p->dist) << 1));}
+int BoxOnPlaneSide3 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]) >= p->dist) | (((p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]) < p->dist) << 1));}
+int BoxOnPlaneSide4 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]) >= p->dist) | (((p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]) < p->dist) << 1));}
+int BoxOnPlaneSide5 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]) >= p->dist) | (((p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]) < p->dist) << 1));}
+int BoxOnPlaneSide6 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]) >= p->dist) | (((p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]) < p->dist) << 1));}
+int BoxOnPlaneSide7 (vec3_t emins, vec3_t emaxs, mplane_t *p) {return (((p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]) >= p->dist) | (((p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]) < p->dist) << 1));}
+
+void BoxOnPlaneSideClassify(mplane_t *p)
+{
+       if (p->normal[2] < 0) // 4
+       {
+               if (p->normal[1] < 0) // 2
+               {
+                       if (p->normal[0] < 0) // 1
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide7;
+                       else
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide6;
+               }
+               else
+               {
+                       if (p->normal[0] < 0) // 1
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide5;
+                       else
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide4;
+               }
+       }
+       else
+       {
+               if (p->normal[1] < 0) // 2
+               {
+                       if (p->normal[0] < 0) // 1
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide3;
+                       else
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide2;
+               }
+               else
+               {
+                       if (p->normal[0] < 0) // 1
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide1;
+                       else
+                               p->BoxOnPlaneSideFunc = BoxOnPlaneSide0;
+               }
+       }
+}
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
+{
+       float           angle;
+       float           sr, sp, sy, cr, cp, cy;
+       
+       angle = angles[YAW] * (M_PI*2 / 360);
+       sy = sin(angle);
+       cy = cos(angle);
+       angle = angles[PITCH] * (M_PI*2 / 360);
+       sp = sin(angle);
+       cp = cos(angle);
+       angle = angles[ROLL] * (M_PI*2 / 360);
+       sr = sin(angle);
+       cr = cos(angle);
+
+       forward[0] = cp*cy;
+       forward[1] = cp*sy;
+       forward[2] = -sp;
+       right[0] = (-1*sr*sp*cy+-1*cr*-sy);
+       right[1] = (-1*sr*sp*sy+-1*cr*cy);
+       right[2] = -1*sr*cp;
+       up[0] = (cr*sp*cy+-sr*-sy);
+       up[1] = (cr*sp*sy+-sr*cy);
+       up[2] = cr*cp;
+}
+
+int VectorCompare (vec3_t v1, vec3_t v2)
+{
+       int             i;
+       
+       for (i=0 ; i<3 ; i++)
+               if (v1[i] != v2[i])
+                       return 0;
+                       
+       return 1;
+}
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
+{
+       vecc[0] = veca[0] + scale*vecb[0];
+       vecc[1] = veca[1] + scale*vecb[1];
+       vecc[2] = veca[2] + scale*vecb[2];
+}
+
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2)
+{
+       return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+       out[0] = veca[0]-vecb[0];
+       out[1] = veca[1]-vecb[1];
+       out[2] = veca[2]-vecb[2];
+}
+
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+       out[0] = veca[0]+vecb[0];
+       out[1] = veca[1]+vecb[1];
+       out[2] = veca[2]+vecb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+}
+
+// LordHavoc: changed CrossProduct to a #define
+/*
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+       cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+       cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+       cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+*/
+
+double sqrt(double x);
+
+vec_t Length(vec3_t v)
+{
+       int             i;
+       float   length;
+       
+       length = 0;
+       for (i=0 ; i< 3 ; i++)
+               length += v[i]*v[i];
+       length = sqrt (length);         // FIXME
+
+       return length;
+}
+
+// LordHavoc: renamed these to Length, and made the normal ones #define
+float VectorNormalizeLength (vec3_t v)
+{
+       float   length, ilength;
+
+       length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+       length = sqrt (length);         // FIXME
+
+       if (length)
+       {
+               ilength = 1/length;
+               v[0] *= ilength;
+               v[1] *= ilength;
+               v[2] *= ilength;
+       }
+               
+       return length;
+
+}
+
+float VectorNormalizeLength2 (vec3_t v, vec3_t dest) // LordHavoc: added to allow copying while doing the calculation...
+{
+       float   length, ilength;
+
+       length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+       length = sqrt (length);         // FIXME
+
+       if (length)
+       {
+               ilength = 1/length;
+               dest[0] = v[0] * ilength;
+               dest[1] = v[1] * ilength;
+               dest[2] = v[2] * ilength;
+       }
+       else
+               dest[0] = dest[1] = dest[2] = 0;
+               
+       return length;
+
+}
+
+void VectorInverse (vec3_t v)
+{
+       v[0] = -v[0];
+       v[1] = -v[1];
+       v[2] = -v[2];
+}
+
+void VectorScale (vec3_t in, vec_t scale, vec3_t out)
+{
+       out[0] = in[0]*scale;
+       out[1] = in[1]*scale;
+       out[2] = in[2]*scale;
+}
+
+
+int Q_log2(int val)
+{
+       int answer=0;
+       while (val>>=1)
+               answer++;
+       return answer;
+}
+
+
+/*
+================
+R_ConcatRotations
+================
+*/
+void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
+{
+       out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+       out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+       out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+       out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+       out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+       out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+       out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+       out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+       out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+}
+
+
+/*
+================
+R_ConcatTransforms
+================
+*/
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
+{
+       out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+       out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+       out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+       out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3];
+       out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+       out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+       out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+       out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3];
+       out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+       out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+       out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+       out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+
+/*
+===================
+FloorDivMod
+
+Returns mathematically correct (floor-based) quotient and remainder for
+numer and denom, both of which should contain no fractional part. The
+quotient must fit in 32 bits.
+====================
+*/
+
+void FloorDivMod (double numer, double denom, int *quotient,
+               int *rem)
+{
+       int             q, r;
+       double  x;
+
+#ifndef PARANOID
+       if (denom <= 0.0)
+               Sys_Error ("FloorDivMod: bad denominator %d\n", denom);
+
+//     if ((floor(numer) != numer) || (floor(denom) != denom))
+//             Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n",
+//                             numer, denom);
+#endif
+
+       if (numer >= 0.0)
+       {
+
+               x = floor(numer / denom);
+               q = (int)x;
+               r = (int)floor(numer - (x * denom));
+       }
+       else
+       {
+       //
+       // perform operations with positive values, and fix mod to make floor-based
+       //
+               x = floor(-numer / denom);
+               q = -(int)x;
+               r = (int)floor(-numer - (x * denom));
+               if (r != 0)
+               {
+                       q--;
+                       r = (int)denom - r;
+               }
+       }
+
+       *quotient = q;
+       *rem = r;
+}
+
+
+/*
+===================
+GreatestCommonDivisor
+====================
+*/
+int GreatestCommonDivisor (int i1, int i2)
+{
+       if (i1 > i2)
+       {
+               if (i2 == 0)
+                       return (i1);
+               return GreatestCommonDivisor (i2, i1 % i2);
+       }
+       else
+       {
+               if (i1 == 0)
+                       return (i2);
+               return GreatestCommonDivisor (i1, i2 % i1);
+       }
+}
+
+
+#if    !id386
+
+// TODO: move to nonintel.c
+
+/*
+===================
+Invert24To16
+
+Inverts an 8.24 value to a 16.16 value
+====================
+*/
+
+fixed16_t Invert24To16(fixed16_t val)
+{
+       if (val < 256)
+               return (0xFFFFFFFF);
+
+       return (fixed16_t)
+                       (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5);
+}
+
+#endif
diff --git a/mathlib.h b/mathlib.h
new file mode 100644 (file)
index 0000000..19d9a80
--- /dev/null
+++ b/mathlib.h
@@ -0,0 +1,102 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// mathlib.h
+
+typedef float vec_t;
+typedef vec_t vec3_t[3];
+typedef vec_t vec5_t[5];
+
+typedef        int     fixed4_t;
+typedef        int     fixed8_t;
+typedef        int     fixed16_t;
+
+#ifndef M_PI
+#define M_PI           3.14159265358979323846  // matches value in gcc v2 math.h
+#endif
+
+struct mplane_s;
+
+extern vec3_t vec3_origin;
+extern int nanmask;
+
+#define        IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
+
+#define bound(min,num,max) (num >= min ? (num < max ? num : max) : min)
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+#define CrossProduct(v1,v2,cross) {cross[0] = v1[1]*v2[2] - v1[2]*v2[1];cross[1] = v1[2]*v2[0] - v1[0]*v2[2];cross[2] = v1[0]*v2[1] - v1[1]*v2[0];}
+#define VectorNormalize(v) {float ilength;if (ilength = sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2])) {ilength = 1/ilength;v[0] *= ilength;v[1] *= ilength;v[2] *= ilength;}}
+#define VectorNormalize2(v,dest) {float ilength;if (ilength = sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2])) {ilength = 1/ilength;dest[0] = v[0] * ilength;dest[1] = v[1] * ilength;dest[2] = v[2] * ilength;}}
+
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc);
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+
+int VectorCompare (vec3_t v1, vec3_t v2);
+vec_t Length (vec3_t v);
+//void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+float VectorNormalizeLength (vec3_t v);                // returns vector length
+float VectorNormalizeLength2 (vec3_t v, vec3_t dest);          // returns vector length
+void VectorInverse (vec3_t v);
+void VectorScale (vec3_t in, vec_t scale, vec3_t out);
+int Q_log2(int val);
+
+void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]);
+
+void FloorDivMod (double numer, double denom, int *quotient,
+               int *rem);
+fixed16_t Invert24To16(fixed16_t val);
+int GreatestCommonDivisor (int i1, int i2);
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
+//int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane);
+float  anglemod(float a);
+
+
+void BoxOnPlaneSideClassify(struct mplane_s *p);
+
+#define BOX_ON_PLANE_SIDE(emins, emaxs, p)     \
+       (((p)->type < 3)?                                               \
+       (                                                                               \
+               ((p)->dist <= (emins)[(p)->type])?      \
+                       1                                                               \
+               :                                                                       \
+               (                                                                       \
+                       ((p)->dist >= (emaxs)[(p)->type])?\
+                               2                                                       \
+                       :                                                               \
+                               3                                                       \
+               )                                                                       \
+       )                                                                               \
+       :                                                                               \
+               (p)->BoxOnPlaneSideFunc( (emins), (emaxs), (p)))
+
+//             BoxOnPlaneSide( (emins), (emaxs), (p)))
+
+#define PlaneDist(point,plane) ((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal))
+#define PlaneDiff(point,plane) ((plane)->type < 3 ? (point)[(plane)->type] - (plane)->dist : DotProduct((point), (plane)->normal) - (plane)->dist)
\ No newline at end of file
diff --git a/menu.c b/menu.c
new file mode 100644 (file)
index 0000000..cde0225
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,3556 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+#ifdef _WIN32
+#include "winquake.h"
+#endif
+
+void (*vid_menudrawfn)(void);
+void (*vid_menukeyfn)(int key);
+
+#define TYPE_DEMO 1
+#define TYPE_GAME 2
+#define TYPE_BOTH 3
+
+int NehGameType;
+
+enum {m_none, m_main, m_demo, m_singleplayer, m_load, m_save, m_multiplayer, m_setup, m_net, m_options, m_video, m_keys, m_help, m_quit, m_serialconfig, m_modemconfig, m_lanconfig, m_gameoptions, m_search, m_slist} m_state;
+
+void M_Menu_Main_f (void);
+       void M_Menu_SinglePlayer_f (void);
+               void M_Menu_Load_f (void);
+               void M_Menu_Save_f (void);
+       void M_Menu_MultiPlayer_f (void);
+               void M_Menu_Setup_f (void);
+               void M_Menu_Net_f (void);
+       void M_Menu_Options_f (void);
+               void M_Menu_Keys_f (void);
+               void M_Menu_Video_f (void);
+       void M_Menu_Help_f (void);
+       void M_Menu_Quit_f (void);
+void M_Menu_SerialConfig_f (void);
+       void M_Menu_ModemConfig_f (void);
+void M_Menu_LanConfig_f (void);
+void M_Menu_GameOptions_f (void);
+void M_Menu_Search_f (void);
+void M_Menu_ServerList_f (void);
+
+void M_Main_Draw (void);
+       void M_SinglePlayer_Draw (void);
+               void M_Load_Draw (void);
+               void M_Save_Draw (void);
+       void M_MultiPlayer_Draw (void);
+               void M_Setup_Draw (void);
+               void M_Net_Draw (void);
+       void M_Options_Draw (void);
+               void M_Keys_Draw (void);
+               void M_Video_Draw (void);
+       void M_Help_Draw (void);
+       void M_Quit_Draw (void);
+void M_SerialConfig_Draw (void);
+       void M_ModemConfig_Draw (void);
+void M_LanConfig_Draw (void);
+void M_GameOptions_Draw (void);
+void M_Search_Draw (void);
+void M_ServerList_Draw (void);
+
+void M_Main_Key (int key);
+       void M_SinglePlayer_Key (int key);
+               void M_Load_Key (int key);
+               void M_Save_Key (int key);
+       void M_MultiPlayer_Key (int key);
+               void M_Setup_Key (int key);
+               void M_Net_Key (int key);
+       void M_Options_Key (int key);
+               void M_Keys_Key (int key);
+               void M_Video_Key (int key);
+       void M_Help_Key (int key);
+       void M_Quit_Key (int key);
+void M_SerialConfig_Key (int key);
+       void M_ModemConfig_Key (int key);
+void M_LanConfig_Key (int key);
+void M_GameOptions_Key (int key);
+void M_Search_Key (int key);
+void M_ServerList_Key (int key);
+
+qboolean       m_entersound;           // play after drawing a frame, so caching
+                                                               // won't disrupt the sound
+qboolean       m_recursiveDraw;
+
+int                    m_return_state;
+qboolean       m_return_onerror;
+char           m_return_reason [32];
+
+#define StartingGame   (m_multiplayer_cursor == 1)
+#define JoiningGame            (m_multiplayer_cursor == 0)
+#define SerialConfig   (m_net_cursor == 0)
+#define DirectConfig   (m_net_cursor == 1)
+#define        IPXConfig               (m_net_cursor == 2)
+#define        TCPIPConfig             (m_net_cursor == 3)
+
+void M_ConfigureNetSubsystem(void);
+
+// Nehahra
+int NumberOfDemos;
+typedef struct
+{
+       char name[50];
+       char desc[50];
+} demonames_t;
+
+demonames_t Demos[35];
+
+/*
+================
+M_DrawCharacter
+
+Draws one solid graphics character
+================
+*/
+void M_DrawCharacter (int cx, int line, int num)
+{
+       Draw_Character ( cx + ((vid.width - 320)>>1), line, num);
+}
+
+void M_Print (int cx, int cy, char *str)
+{
+       while (*str)
+       {
+               M_DrawCharacter (cx, cy, (*str)+128);
+               str++;
+               cx += 8;
+       }
+}
+
+void M_PrintWhite (int cx, int cy, char *str)
+{
+       while (*str)
+       {
+               M_DrawCharacter (cx, cy, *str);
+               str++;
+               cx += 8;
+       }
+}
+
+void M_DrawTransPic (int x, int y, qpic_t *pic)
+{
+       Draw_TransPic (x + ((vid.width - 320)>>1), y, pic);
+}
+
+void M_DrawPic (int x, int y, qpic_t *pic)
+{
+       Draw_Pic (x + ((vid.width - 320)>>1), y, pic);
+}
+
+byte identityTable[256];
+byte translationTable[256];
+
+void M_BuildTranslationTable(int top, int bottom)
+{
+       int             j;
+       byte    *dest, *source;
+
+       for (j = 0; j < 256; j++)
+               identityTable[j] = j;
+       dest = translationTable;
+       source = identityTable;
+       memcpy (dest, source, 256);
+
+       // LordHavoc: corrected skin color ranges
+       if (top < 128 || (top >= 224 && top < 240))     // the artists made some backwards ranges.  sigh.
+               memcpy (dest + TOP_RANGE, source + top, 16);
+       else
+               for (j=0 ; j<16 ; j++)
+                       dest[TOP_RANGE+j] = source[top+15-j];
+
+       // LordHavoc: corrected skin color ranges
+       if (bottom < 128 || (bottom >= 224 && bottom < 240))
+               memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
+       else
+               for (j=0 ; j<16 ; j++)
+                       dest[BOTTOM_RANGE+j] = source[bottom+15-j];
+}
+
+
+void M_DrawTransPicTranslate (int x, int y, qpic_t *pic)
+{
+       Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable);
+}
+
+
+void M_DrawTextBox (int x, int y, int width, int lines)
+{
+       qpic_t  *p;
+       int             cx, cy;
+       int             n;
+
+       // draw left side
+       cx = x;
+       cy = y;
+       p = Draw_CachePic ("gfx/box_tl.lmp");
+       M_DrawTransPic (cx, cy, p);
+       p = Draw_CachePic ("gfx/box_ml.lmp");
+       for (n = 0; n < lines; n++)
+       {
+               cy += 8;
+               M_DrawTransPic (cx, cy, p);
+       }
+       p = Draw_CachePic ("gfx/box_bl.lmp");
+       M_DrawTransPic (cx, cy+8, p);
+
+       // draw middle
+       cx += 8;
+       while (width > 0)
+       {
+               cy = y;
+               p = Draw_CachePic ("gfx/box_tm.lmp");
+               M_DrawTransPic (cx, cy, p);
+               p = Draw_CachePic ("gfx/box_mm.lmp");
+               for (n = 0; n < lines; n++)
+               {
+                       cy += 8;
+                       if (n == 1)
+                               p = Draw_CachePic ("gfx/box_mm2.lmp");
+                       M_DrawTransPic (cx, cy, p);
+               }
+               p = Draw_CachePic ("gfx/box_bm.lmp");
+               M_DrawTransPic (cx, cy+8, p);
+               width -= 2;
+               cx += 16;
+       }
+
+       // draw right side
+       cy = y;
+       p = Draw_CachePic ("gfx/box_tr.lmp");
+       M_DrawTransPic (cx, cy, p);
+       p = Draw_CachePic ("gfx/box_mr.lmp");
+       for (n = 0; n < lines; n++)
+       {
+               cy += 8;
+               M_DrawTransPic (cx, cy, p);
+       }
+       p = Draw_CachePic ("gfx/box_br.lmp");
+       M_DrawTransPic (cx, cy+8, p);
+}
+
+//=============================================================================
+
+int m_save_demonum;
+
+/*
+================
+M_ToggleMenu_f
+================
+*/
+void M_ToggleMenu_f (void)
+{
+       m_entersound = true;
+
+       if (key_dest == key_menu)
+       {
+               if (m_state != m_main)
+               {
+                       M_Menu_Main_f ();
+                       return;
+               }
+               key_dest = key_game;
+               m_state = m_none;
+               return;
+       }
+       if (key_dest == key_console)
+       {
+               Con_ToggleConsole_f ();
+       }
+       else
+       {
+               M_Menu_Main_f ();
+       }
+}
+
+int demo_cursor;
+void M_Demo_Draw (void)
+{
+       int             i;
+
+       for (i=0; i < NumberOfDemos; i++)
+               M_Print (16, 16 + 8*i, Demos[i].desc);
+
+       // line cursor
+       M_DrawCharacter (8, 16 + demo_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Menu_Demos_f (void)
+{
+        key_dest = key_menu;
+        m_state = m_demo;
+        m_entersound = true;
+
+
+        NumberOfDemos = 34;
+
+        strcpy(Demos[0].name,  "INTRO");         strcpy(Demos[0].desc,  "Prologue");
+        strcpy(Demos[1].name,  "GENF");          strcpy(Demos[1].desc,  "The Beginning");
+        strcpy(Demos[2].name,  "GENLAB");        strcpy(Demos[2].desc,  "A Doomed Project");
+        strcpy(Demos[3].name,  "NEHCRE");        strcpy(Demos[3].desc,  "The New Recruits");
+        strcpy(Demos[4].name,  "MAXNEH");        strcpy(Demos[4].desc,  "Breakthrough");
+        strcpy(Demos[5].name,  "MAXCHAR");       strcpy(Demos[5].desc,  "Renewal and Duty");
+        strcpy(Demos[6].name,  "CRISIS");        strcpy(Demos[6].desc,  "Worlds Collide");
+        strcpy(Demos[7].name,  "POSTCRIS");      strcpy(Demos[7].desc,  "Darkening Skies");
+        strcpy(Demos[8].name,  "HEARING");       strcpy(Demos[8].desc,  "The Hearing");
+        strcpy(Demos[9].name,  "GETJACK");       strcpy(Demos[9].desc,  "On a Mexican Radio");
+        strcpy(Demos[10].name, "PRELUDE");       strcpy(Demos[10].desc, "Honor and Justice");
+        strcpy(Demos[11].name, "ABASE");         strcpy(Demos[11].desc, "A Message Sent");
+        strcpy(Demos[12].name, "EFFECT");        strcpy(Demos[12].desc, "The Other Side");
+        strcpy(Demos[13].name, "UHOH");          strcpy(Demos[13].desc, "Missing in Action");
+        strcpy(Demos[14].name, "PREPARE");       strcpy(Demos[14].desc, "The Response");
+        strcpy(Demos[15].name, "VISION");        strcpy(Demos[15].desc, "Farsighted Eyes");
+        strcpy(Demos[16].name, "MAXTURNS");      strcpy(Demos[16].desc, "Enter the Immortal");
+        strcpy(Demos[17].name, "BACKLOT");       strcpy(Demos[17].desc, "Separate Ways");
+        strcpy(Demos[18].name, "MAXSIDE");       strcpy(Demos[18].desc, "The Ancient Runes");
+        strcpy(Demos[19].name, "COUNTER");       strcpy(Demos[19].desc, "The New Initiative");
+        strcpy(Demos[20].name, "WARPREP");       strcpy(Demos[20].desc, "Ghosts to the World");
+        strcpy(Demos[21].name, "COUNTER1");      strcpy(Demos[21].desc, "A Fate Worse Than Death");
+        strcpy(Demos[22].name, "COUNTER2");      strcpy(Demos[22].desc, "Friendly Fire");
+        strcpy(Demos[23].name, "COUNTER3");      strcpy(Demos[23].desc, "Minor Setback");
+        strcpy(Demos[24].name, "MADMAX");        strcpy(Demos[24].desc, "Scores to Settle");
+        strcpy(Demos[25].name, "QUAKE");         strcpy(Demos[25].desc, "One Man");
+        strcpy(Demos[26].name, "CTHMM");         strcpy(Demos[26].desc, "Shattered Masks");
+        strcpy(Demos[27].name, "SHADES");        strcpy(Demos[27].desc, "Deal with the Dead");
+        strcpy(Demos[28].name, "GOPHIL");        strcpy(Demos[28].desc, "An Unlikely Hero");
+        strcpy(Demos[29].name, "CSTRIKE");       strcpy(Demos[29].desc, "War in Hell");
+        strcpy(Demos[30].name, "SHUBSET");       strcpy(Demos[30].desc, "The Conspiracy");
+        strcpy(Demos[31].name, "SHUBDIE");       strcpy(Demos[31].desc, "Even Death May Die");
+        strcpy(Demos[32].name, "NEWRANKS");      strcpy(Demos[32].desc, "An Empty Throne");
+        strcpy(Demos[33].name, "SEAL");          strcpy(Demos[33].desc, "The Seal is Broken");
+}
+
+void M_Demo_Key (int k)
+{
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_Main_f ();
+               break;
+
+       case K_ENTER:
+               S_LocalSound ("misc/menu2.wav");
+               m_state = m_none;
+               key_dest = key_game;
+               SCR_BeginLoadingPlaque ();
+               Cbuf_AddText (va ("playdemo %s\n", Demos[demo_cursor].name));
+               return;
+
+       case K_UPARROW:
+       case K_LEFTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               demo_cursor--;
+               if (demo_cursor < 0)
+                       demo_cursor = NumberOfDemos;
+               break;
+
+       case K_DOWNARROW:
+       case K_RIGHTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               demo_cursor++;
+               if (demo_cursor > NumberOfDemos)
+                       demo_cursor = 0;
+               break;
+       }
+}
+
+//=============================================================================
+/* MAIN MENU */
+
+int    m_main_cursor;
+//#define      MAIN_ITEMS      5
+
+int MAIN_ITEMS = 4; // Nehahra: Menu Disable
+
+void M_Menu_Main_f (void)
+{
+       if (nehahra)
+       {
+               if (NehGameType == TYPE_DEMO)
+                       MAIN_ITEMS = 4;
+               else if (NehGameType == TYPE_GAME)
+                       MAIN_ITEMS = 5;
+               else
+                       MAIN_ITEMS = 6;
+       }
+       else
+               MAIN_ITEMS = 5;
+
+       if (key_dest != key_menu)
+       {
+               m_save_demonum = cls.demonum;
+               cls.demonum = -1;
+       }
+       key_dest = key_menu;
+       m_state = m_main;
+       m_entersound = true;
+}
+
+
+void M_Main_Draw (void)
+{
+       int             f;
+       qpic_t  *p;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/ttl_main.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+// Nehahra
+       if (nehahra)
+       {
+               if (NehGameType == TYPE_BOTH)
+                       M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp"));
+               else if (NehGameType == TYPE_GAME)
+                       M_DrawTransPic (72, 32, Draw_CachePic ("gfx/gamemenu.lmp"));
+               else
+                       M_DrawTransPic (72, 32, Draw_CachePic ("gfx/demomenu.lmp"));
+       }
+       else
+               M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp"));
+
+       f = (int)(host_time * 10)%6;
+
+       M_DrawTransPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_Main_Key (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+               key_dest = key_game;
+               m_state = m_none;
+               cls.demonum = m_save_demonum;
+               if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected)
+                       CL_NextDemo ();
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (++m_main_cursor >= MAIN_ITEMS)
+                       m_main_cursor = 0;
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (--m_main_cursor < 0)
+                       m_main_cursor = MAIN_ITEMS - 1;
+               break;
+
+       case K_ENTER:
+               m_entersound = true;
+
+               if (nehahra)
+               {
+                       switch (NehGameType)
+                       {
+                       case TYPE_BOTH:
+                               switch (m_main_cursor)
+                               {
+                               case 0:
+                                       M_Menu_SinglePlayer_f ();
+                                       break;
+
+                               case 1:
+                                       M_Menu_Demos_f ();
+                                       break;
+
+                               case 2:
+                                       M_Menu_MultiPlayer_f ();
+                                       break;
+
+                               case 3:
+                                       M_Menu_Options_f ();
+                                       break;
+
+                               case 4:
+                                       key_dest = key_game;
+                                       if (sv.active)
+                                               Cbuf_AddText ("disconnect\n");
+                                       Cbuf_AddText ("playdemo ENDCRED\n");
+                                       break;
+
+                               case 5:
+                                       M_Menu_Quit_f ();
+                                       break;
+                               }
+                               break;
+                       case TYPE_GAME:
+                               switch (m_main_cursor)
+                               {
+                               case 0:
+                                       M_Menu_SinglePlayer_f ();
+                                       break;
+
+                               case 1:
+                                       M_Menu_MultiPlayer_f ();
+                                       break;
+
+                               case 2:
+                                       M_Menu_Options_f ();
+                                       break;
+
+                               case 3:
+                                       key_dest = key_game;
+                                       if (sv.active)
+                                               Cbuf_AddText ("disconnect\n");
+                                       Cbuf_AddText ("playdemo ENDCRED\n");
+                                       break;
+
+                               case 4:
+                                       M_Menu_Quit_f ();
+                                       break;
+                               }
+                               break;
+                       case TYPE_DEMO:
+                               switch (m_main_cursor)
+                               {
+                               case 0:
+                                       M_Menu_Demos_f ();
+                                       break;
+
+                               case 1:
+                                       key_dest = key_game;
+                                       if (sv.active)
+                                               Cbuf_AddText ("disconnect\n");
+                                       Cbuf_AddText ("playdemo ENDCRED\n");
+                                       break;
+
+                               case 2:
+                                       M_Menu_Options_f ();
+                                       break;
+
+                               case 3:
+                                       M_Menu_Quit_f ();
+                                       break;
+                               }
+                               break;
+                       }
+               }
+               else
+               {
+                       switch (m_main_cursor)
+                       {
+                       case 0:
+                               M_Menu_SinglePlayer_f ();
+                               break;
+
+                       case 1:
+                               M_Menu_MultiPlayer_f ();
+                               break;
+
+                       case 2:
+                               M_Menu_Options_f ();
+                               break;
+
+                       case 3:
+                               M_Menu_Help_f ();
+                               break;
+
+                       case 4:
+                               M_Menu_Quit_f ();
+                               break;
+                       }
+               }
+       }
+}
+
+//=============================================================================
+/* SINGLE PLAYER MENU */
+
+int    m_singleplayer_cursor;
+#define        SINGLEPLAYER_ITEMS      3
+
+
+void M_Menu_SinglePlayer_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_singleplayer;
+       m_entersound = true;
+}
+
+
+void M_SinglePlayer_Draw (void)
+{
+       int             f;
+       qpic_t  *p;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/ttl_sgl.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+       M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") );
+
+       f = (int)(host_time * 10)%6;
+
+       M_DrawTransPic (54, 32 + m_singleplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_SinglePlayer_Key (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_Main_f ();
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS)
+                       m_singleplayer_cursor = 0;
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (--m_singleplayer_cursor < 0)
+                       m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1;
+               break;
+
+       case K_ENTER:
+               m_entersound = true;
+
+               switch (m_singleplayer_cursor)
+               {
+               case 0:
+                       if (sv.active)
+                               if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n"))
+                                       break;
+                       key_dest = key_game;
+                       if (sv.active)
+                               Cbuf_AddText ("disconnect\n");
+                       Cbuf_AddText ("maxplayers 1\n");
+                       if (nehahra)
+                               Cbuf_AddText ("map nehstart\n");
+                       else
+                               Cbuf_AddText ("map start\n");
+                       break;
+
+               case 1:
+                       M_Menu_Load_f ();
+                       break;
+
+               case 2:
+                       M_Menu_Save_f ();
+                       break;
+               }
+       }
+}
+
+//=============================================================================
+/* LOAD/SAVE MENU */
+
+int            load_cursor;            // 0 < load_cursor < MAX_SAVEGAMES
+
+#define        MAX_SAVEGAMES           12
+char   m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1];
+int            loadable[MAX_SAVEGAMES];
+
+void M_ScanSaves (void)
+{
+       int             i, j;
+       char    name[MAX_OSPATH];
+       FILE    *f;
+       int             version;
+
+       for (i=0 ; i<MAX_SAVEGAMES ; i++)
+       {
+               strcpy (m_filenames[i], "--- UNUSED SLOT ---");
+               loadable[i] = false;
+               sprintf (name, "%s/s%i.sav", com_gamedir, i);
+               f = fopen (name, "r");
+               if (!f)
+                       continue;
+               fscanf (f, "%i\n", &version);
+               fscanf (f, "%79s\n", name);
+               strncpy (m_filenames[i], name, sizeof(m_filenames[i])-1);
+
+       // change _ back to space
+               for (j=0 ; j<SAVEGAME_COMMENT_LENGTH ; j++)
+                       if (m_filenames[i][j] == '_')
+                               m_filenames[i][j] = ' ';
+               loadable[i] = true;
+               fclose (f);
+       }
+}
+
+void M_Menu_Load_f (void)
+{
+       m_entersound = true;
+       m_state = m_load;
+       key_dest = key_menu;
+       M_ScanSaves ();
+}
+
+
+void M_Menu_Save_f (void)
+{
+       if (!sv.active)
+               return;
+       if (cl.intermission)
+               return;
+       if (svs.maxclients != 1)
+               return;
+       m_entersound = true;
+       m_state = m_save;
+       key_dest = key_menu;
+       M_ScanSaves ();
+}
+
+
+void M_Load_Draw (void)
+{
+       int             i;
+       qpic_t  *p;
+
+       p = Draw_CachePic ("gfx/p_load.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       for (i=0 ; i< MAX_SAVEGAMES; i++)
+               M_Print (16, 32 + 8*i, m_filenames[i]);
+
+// line cursor
+       M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Save_Draw (void)
+{
+       int             i;
+       qpic_t  *p;
+
+       p = Draw_CachePic ("gfx/p_save.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       for (i=0 ; i<MAX_SAVEGAMES ; i++)
+               M_Print (16, 32 + 8*i, m_filenames[i]);
+
+// line cursor
+       M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Load_Key (int k)
+{
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_SinglePlayer_f ();
+               break;
+
+       case K_ENTER:
+               S_LocalSound ("misc/menu2.wav");
+               if (!loadable[load_cursor])
+                       return;
+               m_state = m_none;
+               key_dest = key_game;
+
+       // Host_Loadgame_f can't bring up the loading plaque because too much
+       // stack space has been used, so do it now
+               SCR_BeginLoadingPlaque ();
+
+       // issue the load command
+               Cbuf_AddText (va ("load s%i\n", load_cursor) );
+               return;
+
+       case K_UPARROW:
+       case K_LEFTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               load_cursor--;
+               if (load_cursor < 0)
+                       load_cursor = MAX_SAVEGAMES-1;
+               break;
+
+       case K_DOWNARROW:
+       case K_RIGHTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               load_cursor++;
+               if (load_cursor >= MAX_SAVEGAMES)
+                       load_cursor = 0;
+               break;
+       }
+}
+
+
+void M_Save_Key (int k)
+{
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_SinglePlayer_f ();
+               break;
+
+       case K_ENTER:
+               m_state = m_none;
+               key_dest = key_game;
+               Cbuf_AddText (va("save s%i\n", load_cursor));
+               return;
+
+       case K_UPARROW:
+       case K_LEFTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               load_cursor--;
+               if (load_cursor < 0)
+                       load_cursor = MAX_SAVEGAMES-1;
+               break;
+
+       case K_DOWNARROW:
+       case K_RIGHTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               load_cursor++;
+               if (load_cursor >= MAX_SAVEGAMES)
+                       load_cursor = 0;
+               break;
+       }
+}
+
+//=============================================================================
+/* MULTIPLAYER MENU */
+
+int    m_multiplayer_cursor;
+#define        MULTIPLAYER_ITEMS       3
+
+
+void M_Menu_MultiPlayer_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_multiplayer;
+       m_entersound = true;
+}
+
+
+void M_MultiPlayer_Draw (void)
+{
+       int             f;
+       qpic_t  *p;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+       M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") );
+
+       f = (int)(host_time * 10)%6;
+
+       M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+
+       if (serialAvailable || ipxAvailable || tcpipAvailable)
+               return;
+       M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available");
+}
+
+
+void M_MultiPlayer_Key (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_Main_f ();
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS)
+                       m_multiplayer_cursor = 0;
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (--m_multiplayer_cursor < 0)
+                       m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
+               break;
+
+       case K_ENTER:
+               m_entersound = true;
+               switch (m_multiplayer_cursor)
+               {
+               case 0:
+                       if (serialAvailable || ipxAvailable || tcpipAvailable)
+                               M_Menu_Net_f ();
+                       break;
+
+               case 1:
+                       if (serialAvailable || ipxAvailable || tcpipAvailable)
+                               M_Menu_Net_f ();
+                       break;
+
+               case 2:
+                       M_Menu_Setup_f ();
+                       break;
+               }
+       }
+}
+
+//=============================================================================
+/* SETUP MENU */
+
+int            setup_cursor = 4;
+int            setup_cursor_table[] = {40, 56, 80, 104, 140};
+
+char   setup_hostname[16];
+char   setup_myname[16];
+int            setup_oldtop;
+int            setup_oldbottom;
+int            setup_top;
+int            setup_bottom;
+
+#define        NUM_SETUP_CMDS  5
+
+void M_Menu_Setup_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_setup;
+       m_entersound = true;
+       strcpy(setup_myname, cl_name.string);
+       strcpy(setup_hostname, hostname.string);
+       setup_top = setup_oldtop = ((int)cl_color.value) >> 4;
+       setup_bottom = setup_oldbottom = ((int)cl_color.value) & 15;
+}
+
+
+void M_Setup_Draw (void)
+{
+       qpic_t  *p;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       M_Print (64, 40, "Hostname");
+       M_DrawTextBox (160, 32, 16, 1);
+       M_Print (168, 40, setup_hostname);
+
+       M_Print (64, 56, "Your name");
+       M_DrawTextBox (160, 48, 16, 1);
+       M_Print (168, 56, setup_myname);
+
+       M_Print (64, 80, "Shirt color");
+       M_Print (64, 104, "Pants color");
+
+       M_DrawTextBox (64, 140-8, 14, 1);
+       M_Print (72, 140, "Accept Changes");
+
+       p = Draw_CachePic ("gfx/bigbox.lmp");
+       M_DrawTransPic (160, 64, p);
+       p = Draw_CachePic ("gfx/menuplyr.lmp");
+       M_BuildTranslationTable(setup_top*16, setup_bottom*16);
+       M_DrawTransPicTranslate (172, 72, p);
+
+       M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
+
+       if (setup_cursor == 0)
+               M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
+
+       if (setup_cursor == 1)
+               M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
+}
+
+
+void M_Setup_Key (int k)
+{
+       int                     l;
+
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_MultiPlayer_f ();
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               setup_cursor--;
+               if (setup_cursor < 0)
+                       setup_cursor = NUM_SETUP_CMDS-1;
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               setup_cursor++;
+               if (setup_cursor >= NUM_SETUP_CMDS)
+                       setup_cursor = 0;
+               break;
+
+       case K_LEFTARROW:
+               if (setup_cursor < 2)
+                       return;
+               S_LocalSound ("misc/menu3.wav");
+               if (setup_cursor == 2)
+                       setup_top = setup_top - 1;
+               if (setup_cursor == 3)
+                       setup_bottom = setup_bottom - 1;
+               break;
+       case K_RIGHTARROW:
+               if (setup_cursor < 2)
+                       return;
+forward:
+               S_LocalSound ("misc/menu3.wav");
+               if (setup_cursor == 2)
+                       setup_top = setup_top + 1;
+               if (setup_cursor == 3)
+                       setup_bottom = setup_bottom + 1;
+               break;
+
+       case K_ENTER:
+               if (setup_cursor == 0 || setup_cursor == 1)
+                       return;
+
+               if (setup_cursor == 2 || setup_cursor == 3)
+                       goto forward;
+
+               // setup_cursor == 4 (OK)
+               if (strcmp(cl_name.string, setup_myname) != 0)
+                       Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
+               if (strcmp(hostname.string, setup_hostname) != 0)
+                       Cvar_Set("hostname", setup_hostname);
+               if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
+                       Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) );
+               m_entersound = true;
+               M_Menu_MultiPlayer_f ();
+               break;
+
+       case K_BACKSPACE:
+               if (setup_cursor == 0)
+               {
+                       if (strlen(setup_hostname))
+                               setup_hostname[strlen(setup_hostname)-1] = 0;
+               }
+
+               if (setup_cursor == 1)
+               {
+                       if (strlen(setup_myname))
+                               setup_myname[strlen(setup_myname)-1] = 0;
+               }
+               break;
+
+       default:
+               if (k < 32 || k > 127)
+                       break;
+               if (setup_cursor == 0)
+               {
+                       l = strlen(setup_hostname);
+                       if (l < 15)
+                       {
+                               setup_hostname[l+1] = 0;
+                               setup_hostname[l] = k;
+                       }
+               }
+               if (setup_cursor == 1)
+               {
+                       l = strlen(setup_myname);
+                       if (l < 15)
+                       {
+                               setup_myname[l+1] = 0;
+                               setup_myname[l] = k;
+                       }
+               }
+       }
+
+       if (setup_top > 13)
+               setup_top = 0;
+       if (setup_top < 0)
+               setup_top = 13;
+       if (setup_bottom > 13)
+               setup_bottom = 0;
+       if (setup_bottom < 0)
+               setup_bottom = 13;
+}
+
+//=============================================================================
+/* NET MENU */
+
+int    m_net_cursor;
+int m_net_items;
+int m_net_saveHeight;
+
+char *net_helpMessage [] =
+{
+/* .........1.........2.... */
+  "                        ",
+  " Two computers connected",
+  "   through two modems.  ",
+  "                        ",
+
+  "                        ",
+  " Two computers connected",
+  " by a null-modem cable. ",
+  "                        ",
+
+  " Novell network LANs    ",
+  " or Windows 95 DOS-box. ",
+  "                        ",
+  "(LAN=Local Area Network)",
+
+  " Commonly used to play  ",
+  " over the Internet, but ",
+  " also used on a Local   ",
+  " Area Network.          "
+};
+
+void M_Menu_Net_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_net;
+       m_entersound = true;
+       m_net_items = 4;
+
+       if (m_net_cursor >= m_net_items)
+               m_net_cursor = 0;
+       m_net_cursor--;
+       M_Net_Key (K_DOWNARROW);
+}
+
+
+void M_Net_Draw (void)
+{
+       int             f;
+       qpic_t  *p;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       f = 32;
+
+       if (serialAvailable)
+       {
+               p = Draw_CachePic ("gfx/netmen1.lmp");
+       }
+       else
+       {
+#ifdef _WIN32
+               p = NULL;
+#else
+               p = Draw_CachePic ("gfx/dim_modm.lmp");
+#endif
+       }
+
+       if (p)
+               M_DrawTransPic (72, f, p);
+
+       f += 19;
+
+       if (serialAvailable)
+       {
+               p = Draw_CachePic ("gfx/netmen2.lmp");
+       }
+       else
+       {
+#ifdef _WIN32
+               p = NULL;
+#else
+               p = Draw_CachePic ("gfx/dim_drct.lmp");
+#endif
+       }
+
+       if (p)
+               M_DrawTransPic (72, f, p);
+
+       f += 19;
+       if (ipxAvailable)
+               p = Draw_CachePic ("gfx/netmen3.lmp");
+       else
+               p = Draw_CachePic ("gfx/dim_ipx.lmp");
+       M_DrawTransPic (72, f, p);
+
+       f += 19;
+       if (tcpipAvailable)
+               p = Draw_CachePic ("gfx/netmen4.lmp");
+       else
+               p = Draw_CachePic ("gfx/dim_tcp.lmp");
+       M_DrawTransPic (72, f, p);
+
+       if (m_net_items == 5)   // JDC, could just be removed
+       {
+               f += 19;
+               p = Draw_CachePic ("gfx/netmen5.lmp");
+               M_DrawTransPic (72, f, p);
+       }
+
+       f = (320-26*8)/2;
+       M_DrawTextBox (f, 134, 24, 4);
+       f += 8;
+       M_Print (f, 142, net_helpMessage[m_net_cursor*4+0]);
+       M_Print (f, 150, net_helpMessage[m_net_cursor*4+1]);
+       M_Print (f, 158, net_helpMessage[m_net_cursor*4+2]);
+       M_Print (f, 166, net_helpMessage[m_net_cursor*4+3]);
+
+       f = (int)(host_time * 10)%6;
+       M_DrawTransPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_Net_Key (int k)
+{
+again:
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_MultiPlayer_f ();
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (++m_net_cursor >= m_net_items)
+                       m_net_cursor = 0;
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               if (--m_net_cursor < 0)
+                       m_net_cursor = m_net_items - 1;
+               break;
+
+       case K_ENTER:
+               m_entersound = true;
+
+               switch (m_net_cursor)
+               {
+               case 0:
+                       M_Menu_SerialConfig_f ();
+                       break;
+
+               case 1:
+                       M_Menu_SerialConfig_f ();
+                       break;
+
+               case 2:
+                       M_Menu_LanConfig_f ();
+                       break;
+
+               case 3:
+                       M_Menu_LanConfig_f ();
+                       break;
+
+               case 4:
+// multiprotocol
+                       break;
+               }
+       }
+
+       if (m_net_cursor == 0 && !serialAvailable)
+               goto again;
+       if (m_net_cursor == 1 && !serialAvailable)
+               goto again;
+       if (m_net_cursor == 2 && !ipxAvailable)
+               goto again;
+       if (m_net_cursor == 3 && !tcpipAvailable)
+               goto again;
+}
+
+//=============================================================================
+/* OPTIONS MENU */
+
+#ifdef _WIN32
+#define        OPTIONS_ITEMS   14
+#else
+#define        OPTIONS_ITEMS   13
+#endif
+
+#define        SLIDER_RANGE    10
+
+int            options_cursor;
+
+void M_Menu_Options_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_options;
+       m_entersound = true;
+
+#ifdef _WIN32
+       if ((options_cursor == 13) && (modestate != MS_WINDOWED))
+       {
+               options_cursor = 0;
+       }
+#endif
+}
+
+
+void M_AdjustSliders (int dir)
+{
+       S_LocalSound ("misc/menu3.wav");
+
+       switch (options_cursor)
+       {
+       case 3: // screen size
+               scr_viewsize.value += dir * 10;
+               if (scr_viewsize.value < 30)
+                       scr_viewsize.value = 30;
+               if (scr_viewsize.value > 120)
+                       scr_viewsize.value = 120;
+               Cvar_SetValue ("viewsize", scr_viewsize.value);
+               break;
+       case 4: // brightness
+               brightness.value += dir * 0.25;
+               if (brightness.value < 1)
+                       brightness.value = 1;
+               if (brightness.value > 5)
+                       brightness.value = 5;
+               Cvar_SetValue ("brightness", brightness.value);
+               break;
+       case 5: // mouse speed
+               sensitivity.value += dir * 0.5;
+               if (sensitivity.value < 1)
+                       sensitivity.value = 1;
+               if (sensitivity.value > 50)
+                       sensitivity.value = 50;
+               Cvar_SetValue ("sensitivity", sensitivity.value);
+               break;
+       case 6: // music volume
+#ifdef _WIN32
+               bgmvolume.value += dir * 1.0;
+#else
+               bgmvolume.value += dir * 0.1;
+#endif
+               if (bgmvolume.value < 0)
+                       bgmvolume.value = 0;
+               if (bgmvolume.value > 1)
+                       bgmvolume.value = 1;
+               Cvar_SetValue ("bgmvolume", bgmvolume.value);
+               break;
+       case 7: // sfx volume
+               volume.value += dir * 0.1;
+               if (volume.value < 0)
+                       volume.value = 0;
+               if (volume.value > 1)
+                       volume.value = 1;
+               Cvar_SetValue ("volume", volume.value);
+               break;
+
+       case 8: // allways run
+               if (cl_forwardspeed.value > 200)
+               {
+                       Cvar_SetValue ("cl_forwardspeed", 200);
+                       Cvar_SetValue ("cl_backspeed", 200);
+               }
+               else
+               {
+                       Cvar_SetValue ("cl_forwardspeed", 400);
+                       Cvar_SetValue ("cl_backspeed", 400);
+               }
+               break;
+
+       case 9: // invert mouse
+               Cvar_SetValue ("m_pitch", -m_pitch.value);
+               break;
+
+       case 10:        // lookspring
+               Cvar_SetValue ("lookspring", !lookspring.value);
+               break;
+
+       case 11:        // lookstrafe
+               Cvar_SetValue ("lookstrafe", !lookstrafe.value);
+               break;
+
+#ifdef _WIN32
+       case 13:        // _windowed_mouse
+               Cvar_SetValue ("_windowed_mouse", !_windowed_mouse.value);
+               break;
+#endif
+       }
+}
+
+
+void M_DrawSlider (int x, int y, float range)
+{
+       int     i;
+
+       if (range < 0)
+               range = 0;
+       if (range > 1)
+               range = 1;
+       M_DrawCharacter (x-8, y, 128);
+       for (i=0 ; i<SLIDER_RANGE ; i++)
+               M_DrawCharacter (x + i*8, y, 129);
+       M_DrawCharacter (x+i*8, y, 130);
+       M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131);
+}
+
+void M_DrawCheckbox (int x, int y, int on)
+{
+#if 0
+       if (on)
+               M_DrawCharacter (x, y, 131);
+       else
+               M_DrawCharacter (x, y, 129);
+#endif
+       if (on)
+               M_Print (x, y, "on");
+       else
+               M_Print (x, y, "off");
+}
+
+void M_Options_Draw (void)
+{
+       float           r;
+       qpic_t  *p;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_option.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       M_Print (16, 32, "    Customize controls");
+       M_Print (16, 40, "         Go to console");
+       M_Print (16, 48, "     Reset to defaults");
+
+       M_Print (16, 56, "           Screen size");
+       r = (scr_viewsize.value - 30) / (120 - 30);
+       M_DrawSlider (220, 56, r);
+
+       M_Print (16, 64, "            Brightness");
+       r = (brightness.value - 1) / 4;
+       M_DrawSlider (220, 64, r);
+
+       M_Print (16, 72, "           Mouse Speed");
+       r = (sensitivity.value - 1)/50;
+       M_DrawSlider (220, 72, r);
+
+       M_Print (16, 80, "       CD Music Volume");
+       r = bgmvolume.value;
+       M_DrawSlider (220, 80, r);
+
+       M_Print (16, 88, "          Sound Volume");
+       r = volume.value;
+       M_DrawSlider (220, 88, r);
+
+       M_Print (16, 96,  "            Always Run");
+       M_DrawCheckbox (220, 96, cl_forwardspeed.value > 200);
+
+       M_Print (16, 104, "          Invert Mouse");
+       M_DrawCheckbox (220, 104, m_pitch.value < 0);
+
+       M_Print (16, 112, "            Lookspring");
+       M_DrawCheckbox (220, 112, lookspring.value);
+
+       M_Print (16, 120, "            Lookstrafe");
+       M_DrawCheckbox (220, 120, lookstrafe.value);
+
+       if (vid_menudrawfn)
+               M_Print (16, 128, "         Video Options");
+
+#ifdef _WIN32
+       if (modestate == MS_WINDOWED)
+       {
+               M_Print (16, 136, "             Use Mouse");
+               M_DrawCheckbox (220, 136, _windowed_mouse.value);
+       }
+#endif
+
+// cursor
+       M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Options_Key (int k)
+{
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_Main_f ();
+               break;
+
+       case K_ENTER:
+               m_entersound = true;
+               switch (options_cursor)
+               {
+               case 0:
+                       M_Menu_Keys_f ();
+                       break;
+               case 1:
+                       m_state = m_none;
+                       Con_ToggleConsole_f ();
+                       break;
+               case 2:
+                       Cbuf_AddText ("exec default.cfg\n");
+                       break;
+               case 12:
+                       M_Menu_Video_f ();
+                       break;
+               default:
+                       M_AdjustSliders (1);
+                       break;
+               }
+               return;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               options_cursor--;
+               if (options_cursor < 0)
+                       options_cursor = OPTIONS_ITEMS-1;
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               options_cursor++;
+               if (options_cursor >= OPTIONS_ITEMS)
+                       options_cursor = 0;
+               break;
+
+       case K_LEFTARROW:
+               M_AdjustSliders (-1);
+               break;
+
+       case K_RIGHTARROW:
+               M_AdjustSliders (1);
+               break;
+       }
+
+       if (options_cursor == 12 && vid_menudrawfn == NULL)
+       {
+               if (k == K_UPARROW)
+                       options_cursor = 11;
+               else
+                       options_cursor = 0;
+       }
+
+#ifdef _WIN32
+       if ((options_cursor == 13) && (modestate != MS_WINDOWED))
+       {
+               if (k == K_UPARROW)
+                       options_cursor = 12;
+               else
+                       options_cursor = 0;
+       }
+#endif
+}
+
+//=============================================================================
+/* KEYS MENU */
+
+char *bindnames[][2] =
+{
+{"+attack",            "attack"},
+{"impulse 10",                 "change weapon"},
+{"+jump",                      "jump / swim up"},
+{"+forward",           "walk forward"},
+{"+back",                      "backpedal"},
+{"+left",                      "turn left"},
+{"+right",                     "turn right"},
+{"+speed",                     "run"},
+{"+moveleft",          "step left"},
+{"+moveright",                 "step right"},
+{"+strafe",            "sidestep"},
+{"+lookup",            "look up"},
+{"+lookdown",          "look down"},
+{"centerview",                 "center view"},
+{"+mlook",                     "mouse look"},
+{"+klook",                     "keyboard look"},
+{"+moveup",                    "swim up"},
+{"+movedown",          "swim down"}
+};
+
+#define        NUMCOMMANDS     (sizeof(bindnames)/sizeof(bindnames[0]))
+
+int            keys_cursor;
+int            bind_grab;
+
+void M_Menu_Keys_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_keys;
+       m_entersound = true;
+}
+
+
+void M_FindKeysForCommand (char *command, int *twokeys)
+{
+       int             count;
+       int             j;
+       int             l;
+       char    *b;
+
+       twokeys[0] = twokeys[1] = -1;
+       l = strlen(command);
+       count = 0;
+
+       for (j=0 ; j<256 ; j++)
+       {
+               b = keybindings[j];
+               if (!b)
+                       continue;
+               if (!strncmp (b, command, l) )
+               {
+                       twokeys[count] = j;
+                       count++;
+                       if (count == 2)
+                               break;
+               }
+       }
+}
+
+void M_UnbindCommand (char *command)
+{
+       int             j;
+       int             l;
+       char    *b;
+
+       l = strlen(command);
+
+       for (j=0 ; j<256 ; j++)
+       {
+               b = keybindings[j];
+               if (!b)
+                       continue;
+               if (!strncmp (b, command, l) )
+                       Key_SetBinding (j, "");
+       }
+}
+
+
+void M_Keys_Draw (void)
+{
+       int             i, l;
+       int             keys[2];
+       char    *name;
+       int             x, y;
+       qpic_t  *p;
+
+       p = Draw_CachePic ("gfx/ttl_cstm.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       if (bind_grab)
+               M_Print (12, 32, "Press a key or button for this action");
+       else
+               M_Print (18, 32, "Enter to change, backspace to clear");
+
+// search for known bindings
+       for (i=0 ; i<NUMCOMMANDS ; i++)
+       {
+               y = 48 + 8*i;
+
+               M_Print (16, y, bindnames[i][1]);
+
+               l = strlen (bindnames[i][0]);
+
+               M_FindKeysForCommand (bindnames[i][0], keys);
+
+               if (keys[0] == -1)
+               {
+                       M_Print (140, y, "???");
+               }
+               else
+               {
+                       name = Key_KeynumToString (keys[0]);
+                       M_Print (140, y, name);
+                       x = strlen(name) * 8;
+                       if (keys[1] != -1)
+                       {
+                               M_Print (140 + x + 8, y, "or");
+                               M_Print (140 + x + 32, y, Key_KeynumToString (keys[1]));
+                       }
+               }
+       }
+
+       if (bind_grab)
+               M_DrawCharacter (130, 48 + keys_cursor*8, '=');
+       else
+               M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Keys_Key (int k)
+{
+       char    cmd[80];
+       int             keys[2];
+
+       if (bind_grab)
+       {       // defining a key
+               S_LocalSound ("misc/menu1.wav");
+               if (k == K_ESCAPE)
+               {
+                       bind_grab = false;
+               }
+               else if (k != '`')
+               {
+                       sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
+                       Cbuf_InsertText (cmd);
+               }
+
+               bind_grab = false;
+               return;
+       }
+
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_Options_f ();
+               break;
+
+       case K_LEFTARROW:
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               keys_cursor--;
+               if (keys_cursor < 0)
+                       keys_cursor = NUMCOMMANDS-1;
+               break;
+
+       case K_DOWNARROW:
+       case K_RIGHTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               keys_cursor++;
+               if (keys_cursor >= NUMCOMMANDS)
+                       keys_cursor = 0;
+               break;
+
+       case K_ENTER:           // go into bind mode
+               M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
+               S_LocalSound ("misc/menu2.wav");
+               if (keys[1] != -1)
+                       M_UnbindCommand (bindnames[keys_cursor][0]);
+               bind_grab = true;
+               break;
+
+       case K_BACKSPACE:               // delete bindings
+       case K_DEL:                             // delete bindings
+               S_LocalSound ("misc/menu2.wav");
+               M_UnbindCommand (bindnames[keys_cursor][0]);
+               break;
+       }
+}
+
+//=============================================================================
+/* VIDEO MENU */
+
+void M_Menu_Video_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_video;
+       m_entersound = true;
+}
+
+
+void M_Video_Draw (void)
+{
+       (*vid_menudrawfn) ();
+}
+
+
+void M_Video_Key (int key)
+{
+       (*vid_menukeyfn) (key);
+}
+
+//=============================================================================
+/* HELP MENU */
+
+int            help_page;
+#define        NUM_HELP_PAGES  6
+
+
+void M_Menu_Help_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_help;
+       m_entersound = true;
+       help_page = 0;
+}
+
+
+
+void M_Help_Draw (void)
+{
+       M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) );
+}
+
+
+void M_Help_Key (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_Main_f ();
+               break;
+
+       case K_UPARROW:
+       case K_RIGHTARROW:
+               m_entersound = true;
+               if (++help_page >= NUM_HELP_PAGES)
+                       help_page = 0;
+               break;
+
+       case K_DOWNARROW:
+       case K_LEFTARROW:
+               m_entersound = true;
+               if (--help_page < 0)
+                       help_page = NUM_HELP_PAGES-1;
+               break;
+       }
+
+}
+
+//=============================================================================
+/* QUIT MENU */
+
+int            msgNumber;
+int            m_quit_prevstate;
+qboolean       wasInMenus;
+
+#ifndef        _WIN32
+char *quitMessage [] = 
+{
+/* .........1.........2.... */
+  "  Are you gonna quit    ",
+  "  this game just like   ",
+  "   everything else?     ",
+  "                        ",
+  " Milord, methinks that  ",
+  "   thou art a lowly     ",
+  " quitter. Is this true? ",
+  "                        ",
+
+  " Do I need to bust your ",
+  "  face open for trying  ",
+  "        to quit?        ",
+  "                        ",
+
+  " Man, I oughta smack you",
+  "   for trying to quit!  ",
+  "     Press Y to get     ",
+  "      smacked out.      ",
+  " Press Y to quit like a ",
+  "   big loser in life.   ",
+  "  Press N to stay proud ",
+  "    and successful!     ",
+  "   If you press Y to    ",
+  "  quit, I will summon   ",
+  "  Satan all over your   ",
+  "      hard drive!       ",
+  "  Um, Asmodeus dislikes ",
+  " his children trying to ",
+  " quit. Press Y to return",
+  "   to your Tinkertoys.  ",
+  "  If you quit now, I'll ",
+  "  throw a blanket-party ",
+  "   for you next time!   ",
+  "                        "
+};
+#endif
+
+void M_Menu_Quit_f (void)
+{
+       if (m_state == m_quit)
+               return;
+       wasInMenus = (key_dest == key_menu);
+       key_dest = key_menu;
+       m_quit_prevstate = m_state;
+       m_state = m_quit;
+       m_entersound = true;
+       msgNumber = rand()&7;
+}
+
+
+void M_Quit_Key (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+       case 'n':
+       case 'N':
+               if (wasInMenus)
+               {
+                       m_state = m_quit_prevstate;
+                       m_entersound = true;
+               }
+               else
+               {
+                       key_dest = key_game;
+                       m_state = m_none;
+               }
+               break;
+
+       case 'Y':
+       case 'y':
+               key_dest = key_console;
+               Host_Quit_f ();
+               break;
+
+       default:
+               break;
+       }
+
+}
+
+
+void M_Quit_Draw (void)
+{
+       if (wasInMenus)
+       {
+               m_state = m_quit_prevstate;
+               m_recursiveDraw = true;
+               M_Draw ();
+               m_state = m_quit;
+       }
+
+#ifdef _WIN32
+       M_DrawTextBox (0, 0, 38, 23);
+       M_PrintWhite (16, 12,  "  Quake version 1.09 by id Software\n\n");
+       M_PrintWhite (16, 28,  "Programming        Art \n");
+       M_Print (16, 36,  " John Carmack       Adrian Carmack\n");
+       M_Print (16, 44,  " Michael Abrash     Kevin Cloud\n");
+       M_Print (16, 52,  " John Cash          Paul Steed\n");
+       M_Print (16, 60,  " Dave 'Zoid' Kirsch\n");
+       M_PrintWhite (16, 68,  "Design             Biz\n");
+       M_Print (16, 76,  " John Romero        Jay Wilbur\n");
+       M_Print (16, 84,  " Sandy Petersen     Mike Wilson\n");
+       M_Print (16, 92,  " American McGee     Donna Jackson\n");
+       M_Print (16, 100,  " Tim Willits        Todd Hollenshead\n");
+       M_PrintWhite (16, 108, "Support            Projects\n");
+       M_Print (16, 116, " Barrett Alexander  Shawn Green\n");
+       M_PrintWhite (16, 124, "Sound Effects\n");
+       M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n");
+       M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n");
+       M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n");
+       M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n");
+       M_PrintWhite (16, 164, "registered trademark licensed to\n");
+       M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n");
+       M_PrintWhite (16, 180, "reserved. Press y to exit\n");
+#else
+       M_DrawTextBox (56, 76, 24, 4);
+       M_Print (64, 84,  quitMessage[msgNumber*4+0]);
+       M_Print (64, 92,  quitMessage[msgNumber*4+1]);
+       M_Print (64, 100, quitMessage[msgNumber*4+2]);
+       M_Print (64, 108, quitMessage[msgNumber*4+3]);
+#endif
+}
+
+//=============================================================================
+
+/* SERIAL CONFIG MENU */
+
+int            serialConfig_cursor;
+int            serialConfig_cursor_table[] = {48, 64, 80, 96, 112, 132};
+#define        NUM_SERIALCONFIG_CMDS   6
+
+static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8};
+static int ISA_IRQs[]  = {4,3,4,3};
+int serialConfig_baudrate[] = {9600,14400,19200,28800,38400,57600};
+
+int            serialConfig_comport;
+int            serialConfig_irq ;
+int            serialConfig_baud;
+char   serialConfig_phone[16];
+
+void M_Menu_SerialConfig_f (void)
+{
+       int             n;
+       int             port;
+       int             baudrate;
+       qboolean        useModem;
+
+       key_dest = key_menu;
+       m_state = m_serialconfig;
+       m_entersound = true;
+       if (JoiningGame && SerialConfig)
+               serialConfig_cursor = 4;
+       else
+               serialConfig_cursor = 5;
+
+       (*GetComPortConfig) (0, &port, &serialConfig_irq, &baudrate, &useModem);
+
+       // map uart's port to COMx
+       for (n = 0; n < 4; n++)
+               if (ISA_uarts[n] == port)
+                       break;
+       if (n == 4)
+       {
+               n = 0;
+               serialConfig_irq = 4;
+       }
+       serialConfig_comport = n + 1;
+
+       // map baudrate to index
+       for (n = 0; n < 6; n++)
+               if (serialConfig_baudrate[n] == baudrate)
+                       break;
+       if (n == 6)
+               n = 5;
+       serialConfig_baud = n;
+
+       m_return_onerror = false;
+       m_return_reason[0] = 0;
+}
+
+
+void M_SerialConfig_Draw (void)
+{
+       qpic_t  *p;
+       int             basex;
+       char    *startJoin;
+       char    *directModem;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       basex = (320-p->width)/2;
+       M_DrawPic (basex, 4, p);
+
+       if (StartingGame)
+               startJoin = "New Game";
+       else
+               startJoin = "Join Game";
+       if (SerialConfig)
+               directModem = "Modem";
+       else
+               directModem = "Direct Connect";
+       M_Print (basex, 32, va ("%s - %s", startJoin, directModem));
+       basex += 8;
+
+       M_Print (basex, serialConfig_cursor_table[0], "Port");
+       M_DrawTextBox (160, 40, 4, 1);
+       M_Print (168, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport));
+
+       M_Print (basex, serialConfig_cursor_table[1], "IRQ");
+       M_DrawTextBox (160, serialConfig_cursor_table[1]-8, 1, 1);
+       M_Print (168, serialConfig_cursor_table[1], va("%u", serialConfig_irq));
+
+       M_Print (basex, serialConfig_cursor_table[2], "Baud");
+       M_DrawTextBox (160, serialConfig_cursor_table[2]-8, 5, 1);
+       M_Print (168, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud]));
+
+       if (SerialConfig)
+       {
+               M_Print (basex, serialConfig_cursor_table[3], "Modem Setup...");
+               if (JoiningGame)
+               {
+                       M_Print (basex, serialConfig_cursor_table[4], "Phone number");
+                       M_DrawTextBox (160, serialConfig_cursor_table[4]-8, 16, 1);
+                       M_Print (168, serialConfig_cursor_table[4], serialConfig_phone);
+               }
+       }
+
+       if (JoiningGame)
+       {
+               M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 7, 1);
+               M_Print (basex+8, serialConfig_cursor_table[5], "Connect");
+       }
+       else
+       {
+               M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 2, 1);
+               M_Print (basex+8, serialConfig_cursor_table[5], "OK");
+       }
+
+       M_DrawCharacter (basex-8, serialConfig_cursor_table [serialConfig_cursor], 12+((int)(realtime*4)&1));
+
+       if (serialConfig_cursor == 4)
+               M_DrawCharacter (168 + 8*strlen(serialConfig_phone), serialConfig_cursor_table [serialConfig_cursor], 10+((int)(realtime*4)&1));
+
+       if (*m_return_reason)
+               M_PrintWhite (basex, 148, m_return_reason);
+}
+
+
+void M_SerialConfig_Key (int key)
+{
+       int             l;
+
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_Net_f ();
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               serialConfig_cursor--;
+               if (serialConfig_cursor < 0)
+                       serialConfig_cursor = NUM_SERIALCONFIG_CMDS-1;
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               serialConfig_cursor++;
+               if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS)
+                       serialConfig_cursor = 0;
+               break;
+
+       case K_LEFTARROW:
+               if (serialConfig_cursor > 2)
+                       break;
+               S_LocalSound ("misc/menu3.wav");
+
+               if (serialConfig_cursor == 0)
+               {
+                       serialConfig_comport--;
+                       if (serialConfig_comport == 0)
+                               serialConfig_comport = 4;
+                       serialConfig_irq = ISA_IRQs[serialConfig_comport-1];
+               }
+
+               if (serialConfig_cursor == 1)
+               {
+                       serialConfig_irq--;
+                       if (serialConfig_irq == 6)
+                               serialConfig_irq = 5;
+                       if (serialConfig_irq == 1)
+                               serialConfig_irq = 7;
+               }
+
+               if (serialConfig_cursor == 2)
+               {
+                       serialConfig_baud--;
+                       if (serialConfig_baud < 0)
+                               serialConfig_baud = 5;
+               }
+
+               break;
+
+       case K_RIGHTARROW:
+               if (serialConfig_cursor > 2)
+                       break;
+forward:
+               S_LocalSound ("misc/menu3.wav");
+
+               if (serialConfig_cursor == 0)
+               {
+                       serialConfig_comport++;
+                       if (serialConfig_comport > 4)
+                               serialConfig_comport = 1;
+                       serialConfig_irq = ISA_IRQs[serialConfig_comport-1];
+               }
+
+               if (serialConfig_cursor == 1)
+               {
+                       serialConfig_irq++;
+                       if (serialConfig_irq == 6)
+                               serialConfig_irq = 7;
+                       if (serialConfig_irq == 8)
+                               serialConfig_irq = 2;
+               }
+
+               if (serialConfig_cursor == 2)
+               {
+                       serialConfig_baud++;
+                       if (serialConfig_baud > 5)
+                               serialConfig_baud = 0;
+               }
+
+               break;
+
+       case K_ENTER:
+               if (serialConfig_cursor < 3)
+                       goto forward;
+
+               m_entersound = true;
+
+               if (serialConfig_cursor == 3)
+               {
+                       (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
+
+                       M_Menu_ModemConfig_f ();
+                       break;
+               }
+
+               if (serialConfig_cursor == 4)
+               {
+                       serialConfig_cursor = 5;
+                       break;
+               }
+
+               // serialConfig_cursor == 5 (OK/CONNECT)
+               (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
+
+               M_ConfigureNetSubsystem ();
+
+               if (StartingGame)
+               {
+                       M_Menu_GameOptions_f ();
+                       break;
+               }
+
+               m_return_state = m_state;
+               m_return_onerror = true;
+               key_dest = key_game;
+               m_state = m_none;
+
+               if (SerialConfig)
+                       Cbuf_AddText (va ("connect \"%s\"\n", serialConfig_phone));
+               else
+                       Cbuf_AddText ("connect\n");
+               break;
+
+       case K_BACKSPACE:
+               if (serialConfig_cursor == 4)
+               {
+                       if (strlen(serialConfig_phone))
+                               serialConfig_phone[strlen(serialConfig_phone)-1] = 0;
+               }
+               break;
+
+       default:
+               if (key < 32 || key > 127)
+                       break;
+               if (serialConfig_cursor == 4)
+               {
+                       l = strlen(serialConfig_phone);
+                       if (l < 15)
+                       {
+                               serialConfig_phone[l+1] = 0;
+                               serialConfig_phone[l] = key;
+                       }
+               }
+       }
+
+       if (DirectConfig && (serialConfig_cursor == 3 || serialConfig_cursor == 4))
+               if (key == K_UPARROW)
+                       serialConfig_cursor = 2;
+               else
+                       serialConfig_cursor = 5;
+
+       if (SerialConfig && StartingGame && serialConfig_cursor == 4)
+               if (key == K_UPARROW)
+                       serialConfig_cursor = 3;
+               else
+                       serialConfig_cursor = 5;
+}
+
+//=============================================================================
+/* MODEM CONFIG MENU */
+
+int            modemConfig_cursor;
+int            modemConfig_cursor_table [] = {40, 56, 88, 120, 156};
+#define NUM_MODEMCONFIG_CMDS   5
+
+char   modemConfig_dialing;
+char   modemConfig_clear [16];
+char   modemConfig_init [32];
+char   modemConfig_hangup [16];
+
+void M_Menu_ModemConfig_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_modemconfig;
+       m_entersound = true;
+       (*GetModemConfig) (0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup);
+}
+
+
+void M_ModemConfig_Draw (void)
+{
+       qpic_t  *p;
+       int             basex;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       basex = (320-p->width)/2;
+       M_DrawPic (basex, 4, p);
+       basex += 8;
+
+       if (modemConfig_dialing == 'P')
+               M_Print (basex, modemConfig_cursor_table[0], "Pulse Dialing");
+       else
+               M_Print (basex, modemConfig_cursor_table[0], "Touch Tone Dialing");
+
+       M_Print (basex, modemConfig_cursor_table[1], "Clear");
+       M_DrawTextBox (basex, modemConfig_cursor_table[1]+4, 16, 1);
+       M_Print (basex+8, modemConfig_cursor_table[1]+12, modemConfig_clear);
+       if (modemConfig_cursor == 1)
+               M_DrawCharacter (basex+8 + 8*strlen(modemConfig_clear), modemConfig_cursor_table[1]+12, 10+((int)(realtime*4)&1));
+
+       M_Print (basex, modemConfig_cursor_table[2], "Init");
+       M_DrawTextBox (basex, modemConfig_cursor_table[2]+4, 30, 1);
+       M_Print (basex+8, modemConfig_cursor_table[2]+12, modemConfig_init);
+       if (modemConfig_cursor == 2)
+               M_DrawCharacter (basex+8 + 8*strlen(modemConfig_init), modemConfig_cursor_table[2]+12, 10+((int)(realtime*4)&1));
+
+       M_Print (basex, modemConfig_cursor_table[3], "Hangup");
+       M_DrawTextBox (basex, modemConfig_cursor_table[3]+4, 16, 1);
+       M_Print (basex+8, modemConfig_cursor_table[3]+12, modemConfig_hangup);
+       if (modemConfig_cursor == 3)
+               M_DrawCharacter (basex+8 + 8*strlen(modemConfig_hangup), modemConfig_cursor_table[3]+12, 10+((int)(realtime*4)&1));
+
+       M_DrawTextBox (basex, modemConfig_cursor_table[4]-8, 2, 1);
+       M_Print (basex+8, modemConfig_cursor_table[4], "OK");
+
+       M_DrawCharacter (basex-8, modemConfig_cursor_table [modemConfig_cursor], 12+((int)(realtime*4)&1));
+}
+
+
+void M_ModemConfig_Key (int key)
+{
+       int             l;
+
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_SerialConfig_f ();
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               modemConfig_cursor--;
+               if (modemConfig_cursor < 0)
+                       modemConfig_cursor = NUM_MODEMCONFIG_CMDS-1;
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               modemConfig_cursor++;
+               if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS)
+                       modemConfig_cursor = 0;
+               break;
+
+       case K_LEFTARROW:
+       case K_RIGHTARROW:
+               if (modemConfig_cursor == 0)
+               {
+                       if (modemConfig_dialing == 'P')
+                               modemConfig_dialing = 'T';
+                       else
+                               modemConfig_dialing = 'P';
+                       S_LocalSound ("misc/menu1.wav");
+               }
+               break;
+
+       case K_ENTER:
+               if (modemConfig_cursor == 0)
+               {
+                       if (modemConfig_dialing == 'P')
+                               modemConfig_dialing = 'T';
+                       else
+                               modemConfig_dialing = 'P';
+                       m_entersound = true;
+               }
+
+               if (modemConfig_cursor == 4)
+               {
+                       (*SetModemConfig) (0, va ("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup);
+                       m_entersound = true;
+                       M_Menu_SerialConfig_f ();
+               }
+               break;
+
+       case K_BACKSPACE:
+               if (modemConfig_cursor == 1)
+               {
+                       if (strlen(modemConfig_clear))
+                               modemConfig_clear[strlen(modemConfig_clear)-1] = 0;
+               }
+
+               if (modemConfig_cursor == 2)
+               {
+                       if (strlen(modemConfig_init))
+                               modemConfig_init[strlen(modemConfig_init)-1] = 0;
+               }
+
+               if (modemConfig_cursor == 3)
+               {
+                       if (strlen(modemConfig_hangup))
+                               modemConfig_hangup[strlen(modemConfig_hangup)-1] = 0;
+               }
+               break;
+
+       default:
+               if (key < 32 || key > 127)
+                       break;
+
+               if (modemConfig_cursor == 1)
+               {
+                       l = strlen(modemConfig_clear);
+                       if (l < 15)
+                       {
+                               modemConfig_clear[l+1] = 0;
+                               modemConfig_clear[l] = key;
+                       }
+               }
+
+               if (modemConfig_cursor == 2)
+               {
+                       l = strlen(modemConfig_init);
+                       if (l < 29)
+                       {
+                               modemConfig_init[l+1] = 0;
+                               modemConfig_init[l] = key;
+                       }
+               }
+
+               if (modemConfig_cursor == 3)
+               {
+                       l = strlen(modemConfig_hangup);
+                       if (l < 15)
+                       {
+                               modemConfig_hangup[l+1] = 0;
+                               modemConfig_hangup[l] = key;
+                       }
+               }
+       }
+}
+
+//=============================================================================
+/* LAN CONFIG MENU */
+
+int            lanConfig_cursor = -1;
+int            lanConfig_cursor_table [] = {72, 92, 124};
+#define NUM_LANCONFIG_CMDS     3
+
+int    lanConfig_port;
+char   lanConfig_portname[6];
+char   lanConfig_joinname[22];
+
+void M_Menu_LanConfig_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_lanconfig;
+       m_entersound = true;
+       if (lanConfig_cursor == -1)
+       {
+               if (JoiningGame && TCPIPConfig)
+                       lanConfig_cursor = 2;
+               else
+                       lanConfig_cursor = 1;
+       }
+       if (StartingGame && lanConfig_cursor == 2)
+               lanConfig_cursor = 1;
+       lanConfig_port = DEFAULTnet_hostport;
+       sprintf(lanConfig_portname, "%u", lanConfig_port);
+
+       m_return_onerror = false;
+       m_return_reason[0] = 0;
+}
+
+
+void M_LanConfig_Draw (void)
+{
+       qpic_t  *p;
+       int             basex;
+       char    *startJoin;
+       char    *protocol;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       basex = (320-p->width)/2;
+       M_DrawPic (basex, 4, p);
+
+       if (StartingGame)
+               startJoin = "New Game";
+       else
+               startJoin = "Join Game";
+       if (IPXConfig)
+               protocol = "IPX";
+       else
+               protocol = "TCP/IP";
+       M_Print (basex, 32, va ("%s - %s", startJoin, protocol));
+       basex += 8;
+
+       M_Print (basex, 52, "Address:");
+       if (IPXConfig)
+               M_Print (basex+9*8, 52, my_ipx_address);
+       else
+               M_Print (basex+9*8, 52, my_tcpip_address);
+
+       M_Print (basex, lanConfig_cursor_table[0], "Port");
+       M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
+       M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
+
+       if (JoiningGame)
+       {
+               M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
+               M_Print (basex, 108, "Join game at:");
+               M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
+               M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
+       }
+       else
+       {
+               M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
+               M_Print (basex+8, lanConfig_cursor_table[1], "OK");
+       }
+
+       M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
+
+       if (lanConfig_cursor == 0)
+               M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
+
+       if (lanConfig_cursor == 2)
+               M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
+
+       if (*m_return_reason)
+               M_PrintWhite (basex, 148, m_return_reason);
+}
+
+
+void M_LanConfig_Key (int key)
+{
+       int             l;
+
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_Net_f ();
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               lanConfig_cursor--;
+               if (lanConfig_cursor < 0)
+                       lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               lanConfig_cursor++;
+               if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
+                       lanConfig_cursor = 0;
+               break;
+
+       case K_ENTER:
+               if (lanConfig_cursor == 0)
+                       break;
+
+               m_entersound = true;
+
+               M_ConfigureNetSubsystem ();
+
+               if (lanConfig_cursor == 1)
+               {
+                       if (StartingGame)
+                       {
+                               M_Menu_GameOptions_f ();
+                               break;
+                       }
+                       M_Menu_Search_f();
+                       break;
+               }
+
+               if (lanConfig_cursor == 2)
+               {
+                       m_return_state = m_state;
+                       m_return_onerror = true;
+                       key_dest = key_game;
+                       m_state = m_none;
+                       Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
+                       break;
+               }
+
+               break;
+
+       case K_BACKSPACE:
+               if (lanConfig_cursor == 0)
+               {
+                       if (strlen(lanConfig_portname))
+                               lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
+               }
+
+               if (lanConfig_cursor == 2)
+               {
+                       if (strlen(lanConfig_joinname))
+                               lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
+               }
+               break;
+
+       default:
+               if (key < 32 || key > 127)
+                       break;
+
+               if (lanConfig_cursor == 2)
+               {
+                       l = strlen(lanConfig_joinname);
+                       if (l < 21)
+                       {
+                               lanConfig_joinname[l+1] = 0;
+                               lanConfig_joinname[l] = key;
+                       }
+               }
+
+               if (key < '0' || key > '9')
+                       break;
+               if (lanConfig_cursor == 0)
+               {
+                       l = strlen(lanConfig_portname);
+                       if (l < 5)
+                       {
+                               lanConfig_portname[l+1] = 0;
+                               lanConfig_portname[l] = key;
+                       }
+               }
+       }
+
+       if (StartingGame && lanConfig_cursor == 2)
+               if (key == K_UPARROW)
+                       lanConfig_cursor = 1;
+               else
+                       lanConfig_cursor = 0;
+
+       l =  atoi(lanConfig_portname);
+       if (l > 65535)
+               l = lanConfig_port;
+       else
+               lanConfig_port = l;
+       sprintf(lanConfig_portname, "%u", lanConfig_port);
+}
+
+//=============================================================================
+/* GAME OPTIONS MENU */
+
+typedef struct
+{
+       char    *name;
+       char    *description;
+} level_t;
+
+level_t                levels[] =
+{
+       {"start", "Entrance"},  // 0
+
+       {"e1m1", "Slipgate Complex"},                           // 1
+       {"e1m2", "Castle of the Damned"},
+       {"e1m3", "The Necropolis"},
+       {"e1m4", "The Grisly Grotto"},
+       {"e1m5", "Gloom Keep"},
+       {"e1m6", "The Door To Chthon"},
+       {"e1m7", "The House of Chthon"},
+       {"e1m8", "Ziggurat Vertigo"},
+
+       {"e2m1", "The Installation"},                           // 9
+       {"e2m2", "Ogre Citadel"},
+       {"e2m3", "Crypt of Decay"},
+       {"e2m4", "The Ebon Fortress"},
+       {"e2m5", "The Wizard's Manse"},
+       {"e2m6", "The Dismal Oubliette"},
+       {"e2m7", "Underearth"},
+
+       {"e3m1", "Termination Central"},                        // 16
+       {"e3m2", "The Vaults of Zin"},
+       {"e3m3", "The Tomb of Terror"},
+       {"e3m4", "Satan's Dark Delight"},
+       {"e3m5", "Wind Tunnels"},
+       {"e3m6", "Chambers of Torment"},
+       {"e3m7", "The Haunted Halls"},
+
+       {"e4m1", "The Sewage System"},                          // 23
+       {"e4m2", "The Tower of Despair"},
+       {"e4m3", "The Elder God Shrine"},
+       {"e4m4", "The Palace of Hate"},
+       {"e4m5", "Hell's Atrium"},
+       {"e4m6", "The Pain Maze"},
+       {"e4m7", "Azure Agony"},
+       {"e4m8", "The Nameless City"},
+
+       {"end", "Shub-Niggurath's Pit"},                        // 31
+
+       {"dm1", "Place of Two Deaths"},                         // 32
+       {"dm2", "Claustrophobopolis"},
+       {"dm3", "The Abandoned Base"},
+       {"dm4", "The Bad Place"},
+       {"dm5", "The Cistern"},
+       {"dm6", "The Dark Zone"}
+};
+
+//MED 01/06/97 added hipnotic levels
+level_t     hipnoticlevels[] =
+{
+   {"start", "Command HQ"},  // 0
+
+   {"hip1m1", "The Pumping Station"},          // 1
+   {"hip1m2", "Storage Facility"},
+   {"hip1m3", "The Lost Mine"},
+   {"hip1m4", "Research Facility"},
+   {"hip1m5", "Military Complex"},
+
+   {"hip2m1", "Ancient Realms"},          // 6
+   {"hip2m2", "The Black Cathedral"},
+   {"hip2m3", "The Catacombs"},
+   {"hip2m4", "The Crypt"},
+   {"hip2m5", "Mortum's Keep"},
+   {"hip2m6", "The Gremlin's Domain"},
+
+   {"hip3m1", "Tur Torment"},       // 12
+   {"hip3m2", "Pandemonium"},
+   {"hip3m3", "Limbo"},
+   {"hip3m4", "The Gauntlet"},
+
+   {"hipend", "Armagon's Lair"},       // 16
+
+   {"hipdm1", "The Edge of Oblivion"}           // 17
+};
+
+//PGM 01/07/97 added rogue levels
+//PGM 03/02/97 added dmatch level
+level_t                roguelevels[] =
+{
+       {"start",       "Split Decision"},
+       {"r1m1",        "Deviant's Domain"},
+       {"r1m2",        "Dread Portal"},
+       {"r1m3",        "Judgement Call"},
+       {"r1m4",        "Cave of Death"},
+       {"r1m5",        "Towers of Wrath"},
+       {"r1m6",        "Temple of Pain"},
+       {"r1m7",        "Tomb of the Overlord"},
+       {"r2m1",        "Tempus Fugit"},
+       {"r2m2",        "Elemental Fury I"},
+       {"r2m3",        "Elemental Fury II"},
+       {"r2m4",        "Curse of Osiris"},
+       {"r2m5",        "Wizard's Keep"},
+       {"r2m6",        "Blood Sacrifice"},
+       {"r2m7",        "Last Bastion"},
+       {"r2m8",        "Source of Evil"},
+       {"ctf1",    "Division of Change"}
+};
+
+typedef struct
+{
+       char    *description;
+       int             firstLevel;
+       int             levels;
+} episode_t;
+
+episode_t      episodes[] =
+{
+       {"Welcome to Quake", 0, 1},
+       {"Doomed Dimension", 1, 8},
+       {"Realm of Black Magic", 9, 7},
+       {"Netherworld", 16, 7},
+       {"The Elder World", 23, 8},
+       {"Final Level", 31, 1},
+       {"Deathmatch Arena", 32, 6}
+};
+
+//MED 01/06/97  added hipnotic episodes
+episode_t   hipnoticepisodes[] =
+{
+   {"Scourge of Armagon", 0, 1},
+   {"Fortress of the Dead", 1, 5},
+   {"Dominion of Darkness", 6, 6},
+   {"The Rift", 12, 4},
+   {"Final Level", 16, 1},
+   {"Deathmatch Arena", 17, 1}
+};
+
+//PGM 01/07/97 added rogue episodes
+//PGM 03/02/97 added dmatch episode
+episode_t      rogueepisodes[] =
+{
+       {"Introduction", 0, 1},
+       {"Hell's Fortress", 1, 7},
+       {"Corridors of Time", 8, 8},
+       {"Deathmatch Arena", 16, 1}
+};
+
+level_t                nehahralevels[] =
+{
+       {"nehstart",    "Welcome to Nehahra"},
+       {"neh1m1",      "Forge City1: Slipgates"},
+       {"neh1m2",      "Forge City2: Boiler"},
+       {"neh1m3",      "Forge City3: Escape"},
+       {"neh1m4",      "Grind Core"},
+       {"neh1m5",      "Industrial Silence"},
+       {"neh1m6",      "Locked-Up Anger"},
+       {"neh1m7",      "Wanderer of the Wastes"},
+       {"neh1m8",      "Artemis System Net"},
+       {"neh1m9",      "To the Death"},
+       {"neh2m1",      "The Gates of Ghoro"},
+       {"neh2m2",      "Sacred Trinity"},
+       {"neh2m3",      "Realm of the Ancients"},
+       {"neh2m4",      "Temple of the Ancients"},
+       {"neh2m5",      "Dreams Made Flesh"},
+       {"neh2m6",      "Your Last Cup of Sorrow"},
+       {"nehsec",      "Ogre's Bane"},
+       {"nehahra",     "Nehahra's Den"},
+       {"nehend",      "Quintessence"}
+};
+
+episode_t      nehahraepisodes[] =
+{
+       {"Welcome to Nehahra", 0, 1},
+       {"The Fall of Forge", 1, 9},
+       {"The Outlands", 10, 7},
+       {"Dimension of the Lost", 17, 2}
+};
+
+int    startepisode;
+int    startlevel;
+int maxplayers;
+qboolean m_serverInfoMessage = false;
+double m_serverInfoMessageTime;
+
+void M_Menu_GameOptions_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_gameoptions;
+       m_entersound = true;
+       if (maxplayers == 0)
+               maxplayers = svs.maxclients;
+       if (maxplayers < 2)
+               maxplayers = svs.maxclientslimit;
+}
+
+
+int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
+#define        NUM_GAMEOPTIONS 9
+int            gameoptions_cursor;
+
+void M_GameOptions_Draw (void)
+{
+       qpic_t  *p;
+       int             x;
+
+       M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       M_DrawTextBox (152, 32, 10, 1);
+       M_Print (160, 40, "begin game");
+
+       M_Print (0, 56, "      Max players");
+       M_Print (160, 56, va("%i", maxplayers) );
+
+       M_Print (0, 64, "        Game Type");
+       if (!coop.value && !deathmatch.value)
+               Cvar_SetValue("deathmatch", 1);
+       if (coop.value)
+               M_Print (160, 64, "Cooperative");
+       else
+               M_Print (160, 64, "Deathmatch");
+
+       M_Print (0, 72, "        Teamplay");
+       if (rogue)
+       {
+               char *msg;
+
+               switch((int)teamplay.value)
+               {
+                       case 1: msg = "No Friendly Fire"; break;
+                       case 2: msg = "Friendly Fire"; break;
+                       case 3: msg = "Tag"; break;
+                       case 4: msg = "Capture the Flag"; break;
+                       case 5: msg = "One Flag CTF"; break;
+                       case 6: msg = "Three Team CTF"; break;
+                       default: msg = "Off"; break;
+               }
+               M_Print (160, 72, msg);
+       }
+       else
+       {
+               char *msg;
+
+               switch((int)teamplay.value)
+               {
+                       case 1: msg = "No Friendly Fire"; break;
+                       case 2: msg = "Friendly Fire"; break;
+                       default: msg = "Off"; break;
+               }
+               M_Print (160, 72, msg);
+       }
+
+       M_Print (0, 80, "            Skill");
+       if (skill.value == 0)
+               M_Print (160, 80, "Easy difficulty");
+       else if (skill.value == 1)
+               M_Print (160, 80, "Normal difficulty");
+       else if (skill.value == 2)
+               M_Print (160, 80, "Hard difficulty");
+       else
+               M_Print (160, 80, "Nightmare difficulty");
+
+       M_Print (0, 88, "       Frag Limit");
+       if (fraglimit.value == 0)
+               M_Print (160, 88, "none");
+       else
+               M_Print (160, 88, va("%i frags", (int)fraglimit.value));
+
+       M_Print (0, 96, "       Time Limit");
+       if (timelimit.value == 0)
+               M_Print (160, 96, "none");
+       else
+               M_Print (160, 96, va("%i minutes", (int)timelimit.value));
+
+       M_Print (0, 112, "         Episode");
+   //MED 01/06/97 added hipnotic episodes
+   if (hipnotic)
+      M_Print (160, 112, hipnoticepisodes[startepisode].description);
+   //PGM 01/07/97 added rogue episodes
+   else if (rogue)
+      M_Print (160, 112, rogueepisodes[startepisode].description);
+   else if (nehahra)
+      M_Print (160, 112, nehahraepisodes[startepisode].description);
+   else
+      M_Print (160, 112, episodes[startepisode].description);
+
+       M_Print (0, 120, "           Level");
+   //MED 01/06/97 added hipnotic episodes
+   if (hipnotic)
+   {
+      M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
+   }
+   //PGM 01/07/97 added rogue episodes
+   else if (rogue)
+   {
+      M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
+   }
+   else if (nehahra)
+   {
+      M_Print (160, 120, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name);
+   }
+   else
+   {
+      M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
+   }
+
+// line cursor
+       M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
+
+       if (m_serverInfoMessage)
+       {
+               if ((realtime - m_serverInfoMessageTime) < 5.0)
+               {
+                       x = (320-26*8)/2;
+                       M_DrawTextBox (x, 138, 24, 4);
+                       x += 8;
+                       M_Print (x, 146, " More than 64 players?? ");
+                       M_Print (x, 154, "  First, question your  ");
+                       M_Print (x, 162, "   sanity, then email   ");
+                       M_Print (x, 170, " havoc@gamevisions.com  ");
+                       /*
+                       M_Print (x, 146, "  More than 4 players   ");
+                       M_Print (x, 154, " requires using command ");
+                       M_Print (x, 162, "line parameters; please ");
+                       M_Print (x, 170, "   see techinfo.txt.    ");
+                       */
+               }
+               else
+               {
+                       m_serverInfoMessage = false;
+               }
+       }
+}
+
+
+void M_NetStart_Change (int dir)
+{
+       int count;
+
+       switch (gameoptions_cursor)
+       {
+       case 1:
+               maxplayers += dir;
+               if (maxplayers > svs.maxclientslimit)
+               {
+                       maxplayers = svs.maxclientslimit;
+                       m_serverInfoMessage = true;
+                       m_serverInfoMessageTime = realtime;
+               }
+               if (maxplayers < 2)
+                       maxplayers = 2;
+               break;
+
+       case 2:
+               if (deathmatch.value) // changing from deathmatch to coop
+               {
+                       Cvar_SetValue ("coop", 1);
+                       Cvar_SetValue ("deathmatch", 0);
+               }
+               else // changing from coop to deathmatch
+               {
+                       Cvar_SetValue ("coop", 0);
+                       Cvar_SetValue ("deathmatch", 1);
+               }
+               break;
+
+       case 3:
+               if (rogue)
+                       count = 6;
+               else
+                       count = 2;
+
+               Cvar_SetValue ("teamplay", teamplay.value + dir);
+               if (teamplay.value > count)
+                       Cvar_SetValue ("teamplay", 0);
+               else if (teamplay.value < 0)
+                       Cvar_SetValue ("teamplay", count);
+               break;
+
+       case 4:
+               Cvar_SetValue ("skill", skill.value + dir);
+               if (skill.value > 3)
+                       Cvar_SetValue ("skill", 0);
+               if (skill.value < 0)
+                       Cvar_SetValue ("skill", 3);
+               break;
+
+       case 5:
+               Cvar_SetValue ("fraglimit", fraglimit.value + dir*10);
+               if (fraglimit.value > 100)
+                       Cvar_SetValue ("fraglimit", 0);
+               if (fraglimit.value < 0)
+                       Cvar_SetValue ("fraglimit", 100);
+               break;
+
+       case 6:
+               Cvar_SetValue ("timelimit", timelimit.value + dir*5);
+               if (timelimit.value > 60)
+                       Cvar_SetValue ("timelimit", 0);
+               if (timelimit.value < 0)
+                       Cvar_SetValue ("timelimit", 60);
+               break;
+
+       case 7:
+               startepisode += dir;
+       //MED 01/06/97 added hipnotic count
+               if (hipnotic)
+                       count = 6;
+       //PGM 01/07/97 added rogue count
+       //PGM 03/02/97 added 1 for dmatch episode
+               else if (rogue)
+                       count = 4;
+               else if (nehahra)
+                       count = 4;
+               else if (registered.value)
+                       count = 7;
+               else
+                       count = 2;
+
+               if (startepisode < 0)
+                       startepisode = count - 1;
+
+               if (startepisode >= count)
+                       startepisode = 0;
+
+               startlevel = 0;
+               break;
+
+       case 8:
+               startlevel += dir;
+    //MED 01/06/97 added hipnotic episodes
+               if (hipnotic)
+                       count = hipnoticepisodes[startepisode].levels;
+       //PGM 01/06/97 added hipnotic episodes
+               else if (rogue)
+                       count = rogueepisodes[startepisode].levels;
+               else if (nehahra)
+                       count = nehahraepisodes[startepisode].levels;
+               else
+                       count = episodes[startepisode].levels;
+
+               if (startlevel < 0)
+                       startlevel = count - 1;
+
+               if (startlevel >= count)
+                       startlevel = 0;
+               break;
+       }
+}
+
+void M_GameOptions_Key (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+               M_Menu_Net_f ();
+               break;
+
+       case K_UPARROW:
+               S_LocalSound ("misc/menu1.wav");
+               gameoptions_cursor--;
+               if (gameoptions_cursor < 0)
+                       gameoptions_cursor = NUM_GAMEOPTIONS-1;
+               break;
+
+       case K_DOWNARROW:
+               S_LocalSound ("misc/menu1.wav");
+               gameoptions_cursor++;
+               if (gameoptions_cursor >= NUM_GAMEOPTIONS)
+                       gameoptions_cursor = 0;
+               break;
+
+       case K_LEFTARROW:
+               if (gameoptions_cursor == 0)
+                       break;
+               S_LocalSound ("misc/menu3.wav");
+               M_NetStart_Change (-1);
+               break;
+
+       case K_RIGHTARROW:
+               if (gameoptions_cursor == 0)
+                       break;
+               S_LocalSound ("misc/menu3.wav");
+               M_NetStart_Change (1);
+               break;
+
+       case K_ENTER:
+               S_LocalSound ("misc/menu2.wav");
+               if (gameoptions_cursor == 0)
+               {
+                       if (sv.active)
+                               Cbuf_AddText ("disconnect\n");
+                       Cbuf_AddText ("listen 0\n");    // so host_netport will be re-examined
+                       Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
+                       SCR_BeginLoadingPlaque ();
+
+                       if (hipnotic)
+                               Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) );
+                       else if (rogue)
+                               Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) );
+                       else if (nehahra)
+                               Cbuf_AddText ( va ("map %s\n", nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name) );
+                       else
+                               Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) );
+
+                       return;
+               }
+
+               M_NetStart_Change (1);
+               break;
+       }
+}
+
+//=============================================================================
+/* SEARCH MENU */
+
+qboolean       searchComplete = false;
+double         searchCompleteTime;
+
+void M_Menu_Search_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_search;
+       m_entersound = false;
+       slistSilent = true;
+       slistLocal = false;
+       searchComplete = false;
+       NET_Slist_f();
+
+}
+
+
+void M_Search_Draw (void)
+{
+       qpic_t  *p;
+       int x;
+
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+       x = (320/2) - ((12*8)/2) + 4;
+       M_DrawTextBox (x-8, 32, 12, 1);
+       M_Print (x, 40, "Searching...");
+
+       if(slistInProgress)
+       {
+               NET_Poll();
+               return;
+       }
+
+       if (! searchComplete)
+       {
+               searchComplete = true;
+               searchCompleteTime = realtime;
+       }
+
+       if (hostCacheCount)
+       {
+               M_Menu_ServerList_f ();
+               return;
+       }
+
+       M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
+       if ((realtime - searchCompleteTime) < 3.0)
+               return;
+
+       M_Menu_LanConfig_f ();
+}
+
+
+void M_Search_Key (int key)
+{
+}
+
+//=============================================================================
+/* SLIST MENU */
+
+int            slist_cursor;
+qboolean slist_sorted;
+
+void M_Menu_ServerList_f (void)
+{
+       key_dest = key_menu;
+       m_state = m_slist;
+       m_entersound = true;
+       slist_cursor = 0;
+       m_return_onerror = false;
+       m_return_reason[0] = 0;
+       slist_sorted = false;
+}
+
+
+void M_ServerList_Draw (void)
+{
+       int             n;
+       char    string [64];
+       qpic_t  *p;
+
+       if (!slist_sorted)
+       {
+               if (hostCacheCount > 1)
+               {
+                       int     i,j;
+                       hostcache_t temp;
+                       for (i = 0; i < hostCacheCount; i++)
+                               for (j = i+1; j < hostCacheCount; j++)
+                                       if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
+                                       {
+                                               memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
+                                               memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
+                                               memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
+                                       }
+               }
+               slist_sorted = true;
+       }
+
+       p = Draw_CachePic ("gfx/p_multi.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+       for (n = 0; n < hostCacheCount; n++)
+       {
+               if (hostcache[n].maxusers)
+                       sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
+               else
+                       sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
+               M_Print (16, 32 + 8*n, string);
+       }
+       M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
+
+       if (*m_return_reason)
+               M_PrintWhite (16, 148, m_return_reason);
+}
+
+
+void M_ServerList_Key (int k)
+{
+       switch (k)
+       {
+       case K_ESCAPE:
+               M_Menu_LanConfig_f ();
+               break;
+
+       case K_SPACE:
+               M_Menu_Search_f ();
+               break;
+
+       case K_UPARROW:
+       case K_LEFTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               slist_cursor--;
+               if (slist_cursor < 0)
+                       slist_cursor = hostCacheCount - 1;
+               break;
+
+       case K_DOWNARROW:
+       case K_RIGHTARROW:
+               S_LocalSound ("misc/menu1.wav");
+               slist_cursor++;
+               if (slist_cursor >= hostCacheCount)
+                       slist_cursor = 0;
+               break;
+
+       case K_ENTER:
+               S_LocalSound ("misc/menu2.wav");
+               m_return_state = m_state;
+               m_return_onerror = true;
+               slist_sorted = false;
+               key_dest = key_game;
+               m_state = m_none;
+               Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
+               break;
+
+       default:
+               break;
+       }
+
+}
+
+//=============================================================================
+/* Menu Subsystem */
+
+
+void M_Init (void)
+{
+       Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
+
+       Cmd_AddCommand ("menu_main", M_Menu_Main_f);
+       Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
+       Cmd_AddCommand ("menu_load", M_Menu_Load_f);
+       Cmd_AddCommand ("menu_save", M_Menu_Save_f);
+       Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
+       Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
+       Cmd_AddCommand ("menu_options", M_Menu_Options_f);
+       Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
+       Cmd_AddCommand ("menu_video", M_Menu_Video_f);
+       Cmd_AddCommand ("help", M_Menu_Help_f);
+       Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
+
+       if (nehahra)
+       {
+               if (COM_FileExists("maps/neh1m4.bsp"))
+               {
+                       if (COM_FileExists("hearing.dem"))
+                       {
+                               Con_Printf("Nehahra movie and game detected.\n");
+                               NehGameType = TYPE_BOTH;
+                       }
+                       else
+                       {
+                               Con_Printf("Nehahra game detected.\n");
+                               NehGameType = TYPE_GAME;
+                       }
+               }
+               else
+               {
+                       if (COM_FileExists("hearing.dem"))
+                       {
+                               Con_Printf("Nehahra movie detected.\n");
+                               NehGameType = TYPE_DEMO;
+                       }
+                       else
+                       {
+                               Con_Printf("Nehahra not found.\n");
+                               NehGameType = TYPE_GAME; // could just complain, but...
+                       }
+               }
+       }
+}
+
+
+void M_Draw (void)
+{
+       if (m_state == m_none || key_dest != key_menu)
+               return;
+
+       if (!m_recursiveDraw)
+       {
+               scr_copyeverything = 1;
+
+               if (scr_con_current)
+               {
+                       Draw_ConsoleBackground (vid.height);
+                       VID_UnlockBuffer ();
+                       S_ExtraUpdate ();
+                       VID_LockBuffer ();
+               }
+//             else
+//                     Draw_FadeScreen ();
+
+               scr_fullupdate = 0;
+       }
+       else
+       {
+               m_recursiveDraw = false;
+       }
+
+       switch (m_state)
+       {
+       case m_none:
+               break;
+
+       case m_main:
+               M_Main_Draw ();
+               break;
+
+       case m_demo:
+               M_Demo_Draw ();
+               break;
+
+       case m_singleplayer:
+               M_SinglePlayer_Draw ();
+               break;
+
+       case m_load:
+               M_Load_Draw ();
+               break;
+
+       case m_save:
+               M_Save_Draw ();
+               break;
+
+       case m_multiplayer:
+               M_MultiPlayer_Draw ();
+               break;
+
+       case m_setup:
+               M_Setup_Draw ();
+               break;
+
+       case m_net:
+               M_Net_Draw ();
+               break;
+
+       case m_options:
+               M_Options_Draw ();
+               break;
+
+       case m_keys:
+               M_Keys_Draw ();
+               break;
+
+       case m_video:
+               M_Video_Draw ();
+               break;
+
+       case m_help:
+               M_Help_Draw ();
+               break;
+
+       case m_quit:
+               M_Quit_Draw ();
+               break;
+
+       case m_serialconfig:
+               M_SerialConfig_Draw ();
+               break;
+
+       case m_modemconfig:
+               M_ModemConfig_Draw ();
+               break;
+
+       case m_lanconfig:
+               M_LanConfig_Draw ();
+               break;
+
+       case m_gameoptions:
+               M_GameOptions_Draw ();
+               break;
+
+       case m_search:
+               M_Search_Draw ();
+               break;
+
+       case m_slist:
+               M_ServerList_Draw ();
+               break;
+       }
+
+       if (m_entersound)
+       {
+               S_LocalSound ("misc/menu2.wav");
+               m_entersound = false;
+       }
+
+       VID_UnlockBuffer ();
+       S_ExtraUpdate ();
+       VID_LockBuffer ();
+}
+
+
+void M_Keydown (int key)
+{
+       switch (m_state)
+       {
+       case m_none:
+               return;
+
+       case m_main:
+               M_Main_Key (key);
+               return;
+
+       case m_demo:
+               M_Demo_Key (key);
+               return;
+
+       case m_singleplayer:
+               M_SinglePlayer_Key (key);
+               return;
+
+       case m_load:
+               M_Load_Key (key);
+               return;
+
+       case m_save:
+               M_Save_Key (key);
+               return;
+
+       case m_multiplayer:
+               M_MultiPlayer_Key (key);
+               return;
+
+       case m_setup:
+               M_Setup_Key (key);
+               return;
+
+       case m_net:
+               M_Net_Key (key);
+               return;
+
+       case m_options:
+               M_Options_Key (key);
+               return;
+
+       case m_keys:
+               M_Keys_Key (key);
+               return;
+
+       case m_video:
+               M_Video_Key (key);
+               return;
+
+       case m_help:
+               M_Help_Key (key);
+               return;
+
+       case m_quit:
+               M_Quit_Key (key);
+               return;
+
+       case m_serialconfig:
+               M_SerialConfig_Key (key);
+               return;
+
+       case m_modemconfig:
+               M_ModemConfig_Key (key);
+               return;
+
+       case m_lanconfig:
+               M_LanConfig_Key (key);
+               return;
+
+       case m_gameoptions:
+               M_GameOptions_Key (key);
+               return;
+
+       case m_search:
+               M_Search_Key (key);
+               break;
+
+       case m_slist:
+               M_ServerList_Key (key);
+               return;
+       }
+}
+
+
+void M_ConfigureNetSubsystem(void)
+{
+// enable/disable net systems to match desired config
+
+       Cbuf_AddText ("stopdemo\n");
+       if (SerialConfig || DirectConfig)
+       {
+               Cbuf_AddText ("com1 enable\n");
+       }
+
+       if (IPXConfig || TCPIPConfig)
+               net_hostport = lanConfig_port;
+}
diff --git a/menu.h b/menu.h
new file mode 100644 (file)
index 0000000..616de3f
--- /dev/null
+++ b/menu.h
@@ -0,0 +1,38 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// the net drivers should just set the apropriate bits in m_activenet,
+// instead of having the menu code look through their internal tables
+//
+#define        MNET_IPX                1
+#define        MNET_TCP                2
+
+extern int     m_activenet;
+
+//
+// menus
+//
+void M_Init (void);
+void M_Keydown (int key);
+void M_Draw (void);
+void M_ToggleMenu_f (void);
+
+
diff --git a/model_alias.c b/model_alias.c
new file mode 100644 (file)
index 0000000..05a61b0
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+/*
+===============
+Mod_AliasInit
+===============
+*/
+void Mod_AliasInit (void)
+{
+}
+
+aliashdr_t     *pheader;
+
+typedef struct
+{
+       int v[3];
+       vec3_t n;
+} temptris_t;
+temptris_t *temptris;
+//stvert_t     stverts[MAXALIASVERTS];
+//mtriangle_t  triangles[MAXALIASTRIS];
+
+// a pose is a single set of vertexes.  a frame may be
+// an animating sequence of poses
+//trivertx_t   *poseverts[MAXALIASFRAMES];
+int                    posenum;
+
+byte           **player_8bit_texels_tbl;
+byte           *player_8bit_texels;
+
+float          aliasbboxmin[3], aliasbboxmax[3]; // LordHavoc: proper bounding box considerations
+
+void Mod_ConvertAliasVerts (int numverts, int numtris, vec3_t scale, vec3_t translate, trivertx_t *v, trivert2 *out)
+{
+       int i, j;
+       vec3_t t1, t2;
+       struct
+       {
+               vec3_t v;
+               vec3_t n;
+               int count;
+       } tempvert[MD2MAX_VERTS];
+       temptris_t *tris;
+       // decompress vertices
+       for (i = 0;i < numverts;i++)
+       {
+               VectorCopy(v[i].v, out[i].v);
+               tempvert[i].v[0] = v[i].v[0] * scale[0] + translate[0];
+               tempvert[i].v[1] = v[i].v[1] * scale[1] + translate[1];
+               tempvert[i].v[2] = v[i].v[2] * scale[2] + translate[2];
+               tempvert[i].n[0] = tempvert[i].n[1] = tempvert[i].n[2] = 0;
+               tempvert[i].count = 0;
+               // update bounding box
+               if (tempvert[i].v[0] < aliasbboxmin[0]) aliasbboxmin[0] = tempvert[i].v[0];
+               if (tempvert[i].v[1] < aliasbboxmin[1]) aliasbboxmin[1] = tempvert[i].v[1];
+               if (tempvert[i].v[2] < aliasbboxmin[2]) aliasbboxmin[2] = tempvert[i].v[2];
+               if (tempvert[i].v[0] > aliasbboxmax[0]) aliasbboxmax[0] = tempvert[i].v[0];
+               if (tempvert[i].v[1] > aliasbboxmax[1]) aliasbboxmax[1] = tempvert[i].v[1];
+               if (tempvert[i].v[2] > aliasbboxmax[2]) aliasbboxmax[2] = tempvert[i].v[2];
+       }
+       // calculate surface normals
+       tris = temptris;
+       for (i = 0;i < numtris;i++)
+       {
+               VectorSubtract(tempvert[tris->v[0]].v, tempvert[tris->v[1]].v, t1);
+               VectorSubtract(tempvert[tris->v[2]].v, tempvert[tris->v[1]].v, t2);
+               CrossProduct(t1, t2, tris->n);
+               VectorNormalize(tris->n);
+               // add surface normal to vertices
+               for (j = 0;j < 3;j++)
+               {
+                       VectorAdd(tris->n, tempvert[tris->v[j]].n, tempvert[tris->v[j]].n);
+                       tempvert[tris->v[j]].count++;
+               }
+               tris++;
+       }
+       // average normals and write out 1.7bit format
+       for (i = 0;i < pheader->numtris;i++)
+       {
+               VectorNormalize(tempvert[i].n);
+               out[i].n[0] = (signed char) (tempvert[i].n[0] * 127.0);
+               out[i].n[1] = (signed char) (tempvert[i].n[1] * 127.0);
+               out[i].n[2] = (signed char) (tempvert[i].n[2] * 127.0);
+       }
+}
+
+/*
+=================
+Mod_LoadAliasFrame
+=================
+*/
+void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame)
+{
+       trivertx_t              *pinframe;
+       int                             i;
+       daliasframe_t   *pdaliasframe;
+       
+       pdaliasframe = (daliasframe_t *)pin;
+
+       strcpy (frame->name, pdaliasframe->name);
+       frame->firstpose = posenum;
+       frame->numposes = 1;
+
+       for (i=0 ; i<3 ; i++)
+       {
+       // these are byte values, so we don't have to worry about
+       // endianness
+               frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
+               frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; // LordHavoc: was setting bboxmin a second time (bug)
+       }
+
+       pinframe = (trivertx_t *)(pdaliasframe + 1);
+
+//     poseverts[posenum] = pinframe;
+       Mod_ConvertAliasVerts(pheader->numverts, pheader->numtris, pheader->scale, pheader->scale_origin, pinframe, (void *)((int) pheader + pheader->posedata + sizeof(trivert2) * pheader->numverts * posenum));
+//     // LordHavoc: copy the frame data
+//     memcpy((void *)((int) pheader + pheader->posedata + sizeof(trivertx_t) * pheader->numverts * posenum), pinframe, sizeof(trivertx_t) * pheader->numverts);
+       posenum++;
+
+       pinframe += pheader->numverts;
+
+       return (void *)pinframe;
+}
+
+
+/*
+=================
+Mod_LoadAliasGroup
+=================
+*/
+void *Mod_LoadAliasGroup (void * pin,  maliasframedesc_t *frame)
+{
+       daliasgroup_t           *pingroup;
+       int                                     i, numframes;
+       daliasinterval_t        *pin_intervals;
+       void                            *ptemp;
+       
+       pingroup = (daliasgroup_t *)pin;
+
+       numframes = LittleLong (pingroup->numframes);
+
+       frame->firstpose = posenum;
+       frame->numposes = numframes;
+
+       for (i=0 ; i<3 ; i++)
+       {
+       // these are byte values, so we don't have to worry about endianness
+               frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
+               frame->bboxmax.v[i] = pingroup->bboxmax.v[i]; // LordHavoc: was setting bboxmin a second time (bug)
+       }
+
+       pin_intervals = (daliasinterval_t *)(pingroup + 1);
+
+       frame->interval = LittleFloat (pin_intervals->interval);
+
+       pin_intervals += numframes;
+
+       ptemp = (void *)pin_intervals;
+
+       for (i=0 ; i<numframes ; i++)
+       {
+//             poseverts[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1);
+               Mod_ConvertAliasVerts(pheader->numverts, pheader->numtris, pheader->scale, pheader->scale_origin, (void *)((daliasframe_t *)ptemp + 1), (void *)((int) pheader + pheader->posedata + sizeof(trivert2) * pheader->numverts * posenum));
+//             // LordHavoc: copy the frame data
+//             memcpy((void *)((int) pheader + pheader->posedata + sizeof(trivertx_t) * pheader->numverts * posenum), (void *)((daliasframe_t *)ptemp + 1), sizeof(trivertx_t) * pheader->numverts);
+               posenum++;
+
+               ptemp = (trivertx_t *)((daliasframe_t *)ptemp + 1) + pheader->numverts;
+       }
+
+       return ptemp;
+}
+
+//=========================================================
+
+/*
+=================
+Mod_FloodFillSkin
+
+Fill background pixels so mipmapping doesn't have haloes - Ed
+=================
+*/
+
+typedef struct
+{
+       short           x, y;
+} floodfill_t;
+
+extern unsigned d_8to24table[];
+
+// must be a power of 2
+#define FLOODFILL_FIFO_SIZE 0x1000
+#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
+
+#define FLOODFILL_STEP( off, dx, dy ) \
+{ \
+       if (pos[off] == fillcolor) \
+       { \
+               pos[off] = 255; \
+               fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
+               inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
+       } \
+       else if (pos[off] != 255) fdc = pos[off]; \
+}
+
+void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight )
+{
+       byte                            fillcolor = *skin; // assume this is the pixel to fill
+       floodfill_t                     fifo[FLOODFILL_FIFO_SIZE];
+       int                                     inpt = 0, outpt = 0;
+       int                                     filledcolor = -1;
+       int                                     i;
+
+       if (filledcolor == -1)
+       {
+               filledcolor = 0;
+               // attempt to find opaque black
+               for (i = 0; i < 256; ++i)
+                       if (d_8to24table[i] == (255 << 0)) // alpha 1.0
+                       {
+                               filledcolor = i;
+                               break;
+                       }
+       }
+
+       // can't fill to filled color or to transparent color (used as visited marker)
+       if ((fillcolor == filledcolor) || (fillcolor == 255))
+       {
+               //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor );
+               return;
+       }
+
+       fifo[inpt].x = 0, fifo[inpt].y = 0;
+       inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+
+       while (outpt != inpt)
+       {
+               int                     x = fifo[outpt].x, y = fifo[outpt].y;
+               int                     fdc = filledcolor;
+               byte            *pos = &skin[x + skinwidth * y];
+
+               outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
+
+               if (x > 0)                              FLOODFILL_STEP( -1, -1, 0 );
+               if (x < skinwidth - 1)  FLOODFILL_STEP( 1, 1, 0 );
+               if (y > 0)                              FLOODFILL_STEP( -skinwidth, 0, -1 );
+               if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 );
+               skin[x + skinwidth * y] = fdc;
+       }
+}
+
+/*
+===============
+Mod_LoadAllSkins
+===============
+*/
+void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int bytesperpixel)
+{
+       int             i, j, k;
+       char    name[32];
+       int             s;
+       byte    *skin;
+       byte    *texels;
+       daliasskingroup_t               *pinskingroup;
+       int             groupskins;
+       daliasskininterval_t    *pinskinintervals;
+       
+       skin = (byte *)(pskintype + 1);
+
+       if (numskins < 1 || numskins > MAX_SKINS)
+               Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
+
+       s = pheader->skinwidth * pheader->skinheight;
+
+       for (i = 0;i < numskins;i++)
+       {
+               if (pskintype->type == ALIAS_SKIN_SINGLE)
+               {
+                       if (bytesperpixel == 1)
+                               Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
+
+                       // save 8 bit texels for the player model to remap
+       //              if (!strcmp(loadmodel->name,"progs/player.mdl")) {
+                               texels = Hunk_AllocName(s, loadname);
+                               pheader->texels[i] = texels - (byte *)pheader;
+                               memcpy (texels, (byte *)(pskintype + 1), s);
+       //              }
+                       sprintf (name, "%s_%i", loadmodel->name, i);
+                       pheader->gl_texturenum[i][0] =
+                       pheader->gl_texturenum[i][1] =
+                       pheader->gl_texturenum[i][2] =
+                       pheader->gl_texturenum[i][3] =
+                               GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), true, false, bytesperpixel);
+                       pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s);
+               }
+               else
+               {
+                       // animating skin group.  yuck.
+                       pskintype++;
+                       pinskingroup = (daliasskingroup_t *)pskintype;
+                       groupskins = LittleLong (pinskingroup->numskins);
+                       pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
+
+                       pskintype = (void *)(pinskinintervals + groupskins);
+
+                       for (j = 0;j < groupskins;j++)
+                       {
+                                       if (bytesperpixel == 1)
+                                               Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
+                                       if (j == 0)
+                                       {
+                                               texels = Hunk_AllocName(s, loadname);
+                                               pheader->texels[i] = texels - (byte *)pheader;
+                                               memcpy (texels, (byte *)(pskintype), s);
+                                       }
+                                       sprintf (name, "%s_%i_%i", loadmodel->name, i,j);
+                                       pheader->gl_texturenum[i][j&3] = 
+                                               GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype), true, false, bytesperpixel);
+                                       pskintype = (daliasskintype_t *)((byte *)(pskintype) + s);
+                       }
+                       k = j;
+                       for (;j < 4;j++)
+                               pheader->gl_texturenum[i][j&3] = pheader->gl_texturenum[i][j - k]; 
+               }
+       }
+
+       return (void *)pskintype;
+}
+
+//=========================================================================
+
+//void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr);
+
+/*
+=================
+Mod_LoadAliasModel
+=================
+*/
+#define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid VALUE (%d exceeds %d - %d)\n", mod->name, VALUE, MIN, MAX);
+#define BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid VALUE (%g exceeds %g - %g)\n", mod->name, VALUE, MIN, MAX);
+void Mod_LoadAliasModel (model_t *mod, void *buffer)
+{
+       int                                     i, j, version, numframes, size, start, end, total;
+       mdl_t                           *pinmodel;
+       stvert_t                        *pinstverts;
+       dtriangle_t                     *pintriangles;
+       daliasframetype_t       *pframetype;
+       daliasskintype_t        *pskintype;
+       // LordHavoc: 32bit textures
+       int                                     bytesperpixel;
+       unsigned short          *poutvertindices;
+       float                           *pouttexcoords, scales, scalet;
+       temptris_t                      *tris;
+
+       start = Hunk_LowMark ();
+
+       if (!temptris)
+               temptris = malloc(sizeof(temptris_t) * MD2MAX_TRIANGLES);
+
+       pinmodel = (mdl_t *)buffer;
+
+       version = LittleLong (pinmodel->version);
+       if (version != ALIAS_VERSION && version != ALIAS32_VERSION)
+               Host_Error ("%s has wrong version number (%i should be %i or %i)",
+                                mod->name, version, ALIAS_VERSION, ALIAS32_VERSION);
+
+       mod->type = ALIASTYPE_MDL;
+
+//
+// allocate space for a working header, plus all the data except the frames,
+// skin and group info
+//
+//     size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pinmodel->frames[0]));
+       size = sizeof (aliashdr_t);
+       size += LittleLong (pinmodel->numverts) * sizeof(float[2][2]);
+       size += LittleLong (pinmodel->numtris) * sizeof(unsigned short[3]);
+       size += LittleLong (pinmodel->numframes) * (sizeof(trivert2) * LittleLong (pinmodel->numverts) + sizeof(maliasframedesc_t));
+       BOUNDI(size,256,4194304);
+       pheader = Hunk_AllocName (size, loadname);
+       
+       mod->flags = LittleLong (pinmodel->flags);
+       mod->type = mod_alias;
+
+// endian-adjust and copy the data, starting with the alias model header
+       pheader->boundingradius = LittleFloat (pinmodel->boundingradius);
+       BOUNDF(pheader->boundingradius,0,65536);
+       pheader->numskins = LittleLong (pinmodel->numskins);
+       BOUNDI(pheader->numskins,0,256);
+       pheader->skinwidth = LittleLong (pinmodel->skinwidth);
+       BOUNDI(pheader->skinwidth,0,4096);
+       pheader->skinheight = LittleLong (pinmodel->skinheight);
+       BOUNDI(pheader->skinheight,0,1024);
+//LordHavoc: 32bit textures
+       bytesperpixel = version == ALIAS32_VERSION ? 4 : 1;
+
+//     if (pheader->skinheight > MAX_LBM_HEIGHT)
+//             Host_Error ("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT);
+
+       pheader->numverts = LittleLong (pinmodel->numverts);
+       BOUNDI(pheader->numverts,0,MAXALIASVERTS);
+       /*
+       if (pheader->numverts <= 0)
+               Host_Error ("model %s has no vertices", mod->name);
+       if (pheader->numverts > MAXALIASVERTS)
+               Host_Error ("model %s has too many vertices", mod->name);
+       */
+
+       pheader->numtris = LittleLong (pinmodel->numtris);
+       BOUNDI(pheader->numtris,0,65536);
+//     if (pheader->numtris <= 0)
+//             Host_Error ("model %s has no triangles", mod->name);
+
+       pheader->numframes = LittleLong (pinmodel->numframes);
+       BOUNDI(pheader->numframes,0,65536);
+       numframes = pheader->numframes;
+//     if (numframes < 1)
+//             Host_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
+
+       pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
+       BOUNDF(pheader->size,0,65536);
+       mod->synctype = LittleLong (pinmodel->synctype);
+       BOUNDI(pheader->synctype,0,2);
+       mod->numframes = pheader->numframes;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               pheader->scale[i] = LittleFloat (pinmodel->scale[i]);
+               BOUNDF(pheader->scale[i],0,65536);
+               pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
+               BOUNDF(pheader->scale_origin[i],-65536,65536);
+               pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]);
+               BOUNDF(pheader->eyeposition[i],-65536,65536);
+       }
+
+// load the skins
+       pskintype = (daliasskintype_t *)&pinmodel[1];
+       pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype, bytesperpixel);
+
+// load base s and t vertices
+       pinstverts = (stvert_t *)pskintype;
+       pouttexcoords = (float *)&pheader->frames[numframes];
+       pheader->texcoords = (int) pouttexcoords - (int) pheader;
+
+       // LordHavoc: byteswap and convert stvert data
+       scales = 1.0 / pheader->skinwidth;
+       scalet = 1.0 / pheader->skinheight;
+       for (i = 0;i < pheader->numverts;i++)
+       {
+               pouttexcoords[i*2] = LittleLong (pinstverts[i].s) * scales;
+               pouttexcoords[i*2+1] = LittleLong (pinstverts[i].t) * scalet;
+               pouttexcoords[(i+pheader->numverts)*2] = LittleLong (pinstverts[i].s) * scales + 0.5;
+               pouttexcoords[(i+pheader->numverts)*2+1] = LittleLong (pinstverts[i].t) * scalet;
+               if (pouttexcoords[i*2] >= 0.5) // already a back side coordinate
+               {
+                       pouttexcoords[i*2] -= 0.5;
+                       pouttexcoords[(i+pheader->numverts)*2] -= 0.5;
+               }
+               BOUNDF(pouttexcoords[i*2],0,1);
+               BOUNDF(pouttexcoords[i*2+1],0,1);
+               BOUNDF(pouttexcoords[(i+pheader->numverts)*2],0,1);
+               BOUNDF(pouttexcoords[(i+pheader->numverts)*2+1],0,1);
+       }
+
+// load triangle data
+       pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts];
+       poutvertindices = (unsigned short *)&pouttexcoords[pheader->numverts*4];
+       pheader->vertindices = (int) poutvertindices - (int) pheader;
+       // LordHavoc: sort triangles into front and back lists
+       // so they can be drawn refering to different texture coordinate arrays,
+       // but sharing vertex data
+       pheader->frontfaces = 0;
+       pheader->backfaces = 0;
+       tris = temptris;
+       for (i=0 ; i<pheader->numtris ; i++)
+       {
+               if (LittleLong(pintriangles[i].facesfront))
+               {
+                       pheader->frontfaces++;
+                       for (j=0 ; j<3 ; j++)
+                               *poutvertindices++ = LittleLong (pintriangles[i].vertindex[j]);
+               }
+               for (j=0 ; j<3 ; j++)
+                       tris->v[j] = LittleLong (pintriangles[i].vertindex[j]);
+               tris++;
+       }
+       for (i=0 ; i<pheader->numtris ; i++)
+       {
+               if (!LittleLong(pintriangles[i].facesfront))
+               {
+                       pheader->backfaces++;
+                       for (j=0 ; j<3 ; j++)
+                               *poutvertindices++ = LittleLong (pintriangles[i].vertindex[j]);
+               }
+       }
+
+// load the frames
+       posenum = 0;
+       pheader->posedata = (int) poutvertindices - (int) pheader;
+       pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris];
+
+       // LordHavoc: doing proper bbox for model
+       aliasbboxmin[0] = aliasbboxmin[1] = aliasbboxmin[2] = 1000000000;
+       aliasbboxmax[0] = aliasbboxmax[1] = aliasbboxmax[2] = -1000000000;
+
+       for (i=0 ; i<numframes ; i++)
+       {
+               aliasframetype_t        frametype;
+
+               frametype = LittleLong (pframetype->type);
+
+               if (frametype == ALIAS_SINGLE)
+                       pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]);
+               else
+                       pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]);
+       }
+
+       pheader->numposes = posenum;
+
+       // LordHavoc: fixed model bbox - was //FIXME: do this right
+       //mod->mins[0] = mod->mins[1] = mod->mins[2] = -16;
+       //mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16;
+       for (j = 0;j < 3;j++)
+       {
+               mod->mins[j] = aliasbboxmin[j];
+               mod->maxs[j] = aliasbboxmax[j];
+       }
+
+// move the complete, relocatable alias model to the cache
+       end = Hunk_LowMark ();
+       total = end - start;
+       
+       Cache_Alloc (&mod->cache, total, loadname);
+       if (!mod->cache.data)
+               return;
+       memcpy (mod->cache.data, pheader, total);
+
+       Hunk_FreeToLowMark (start);
+}
+
+/*
+=================
+Mod_LoadQ2AliasModel
+=================
+*/
+int loadtextureimage (int texnum, char* filename, qboolean complain, int matchwidth, int matchheight);
+void Mod_LoadQ2AliasModel (model_t *mod, void *buffer)
+{
+       int                                     i, j, version, size, *pinglcmd, *poutglcmd, start, end, total, framesize;
+       md2_t                           *pinmodel;
+       md2mem_t                        *pheader;
+       md2triangle_t           *pintriangles, *pouttriangles;
+       md2frame_t                      *pinframe;
+       md2memframe_t           *poutframe;
+       char                            *pinskins;
+       temptris_t                      *tris;
+
+       start = Hunk_LowMark ();
+
+       if (!temptris)
+               temptris = malloc(sizeof(temptris_t) * MD2MAX_TRIANGLES);
+
+       pinmodel = (md2_t *)buffer;
+
+       version = LittleLong (pinmodel->version);
+       if (version != MD2ALIAS_VERSION)
+               Host_Error ("%s has wrong version number (%i should be %i)",
+                                mod->name, version, MD2ALIAS_VERSION);
+
+       mod->type = mod_alias;
+       mod->aliastype = ALIASTYPE_MD2;
+
+       framesize = sizeof(md2memframe_t) + LittleLong(pinmodel->num_xyz) * sizeof(trivert2);
+       // LordHavoc: calculate size for in memory version
+       size = sizeof(md2mem_t)
+                + LittleLong(pinmodel->num_st) * sizeof(md2stvert_t)
+                + LittleLong(pinmodel->num_tris) * sizeof(md2triangle_t)
+                + LittleLong(pinmodel->num_frames) * framesize
+                + LittleLong(pinmodel->num_glcmds) * sizeof(int);
+       if (size <= 0 || size >= MD2MAX_SIZE)
+               Host_Error ("%s is not a valid model", mod->name);
+       pheader = Hunk_AllocName (size, loadname);
+       
+       mod->flags = 0; // there are no MD2 flags
+       mod->numframes = LittleLong(pinmodel->num_frames);
+       mod->synctype = ST_RAND;
+
+       if (LittleLong(pinmodel->num_skins) >= 1 && (LittleLong(pinmodel->ofs_skins <= 0) || LittleLong(pinmodel->ofs_skins) >= LittleLong(pinmodel->ofs_end)))
+               Host_Error ("%s is not a valid model", mod->name);
+       if (LittleLong(pinmodel->ofs_st <= 0) || LittleLong(pinmodel->ofs_st) >= LittleLong(pinmodel->ofs_end))
+               Host_Error ("%s is not a valid model", mod->name);
+       if (LittleLong(pinmodel->ofs_tris <= 0) || LittleLong(pinmodel->ofs_tris) >= LittleLong(pinmodel->ofs_end))
+               Host_Error ("%s is not a valid model", mod->name);
+       if (LittleLong(pinmodel->ofs_frames <= 0) || LittleLong(pinmodel->ofs_frames) >= LittleLong(pinmodel->ofs_end))
+               Host_Error ("%s is not a valid model", mod->name);
+       if (LittleLong(pinmodel->ofs_glcmds <= 0) || LittleLong(pinmodel->ofs_glcmds) >= LittleLong(pinmodel->ofs_end))
+               Host_Error ("%s is not a valid model", mod->name);
+
+       if (LittleLong(pinmodel->num_tris < 1) || LittleLong(pinmodel->num_tris) > MD2MAX_TRIANGLES)
+               Host_Error ("%s has invalid number of triangles: %i", mod->name, LittleLong(pinmodel->num_tris));
+       if (LittleLong(pinmodel->num_xyz < 1) || LittleLong(pinmodel->num_xyz) > MD2MAX_VERTS)
+               Host_Error ("%s has invalid number of vertices: %i", mod->name, LittleLong(pinmodel->num_xyz));
+       if (LittleLong(pinmodel->num_frames < 1) || LittleLong(pinmodel->num_frames) > 256) //MD2MAX_FRAMES)
+               Host_Error ("%s has invalid number of frames: %i", mod->name, LittleLong(pinmodel->num_frames));
+       if (LittleLong(pinmodel->num_skins < 0) || LittleLong(pinmodel->num_skins) > MD2MAX_SKINS)
+               Host_Error ("%s has invalid number of skins: %i", mod->name, LittleLong(pinmodel->num_skins));
+
+       pheader->framesize = framesize;
+       pheader->num_skins = LittleLong(pinmodel->num_skins);
+       pheader->num_xyz = LittleLong(pinmodel->num_xyz);
+       pheader->num_st = LittleLong(pinmodel->num_st);
+       pheader->num_tris = LittleLong(pinmodel->num_tris);
+       pheader->num_frames = LittleLong(pinmodel->num_frames);
+       pheader->num_glcmds = LittleLong(pinmodel->num_glcmds);
+
+// load the skins
+       if (pheader->num_skins)
+       {
+               pinskins = (void*)((int) pinmodel + LittleLong(pinmodel->ofs_skins));
+               for (i = 0;i < pheader->num_skins;i++)
+               {
+                       pheader->gl_texturenum[i] = loadtextureimage (-1, pinskins, TRUE, 0, 0);
+                       pinskins += MD2MAX_SKINNAME;
+               }
+       }
+
+// load triangles
+       pintriangles = (void*)((int) pinmodel + LittleLong(pinmodel->ofs_tris));
+       pouttriangles = (void*)&pheader[1];
+       pheader->ofs_tris = (int) pouttriangles - (int) pheader;
+       tris = temptris;
+       // swap the triangle list
+       for (i=0 ; i<pheader->num_tris ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       tris->v[j] = pouttriangles->index_xyz[j] = LittleShort (pintriangles->index_xyz[j]);
+                       pouttriangles->index_st[j] = LittleShort (pintriangles->index_st[j]);
+                       if (pouttriangles->index_xyz[j] >= pheader->num_xyz)
+                               Sys_Error ("%s has invalid vertex indices", mod->name);
+                       if (pouttriangles->index_st[j] >= pheader->num_st)
+                               Sys_Error ("%s has invalid vertex indices", mod->name);
+               }
+               pintriangles++;
+               pouttriangles++;
+               tris++;
+       }
+
+       // LordHavoc: doing proper bbox for model
+       aliasbboxmin[0] = aliasbboxmin[1] = aliasbboxmin[2] = 1000000000;
+       aliasbboxmax[0] = aliasbboxmax[1] = aliasbboxmax[2] = -1000000000;
+
+// load the frames
+       pinframe = (void*) ((int) pinmodel + LittleLong(pinmodel->ofs_frames));
+       poutframe = (void*) pouttriangles;
+       pheader->ofs_frames = (int) poutframe - (int) pheader;
+       for (i=0 ; i<pheader->num_frames ; i++)
+       {
+               for (j = 0;j < 3;j++)
+               {
+                       poutframe->scale[j] = LittleFloat(pinframe->scale[j]);
+                       poutframe->translate[j] = LittleFloat(pinframe->translate[j]);
+               }
+               Mod_ConvertAliasVerts (pheader->num_xyz, pheader->num_tris, poutframe->scale, poutframe->translate, &pinframe->verts[0], &poutframe->verts[0]);
+               pinframe = (void*) &pinframe->verts[j];
+               poutframe = (void*) &poutframe->verts[j];
+       }
+
+       // LordHavoc: model bbox
+       for (j = 0;j < 3;j++)
+       {
+               mod->mins[j] = aliasbboxmin[j];
+               mod->maxs[j] = aliasbboxmax[j];
+       }
+
+       // load the draw list
+       pinglcmd = (void*) ((int) pinmodel + LittleLong(pinmodel->ofs_glcmds));
+       poutglcmd = (void*) poutframe;
+       pheader->ofs_glcmds = (int) poutglcmd - (int) pheader;
+       for (i = 0;i < pheader->num_glcmds;i++)
+               *poutglcmd++ = LittleLong(*pinglcmd++);
+
+// move the complete, relocatable alias model to the cache
+       end = Hunk_LowMark ();
+       total = end - start;
+       
+       Cache_Alloc (&mod->cache, total, loadname);
+       if (!mod->cache.data)
+               return;
+       memcpy (mod->cache.data, pheader, total);
+
+       Hunk_FreeToLowMark (start);
+}
diff --git a/model_alias.h b/model_alias.h
new file mode 100644 (file)
index 0000000..8908bec
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+/*
+==============================================================================
+
+ALIAS MODELS
+
+Alias models are position independent, so the cache manager can move them.
+==============================================================================
+*/
+
+#include "modelgen.h"
+
+typedef struct
+{
+       int                                     firstpose;
+       int                                     numposes;
+       float                           interval;
+       trivertx_t                      bboxmin;
+       trivertx_t                      bboxmax;
+       int                                     frame;
+       char                            name[16];
+} maliasframedesc_t;
+
+typedef struct
+{
+       trivertx_t                      bboxmin;
+       trivertx_t                      bboxmax;
+       int                                     frame;
+} maliasgroupframedesc_t;
+
+typedef struct
+{
+       int                                             numframes;
+       int                                             intervals;
+       maliasgroupframedesc_t  frames[1];
+} maliasgroup_t;
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct mtriangle_s {
+       int                                     facesfront;
+       int                                     vertindex[3];
+} mtriangle_t;
+
+// LordHavoc: new vertex format
+typedef struct {
+       byte v[3]; // location
+       signed char n[3]; // surface normal for lighting *127.0
+} trivert2;
+
+#define        MAX_SKINS       32
+typedef struct {
+       int                     ident;
+       int                     version;
+       vec3_t          scale;
+       vec3_t          scale_origin;
+       float           boundingradius;
+       vec3_t          eyeposition;
+       int                     numskins;
+       int                     skinwidth;
+       int                     skinheight;
+       int                     numverts;
+       int                     numtris;
+       int                     numframes;
+       synctype_t      synctype;
+       int                     flags;
+       float           size;
+
+       int                                     numposes;
+       int                                     posedata;       // LordHavoc: numposes*numverts*trivert2
+       int                                     frontfaces; // LordHavoc: how many front faces
+       int                                     backfaces; // LordHavoc: how many back faces
+//     int                                     poseverts;
+//     int                                     posedata;       // numposes*poseverts trivert_t
+//     int                                     commands;       // gl command list with embedded s/t
+       int                                     texcoords;      // LordHavoc: texture coordinates
+       int                                     vertindices;    // LordHavoc: vertex numbers
+       int                                     gl_texturenum[MAX_SKINS][4];
+       int                                     texels[MAX_SKINS];      // only for player skins
+       maliasframedesc_t       frames[1];      // variable sized
+} aliashdr_t;
+
+#define        MAXALIASVERTS   4096
+#define        MAXALIASFRAMES  1024
+#define        MAXALIASTRIS    4096
+
+extern aliashdr_t      *pheader;
+//extern       stvert_t        stverts[MAXALIASVERTS];
+//extern       mtriangle_t     triangles[MAXALIASTRIS];
+//extern       trivertx_t      *poseverts[MAXALIASFRAMES];
+
+/*
+========================================================================
+
+.MD2 triangle model file format
+
+========================================================================
+*/
+
+// LordHavoc: grabbed this from the Q2 utility source,
+// renamed a things to avoid conflicts
+
+#define MD2IDALIASHEADER               (('2'<<24)+('P'<<16)+('D'<<8)+'I')
+#define MD2ALIAS_VERSION       8
+
+#define        MD2MAX_TRIANGLES        4096
+#define MD2MAX_VERTS           4096
+#define MD2MAX_FRAMES          1024
+#define MD2MAX_SKINS   32
+#define        MD2MAX_SKINNAME 64
+// sanity checking size
+#define MD2MAX_SIZE    (16777216)
+
+typedef struct
+{
+       short   s;
+       short   t;
+} md2stvert_t;
+
+typedef struct 
+{
+       short   index_xyz[3];
+       short   index_st[3];
+} md2triangle_t;
+
+typedef struct
+{
+       float           scale[3];       // multiply byte verts by this
+       float           translate[3];   // then add this
+       char            name[16];       // frame name from grabbing
+       trivertx_t      verts[];        // variable sized
+} md2frame_t;
+
+// LordHavoc: memory representation is different than disk
+typedef struct
+{
+       float           scale[3];       // multiply byte verts by this
+       float           translate[3];   // then add this
+       trivert2        verts[];        // variable sized
+} md2memframe_t;
+
+
+// the glcmd format:
+// a positive integer starts a tristrip command, followed by that many
+// vertex structures.
+// a negative integer starts a trifan command, followed by -x vertexes
+// a zero indicates the end of the command list.
+// a vertex consists of a floating point s, a floating point t,
+// and an integer vertex index.
+
+
+typedef struct
+{
+       int                     ident;
+       int                     version;
+
+       int                     skinwidth;
+       int                     skinheight;
+       int                     framesize;              // byte size of each frame
+
+       int                     num_skins;
+       int                     num_xyz;
+       int                     num_st;                 // greater than num_xyz for seams
+       int                     num_tris;
+       int                     num_glcmds;             // dwords in strip/fan command list
+       int                     num_frames;
+
+       int                     ofs_skins;              // each skin is a MAX_SKINNAME string
+       int                     ofs_st;                 // byte offset from start for stverts
+       int                     ofs_tris;               // offset for dtriangles
+       int                     ofs_frames;             // offset for first frame
+       int                     ofs_glcmds;     
+       int                     ofs_end;                // end of file
+} md2_t;
+
+typedef struct
+{
+       int                     framesize;              // byte size of each frame
+
+       int                     num_skins;
+       int                     num_xyz;
+       int                     num_st;                 // greater than num_xyz for seams
+       int                     num_tris;
+       int                     num_glcmds;             // dwords in strip/fan command list
+       int                     num_frames;
+
+       int                     ofs_tris;               // offset for dtriangles
+       int                     ofs_frames;             // offset for first frame
+       int                     ofs_glcmds;     
+
+       int                     gl_texturenum[MAX_SKINS];
+} md2mem_t;
+
+#define ALIASTYPE_MDL 1
+#define ALIASTYPE_MD2 2
diff --git a/model_brush.c b/model_brush.c
new file mode 100644 (file)
index 0000000..c742c10
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+byte   mod_novis[MAX_MAP_LEAFS/8];
+
+qboolean       hlbsp; // LordHavoc: true if it is a HalfLife BSP file (version 30)
+
+cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", true};
+cvar_t halflifebsp = {"halflifebsp", "0"};
+
+/*
+===============
+Mod_BrushInit
+===============
+*/
+void Mod_BrushInit (void)
+{
+       Cvar_RegisterVariable (&gl_subdivide_size);
+       Cvar_RegisterVariable (&halflifebsp);
+       memset (mod_novis, 0xff, sizeof(mod_novis));
+}
+
+/*
+===============
+Mod_PointInLeaf
+===============
+*/
+mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
+{
+       mnode_t         *node;
+       float           d;
+       mplane_t        *plane;
+       
+       if (!model || !model->nodes)
+               Sys_Error ("Mod_PointInLeaf: bad model");
+
+       node = model->nodes;
+       while (1)
+       {
+               if (node->contents < 0)
+                       return (mleaf_t *)node;
+               plane = node->plane;
+               d = DotProduct (p,plane->normal) - plane->dist;
+               if (d > 0)
+                       node = node->children[0];
+               else
+                       node = node->children[1];
+       }
+       
+       return NULL;    // never reached
+}
+
+
+/*
+===================
+Mod_DecompressVis
+===================
+*/
+byte *Mod_DecompressVis (byte *in, model_t *model)
+{
+       static byte     decompressed[MAX_MAP_LEAFS/8];
+       int             c;
+       byte    *out;
+       int             row;
+
+       row = (model->numleafs+7)>>3;   
+       out = decompressed;
+
+       if (!in)
+       {       // no vis info, so make all visible
+               while (row)
+               {
+                       *out++ = 0xff;
+                       row--;
+               }
+               return decompressed;            
+       }
+
+       do
+       {
+               if (*in)
+               {
+                       *out++ = *in++;
+                       continue;
+               }
+       
+               c = in[1];
+               in += 2;
+               while (c)
+               {
+                       *out++ = 0;
+                       c--;
+               }
+       } while (out - decompressed < row);
+       
+       return decompressed;
+}
+
+byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
+{
+       if (leaf == model->leafs)
+               return mod_novis;
+       return Mod_DecompressVis (leaf->compressed_vis, model);
+}
+
+byte   *mod_base;
+
+extern cvar_t r_fullbrights;
+
+/*
+=================
+Mod_LoadTextures
+=================
+*/
+void Mod_LoadTextures (lump_t *l)
+{
+       int             i, j, num, max, altmax, bytesperpixel, freeimage, transparent, fullbrights;
+       miptex_t        *mt;
+       texture_t       *tx, *tx2;
+       texture_t       *anims[10];
+       texture_t       *altanims[10];
+       dmiptexlump_t *m;
+       byte *data;
+
+       if (!l->filelen)
+       {
+               loadmodel->textures = NULL;
+               return;
+       }
+
+       m = (dmiptexlump_t *)(mod_base + l->fileofs);
+       
+       m->nummiptex = LittleLong (m->nummiptex);
+       
+       loadmodel->numtextures = m->nummiptex;
+       loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname);
+
+       for (i=0 ; i<m->nummiptex ; i++)
+       {
+               m->dataofs[i] = LittleLong(m->dataofs[i]);
+               if (m->dataofs[i] == -1)
+                       continue;
+               mt = (miptex_t *)((byte *)m + m->dataofs[i]);
+               mt->width = LittleLong (mt->width);
+               mt->height = LittleLong (mt->height);
+               for (j=0 ; j<MIPLEVELS ; j++)
+                       mt->offsets[j] = LittleLong (mt->offsets[j]);
+               
+               if ( (mt->width & 15) || (mt->height & 15) )
+                       Sys_Error ("Texture %s is not 16 aligned", mt->name);
+               // LordHavoc: rewriting the map texture loader for GLQuake
+               tx = Hunk_AllocName (sizeof(texture_t), loadname );
+               loadmodel->textures[i] = tx;
+
+               memcpy (tx->name, mt->name, sizeof(tx->name));
+               tx->width = mt->width;
+               tx->height = mt->height;
+               for (j=0 ; j<MIPLEVELS ; j++)
+                       tx->offsets[j] = 0;
+               freeimage = TRUE;
+               bytesperpixel = 4;
+               fullbrights = FALSE;
+               transparent = FALSE;
+               data = loadimagepixels(mt->name, FALSE, tx->width, tx->height);
+               if (!data) // no external texture found
+               {
+                       freeimage = FALSE;
+                       bytesperpixel = 1;
+                       if (!hlbsp && mt->offsets[0]) // texture included
+                       {
+                               data = (byte *)((int) mt + mt->offsets[0]);
+                               if (r_fullbrights.value && mt->name[0] != '*')
+                               {
+                                       for (j = 0;j < tx->width*tx->height;j++)
+                                               if (data[j] >= 224) // fullbright
+                                               {
+                                                       fullbrights = TRUE;
+                                                       break;
+                                               }
+                               }
+                       }
+                       else // no texture, and no external replacement texture was found
+                       {
+                               tx->width = tx->height = 16;
+                               data = (byte *)((int) r_notexture_mip + r_notexture_mip->offsets[0]);
+                       }
+               }
+               else
+               {
+                       for (j = 0;j < image_width*image_height;j++)
+                               if (data[j*4+3] < 255)
+                               {
+                                       transparent = TRUE;
+                                       break;
+                               }
+               }
+               if (!hlbsp && !strncmp(mt->name,"sky",3)) // LordHavoc: HL sky textures are entirely unrelated
+               {
+                       tx->transparent = FALSE;
+                       R_InitSky (data, bytesperpixel);
+               }
+               else
+               {
+                       tx->transparent = transparent;
+                       if (fullbrights)
+                       {
+                               char name[64];
+                               byte *data2;
+                               data2 = malloc(tx->width*tx->height);
+                               for (j = 0;j < tx->width*tx->height;j++)
+                                       data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
+                               tx->gl_texturenum = GL_LoadTexture (tx->name, tx->width, tx->height, data2, true, 0, 1);
+                               strcpy(name, tx->name);
+                               strcat(name, "_glow");
+                               for (j = 0;j < tx->width*tx->height;j++)
+                                       data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
+                               tx->gl_glowtexturenum = GL_LoadTexture (name, tx->width, tx->height, data2, true, 0, 1);
+                               free(data2);
+                       }
+                       else
+                       {
+                               tx->gl_texturenum = GL_LoadTexture (tx->name, tx->width, tx->height, data, true, transparent, bytesperpixel);
+                               tx->gl_glowtexturenum = 0;
+                       }
+               }
+               if (freeimage)
+                       free(data);
+
+               /*
+               pixels = mt->width*mt->height/64*85;
+               tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname );
+               loadmodel->textures[i] = tx;
+
+               memcpy (tx->name, mt->name, sizeof(tx->name));
+               tx->width = mt->width;
+               tx->height = mt->height;
+               for (j=0 ; j<MIPLEVELS ; j++)
+                       tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
+               // the pixels immediately follow the structures
+               memcpy ( tx+1, mt+1, pixels);
+               
+
+               if (!strncmp(mt->name,"sky",3)) 
+                       R_InitSky (tx);
+               else
+                       tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, 1);
+               */
+       }
+
+//
+// sequence the animations
+//
+       for (i=0 ; i<m->nummiptex ; i++)
+       {
+               tx = loadmodel->textures[i];
+               if (!tx || tx->name[0] != '+')
+                       continue;
+               if (tx->anim_next)
+                       continue;       // allready sequenced
+
+       // find the number of frames in the animation
+               memset (anims, 0, sizeof(anims));
+               memset (altanims, 0, sizeof(altanims));
+
+               max = tx->name[1];
+               altmax = 0;
+               if (max >= 'a' && max <= 'z')
+                       max -= 'a' - 'A';
+               if (max >= '0' && max <= '9')
+               {
+                       max -= '0';
+                       altmax = 0;
+                       anims[max] = tx;
+                       max++;
+               }
+               else if (max >= 'A' && max <= 'J')
+               {
+                       altmax = max - 'A';
+                       max = 0;
+                       altanims[altmax] = tx;
+                       altmax++;
+               }
+               else
+                       Host_Error ("Bad animating texture %s", tx->name);
+
+               for (j=i+1 ; j<m->nummiptex ; j++)
+               {
+                       tx2 = loadmodel->textures[j];
+                       if (!tx2 || tx2->name[0] != '+')
+                               continue;
+                       if (strcmp (tx2->name+2, tx->name+2))
+                               continue;
+
+                       num = tx2->name[1];
+                       if (num >= 'a' && num <= 'z')
+                               num -= 'a' - 'A';
+                       if (num >= '0' && num <= '9')
+                       {
+                               num -= '0';
+                               anims[num] = tx2;
+                               if (num+1 > max)
+                                       max = num + 1;
+                       }
+                       else if (num >= 'A' && num <= 'J')
+                       {
+                               num = num - 'A';
+                               altanims[num] = tx2;
+                               if (num+1 > altmax)
+                                       altmax = num+1;
+                       }
+                       else
+                               Sys_Error ("Bad animating texture %s", tx->name);
+               }
+               
+#define        ANIM_CYCLE      2
+       // link them all together
+               for (j=0 ; j<max ; j++)
+               {
+                       tx2 = anims[j];
+                       if (!tx2)
+                               Sys_Error ("Missing frame %i of %s",j, tx->name);
+                       tx2->anim_total = max * ANIM_CYCLE;
+                       tx2->anim_min = j * ANIM_CYCLE;
+                       tx2->anim_max = (j+1) * ANIM_CYCLE;
+                       tx2->anim_next = anims[ (j+1)%max ];
+                       if (altmax)
+                               tx2->alternate_anims = altanims[0];
+               }
+               for (j=0 ; j<altmax ; j++)
+               {
+                       tx2 = altanims[j];
+                       if (!tx2)
+                               Sys_Error ("Missing frame %i of %s",j, tx->name);
+                       tx2->anim_total = altmax * ANIM_CYCLE;
+                       tx2->anim_min = j * ANIM_CYCLE;
+                       tx2->anim_max = (j+1) * ANIM_CYCLE;
+                       tx2->anim_next = altanims[ (j+1)%altmax ];
+                       if (max)
+                               tx2->alternate_anims = anims[0];
+               }
+       }
+}
+
+/*
+=================
+Mod_LoadLighting
+=================
+*/
+void Mod_LoadLighting (lump_t *l)
+{
+       int i;
+       byte *in, *out, *data;
+       byte d;
+       char litfilename[1024];
+       loadmodel->lightdata = NULL;
+       if (!l->filelen)
+               return;
+       if (hlbsp) // LordHavoc: load the colored lighting data straight
+       {
+               loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);
+               memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
+       }
+       else // LordHavoc: bsp version 29 (normal white lighting)
+       {
+               // LordHavoc: hope is not lost yet, check for a .lit file to load
+               strcpy(litfilename, loadmodel->name);
+               COM_StripExtension(litfilename, litfilename);
+               strcat(litfilename, ".lit");
+               data = (byte*) COM_LoadHunkFile (litfilename, false);
+               if (data)
+               {
+                       if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
+                       {
+                               i = LittleLong(((int *)data)[1]);
+                               if (i == 1)
+                               {
+                                       loadmodel->lightdata = data + 8;
+                                       return;
+                               }
+                               else
+                                       Con_Printf("Unknown .lit file version (%d)\n", i);
+                       }
+                       else
+                               Con_Printf("Corrupt .lit file (old version?), ignoring\n");
+               }
+               // LordHavoc: oh well, expand the white lighting data
+               loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, litfilename);
+               in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
+               out = loadmodel->lightdata;
+               memcpy (in, mod_base + l->fileofs, l->filelen);
+               for (i = 0;i < l->filelen;i++)
+               {
+                       d = *in++;
+                       *out++ = d;
+                       *out++ = d;
+                       *out++ = d;
+               }
+       }
+}
+
+
+/*
+=================
+Mod_LoadVisibility
+=================
+*/
+void Mod_LoadVisibility (lump_t *l)
+{
+       if (!l->filelen)
+       {
+               loadmodel->visdata = NULL;
+               return;
+       }
+       loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname);    
+       memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
+}
+
+void CL_ParseEntityLump(char *entdata);
+
+extern qboolean isworldmodel;
+
+/*
+=================
+Mod_LoadEntities
+=================
+*/
+void Mod_LoadEntities (lump_t *l)
+{
+       if (!l->filelen)
+       {
+               loadmodel->entities = NULL;
+               return;
+       }
+       loadmodel->entities = Hunk_AllocName ( l->filelen, loadname);   
+       memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
+
+       if (isworldmodel)
+               CL_ParseEntityLump(loadmodel->entities);
+}
+
+
+/*
+=================
+Mod_LoadVertexes
+=================
+*/
+void Mod_LoadVertexes (lump_t *l)
+{
+       dvertex_t       *in;
+       mvertex_t       *out;
+       int                     i, count;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->vertexes = out;
+       loadmodel->numvertexes = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               out->position[0] = LittleFloat (in->point[0]);
+               out->position[1] = LittleFloat (in->point[1]);
+               out->position[2] = LittleFloat (in->point[2]);
+       }
+}
+
+/*
+=================
+Mod_LoadSubmodels
+=================
+*/
+void Mod_LoadSubmodels (lump_t *l)
+{
+       dmodel_t        *in;
+       dmodel_t        *out;
+       int                     i, j, count;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->submodels = out;
+       loadmodel->numsubmodels = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {       // spread the mins / maxs by a pixel
+                       out->mins[j] = LittleFloat (in->mins[j]) - 1;
+                       out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
+                       out->origin[j] = LittleFloat (in->origin[j]);
+               }
+               for (j=0 ; j<MAX_MAP_HULLS ; j++)
+                       out->headnode[j] = LittleLong (in->headnode[j]);
+               out->visleafs = LittleLong (in->visleafs);
+               out->firstface = LittleLong (in->firstface);
+               out->numfaces = LittleLong (in->numfaces);
+       }
+}
+
+/*
+=================
+Mod_LoadEdges
+=================
+*/
+void Mod_LoadEdges (lump_t *l)
+{
+       dedge_t *in;
+       medge_t *out;
+       int     i, count;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname);   
+
+       loadmodel->edges = out;
+       loadmodel->numedges = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               out->v[0] = (unsigned short)LittleShort(in->v[0]);
+               out->v[1] = (unsigned short)LittleShort(in->v[1]);
+       }
+}
+
+/*
+=================
+Mod_LoadTexinfo
+=================
+*/
+void Mod_LoadTexinfo (lump_t *l)
+{
+       texinfo_t *in;
+       mtexinfo_t *out;
+       int     i, j, count;
+       int             miptex;
+       float   len1, len2;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->texinfo = out;
+       loadmodel->numtexinfo = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               for (j=0 ; j<8 ; j++)
+                       out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
+               len1 = Length (out->vecs[0]);
+               len2 = Length (out->vecs[1]);
+               len1 = (len1 + len2)/2;
+               if (len1 < 0.32)
+                       out->mipadjust = 4;
+               else if (len1 < 0.49)
+                       out->mipadjust = 3;
+               else if (len1 < 0.99)
+                       out->mipadjust = 2;
+               else
+                       out->mipadjust = 1;
+#if 0
+               if (len1 + len2 < 0.001)
+                       out->mipadjust = 1;             // don't crash
+               else
+                       out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 );
+#endif
+
+               miptex = LittleLong (in->miptex);
+               out->flags = LittleLong (in->flags);
+       
+               if (!loadmodel->textures)
+               {
+                       out->texture = r_notexture_mip; // checkerboard texture
+                       out->flags = 0;
+               }
+               else
+               {
+                       if (miptex >= loadmodel->numtextures)
+                               Sys_Error ("miptex >= loadmodel->numtextures");
+                       out->texture = loadmodel->textures[miptex];
+                       if (!out->texture)
+                       {
+                               out->texture = r_notexture_mip; // texture not found
+                               out->flags = 0;
+                       }
+                       else
+                               out->texture->transparent = FALSE;
+               }
+       }
+}
+
+/*
+================
+CalcSurfaceExtents
+
+Fills in s->texturemins[] and s->extents[]
+================
+*/
+void CalcSurfaceExtents (msurface_t *s)
+{
+       float   mins[2], maxs[2], val;
+       int             i,j, e;
+       mvertex_t       *v;
+       mtexinfo_t      *tex;
+       int             bmins[2], bmaxs[2];
+
+       mins[0] = mins[1] = 999999;
+       maxs[0] = maxs[1] = -99999;
+
+       tex = s->texinfo;
+       
+       for (i=0 ; i<s->numedges ; i++)
+       {
+               e = loadmodel->surfedges[s->firstedge+i];
+               if (e >= 0)
+                       v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
+               else
+                       v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
+               
+               for (j=0 ; j<2 ; j++)
+               {
+                       val = v->position[0] * tex->vecs[j][0] + 
+                               v->position[1] * tex->vecs[j][1] +
+                               v->position[2] * tex->vecs[j][2] +
+                               tex->vecs[j][3];
+                       if (val < mins[j])
+                               mins[j] = val;
+                       if (val > maxs[j])
+                               maxs[j] = val;
+               }
+       }
+
+       for (i=0 ; i<2 ; i++)
+       {       
+               bmins[i] = floor(mins[i]/16);
+               bmaxs[i] = ceil(maxs[i]/16);
+
+               s->texturemins[i] = bmins[i] * 16;
+               s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
+               if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ )
+                       Sys_Error ("Bad surface extents");
+       }
+}
+
+void GL_SubdivideSurface (msurface_t *fa);
+
+extern char skyname[];
+
+/*
+=================
+Mod_LoadFaces
+=================
+*/
+void Mod_LoadFaces (lump_t *l)
+{
+       dface_t         *in;
+       msurface_t      *out;
+       int                     i, count, surfnum;
+       int                     planenum, side;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->surfaces = out;
+       loadmodel->numsurfaces = count;
+
+       for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
+       {
+               out->firstedge = LittleLong(in->firstedge);
+               out->numedges = LittleShort(in->numedges);              
+               out->flags = 0;
+
+               planenum = LittleShort(in->planenum);
+               side = LittleShort(in->side);
+               if (side)
+                       out->flags |= SURF_PLANEBACK;                   
+
+               out->plane = loadmodel->planes + planenum;
+
+               out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
+
+               CalcSurfaceExtents (out);
+                               
+       // lighting info
+
+               for (i=0 ; i<MAXLIGHTMAPS ; i++)
+                       out->styles[i] = in->styles[i];
+               i = LittleLong(in->lightofs);
+               if (i == -1)
+                       out->samples = NULL;
+               else if (hlbsp) // LordHavoc: HalfLife map (bsp version 30)
+                       out->samples = loadmodel->lightdata + i;
+               else // LordHavoc: white lighting (bsp version 29)
+                       out->samples = loadmodel->lightdata + (i * 3); 
+               
+       // set the drawing flags flag
+               
+//             if (!strncmp(out->texinfo->texture->name,"sky",3))      // sky
+               // LordHavoc: faster check
+               if ((out->texinfo->texture->name[0] == 's' || out->texinfo->texture->name[0] == 'S')
+                && (out->texinfo->texture->name[1] == 'k' || out->texinfo->texture->name[1] == 'K')
+                && (out->texinfo->texture->name[2] == 'y' || out->texinfo->texture->name[2] == 'Y'))
+               {
+                       // LordHavoc: for consistency reasons, mark sky as fullbright and solid as well
+                       out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
+                       GL_SubdivideSurface (out);      // cut up polygon for warps
+                       continue;
+               }
+               
+//             if (!strncmp(out->texinfo->texture->name,"*",1))                // turbulent
+               if (out->texinfo->texture->name[0] == '*') // LordHavoc: faster check
+               {
+                       out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
+                       // LordHavoc: some turbulent textures should be fullbright and solid
+                       if (!strncmp(out->texinfo->texture->name,"*lava",5)
+                        || !strncmp(out->texinfo->texture->name,"*teleport",9)
+                        || !strncmp(out->texinfo->texture->name,"*rift",5)) // Scourge of Armagon texture
+                               out->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
+                       for (i=0 ; i<2 ; i++)
+                       {
+                               out->extents[i] = 16384;
+                               out->texturemins[i] = -8192;
+                       }
+                       GL_SubdivideSurface (out);      // cut up polygon for warps
+                       continue;
+               }
+
+       }
+}
+
+
+/*
+=================
+Mod_SetParent
+=================
+*/
+void Mod_SetParent (mnode_t *node, mnode_t *parent)
+{
+       node->parent = parent;
+       if (node->contents < 0)
+               return;
+       Mod_SetParent (node->children[0], node);
+       Mod_SetParent (node->children[1], node);
+}
+
+/*
+=================
+Mod_LoadNodes
+=================
+*/
+void Mod_LoadNodes (lump_t *l)
+{
+       int                     i, j, count, p;
+       dnode_t         *in;
+       mnode_t         *out;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->nodes = out;
+       loadmodel->numnodes = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       out->minmaxs[j] = LittleShort (in->mins[j]);
+                       out->minmaxs[3+j] = LittleShort (in->maxs[j]);
+               }
+       
+               p = LittleLong(in->planenum);
+               out->plane = loadmodel->planes + p;
+
+               out->firstsurface = LittleShort (in->firstface);
+               out->numsurfaces = LittleShort (in->numfaces);
+               
+               for (j=0 ; j<2 ; j++)
+               {
+                       p = LittleShort (in->children[j]);
+                       if (p >= 0)
+                               out->children[j] = loadmodel->nodes + p;
+                       else
+                               out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
+               }
+       }
+       
+       Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
+}
+
+/*
+=================
+Mod_LoadLeafs
+=================
+*/
+void Mod_LoadLeafs (lump_t *l)
+{
+       dleaf_t         *in;
+       mleaf_t         *out;
+       int                     i, j, count, p;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->leafs = out;
+       loadmodel->numleafs = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       out->minmaxs[j] = LittleShort (in->mins[j]);
+                       out->minmaxs[3+j] = LittleShort (in->maxs[j]);
+               }
+
+               p = LittleLong(in->contents);
+               out->contents = p;
+
+               out->firstmarksurface = loadmodel->marksurfaces +
+                       LittleShort(in->firstmarksurface);
+               out->nummarksurfaces = LittleShort(in->nummarksurfaces);
+               
+               p = LittleLong(in->visofs);
+               if (p == -1)
+                       out->compressed_vis = NULL;
+               else
+                       out->compressed_vis = loadmodel->visdata + p;
+               out->efrags = NULL;
+               
+               for (j=0 ; j<4 ; j++)
+                       out->ambient_sound_level[j] = in->ambient_level[j];
+
+               // gl underwater warp
+               // LordHavoc: disabled underwater warping
+               /*
+               if (out->contents != CONTENTS_EMPTY)
+               {
+                       for (j=0 ; j<out->nummarksurfaces ; j++)
+                               out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
+               }
+               */
+       }       
+}
+
+/*
+=================
+Mod_LoadClipnodes
+=================
+*/
+void Mod_LoadClipnodes (lump_t *l)
+{
+       dclipnode_t *in, *out;
+       int                     i, count;
+       hull_t          *hull;
+
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->clipnodes = out;
+       loadmodel->numclipnodes = count;
+
+       hull = &loadmodel->hulls[1];
+       hull->clipnodes = out;
+       hull->firstclipnode = 0;
+       hull->lastclipnode = count-1;
+       hull->planes = loadmodel->planes;
+       hull->clip_mins[0] = -16;
+       hull->clip_mins[1] = -16;
+       hull->clip_mins[2] = -24;
+       hull->clip_maxs[0] = 16;
+       hull->clip_maxs[1] = 16;
+       hull->clip_maxs[2] = 32;
+
+       hull = &loadmodel->hulls[2];
+       hull->clipnodes = out;
+       hull->firstclipnode = 0;
+       hull->lastclipnode = count-1;
+       hull->planes = loadmodel->planes;
+       hull->clip_mins[0] = -32;
+       hull->clip_mins[1] = -32;
+       hull->clip_mins[2] = -24;
+       hull->clip_maxs[0] = 32;
+       hull->clip_maxs[1] = 32;
+       hull->clip_maxs[2] = 64;
+
+       for (i=0 ; i<count ; i++, out++, in++)
+       {
+               out->planenum = LittleLong(in->planenum);
+               out->children[0] = LittleShort(in->children[0]);
+               out->children[1] = LittleShort(in->children[1]);
+       }
+}
+
+/*
+=================
+Mod_MakeHull0
+
+Duplicate the drawing hull structure as a clipping hull
+=================
+*/
+void Mod_MakeHull0 (void)
+{
+       mnode_t         *in, *child;
+       dclipnode_t *out;
+       int                     i, j, count;
+       hull_t          *hull;
+       
+       hull = &loadmodel->hulls[0];    
+       
+       in = loadmodel->nodes;
+       count = loadmodel->numnodes;
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       hull->clipnodes = out;
+       hull->firstclipnode = 0;
+       hull->lastclipnode = count-1;
+       hull->planes = loadmodel->planes;
+
+       for (i=0 ; i<count ; i++, out++, in++)
+       {
+               out->planenum = in->plane - loadmodel->planes;
+               for (j=0 ; j<2 ; j++)
+               {
+                       child = in->children[j];
+                       if (child->contents < 0)
+                               out->children[j] = child->contents;
+                       else
+                               out->children[j] = child - loadmodel->nodes;
+               }
+       }
+}
+
+/*
+=================
+Mod_LoadMarksurfaces
+=================
+*/
+void Mod_LoadMarksurfaces (lump_t *l)
+{      
+       int             i, j, count;
+       short           *in;
+       msurface_t **out;
+       
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->marksurfaces = out;
+       loadmodel->nummarksurfaces = count;
+
+       for ( i=0 ; i<count ; i++)
+       {
+               j = LittleShort(in[i]);
+               if (j >= loadmodel->numsurfaces)
+                       Sys_Error ("Mod_ParseMarksurfaces: bad surface number");
+               out[i] = loadmodel->surfaces + j;
+       }
+}
+
+/*
+=================
+Mod_LoadSurfedges
+=================
+*/
+void Mod_LoadSurfedges (lump_t *l)
+{      
+       int             i, count;
+       int             *in, *out;
+       
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*sizeof(*out), loadname);   
+
+       loadmodel->surfedges = out;
+       loadmodel->numsurfedges = count;
+
+       for ( i=0 ; i<count ; i++)
+               out[i] = LittleLong (in[i]);
+}
+
+
+/*
+=================
+Mod_LoadPlanes
+=================
+*/
+void Mod_LoadPlanes (lump_t *l)
+{
+       int                     i, j;
+       mplane_t        *out;
+       dplane_t        *in;
+       int                     count;
+       int                     bits;
+       
+       in = (void *)(mod_base + l->fileofs);
+       if (l->filelen % sizeof(*in))
+               Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+       count = l->filelen / sizeof(*in);
+       out = Hunk_AllocName ( count*2*sizeof(*out), loadname); 
+
+       loadmodel->planes = out;
+       loadmodel->numplanes = count;
+
+       for ( i=0 ; i<count ; i++, in++, out++)
+       {
+               bits = 0;
+               for (j=0 ; j<3 ; j++)
+               {
+                       out->normal[j] = LittleFloat (in->normal[j]);
+//                     if (out->normal[j] < 0)
+//                             bits |= 1<<j;
+               }
+
+               out->dist = LittleFloat (in->dist);
+               out->type = LittleLong (in->type);
+//             out->signbits = bits;
+               BoxOnPlaneSideClassify(out);
+       }
+}
+
+/*
+=================
+Mod_LoadBrushModel
+=================
+*/
+void Mod_LoadBrushModel (model_t *mod, void *buffer)
+{
+       int                     i, j;
+       dheader_t       *header;
+       dmodel_t        *bm;
+       
+       loadmodel->type = mod_brush;
+       
+       header = (dheader_t *)buffer;
+
+       i = LittleLong (header->version);
+       if (i != BSPVERSION & i != 30)
+               Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
+       hlbsp = i == 30;
+       halflifebsp.value = hlbsp;
+
+// swap all the lumps
+       mod_base = (byte *)header;
+
+       for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
+               ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+// load into heap
+       
+       // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
+       Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
+
+       Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
+       Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
+       Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
+       Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
+       Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
+       Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
+       Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
+       Mod_LoadFaces (&header->lumps[LUMP_FACES]);
+       Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
+       Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
+       Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
+       Mod_LoadNodes (&header->lumps[LUMP_NODES]);
+       Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
+//     Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
+       Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
+
+       Mod_MakeHull0 ();
+       
+       mod->numframes = 2;             // regular and alternate animation
+       
+//
+// set up the submodels (FIXME: this is confusing)
+//
+       for (i=0 ; i<mod->numsubmodels ; i++)
+       {
+               bm = &mod->submodels[i];
+
+               mod->hulls[0].firstclipnode = bm->headnode[0];
+               for (j=1 ; j<MAX_MAP_HULLS ; j++)
+               {
+                       mod->hulls[j].firstclipnode = bm->headnode[j];
+                       mod->hulls[j].lastclipnode = mod->numclipnodes-1;
+               }
+               
+               mod->firstmodelsurface = bm->firstface;
+               mod->nummodelsurfaces = bm->numfaces;
+               
+               VectorCopy (bm->maxs, mod->maxs);
+               VectorCopy (bm->mins, mod->mins);
+
+               mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
+
+               mod->numleafs = bm->visleafs;
+
+               if (isworldmodel && i < (mod->numsubmodels-1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels)
+               {       // duplicate the basic information
+                       char    name[10];
+
+                       sprintf (name, "*%i", i+1);
+                       loadmodel = Mod_FindName (name);
+                       *loadmodel = *mod;
+                       strcpy (loadmodel->name, name);
+                       mod = loadmodel;
+               }
+       }
+}
diff --git a/model_brush.h b/model_brush.h
new file mode 100644 (file)
index 0000000..625da01
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+/*
+==============================================================================
+
+BRUSH MODELS
+
+==============================================================================
+*/
+
+
+//
+// in memory representation
+//
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct
+{
+       vec3_t          position;
+} mvertex_t;
+
+#define        SIDE_FRONT      0
+#define        SIDE_BACK       1
+#define        SIDE_ON         2
+
+
+// plane_t structure
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct mplane_s
+{
+       vec3_t  normal;
+       float   dist;
+       byte    type;                   // for texture axis selection and fast side tests
+//     byte    signbits;               // signx + signy<<1 + signz<<2
+//     byte    pad[2];
+       byte    pad[3];
+       int (*BoxOnPlaneSideFunc) (vec3_t emins, vec3_t emaxs, struct mplane_s *p);
+} mplane_t;
+
+typedef struct texture_s
+{
+       char            name[16];
+       unsigned        width, height;
+       int                     gl_texturenum;
+       int                     gl_glowtexturenum; // LordHavoc: fullbrights on walls
+       struct msurface_s       *texturechain;  // for gl_texsort drawing
+       int                     anim_total;                             // total tenths in sequence ( 0 = no)
+       int                     anim_min, anim_max;             // time for this frame min <=time< max
+       struct texture_s *anim_next;            // in the animation sequence
+       struct texture_s *alternate_anims;      // bmodels in frame 1 use these
+       unsigned        offsets[MIPLEVELS];             // four mip maps stored
+       int                     transparent;    // LordHavoc: transparent texture support
+} texture_t;
+
+
+#define        SURF_PLANEBACK          2
+#define        SURF_DRAWSKY            4
+#define SURF_DRAWSPRITE                8
+#define SURF_DRAWTURB          0x10
+#define SURF_DRAWTILED         0x20
+#define SURF_DRAWBACKGROUND    0x40
+//#define SURF_UNDERWATER              0x80
+// LordHavoc: added these for lava and teleport textures
+#define SURF_DRAWNOALPHA       0x100
+#define SURF_DRAWFULLBRIGHT    0x200
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct
+{
+       unsigned short  v[2];
+       unsigned int    cachededgeoffset;
+} medge_t;
+
+typedef struct
+{
+       float           vecs[2][4];
+       float           mipadjust;
+       texture_t       *texture;
+       int                     flags;
+} mtexinfo_t;
+
+// LordHavoc: was 7, I added two more for raw lightmap coordinates
+#define        VERTEXSIZE      9
+
+typedef struct glpoly_s
+{
+       struct  glpoly_s        *next;
+       struct  glpoly_s        *chain;
+       int             numverts;
+       int             flags;                  // for SURF_UNDERWATER
+       float   verts[4][VERTEXSIZE];   // variable sized (xyz s1t1 s2t2)
+} glpoly_t;
+
+typedef struct msurface_s
+{
+       int                     visframe;               // should be drawn when node is crossed
+
+       mplane_t        *plane;
+       int                     flags;
+
+       int                     firstedge;      // look up in model->surfedges[], negative numbers
+       int                     numedges;       // are backwards edges
+       
+       short           texturemins[2];
+       short           extents[2];
+
+       int                     light_s, light_t;       // gl lightmap coordinates
+
+       glpoly_t        *polys;                         // multiple if warped
+       struct  msurface_s      *texturechain;
+
+       mtexinfo_t      *texinfo;
+       
+// lighting info
+       int                     dlightframe;
+       int                     dlightbits[8];
+
+       int                     lightmaptexturenum;
+       byte            styles[MAXLIGHTMAPS];
+       int                     cached_light[MAXLIGHTMAPS];     // values currently used in lightmap
+       qboolean        cached_dlight;                          // true if dynamic light in cache
+       qboolean        cached_lighthalf;                       // LordHavoc: to cause lightmap to be rerendered when lighthalf changes
+       byte            *samples;               // [numstyles*surfsize]
+} msurface_t;
+
+typedef struct mnode_s
+{
+// common with leaf
+       int                     contents;               // 0, to differentiate from leafs
+       int                     visframe;               // node needs to be traversed if current
+       
+       float           minmaxs[6];             // for bounding box culling
+
+       struct mnode_s  *parent;
+
+// node specific
+       mplane_t        *plane;
+       struct mnode_s  *children[2];   
+
+       unsigned short          firstsurface;
+       unsigned short          numsurfaces;
+} mnode_t;
+
+
+
+typedef struct mleaf_s
+{
+// common with node
+       int                     contents;               // wil be a negative contents number
+       int                     visframe;               // node needs to be traversed if current
+
+       float           minmaxs[6];             // for bounding box culling
+
+       struct mnode_s  *parent;
+
+// leaf specific
+       byte            *compressed_vis;
+       efrag_t         *efrags;
+
+       msurface_t      **firstmarksurface;
+       int                     nummarksurfaces;
+       int                     key;                    // BSP sequence number for leaf's contents
+       byte            ambient_sound_level[NUM_AMBIENTS];
+} mleaf_t;
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+       dclipnode_t     *clipnodes;
+       mplane_t        *planes;
+       int                     firstclipnode;
+       int                     lastclipnode;
+       vec3_t          clip_mins;
+       vec3_t          clip_maxs;
+} hull_t;
diff --git a/model_shared.c b/model_shared.c
new file mode 100644 (file)
index 0000000..9d16ee2
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// models.c -- model loading and caching
+
+// models are the only shared resource between a client and server running
+// on the same machine.
+
+#include "quakedef.h"
+
+model_t        *loadmodel;
+char   loadname[32];   // for hunk tags
+
+void Mod_LoadSpriteModel (model_t *mod, void *buffer);
+void Mod_LoadBrushModel (model_t *mod, void *buffer);
+void Mod_LoadAliasModel (model_t *mod, void *buffer);
+void Mod_LoadQ2AliasModel (model_t *mod, void *buffer);
+model_t *Mod_LoadModel (model_t *mod, qboolean crash);
+
+#define        MAX_MOD_KNOWN   512
+model_t        mod_known[MAX_MOD_KNOWN];
+int            mod_numknown;
+
+extern void Mod_BrushInit();
+extern void Mod_AliasInit();
+extern void Mod_SpriteInit();
+
+/*
+===============
+Mod_Init
+===============
+*/
+void Mod_Init (void)
+{
+       Mod_BrushInit();
+       Mod_AliasInit();
+       Mod_SpriteInit();
+}
+
+/*
+===============
+Mod_Init
+
+Caches the data if needed
+===============
+*/
+void *Mod_Extradata (model_t *mod)
+{
+       void    *r;
+       
+       r = Cache_Check (&mod->cache);
+       if (r)
+               return r;
+
+       Mod_LoadModel (mod, true);
+       
+       if (!mod->cache.data)
+               Sys_Error ("Mod_Extradata: caching failed");
+       return mod->cache.data;
+}
+
+/*
+===================
+Mod_ClearAll
+===================
+*/
+void Mod_ClearAll (void)
+{
+       int             i;
+       model_t *mod;
+       
+       for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
+               if (mod->type != mod_alias)
+                       mod->needload = true;
+}
+
+/*
+==================
+Mod_FindName
+
+==================
+*/
+model_t *Mod_FindName (char *name)
+{
+       int             i;
+       model_t *mod;
+       
+       if (!name[0])
+               Sys_Error ("Mod_ForName: NULL name");
+               
+//
+// search the currently loaded models
+//
+       for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
+               if (!strcmp (mod->name, name) )
+                       break;
+                       
+       if (i == mod_numknown)
+       {
+               if (mod_numknown == MAX_MOD_KNOWN)
+                       Sys_Error ("mod_numknown == MAX_MOD_KNOWN");
+               strcpy (mod->name, name);
+               mod->needload = true;
+               mod_numknown++;
+       }
+
+       return mod;
+}
+
+/*
+==================
+Mod_TouchModel
+
+==================
+*/
+void Mod_TouchModel (char *name)
+{
+       model_t *mod;
+       
+       mod = Mod_FindName (name);
+       
+       if (!mod->needload)
+       {
+               if (mod->type == mod_alias)
+                       Cache_Check (&mod->cache);
+       }
+}
+
+/*
+==================
+Mod_LoadModel
+
+Loads a model into the cache
+==================
+*/
+model_t *Mod_LoadModel (model_t *mod, qboolean crash)
+{
+       void    *d;
+       unsigned *buf;
+       byte    stackbuf[1024];         // avoid dirtying the cache heap
+
+       if (!mod->needload)
+       {
+               if (mod->type == mod_alias)
+               {
+                       d = Cache_Check (&mod->cache);
+                       if (d)
+                               return mod;
+               }
+               else
+                       return mod;             // not cached at all
+       }
+
+// load the file
+       buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), false);
+       if (!buf)
+       {
+               if (crash)
+                       Host_Error ("Mod_NumForName: %s not found", mod->name); // LordHavoc: Sys_Error was *ANNOYING*
+               return NULL;
+       }
+       
+// allocate a new model
+       COM_FileBase (mod->name, loadname);
+       
+       loadmodel = mod;
+
+// call the apropriate loader
+       mod->needload = false;
+       
+       switch (LittleLong(*(unsigned *)buf))
+       {
+       case IDPOLYHEADER:
+               Mod_LoadAliasModel (mod, buf);
+               break;
+
+       case MD2IDALIASHEADER: // LordHavoc: added Quake2 model support
+               Mod_LoadQ2AliasModel (mod, buf);
+               break;
+               
+       case IDSPRITEHEADER:
+               Mod_LoadSpriteModel (mod, buf);
+               break;
+       
+       default:
+               Mod_LoadBrushModel (mod, buf);
+               break;
+       }
+
+       return mod;
+}
+
+/*
+==================
+Mod_ForName
+
+Loads in a model for the given name
+==================
+*/
+model_t *Mod_ForName (char *name, qboolean crash)
+{
+       model_t *mod;
+       
+       mod = Mod_FindName (name);
+       
+       return Mod_LoadModel (mod, crash);
+}
+
+byte   *mod_base;
+
+/*
+=================
+RadiusFromBounds
+=================
+*/
+float RadiusFromBounds (vec3_t mins, vec3_t maxs)
+{
+       int             i;
+       vec3_t  corner;
+
+       for (i=0 ; i<3 ; i++)
+               corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
+
+       return Length (corner);
+}
+
+//=============================================================================
+
+/*
+================
+Mod_Print
+================
+*/
+void Mod_Print (void)
+{
+       int             i;
+       model_t *mod;
+
+       Con_Printf ("Cached models:\n");
+       for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
+       {
+               Con_Printf ("%8p : %s\n",mod->cache.data, mod->name);
+       }
+}
+
+
diff --git a/model_shared.h b/model_shared.h
new file mode 100644 (file)
index 0000000..865ef39
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+#ifndef __MODEL__
+#define __MODEL__
+
+#ifndef SYNCTYPE_T
+#define SYNCTYPE_T
+typedef enum {ST_SYNC=0, ST_RAND } synctype_t;
+#endif
+
+/*
+
+d*_t structures are on-disk representations
+m*_t structures are in-memory
+
+*/
+
+typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t;
+
+#include "model_brush.h"
+#include "model_sprite.h"
+#include "model_alias.h"
+
+typedef struct model_s
+{
+       char            name[MAX_QPATH];
+       qboolean        needload;               // bmodels and sprites don't cache normally
+
+       modtype_t       type;
+       int                     aliastype; // LordHavoc: Q2 model support
+       int                     fullbright; // LordHavoc: if true (normally only for sprites) the model/sprite/bmodel is always rendered fullbright
+       int                     numframes;
+       synctype_t      synctype;
+       
+       int                     flags;
+
+// volume occupied by the model graphics
+       vec3_t          mins, maxs;
+       float           radius;
+
+// solid volume for clipping 
+       qboolean        clipbox;
+       vec3_t          clipmins, clipmaxs;
+
+// brush model
+       int                     firstmodelsurface, nummodelsurfaces;
+
+       int                     numsubmodels;
+       dmodel_t        *submodels;
+
+       int                     numplanes;
+       mplane_t        *planes;
+
+       int                     numleafs;               // number of visible leafs, not counting 0
+       mleaf_t         *leafs;
+
+       int                     numvertexes;
+       mvertex_t       *vertexes;
+
+       int                     numedges;
+       medge_t         *edges;
+
+       int                     numnodes;
+       mnode_t         *nodes;
+
+       int                     numtexinfo;
+       mtexinfo_t      *texinfo;
+
+       int                     numsurfaces;
+       msurface_t      *surfaces;
+
+       int                     numsurfedges;
+       int                     *surfedges;
+
+       int                     numclipnodes;
+       dclipnode_t     *clipnodes;
+
+       int                     nummarksurfaces;
+       msurface_t      **marksurfaces;
+
+       hull_t          hulls[MAX_MAP_HULLS];
+
+       int                     numtextures;
+       texture_t       **textures;
+
+       byte            *visdata;
+       byte            *lightdata;
+       char            *entities;
+
+// additional model data
+       cache_user_t    cache;          // only access through Mod_Extradata
+
+} model_t;
+
+//============================================================================
+
+void   Mod_Init (void);
+void   Mod_ClearAll (void);
+model_t *Mod_ForName (char *name, qboolean crash);
+void   *Mod_Extradata (model_t *mod);  // handles caching
+void   Mod_TouchModel (char *name);
+
+mleaf_t *Mod_PointInLeaf (float *p, model_t *model);
+byte   *Mod_LeafPVS (mleaf_t *leaf, model_t *model);
+
+extern model_t *loadmodel;
+extern char    loadname[32];   // for hunk tags
+
+extern model_t *Mod_LoadModel (model_t *mod, qboolean crash);
+
+extern float RadiusFromBounds (vec3_t mins, vec3_t maxs);
+extern model_t *Mod_FindName (char *name);
+#endif // __MODEL__
diff --git a/model_sprite.c b/model_sprite.c
new file mode 100644 (file)
index 0000000..6e2d784
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// models.c -- model loading and caching
+
+// models are the only shared resource between a client and server running
+// on the same machine.
+
+#include "quakedef.h"
+
+/*
+===============
+Mod_SpriteInit
+===============
+*/
+void Mod_SpriteInit (void)
+{
+}
+
+/*
+=================
+Mod_LoadSpriteFrame
+=================
+*/
+void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum, int bytesperpixel)
+{
+       dspriteframe_t          *pinframe;
+       mspriteframe_t          *pspriteframe;
+       int                                     i, width, height, size, origin[2];
+       char                            name[64];
+       byte                            *pixbuf, *pixel, *inpixel;
+
+       pinframe = (dspriteframe_t *)pin;
+
+       width = LittleLong (pinframe->width);
+       height = LittleLong (pinframe->height);
+       size = width * height * bytesperpixel;
+
+       pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname);
+
+       memset (pspriteframe, 0, sizeof (mspriteframe_t));
+
+       *ppframe = pspriteframe;
+
+       pspriteframe->width = width;
+       pspriteframe->height = height;
+       origin[0] = LittleLong (pinframe->origin[0]);
+       origin[1] = LittleLong (pinframe->origin[1]);
+
+       pspriteframe->up = origin[1];
+       pspriteframe->down = origin[1] - height;
+       pspriteframe->left = origin[0];
+       pspriteframe->right = width + origin[0];
+
+       sprintf (name, "%s_%i", loadmodel->name, framenum);
+       pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, bytesperpixel);
+       // make fog version (just alpha)
+       pixbuf = pixel = malloc(width*height*4);
+       inpixel = (byte *)(pinframe + 1);
+       if (bytesperpixel == 1)
+       {
+               for (i = 0;i < width*height;i++)
+               {
+                       *pixel++ = 255;
+                       *pixel++ = 255;
+                       *pixel++ = 255;
+                       if (*inpixel++ != 255)
+                               *pixel++ = 255;
+                       else
+                               *pixel++ = 0;
+               }
+       }
+       else
+       {
+               inpixel+=3;
+               for (i = 0;i < width*height;i++)
+               {
+                       *pixel++ = 255;
+                       *pixel++ = 255;
+                       *pixel++ = 255;
+                       *pixel++ = *inpixel;
+                       inpixel+=4;
+               }
+       }
+       sprintf (name, "%s_%ifog", loadmodel->name, framenum);
+       pspriteframe->gl_fogtexturenum = GL_LoadTexture (name, width, height, pixbuf, true, true, 4);
+       free(pixbuf);
+
+       return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size);
+}
+
+
+/*
+=================
+Mod_LoadSpriteGroup
+=================
+*/
+void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum, int bytesperpixel)
+{
+       dspritegroup_t          *pingroup;
+       mspritegroup_t          *pspritegroup;
+       int                                     i, numframes;
+       dspriteinterval_t       *pin_intervals;
+       float                           *poutintervals;
+       void                            *ptemp;
+
+       pingroup = (dspritegroup_t *)pin;
+
+       numframes = LittleLong (pingroup->numframes);
+
+       pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) +
+                               (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname);
+
+       pspritegroup->numframes = numframes;
+
+       *ppframe = (mspriteframe_t *)pspritegroup;
+
+       pin_intervals = (dspriteinterval_t *)(pingroup + 1);
+
+       poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname);
+
+       pspritegroup->intervals = poutintervals;
+
+       for (i=0 ; i<numframes ; i++)
+       {
+               *poutintervals = LittleFloat (pin_intervals->interval);
+               if (*poutintervals <= 0.0)
+                       Host_Error ("Mod_LoadSpriteGroup: interval<=0");
+
+               poutintervals++;
+               pin_intervals++;
+       }
+
+       ptemp = (void *)pin_intervals;
+
+       for (i=0 ; i<numframes ; i++)
+       {
+               ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], framenum * 100 + i, bytesperpixel);
+       }
+
+       return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadSpriteModel
+=================
+*/
+void Mod_LoadSpriteModel (model_t *mod, void *buffer)
+{
+       int                                     i;
+       int                                     version;
+       dsprite_t                       *pin;
+       msprite_t                       *psprite;
+       int                                     numframes;
+       int                                     size;
+       dspriteframetype_t      *pframetype;
+       // LordHavoc: 32bit textures
+       int             bytesperpixel;
+
+       mod->flags = EF_FULLBRIGHT;
+       // LordHavoc: hack to allow sprites to be non-fullbright
+       for (i = 0;i < MAX_QPATH && mod->name[i];i++)
+       {
+               if (mod->name[i] == '!')
+               {
+                       mod->flags &= ~EF_FULLBRIGHT;
+                       break;
+               }
+       }
+
+       pin = (dsprite_t *)buffer;
+
+       version = LittleLong (pin->version);
+       if (version == 2)
+       {
+               version = 32;
+               Con_Printf("warning: %s is a version 2 sprite (RGBA), supported for now, please hex edit to version 32 incase HalfLife sprites might be supported at some point.\n", mod->name);
+       }
+       // LordHavoc: 32bit textures
+       if (version != SPRITE_VERSION && version != SPRITE32_VERSION)
+               Host_Error ("%s has wrong version number "
+                                "(%i should be %i or %i)", mod->name, version, SPRITE_VERSION, SPRITE32_VERSION);
+       bytesperpixel = 1;
+       if (version == SPRITE32_VERSION)
+               bytesperpixel = 4;
+
+       numframes = LittleLong (pin->numframes);
+
+       size = sizeof (msprite_t) +     (numframes - 1) * sizeof (psprite->frames);
+
+       psprite = Hunk_AllocName (size, loadname);
+
+       mod->cache.data = psprite;
+
+       psprite->type = LittleLong (pin->type);
+       psprite->maxwidth = LittleLong (pin->width);
+       psprite->maxheight = LittleLong (pin->height);
+       psprite->beamlength = LittleFloat (pin->beamlength);
+       mod->synctype = LittleLong (pin->synctype);
+       psprite->numframes = numframes;
+
+       mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2;
+       mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2;
+       mod->mins[2] = -psprite->maxheight/2;
+       mod->maxs[2] = psprite->maxheight/2;
+       
+//
+// load the frames
+//
+       if (numframes < 1)
+               Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
+
+       mod->numframes = numframes;
+
+       pframetype = (dspriteframetype_t *)(pin + 1);
+
+       for (i=0 ; i<numframes ; i++)
+       {
+               spriteframetype_t       frametype;
+
+               frametype = LittleLong (pframetype->type);
+               psprite->frames[i].type = frametype;
+
+               if (frametype == SPR_SINGLE)
+               {
+                       pframetype = (dspriteframetype_t *)
+                                       Mod_LoadSpriteFrame (pframetype + 1,
+                                                                                &psprite->frames[i].frameptr, i, bytesperpixel);
+               }
+               else
+               {
+                       pframetype = (dspriteframetype_t *)
+                                       Mod_LoadSpriteGroup (pframetype + 1,
+                                                                                &psprite->frames[i].frameptr, i, bytesperpixel);
+               }
+       }
+
+       mod->type = mod_sprite;
+}
diff --git a/model_sprite.h b/model_sprite.h
new file mode 100644 (file)
index 0000000..c07c888
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+/*
+==============================================================================
+
+SPRITE MODELS
+
+==============================================================================
+*/
+
+#include "spritegn.h"
+
+// FIXME: shorten these?
+typedef struct mspriteframe_s
+{
+       int             width;
+       int             height;
+       float   up, down, left, right;
+       int             gl_texturenum, gl_fogtexturenum;
+} mspriteframe_t;
+
+typedef struct
+{
+       int                             numframes;
+       float                   *intervals;
+       mspriteframe_t  *frames[1];
+} mspritegroup_t;
+
+typedef struct
+{
+       spriteframetype_t       type;
+       mspriteframe_t          *frameptr;
+} mspriteframedesc_t;
+
+typedef struct
+{
+       int                                     type;
+       int                                     maxwidth;
+       int                                     maxheight;
+       int                                     numframes;
+       float                           beamlength;             // remove?
+       void                            *cachespot;             // remove?
+       mspriteframedesc_t      frames[1];
+} msprite_t;
diff --git a/modelgen.h b/modelgen.h
new file mode 100644 (file)
index 0000000..b8b75dd
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// modelgen.h: header file for model generation program
+//
+
+// *********************************************************
+// * This file must be identical in the modelgen directory *
+// * and in the Quake directory, because it's used to      *
+// * pass data from one to the other via model files.      *
+// *********************************************************
+
+#define ALIAS_VERSION  6
+#define ALIAS32_VERSION        7
+
+#define ALIAS_ONSEAM                           0x0020
+
+typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t;
+
+typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t;
+
+typedef struct {
+       int                     ident;
+       int                     version;
+       vec3_t          scale;
+       vec3_t          scale_origin;
+       float           boundingradius;
+       vec3_t          eyeposition;
+       int                     numskins;
+       int                     skinwidth;
+       int                     skinheight;
+       int                     numverts;
+       int                     numtris;
+       int                     numframes;
+       synctype_t      synctype;
+       int                     flags;
+       float           size;
+} mdl_t;
+
+// TODO: could be shorts
+
+typedef struct {
+       int             onseam;
+       int             s;
+       int             t;
+} stvert_t;
+
+typedef struct dtriangle_s {
+       int                                     facesfront;
+       int                                     vertindex[3];
+} dtriangle_t;
+
+#define DT_FACES_FRONT                         0x0010
+
+// This mirrors trivert_t in trilib.h, is present so Quake knows how to
+// load this data
+
+typedef struct {
+       byte    v[3];
+       byte    lightnormalindex;
+} trivertx_t;
+
+typedef struct {
+       trivertx_t      bboxmin;        // lightnormal isn't used
+       trivertx_t      bboxmax;        // lightnormal isn't used
+       char            name[16];       // frame name from grabbing
+} daliasframe_t;
+
+typedef struct {
+       int                     numframes;
+       trivertx_t      bboxmin;        // lightnormal isn't used
+       trivertx_t      bboxmax;        // lightnormal isn't used
+} daliasgroup_t;
+
+typedef struct {
+       int                     numskins;
+} daliasskingroup_t;
+
+typedef struct {
+       float   interval;
+} daliasinterval_t;
+
+typedef struct {
+       float   interval;
+} daliasskininterval_t;
+
+typedef struct {
+       aliasframetype_t        type;
+} daliasframetype_t;
+
+typedef struct {
+       aliasskintype_t type;
+} daliasskintype_t;
+
+#define IDPOLYHEADER   (('O'<<24)+('P'<<16)+('D'<<8)+'I')
+                                                                                                               // little-endian "IDPO"
+
diff --git a/net.h b/net.h
new file mode 100644 (file)
index 0000000..af46125
--- /dev/null
+++ b/net.h
@@ -0,0 +1,337 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net.h -- quake's interface to the networking layer
+
+struct qsockaddr
+{
+       short sa_family;
+       unsigned char sa_data[14];
+};
+
+
+#define        NET_NAMELEN                     64
+
+#define NET_MAXMESSAGE         8192
+#define NET_HEADERSIZE         (2 * sizeof(unsigned int))
+#define NET_DATAGRAMSIZE       (MAX_DATAGRAM + NET_HEADERSIZE)
+
+// NetHeader flags
+#define NETFLAG_LENGTH_MASK    0x0000ffff
+#define NETFLAG_DATA           0x00010000
+#define NETFLAG_ACK                    0x00020000
+#define NETFLAG_NAK                    0x00040000
+#define NETFLAG_EOM                    0x00080000
+#define NETFLAG_UNRELIABLE     0x00100000
+#define NETFLAG_CTL                    0x80000000
+
+
+#define NET_PROTOCOL_VERSION   3
+
+// This is the network info/connection protocol.  It is used to find Quake
+// servers, get info about them, and connect to them.  Once connected, the
+// Quake game protocol (documented elsewhere) is used.
+//
+//
+// General notes:
+//     game_name is currently always "QUAKE", but is there so this same protocol
+//             can be used for future games as well; can you say Quake2?
+//
+// CCREQ_CONNECT
+//             string  game_name                               "QUAKE"
+//             byte    net_protocol_version    NET_PROTOCOL_VERSION
+//
+// CCREQ_SERVER_INFO
+//             string  game_name                               "QUAKE"
+//             byte    net_protocol_version    NET_PROTOCOL_VERSION
+//
+// CCREQ_PLAYER_INFO
+//             byte    player_number
+//
+// CCREQ_RULE_INFO
+//             string  rule
+//
+//
+//
+// CCREP_ACCEPT
+//             long    port
+//
+// CCREP_REJECT
+//             string  reason
+//
+// CCREP_SERVER_INFO
+//             string  server_address
+//             string  host_name
+//             string  level_name
+//             byte    current_players
+//             byte    max_players
+//             byte    protocol_version        NET_PROTOCOL_VERSION
+//
+// CCREP_PLAYER_INFO
+//             byte    player_number
+//             string  name
+//             long    colors
+//             long    frags
+//             long    connect_time
+//             string  address
+//
+// CCREP_RULE_INFO
+//             string  rule
+//             string  value
+
+//     note:
+//             There are two address forms used above.  The short form is just a
+//             port number.  The address that goes along with the port is defined as
+//             "whatever address you receive this reponse from".  This lets us use
+//             the host OS to solve the problem of multiple host addresses (possibly
+//             with no routing between them); the host will use the right address
+//             when we reply to the inbound connection request.  The long from is
+//             a full address and port in a string.  It is used for returning the
+//             address of a server that is not running locally.
+
+#define CCREQ_CONNECT          0x01
+#define CCREQ_SERVER_INFO      0x02
+#define CCREQ_PLAYER_INFO      0x03
+#define CCREQ_RULE_INFO                0x04
+
+#define CCREP_ACCEPT           0x81
+#define CCREP_REJECT           0x82
+#define CCREP_SERVER_INFO      0x83
+#define CCREP_PLAYER_INFO      0x84
+#define CCREP_RULE_INFO                0x85
+
+typedef struct qsocket_s
+{
+       struct qsocket_s        *next;
+       double                  connecttime;
+       double                  lastMessageTime;
+       double                  lastSendTime;
+
+       qboolean                disconnected;
+       qboolean                canSend;
+       qboolean                sendNext;
+       
+       int                             driver;
+       int                             landriver;
+       int                             socket;
+       void                    *driverdata;
+
+       unsigned int    ackSequence;
+       unsigned int    sendSequence;
+       unsigned int    unreliableSendSequence;
+       int                             sendMessageLength;
+       byte                    sendMessage [NET_MAXMESSAGE];
+
+       unsigned int    receiveSequence;
+       unsigned int    unreliableReceiveSequence;
+       int                             receiveMessageLength;
+       byte                    receiveMessage [NET_MAXMESSAGE];
+
+       struct qsockaddr        addr;
+       char                            address[NET_NAMELEN];
+
+} qsocket_t;
+
+extern qsocket_t       *net_activeSockets;
+extern qsocket_t       *net_freeSockets;
+extern int                     net_numsockets;
+
+typedef struct
+{
+       char            *name;
+       qboolean        initialized;
+       int                     controlSock;
+       int                     (*Init) (void);
+       void            (*Shutdown) (void);
+       void            (*Listen) (qboolean state);
+       int             (*OpenSocket) (int port);
+       int             (*CloseSocket) (int socket);
+       int             (*Connect) (int socket, struct qsockaddr *addr);
+       int             (*CheckNewConnections) (void);
+       int             (*Read) (int socket, byte *buf, int len, struct qsockaddr *addr);
+       int             (*Write) (int socket, byte *buf, int len, struct qsockaddr *addr);
+       int             (*Broadcast) (int socket, byte *buf, int len);
+       char *          (*AddrToString) (struct qsockaddr *addr);
+       int             (*StringToAddr) (char *string, struct qsockaddr *addr);
+       int             (*GetSocketAddr) (int socket, struct qsockaddr *addr);
+       int             (*GetNameFromAddr) (struct qsockaddr *addr, char *name);
+       int             (*GetAddrFromName) (char *name, struct qsockaddr *addr);
+       int                     (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2);
+       int                     (*GetSocketPort) (struct qsockaddr *addr);
+       int                     (*SetSocketPort) (struct qsockaddr *addr, int port);
+} net_landriver_t;
+
+#define        MAX_NET_DRIVERS         8
+extern int                             net_numlandrivers;
+extern net_landriver_t net_landrivers[MAX_NET_DRIVERS];
+
+typedef struct
+{
+       char            *name;
+       qboolean        initialized;
+       int                     (*Init) (void);
+       void            (*Listen) (qboolean state);
+       void            (*SearchForHosts) (qboolean xmit);
+       qsocket_t       *(*Connect) (char *host);
+       qsocket_t       *(*CheckNewConnections) (void);
+       int                     (*QGetMessage) (qsocket_t *sock);
+       int                     (*QSendMessage) (qsocket_t *sock, sizebuf_t *data);
+       int                     (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data);
+       qboolean        (*CanSendMessage) (qsocket_t *sock);
+       qboolean        (*CanSendUnreliableMessage) (qsocket_t *sock);
+       void            (*Close) (qsocket_t *sock);
+       void            (*Shutdown) (void);
+       int                     controlSock;
+} net_driver_t;
+
+extern int                     net_numdrivers;
+extern net_driver_t    net_drivers[MAX_NET_DRIVERS];
+
+extern int                     DEFAULTnet_hostport;
+extern int                     net_hostport;
+
+extern int net_driverlevel;
+extern cvar_t          hostname;
+extern char                    playername[];
+extern int                     playercolor;
+
+extern int             messagesSent;
+extern int             messagesReceived;
+extern int             unreliableMessagesSent;
+extern int             unreliableMessagesReceived;
+
+qsocket_t *NET_NewQSocket (void);
+void NET_FreeQSocket(qsocket_t *);
+double SetNetTime(void);
+
+
+#define HOSTCACHESIZE  8
+
+typedef struct
+{
+       char    name[16];
+       char    map[16];
+       char    cname[32];
+       int             users;
+       int             maxusers;
+       int             driver;
+       int             ldriver;
+       struct qsockaddr addr;
+} hostcache_t;
+
+extern int hostCacheCount;
+extern hostcache_t hostcache[HOSTCACHESIZE];
+
+#if !defined(_WIN32 ) && !defined (__linux__) && !defined (__sun__)
+#ifndef htonl
+extern unsigned long htonl (unsigned long hostlong);
+#endif
+#ifndef htons
+extern unsigned short htons (unsigned short hostshort);
+#endif
+#ifndef ntohl
+extern unsigned long ntohl (unsigned long netlong);
+#endif
+#ifndef ntohs
+extern unsigned short ntohs (unsigned short netshort);
+#endif
+#endif
+
+#ifdef IDGODS
+qboolean IsID(struct qsockaddr *addr);
+#endif
+
+//============================================================================
+//
+// public network functions
+//
+//============================================================================
+
+extern double          net_time;
+extern sizebuf_t       net_message;
+extern int                     net_activeconnections;
+
+void           NET_Init (void);
+void           NET_Shutdown (void);
+
+struct qsocket_s       *NET_CheckNewConnections (void);
+// returns a new connection number if there is one pending, else -1
+
+struct qsocket_s       *NET_Connect (char *host);
+// called by client to connect to a host.  Returns -1 if not able to
+
+qboolean NET_CanSendMessage (qsocket_t *sock);
+// Returns true or false if the given qsocket can currently accept a
+// message to be transmitted.
+
+int                    NET_GetMessage (struct qsocket_s *sock);
+// returns data in net_message sizebuf
+// returns 0 if no data is waiting
+// returns 1 if a message was received
+// returns 2 if an unreliable message was received
+// returns -1 if the connection died
+
+int                    NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data);
+int                    NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data);
+// returns 0 if the message connot be delivered reliably, but the connection
+//             is still considered valid
+// returns 1 if the message was sent properly
+// returns -1 if the connection died
+
+int                    NET_SendToAll(sizebuf_t *data, int blocktime);
+// This is a reliable *blocking* send to all attached clients.
+
+
+void           NET_Close (struct qsocket_s *sock);
+// if a dead connection is returned by a get or send function, this function
+// should be called when it is convenient
+
+// Server calls when a client is kicked off for a game related misbehavior
+// like an illegal protocal conversation.  Client calls when disconnecting
+// from a server.
+// A netcon_t number will not be reused until this function is called for it
+
+void NET_Poll(void);
+
+
+typedef struct _PollProcedure
+{
+       struct _PollProcedure   *next;
+       double                                  nextTime;
+       void                                    (*procedure)();
+       void                                    *arg;
+} PollProcedure;
+
+void SchedulePollProcedure(PollProcedure *pp, double timeOffset);
+
+extern qboolean        serialAvailable;
+extern qboolean        ipxAvailable;
+extern qboolean        tcpipAvailable;
+extern char            my_ipx_address[NET_NAMELEN];
+extern char            my_tcpip_address[NET_NAMELEN];
+extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem);
+extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
+extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+extern void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+
+extern qboolean        slistInProgress;
+extern qboolean        slistSilent;
+extern qboolean        slistLocal;
+
+void NET_Slist_f (void);
diff --git a/net_bsd.c b/net_bsd.c
new file mode 100644 (file)
index 0000000..79d62f8
--- /dev/null
+++ b/net_bsd.c
@@ -0,0 +1,93 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+#include "net_loop.h"
+#include "net_dgrm.h"
+
+net_driver_t net_drivers[MAX_NET_DRIVERS] =
+{
+       {
+       "Loopback",
+       false,
+       Loop_Init,
+       Loop_Listen,
+       Loop_SearchForHosts,
+       Loop_Connect,
+       Loop_CheckNewConnections,
+       Loop_GetMessage,
+       Loop_SendMessage,
+       Loop_SendUnreliableMessage,
+       Loop_CanSendMessage,
+       Loop_CanSendUnreliableMessage,
+       Loop_Close,
+       Loop_Shutdown
+       }
+       ,
+       {
+       "Datagram",
+       false,
+       Datagram_Init,
+       Datagram_Listen,
+       Datagram_SearchForHosts,
+       Datagram_Connect,
+       Datagram_CheckNewConnections,
+       Datagram_GetMessage,
+       Datagram_SendMessage,
+       Datagram_SendUnreliableMessage,
+       Datagram_CanSendMessage,
+       Datagram_CanSendUnreliableMessage,
+       Datagram_Close,
+       Datagram_Shutdown
+       }
+};
+
+int net_numdrivers = 2;
+
+#include "net_udp.h"
+
+net_landriver_t        net_landrivers[MAX_NET_DRIVERS] =
+{
+       {
+       "UDP",
+       false,
+       0,
+       UDP_Init,
+       UDP_Shutdown,
+       UDP_Listen,
+       UDP_OpenSocket,
+       UDP_CloseSocket,
+       UDP_Connect,
+       UDP_CheckNewConnections,
+       UDP_Read,
+       UDP_Write,
+       UDP_Broadcast,
+       UDP_AddrToString,
+       UDP_StringToAddr,
+       UDP_GetSocketAddr,
+       UDP_GetNameFromAddr,
+       UDP_GetAddrFromName,
+       UDP_AddrCompare,
+       UDP_GetSocketPort,
+       UDP_SetSocketPort
+       }
+};
+
+int net_numlandrivers = 1;
diff --git a/net_dgrm.c b/net_dgrm.c
new file mode 100644 (file)
index 0000000..3f8f22e
--- /dev/null
@@ -0,0 +1,1390 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_dgrm.c
+
+// This is enables a simple IP banning mechanism
+#define BAN_TEST
+
+#ifdef BAN_TEST
+#if defined(_WIN32)
+#include <windows.h>
+#elif defined (NeXT)
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#else
+#define AF_INET                2       /* internet */
+struct in_addr
+{
+       union
+       {
+               struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+               struct { unsigned short s_w1,s_w2; } S_un_w;
+               unsigned long S_addr;
+       } S_un;
+};
+#define        s_addr  S_un.S_addr     /* can be used for most tcp & ip code */
+struct sockaddr_in
+{
+    short                      sin_family;
+    unsigned short     sin_port;
+       struct in_addr  sin_addr;
+    char                       sin_zero[8];
+};
+char *inet_ntoa(struct in_addr in);
+unsigned long inet_addr(const char *cp);
+#endif
+#endif // BAN_TEST
+
+#include "quakedef.h"
+#include "net_dgrm.h"
+
+// these two macros are to make the code more readable
+#define sfunc  net_landrivers[sock->landriver]
+#define dfunc  net_landrivers[net_landriverlevel]
+
+static int net_landriverlevel;
+
+/* statistic counters */
+int    packetsSent = 0;
+int    packetsReSent = 0;
+int packetsReceived = 0;
+int receivedDuplicateCount = 0;
+int shortPacketCount = 0;
+int droppedDatagrams;
+
+static int myDriverLevel;
+
+struct
+{
+       unsigned int    length;
+       unsigned int    sequence;
+       byte                    data[MAX_DATAGRAM];
+} packetBuffer;
+
+extern int m_return_state;
+extern int m_state;
+extern qboolean m_return_onerror;
+extern char m_return_reason[32];
+
+
+#ifdef DEBUG
+char *StrAddr (struct qsockaddr *addr)
+{
+       static char buf[34];
+       byte *p = (byte *)addr;
+       int n;
+
+       for (n = 0; n < 16; n++)
+               sprintf (buf + n * 2, "%02x", *p++);
+       return buf;
+}
+#endif
+
+
+#ifdef BAN_TEST
+unsigned long banAddr = 0x00000000;
+unsigned long banMask = 0xffffffff;
+
+void NET_Ban_f (void)
+{
+       char    addrStr [32];
+       char    maskStr [32];
+       void    (*print) (char *fmt, ...);
+
+       if (cmd_source == src_command)
+       {
+               if (!sv.active)
+               {
+                       Cmd_ForwardToServer ();
+                       return;
+               }
+               print = Con_Printf;
+       }
+       else
+       {
+               if (pr_global_struct->deathmatch && !host_client->privileged)
+                       return;
+               print = SV_ClientPrintf;
+       }
+
+       switch (Cmd_Argc ())
+       {
+               case 1:
+                       if (((struct in_addr *)&banAddr)->s_addr)
+                       {
+                               strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr));
+                               strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask));
+                               print("Banning %s [%s]\n", addrStr, maskStr);
+                       }
+                       else
+                               print("Banning not active\n");
+                       break;
+
+               case 2:
+                       if (Q_strcasecmp(Cmd_Argv(1), "off") == 0)
+                               banAddr = 0x00000000;
+                       else
+                               banAddr = inet_addr(Cmd_Argv(1));
+                       banMask = 0xffffffff;
+                       break;
+
+               case 3:
+                       banAddr = inet_addr(Cmd_Argv(1));
+                       banMask = inet_addr(Cmd_Argv(2));
+                       break;
+
+               default:
+                       print("BAN ip_address [mask]\n");
+                       break;
+       }
+}
+#endif
+
+
+int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       unsigned int    packetLen;
+       unsigned int    dataLen;
+       unsigned int    eom;
+
+#ifdef DEBUG
+       if (data->cursize == 0)
+               Sys_Error("Datagram_SendMessage: zero length message\n");
+
+       if (data->cursize > NET_MAXMESSAGE)
+               Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize);
+
+       if (sock->canSend == false)
+               Sys_Error("SendMessage: called with canSend == false\n");
+#endif
+
+       memcpy(sock->sendMessage, data->data, data->cursize);
+       sock->sendMessageLength = data->cursize;
+
+       if (data->cursize <= MAX_DATAGRAM)
+       {
+               dataLen = data->cursize;
+               eom = NETFLAG_EOM;
+       }
+       else
+       {
+               dataLen = MAX_DATAGRAM;
+               eom = 0;
+       }
+       packetLen = NET_HEADERSIZE + dataLen;
+
+       packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+       packetBuffer.sequence = BigLong(sock->sendSequence++);
+       memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+       sock->canSend = false;
+
+       if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+               return -1;
+
+       sock->lastSendTime = net_time;
+       packetsSent++;
+       return 1;
+}
+
+
+int SendMessageNext (qsocket_t *sock)
+{
+       unsigned int    packetLen;
+       unsigned int    dataLen;
+       unsigned int    eom;
+
+       if (sock->sendMessageLength <= MAX_DATAGRAM)
+       {
+               dataLen = sock->sendMessageLength;
+               eom = NETFLAG_EOM;
+       }
+       else
+       {
+               dataLen = MAX_DATAGRAM;
+               eom = 0;
+       }
+       packetLen = NET_HEADERSIZE + dataLen;
+
+       packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+       packetBuffer.sequence = BigLong(sock->sendSequence++);
+       memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+       sock->sendNext = false;
+
+       if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+               return -1;
+
+       sock->lastSendTime = net_time;
+       packetsSent++;
+       return 1;
+}
+
+
+int ReSendMessage (qsocket_t *sock)
+{
+       unsigned int    packetLen;
+       unsigned int    dataLen;
+       unsigned int    eom;
+
+       if (sock->sendMessageLength <= MAX_DATAGRAM)
+       {
+               dataLen = sock->sendMessageLength;
+               eom = NETFLAG_EOM;
+       }
+       else
+       {
+               dataLen = MAX_DATAGRAM;
+               eom = 0;
+       }
+       packetLen = NET_HEADERSIZE + dataLen;
+
+       packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+       packetBuffer.sequence = BigLong(sock->sendSequence - 1);
+       memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+       sock->sendNext = false;
+
+       if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+               return -1;
+
+       sock->lastSendTime = net_time;
+       packetsReSent++;
+       return 1;
+}
+
+
+qboolean Datagram_CanSendMessage (qsocket_t *sock)
+{
+       if (sock->sendNext)
+               SendMessageNext (sock);
+
+       return sock->canSend;
+}
+
+
+qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock)
+{
+       return true;
+}
+
+
+int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       int     packetLen;
+
+#ifdef DEBUG
+       if (data->cursize == 0)
+               Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
+
+       if (data->cursize > MAX_DATAGRAM)
+               Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
+#endif
+
+       packetLen = NET_HEADERSIZE + data->cursize;
+
+       packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE);
+       packetBuffer.sequence = BigLong(sock->unreliableSendSequence++);
+       memcpy (packetBuffer.data, data->data, data->cursize);
+
+       if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+               return -1;
+
+       packetsSent++;
+       return 1;
+}
+
+
+int    Datagram_GetMessage (qsocket_t *sock)
+{
+       unsigned int    length;
+       unsigned int    flags;
+       int                             ret = 0;
+       struct qsockaddr readaddr;
+       unsigned int    sequence;
+       unsigned int    count;
+
+       if (!sock->canSend)
+               if ((net_time - sock->lastSendTime) > 1.0)
+                       ReSendMessage (sock);
+
+       while(1)
+       {       
+               length = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr);
+
+//     if ((rand() & 255) > 220)
+//             continue;
+
+               if (length == 0)
+                       break;
+
+               if (length == -1)
+               {
+                       Con_Printf("Read error\n");
+                       return -1;
+               }
+
+               if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0)
+               {
+#ifdef DEBUG
+                       Con_DPrintf("Forged packet received\n");
+                       Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr));
+                       Con_DPrintf("Received: %s\n", StrAddr (&readaddr));
+#endif
+                       continue;
+               }
+
+               if (length < NET_HEADERSIZE)
+               {
+                       shortPacketCount++;
+                       continue;
+               }
+
+               length = BigLong(packetBuffer.length);
+               flags = length & (~NETFLAG_LENGTH_MASK);
+               length &= NETFLAG_LENGTH_MASK;
+
+               if (flags & NETFLAG_CTL)
+                       continue;
+
+               sequence = BigLong(packetBuffer.sequence);
+               packetsReceived++;
+
+               if (flags & NETFLAG_UNRELIABLE)
+               {
+                       if (sequence < sock->unreliableReceiveSequence)
+                       {
+                               Con_DPrintf("Got a stale datagram\n");
+                               ret = 0;
+                               break;
+                       }
+                       if (sequence != sock->unreliableReceiveSequence)
+                       {
+                               count = sequence - sock->unreliableReceiveSequence;
+                               droppedDatagrams += count;
+                               Con_DPrintf("Dropped %u datagram(s)\n", count);
+                       }
+                       sock->unreliableReceiveSequence = sequence + 1;
+
+                       length -= NET_HEADERSIZE;
+
+                       SZ_Clear (&net_message);
+                       SZ_Write (&net_message, packetBuffer.data, length);
+
+                       ret = 2;
+                       break;
+               }
+
+               if (flags & NETFLAG_ACK)
+               {
+                       if (sequence != (sock->sendSequence - 1))
+                       {
+                               Con_DPrintf("Stale ACK received\n");
+                               continue;
+                       }
+                       if (sequence == sock->ackSequence)
+                       {
+                               sock->ackSequence++;
+                               if (sock->ackSequence != sock->sendSequence)
+                                       Con_DPrintf("ack sequencing error\n");
+                       }
+                       else
+                       {
+                               Con_DPrintf("Duplicate ACK received\n");
+                               continue;
+                       }
+                       sock->sendMessageLength -= MAX_DATAGRAM;
+                       if (sock->sendMessageLength > 0)
+                       {
+                               memcpy(sock->sendMessage, sock->sendMessage+MAX_DATAGRAM, sock->sendMessageLength);
+                               sock->sendNext = true;
+                       }
+                       else
+                       {
+                               sock->sendMessageLength = 0;
+                               sock->canSend = true;
+                       }
+                       continue;
+               }
+
+               if (flags & NETFLAG_DATA)
+               {
+                       packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK);
+                       packetBuffer.sequence = BigLong(sequence);
+                       sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr);
+
+                       if (sequence != sock->receiveSequence)
+                       {
+                               receivedDuplicateCount++;
+                               continue;
+                       }
+                       sock->receiveSequence++;
+
+                       length -= NET_HEADERSIZE;
+
+                       if (flags & NETFLAG_EOM)
+                       {
+                               SZ_Clear(&net_message);
+                               SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength);
+                               SZ_Write(&net_message, packetBuffer.data, length);
+                               sock->receiveMessageLength = 0;
+
+                               ret = 1;
+                               break;
+                       }
+
+                       memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length);
+                       sock->receiveMessageLength += length;
+                       continue;
+               }
+       }
+
+       if (sock->sendNext)
+               SendMessageNext (sock);
+
+       return ret;
+}
+
+
+void PrintStats(qsocket_t *s)
+{
+       Con_Printf("canSend = %4u   \n", s->canSend);
+       Con_Printf("sendSeq = %4u   ", s->sendSequence);
+       Con_Printf("recvSeq = %4u   \n", s->receiveSequence);
+       Con_Printf("\n");
+}
+
+void NET_Stats_f (void)
+{
+       qsocket_t       *s;
+
+       if (Cmd_Argc () == 1)
+       {
+               Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
+               Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
+               Con_Printf("reliable messages sent     = %i\n", messagesSent);
+               Con_Printf("reliable messages received = %i\n", messagesReceived);
+               Con_Printf("packetsSent                = %i\n", packetsSent);
+               Con_Printf("packetsReSent              = %i\n", packetsReSent);
+               Con_Printf("packetsReceived            = %i\n", packetsReceived);
+               Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
+               Con_Printf("shortPacketCount           = %i\n", shortPacketCount);
+               Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
+       }
+       else if (strcmp(Cmd_Argv(1), "*") == 0)
+       {
+               for (s = net_activeSockets; s; s = s->next)
+                       PrintStats(s);
+               for (s = net_freeSockets; s; s = s->next)
+                       PrintStats(s);
+       }
+       else
+       {
+               for (s = net_activeSockets; s; s = s->next)
+                       if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
+                               break;
+               if (s == NULL)
+                       for (s = net_freeSockets; s; s = s->next)
+                               if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
+                                       break;
+               if (s == NULL)
+                       return;
+               PrintStats(s);
+       }
+}
+
+
+static qboolean testInProgress = false;
+static int             testPollCount;
+static int             testDriver;
+static int             testSocket;
+
+static void Test_Poll(void);
+PollProcedure  testPollProcedure = {NULL, 0.0, Test_Poll};
+
+static void Test_Poll(void)
+{
+       struct qsockaddr clientaddr;
+       int             control;
+       int             len;
+       char    name[32];
+       char    address[64];
+       int             colors;
+       int             frags;
+       int             connectTime;
+       byte    playerNumber;
+
+       net_landriverlevel = testDriver;
+
+       while (1)
+       {
+               len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr);
+               if (len < sizeof(int))
+                       break;
+
+               net_message.cursize = len;
+
+               MSG_BeginReading ();
+               control = BigLong(*((int *)net_message.data));
+               MSG_ReadLong();
+               if (control == -1)
+                       break;
+               if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+                       break;
+               if ((control & NETFLAG_LENGTH_MASK) != len)
+                       break;
+
+               if (MSG_ReadByte() != CCREP_PLAYER_INFO)
+                       Sys_Error("Unexpected repsonse to Player Info request\n");
+
+               playerNumber = MSG_ReadByte();
+               strcpy(name, MSG_ReadString());
+               colors = MSG_ReadLong();
+               frags = MSG_ReadLong();
+               connectTime = MSG_ReadLong();
+               strcpy(address, MSG_ReadString());
+
+               Con_Printf("%s\n  frags:%3i  colors:%u %u  time:%u\n  %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address);
+       }
+
+       testPollCount--;
+       if (testPollCount)
+       {
+               SchedulePollProcedure(&testPollProcedure, 0.1);
+       }
+       else
+       {
+               dfunc.CloseSocket(testSocket);
+               testInProgress = false;
+       }
+}
+
+static void Test_f (void)
+{
+       char    *host;
+       int             n;
+       int             max = MAX_SCOREBOARD;
+       struct qsockaddr sendaddr;
+
+       if (testInProgress)
+               return;
+
+       host = Cmd_Argv (1);
+
+       if (host && hostCacheCount)
+       {
+               for (n = 0; n < hostCacheCount; n++)
+                       if (Q_strcasecmp (host, hostcache[n].name) == 0)
+                       {
+                               if (hostcache[n].driver != myDriverLevel)
+                                       continue;
+                               net_landriverlevel = hostcache[n].ldriver;
+                               max = hostcache[n].maxusers;
+                               memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
+                               break;
+                       }
+               if (n < hostCacheCount)
+                       goto JustDoIt;
+       }
+
+       for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+       {
+               if (!net_landrivers[net_landriverlevel].initialized)
+                       continue;
+
+               // see if we can resolve the host name
+               if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
+                       break;
+       }
+       if (net_landriverlevel == net_numlandrivers)
+               return;
+
+JustDoIt:
+       testSocket = dfunc.OpenSocket(0);
+       if (testSocket == -1)
+               return;
+
+       testInProgress = true;
+       testPollCount = 20;
+       testDriver = net_landriverlevel;
+
+       for (n = 0; n < max; n++)
+       {
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);
+               MSG_WriteByte(&net_message, n);
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL |      (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr);
+       }
+       SZ_Clear(&net_message);
+       SchedulePollProcedure(&testPollProcedure, 0.1);
+}
+
+
+static qboolean test2InProgress = false;
+static int             test2Driver;
+static int             test2Socket;
+
+static void Test2_Poll(void);
+PollProcedure  test2PollProcedure = {NULL, 0.0, Test2_Poll};
+
+static void Test2_Poll(void)
+{
+       struct qsockaddr clientaddr;
+       int             control;
+       int             len;
+       char    name[256];
+       char    value[256];
+
+       net_landriverlevel = test2Driver;
+       name[0] = 0;
+
+       len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr);
+       if (len < sizeof(int))
+               goto Reschedule;
+
+       net_message.cursize = len;
+
+       MSG_BeginReading ();
+       control = BigLong(*((int *)net_message.data));
+       MSG_ReadLong();
+       if (control == -1)
+               goto Error;
+       if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+               goto Error;
+       if ((control & NETFLAG_LENGTH_MASK) != len)
+               goto Error;
+
+       if (MSG_ReadByte() != CCREP_RULE_INFO)
+               goto Error;
+
+       strcpy(name, MSG_ReadString());
+       if (name[0] == 0)
+               goto Done;
+       strcpy(value, MSG_ReadString());
+
+       Con_Printf("%-16.16s  %-16.16s\n", name, value);
+
+       SZ_Clear(&net_message);
+       // save space for the header, filled in later
+       MSG_WriteLong(&net_message, 0);
+       MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
+       MSG_WriteString(&net_message, name);
+       *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+       dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr);
+       SZ_Clear(&net_message);
+
+Reschedule:
+       SchedulePollProcedure(&test2PollProcedure, 0.05);
+       return;
+
+Error:
+       Con_Printf("Unexpected repsonse to Rule Info request\n");
+Done:
+       dfunc.CloseSocket(test2Socket);
+       test2InProgress = false;
+       return;
+}
+
+static void Test2_f (void)
+{
+       char    *host;
+       int             n;
+       struct qsockaddr sendaddr;
+
+       if (test2InProgress)
+               return;
+
+       host = Cmd_Argv (1);
+
+       if (host && hostCacheCount)
+       {
+               for (n = 0; n < hostCacheCount; n++)
+                       if (Q_strcasecmp (host, hostcache[n].name) == 0)
+                       {
+                               if (hostcache[n].driver != myDriverLevel)
+                                       continue;
+                               net_landriverlevel = hostcache[n].ldriver;
+                               memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
+                               break;
+                       }
+               if (n < hostCacheCount)
+                       goto JustDoIt;
+       }
+
+       for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+       {
+               if (!net_landrivers[net_landriverlevel].initialized)
+                       continue;
+
+               // see if we can resolve the host name
+               if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
+                       break;
+       }
+       if (net_landriverlevel == net_numlandrivers)
+               return;
+
+JustDoIt:
+       test2Socket = dfunc.OpenSocket(0);
+       if (test2Socket == -1)
+               return;
+
+       test2InProgress = true;
+       test2Driver = net_landriverlevel;
+
+       SZ_Clear(&net_message);
+       // save space for the header, filled in later
+       MSG_WriteLong(&net_message, 0);
+       MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
+       MSG_WriteString(&net_message, "");
+       *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+       dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr);
+       SZ_Clear(&net_message);
+       SchedulePollProcedure(&test2PollProcedure, 0.05);
+}
+
+
+int Datagram_Init (void)
+{
+       int i;
+       int csock;
+
+       myDriverLevel = net_driverlevel;
+       Cmd_AddCommand ("net_stats", NET_Stats_f);
+
+       if (COM_CheckParm("-nolan"))
+               return -1;
+
+       for (i = 0; i < net_numlandrivers; i++)
+               {
+               csock = net_landrivers[i].Init ();
+               if (csock == -1)
+                       continue;
+               net_landrivers[i].initialized = true;
+               net_landrivers[i].controlSock = csock;
+               }
+
+#ifdef BAN_TEST
+       Cmd_AddCommand ("ban", NET_Ban_f);
+#endif
+       Cmd_AddCommand ("test", Test_f);
+       Cmd_AddCommand ("test2", Test2_f);
+
+       return 0;
+}
+
+
+void Datagram_Shutdown (void)
+{
+       int i;
+
+//
+// shutdown the lan drivers
+//
+       for (i = 0; i < net_numlandrivers; i++)
+       {
+               if (net_landrivers[i].initialized)
+               {
+                       net_landrivers[i].Shutdown ();
+                       net_landrivers[i].initialized = false;
+               }
+       }
+}
+
+
+void Datagram_Close (qsocket_t *sock)
+{
+       sfunc.CloseSocket(sock->socket);
+}
+
+
+void Datagram_Listen (qboolean state)
+{
+       int i;
+
+       for (i = 0; i < net_numlandrivers; i++)
+               if (net_landrivers[i].initialized)
+                       net_landrivers[i].Listen (state);
+}
+
+
+static qsocket_t *_Datagram_CheckNewConnections (void)
+{
+       struct qsockaddr clientaddr;
+       struct qsockaddr newaddr;
+       int                     newsock;
+       int                     acceptsock;
+       qsocket_t       *sock;
+       qsocket_t       *s;
+       int                     len;
+       int                     command;
+       int                     control;
+       int                     ret;
+
+       acceptsock = dfunc.CheckNewConnections();
+       if (acceptsock == -1)
+               return NULL;
+
+       SZ_Clear(&net_message);
+
+       len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr);
+       if (len < sizeof(int))
+               return NULL;
+       net_message.cursize = len;
+
+       MSG_BeginReading ();
+       control = BigLong(*((int *)net_message.data));
+       MSG_ReadLong();
+       if (control == -1)
+               return NULL;
+       if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+               return NULL;
+       if ((control & NETFLAG_LENGTH_MASK) != len)
+               return NULL;
+
+       command = MSG_ReadByte();
+       if (command == CCREQ_SERVER_INFO)
+       {
+               if (strcmp(MSG_ReadString(), "QUAKE") != 0)
+                       return NULL;
+
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+               dfunc.GetSocketAddr(acceptsock, &newaddr);
+               MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
+               MSG_WriteString(&net_message, hostname.string);
+               MSG_WriteString(&net_message, sv.name);
+               MSG_WriteByte(&net_message, net_activeconnections);
+               MSG_WriteByte(&net_message, svs.maxclients);
+               MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+               SZ_Clear(&net_message);
+               return NULL;
+       }
+
+       if (command == CCREQ_PLAYER_INFO)
+       {
+               int                     playerNumber;
+               int                     activeNumber;
+               int                     clientNumber;
+               client_t        *client;
+               
+               playerNumber = MSG_ReadByte();
+               activeNumber = -1;
+               for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
+               {
+                       if (client->active)
+                       {
+                               activeNumber++;
+                               if (activeNumber == playerNumber)
+                                       break;
+                       }
+               }
+               if (clientNumber == svs.maxclients)
+                       return NULL;
+
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
+               MSG_WriteByte(&net_message, playerNumber);
+               MSG_WriteString(&net_message, client->name);
+               MSG_WriteLong(&net_message, client->colors);
+               MSG_WriteLong(&net_message, (int)client->edict->v.frags);
+               MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime));
+               MSG_WriteString(&net_message, client->netconnection->address);
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+               SZ_Clear(&net_message);
+
+               return NULL;
+       }
+
+       if (command == CCREQ_RULE_INFO)
+       {
+               char    *prevCvarName;
+               cvar_t  *var;
+
+               // find the search start location
+               prevCvarName = MSG_ReadString();
+               if (*prevCvarName)
+               {
+                       var = Cvar_FindVar (prevCvarName);
+                       if (!var)
+                               return NULL;
+                       var = var->next;
+               }
+               else
+                       var = cvar_vars;
+
+               // search for the next server cvar
+               while (var)
+               {
+                       if (var->server)
+                               break;
+                       var = var->next;
+               }
+
+               // send the response
+
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREP_RULE_INFO);
+               if (var)
+               {
+                       MSG_WriteString(&net_message, var->name);
+                       MSG_WriteString(&net_message, var->string);
+               }
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+               SZ_Clear(&net_message);
+
+               return NULL;
+       }
+
+       if (command != CCREQ_CONNECT)
+               return NULL;
+
+       if (strcmp(MSG_ReadString(), "QUAKE") != 0)
+               return NULL;
+
+       if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
+       {
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREP_REJECT);
+               MSG_WriteString(&net_message, "Incompatible version.\n");
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+               SZ_Clear(&net_message);
+               return NULL;
+       }
+
+#ifdef BAN_TEST
+       // check for a ban
+       if (clientaddr.sa_family == AF_INET)
+       {
+               unsigned long testAddr;
+               testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr;
+               if ((testAddr & banMask) == banAddr)
+               {
+                       SZ_Clear(&net_message);
+                       // save space for the header, filled in later
+                       MSG_WriteLong(&net_message, 0);
+                       MSG_WriteByte(&net_message, CCREP_REJECT);
+                       MSG_WriteString(&net_message, "You have been banned.\n");
+                       *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+                       dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+                       SZ_Clear(&net_message);
+                       return NULL;
+               }
+       }
+#endif
+
+       // see if this guy is already connected
+       for (s = net_activeSockets; s; s = s->next)
+       {
+               if (s->driver != net_driverlevel)
+                       continue;
+               ret = dfunc.AddrCompare(&clientaddr, &s->addr);
+               if (ret >= 0)
+               {
+                       // is this a duplicate connection reqeust?
+                       if (ret == 0 && net_time - s->connecttime < 2.0)
+                       {
+                               // yes, so send a duplicate reply
+                               SZ_Clear(&net_message);
+                               // save space for the header, filled in later
+                               MSG_WriteLong(&net_message, 0);
+                               MSG_WriteByte(&net_message, CCREP_ACCEPT);
+                               dfunc.GetSocketAddr(s->socket, &newaddr);
+                               MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
+                               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+                               dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+                               SZ_Clear(&net_message);
+                               return NULL;
+                       }
+                       // it's somebody coming back in from a crash/disconnect
+                       // so close the old qsocket and let their retry get them back in
+                       NET_Close(s);
+                       return NULL;
+               }
+       }
+
+       // allocate a QSocket
+       sock = NET_NewQSocket ();
+       if (sock == NULL)
+       {
+               // no room; try to let him know
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREP_REJECT);
+               MSG_WriteString(&net_message, "Server is full.\n");
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+               SZ_Clear(&net_message);
+               return NULL;
+       }
+
+       // allocate a network socket
+       newsock = dfunc.OpenSocket(0);
+       if (newsock == -1)
+       {
+               NET_FreeQSocket(sock);
+               return NULL;
+       }
+
+       // connect to the client
+       if (dfunc.Connect (newsock, &clientaddr) == -1)
+       {
+               dfunc.CloseSocket(newsock);
+               NET_FreeQSocket(sock);
+               return NULL;
+       }
+
+       // everything is allocated, just fill in the details    
+       sock->socket = newsock;
+       sock->landriver = net_landriverlevel;
+       sock->addr = clientaddr;
+       strcpy(sock->address, dfunc.AddrToString(&clientaddr));
+
+       // send him back the info about the server connection he has been allocated
+       SZ_Clear(&net_message);
+       // save space for the header, filled in later
+       MSG_WriteLong(&net_message, 0);
+       MSG_WriteByte(&net_message, CCREP_ACCEPT);
+       dfunc.GetSocketAddr(newsock, &newaddr);
+       MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
+//     MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
+       *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+       dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+       SZ_Clear(&net_message);
+
+       return sock;
+}
+
+qsocket_t *Datagram_CheckNewConnections (void)
+{
+       qsocket_t *ret = NULL;
+
+       for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+               if (net_landrivers[net_landriverlevel].initialized)
+                       if ((ret = _Datagram_CheckNewConnections ()) != NULL)
+                               break;
+       return ret;
+}
+
+
+static void _Datagram_SearchForHosts (qboolean xmit)
+{
+       int             ret;
+       int             n;
+       int             i;
+       struct qsockaddr readaddr;
+       struct qsockaddr myaddr;
+       int             control;
+
+       dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
+       if (xmit)
+       {
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
+               MSG_WriteString(&net_message, "QUAKE");
+               MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize);
+               SZ_Clear(&net_message);
+       }
+
+       while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
+       {
+               if (ret < sizeof(int))
+                       continue;
+               net_message.cursize = ret;
+
+               // don't answer our own query
+               if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
+                       continue;
+
+               // is the cache full?
+               if (hostCacheCount == HOSTCACHESIZE)
+                       continue;
+
+               MSG_BeginReading ();
+               control = BigLong(*((int *)net_message.data));
+               MSG_ReadLong();
+               if (control == -1)
+                       continue;
+               if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+                       continue;
+               if ((control & NETFLAG_LENGTH_MASK) != ret)
+                       continue;
+
+               if (MSG_ReadByte() != CCREP_SERVER_INFO)
+                       continue;
+
+               dfunc.GetAddrFromName(MSG_ReadString(), &readaddr);
+               // search the cache for this server
+               for (n = 0; n < hostCacheCount; n++)
+                       if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0)
+                               break;
+
+               // is it already there?
+               if (n < hostCacheCount)
+                       continue;
+
+               // add it
+               hostCacheCount++;
+               strcpy(hostcache[n].name, MSG_ReadString());
+               strcpy(hostcache[n].map, MSG_ReadString());
+               hostcache[n].users = MSG_ReadByte();
+               hostcache[n].maxusers = MSG_ReadByte();
+               if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
+               {
+                       strcpy(hostcache[n].cname, hostcache[n].name);
+                       hostcache[n].cname[14] = 0;
+                       strcpy(hostcache[n].name, "*");
+                       strcat(hostcache[n].name, hostcache[n].cname);
+               }
+               memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
+               hostcache[n].driver = net_driverlevel;
+               hostcache[n].ldriver = net_landriverlevel;
+               strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr));
+
+               // check for a name conflict
+               for (i = 0; i < hostCacheCount; i++)
+               {
+                       if (i == n)
+                               continue;
+                       if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0)
+                       {
+                               i = strlen(hostcache[n].name);
+                               if (i < 15 && hostcache[n].name[i-1] > '8')
+                               {
+                                       hostcache[n].name[i] = '0';
+                                       hostcache[n].name[i+1] = 0;
+                               }
+                               else
+                                       hostcache[n].name[i-1]++;
+                               i = -1;
+                       }
+               }
+       }
+}
+
+void Datagram_SearchForHosts (qboolean xmit)
+{
+       for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+       {
+               if (hostCacheCount == HOSTCACHESIZE)
+                       break;
+               if (net_landrivers[net_landriverlevel].initialized)
+                       _Datagram_SearchForHosts (xmit);
+       }
+}
+
+
+static qsocket_t *_Datagram_Connect (char *host)
+{
+       struct qsockaddr sendaddr;
+       struct qsockaddr readaddr;
+       qsocket_t       *sock;
+       int                     newsock;
+       int                     ret;
+       int                     reps;
+       double          start_time;
+       int                     control;
+       char            *reason;
+
+       // see if we can resolve the host name
+       if (dfunc.GetAddrFromName(host, &sendaddr) == -1)
+               return NULL;
+
+       newsock = dfunc.OpenSocket (0);
+       if (newsock == -1)
+               return NULL;
+
+       sock = NET_NewQSocket ();
+       if (sock == NULL)
+               goto ErrorReturn2;
+       sock->socket = newsock;
+       sock->landriver = net_landriverlevel;
+
+       // connect to the host
+       if (dfunc.Connect (newsock, &sendaddr) == -1)
+               goto ErrorReturn;
+
+       // send the connection request
+       Con_Printf("trying...\n"); SCR_UpdateScreen ();
+       start_time = net_time;
+
+       for (reps = 0; reps < 3; reps++)
+       {
+               SZ_Clear(&net_message);
+               // save space for the header, filled in later
+               MSG_WriteLong(&net_message, 0);
+               MSG_WriteByte(&net_message, CCREQ_CONNECT);
+               MSG_WriteString(&net_message, "QUAKE");
+               MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr);
+               SZ_Clear(&net_message);
+               do
+               {
+                       ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr);
+                       // if we got something, validate it
+                       if (ret > 0)
+                       {
+                               // is it from the right place?
+                               if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0)
+                               {
+#ifdef DEBUG
+                                       Con_Printf("wrong reply address\n");
+                                       Con_Printf("Expected: %s\n", StrAddr (&sendaddr));
+                                       Con_Printf("Received: %s\n", StrAddr (&readaddr));
+                                       SCR_UpdateScreen ();
+#endif
+                                       ret = 0;
+                                       continue;
+                               }
+
+                               if (ret < sizeof(int))
+                               {
+                                       ret = 0;
+                                       continue;
+                               }
+
+                               net_message.cursize = ret;
+                               MSG_BeginReading ();
+
+                               control = BigLong(*((int *)net_message.data));
+                               MSG_ReadLong();
+                               if (control == -1)
+                               {
+                                       ret = 0;
+                                       continue;
+                               }
+                               if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+                               {
+                                       ret = 0;
+                                       continue;
+                               }
+                               if ((control & NETFLAG_LENGTH_MASK) != ret)
+                               {
+                                       ret = 0;
+                                       continue;
+                               }
+                       }
+               }
+               while (ret == 0 && (SetNetTime() - start_time) < 2.5);
+               if (ret)
+                       break;
+               Con_Printf("still trying...\n"); SCR_UpdateScreen ();
+               start_time = SetNetTime();
+       }
+
+       if (ret == 0)
+       {
+               reason = "No Response";
+               Con_Printf("%s\n", reason);
+               strcpy(m_return_reason, reason);
+               goto ErrorReturn;
+       }
+
+       if (ret == -1)
+       {
+               reason = "Network Error";
+               Con_Printf("%s\n", reason);
+               strcpy(m_return_reason, reason);
+               goto ErrorReturn;
+       }
+
+       ret = MSG_ReadByte();
+       if (ret == CCREP_REJECT)
+       {
+               reason = MSG_ReadString();
+               Con_Printf(reason);
+               strncpy(m_return_reason, reason, 31);
+               goto ErrorReturn;
+       }
+
+       if (ret == CCREP_ACCEPT)
+       {
+               memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr));
+               dfunc.SetSocketPort (&sock->addr, MSG_ReadLong());
+       }
+       else
+       {
+               reason = "Bad Response";
+               Con_Printf("%s\n", reason);
+               strcpy(m_return_reason, reason);
+               goto ErrorReturn;
+       }
+
+       dfunc.GetNameFromAddr (&sendaddr, sock->address);
+
+       Con_Printf ("Connection accepted\n");
+       sock->lastMessageTime = SetNetTime();
+
+       // switch the connection to the specified address
+       if (dfunc.Connect (newsock, &sock->addr) == -1)
+       {
+               reason = "Connect to Game failed";
+               Con_Printf("%s\n", reason);
+               strcpy(m_return_reason, reason);
+               goto ErrorReturn;
+       }
+
+       m_return_onerror = false;
+       return sock;
+
+ErrorReturn:
+       NET_FreeQSocket(sock);
+ErrorReturn2:
+       dfunc.CloseSocket(newsock);
+       if (m_return_onerror)
+       {
+               key_dest = key_menu;
+               m_state = m_return_state;
+               m_return_onerror = false;
+       }
+       return NULL;
+}
+
+qsocket_t *Datagram_Connect (char *host)
+{
+       qsocket_t *ret = NULL;
+
+       for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+               if (net_landrivers[net_landriverlevel].initialized)
+                       if ((ret = _Datagram_Connect (host)) != NULL)
+                               break;
+       return ret;
+}
diff --git a/net_dgrm.h b/net_dgrm.h
new file mode 100644 (file)
index 0000000..da052e7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_dgrm.h
+
+
+int                    Datagram_Init (void);
+void           Datagram_Listen (qboolean state);
+void           Datagram_SearchForHosts (qboolean xmit);
+qsocket_t      *Datagram_Connect (char *host);
+qsocket_t      *Datagram_CheckNewConnections (void);
+int                    Datagram_GetMessage (qsocket_t *sock);
+int                    Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data);
+int                    Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean       Datagram_CanSendMessage (qsocket_t *sock);
+qboolean       Datagram_CanSendUnreliableMessage (qsocket_t *sock);
+void           Datagram_Close (qsocket_t *sock);
+void           Datagram_Shutdown (void);
diff --git a/net_loop.c b/net_loop.c
new file mode 100644 (file)
index 0000000..ac2a5de
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_loop.c
+
+#include "quakedef.h"
+#include "net_loop.h"
+
+qboolean       localconnectpending = false;
+qsocket_t      *loop_client = NULL;
+qsocket_t      *loop_server = NULL;
+
+int Loop_Init (void)
+{
+       if (cls.state == ca_dedicated)
+               return -1;
+       return 0;
+}
+
+
+void Loop_Shutdown (void)
+{
+}
+
+
+void Loop_Listen (qboolean state)
+{
+}
+
+
+void Loop_SearchForHosts (qboolean xmit)
+{
+       if (!sv.active)
+               return;
+
+       hostCacheCount = 1;
+       if (strcmp(hostname.string, "UNNAMED") == 0)
+               strcpy(hostcache[0].name, "local");
+       else
+               strcpy(hostcache[0].name, hostname.string);
+       strcpy(hostcache[0].map, sv.name);
+       hostcache[0].users = net_activeconnections;
+       hostcache[0].maxusers = svs.maxclients;
+       hostcache[0].driver = net_driverlevel;
+       strcpy(hostcache[0].cname, "local");
+}
+
+
+qsocket_t *Loop_Connect (char *host)
+{
+       if (strcmp(host,"local") != 0)
+               return NULL;
+       
+       localconnectpending = true;
+
+       if (!loop_client)
+       {
+               if ((loop_client = NET_NewQSocket ()) == NULL)
+               {
+                       Con_Printf("Loop_Connect: no qsocket available\n");
+                       return NULL;
+               }
+               strcpy (loop_client->address, "localhost");
+       }
+       loop_client->receiveMessageLength = 0;
+       loop_client->sendMessageLength = 0;
+       loop_client->canSend = true;
+
+       if (!loop_server)
+       {
+               if ((loop_server = NET_NewQSocket ()) == NULL)
+               {
+                       Con_Printf("Loop_Connect: no qsocket available\n");
+                       return NULL;
+               }
+               strcpy (loop_server->address, "LOCAL");
+       }
+       loop_server->receiveMessageLength = 0;
+       loop_server->sendMessageLength = 0;
+       loop_server->canSend = true;
+
+       loop_client->driverdata = (void *)loop_server;
+       loop_server->driverdata = (void *)loop_client;
+       
+       return loop_client;     
+}
+
+
+qsocket_t *Loop_CheckNewConnections (void)
+{
+       if (!localconnectpending)
+               return NULL;
+
+       localconnectpending = false;
+       loop_server->sendMessageLength = 0;
+       loop_server->receiveMessageLength = 0;
+       loop_server->canSend = true;
+       loop_client->sendMessageLength = 0;
+       loop_client->receiveMessageLength = 0;
+       loop_client->canSend = true;
+       return loop_server;
+}
+
+
+static int IntAlign(int value)
+{
+       return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1));
+}
+
+
+int Loop_GetMessage (qsocket_t *sock)
+{
+       int             ret;
+       int             length;
+
+       if (sock->receiveMessageLength == 0)
+               return 0;
+
+       ret = sock->receiveMessage[0];
+       length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
+       // alignment byte skipped here
+       SZ_Clear (&net_message);
+       SZ_Write (&net_message, &sock->receiveMessage[4], length);
+
+       length = IntAlign(length + 4);
+       sock->receiveMessageLength -= length;
+
+       if (sock->receiveMessageLength)
+               memcpy(sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength);
+
+       if (sock->driverdata && ret == 1)
+               ((qsocket_t *)sock->driverdata)->canSend = true;
+
+       return ret;
+}
+
+
+int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       byte *buffer;
+       int  *bufferLength;
+
+       if (!sock->driverdata)
+               return -1;
+
+       bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
+
+       if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE)
+               Sys_Error("Loop_SendMessage: overflow\n");
+
+       buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
+
+       // message type
+       *buffer++ = 1;
+
+       // length
+       *buffer++ = data->cursize & 0xff;
+       *buffer++ = data->cursize >> 8;
+
+       // align
+       buffer++;
+
+       // message
+       memcpy(buffer, data->data, data->cursize);
+       *bufferLength = IntAlign(*bufferLength + data->cursize + 4);
+
+       sock->canSend = false;
+       return 1;
+}
+
+
+int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       byte *buffer;
+       int  *bufferLength;
+
+       if (!sock->driverdata)
+               return -1;
+
+       bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
+
+       if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE)
+               return 0;
+
+       buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
+
+       // message type
+       *buffer++ = 2;
+
+       // length
+       *buffer++ = data->cursize & 0xff;
+       *buffer++ = data->cursize >> 8;
+
+       // align
+       buffer++;
+
+       // message
+       memcpy(buffer, data->data, data->cursize);
+       *bufferLength = IntAlign(*bufferLength + data->cursize + 4);
+       return 1;
+}
+
+
+qboolean Loop_CanSendMessage (qsocket_t *sock)
+{
+       if (!sock->driverdata)
+               return false;
+       return sock->canSend;
+}
+
+
+qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock)
+{
+       return true;
+}
+
+
+void Loop_Close (qsocket_t *sock)
+{
+       if (sock->driverdata)
+               ((qsocket_t *)sock->driverdata)->driverdata = NULL;
+       sock->receiveMessageLength = 0;
+       sock->sendMessageLength = 0;
+       sock->canSend = true;
+       if (sock == loop_client)
+               loop_client = NULL;
+       else
+               loop_server = NULL;
+}
diff --git a/net_loop.h b/net_loop.h
new file mode 100644 (file)
index 0000000..90cdb2c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_loop.h
+
+int                    Loop_Init (void);
+void           Loop_Listen (qboolean state);
+void           Loop_SearchForHosts (qboolean xmit);
+qsocket_t      *Loop_Connect (char *host);
+qsocket_t      *Loop_CheckNewConnections (void);
+int                    Loop_GetMessage (qsocket_t *sock);
+int                    Loop_SendMessage (qsocket_t *sock, sizebuf_t *data);
+int                    Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean       Loop_CanSendMessage (qsocket_t *sock);
+qboolean       Loop_CanSendUnreliableMessage (qsocket_t *sock);
+void           Loop_Close (qsocket_t *sock);
+void           Loop_Shutdown (void);
diff --git a/net_main.c b/net_main.c
new file mode 100644 (file)
index 0000000..32b7165
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_main.c
+
+#include "quakedef.h"
+#include "net_vcr.h"
+
+qsocket_t      *net_activeSockets = NULL;
+qsocket_t      *net_freeSockets = NULL;
+int                    net_numsockets = 0;
+
+qboolean       serialAvailable = false;
+qboolean       ipxAvailable = false;
+qboolean       tcpipAvailable = false;
+
+int                    net_hostport;
+int                    DEFAULTnet_hostport = 26000;
+
+char           my_ipx_address[NET_NAMELEN];
+char           my_tcpip_address[NET_NAMELEN];
+
+void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem);
+void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
+void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+
+static qboolean        listening = false;
+
+qboolean       slistInProgress = false;
+qboolean       slistSilent = false;
+qboolean       slistLocal = true;
+static double  slistStartTime;
+static int             slistLastShown;
+
+static void Slist_Send(void);
+static void Slist_Poll(void);
+PollProcedure  slistSendProcedure = {NULL, 0.0, Slist_Send};
+PollProcedure  slistPollProcedure = {NULL, 0.0, Slist_Poll};
+
+
+sizebuf_t              net_message;
+int                            net_activeconnections = 0;
+
+int messagesSent = 0;
+int messagesReceived = 0;
+int unreliableMessagesSent = 0;
+int unreliableMessagesReceived = 0;
+
+cvar_t net_messagetimeout = {"net_messagetimeout","300"};
+cvar_t hostname = {"hostname", "UNNAMED"};
+
+qboolean       configRestored = false;
+cvar_t config_com_port = {"_config_com_port", "0x3f8", true};
+cvar_t config_com_irq = {"_config_com_irq", "4", true};
+cvar_t config_com_baud = {"_config_com_baud", "57600", true};
+cvar_t config_com_modem = {"_config_com_modem", "1", true};
+cvar_t config_modem_dialtype = {"_config_modem_dialtype", "T", true};
+cvar_t config_modem_clear = {"_config_modem_clear", "ATZ", true};
+cvar_t config_modem_init = {"_config_modem_init", "", true};
+cvar_t config_modem_hangup = {"_config_modem_hangup", "AT H", true};
+
+#ifdef IDGODS
+cvar_t idgods = {"idgods", "0"};
+#endif
+
+int    vcrFile = -1;
+qboolean recording = false;
+
+// these two macros are to make the code more readable
+#define sfunc  net_drivers[sock->driver]
+#define dfunc  net_drivers[net_driverlevel]
+
+int    net_driverlevel;
+
+
+double                 net_time;
+
+double SetNetTime(void)
+{
+       net_time = Sys_FloatTime();
+       return net_time;
+}
+
+
+/*
+===================
+NET_NewQSocket
+
+Called by drivers when a new communications endpoint is required
+The sequence and buffer fields will be filled in properly
+===================
+*/
+qsocket_t *NET_NewQSocket (void)
+{
+       qsocket_t       *sock;
+
+       if (net_freeSockets == NULL)
+               return NULL;
+
+       if (net_activeconnections >= svs.maxclients)
+               return NULL;
+
+       // get one from free list
+       sock = net_freeSockets;
+       net_freeSockets = sock->next;
+
+       // add it to active list
+       sock->next = net_activeSockets;
+       net_activeSockets = sock;
+
+       sock->disconnected = false;
+       sock->connecttime = net_time;
+       strcpy (sock->address,"UNSET ADDRESS");
+       sock->driver = net_driverlevel;
+       sock->socket = 0;
+       sock->driverdata = NULL;
+       sock->canSend = true;
+       sock->sendNext = false;
+       sock->lastMessageTime = net_time;
+       sock->ackSequence = 0;
+       sock->sendSequence = 0;
+       sock->unreliableSendSequence = 0;
+       sock->sendMessageLength = 0;
+       sock->receiveSequence = 0;
+       sock->unreliableReceiveSequence = 0;
+       sock->receiveMessageLength = 0;
+
+       return sock;
+}
+
+
+void NET_FreeQSocket(qsocket_t *sock)
+{
+       qsocket_t       *s;
+
+       // remove it from active list
+       if (sock == net_activeSockets)
+               net_activeSockets = net_activeSockets->next;
+       else
+       {
+               for (s = net_activeSockets; s; s = s->next)
+                       if (s->next == sock)
+                       {
+                               s->next = sock->next;
+                               break;
+                       }
+               if (!s)
+                       Sys_Error ("NET_FreeQSocket: not active\n");
+       }
+
+       // add it to free list
+       sock->next = net_freeSockets;
+       net_freeSockets = sock;
+       sock->disconnected = true;
+}
+
+
+static void NET_Listen_f (void)
+{
+       if (Cmd_Argc () != 2)
+       {
+               Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0);
+               return;
+       }
+
+       listening = atoi(Cmd_Argv(1)) ? true : false;
+
+       for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
+       {
+               if (net_drivers[net_driverlevel].initialized == false)
+                       continue;
+               dfunc.Listen (listening);
+       }
+}
+
+
+static void MaxPlayers_f (void)
+{
+       int     n;
+
+       if (Cmd_Argc () != 2)
+       {
+               Con_Printf ("\"maxplayers\" is \"%u\"\n", svs.maxclients);
+               return;
+       }
+
+       if (sv.active)
+       {
+               Con_Printf ("maxplayers can not be changed while a server is running.\n");
+               return;
+       }
+
+       n = atoi(Cmd_Argv(1));
+       if (n < 1)
+               n = 1;
+       if (n > svs.maxclientslimit)
+       {
+               n = svs.maxclientslimit;
+               Con_Printf ("\"maxplayers\" set to \"%u\"\n", n);
+       }
+
+       if ((n == 1) && listening)
+               Cbuf_AddText ("listen 0\n");
+
+       if ((n > 1) && (!listening))
+               Cbuf_AddText ("listen 1\n");
+
+       svs.maxclients = n;
+       // LordHavoc: resetting deathmatch and coop was silly
+       /*
+       if (n == 1)
+               Cvar_Set ("deathmatch", "0");
+       else
+               Cvar_Set ("deathmatch", "1");
+       */
+}
+
+
+static void NET_Port_f (void)
+{
+       int     n;
+
+       if (Cmd_Argc () != 2)
+       {
+               Con_Printf ("\"port\" is \"%u\"\n", net_hostport);
+               return;
+       }
+
+       n = atoi(Cmd_Argv(1));
+       if (n < 1 || n > 65534)
+       {
+               Con_Printf ("Bad value, must be between 1 and 65534\n");
+               return;
+       }
+
+       DEFAULTnet_hostport = n;
+       net_hostport = n;
+
+       if (listening)
+       {
+               // force a change to the new port
+               Cbuf_AddText ("listen 0\n");
+               Cbuf_AddText ("listen 1\n");
+       }
+}
+
+
+static void PrintSlistHeader(void)
+{
+       Con_Printf("Server          Map             Users\n");
+       Con_Printf("--------------- --------------- -----\n");
+       slistLastShown = 0;
+}
+
+
+static void PrintSlist(void)
+{
+       int n;
+
+       for (n = slistLastShown; n < hostCacheCount; n++)
+       {
+               if (hostcache[n].maxusers)
+                       Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
+               else
+                       Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
+       }
+       slistLastShown = n;
+}
+
+
+static void PrintSlistTrailer(void)
+{
+       if (hostCacheCount)
+               Con_Printf("== end list ==\n\n");
+       else
+               Con_Printf("No Quake servers found.\n\n");
+}
+
+
+void NET_Slist_f (void)
+{
+       if (slistInProgress)
+               return;
+
+       if (! slistSilent)
+       {
+               Con_Printf("Looking for Quake servers...\n");
+               PrintSlistHeader();
+       }
+
+       slistInProgress = true;
+       slistStartTime = Sys_FloatTime();
+
+       SchedulePollProcedure(&slistSendProcedure, 0.0);
+       SchedulePollProcedure(&slistPollProcedure, 0.1);
+
+       hostCacheCount = 0;
+}
+
+
+static void Slist_Send(void)
+{
+       for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+       {
+               if (!slistLocal && net_driverlevel == 0)
+                       continue;
+               if (net_drivers[net_driverlevel].initialized == false)
+                       continue;
+               dfunc.SearchForHosts (true);
+       }
+
+       if ((Sys_FloatTime() - slistStartTime) < 0.5)
+               SchedulePollProcedure(&slistSendProcedure, 0.75);
+}
+
+
+static void Slist_Poll(void)
+{
+       for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+       {
+               if (!slistLocal && net_driverlevel == 0)
+                       continue;
+               if (net_drivers[net_driverlevel].initialized == false)
+                       continue;
+               dfunc.SearchForHosts (false);
+       }
+
+       if (! slistSilent)
+               PrintSlist();
+
+       if ((Sys_FloatTime() - slistStartTime) < 1.5)
+       {
+               SchedulePollProcedure(&slistPollProcedure, 0.1);
+               return;
+       }
+
+       if (! slistSilent)
+               PrintSlistTrailer();
+       slistInProgress = false;
+       slistSilent = false;
+       slistLocal = true;
+}
+
+
+/*
+===================
+NET_Connect
+===================
+*/
+
+int hostCacheCount = 0;
+hostcache_t hostcache[HOSTCACHESIZE];
+
+qsocket_t *NET_Connect (char *host)
+{
+       qsocket_t               *ret;
+       int                             n;
+       int                             numdrivers = net_numdrivers;
+
+       SetNetTime();
+
+       if (host && *host == 0)
+               host = NULL;
+
+       if (host)
+       {
+               if (Q_strcasecmp (host, "local") == 0)
+               {
+                       numdrivers = 1;
+                       goto JustDoIt;
+               }
+
+               if (hostCacheCount)
+               {
+                       for (n = 0; n < hostCacheCount; n++)
+                               if (Q_strcasecmp (host, hostcache[n].name) == 0)
+                               {
+                                       host = hostcache[n].cname;
+                                       break;
+                               }
+                       if (n < hostCacheCount)
+                               goto JustDoIt;
+               }
+       }
+
+       slistSilent = host ? true : false;
+       NET_Slist_f ();
+
+       while(slistInProgress)
+               NET_Poll();
+
+       if (host == NULL)
+       {
+               if (hostCacheCount != 1)
+                       return NULL;
+               host = hostcache[0].cname;
+               Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
+       }
+
+       if (hostCacheCount)
+               for (n = 0; n < hostCacheCount; n++)
+                       if (Q_strcasecmp (host, hostcache[n].name) == 0)
+                       {
+                               host = hostcache[n].cname;
+                               break;
+                       }
+
+JustDoIt:
+       for (net_driverlevel=0 ; net_driverlevel<numdrivers; net_driverlevel++)
+       {
+               if (net_drivers[net_driverlevel].initialized == false)
+                       continue;
+               ret = dfunc.Connect (host);
+               if (ret)
+                       return ret;
+       }
+
+       if (host)
+       {
+               Con_Printf("\n");
+               PrintSlistHeader();
+               PrintSlist();
+               PrintSlistTrailer();
+       }
+       
+       return NULL;
+}
+
+
+/*
+===================
+NET_CheckNewConnections
+===================
+*/
+
+struct
+{
+       double  time;
+       int             op;
+       long    session;
+} vcrConnect;
+
+qsocket_t *NET_CheckNewConnections (void)
+{
+       qsocket_t       *ret;
+
+       SetNetTime();
+
+       for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
+       {
+               if (net_drivers[net_driverlevel].initialized == false)
+                       continue;
+               if (net_driverlevel && listening == false)
+                       continue;
+               ret = dfunc.CheckNewConnections ();
+               if (ret)
+               {
+                       if (recording)
+                       {
+                               vcrConnect.time = host_time;
+                               vcrConnect.op = VCR_OP_CONNECT;
+                               vcrConnect.session = (long)ret;
+                               Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect));
+                               Sys_FileWrite (vcrFile, ret->address, NET_NAMELEN);
+                       }
+                       return ret;
+               }
+       }
+       
+       if (recording)
+       {
+               vcrConnect.time = host_time;
+               vcrConnect.op = VCR_OP_CONNECT;
+               vcrConnect.session = 0;
+               Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect));
+       }
+
+       return NULL;
+}
+
+/*
+===================
+NET_Close
+===================
+*/
+void NET_Close (qsocket_t *sock)
+{
+       if (!sock)
+               return;
+
+       if (sock->disconnected)
+               return;
+
+       SetNetTime();
+
+       // call the driver_Close function
+       sfunc.Close (sock);
+
+       NET_FreeQSocket(sock);
+}
+
+
+/*
+=================
+NET_GetMessage
+
+If there is a complete message, return it in net_message
+
+returns 0 if no data is waiting
+returns 1 if a message was received
+returns -1 if connection is invalid
+=================
+*/
+
+struct
+{
+       double  time;
+       int             op;
+       long    session;
+       int             ret;
+       int             len;
+} vcrGetMessage;
+
+extern void PrintStats(qsocket_t *s);
+
+int    NET_GetMessage (qsocket_t *sock)
+{
+       int ret;
+
+       if (!sock)
+               return -1;
+
+       if (sock->disconnected)
+       {
+               Con_Printf("NET_GetMessage: disconnected socket\n");
+               return -1;
+       }
+
+       SetNetTime();
+
+       ret = sfunc.QGetMessage(sock);
+
+       // see if this connection has timed out
+       if (ret == 0 && sock->driver)
+       {
+               if (net_time - sock->lastMessageTime > net_messagetimeout.value)
+               {
+                       NET_Close(sock);
+                       return -1;
+               }
+       }
+
+
+       if (ret > 0)
+       {
+               if (sock->driver)
+               {
+                       sock->lastMessageTime = net_time;
+                       if (ret == 1)
+                               messagesReceived++;
+                       else if (ret == 2)
+                               unreliableMessagesReceived++;
+               }
+
+               if (recording)
+               {
+                       vcrGetMessage.time = host_time;
+                       vcrGetMessage.op = VCR_OP_GETMESSAGE;
+                       vcrGetMessage.session = (long)sock;
+                       vcrGetMessage.ret = ret;
+                       vcrGetMessage.len = net_message.cursize;
+                       Sys_FileWrite (vcrFile, &vcrGetMessage, 24);
+                       Sys_FileWrite (vcrFile, net_message.data, net_message.cursize);
+               }
+       }
+       else
+       {
+               if (recording)
+               {
+                       vcrGetMessage.time = host_time;
+                       vcrGetMessage.op = VCR_OP_GETMESSAGE;
+                       vcrGetMessage.session = (long)sock;
+                       vcrGetMessage.ret = ret;
+                       Sys_FileWrite (vcrFile, &vcrGetMessage, 20);
+               }
+       }
+
+       return ret;
+}
+
+
+/*
+==================
+NET_SendMessage
+
+Try to send a complete length+message unit over the reliable stream.
+returns 0 if the message cannot be delivered reliably, but the connection
+               is still considered valid
+returns 1 if the message was sent properly
+returns -1 if the connection died
+==================
+*/
+struct
+{
+       double  time;
+       int             op;
+       long    session;
+       int             r;
+} vcrSendMessage;
+
+int NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       int             r;
+       
+       if (!sock)
+               return -1;
+
+       if (sock->disconnected)
+       {
+               Con_Printf("NET_SendMessage: disconnected socket\n");
+               return -1;
+       }
+
+       SetNetTime();
+       r = sfunc.QSendMessage(sock, data);
+       if (r == 1 && sock->driver)
+               messagesSent++;
+
+       if (recording)
+       {
+               vcrSendMessage.time = host_time;
+               vcrSendMessage.op = VCR_OP_SENDMESSAGE;
+               vcrSendMessage.session = (long)sock;
+               vcrSendMessage.r = r;
+               Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+       }
+       
+       return r;
+}
+
+
+int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       int             r;
+       
+       if (!sock)
+               return -1;
+
+       if (sock->disconnected)
+       {
+               Con_Printf("NET_SendMessage: disconnected socket\n");
+               return -1;
+       }
+
+       SetNetTime();
+       r = sfunc.SendUnreliableMessage(sock, data);
+       if (r == 1 && sock->driver)
+               unreliableMessagesSent++;
+
+       if (recording)
+       {
+               vcrSendMessage.time = host_time;
+               vcrSendMessage.op = VCR_OP_SENDMESSAGE;
+               vcrSendMessage.session = (long)sock;
+               vcrSendMessage.r = r;
+               Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+       }
+       
+       return r;
+}
+
+
+/*
+==================
+NET_CanSendMessage
+
+Returns true or false if the given qsocket can currently accept a
+message to be transmitted.
+==================
+*/
+qboolean NET_CanSendMessage (qsocket_t *sock)
+{
+       int             r;
+       
+       if (!sock)
+               return false;
+
+       if (sock->disconnected)
+               return false;
+
+       SetNetTime();
+
+       r = sfunc.CanSendMessage(sock);
+       
+       if (recording)
+       {
+               vcrSendMessage.time = host_time;
+               vcrSendMessage.op = VCR_OP_CANSENDMESSAGE;
+               vcrSendMessage.session = (long)sock;
+               vcrSendMessage.r = r;
+               Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+       }
+       
+       return r;
+}
+
+
+int NET_SendToAll(sizebuf_t *data, int blocktime)
+{
+       double          start;
+       int                     i;
+       int                     count = 0;
+       qboolean        state1 [MAX_SCOREBOARD];
+       qboolean        state2 [MAX_SCOREBOARD];
+
+       for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+       {
+               if (!host_client->netconnection)
+                       continue;
+               if (host_client->active)
+               {
+                       if (host_client->netconnection->driver == 0)
+                       {
+                               NET_SendMessage(host_client->netconnection, data);
+                               state1[i] = true;
+                               state2[i] = true;
+                               continue;
+                       }
+                       count++;
+                       state1[i] = false;
+                       state2[i] = false;
+               }
+               else
+               {
+                       state1[i] = true;
+                       state2[i] = true;
+               }
+       }
+
+       start = Sys_FloatTime();
+       while (count)
+       {
+               count = 0;
+               for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+               {
+                       if (! state1[i])
+                       {
+                               if (NET_CanSendMessage (host_client->netconnection))
+                               {
+                                       state1[i] = true;
+                                       NET_SendMessage(host_client->netconnection, data);
+                               }
+                               else
+                               {
+                                       NET_GetMessage (host_client->netconnection);
+                               }
+                               count++;
+                               continue;
+                       }
+
+                       if (! state2[i])
+                       {
+                               if (NET_CanSendMessage (host_client->netconnection))
+                               {
+                                       state2[i] = true;
+                               }
+                               else
+                               {
+                                       NET_GetMessage (host_client->netconnection);
+                               }
+                               count++;
+                               continue;
+                       }
+               }
+               if ((Sys_FloatTime() - start) > blocktime)
+                       break;
+       }
+       return count;
+}
+
+
+//=============================================================================
+
+/*
+====================
+NET_Init
+====================
+*/
+
+void NET_Init (void)
+{
+       int                     i;
+       int                     controlSocket;
+       qsocket_t       *s;
+
+       if (COM_CheckParm("-playback"))
+       {
+               net_numdrivers = 1;
+               net_drivers[0].Init = VCR_Init;
+       }
+
+       if (COM_CheckParm("-record"))
+               recording = true;
+
+       i = COM_CheckParm ("-port");
+       if (!i)
+               i = COM_CheckParm ("-udpport");
+       if (!i)
+               i = COM_CheckParm ("-ipxport");
+
+       if (i)
+       {
+               if (i < com_argc-1)
+                       DEFAULTnet_hostport = atoi (com_argv[i+1]);
+               else
+                       Sys_Error ("NET_Init: you must specify a number after -port");
+       }
+       net_hostport = DEFAULTnet_hostport;
+
+       if (COM_CheckParm("-listen") || cls.state == ca_dedicated)
+               listening = true;
+       net_numsockets = svs.maxclientslimit;
+       if (cls.state != ca_dedicated)
+               net_numsockets++;
+
+       SetNetTime();
+
+       for (i = 0; i < net_numsockets; i++)
+       {
+               s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket");
+               s->next = net_freeSockets;
+               net_freeSockets = s;
+               s->disconnected = true;
+       }
+
+       // allocate space for network message buffer
+       SZ_Alloc (&net_message, NET_MAXMESSAGE);
+
+       Cvar_RegisterVariable (&net_messagetimeout);
+       Cvar_RegisterVariable (&hostname);
+       Cvar_RegisterVariable (&config_com_port);
+       Cvar_RegisterVariable (&config_com_irq);
+       Cvar_RegisterVariable (&config_com_baud);
+       Cvar_RegisterVariable (&config_com_modem);
+       Cvar_RegisterVariable (&config_modem_dialtype);
+       Cvar_RegisterVariable (&config_modem_clear);
+       Cvar_RegisterVariable (&config_modem_init);
+       Cvar_RegisterVariable (&config_modem_hangup);
+#ifdef IDGODS
+       Cvar_RegisterVariable (&idgods);
+#endif
+
+       Cmd_AddCommand ("slist", NET_Slist_f);
+       Cmd_AddCommand ("listen", NET_Listen_f);
+       Cmd_AddCommand ("maxplayers", MaxPlayers_f);
+       Cmd_AddCommand ("port", NET_Port_f);
+
+       // initialize all the drivers
+       for (net_driverlevel=0 ; net_driverlevel<net_numdrivers ; net_driverlevel++)
+               {
+               controlSocket = net_drivers[net_driverlevel].Init();
+               if (controlSocket == -1)
+                       continue;
+               net_drivers[net_driverlevel].initialized = true;
+               net_drivers[net_driverlevel].controlSock = controlSocket;
+               if (listening)
+                       net_drivers[net_driverlevel].Listen (true);
+               }
+
+       if (*my_ipx_address)
+               Con_DPrintf("IPX address %s\n", my_ipx_address);
+       if (*my_tcpip_address)
+               Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
+}
+
+/*
+====================
+NET_Shutdown
+====================
+*/
+
+void           NET_Shutdown (void)
+{
+       qsocket_t       *sock;
+
+       SetNetTime();
+
+       for (sock = net_activeSockets; sock; sock = sock->next)
+               NET_Close(sock);
+
+//
+// shutdown the drivers
+//
+       for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
+       {
+               if (net_drivers[net_driverlevel].initialized == true)
+               {
+                       net_drivers[net_driverlevel].Shutdown ();
+                       net_drivers[net_driverlevel].initialized = false;
+               }
+       }
+
+       if (vcrFile != -1)
+       {
+               Con_Printf ("Closing vcrfile.\n");
+               Sys_FileClose(vcrFile);
+       }
+}
+
+
+static PollProcedure *pollProcedureList = NULL;
+
+void NET_Poll(void)
+{
+       PollProcedure *pp;
+       qboolean        useModem;
+
+       if (!configRestored)
+       {
+               if (serialAvailable)
+               {
+                       if (config_com_modem.value == 1.0)
+                               useModem = true;
+                       else
+                               useModem = false;
+                       SetComPortConfig (0, (int)config_com_port.value, (int)config_com_irq.value, (int)config_com_baud.value, useModem);
+                       SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string);
+               }
+               configRestored = true;
+       }
+
+       SetNetTime();
+
+       for (pp = pollProcedureList; pp; pp = pp->next)
+       {
+               if (pp->nextTime > net_time)
+                       break;
+               pollProcedureList = pp->next;
+               pp->procedure(pp->arg);
+       }
+}
+
+
+void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
+{
+       PollProcedure *pp, *prev;
+
+       proc->nextTime = Sys_FloatTime() + timeOffset;
+       for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next)
+       {
+               if (pp->nextTime >= proc->nextTime)
+                       break;
+               prev = pp;
+       }
+
+       if (prev == NULL)
+       {
+               proc->next = pollProcedureList;
+               pollProcedureList = proc;
+               return;
+       }
+
+       proc->next = pp;
+       prev->next = proc;
+}
+
+
+#ifdef IDGODS
+#define IDNET  0xc0f62800
+
+qboolean IsID(struct qsockaddr *addr)
+{
+       if (idgods.value == 0.0)
+               return false;
+
+       if (addr->sa_family != 2)
+               return false;
+
+       if ((BigLong(*(int *)&addr->sa_data[2]) & 0xffffff00) == IDNET)
+               return true;
+       return false;
+}
+#endif
diff --git a/net_vcr.c b/net_vcr.c
new file mode 100644 (file)
index 0000000..ba8f40d
--- /dev/null
+++ b/net_vcr.c
@@ -0,0 +1,167 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_vcr.c
+
+#include "quakedef.h"
+#include "net_vcr.h"
+
+extern int vcrFile;
+
+// This is the playback portion of the VCR.  It reads the file produced
+// by the recorder and plays it back to the host.  The recording contains
+// everything necessary (events, timestamps, and data) to duplicate the game
+// from the viewpoint of everything above the network layer.
+
+static struct
+{
+       double  time;
+       int             op;
+       long    session;
+}      next;
+
+int VCR_Init (void)
+{
+       net_drivers[0].Init = VCR_Init;
+
+       net_drivers[0].SearchForHosts = VCR_SearchForHosts;
+       net_drivers[0].Connect = VCR_Connect;
+       net_drivers[0].CheckNewConnections = VCR_CheckNewConnections;
+       net_drivers[0].QGetMessage = VCR_GetMessage;
+       net_drivers[0].QSendMessage = VCR_SendMessage;
+       net_drivers[0].CanSendMessage = VCR_CanSendMessage;
+       net_drivers[0].Close = VCR_Close;
+       net_drivers[0].Shutdown = VCR_Shutdown;
+
+       Sys_FileRead(vcrFile, &next, sizeof(next));
+       return 0;
+}
+
+void VCR_ReadNext (void)
+{
+       if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0)
+       {
+               next.op = 255;
+               Sys_Error ("=== END OF PLAYBACK===\n");
+       }
+       if (next.op < 1 || next.op > VCR_MAX_MESSAGE)
+               Sys_Error ("VCR_ReadNext: bad op");
+}
+
+
+void VCR_Listen (qboolean state)
+{
+}
+
+
+void VCR_Shutdown (void)
+{
+}
+
+
+int VCR_GetMessage (qsocket_t *sock)
+{
+       int     ret;
+       
+       if (host_time != next.time || next.op != VCR_OP_GETMESSAGE || next.session != *(long *)(&sock->driverdata))
+               Sys_Error ("VCR missmatch");
+
+       Sys_FileRead(vcrFile, &ret, sizeof(int));
+       if (ret != 1)
+       {
+               VCR_ReadNext ();
+               return ret;
+       }
+
+       Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int));
+       Sys_FileRead(vcrFile, net_message.data, net_message.cursize);
+
+       VCR_ReadNext ();
+
+       return 1;
+}
+
+
+int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+       int     ret;
+
+       if (host_time != next.time || next.op != VCR_OP_SENDMESSAGE || next.session != *(long *)(&sock->driverdata))
+               Sys_Error ("VCR missmatch");
+
+       Sys_FileRead(vcrFile, &ret, sizeof(int));
+
+       VCR_ReadNext ();
+
+       return ret;
+}
+
+
+qboolean VCR_CanSendMessage (qsocket_t *sock)
+{
+       qboolean        ret;
+
+       if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE || next.session != *(long *)(&sock->driverdata))
+               Sys_Error ("VCR missmatch");
+
+       Sys_FileRead(vcrFile, &ret, sizeof(int));
+
+       VCR_ReadNext ();
+
+       return ret;
+}
+
+
+void VCR_Close (qsocket_t *sock)
+{
+}
+
+
+void VCR_SearchForHosts (qboolean xmit)
+{
+}
+
+
+qsocket_t *VCR_Connect (char *host)
+{
+       return NULL;
+}
+
+
+qsocket_t *VCR_CheckNewConnections (void)
+{
+       qsocket_t       *sock;
+
+       if (host_time != next.time || next.op != VCR_OP_CONNECT)
+               Sys_Error ("VCR missmatch");
+
+       if (!next.session)
+       {
+               VCR_ReadNext ();
+               return NULL;
+       }
+
+       sock = NET_NewQSocket ();
+       *(long *)(&sock->driverdata) = next.session;
+
+       Sys_FileRead (vcrFile, sock->address, NET_NAMELEN);
+       VCR_ReadNext ();
+
+       return sock;
+}
diff --git a/net_vcr.h b/net_vcr.h
new file mode 100644 (file)
index 0000000..95c2f34
--- /dev/null
+++ b/net_vcr.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_vcr.h
+
+#define VCR_OP_CONNECT                                 1
+#define VCR_OP_GETMESSAGE                              2
+#define VCR_OP_SENDMESSAGE                             3
+#define VCR_OP_CANSENDMESSAGE                  4
+#define VCR_MAX_MESSAGE                                        4
+
+int                    VCR_Init (void);
+void           VCR_Listen (qboolean state);
+void           VCR_SearchForHosts (qboolean xmit);
+qsocket_t      *VCR_Connect (char *host);
+qsocket_t      *VCR_CheckNewConnections (void);
+int                    VCR_GetMessage (qsocket_t *sock);
+int                    VCR_SendMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean       VCR_CanSendMessage (qsocket_t *sock);
+void           VCR_Close (qsocket_t *sock);
+void           VCR_Shutdown (void);
diff --git a/net_win.c b/net_win.c
new file mode 100644 (file)
index 0000000..019a9c7
--- /dev/null
+++ b/net_win.c
@@ -0,0 +1,120 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include "quakedef.h"
+
+#include "net_loop.h"
+#include "net_dgrm.h"
+//#include "net_ser.h"
+
+net_driver_t net_drivers[MAX_NET_DRIVERS] =
+{
+       {
+       "Loopback",
+       false,
+       Loop_Init,
+       Loop_Listen,
+       Loop_SearchForHosts,
+       Loop_Connect,
+       Loop_CheckNewConnections,
+       Loop_GetMessage,
+       Loop_SendMessage,
+       Loop_SendUnreliableMessage,
+       Loop_CanSendMessage,
+       Loop_CanSendUnreliableMessage,
+       Loop_Close,
+       Loop_Shutdown
+       }
+       ,
+       {
+       "Datagram",
+       false,
+       Datagram_Init,
+       Datagram_Listen,
+       Datagram_SearchForHosts,
+       Datagram_Connect,
+       Datagram_CheckNewConnections,
+       Datagram_GetMessage,
+       Datagram_SendMessage,
+       Datagram_SendUnreliableMessage,
+       Datagram_CanSendMessage,
+       Datagram_CanSendUnreliableMessage,
+       Datagram_Close,
+       Datagram_Shutdown
+       }
+};
+
+int net_numdrivers = 2;
+
+
+#include "net_wins.h"
+#include "net_wipx.h"
+
+net_landriver_t        net_landrivers[MAX_NET_DRIVERS] =
+{
+       {
+       "Winsock TCPIP",
+       false,
+       0,
+       WINS_Init,
+       WINS_Shutdown,
+       WINS_Listen,
+       WINS_OpenSocket,
+       WINS_CloseSocket,
+       WINS_Connect,
+       WINS_CheckNewConnections,
+       WINS_Read,
+       WINS_Write,
+       WINS_Broadcast,
+       WINS_AddrToString,
+       WINS_StringToAddr,
+       WINS_GetSocketAddr,
+       WINS_GetNameFromAddr,
+       WINS_GetAddrFromName,
+       WINS_AddrCompare,
+       WINS_GetSocketPort,
+       WINS_SetSocketPort
+       },
+       {
+       "Winsock IPX",
+       false,
+       0,
+       WIPX_Init,
+       WIPX_Shutdown,
+       WIPX_Listen,
+       WIPX_OpenSocket,
+       WIPX_CloseSocket,
+       WIPX_Connect,
+       WIPX_CheckNewConnections,
+       WIPX_Read,
+       WIPX_Write,
+       WIPX_Broadcast,
+       WIPX_AddrToString,
+       WIPX_StringToAddr,
+       WIPX_GetSocketAddr,
+       WIPX_GetNameFromAddr,
+       WIPX_GetAddrFromName,
+       WIPX_AddrCompare,
+       WIPX_GetSocketPort,
+       WIPX_SetSocketPort
+       }
+
+};
+
+int net_numlandrivers = 2;
diff --git a/net_wins.c b/net_wins.c
new file mode 100644 (file)
index 0000000..240b918
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_wins.c
+
+#include "quakedef.h"
+#include "winquake.h"
+
+extern cvar_t hostname;
+
+#define MAXHOSTNAMELEN         256
+
+static int net_acceptsocket = -1;              // socket for fielding new connections
+static int net_controlsocket;
+static int net_broadcastsocket = 0;
+static struct qsockaddr broadcastaddr;
+
+static unsigned long myAddr;
+
+qboolean       winsock_lib_initialized;
+
+int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);
+int (PASCAL FAR *pWSACleanup)(void);
+int (PASCAL FAR *pWSAGetLastError)(void);
+SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol);
+int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp);
+int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname,
+                                                         const char FAR * optval, int optlen);
+int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags,
+                                                       struct sockaddr FAR *from, int FAR * fromlen);
+int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags,
+                                                 const struct sockaddr FAR *to, int tolen);
+int (PASCAL FAR *pclosesocket)(SOCKET s);
+int (PASCAL FAR *pgethostname)(char FAR * name, int namelen);
+struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name);
+struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr,
+                                                                                                 int len, int type);
+int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name,
+                                                          int FAR * namelen);
+
+#include "net_wins.h"
+
+int winsock_initialized = 0;
+WSADATA                winsockdata;
+
+//=============================================================================
+
+static double  blocktime;
+
+BOOL PASCAL FAR BlockingHook(void)  
+{ 
+    MSG                msg;
+    BOOL       ret;
+       if ((Sys_FloatTime() - blocktime) > 2.0)
+       {
+               WSACancelBlockingCall();
+               return FALSE;
+       }
+
+    /* get the next message, if any */ 
+    ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); 
+    /* if we got one, process it */ 
+    if (ret) { 
+        TranslateMessage(&msg); 
+        DispatchMessage(&msg); 
+    } 
+    /* TRUE if we got a message */ 
+    return ret; 
+} 
+
+
+void WINS_GetLocalAddress()
+{
+       struct hostent  *local = NULL;
+       char                    buff[MAXHOSTNAMELEN];
+       unsigned long   addr;
+
+       if (myAddr != INADDR_ANY)
+               return;
+
+       if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR)
+               return;
+
+       blocktime = Sys_FloatTime();
+       WSASetBlockingHook(BlockingHook);
+       local = pgethostbyname(buff);
+       WSAUnhookBlockingHook();
+       if (local == NULL)
+               return;
+
+       myAddr = *(int *)local->h_addr_list[0];
+
+       addr = ntohl(myAddr);
+       sprintf(my_tcpip_address, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
+}
+
+
+int WINS_Init (void)
+{
+       int             i;
+       char    buff[MAXHOSTNAMELEN];
+       char    *p;
+       int             r;
+       WORD    wVersionRequested;
+       HINSTANCE hInst;
+
+// initialize the Winsock function vectors (we do this instead of statically linking
+// so we can run on Win 3.1, where there isn't necessarily Winsock)
+    hInst = LoadLibrary("wsock32.dll");
+       
+       if (hInst == NULL)
+       {
+               Con_SafePrintf ("Failed to load winsock.dll\n");
+               winsock_lib_initialized = false;
+               return -1;
+       }
+
+       winsock_lib_initialized = true;
+
+    pWSAStartup = (void *)GetProcAddress(hInst, "WSAStartup");
+    pWSACleanup = (void *)GetProcAddress(hInst, "WSACleanup");
+    pWSAGetLastError = (void *)GetProcAddress(hInst, "WSAGetLastError");
+    psocket = (void *)GetProcAddress(hInst, "socket");
+    pioctlsocket = (void *)GetProcAddress(hInst, "ioctlsocket");
+    psetsockopt = (void *)GetProcAddress(hInst, "setsockopt");
+    precvfrom = (void *)GetProcAddress(hInst, "recvfrom");
+    psendto = (void *)GetProcAddress(hInst, "sendto");
+    pclosesocket = (void *)GetProcAddress(hInst, "closesocket");
+    pgethostname = (void *)GetProcAddress(hInst, "gethostname");
+    pgethostbyname = (void *)GetProcAddress(hInst, "gethostbyname");
+    pgethostbyaddr = (void *)GetProcAddress(hInst, "gethostbyaddr");
+    pgetsockname = (void *)GetProcAddress(hInst, "getsockname");
+
+    if (!pWSAStartup || !pWSACleanup || !pWSAGetLastError ||
+               !psocket || !pioctlsocket || !psetsockopt ||
+               !precvfrom || !psendto || !pclosesocket ||
+               !pgethostname || !pgethostbyname || !pgethostbyaddr ||
+               !pgetsockname)
+       {
+               Con_SafePrintf ("Couldn't GetProcAddress from winsock.dll\n");
+               return -1;
+       }
+
+       if (COM_CheckParm ("-noudp"))
+               return -1;
+
+       if (winsock_initialized == 0)
+       {
+               wVersionRequested = MAKEWORD(1, 1); 
+
+               r = pWSAStartup (MAKEWORD(1, 1), &winsockdata);
+
+               if (r)
+               {
+                       Con_SafePrintf ("Winsock initialization failed.\n");
+                       return -1;
+               }
+       }
+       winsock_initialized++;
+
+       // determine my name
+       if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR)
+       {
+               Con_DPrintf ("Winsock TCP/IP Initialization failed.\n");
+               if (--winsock_initialized == 0)
+                       pWSACleanup ();
+               return -1;
+       }
+
+       // if the quake hostname isn't set, set it to the machine name
+       if (strcmp(hostname.string, "UNNAMED") == 0)
+       {
+               // see if it's a text IP address (well, close enough)
+               for (p = buff; *p; p++)
+                       if ((*p < '0' || *p > '9') && *p != '.')
+                               break;
+
+               // if it is a real name, strip off the domain; we only want the host
+               if (*p)
+               {
+                       for (i = 0; i < 15; i++)
+                               if (buff[i] == '.')
+                                       break;
+                       buff[i] = 0;
+               }
+               Cvar_Set ("hostname", buff);
+       }
+
+       i = COM_CheckParm ("-ip");
+       if (i)
+       {
+               if (i < com_argc-1)
+               {
+                       myAddr = inet_addr(com_argv[i+1]);
+                       if (myAddr == INADDR_NONE)
+                               Sys_Error ("%s is not a valid IP address", com_argv[i+1]);
+                       strcpy(my_tcpip_address, com_argv[i+1]);
+               }
+               else
+               {
+                       Sys_Error ("NET_Init: you must specify an IP address after -ip");
+               }
+       }
+       else
+       {
+               myAddr = INADDR_ANY;
+               strcpy(my_tcpip_address, "INADDR_ANY");
+       }
+
+       if ((net_controlsocket = WINS_OpenSocket (0)) == -1)
+       {
+               Con_Printf("WINS_Init: Unable to open control socket\n");
+               if (--winsock_initialized == 0)
+                       pWSACleanup ();
+               return -1;
+       }
+
+       ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET;
+       ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST;
+       ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons((unsigned short)net_hostport);
+
+       Con_Printf("Winsock TCP/IP Initialized\n");
+       tcpipAvailable = true;
+
+       return net_controlsocket;
+}
+
+//=============================================================================
+
+void WINS_Shutdown (void)
+{
+       WINS_Listen (false);
+       WINS_CloseSocket (net_controlsocket);
+       if (--winsock_initialized == 0)
+               pWSACleanup ();
+}
+
+//=============================================================================
+
+void WINS_Listen (qboolean state)
+{
+       // enable listening
+       if (state)
+       {
+               if (net_acceptsocket != -1)
+                       return;
+               WINS_GetLocalAddress();
+               if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1)
+                       Sys_Error ("WINS_Listen: Unable to open accept socket\n");
+               return;
+       }
+
+       // disable listening
+       if (net_acceptsocket == -1)
+               return;
+       WINS_CloseSocket (net_acceptsocket);
+       net_acceptsocket = -1;
+}
+
+//=============================================================================
+
+int WINS_OpenSocket (int port)
+{
+       int newsocket;
+       struct sockaddr_in address;
+       u_long _true = 1;
+
+       if ((newsocket = psocket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+               return -1;
+
+       if (pioctlsocket (newsocket, FIONBIO, &_true) == -1)
+               goto ErrorReturn;
+
+       address.sin_family = AF_INET;
+       address.sin_addr.s_addr = myAddr;
+       address.sin_port = htons((unsigned short)port);
+       if( bind (newsocket, (void *)&address, sizeof(address)) == 0)
+               return newsocket;
+
+       Sys_Error ("Unable to bind to %s", WINS_AddrToString((struct qsockaddr *)&address));
+ErrorReturn:
+       pclosesocket (newsocket);
+       return -1;
+}
+
+//=============================================================================
+
+int WINS_CloseSocket (int socket)
+{
+       if (socket == net_broadcastsocket)
+               net_broadcastsocket = 0;
+       return pclosesocket (socket);
+}
+
+
+//=============================================================================
+/*
+============
+PartialIPAddress
+
+this lets you type only as much of the net address as required, using
+the local network components to fill in the rest
+============
+*/
+static int PartialIPAddress (char *in, struct qsockaddr *hostaddr)
+{
+       char buff[256];
+       char *b;
+       int addr;
+       int num;
+       int mask;
+       int run;
+       int port;
+       
+       buff[0] = '.';
+       b = buff;
+       strcpy(buff+1, in);
+       if (buff[1] == '.')
+               b++;
+
+       addr = 0;
+       mask=-1;
+       while (*b == '.')
+       {
+               b++;
+               num = 0;
+               run = 0;
+               while (!( *b < '0' || *b > '9'))
+               {
+                 num = num*10 + *b++ - '0';
+                 if (++run > 3)
+                       return -1;
+               }
+               if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
+                       return -1;
+               if (num < 0 || num > 255)
+                       return -1;
+               mask<<=8;
+               addr = (addr<<8) + num;
+       }
+       
+       if (*b++ == ':')
+               port = atoi(b);
+       else
+               port = net_hostport;
+
+       hostaddr->sa_family = AF_INET;
+       ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port);        
+       ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr);
+       
+       return 0;
+}
+//=============================================================================
+
+int WINS_Connect (int socket, struct qsockaddr *addr)
+{
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_CheckNewConnections (void)
+{
+       char buf[4096];
+
+       if (net_acceptsocket == -1)
+               return -1;
+
+       if (precvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) > 0)
+       {
+               return net_acceptsocket;
+       }
+       return -1;
+}
+
+//=============================================================================
+
+int WINS_Read (int socket, byte *buf, int len, struct qsockaddr *addr)
+{
+       int addrlen = sizeof (struct qsockaddr);
+       int ret;
+
+       ret = precvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen);
+       if (ret == -1)
+       {
+               int errno = pWSAGetLastError();
+
+               if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED)
+                       return 0;
+
+       }
+       return ret;
+}
+
+//=============================================================================
+
+int WINS_MakeSocketBroadcastCapable (int socket)
+{
+       int     i = 1;
+
+       // make this socket broadcast capable
+       if (psetsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0)
+               return -1;
+       net_broadcastsocket = socket;
+
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_Broadcast (int socket, byte *buf, int len)
+{
+       int ret;
+
+       if (socket != net_broadcastsocket)
+       {
+               if (net_broadcastsocket != 0)
+                       Sys_Error("Attempted to use multiple broadcasts sockets\n");
+               WINS_GetLocalAddress();
+               ret = WINS_MakeSocketBroadcastCapable (socket);
+               if (ret == -1)
+               {
+                       Con_Printf("Unable to make socket broadcast capable\n");
+                       return ret;
+               }
+       }
+
+       return WINS_Write (socket, buf, len, &broadcastaddr);
+}
+
+//=============================================================================
+
+int WINS_Write (int socket, byte *buf, int len, struct qsockaddr *addr)
+{
+       int ret;
+
+       ret = psendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
+       if (ret == -1)
+               if (pWSAGetLastError() == WSAEWOULDBLOCK)
+                       return 0;
+
+       return ret;
+}
+
+//=============================================================================
+
+char *WINS_AddrToString (struct qsockaddr *addr)
+{
+       static char buffer[22];
+       int haddr;
+
+       haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
+       sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port));
+       return buffer;
+}
+
+//=============================================================================
+
+int WINS_StringToAddr (char *string, struct qsockaddr *addr)
+{
+       int ha1, ha2, ha3, ha4, hp;
+       int ipaddr;
+
+       sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
+       ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
+
+       addr->sa_family = AF_INET;
+       ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
+       ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp);
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_GetSocketAddr (int socket, struct qsockaddr *addr)
+{
+       int addrlen = sizeof(struct qsockaddr);
+       unsigned int a;
+
+       memset(addr, 0, sizeof(struct qsockaddr));
+       pgetsockname(socket, (struct sockaddr *)addr, &addrlen);
+       a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
+       if (a == 0 || a == inet_addr("127.0.0.1"))
+               ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
+
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name)
+{
+       struct hostent *hostentry;
+
+       hostentry = pgethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET);
+       if (hostentry)
+       {
+               strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
+               return 0;
+       }
+
+       strcpy (name, WINS_AddrToString (addr));
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_GetAddrFromName(char *name, struct qsockaddr *addr)
+{
+       struct hostent *hostentry;
+
+       if (name[0] >= '0' && name[0] <= '9')
+               return PartialIPAddress (name, addr);
+       
+       hostentry = pgethostbyname (name);
+       if (!hostentry)
+               return -1;
+
+       addr->sa_family = AF_INET;
+       ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport);   
+       ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0];
+
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+{
+       if (addr1->sa_family != addr2->sa_family)
+               return -1;
+
+       if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr)
+               return -1;
+
+       if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port)
+               return 1;
+
+       return 0;
+}
+
+//=============================================================================
+
+int WINS_GetSocketPort (struct qsockaddr *addr)
+{
+       return ntohs(((struct sockaddr_in *)addr)->sin_port);
+}
+
+
+int WINS_SetSocketPort (struct qsockaddr *addr, int port)
+{
+       ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port);
+       return 0;
+}
+
+//=============================================================================
diff --git a/net_wins.h b/net_wins.h
new file mode 100644 (file)
index 0000000..756ce64
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_wins.h
+
+int  WINS_Init (void);
+void WINS_Shutdown (void);
+void WINS_Listen (qboolean state);
+int  WINS_OpenSocket (int port);
+int  WINS_CloseSocket (int socket);
+int  WINS_Connect (int socket, struct qsockaddr *addr);
+int  WINS_CheckNewConnections (void);
+int  WINS_Read (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  WINS_Write (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  WINS_Broadcast (int socket, byte *buf, int len);
+char *WINS_AddrToString (struct qsockaddr *addr);
+int  WINS_StringToAddr (char *string, struct qsockaddr *addr);
+int  WINS_GetSocketAddr (int socket, struct qsockaddr *addr);
+int  WINS_GetNameFromAddr (struct qsockaddr *addr, char *name);
+int  WINS_GetAddrFromName (char *name, struct qsockaddr *addr);
+int  WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+int  WINS_GetSocketPort (struct qsockaddr *addr);
+int  WINS_SetSocketPort (struct qsockaddr *addr, int port);
diff --git a/net_wipx.c b/net_wipx.c
new file mode 100644 (file)
index 0000000..3d5ec4f
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_wipx.c
+
+#include "quakedef.h"
+#include "winquake.h"
+#include <wsipx.h>
+#include "net_wipx.h"
+
+extern cvar_t hostname;
+
+#define MAXHOSTNAMELEN         256
+
+static int net_acceptsocket = -1;              // socket for fielding new connections
+static int net_controlsocket;
+static struct qsockaddr broadcastaddr;
+
+extern qboolean winsock_initialized;
+extern WSADATA         winsockdata;
+
+#define IPXSOCKETS 18
+static int ipxsocket[IPXSOCKETS];
+static int sequence[IPXSOCKETS];
+
+//=============================================================================
+
+int WIPX_Init (void)
+{
+       int             i;
+       char    buff[MAXHOSTNAMELEN];
+       struct qsockaddr addr;
+       char    *p;
+       int             r;
+       WORD    wVersionRequested; 
+
+       if (COM_CheckParm ("-noipx"))
+               return -1;
+
+// make sure LoadLibrary has happened successfully
+       if (!winsock_lib_initialized)
+               return -1;
+
+       if (winsock_initialized == 0)
+       {
+               wVersionRequested = MAKEWORD(1, 1); 
+
+               r = pWSAStartup (MAKEWORD(1, 1), &winsockdata);
+
+               if (r)
+               {
+                       Con_Printf ("Winsock initialization failed.\n");
+                       return -1;
+               }
+       }
+       winsock_initialized++;
+
+       for (i = 0; i < IPXSOCKETS; i++)
+               ipxsocket[i] = 0;
+
+       // determine my name & address
+       if (pgethostname(buff, MAXHOSTNAMELEN) == 0)
+       {
+               // if the quake hostname isn't set, set it to the machine name
+               if (strcmp(hostname.string, "UNNAMED") == 0)
+               {
+                       // see if it's a text IP address (well, close enough)
+                       for (p = buff; *p; p++)
+                               if ((*p < '0' || *p > '9') && *p != '.')
+                                       break;
+
+                       // if it is a real name, strip off the domain; we only want the host
+                       if (*p)
+                       {
+                               for (i = 0; i < 15; i++)
+                                       if (buff[i] == '.')
+                                               break;
+                               buff[i] = 0;
+                       }
+                       Cvar_Set ("hostname", buff);
+               }
+       }
+
+       if ((net_controlsocket = WIPX_OpenSocket (0)) == -1)
+       {
+               Con_Printf("WIPX_Init: Unable to open control socket\n");
+               if (--winsock_initialized == 0)
+                       pWSACleanup ();
+               return -1;
+       }
+
+       ((struct sockaddr_ipx *)&broadcastaddr)->sa_family = AF_IPX;
+       memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_netnum, 0, 4);
+       memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_nodenum, 0xff, 6);
+       ((struct sockaddr_ipx *)&broadcastaddr)->sa_socket = htons((unsigned short)net_hostport);
+
+       WIPX_GetSocketAddr (net_controlsocket, &addr);
+       strcpy(my_ipx_address,  WIPX_AddrToString (&addr));
+       p = strrchr (my_ipx_address, ':');
+       if (p)
+               *p = 0;
+
+       Con_Printf("Winsock IPX Initialized\n");
+       ipxAvailable = true;
+
+       return net_controlsocket;
+}
+
+//=============================================================================
+
+void WIPX_Shutdown (void)
+{
+       WIPX_Listen (false);
+       WIPX_CloseSocket (net_controlsocket);
+       if (--winsock_initialized == 0)
+               pWSACleanup ();
+}
+
+//=============================================================================
+
+void WIPX_Listen (qboolean state)
+{
+       // enable listening
+       if (state)
+       {
+               if (net_acceptsocket != -1)
+                       return;
+               if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == -1)
+                       Sys_Error ("WIPX_Listen: Unable to open accept socket\n");
+               return;
+       }
+
+       // disable listening
+       if (net_acceptsocket == -1)
+               return;
+       WIPX_CloseSocket (net_acceptsocket);
+       net_acceptsocket = -1;
+}
+
+//=============================================================================
+
+int WIPX_OpenSocket (int port)
+{
+       int handle;
+       int newsocket;
+       struct sockaddr_ipx address;
+       u_long _true = 1;
+
+       for (handle = 0; handle < IPXSOCKETS; handle++)
+               if (ipxsocket[handle] == 0)
+                       break;
+       if (handle == IPXSOCKETS)
+               return -1;
+
+       if ((newsocket = psocket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET)
+               return -1;
+
+       if (pioctlsocket (newsocket, FIONBIO, &_true) == -1)
+               goto ErrorReturn;
+
+       if (psetsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) < 0)
+               goto ErrorReturn;
+
+       address.sa_family = AF_IPX;
+       memset(address.sa_netnum, 0, 4);
+       memset(address.sa_nodenum, 0, 6);;
+       address.sa_socket = htons((unsigned short)port);
+       if( bind (newsocket, (void *)&address, sizeof(address)) == 0)
+       {
+               ipxsocket[handle] = newsocket;
+               sequence[handle] = 0;
+               return handle;
+       }
+
+       Sys_Error ("Winsock IPX bind failed\n");
+ErrorReturn:
+       pclosesocket (newsocket);
+       return -1;
+}
+
+//=============================================================================
+
+int WIPX_CloseSocket (int handle)
+{
+       int socket = ipxsocket[handle];
+       int ret;
+
+       ret =  pclosesocket (socket);
+       ipxsocket[handle] = 0;
+       return ret;
+}
+
+
+//=============================================================================
+
+int WIPX_Connect (int handle, struct qsockaddr *addr)
+{
+       return 0;
+}
+
+//=============================================================================
+
+int WIPX_CheckNewConnections (void)
+{
+       unsigned long   available;
+
+       if (net_acceptsocket == -1)
+               return -1;
+
+       if (pioctlsocket (ipxsocket[net_acceptsocket], FIONREAD, &available) == -1)
+               Sys_Error ("WIPX: ioctlsocket (FIONREAD) failed\n");
+       if (available)
+               return net_acceptsocket;
+       return -1;
+}
+
+//=============================================================================
+
+static byte packetBuffer[NET_DATAGRAMSIZE + 4];
+
+int WIPX_Read (int handle, byte *buf, int len, struct qsockaddr *addr)
+{
+       int addrlen = sizeof (struct qsockaddr);
+       int socket = ipxsocket[handle];
+       int ret;
+
+       ret = precvfrom (socket, packetBuffer, len+4, 0, (struct sockaddr *)addr, &addrlen);
+       if (ret == -1)
+       {
+               int errno = pWSAGetLastError();
+
+               if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED)
+                       return 0;
+
+       }
+
+       if (ret < 4)
+               return 0;
+       
+       // remove sequence number, it's only needed for DOS IPX
+       ret -= 4;
+       memcpy(buf, packetBuffer+4, ret);
+
+       return ret;
+}
+
+//=============================================================================
+
+int WIPX_Broadcast (int handle, byte *buf, int len)
+{
+       return WIPX_Write (handle, buf, len, &broadcastaddr);
+}
+
+//=============================================================================
+
+int WIPX_Write (int handle, byte *buf, int len, struct qsockaddr *addr)
+{
+       int socket = ipxsocket[handle];
+       int ret;
+
+       // build packet with sequence number
+       *(int *)(&packetBuffer[0]) = sequence[handle];
+       sequence[handle]++;
+       memcpy(&packetBuffer[4], buf, len);
+       len += 4;
+
+       ret = psendto (socket, packetBuffer, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
+       if (ret == -1)
+               if (pWSAGetLastError() == WSAEWOULDBLOCK)
+                       return 0;
+
+       return ret;
+}
+
+//=============================================================================
+
+char *WIPX_AddrToString (struct qsockaddr *addr)
+{
+       static char buf[28];
+
+       sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
+               ((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_nodenum[0] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_nodenum[1] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_nodenum[2] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_nodenum[3] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_nodenum[4] & 0xff,
+               ((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff,
+               ntohs(((struct sockaddr_ipx *)addr)->sa_socket)
+               );
+       return buf;
+}
+
+//=============================================================================
+
+int WIPX_StringToAddr (char *string, struct qsockaddr *addr)
+{
+       int  val;
+       char buf[3];
+
+       buf[2] = 0;
+       memset(addr, 0, sizeof(struct qsockaddr));
+       addr->sa_family = AF_IPX;
+
+#define DO(src,dest)   \
+       buf[0] = string[src];   \
+       buf[1] = string[src + 1];       \
+       if (sscanf (buf, "%x", &val) != 1)      \
+               return -1;      \
+       ((struct sockaddr_ipx *)addr)->dest = val
+
+       DO(0, sa_netnum[0]);
+       DO(2, sa_netnum[1]);
+       DO(4, sa_netnum[2]);
+       DO(6, sa_netnum[3]);
+       DO(9, sa_nodenum[0]);
+       DO(11, sa_nodenum[1]);
+       DO(13, sa_nodenum[2]);
+       DO(15, sa_nodenum[3]);
+       DO(17, sa_nodenum[4]);
+       DO(19, sa_nodenum[5]);
+#undef DO
+
+       sscanf (&string[22], "%u", &val);
+       ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)val);
+
+       return 0;
+}
+
+//=============================================================================
+
+int WIPX_GetSocketAddr (int handle, struct qsockaddr *addr)
+{
+       int socket = ipxsocket[handle];
+       int addrlen = sizeof(struct qsockaddr);
+
+       memset(addr, 0, sizeof(struct qsockaddr));
+       if(pgetsockname(socket, (struct sockaddr *)addr, &addrlen) != 0)
+       {
+               int errno = pWSAGetLastError();
+       }
+
+       return 0;
+}
+
+//=============================================================================
+
+int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name)
+{
+       strcpy(name, WIPX_AddrToString(addr));
+       return 0;
+}
+
+//=============================================================================
+
+int WIPX_GetAddrFromName(char *name, struct qsockaddr *addr)
+{
+       int n;
+       char buf[32];
+
+       n = strlen(name);
+
+       if (n == 12)
+       {
+               sprintf(buf, "00000000:%s:%u", name, net_hostport);
+               return WIPX_StringToAddr (buf, addr);
+       }
+       if (n == 21)
+       {
+               sprintf(buf, "%s:%u", name, net_hostport);
+               return WIPX_StringToAddr (buf, addr);
+       }
+       if (n > 21 && n <= 27)
+               return WIPX_StringToAddr (name, addr);
+
+       return -1;
+}
+
+//=============================================================================
+
+int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+{
+       if (addr1->sa_family != addr2->sa_family)
+               return -1;
+
+       if (*((struct sockaddr_ipx *)addr1)->sa_netnum && *((struct sockaddr_ipx *)addr2)->sa_netnum)
+               if (memcmp(((struct sockaddr_ipx *)addr1)->sa_netnum, ((struct sockaddr_ipx *)addr2)->sa_netnum, 4) != 0)
+                       return -1;
+       if (memcmp(((struct sockaddr_ipx *)addr1)->sa_nodenum, ((struct sockaddr_ipx *)addr2)->sa_nodenum, 6) != 0)
+               return -1;
+
+       if (((struct sockaddr_ipx *)addr1)->sa_socket != ((struct sockaddr_ipx *)addr2)->sa_socket)
+               return 1;
+
+       return 0;
+}
+
+//=============================================================================
+
+int WIPX_GetSocketPort (struct qsockaddr *addr)
+{
+       return ntohs(((struct sockaddr_ipx *)addr)->sa_socket);
+}
+
+
+int WIPX_SetSocketPort (struct qsockaddr *addr, int port)
+{
+       ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)port);
+       return 0;
+}
+
+//=============================================================================
diff --git a/net_wipx.h b/net_wipx.h
new file mode 100644 (file)
index 0000000..ed82dc1
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_wipx.h
+
+int  WIPX_Init (void);
+void WIPX_Shutdown (void);
+void WIPX_Listen (qboolean state);
+int  WIPX_OpenSocket (int port);
+int  WIPX_CloseSocket (int socket);
+int  WIPX_Connect (int socket, struct qsockaddr *addr);
+int  WIPX_CheckNewConnections (void);
+int  WIPX_Read (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  WIPX_Write (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  WIPX_Broadcast (int socket, byte *buf, int len);
+char *WIPX_AddrToString (struct qsockaddr *addr);
+int  WIPX_StringToAddr (char *string, struct qsockaddr *addr);
+int  WIPX_GetSocketAddr (int socket, struct qsockaddr *addr);
+int  WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name);
+int  WIPX_GetAddrFromName (char *name, struct qsockaddr *addr);
+int  WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+int  WIPX_GetSocketPort (struct qsockaddr *addr);
+int  WIPX_SetSocketPort (struct qsockaddr *addr, int port);
diff --git a/nonintel.c b/nonintel.c
new file mode 100644 (file)
index 0000000..e7fc244
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// nonintel.c: code for non-Intel processors only
+//
+
+#include "quakedef.h"
+#include "d_local.h"
+
+#if    !id386
+
+/*
+================
+R_Surf8Patch
+================
+*/
+void R_Surf8Patch ()
+{
+       // we only patch code on Intel
+}
+
+
+/*
+================
+R_Surf16Patch
+================
+*/
+void R_Surf16Patch ()
+{
+       // we only patch code on Intel
+}
+
+
+/*
+================
+R_SurfacePatch
+================
+*/
+void R_SurfacePatch (void)
+{
+       // we only patch code on Intel
+}
+
+
+#endif // !id386
+
diff --git a/pr_cmds.c b/pr_cmds.c
new file mode 100644 (file)
index 0000000..d8b6393
--- /dev/null
+++ b/pr_cmds.c
@@ -0,0 +1,2062 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+#define        RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
+
+
+/*
+===============================================================================
+
+                                               BUILT-IN FUNCTIONS
+
+===============================================================================
+*/
+
+char *PF_VarString (int        first)
+{
+       int             i;
+       static char out[256];
+       
+       out[0] = 0;
+       for (i=first ; i<pr_argc ; i++)
+       {
+               strcat (out, G_STRING((OFS_PARM0+i*3)));
+       }
+       return out;
+}
+
+char *QSG_EXTENSIONS = "\
+DP_BLOOD \
+DP_BLOODSHOWER \
+DP_CORPSE \
+DP_DRAWONLYTOCLIENT \
+DP_EXPLOSION3 \
+DP_PARTICLECUBE \
+DP_PARTICLERAIN \
+DP_PARTICLESNOW \
+DP_GETLIGHT \
+DP_NODRAWTOCLIENT \
+DP_RANDOMVEC \
+DP_REGISTERCVAR \
+DP_SPARK \
+DP_SPRITE32 \
+DP_MODEL32 \
+DP_TRACEBOX \
+DP_MINMAXBOUND \
+DP_FINDFLOAT \
+NEH_PLAY2 \
+QSG_ALPHA \
+QSG_BUTTONS \
+QSG_CHANGEPITCH \
+QSG_COLORMOD \
+QSG_DELTA \
+QSG_ETOS \
+QSG_FOG \
+QSG_FOLLOW \
+QSG_GLOW \
+QSG_MATH \
+QSG_MONSTERWALK \
+QSG_QUAKE2MODEL \
+QSG_SCALE \
+QSG_SKYBOX \
+QSG_TRACETOSS \
+QSG_VIEWMODEL \
+";
+
+qboolean checkextension(char *name)
+{
+       int len;
+       char *e;
+       len = strlen(name);
+       for (e = QSG_EXTENSIONS;*e;e++)
+       {
+               if (!*e || e[len] == ' ' && !strnicmp(e, name, len))
+                       return TRUE;
+               while (*e && *e != ' ')
+                       e++;
+               while (*e == ' ')
+                       e++;
+       }
+       return FALSE;
+}
+
+/*
+=================
+PF_checkextension
+
+returns true if the extension is supported by the server
+
+checkextension(extensionname)
+=================
+*/
+void PF_checkextension (void)
+{
+       G_FLOAT(OFS_RETURN) = checkextension(G_STRING(OFS_PARM0));
+}
+
+/*
+=================
+PF_error
+
+This is a TERMINAL error, which will kill off the entire server.
+Dumps self.
+
+error(value)
+=================
+*/
+void PF_error (void)
+{
+       char    *s;
+       edict_t *ed;
+       
+       s = PF_VarString(0);
+       Con_Printf ("======SERVER ERROR in %s:\n%s\n"
+       ,pr_strings + pr_xfunction->s_name,s);
+       ed = PROG_TO_EDICT(pr_global_struct->self);
+       ED_Print (ed);
+
+       Host_Error ("Program error");
+}
+
+/*
+=================
+PF_objerror
+
+Dumps out self, then an error message.  The program is aborted and self is
+removed, but the level can continue.
+
+objerror(value)
+=================
+*/
+void PF_objerror (void)
+{
+       char    *s;
+       edict_t *ed;
+       
+       s = PF_VarString(0);
+       Con_Printf ("======OBJECT ERROR in %s:\n%s\n"
+       ,pr_strings + pr_xfunction->s_name,s);
+       ed = PROG_TO_EDICT(pr_global_struct->self);
+       ED_Print (ed);
+       ED_Free (ed);
+       
+       Host_Error ("Program error");
+}
+
+
+
+/*
+==============
+PF_makevectors
+
+Writes new values for v_forward, v_up, and v_right based on angles
+makevectors(vector)
+==============
+*/
+void PF_makevectors (void)
+{
+       AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
+}
+
+/*
+=================
+PF_setorigin
+
+This is the only valid way to move an object without using the physics of the world (setting velocity and waiting).  Directly changing origin will not set internal links correctly, so clipping would be messed up.  This should be called when an object is spawned, and then only if it is teleported.
+
+setorigin (entity, origin)
+=================
+*/
+void PF_setorigin (void)
+{
+       edict_t *e;
+       float   *org;
+       
+       e = G_EDICT(OFS_PARM0);
+       org = G_VECTOR(OFS_PARM1);
+       VectorCopy (org, e->v.origin);
+       SV_LinkEdict (e, false);
+}
+
+
+void SetMinMaxSize (edict_t *e, float *min, float *max, qboolean rotate)
+{
+       float   *angles;
+       vec3_t  rmin, rmax;
+       float   bounds[2][3];
+       float   xvector[2], yvector[2];
+       float   a;
+       vec3_t  base, transformed;
+       int             i, j, k, l;
+       
+       for (i=0 ; i<3 ; i++)
+               if (min[i] > max[i])
+                       PR_RunError ("backwards mins/maxs");
+
+       rotate = false;         // FIXME: implement rotation properly again
+
+       if (!rotate)
+       {
+               VectorCopy (min, rmin);
+               VectorCopy (max, rmax);
+       }
+       else
+       {
+       // find min / max for rotations
+               angles = e->v.angles;
+               
+               a = angles[1]/180 * M_PI;
+               
+               xvector[0] = cos(a);
+               xvector[1] = sin(a);
+               yvector[0] = -sin(a);
+               yvector[1] = cos(a);
+               
+               VectorCopy (min, bounds[0]);
+               VectorCopy (max, bounds[1]);
+               
+               rmin[0] = rmin[1] = rmin[2] = 9999;
+               rmax[0] = rmax[1] = rmax[2] = -9999;
+               
+               for (i=0 ; i<= 1 ; i++)
+               {
+                       base[0] = bounds[i][0];
+                       for (j=0 ; j<= 1 ; j++)
+                       {
+                               base[1] = bounds[j][1];
+                               for (k=0 ; k<= 1 ; k++)
+                               {
+                                       base[2] = bounds[k][2];
+                                       
+                               // transform the point
+                                       transformed[0] = xvector[0]*base[0] + yvector[0]*base[1];
+                                       transformed[1] = xvector[1]*base[0] + yvector[1]*base[1];
+                                       transformed[2] = base[2];
+                                       
+                                       for (l=0 ; l<3 ; l++)
+                                       {
+                                               if (transformed[l] < rmin[l])
+                                                       rmin[l] = transformed[l];
+                                               if (transformed[l] > rmax[l])
+                                                       rmax[l] = transformed[l];
+                                       }
+                               }
+                       }
+               }
+       }
+       
+// set derived values
+       VectorCopy (rmin, e->v.mins);
+       VectorCopy (rmax, e->v.maxs);
+       VectorSubtract (max, min, e->v.size);
+       
+       SV_LinkEdict (e, false);
+}
+
+/*
+=================
+PF_setsize
+
+the size box is rotated by the current angle
+
+setsize (entity, minvector, maxvector)
+=================
+*/
+void PF_setsize (void)
+{
+       edict_t *e;
+       float   *min, *max;
+       
+       e = G_EDICT(OFS_PARM0);
+       min = G_VECTOR(OFS_PARM1);
+       max = G_VECTOR(OFS_PARM2);
+       SetMinMaxSize (e, min, max, false);
+}
+
+
+/*
+=================
+PF_setmodel
+
+setmodel(entity, model)
+=================
+*/
+void PF_setmodel (void)
+{
+       edict_t *e;
+       char    *m, **check;
+       model_t *mod;
+       int             i;
+
+       e = G_EDICT(OFS_PARM0);
+       m = G_STRING(OFS_PARM1);
+
+// check to see if model was properly precached
+       for (i=0, check = sv.model_precache ; *check ; i++, check++)
+               if (!strcmp(*check, m))
+                       break;
+                       
+       if (!*check)
+               PR_RunError ("no precache: %s\n", m);
+               
+
+       e->v.model = m - pr_strings;
+       e->v.modelindex = i; //SV_ModelIndex (m);
+
+       mod = sv.models[ (int)e->v.modelindex];  // Mod_ForName (m, true);
+       
+       if (mod)
+       /*
+       { // LordHavoc: corrected model bounding box, but for compatibility that means I have to break it here
+               vec3_t min, max;
+               if (mod->type == ALIASTYPE_MDL)
+               {
+                       min[0] = min[1] = min[2] = -16;
+                       max[0] = max[1] = max[2] = 16;
+                       SetMinMaxSize (e, min, max, true);
+               }
+               else
+                       SetMinMaxSize (e, mod->mins, mod->maxs, true);
+       }
+       */
+               SetMinMaxSize (e, mod->mins, mod->maxs, true);
+       else
+               SetMinMaxSize (e, vec3_origin, vec3_origin, true);
+}
+
+/*
+=================
+PF_bprint
+
+broadcast print to everyone on server
+
+bprint(value)
+=================
+*/
+void PF_bprint (void)
+{
+       char            *s;
+
+       s = PF_VarString(0);
+       SV_BroadcastPrintf ("%s", s);
+}
+
+/*
+=================
+PF_sprint
+
+single print to a specific client
+
+sprint(clientent, value)
+=================
+*/
+void PF_sprint (void)
+{
+       char            *s;
+       client_t        *client;
+       int                     entnum;
+       
+       entnum = G_EDICTNUM(OFS_PARM0);
+       s = PF_VarString(1);
+       
+       if (entnum < 1 || entnum > svs.maxclients)
+       {
+               Con_Printf ("tried to sprint to a non-client\n");
+               return;
+       }
+               
+       client = &svs.clients[entnum-1];
+               
+       MSG_WriteChar (&client->message,svc_print);
+       MSG_WriteString (&client->message, s );
+}
+
+
+/*
+=================
+PF_centerprint
+
+single print to a specific client
+
+centerprint(clientent, value)
+=================
+*/
+void PF_centerprint (void)
+{
+       char            *s;
+       client_t        *client;
+       int                     entnum;
+       
+       entnum = G_EDICTNUM(OFS_PARM0);
+       s = PF_VarString(1);
+       
+       if (entnum < 1 || entnum > svs.maxclients)
+       {
+               Con_Printf ("tried to sprint to a non-client\n");
+               return;
+       }
+               
+       client = &svs.clients[entnum-1];
+               
+       MSG_WriteChar (&client->message,svc_centerprint);
+       MSG_WriteString (&client->message, s );
+}
+
+
+/*
+=================
+PF_normalize
+
+vector normalize(vector)
+=================
+*/
+void PF_normalize (void)
+{
+       float   *value1;
+       vec3_t  newvalue;
+       float   new;
+       
+       value1 = G_VECTOR(OFS_PARM0);
+
+       new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
+       new = sqrt(new);
+       
+       if (new == 0)
+               newvalue[0] = newvalue[1] = newvalue[2] = 0;
+       else
+       {
+               new = 1/new;
+               newvalue[0] = value1[0] * new;
+               newvalue[1] = value1[1] * new;
+               newvalue[2] = value1[2] * new;
+       }
+       
+       VectorCopy (newvalue, G_VECTOR(OFS_RETURN));    
+}
+
+/*
+=================
+PF_vlen
+
+scalar vlen(vector)
+=================
+*/
+void PF_vlen (void)
+{
+       float   *value1;
+       float   new;
+       
+       value1 = G_VECTOR(OFS_PARM0);
+
+       new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
+       new = sqrt(new);
+       
+       G_FLOAT(OFS_RETURN) = new;
+}
+
+/*
+=================
+PF_vectoyaw
+
+float vectoyaw(vector)
+=================
+*/
+void PF_vectoyaw (void)
+{
+       float   *value1;
+       float   yaw;
+       
+       value1 = G_VECTOR(OFS_PARM0);
+
+       if (value1[1] == 0 && value1[0] == 0)
+               yaw = 0;
+       else
+       {
+               yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
+               if (yaw < 0)
+                       yaw += 360;
+       }
+
+       G_FLOAT(OFS_RETURN) = yaw;
+}
+
+
+/*
+=================
+PF_vectoangles
+
+vector vectoangles(vector)
+=================
+*/
+void PF_vectoangles (void)
+{
+       float   *value1;
+       float   forward;
+       float   yaw, pitch;
+       
+       value1 = G_VECTOR(OFS_PARM0);
+
+       if (value1[1] == 0 && value1[0] == 0)
+       {
+               yaw = 0;
+               if (value1[2] > 0)
+                       pitch = 90;
+               else
+                       pitch = 270;
+       }
+       else
+       {
+               yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
+               if (yaw < 0)
+                       yaw += 360;
+
+               forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
+               pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
+               if (pitch < 0)
+                       pitch += 360;
+       }
+
+       G_FLOAT(OFS_RETURN+0) = pitch;
+       G_FLOAT(OFS_RETURN+1) = yaw;
+       G_FLOAT(OFS_RETURN+2) = 0;
+}
+
+/*
+=================
+PF_Random
+
+Returns a number from 0<= num < 1
+
+random()
+=================
+*/
+void PF_random (void)
+{
+       float           num;
+               
+       num = (rand ()&0x7fff) / ((float)0x7fff);
+       
+       G_FLOAT(OFS_RETURN) = num;
+}
+
+/*
+=================
+PF_particle
+
+particle(origin, color, count)
+=================
+*/
+void PF_particle (void)
+{
+       float           *org, *dir;
+       float           color;
+       float           count;
+                       
+       org = G_VECTOR(OFS_PARM0);
+       dir = G_VECTOR(OFS_PARM1);
+       color = G_FLOAT(OFS_PARM2);
+       count = G_FLOAT(OFS_PARM3);
+       SV_StartParticle (org, dir, color, count);
+}
+
+
+/*
+=================
+PF_ambientsound
+
+=================
+*/
+void PF_ambientsound (void)
+{
+       char            **check;
+       char            *samp;
+       float           *pos;
+       float           vol, attenuation;
+       int                     i, soundnum;
+
+       pos = G_VECTOR (OFS_PARM0);                     
+       samp = G_STRING(OFS_PARM1);
+       vol = G_FLOAT(OFS_PARM2);
+       attenuation = G_FLOAT(OFS_PARM3);
+       
+// check to see if samp was properly precached
+       for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++)
+               if (!strcmp(*check,samp))
+                       break;
+                       
+       if (!*check)
+       {
+               Con_Printf ("no precache: %s\n", samp);
+               return;
+       }
+
+// add an svc_spawnambient command to the level signon packet
+
+       MSG_WriteByte (&sv.signon,svc_spawnstaticsound);
+       for (i=0 ; i<3 ; i++)
+               MSG_WriteCoord(&sv.signon, pos[i]);
+
+       MSG_WriteByte (&sv.signon, soundnum);
+
+       MSG_WriteByte (&sv.signon, vol*255);
+       MSG_WriteByte (&sv.signon, attenuation*64);
+
+}
+
+/*
+=================
+PF_sound
+
+Each entity can have eight independant sound sources, like voice,
+weapon, feet, etc.
+
+Channel 0 is an auto-allocate channel, the others override anything
+allready running on that entity/channel pair.
+
+An attenuation of 0 will play full volume everywhere in the level.
+Larger attenuations will drop off.
+
+=================
+*/
+void PF_sound (void)
+{
+       char            *sample;
+       int                     channel;
+       edict_t         *entity;
+       int             volume;
+       float attenuation;
+               
+       entity = G_EDICT(OFS_PARM0);
+       channel = G_FLOAT(OFS_PARM1);
+       sample = G_STRING(OFS_PARM2);
+       volume = G_FLOAT(OFS_PARM3) * 255;
+       attenuation = G_FLOAT(OFS_PARM4);
+       
+       if (volume < 0 || volume > 255)
+               Sys_Error ("SV_StartSound: volume = %i", volume);
+
+       if (attenuation < 0 || attenuation > 4)
+               Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
+
+       if (channel < 0 || channel > 7)
+               Sys_Error ("SV_StartSound: channel = %i", channel);
+
+       SV_StartSound (entity, channel, sample, volume, attenuation);
+}
+
+/*
+=================
+PF_break
+
+break()
+=================
+*/
+void PF_break (void)
+{
+Con_Printf ("break statement\n");
+*(int *)-4 = 0;        // dump to debugger
+//     PR_RunError ("break statement");
+}
+
+/*
+=================
+PF_traceline
+
+Used for use tracing and shot targeting
+Traces are blocked by bbox and exact bsp entityes, and also slide box entities
+if the tryents flag is set.
+
+traceline (vector1, vector2, tryents)
+=================
+*/
+void PF_traceline (void)
+{
+       float   *v1, *v2;
+       trace_t trace;
+       int             nomonsters;
+       edict_t *ent;
+
+       v1 = G_VECTOR(OFS_PARM0);
+       v2 = G_VECTOR(OFS_PARM1);
+       nomonsters = G_FLOAT(OFS_PARM2);
+       ent = G_EDICT(OFS_PARM3);
+
+       trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
+
+       pr_global_struct->trace_allsolid = trace.allsolid;
+       pr_global_struct->trace_startsolid = trace.startsolid;
+       pr_global_struct->trace_fraction = trace.fraction;
+       pr_global_struct->trace_inwater = trace.inwater;
+       pr_global_struct->trace_inopen = trace.inopen;
+       VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+       VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+       pr_global_struct->trace_plane_dist =  trace.plane.dist; 
+       if (trace.ent)
+               pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+       else
+               pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+
+
+/*
+=================
+PF_tracebox
+
+Used for use tracing and shot targeting
+Traces are blocked by bbox and exact bsp entityes, and also slide box entities
+if the tryents flag is set.
+
+tracebox (vector1, vector mins, vector maxs, vector2, tryents)
+=================
+*/
+// LordHavoc: added this for my own use, VERY useful, similar to traceline
+void PF_tracebox (void)
+{
+       float   *v1, *v2, *m1, *m2;
+       trace_t trace;
+       int             nomonsters;
+       edict_t *ent;
+
+       v1 = G_VECTOR(OFS_PARM0);
+       m1 = G_VECTOR(OFS_PARM1);
+       m2 = G_VECTOR(OFS_PARM2);
+       v2 = G_VECTOR(OFS_PARM3);
+       nomonsters = G_FLOAT(OFS_PARM4);
+       ent = G_EDICT(OFS_PARM5);
+
+       trace = SV_Move (v1, m1, m2, v2, nomonsters, ent);
+
+       pr_global_struct->trace_allsolid = trace.allsolid;
+       pr_global_struct->trace_startsolid = trace.startsolid;
+       pr_global_struct->trace_fraction = trace.fraction;
+       pr_global_struct->trace_inwater = trace.inwater;
+       pr_global_struct->trace_inopen = trace.inopen;
+       VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+       VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+       pr_global_struct->trace_plane_dist =  trace.plane.dist; 
+       if (trace.ent)
+               pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+       else
+               pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+
+extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore);
+void PF_TraceToss (void)
+{
+       trace_t trace;
+       edict_t *ent;
+       edict_t *ignore;
+
+       ent = G_EDICT(OFS_PARM0);
+       ignore = G_EDICT(OFS_PARM1);
+
+       trace = SV_Trace_Toss (ent, ignore);
+
+       pr_global_struct->trace_allsolid = trace.allsolid;
+       pr_global_struct->trace_startsolid = trace.startsolid;
+       pr_global_struct->trace_fraction = trace.fraction;
+       pr_global_struct->trace_inwater = trace.inwater;
+       pr_global_struct->trace_inopen = trace.inopen;
+       VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+       VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+       pr_global_struct->trace_plane_dist =  trace.plane.dist; 
+       if (trace.ent)
+               pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+       else
+               pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+
+
+/*
+=================
+PF_checkpos
+
+Returns true if the given entity can move to the given position from it's
+current position by walking or rolling.
+FIXME: make work...
+scalar checkpos (entity, vector)
+=================
+*/
+void PF_checkpos (void)
+{
+}
+
+//============================================================================
+
+byte   checkpvs[MAX_MAP_LEAFS/8];
+
+int PF_newcheckclient (int check)
+{
+       int             i;
+       byte    *pvs;
+       edict_t *ent;
+       mleaf_t *leaf;
+       vec3_t  org;
+
+// cycle to the next one
+
+       if (check < 1)
+               check = 1;
+       if (check > svs.maxclients)
+               check = svs.maxclients;
+
+       if (check == svs.maxclients)
+               i = 1;
+       else
+               i = check + 1;
+
+       for ( ;  ; i++)
+       {
+               if (i == svs.maxclients+1)
+                       i = 1;
+
+               ent = EDICT_NUM(i);
+
+               if (i == check)
+                       break;  // didn't find anything else
+
+               if (ent->free)
+                       continue;
+               if (ent->v.health <= 0)
+                       continue;
+               if ((int)ent->v.flags & FL_NOTARGET)
+                       continue;
+
+       // anything that is a client, or has a client as an enemy
+               break;
+       }
+
+// get the PVS for the entity
+       VectorAdd (ent->v.origin, ent->v.view_ofs, org);
+       leaf = Mod_PointInLeaf (org, sv.worldmodel);
+       pvs = Mod_LeafPVS (leaf, sv.worldmodel);
+       memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 );
+
+       return i;
+}
+
+/*
+=================
+PF_checkclient
+
+Returns a client (or object that has a client enemy) that would be a
+valid target.
+
+If there are more than one valid options, they are cycled each frame
+
+If (self.origin + self.viewofs) is not in the PVS of the current target,
+it is not returned at all.
+
+name checkclient ()
+=================
+*/
+#define        MAX_CHECK       16
+int c_invis, c_notvis;
+void PF_checkclient (void)
+{
+       edict_t *ent, *self;
+       mleaf_t *leaf;
+       int             l;
+       vec3_t  view;
+       
+// find a new check if on a new frame
+       if (sv.time - sv.lastchecktime >= 0.1)
+       {
+               sv.lastcheck = PF_newcheckclient (sv.lastcheck);
+               sv.lastchecktime = sv.time;
+       }
+
+// return check if it might be visible 
+       ent = EDICT_NUM(sv.lastcheck);
+       if (ent->free || ent->v.health <= 0)
+       {
+               RETURN_EDICT(sv.edicts);
+               return;
+       }
+
+// if current entity can't possibly see the check entity, return 0
+       self = PROG_TO_EDICT(pr_global_struct->self);
+       VectorAdd (self->v.origin, self->v.view_ofs, view);
+       leaf = Mod_PointInLeaf (view, sv.worldmodel);
+       l = (leaf - sv.worldmodel->leafs) - 1;
+       if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
+       {
+c_notvis++;
+               RETURN_EDICT(sv.edicts);
+               return;
+       }
+
+// might be able to see it
+c_invis++;
+       RETURN_EDICT(ent);
+}
+
+//============================================================================
+
+
+/*
+=================
+PF_stuffcmd
+
+Sends text over to the client's execution buffer
+
+stuffcmd (clientent, value)
+=================
+*/
+void PF_stuffcmd (void)
+{
+       int             entnum;
+       char    *str;
+       client_t        *old;
+       
+       entnum = G_EDICTNUM(OFS_PARM0);
+       if (entnum < 1 || entnum > svs.maxclients)
+               PR_RunError ("Parm 0 not a client");
+       str = G_STRING(OFS_PARM1);      
+       
+       old = host_client;
+       host_client = &svs.clients[entnum-1];
+       Host_ClientCommands ("%s", str);
+       host_client = old;
+}
+
+/*
+=================
+PF_localcmd
+
+Sends text over to the client's execution buffer
+
+localcmd (string)
+=================
+*/
+void PF_localcmd (void)
+{
+       char    *str;
+       
+       str = G_STRING(OFS_PARM0);      
+       Cbuf_AddText (str);
+}
+
+/*
+=================
+PF_cvar
+
+float cvar (string)
+=================
+*/
+void PF_cvar (void)
+{
+       char    *str;
+       
+       str = G_STRING(OFS_PARM0);
+       
+       G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str);
+}
+
+/*
+=================
+PF_cvar_set
+
+float cvar (string)
+=================
+*/
+void PF_cvar_set (void)
+{
+       char    *var, *val;
+       
+       var = G_STRING(OFS_PARM0);
+       val = G_STRING(OFS_PARM1);
+       
+       Cvar_Set (var, val);
+}
+
+/*
+=================
+PF_findradius
+
+Returns a chain of entities that have origins within a spherical area
+
+findradius (origin, radius)
+=================
+*/
+void PF_findradius (void)
+{
+       edict_t *ent, *chain;
+       float   rad;
+       float   *org;
+       vec3_t  eorg;
+       int             i, j;
+
+       chain = (edict_t *)sv.edicts;
+       
+       org = G_VECTOR(OFS_PARM0);
+       rad = G_FLOAT(OFS_PARM1);
+
+       ent = NEXT_EDICT(sv.edicts);
+       for (i=1 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
+       {
+               if (ent->free)
+                       continue;
+               if (ent->v.solid == SOLID_NOT)
+                       continue;
+               for (j=0 ; j<3 ; j++)
+                       eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);                  
+               if (Length(eorg) > rad)
+                       continue;
+                       
+               ent->v.chain = EDICT_TO_PROG(chain);
+               chain = ent;
+       }
+
+       RETURN_EDICT(chain);
+}
+
+
+/*
+=========
+PF_dprint
+=========
+*/
+void PF_dprint (void)
+{
+       Con_DPrintf ("%s",PF_VarString(0));
+}
+
+char   pr_string_temp[128];
+
+void PF_ftos (void)
+{
+       float   v;
+       v = G_FLOAT(OFS_PARM0);
+
+       // LordHavoc: ftos improvement
+       sprintf (pr_string_temp, "%g", v);
+       /*
+       if (v == (int)v)
+               sprintf (pr_string_temp, "%d",(int)v);
+       else
+               sprintf (pr_string_temp, "%5.1f",v);
+       */
+       G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+void PF_fabs (void)
+{
+       float   v;
+       v = G_FLOAT(OFS_PARM0);
+       G_FLOAT(OFS_RETURN) = fabs(v);
+}
+
+void PF_vtos (void)
+{
+       sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
+       G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+void PF_etos (void)
+{
+       sprintf (pr_string_temp, "entity %i", G_EDICTNUM(OFS_PARM0));
+       G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+void PF_Spawn (void)
+{
+       edict_t *ed;
+       ed = ED_Alloc();
+       RETURN_EDICT(ed);
+}
+
+void PF_Remove (void)
+{
+       edict_t *ed;
+       
+       ed = G_EDICT(OFS_PARM0);
+       ED_Free (ed);
+}
+
+
+// entity (entity start, .string field, string match) find = #5;
+void PF_Find (void)
+{
+       int             e;      
+       int             f;
+       char    *s, *t;
+       edict_t *ed;
+
+       e = G_EDICTNUM(OFS_PARM0);
+       f = G_INT(OFS_PARM1);
+       s = G_STRING(OFS_PARM2);
+       if (!s)
+               PR_RunError ("PF_Find: bad search string");
+               
+       for (e++ ; e < sv.num_edicts ; e++)
+       {
+               ed = EDICT_NUM(e);
+               if (ed->free)
+                       continue;
+               t = E_STRING(ed,f);
+               if (!t)
+                       continue;
+               if (!strcmp(t,s))
+               {
+                       RETURN_EDICT(ed);
+                       return;
+               }
+       }
+
+       RETURN_EDICT(sv.edicts);
+}
+
+// LordHavoc: added this for searching float, int, and entity reference fields
+void PF_FindFloat (void)
+{
+       int             e;      
+       int             f;
+       float   s;
+       edict_t *ed;
+
+       e = G_EDICTNUM(OFS_PARM0);
+       f = G_INT(OFS_PARM1);
+       s = G_FLOAT(OFS_PARM2);
+               
+       for (e++ ; e < sv.num_edicts ; e++)
+       {
+               ed = EDICT_NUM(e);
+               if (ed->free)
+                       continue;
+               if (E_FLOAT(ed,f) == s)
+               {
+                       RETURN_EDICT(ed);
+                       return;
+               }
+       }
+
+       RETURN_EDICT(sv.edicts);
+}
+
+void PR_CheckEmptyString (char *s)
+{
+       if (s[0] <= ' ')
+               PR_RunError ("Bad string");
+}
+
+void PF_precache_file (void)
+{      // precache_file is only used to copy files with qcc, it does nothing
+       G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+}
+
+void PF_precache_sound (void)
+{
+       char    *s;
+       int             i;
+       
+       if (sv.state != ss_loading)
+               PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+               
+       s = G_STRING(OFS_PARM0);
+       G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+       PR_CheckEmptyString (s);
+       
+       for (i=0 ; i<MAX_SOUNDS ; i++)
+       {
+               if (!sv.sound_precache[i])
+               {
+                       sv.sound_precache[i] = s;
+                       return;
+               }
+               if (!strcmp(sv.sound_precache[i], s))
+                       return;
+       }
+       PR_RunError ("PF_precache_sound: overflow");
+}
+
+int blahblah = 0;
+void PF_precache_model (void)
+{
+       char    *s;
+       int             i;
+       
+       if (sv.state != ss_loading)
+               PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+               
+       s = G_STRING(OFS_PARM0);
+       G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+       PR_CheckEmptyString (s);
+
+       for (i=0 ; i<MAX_MODELS ; i++)
+       {
+               if (!sv.model_precache[i])
+               {
+                       sv.model_precache[i] = s;
+                       if (sv.active < 0)
+                               blahblah++;
+                       sv.models[i] = Mod_ForName (s, true);
+                       if (sv.active < 0)
+                               blahblah++;
+                       return;
+               }
+               if (!strcmp(sv.model_precache[i], s))
+                       return;
+       }
+       PR_RunError ("PF_precache_model: overflow");
+}
+
+
+void PF_coredump (void)
+{
+       ED_PrintEdicts ();
+}
+
+void PF_traceon (void)
+{
+       pr_trace = true;
+}
+
+void PF_traceoff (void)
+{
+       pr_trace = false;
+}
+
+void PF_eprint (void)
+{
+       ED_PrintNum (G_EDICTNUM(OFS_PARM0));
+}
+
+/*
+===============
+PF_walkmove
+
+float(float yaw, float dist) walkmove
+===============
+*/
+void PF_walkmove (void)
+{
+       edict_t *ent;
+       float   yaw, dist;
+       vec3_t  move;
+       dfunction_t     *oldf;
+       int     oldself;
+       
+       ent = PROG_TO_EDICT(pr_global_struct->self);
+       yaw = G_FLOAT(OFS_PARM0);
+       dist = G_FLOAT(OFS_PARM1);
+       
+       if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
+       {
+               G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+
+       yaw = yaw*M_PI*2 / 360;
+       
+       move[0] = cos(yaw)*dist;
+       move[1] = sin(yaw)*dist;
+       move[2] = 0;
+
+// save program state, because SV_movestep may call other progs
+       oldf = pr_xfunction;
+       oldself = pr_global_struct->self;
+       
+       G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true);
+       
+       
+// restore program state
+       pr_xfunction = oldf;
+       pr_global_struct->self = oldself;
+}
+
+/*
+===============
+PF_droptofloor
+
+void() droptofloor
+===============
+*/
+void PF_droptofloor (void)
+{
+       edict_t         *ent;
+       vec3_t          end;
+       trace_t         trace;
+       
+       ent = PROG_TO_EDICT(pr_global_struct->self);
+
+       VectorCopy (ent->v.origin, end);
+       end[2] -= 256;
+       
+       trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+
+       if (trace.fraction == 1 || trace.allsolid)
+               G_FLOAT(OFS_RETURN) = 0;
+       else
+       {
+               VectorCopy (trace.endpos, ent->v.origin);
+               SV_LinkEdict (ent, false);
+               ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
+               ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+               G_FLOAT(OFS_RETURN) = 1;
+       }
+}
+
+/*
+===============
+PF_lightstyle
+
+void(float style, string value) lightstyle
+===============
+*/
+void PF_lightstyle (void)
+{
+       int             style;
+       char    *val;
+       client_t        *client;
+       int                     j;
+       
+       style = G_FLOAT(OFS_PARM0);
+       val = G_STRING(OFS_PARM1);
+
+// change the string in sv
+       sv.lightstyles[style] = val;
+       
+// send message to all clients on this server
+       if (sv.state != ss_active)
+               return;
+       
+       for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+               if (client->active || client->spawned)
+               {
+                       MSG_WriteChar (&client->message, svc_lightstyle);
+                       MSG_WriteChar (&client->message,style);
+                       MSG_WriteString (&client->message, val);
+               }
+}
+
+void PF_rint (void)
+{
+       float   f;
+       f = G_FLOAT(OFS_PARM0);
+       if (f > 0)
+               G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
+       else
+               G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
+}
+void PF_floor (void)
+{
+       G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));
+}
+void PF_ceil (void)
+{
+       G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0));
+}
+
+
+/*
+=============
+PF_checkbottom
+=============
+*/
+void PF_checkbottom (void)
+{
+       G_FLOAT(OFS_RETURN) = SV_CheckBottom (G_EDICT(OFS_PARM0));
+}
+
+/*
+=============
+PF_pointcontents
+=============
+*/
+void PF_pointcontents (void)
+{
+       G_FLOAT(OFS_RETURN) = SV_PointContents (G_VECTOR(OFS_PARM0));
+}
+
+/*
+=============
+PF_nextent
+
+entity nextent(entity)
+=============
+*/
+void PF_nextent (void)
+{
+       int             i;
+       edict_t *ent;
+       
+       i = G_EDICTNUM(OFS_PARM0);
+       while (1)
+       {
+               i++;
+               if (i == sv.num_edicts)
+               {
+                       RETURN_EDICT(sv.edicts);
+                       return;
+               }
+               ent = EDICT_NUM(i);
+               if (!ent->free)
+               {
+                       RETURN_EDICT(ent);
+                       return;
+               }
+       }
+}
+
+/*
+=============
+PF_aim
+
+Pick a vector for the player to shoot along
+vector aim(entity, missilespeed)
+=============
+*/
+cvar_t sv_aim = {"sv_aim", "0.93"};
+void PF_aim (void)
+{
+       edict_t *ent, *check, *bestent;
+       vec3_t  start, dir, end, bestdir;
+       int             i, j;
+       trace_t tr;
+       float   dist, bestdist;
+       float   speed;
+       
+       ent = G_EDICT(OFS_PARM0);
+       speed = G_FLOAT(OFS_PARM1);
+
+       VectorCopy (ent->v.origin, start);
+       start[2] += 20;
+
+// try sending a trace straight
+       VectorCopy (pr_global_struct->v_forward, dir);
+       VectorMA (start, 2048, dir, end);
+       tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+       if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM
+       && (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) )
+       {
+               VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
+               return;
+       }
+
+
+// try all possible entities
+       VectorCopy (dir, bestdir);
+       bestdist = sv_aim.value;
+       bestent = NULL;
+       
+       check = NEXT_EDICT(sv.edicts);
+       for (i=1 ; i<sv.num_edicts ; i++, check = NEXT_EDICT(check) )
+       {
+               if (check->v.takedamage != DAMAGE_AIM)
+                       continue;
+               if (check == ent)
+                       continue;
+               if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team)
+                       continue;       // don't aim at teammate
+               for (j=0 ; j<3 ; j++)
+                       end[j] = check->v.origin[j]
+                       + 0.5*(check->v.mins[j] + check->v.maxs[j]);
+               VectorSubtract (end, start, dir);
+               VectorNormalize (dir);
+               dist = DotProduct (dir, pr_global_struct->v_forward);
+               if (dist < bestdist)
+                       continue;       // to far to turn
+               tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+               if (tr.ent == check)
+               {       // can shoot at this one
+                       bestdist = dist;
+                       bestent = check;
+               }
+       }
+       
+       if (bestent)
+       {
+               VectorSubtract (bestent->v.origin, ent->v.origin, dir);
+               dist = DotProduct (dir, pr_global_struct->v_forward);
+               VectorScale (pr_global_struct->v_forward, dist, end);
+               end[2] = dir[2];
+               VectorNormalize (end);
+               VectorCopy (end, G_VECTOR(OFS_RETURN)); 
+       }
+       else
+       {
+               VectorCopy (bestdir, G_VECTOR(OFS_RETURN));
+       }
+}
+
+/*
+==============
+PF_changeyaw
+
+This was a major timewaster in progs, so it was converted to C
+==============
+*/
+void PF_changeyaw (void)
+{
+       edict_t         *ent;
+       float           ideal, current, move, speed;
+       
+       ent = PROG_TO_EDICT(pr_global_struct->self);
+       current = anglemod( ent->v.angles[1] );
+       ideal = ent->v.ideal_yaw;
+       speed = ent->v.yaw_speed;
+       
+       if (current == ideal)
+               return;
+       move = ideal - current;
+       if (ideal > current)
+       {
+               if (move >= 180)
+                       move = move - 360;
+       }
+       else
+       {
+               if (move <= -180)
+                       move = move + 360;
+       }
+       if (move > 0)
+       {
+               if (move > speed)
+                       move = speed;
+       }
+       else
+       {
+               if (move < -speed)
+                       move = -speed;
+       }
+       
+       ent->v.angles[1] = anglemod (current + move);
+}
+
+/*
+==============
+PF_changepitch
+==============
+*/
+void PF_changepitch (void)
+{
+       edict_t         *ent;
+       float           ideal, current, move, speed;
+       eval_t          *val;
+       
+       ent = G_EDICT(OFS_PARM0);
+       current = anglemod( ent->v.angles[0] );
+       if (val = GETEDICTFIELDVALUE(ent, eval_idealpitch))
+               ideal = val->_float;
+       else
+       {
+               PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
+               return;
+       }
+       if (val = GETEDICTFIELDVALUE(ent, eval_pitch_speed))
+               speed = val->_float;
+       else
+       {
+               PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
+               return;
+       }
+       
+       if (current == ideal)
+               return;
+       move = ideal - current;
+       if (ideal > current)
+       {
+               if (move >= 180)
+                       move = move - 360;
+       }
+       else
+       {
+               if (move <= -180)
+                       move = move + 360;
+       }
+       if (move > 0)
+       {
+               if (move > speed)
+                       move = speed;
+       }
+       else
+       {
+               if (move < -speed)
+                       move = -speed;
+       }
+       
+       ent->v.angles[0] = anglemod (current + move);
+}
+
+/*
+===============================================================================
+
+MESSAGE WRITING
+
+===============================================================================
+*/
+
+#define        MSG_BROADCAST   0               // unreliable to all
+#define        MSG_ONE                 1               // reliable to one (msg_entity)
+#define        MSG_ALL                 2               // reliable to all
+#define        MSG_INIT                3               // write to the init string
+
+sizebuf_t *WriteDest (void)
+{
+       int             entnum;
+       int             dest;
+       edict_t *ent;
+
+       dest = G_FLOAT(OFS_PARM0);
+       switch (dest)
+       {
+       case MSG_BROADCAST:
+               return &sv.datagram;
+       
+       case MSG_ONE:
+               ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
+               entnum = NUM_FOR_EDICT(ent);
+               if (entnum < 1 || entnum > svs.maxclients)
+                       PR_RunError ("WriteDest: not a client");
+               return &svs.clients[entnum-1].message;
+               
+       case MSG_ALL:
+               return &sv.reliable_datagram;
+       
+       case MSG_INIT:
+               return &sv.signon;
+
+       default:
+               PR_RunError ("WriteDest: bad destination");
+               break;
+       }
+       
+       return NULL;
+}
+
+void PF_WriteByte (void)
+{
+       MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteChar (void)
+{
+       MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteShort (void)
+{
+       MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteLong (void)
+{
+       MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteAngle (void)
+{
+       MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteCoord (void)
+{
+       MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteString (void)
+{
+       MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1));
+}
+
+
+void PF_WriteEntity (void)
+{
+       MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1));
+}
+
+//=============================================================================
+
+int SV_ModelIndex (char *name);
+
+void PF_makestatic (void)
+{
+       edict_t *ent;
+       int             i;
+       
+       ent = G_EDICT(OFS_PARM0);
+
+       MSG_WriteByte (&sv.signon,svc_spawnstatic);
+
+       MSG_WriteByte (&sv.signon, SV_ModelIndex(pr_strings + ent->v.model));
+
+       MSG_WriteByte (&sv.signon, ent->v.frame);
+       MSG_WriteByte (&sv.signon, ent->v.colormap);
+       MSG_WriteByte (&sv.signon, ent->v.skin);
+       for (i=0 ; i<3 ; i++)
+       {
+               MSG_WriteCoord(&sv.signon, ent->v.origin[i]);
+               MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
+       }
+
+// throw the entity away now
+       ED_Free (ent);
+}
+
+//=============================================================================
+
+/*
+==============
+PF_setspawnparms
+==============
+*/
+void PF_setspawnparms (void)
+{
+       edict_t *ent;
+       int             i;
+       client_t        *client;
+
+       ent = G_EDICT(OFS_PARM0);
+       i = NUM_FOR_EDICT(ent);
+       if (i < 1 || i > svs.maxclients)
+               PR_RunError ("Entity is not a client");
+
+       // copy spawn parms out of the client_t
+       client = svs.clients + (i-1);
+
+       for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+               (&pr_global_struct->parm1)[i] = client->spawn_parms[i];
+}
+
+/*
+==============
+PF_changelevel
+==============
+*/
+void PF_changelevel (void)
+{
+       char    *s;
+
+// make sure we don't issue two changelevels
+       if (svs.changelevel_issued)
+               return;
+       svs.changelevel_issued = true;
+       
+       s = G_STRING(OFS_PARM0);
+       Cbuf_AddText (va("changelevel %s\n",s));
+}
+
+void PF_sin (void)
+{
+       G_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0));
+}
+
+void PF_cos (void)
+{
+       G_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0));
+}
+
+void PF_sqrt (void)
+{
+       G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0));
+}
+
+/*
+=================
+PF_RandomVec
+
+Returns a vector of length < 1
+
+randomvec()
+=================
+*/
+void PF_randomvec (void)
+{
+       vec3_t          temp;
+       do
+       {
+               temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
+               temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
+               temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
+       }
+       while (DotProduct(temp, temp) >= 1);
+       VectorCopy (temp, G_VECTOR(OFS_RETURN));        
+}
+
+void SV_LightPoint (vec3_t color, vec3_t p);
+/*
+=================
+PF_GetLight
+
+Returns a color vector indicating the lighting at the requested point.
+
+(Internal Operation note: actually measures the light beneath the point, just like
+                          the model lighting on the client)
+
+getlight(vector)
+=================
+*/
+void PF_GetLight (void)
+{
+       vec3_t          color;
+       vec_t*          p;
+       p = G_VECTOR(OFS_PARM0);
+       SV_LightPoint (color, p);
+       VectorCopy (color, G_VECTOR(OFS_RETURN));       
+}
+
+#define MAX_QC_CVARS 128
+cvar_t qc_cvar[MAX_QC_CVARS];
+int currentqc_cvar;
+
+void PF_registercvar (void)
+{
+       char    *name, *value;
+       cvar_t  *variable;
+       name = G_STRING(OFS_PARM1);
+       value = G_STRING(OFS_PARM2);
+       G_FLOAT(OFS_RETURN) = 0;
+// first check to see if it has allready been defined
+       if (Cvar_FindVar (name))
+               return;
+       
+// check for overlap with a command
+       if (Cmd_Exists (name))
+       {
+               Con_Printf ("PF_registercvar: %s is a command\n", name);
+               return;
+       }
+
+       if (currentqc_cvar >= MAX_QC_CVARS)
+               PR_RunError ("PF_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
+
+// copy the name and value
+       variable = &qc_cvar[currentqc_cvar++];
+       variable->name = Z_Malloc (strlen(name)+1);     
+       strcpy (variable->name, name);
+       variable->string = Z_Malloc (strlen(value)+1);  
+       strcpy (variable->string, value);
+       variable->value = atof (value);
+       
+// link the variable in
+       variable->next = cvar_vars;
+       cvar_vars = variable;
+       G_FLOAT(OFS_RETURN) = 1; // success
+}
+
+/*
+=================
+PF_min
+
+returns the minimum of two supplied floats
+
+min(a, b)
+=================
+*/
+void PF_min (void)
+{
+       // LordHavoc: 3+ argument enhancement suggested by FrikaC
+       if (pr_argc == 2)
+               G_FLOAT(OFS_RETURN) = min(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
+       else if (pr_argc >= 3)
+       {
+               int i;
+               float f = G_FLOAT(OFS_PARM0);
+               for (i = 1;i < pr_argc;i++)
+                       if (G_FLOAT((OFS_PARM0+i*3)) < f)
+                               f = G_FLOAT((OFS_PARM0+i*3));
+               G_FLOAT(OFS_RETURN) = f;
+       }
+       else
+               PR_RunError("min: must supply at least 2 floats\n");
+}
+
+/*
+=================
+PF_max
+
+returns the maximum of two supplied floats
+
+max(a, b)
+=================
+*/
+void PF_max (void)
+{
+       // LordHavoc: 3+ argument enhancement suggested by FrikaC
+       if (pr_argc == 2)
+               G_FLOAT(OFS_RETURN) = max(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
+       else if (pr_argc >= 3)
+       {
+               int i;
+               float f = G_FLOAT(OFS_PARM0);
+               for (i = 1;i < pr_argc;i++)
+                       if (G_FLOAT((OFS_PARM0+i*3)) > f)
+                               f = G_FLOAT((OFS_PARM0+i*3));
+               G_FLOAT(OFS_RETURN) = f;
+       }
+       else
+               PR_RunError("max: must supply at least 2 floats\n");
+}
+
+/*
+=================
+PF_bound
+
+returns number bounded by supplied range
+
+min(min, value, max)
+=================
+*/
+void PF_bound (void)
+{
+       G_FLOAT(OFS_RETURN) = bound(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1), G_FLOAT(OFS_PARM2));
+}
+
+/*
+=================
+PF_pow
+
+returns a raised to power b
+
+pow(a, b)
+=================
+*/
+void PF_pow (void)
+{
+       G_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
+}
+
+void PF_Fixme (void)
+{
+       PR_RunError ("unimplemented builtin"); // LordHavoc: was misspelled (bulitin)
+}
+
+
+
+builtin_t pr_builtin[] =
+{
+PF_Fixme,
+PF_makevectors,        // void(entity e)       makevectors             = #1;
+PF_setorigin,  // void(entity e, vector o) setorigin   = #2;
+PF_setmodel,   // void(entity e, string m) setmodel    = #3;
+PF_setsize,    // void(entity e, vector min, vector max) setsize = #4;
+PF_Fixme,      // void(entity e, vector min, vector max) setabssize = #5;
+PF_break,      // void() break                                         = #6;
+PF_random,     // float() random                                               = #7;
+PF_sound,      // void(entity e, float chan, string samp) sound = #8;
+PF_normalize,  // vector(vector v) normalize                   = #9;
+PF_error,      // void(string e) error                         = #10;
+PF_objerror,   // void(string e) objerror                              = #11;
+PF_vlen,       // float(vector v) vlen                         = #12;
+PF_vectoyaw,   // float(vector v) vectoyaw             = #13;
+PF_Spawn,      // entity() spawn                                               = #14;
+PF_Remove,     // void(entity e) remove                                = #15;
+PF_traceline,  // float(vector v1, vector v2, float tryents) traceline = #16;
+PF_checkclient,        // entity() clientlist                                  = #17;
+PF_Find,       // entity(entity start, .string fld, string match) find = #18;
+PF_precache_sound,     // void(string s) precache_sound                = #19;
+PF_precache_model,     // void(string s) precache_model                = #20;
+PF_stuffcmd,   // void(entity client, string s)stuffcmd = #21;
+PF_findradius, // entity(vector org, float rad) findradius = #22;
+PF_bprint,     // void(string s) bprint                                = #23;
+PF_sprint,     // void(entity client, string s) sprint = #24;
+PF_dprint,     // void(string s) dprint                                = #25;
+PF_ftos,       // void(string s) ftos                          = #26;
+PF_vtos,       // void(string s) vtos                          = #27;
+PF_coredump,
+PF_traceon,
+PF_traceoff,
+PF_eprint,     // void(entity e) debug print an entire entity
+PF_walkmove, // float(float yaw, float dist) walkmove
+PF_Fixme, // float(float yaw, float dist) walkmove
+PF_droptofloor,
+PF_lightstyle,
+PF_rint,
+PF_floor,
+PF_ceil,
+PF_Fixme,
+PF_checkbottom,
+PF_pointcontents,
+PF_Fixme,
+PF_fabs,
+PF_aim,
+PF_cvar,
+PF_localcmd,
+PF_nextent,
+PF_particle,
+PF_changeyaw,
+PF_Fixme,
+PF_vectoangles,
+
+PF_WriteByte,
+PF_WriteChar,
+PF_WriteShort,
+PF_WriteLong,
+PF_WriteCoord,
+PF_WriteAngle,
+PF_WriteString,
+PF_WriteEntity,
+
+PF_sin,
+PF_cos,
+PF_sqrt,
+PF_changepitch,
+PF_TraceToss,
+PF_etos,
+PF_Fixme,
+
+SV_MoveToGoal,
+PF_precache_file,
+PF_makestatic,
+
+PF_changelevel,
+PF_Fixme,
+
+PF_cvar_set,
+PF_centerprint,
+
+PF_ambientsound,
+
+PF_precache_model,
+PF_precache_sound,             // precache_sound2 is different only for qcc
+PF_precache_file,
+
+PF_setspawnparms,
+
+PF_Fixme,                      // #79 LordHavoc: dunno who owns 79-89, so these are just padding
+PF_Fixme,                      // #80 
+PF_Fixme,                      // #81
+PF_Fixme,                      // #82
+PF_Fixme,                      // #83
+PF_Fixme,                      // #84
+PF_Fixme,                      // #85
+PF_Fixme,                      // #86
+PF_Fixme,                      // #87
+PF_Fixme,                      // #88
+PF_Fixme,                      // #89
+
+PF_tracebox,           // #90 LordHavoc builtin range (9x)
+PF_randomvec,          // #91
+PF_GetLight,           // #92
+PF_registercvar,       // #93
+PF_min,                                // #94
+PF_max,                                // #95
+PF_bound,                      // #96
+PF_pow,                                // #97
+PF_FindFloat,          // #98
+PF_checkextension      // #99
+};
+
+builtin_t *pr_builtins = pr_builtin;
+int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]);
+
diff --git a/pr_comp.h b/pr_comp.h
new file mode 100644 (file)
index 0000000..94410c4
--- /dev/null
+++ b/pr_comp.h
@@ -0,0 +1,180 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// this file is shared by quake and qcc
+
+typedef int    func_t;
+typedef int    string_t;
+
+typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t;
+
+
+#define        OFS_NULL                0
+#define        OFS_RETURN              1
+#define        OFS_PARM0               4               // leave 3 ofs for each parm to hold vectors
+#define        OFS_PARM1               7
+#define        OFS_PARM2               10
+#define        OFS_PARM3               13
+#define        OFS_PARM4               16
+#define        OFS_PARM5               19
+#define        OFS_PARM6               22
+#define        OFS_PARM7               25
+#define        RESERVED_OFS    28
+
+
+enum {
+       OP_DONE,
+       OP_MUL_F,
+       OP_MUL_V,
+       OP_MUL_FV,
+       OP_MUL_VF,
+       OP_DIV_F,
+       OP_ADD_F,
+       OP_ADD_V,
+       OP_SUB_F,
+       OP_SUB_V,
+       
+       OP_EQ_F,
+       OP_EQ_V,
+       OP_EQ_S,
+       OP_EQ_E,
+       OP_EQ_FNC,
+       
+       OP_NE_F,
+       OP_NE_V,
+       OP_NE_S,
+       OP_NE_E,
+       OP_NE_FNC,
+       
+       OP_LE,
+       OP_GE,
+       OP_LT,
+       OP_GT,
+
+       OP_LOAD_F,
+       OP_LOAD_V,
+       OP_LOAD_S,
+       OP_LOAD_ENT,
+       OP_LOAD_FLD,
+       OP_LOAD_FNC,
+
+       OP_ADDRESS,
+
+       OP_STORE_F,
+       OP_STORE_V,
+       OP_STORE_S,
+       OP_STORE_ENT,
+       OP_STORE_FLD,
+       OP_STORE_FNC,
+
+       OP_STOREP_F,
+       OP_STOREP_V,
+       OP_STOREP_S,
+       OP_STOREP_ENT,
+       OP_STOREP_FLD,
+       OP_STOREP_FNC,
+
+       OP_RETURN,
+       OP_NOT_F,
+       OP_NOT_V,
+       OP_NOT_S,
+       OP_NOT_ENT,
+       OP_NOT_FNC,
+       OP_IF,
+       OP_IFNOT,
+       OP_CALL0,
+       OP_CALL1,
+       OP_CALL2,
+       OP_CALL3,
+       OP_CALL4,
+       OP_CALL5,
+       OP_CALL6,
+       OP_CALL7,
+       OP_CALL8,
+       OP_STATE,
+       OP_GOTO,
+       OP_AND,
+       OP_OR,
+       
+       OP_BITAND,
+       OP_BITOR
+};
+
+
+typedef struct statement_s
+{
+       unsigned short  op;
+       signed short    a,b,c;
+} dstatement_t;
+
+typedef struct
+{
+       unsigned short  type;           // if DEF_SAVEGLOBGAL bit is set
+                                                               // the variable needs to be saved in savegames
+       unsigned short  ofs;
+       int                     s_name;
+} ddef_t;
+#define        DEF_SAVEGLOBAL  (1<<15)
+
+#define        MAX_PARMS       8
+
+typedef struct
+{
+       int             first_statement;        // negative numbers are builtins
+       int             parm_start;
+       int             locals;                         // total ints of parms + locals
+       
+       int             profile;                // runtime
+       
+       int             s_name;
+       int             s_file;                 // source file defined in
+       
+       int             numparms;
+       byte    parm_size[MAX_PARMS];
+} dfunction_t;
+
+
+#define        PROG_VERSION    6
+typedef struct
+{
+       int             version;
+       int             crc;                    // check of header file
+       
+       int             ofs_statements;
+       int             numstatements;  // statement 0 is an error
+
+       int             ofs_globaldefs;
+       int             numglobaldefs;
+       
+       int             ofs_fielddefs;
+       int             numfielddefs;
+       
+       int             ofs_functions;
+       int             numfunctions;   // function 0 is an empty
+       
+       int             ofs_strings;
+       int             numstrings;             // first string is a null string
+
+       int             ofs_globals;
+       int             numglobals;
+       
+       int             entityfields;
+} dprograms_t;
+
diff --git a/pr_edict.c b/pr_edict.c
new file mode 100644 (file)
index 0000000..f327bac
--- /dev/null
@@ -0,0 +1,1342 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_edict.c -- entity dictionary
+
+#include "quakedef.h"
+
+dprograms_t            *progs;
+dfunction_t            *pr_functions;
+char                   *pr_strings;
+ddef_t                 *pr_fielddefs;
+ddef_t                 *pr_globaldefs;
+dstatement_t   *pr_statements;
+globalvars_t   *pr_global_struct;
+float                  *pr_globals;                    // same as pr_global_struct
+int                            pr_edict_size;                  // in bytes
+int                            pr_edictareasize;               // LordHavoc: in bytes
+
+unsigned short         pr_crc;
+
+int            type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
+
+ddef_t *ED_FieldAtOfs (int ofs);
+qboolean       ED_ParseEpair (void *base, ddef_t *key, char *s);
+
+cvar_t nomonsters = {"nomonsters", "0"};
+cvar_t gamecfg = {"gamecfg", "0"};
+cvar_t scratch1 = {"scratch1", "0"};
+cvar_t scratch2 = {"scratch2", "0"};
+cvar_t scratch3 = {"scratch3", "0"};
+cvar_t scratch4 = {"scratch4", "0"};
+cvar_t savedgamecfg = {"savedgamecfg", "0", true};
+cvar_t saved1 = {"saved1", "0", true};
+cvar_t saved2 = {"saved2", "0", true};
+cvar_t saved3 = {"saved3", "0", true};
+cvar_t saved4 = {"saved4", "0", true};
+cvar_t decors = {"decors", "0"};
+cvar_t nehx00 = {"nehx00", "0"};cvar_t nehx01 = {"nehx01", "0"};
+cvar_t nehx02 = {"nehx02", "0"};cvar_t nehx03 = {"nehx03", "0"};
+cvar_t nehx04 = {"nehx04", "0"};cvar_t nehx05 = {"nehx05", "0"};
+cvar_t nehx06 = {"nehx06", "0"};cvar_t nehx07 = {"nehx07", "0"};
+cvar_t nehx08 = {"nehx08", "0"};cvar_t nehx09 = {"nehx09", "0"};
+cvar_t nehx10 = {"nehx10", "0"};cvar_t nehx11 = {"nehx11", "0"};
+cvar_t nehx12 = {"nehx12", "0"};cvar_t nehx13 = {"nehx13", "0"};
+cvar_t nehx14 = {"nehx14", "0"};cvar_t nehx15 = {"nehx15", "0"};
+cvar_t nehx16 = {"nehx16", "0"};cvar_t nehx17 = {"nehx17", "0"};
+cvar_t nehx18 = {"nehx18", "0"};cvar_t nehx19 = {"nehx19", "0"};
+cvar_t cutscene = {"cutscene", "1"};
+// LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
+cvar_t pr_boundscheck = {"pr_boundscheck", "1"};
+
+#define        MAX_FIELD_LEN   64
+#define GEFV_CACHESIZE 2
+
+typedef struct {
+       ddef_t  *pcache;
+       char    field[MAX_FIELD_LEN];
+} gefv_cache;
+
+static gefv_cache      gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}};
+
+ddef_t *ED_FindField (char *name);
+dfunction_t *ED_FindFunction (char *name);
+
+// LordHavoc: in an effort to eliminate time wasted on GetEdictFieldValue...  these are defined as externs in progs.h
+int eval_gravity;
+int eval_button3;
+int eval_button4;
+int eval_button5;
+int eval_button6;
+int eval_button7;
+int eval_button8;
+int eval_glow_size;
+int eval_glow_trail;
+int eval_glow_color;
+int eval_items2;
+int eval_scale;
+int eval_alpha;
+int eval_fullbright;
+int eval_ammo_shells1;
+int eval_ammo_nails1;
+int eval_ammo_lava_nails;
+int eval_ammo_rockets1;
+int eval_ammo_multi_rockets;
+int eval_ammo_cells1;
+int eval_ammo_plasma;
+int eval_idealpitch;
+int eval_pitch_speed;
+int eval_viewmodelforclient;
+int eval_nodrawtoclient;
+int eval_drawonlytoclient;
+int eval_colormod;
+int eval_ping;
+int eval_movement;
+
+dfunction_t *SV_PlayerPhysicsQC;
+dfunction_t *EndFrameQC;
+
+int FindFieldOffset(char *field)
+{
+       ddef_t *d;
+       d = ED_FindField(field);
+       if (!d)
+               return 0;
+       return d->ofs*4;
+}
+
+void FindEdictFieldOffsets()
+{
+       eval_gravity = FindFieldOffset("gravity");
+       eval_button3 = FindFieldOffset("button3");
+       eval_button4 = FindFieldOffset("button4");
+       eval_button5 = FindFieldOffset("button5");
+       eval_button6 = FindFieldOffset("button6");
+       eval_button7 = FindFieldOffset("button7");
+       eval_button8 = FindFieldOffset("button8");
+       eval_glow_size = FindFieldOffset("glow_size");
+       eval_glow_trail = FindFieldOffset("glow_trail");
+       eval_glow_color = FindFieldOffset("glow_color");
+       eval_items2 = FindFieldOffset("items2");
+       eval_scale = FindFieldOffset("scale");
+       eval_alpha = FindFieldOffset("alpha");
+       eval_fullbright = FindFieldOffset("fullbright");
+       eval_ammo_shells1 = FindFieldOffset("ammo_shells1");
+       eval_ammo_nails1 = FindFieldOffset("ammo_nails1");
+       eval_ammo_lava_nails = FindFieldOffset("ammo_lava_nails");
+       eval_ammo_rockets1 = FindFieldOffset("ammo_rockets1");
+       eval_ammo_multi_rockets = FindFieldOffset("ammo_multi_rockets");
+       eval_ammo_cells1 = FindFieldOffset("ammo_cells1");
+       eval_ammo_plasma = FindFieldOffset("ammo_plasma");
+       eval_idealpitch = FindFieldOffset("idealpitch");
+       eval_pitch_speed = FindFieldOffset("pitch_speed");
+       eval_viewmodelforclient = FindFieldOffset("viewmodelforclient");
+       eval_nodrawtoclient = FindFieldOffset("nodrawtoclient");
+       eval_drawonlytoclient = FindFieldOffset("drawonlytoclient");
+       eval_colormod = FindFieldOffset("colormod");
+       eval_ping = FindFieldOffset("ping");
+       eval_movement = FindFieldOffset("movement");
+
+       // LordHavoc: allowing QuakeC to override the player movement code
+       SV_PlayerPhysicsQC = ED_FindFunction ("SV_PlayerPhysics");
+       // LordHavoc: support for endframe
+       EndFrameQC = ED_FindFunction ("EndFrame");
+};
+
+/*
+=================
+ED_ClearEdict
+
+Sets everything to NULL
+=================
+*/
+void ED_ClearEdict (edict_t *e)
+{
+       memset (&e->v, 0, progs->entityfields * 4);
+       e->free = false;
+}
+
+/*
+=================
+ED_Alloc
+
+Either finds a free edict, or allocates a new one.
+Try to avoid reusing an entity that was recently freed, because it
+can cause the client to think the entity morphed into something else
+instead of being removed and recreated, which can cause interpolated
+angles and bad trails.
+=================
+*/
+edict_t *ED_Alloc (void)
+{
+       int                     i;
+       edict_t         *e;
+
+       for ( i=svs.maxclients+1 ; i<sv.num_edicts ; i++)
+       {
+               e = EDICT_NUM(i);
+               // the first couple seconds of server time can involve a lot of
+               // freeing and allocating, so relax the replacement policy
+               if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )
+               {
+                       ED_ClearEdict (e);
+                       return e;
+               }
+       }
+       
+       if (i == MAX_EDICTS)
+               Sys_Error ("ED_Alloc: no free edicts");
+               
+       sv.num_edicts++;
+       e = EDICT_NUM(i);
+       ED_ClearEdict (e);
+
+       return e;
+}
+
+/*
+=================
+ED_Free
+
+Marks the edict as free
+FIXME: walk all entities and NULL out references to this entity
+=================
+*/
+void ED_Free (edict_t *ed)
+{
+       SV_UnlinkEdict (ed);            // unlink from world bsp
+
+       ed->free = true;
+       ed->v.model = 0;
+       ed->v.takedamage = 0;
+       ed->v.modelindex = 0;
+       ed->v.colormap = 0;
+       ed->v.skin = 0;
+       ed->v.frame = 0;
+       VectorCopy (vec3_origin, ed->v.origin);
+       VectorCopy (vec3_origin, ed->v.angles);
+       ed->v.nextthink = -1;
+       ed->v.solid = 0;
+       
+       ed->freetime = sv.time;
+}
+
+//===========================================================================
+
+/*
+============
+ED_GlobalAtOfs
+============
+*/
+ddef_t *ED_GlobalAtOfs (int ofs)
+{
+       ddef_t          *def;
+       int                     i;
+       
+       for (i=0 ; i<progs->numglobaldefs ; i++)
+       {
+               def = &pr_globaldefs[i];
+               if (def->ofs == ofs)
+                       return def;
+       }
+       return NULL;
+}
+
+/*
+============
+ED_FieldAtOfs
+============
+*/
+ddef_t *ED_FieldAtOfs (int ofs)
+{
+       ddef_t          *def;
+       int                     i;
+       
+       for (i=0 ; i<progs->numfielddefs ; i++)
+       {
+               def = &pr_fielddefs[i];
+               if (def->ofs == ofs)
+                       return def;
+       }
+       return NULL;
+}
+
+/*
+============
+ED_FindField
+============
+*/
+ddef_t *ED_FindField (char *name)
+{
+       ddef_t          *def;
+       int                     i;
+       
+       for (i=0 ; i<progs->numfielddefs ; i++)
+       {
+               def = &pr_fielddefs[i];
+               if (!strcmp(pr_strings + def->s_name,name) )
+                       return def;
+       }
+       return NULL;
+}
+
+
+/*
+============
+ED_FindGlobal
+============
+*/
+ddef_t *ED_FindGlobal (char *name)
+{
+       ddef_t          *def;
+       int                     i;
+       
+       for (i=0 ; i<progs->numglobaldefs ; i++)
+       {
+               def = &pr_globaldefs[i];
+               if (!strcmp(pr_strings + def->s_name,name) )
+                       return def;
+       }
+       return NULL;
+}
+
+
+/*
+============
+ED_FindFunction
+============
+*/
+dfunction_t *ED_FindFunction (char *name)
+{
+       dfunction_t             *func;
+       int                             i;
+       
+       for (i=0 ; i<progs->numfunctions ; i++)
+       {
+               func = &pr_functions[i];
+               if (!strcmp(pr_strings + func->s_name,name) )
+                       return func;
+       }
+       return NULL;
+}
+
+
+/*
+eval_t *GetEdictFieldValue(edict_t *ed, char *field)
+{
+       ddef_t                  *def = NULL;
+       int                             i;
+       static int              rep = 0;
+
+       for (i=0 ; i<GEFV_CACHESIZE ; i++)
+       {
+               if (!strcmp(field, gefvCache[i].field))
+               {
+                       def = gefvCache[i].pcache;
+                       goto Done;
+               }
+       }
+
+       def = ED_FindField (field);
+
+       if (strlen(field) < MAX_FIELD_LEN)
+       {
+               gefvCache[rep].pcache = def;
+               strcpy (gefvCache[rep].field, field);
+               rep ^= 1;
+       }
+
+Done:
+       if (!def)
+               return NULL;
+
+       return (eval_t *)((char *)&ed->v + def->ofs*4);
+}
+*/
+
+/*
+============
+PR_ValueString
+
+Returns a string describing *data in a type specific manner
+=============
+*/
+char *PR_ValueString (etype_t type, eval_t *val)
+{
+       static char     line[256];
+       ddef_t          *def;
+       dfunction_t     *f;
+       
+       type &= ~DEF_SAVEGLOBAL;
+
+       switch (type)
+       {
+       case ev_string:
+               sprintf (line, "%s", pr_strings + val->string);
+               break;
+       case ev_entity: 
+               sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) );
+               break;
+       case ev_function:
+               f = pr_functions + val->function;
+               sprintf (line, "%s()", pr_strings + f->s_name);
+               break;
+       case ev_field:
+               def = ED_FieldAtOfs ( val->_int );
+               sprintf (line, ".%s", pr_strings + def->s_name);
+               break;
+       case ev_void:
+               sprintf (line, "void");
+               break;
+       case ev_float:
+               sprintf (line, "%5.1f", val->_float);
+               break;
+       case ev_vector:
+               sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
+               break;
+       case ev_pointer:
+               sprintf (line, "pointer");
+               break;
+       default:
+               sprintf (line, "bad type %i", type);
+               break;
+       }
+       
+       return line;
+}
+
+/*
+============
+PR_UglyValueString
+
+Returns a string describing *data in a type specific manner
+Easier to parse than PR_ValueString
+=============
+*/
+char *PR_UglyValueString (etype_t type, eval_t *val)
+{
+       static char     line[256];
+       ddef_t          *def;
+       dfunction_t     *f;
+       
+       type &= ~DEF_SAVEGLOBAL;
+
+       switch (type)
+       {
+       case ev_string:
+               sprintf (line, "%s", pr_strings + val->string);
+               break;
+       case ev_entity: 
+               sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)));
+               break;
+       case ev_function:
+               f = pr_functions + val->function;
+               sprintf (line, "%s", pr_strings + f->s_name);
+               break;
+       case ev_field:
+               def = ED_FieldAtOfs ( val->_int );
+               sprintf (line, "%s", pr_strings + def->s_name);
+               break;
+       case ev_void:
+               sprintf (line, "void");
+               break;
+       case ev_float:
+               sprintf (line, "%f", val->_float);
+               break;
+       case ev_vector:
+               sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
+               break;
+       default:
+               sprintf (line, "bad type %i", type);
+               break;
+       }
+       
+       return line;
+}
+
+/*
+============
+PR_GlobalString
+
+Returns a string with a description and the contents of a global,
+padded to 20 field width
+============
+*/
+char *PR_GlobalString (int ofs)
+{
+       char    *s;
+       int             i;
+       ddef_t  *def;
+       void    *val;
+       static char     line[128];
+       
+       val = (void *)&pr_globals[ofs];
+       def = ED_GlobalAtOfs(ofs);
+       if (!def)
+               sprintf (line,"%i(???)", ofs);
+       else
+       {
+               s = PR_ValueString (def->type, val);
+               sprintf (line,"%i(%s)%s", ofs, pr_strings + def->s_name, s);
+       }
+       
+       i = strlen(line);
+       for ( ; i<20 ; i++)
+               strcat (line," ");
+       strcat (line," ");
+               
+       return line;
+}
+
+char *PR_GlobalStringNoContents (int ofs)
+{
+       int             i;
+       ddef_t  *def;
+       static char     line[128];
+       
+       def = ED_GlobalAtOfs(ofs);
+       if (!def)
+               sprintf (line,"%i(???)", ofs);
+       else
+               sprintf (line,"%i(%s)", ofs, pr_strings + def->s_name);
+       
+       i = strlen(line);
+       for ( ; i<20 ; i++)
+               strcat (line," ");
+       strcat (line," ");
+               
+       return line;
+}
+
+
+/*
+=============
+ED_Print
+
+For debugging
+=============
+*/
+// LordHavoc: optimized this to print out much more quickly
+void ED_Print (edict_t *ed)
+{
+       int             l;
+       ddef_t  *d;
+       int             *v;
+       int             i, j;
+       char    *name;
+       int             type;
+       char    tempstring[8192]; // temporary string buffer
+
+       if (ed->free)
+       {
+               Con_Printf ("FREE\n");
+               return;
+       }
+
+       tempstring[0] = 0;
+       sprintf(tempstring, "\nEDICT %i:\n", NUM_FOR_EDICT(ed));
+       for (i=1 ; i<progs->numfielddefs ; i++)
+       {
+               d = &pr_fielddefs[i];
+               name = pr_strings + d->s_name;
+               if (name[strlen(name)-2] == '_')
+                       continue;       // skip _x, _y, _z vars
+                       
+               v = (int *)((char *)&ed->v + d->ofs*4);
+
+       // if the value is still all 0, skip the field
+               type = d->type & ~DEF_SAVEGLOBAL;
+               
+               for (j=0 ; j<type_size[type] ; j++)
+                       if (v[j])
+                               break;
+               if (j == type_size[type])
+                       continue;
+
+               strcat(tempstring, name);
+               l = strlen (name);
+               while (l++ < 15)
+                       strcat(tempstring, " ");
+
+               strcat(tempstring, PR_ValueString(d->type, (eval_t *)v));
+               strcat(tempstring, "\n");
+       }
+       Con_Printf(tempstring);
+}
+
+/*
+=============
+ED_Write
+
+For savegames
+=============
+*/
+void ED_Write (FILE *f, edict_t *ed)
+{
+       ddef_t  *d;
+       int             *v;
+       int             i, j;
+       char    *name;
+       int             type;
+
+       fprintf (f, "{\n");
+
+       if (ed->free)
+       {
+               fprintf (f, "}\n");
+               return;
+       }
+       
+       for (i=1 ; i<progs->numfielddefs ; i++)
+       {
+               d = &pr_fielddefs[i];
+               name = pr_strings + d->s_name;
+               if (name[strlen(name)-2] == '_')
+                       continue;       // skip _x, _y, _z vars
+                       
+               v = (int *)((char *)&ed->v + d->ofs*4);
+
+       // if the value is still all 0, skip the field
+               type = d->type & ~DEF_SAVEGLOBAL;
+               for (j=0 ; j<type_size[type] ; j++)
+                       if (v[j])
+                               break;
+               if (j == type_size[type])
+                       continue;
+       
+               fprintf (f,"\"%s\" ",name);
+               fprintf (f,"\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v));               
+       }
+
+       fprintf (f, "}\n");
+}
+
+void ED_PrintNum (int ent)
+{
+       ED_Print (EDICT_NUM(ent));
+}
+
+/*
+=============
+ED_PrintEdicts
+
+For debugging, prints all the entities in the current server
+=============
+*/
+void ED_PrintEdicts (void)
+{
+       int             i;
+       
+       Con_Printf ("%i entities\n", sv.num_edicts);
+       for (i=0 ; i<sv.num_edicts ; i++)
+               ED_PrintNum (i);
+}
+
+/*
+=============
+ED_PrintEdict_f
+
+For debugging, prints a single edicy
+=============
+*/
+void ED_PrintEdict_f (void)
+{
+       int             i;
+       
+       i = atoi (Cmd_Argv(1));
+       if (i >= sv.num_edicts)
+       {
+               Con_Printf("Bad edict number\n");
+               return;
+       }
+       ED_PrintNum (i);
+}
+
+/*
+=============
+ED_Count
+
+For debugging
+=============
+*/
+void ED_Count (void)
+{
+       int             i;
+       edict_t *ent;
+       int             active, models, solid, step;
+
+       active = models = solid = step = 0;
+       for (i=0 ; i<sv.num_edicts ; i++)
+       {
+               ent = EDICT_NUM(i);
+               if (ent->free)
+                       continue;
+               active++;
+               if (ent->v.solid)
+                       solid++;
+               if (ent->v.model)
+                       models++;
+               if (ent->v.movetype == MOVETYPE_STEP)
+                       step++;
+       }
+
+       Con_Printf ("num_edicts:%3i\n", sv.num_edicts);
+       Con_Printf ("active    :%3i\n", active);
+       Con_Printf ("view      :%3i\n", models);
+       Con_Printf ("touch     :%3i\n", solid);
+       Con_Printf ("step      :%3i\n", step);
+
+}
+
+/*
+==============================================================================
+
+                                       ARCHIVING GLOBALS
+
+FIXME: need to tag constants, doesn't really work
+==============================================================================
+*/
+
+/*
+=============
+ED_WriteGlobals
+=============
+*/
+void ED_WriteGlobals (FILE *f)
+{
+       ddef_t          *def;
+       int                     i;
+       char            *name;
+       int                     type;
+
+       fprintf (f,"{\n");
+       for (i=0 ; i<progs->numglobaldefs ; i++)
+       {
+               def = &pr_globaldefs[i];
+               type = def->type;
+               if ( !(def->type & DEF_SAVEGLOBAL) )
+                       continue;
+               type &= ~DEF_SAVEGLOBAL;
+
+               if (type != ev_string
+               && type != ev_float
+               && type != ev_entity)
+                       continue;
+
+               name = pr_strings + def->s_name;                
+               fprintf (f,"\"%s\" ", name);
+               fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs]));              
+       }
+       fprintf (f,"}\n");
+}
+
+/*
+=============
+ED_ParseGlobals
+=============
+*/
+void ED_ParseGlobals (char *data)
+{
+       char    keyname[64];
+       ddef_t  *key;
+
+       while (1)
+       {       
+       // parse key
+               data = COM_Parse (data);
+               if (com_token[0] == '}')
+                       break;
+               if (!data)
+                       Host_Error ("ED_ParseEntity: EOF without closing brace");
+
+               strcpy (keyname, com_token);
+
+       // parse value  
+               data = COM_Parse (data);
+               if (!data)
+                       Host_Error ("ED_ParseEntity: EOF without closing brace");
+
+               if (com_token[0] == '}')
+                       Host_Error ("ED_ParseEntity: closing brace without data");
+
+               key = ED_FindGlobal (keyname);
+               if (!key)
+               {
+                       Con_DPrintf ("'%s' is not a global\n", keyname);
+                       continue;
+               }
+
+               if (!ED_ParseEpair ((void *)pr_globals, key, com_token))
+                       Host_Error ("ED_ParseGlobals: parse error");
+       }
+}
+
+//============================================================================
+
+
+/*
+=============
+ED_NewString
+=============
+*/
+char *ED_NewString (char *string)
+{
+       char    *new, *new_p;
+       int             i,l;
+       
+       l = strlen(string) + 1;
+       new = Hunk_Alloc (l);
+       new_p = new;
+
+       for (i=0 ; i< l ; i++)
+       {
+               if (string[i] == '\\' && i < l-1)
+               {
+                       i++;
+                       if (string[i] == 'n')
+                               *new_p++ = '\n';
+                       else
+                               *new_p++ = '\\';
+               }
+               else
+                       *new_p++ = string[i];
+       }
+       
+       return new;
+}
+
+
+/*
+=============
+ED_ParseEval
+
+Can parse either fields or globals
+returns false if error
+=============
+*/
+qboolean       ED_ParseEpair (void *base, ddef_t *key, char *s)
+{
+       int             i;
+       char    string[128];
+       ddef_t  *def;
+       char    *v, *w;
+       void    *d;
+       dfunction_t     *func;
+       
+       d = (void *)((int *)base + key->ofs);
+       
+       switch (key->type & ~DEF_SAVEGLOBAL)
+       {
+       case ev_string:
+               *(string_t *)d = ED_NewString (s) - pr_strings;
+               break;
+               
+       case ev_float:
+               *(float *)d = atof (s);
+               break;
+               
+       case ev_vector:
+               strcpy (string, s);
+               v = string;
+               w = string;
+               for (i=0 ; i<3 ; i++)
+               {
+                       while (*v && *v != ' ')
+                               v++;
+                       *v = 0;
+                       ((float *)d)[i] = atof (w);
+                       w = v = v+1;
+               }
+               break;
+               
+       case ev_entity:
+               *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s)));
+               break;
+               
+       case ev_field:
+               def = ED_FindField (s);
+               if (!def)
+               {
+                       // LordHavoc: don't warn about worldspawn sky/fog fields because they don't require mod support
+                       if (strcmp(s, "sky") && strncmp(s, "fog_", 4) && strcmp(s, "skyboxsize"))
+                               Con_DPrintf ("Can't find field %s\n", s);
+                       return false;
+               }
+               *(int *)d = G_INT(def->ofs);
+               break;
+       
+       case ev_function:
+               func = ED_FindFunction (s);
+               if (!func)
+               {
+                       Con_DPrintf ("Can't find function %s\n", s);
+                       return false;
+               }
+               *(func_t *)d = func - pr_functions;
+               break;
+               
+       default:
+               break;
+       }
+       return true;
+}
+
+/*
+====================
+ED_ParseEdict
+
+Parses an edict out of the given string, returning the new position
+ed should be a properly initialized empty edict.
+Used for initial level load and for savegames.
+====================
+*/
+char *ED_ParseEdict (char *data, edict_t *ent)
+{
+       ddef_t          *key;
+       qboolean        anglehack;
+       qboolean        init;
+       char            keyname[256];
+       int                     n;
+
+       init = false;
+
+// clear it
+       if (ent != sv.edicts)   // hack
+               memset (&ent->v, 0, progs->entityfields * 4);
+
+// go through all the dictionary pairs
+       while (1)
+       {       
+       // parse key
+               data = COM_Parse (data);
+               if (com_token[0] == '}')
+                       break;
+               if (!data)
+                       Host_Error ("ED_ParseEntity: EOF without closing brace");
+               
+// anglehack is to allow QuakeEd to write single scalar angles
+// and allow them to be turned into vectors. (FIXME...)
+if (!strcmp(com_token, "angle"))
+{
+       strcpy (com_token, "angles");
+       anglehack = true;
+}
+else
+       anglehack = false;
+
+// FIXME: change light to _light to get rid of this hack
+if (!strcmp(com_token, "light"))
+       strcpy (com_token, "light_lev");        // hack for single light def
+
+               strcpy (keyname, com_token);
+
+               // another hack to fix heynames with trailing spaces
+               n = strlen(keyname);
+               while (n && keyname[n-1] == ' ')
+               {
+                       keyname[n-1] = 0;
+                       n--;
+               }
+
+       // parse value  
+               data = COM_Parse (data);
+               if (!data)
+                       Host_Error ("ED_ParseEntity: EOF without closing brace");
+
+               if (com_token[0] == '}')
+                       Host_Error ("ED_ParseEntity: closing brace without data");
+
+               init = true;    
+
+// keynames with a leading underscore are used for utility comments,
+// and are immediately discarded by quake
+               if (keyname[0] == '_')
+                       continue;
+               
+               key = ED_FindField (keyname);
+               if (!key)
+               {
+                       Con_DPrintf ("'%s' is not a field\n", keyname);
+                       continue;
+               }
+
+if (anglehack)
+{
+char   temp[32];
+strcpy (temp, com_token);
+sprintf (com_token, "0 %s 0", temp);
+}
+
+               if (!ED_ParseEpair ((void *)&ent->v, key, com_token))
+                       Host_Error ("ED_ParseEdict: parse error");
+       }
+
+       if (!init)
+               ent->free = true;
+
+       return data;
+}
+
+
+/*
+================
+ED_LoadFromFile
+
+The entities are directly placed in the array, rather than allocated with
+ED_Alloc, because otherwise an error loading the map would have entity
+number references out of order.
+
+Creates a server's entity / program execution context by
+parsing textual entity definitions out of an ent file.
+
+Used for both fresh maps and savegame loads.  A fresh map would also need
+to call ED_CallSpawnFunctions () to let the objects initialize themselves.
+================
+*/
+void ED_LoadFromFile (char *data)
+{      
+       edict_t         *ent;
+       int                     inhibit;
+       dfunction_t     *func;
+       
+       ent = NULL;
+       inhibit = 0;
+       pr_global_struct->time = sv.time;
+       
+// parse ents
+       while (1)
+       {
+// parse the opening brace     
+               data = COM_Parse (data);
+               if (!data)
+                       break;
+               if (com_token[0] != '{')
+                       Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token);
+
+               if (!ent)
+                       ent = EDICT_NUM(0);
+               else
+                       ent = ED_Alloc ();
+               data = ED_ParseEdict (data, ent);
+
+// remove things from different skill levels or deathmatch
+               if (deathmatch.value)
+               {
+                       if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
+                       {
+                               ED_Free (ent);  
+                               inhibit++;
+                               continue;
+                       }
+               }
+               else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY  ))
+                         || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM))
+                         || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD  )))
+               {
+                       ED_Free (ent);  
+                       inhibit++;
+                       continue;
+               }
+
+//
+// immediately call spawn function
+//
+               if (!ent->v.classname)
+               {
+                       Con_Printf ("No classname for:\n");
+                       ED_Print (ent);
+                       ED_Free (ent);
+                       continue;
+               }
+
+       // look for the spawn function
+               func = ED_FindFunction ( pr_strings + ent->v.classname );
+
+               if (!func)
+               {
+                       if (developer.value) // don't confuse non-developers with errors
+                       {
+                               Con_Printf ("No spawn function for:\n");
+                               ED_Print (ent);
+                       }
+                       ED_Free (ent);
+                       continue;
+               }
+
+               pr_global_struct->self = EDICT_TO_PROG(ent);
+               PR_ExecuteProgram (func - pr_functions);
+       }       
+
+       Con_DPrintf ("%i entities inhibited\n", inhibit);
+}
+
+
+/*
+===============
+PR_LoadProgs
+===============
+*/
+void PR_LoadProgs (void)
+{
+       int             i;
+       dstatement_t *st;
+
+// flush the non-C variable lookup cache
+       for (i=0 ; i<GEFV_CACHESIZE ; i++)
+               gefvCache[i].field[0] = 0;
+
+       progs = (dprograms_t *)COM_LoadHunkFile ("progs.dat", false);
+       if (!progs)
+               Host_Error ("PR_LoadProgs: couldn't load progs.dat");
+       Con_DPrintf ("Programs occupy %iK.\n", com_filesize/1024);
+
+       pr_crc = CRC_Block((byte *)progs, com_filesize);
+
+// byte swap the header
+       for (i=0 ; i<sizeof(*progs)/4 ; i++)
+               ((int *)progs)[i] = LittleLong ( ((int *)progs)[i] );           
+
+       if (progs->version != PROG_VERSION)
+               Host_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION);
+       if (progs->crc != PROGHEADER_CRC)
+               Host_Error ("progs.dat system vars have been modified, progdefs.h is out of date");
+
+       pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions);
+       pr_strings = (char *)progs + progs->ofs_strings;
+       pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs);
+       pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs);
+       pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements);
+
+       pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals);
+       pr_globals = (float *)pr_global_struct;
+       
+       pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t);
+
+       pr_edictareasize = pr_edict_size * MAX_EDICTS;
+
+// byte swap the lumps
+       for (i=0 ; i<progs->numstatements ; i++)
+       {
+               pr_statements[i].op = LittleShort(pr_statements[i].op);
+               pr_statements[i].a = LittleShort(pr_statements[i].a);
+               pr_statements[i].b = LittleShort(pr_statements[i].b);
+               pr_statements[i].c = LittleShort(pr_statements[i].c);
+       }
+
+       for (i=0 ; i<progs->numfunctions; i++)
+       {
+               pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement);
+               pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start);
+               pr_functions[i].s_name = LittleLong (pr_functions[i].s_name);
+               pr_functions[i].s_file = LittleLong (pr_functions[i].s_file);
+               pr_functions[i].numparms = LittleLong (pr_functions[i].numparms);
+               pr_functions[i].locals = LittleLong (pr_functions[i].locals);
+       }       
+
+       for (i=0 ; i<progs->numglobaldefs ; i++)
+       {
+               pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type);
+               pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs);
+               pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name);
+       }
+
+       for (i=0 ; i<progs->numfielddefs ; i++)
+       {
+               pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type);
+               if (pr_fielddefs[i].type & DEF_SAVEGLOBAL)
+                       Host_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL");
+               pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs);
+               pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name);
+       }
+
+       for (i=0 ; i<progs->numglobals ; i++)
+               ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
+
+       // LordHavoc: bounds check anything static
+       for (i = 0,st = pr_statements;i < progs->numstatements;i++,st++)
+       {
+               switch (st->op)
+               {
+               case OP_IF:
+               case OP_IFNOT:
+                       if ((unsigned short) st->a >= progs->numglobals || st->b + i < 0 || st->b + i >= progs->numstatements)
+                               Host_Error("PR_LoadProgs: out of bounds IF/IFNOT (statement %d)\n", i);
+                       break;
+               case OP_GOTO:
+                       if (st->a + i < 0 || st->a + i >= progs->numstatements)
+                               Host_Error("PR_LoadProgs: out of bounds GOTO (statement %d)\n", i);
+                       break;
+               // global global global
+               case OP_ADD_F:
+               case OP_ADD_V:
+               case OP_SUB_F:
+               case OP_SUB_V:
+               case OP_MUL_F:
+               case OP_MUL_V:
+               case OP_MUL_FV:
+               case OP_MUL_VF:
+               case OP_DIV_F:
+               case OP_BITAND:
+               case OP_BITOR:
+               case OP_GE:
+               case OP_LE:
+               case OP_GT:
+               case OP_LT:
+               case OP_AND:
+               case OP_OR:
+               case OP_EQ_F:
+               case OP_EQ_V:
+               case OP_EQ_S:
+               case OP_EQ_E:
+               case OP_EQ_FNC:
+               case OP_NE_F:
+               case OP_NE_V:
+               case OP_NE_S:
+               case OP_NE_E:
+               case OP_NE_FNC:
+               case OP_ADDRESS:
+               case OP_LOAD_F:
+               case OP_LOAD_FLD:
+               case OP_LOAD_ENT:
+               case OP_LOAD_S:
+               case OP_LOAD_FNC:
+               case OP_LOAD_V:
+                       if ((unsigned short) st->a >= progs->numglobals || (unsigned short) st->b >= progs->numglobals || (unsigned short) st->c >= progs->numglobals)
+                               Host_Error("PR_LoadProgs: out of bounds global index (statement %d)\n", i);
+                       break;
+               // global none global
+               case OP_NOT_F:
+               case OP_NOT_V:
+               case OP_NOT_S:
+               case OP_NOT_FNC:
+               case OP_NOT_ENT:
+                       if ((unsigned short) st->a >= progs->numglobals || (unsigned short) st->c >= progs->numglobals)
+                               Host_Error("PR_LoadProgs: out of bounds global index (statement %d)\n", i);
+                       break;
+               // 2 globals
+               case OP_STOREP_F:
+               case OP_STOREP_ENT:
+               case OP_STOREP_FLD:
+               case OP_STOREP_S:
+               case OP_STOREP_FNC:
+               case OP_STORE_F:
+               case OP_STORE_ENT:
+               case OP_STORE_FLD:
+               case OP_STORE_S:
+               case OP_STORE_FNC:
+               case OP_STATE:
+               case OP_STOREP_V:
+               case OP_STORE_V:
+                       if ((unsigned short) st->a >= progs->numglobals || (unsigned short) st->b >= progs->numglobals)
+                               Host_Error("PR_LoadProgs: out of bounds global index (statement %d)\n", i);
+                       break;
+               // 1 global
+               case OP_CALL0:
+               case OP_CALL1:
+               case OP_CALL2:
+               case OP_CALL3:
+               case OP_CALL4:
+               case OP_CALL5:
+               case OP_CALL6:
+               case OP_CALL7:
+               case OP_CALL8:
+               case OP_DONE:
+               case OP_RETURN:
+                       if ((unsigned short) st->a >= progs->numglobals)
+                               Host_Error("PR_LoadProgs: out of bounds global index (statement %d)\n", i);
+                       break;
+               default:
+                       Host_Error("PR_LoadProgs: unknown opcode %d at statement %d\n", st->op, i);
+                       break;
+               }
+       }
+
+       FindEdictFieldOffsets(); // LordHavoc: update field offset list
+}
+
+
+/*
+===============
+PR_Init
+===============
+*/
+void PR_Init (void)
+{
+       Cmd_AddCommand ("edict", ED_PrintEdict_f);
+       Cmd_AddCommand ("edicts", ED_PrintEdicts);
+       Cmd_AddCommand ("edictcount", ED_Count);
+       Cmd_AddCommand ("profile", PR_Profile_f);
+       Cvar_RegisterVariable (&nomonsters);
+       Cvar_RegisterVariable (&gamecfg);
+       Cvar_RegisterVariable (&scratch1);
+       Cvar_RegisterVariable (&scratch2);
+       Cvar_RegisterVariable (&scratch3);
+       Cvar_RegisterVariable (&scratch4);
+       Cvar_RegisterVariable (&savedgamecfg);
+       Cvar_RegisterVariable (&saved1);
+       Cvar_RegisterVariable (&saved2);
+       Cvar_RegisterVariable (&saved3);
+       Cvar_RegisterVariable (&saved4);
+       // LordHavoc: for DarkPlaces, this overrides the number of decors (shell casings, gibs, etc)
+       Cvar_RegisterVariable (&decors);
+       // LordHavoc: Nehahra uses these to pass data around cutscene demos
+       if (nehahra)
+       {
+               Cvar_RegisterVariable (&nehx00);Cvar_RegisterVariable (&nehx01);
+               Cvar_RegisterVariable (&nehx02);Cvar_RegisterVariable (&nehx03);
+               Cvar_RegisterVariable (&nehx04);Cvar_RegisterVariable (&nehx05);
+               Cvar_RegisterVariable (&nehx06);Cvar_RegisterVariable (&nehx07);
+               Cvar_RegisterVariable (&nehx08);Cvar_RegisterVariable (&nehx09);
+               Cvar_RegisterVariable (&nehx10);Cvar_RegisterVariable (&nehx11);
+               Cvar_RegisterVariable (&nehx12);Cvar_RegisterVariable (&nehx13);
+               Cvar_RegisterVariable (&nehx14);Cvar_RegisterVariable (&nehx15);
+               Cvar_RegisterVariable (&nehx16);Cvar_RegisterVariable (&nehx17);
+               Cvar_RegisterVariable (&nehx18);Cvar_RegisterVariable (&nehx19);
+       }
+       Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
+       // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
+       Cvar_RegisterVariable (&pr_boundscheck);
+}
+
+// LordHavoc: turned EDICT_NUM into a #define for speed reasons
+edict_t *EDICT_NUM_ERROR(int n)
+{
+       Sys_Error ("EDICT_NUM: bad number %i", n);
+       return NULL;
+}
+/*
+edict_t *EDICT_NUM(int n)
+{
+       if (n < 0 || n >= sv.max_edicts)
+               Sys_Error ("EDICT_NUM: bad number %i", n);
+       return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size);
+}
+*/
+
+int NUM_FOR_EDICT(edict_t *e)
+{
+       int             b;
+       
+       b = (byte *)e - (byte *)sv.edicts;
+       b = b / pr_edict_size;
+       
+       if (b < 0 || b >= sv.num_edicts)
+               Sys_Error ("NUM_FOR_EDICT: bad pointer");
+       return b;
+}
diff --git a/pr_exec.c b/pr_exec.c
new file mode 100644 (file)
index 0000000..4a319e6
--- /dev/null
+++ b/pr_exec.c
@@ -0,0 +1,1314 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+
+/*
+
+*/
+
+typedef struct
+{
+       int                             s;
+       dfunction_t             *f;
+} prstack_t;
+
+#define        MAX_STACK_DEPTH         32
+prstack_t      pr_stack[MAX_STACK_DEPTH];
+int                    pr_depth;
+
+#define        LOCALSTACK_SIZE         2048
+int                    localstack[LOCALSTACK_SIZE];
+int                    localstack_used;
+
+
+qboolean       pr_trace;
+dfunction_t    *pr_xfunction;
+int                    pr_xstatement;
+
+
+int            pr_argc;
+
+char *pr_opnames[] =
+{
+"DONE",
+
+"MUL_F",
+"MUL_V", 
+"MUL_FV",
+"MUL_VF",
+"DIV",
+
+"ADD_F",
+"ADD_V", 
+  
+"SUB_F",
+"SUB_V",
+
+"EQ_F",
+"EQ_V",
+"EQ_S", 
+"EQ_E",
+"EQ_FNC",
+"NE_F",
+"NE_V", 
+"NE_S",
+"NE_E", 
+"NE_FNC",
+"LE",
+"GE",
+"LT",
+"GT", 
+
+"INDIRECT",
+"INDIRECT",
+"INDIRECT", 
+"INDIRECT", 
+"INDIRECT",
+"INDIRECT", 
+
+"ADDRESS", 
+
+"STORE_F",
+"STORE_V",
+"STORE_S",
+"STORE_ENT",
+"STORE_FLD",
+"STORE_FNC",
+
+"STOREP_F",
+"STOREP_V",
+"STOREP_S",
+"STOREP_ENT",
+"STOREP_FLD",
+"STOREP_FNC",
+
+"RETURN",
+  
+"NOT_F",
+"NOT_V",
+"NOT_S", 
+"NOT_ENT", 
+"NOT_FNC", 
+  
+"IF",
+"IFNOT",
+  
+"CALL0",
+"CALL1",
+"CALL2",
+"CALL3",
+"CALL4",
+"CALL5",
+"CALL6",
+"CALL7",
+"CALL8",
+  
+"STATE",
+  
+"GOTO", 
+  
+"AND",
+"OR", 
+
+"BITAND",
+"BITOR"
+};
+
+char *PR_GlobalString (int ofs);
+char *PR_GlobalStringNoContents (int ofs);
+
+
+//=============================================================================
+
+/*
+=================
+PR_PrintStatement
+=================
+*/
+void PR_PrintStatement (dstatement_t *s)
+{
+       int             i;
+       
+       if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0]))
+       {
+               Con_Printf ("%s ",  pr_opnames[s->op]);
+               i = strlen(pr_opnames[s->op]);
+               for ( ; i<10 ; i++)
+                       Con_Printf (" ");
+       }
+               
+       if (s->op == OP_IF || s->op == OP_IFNOT)
+               Con_Printf ("%sbranch %i",PR_GlobalString((unsigned short) s->a),s->b);
+       else if (s->op == OP_GOTO)
+       {
+               Con_Printf ("branch %i",s->a);
+       }
+       else if ( (unsigned)(s->op - OP_STORE_F) < 6)
+       {
+               Con_Printf ("%s",PR_GlobalString((unsigned short) s->a));
+               Con_Printf ("%s", PR_GlobalStringNoContents((unsigned short) s->b));
+       }
+       else
+       {
+               if (s->a)
+                       Con_Printf ("%s",PR_GlobalString((unsigned short) s->a));
+               if (s->b)
+                       Con_Printf ("%s",PR_GlobalString((unsigned short) s->b));
+               if (s->c)
+                       Con_Printf ("%s", PR_GlobalStringNoContents((unsigned short) s->c));
+       }
+       Con_Printf ("\n");
+}
+
+/*
+============
+PR_StackTrace
+============
+*/
+void PR_StackTrace (void)
+{
+       dfunction_t     *f;
+       int                     i;
+       
+       if (pr_depth == 0)
+       {
+               Con_Printf ("<NO STACK>\n");
+               return;
+       }
+       
+       pr_stack[pr_depth].f = pr_xfunction;
+       for (i=pr_depth ; i>=0 ; i--)
+       {
+               f = pr_stack[i].f;
+               
+               if (!f)
+               {
+                       Con_Printf ("<NO FUNCTION>\n");
+               }
+               else
+                       Con_Printf ("%12s : %s\n", pr_strings + f->s_file, pr_strings + f->s_name);             
+       }
+}
+
+
+/*
+============
+PR_Profile_f
+
+============
+*/
+void PR_Profile_f (void)
+{
+       dfunction_t     *f, *best;
+       int                     max;
+       int                     num;
+       int                     i;
+       
+       num = 0;        
+       do
+       {
+               max = 0;
+               best = NULL;
+               for (i=0 ; i<progs->numfunctions ; i++)
+               {
+                       f = &pr_functions[i];
+                       if (f->profile > max)
+                       {
+                               max = f->profile;
+                               best = f;
+                       }
+               }
+               if (best)
+               {
+                       if (num < 10)
+                               Con_Printf ("%7i %s\n", best->profile, pr_strings+best->s_name);
+                       num++;
+                       best->profile = 0;
+               }
+       } while (best);
+}
+
+
+/*
+============
+PR_RunError
+
+Aborts the currently executing function
+============
+*/
+void PR_RunError (char *error, ...)
+{
+       va_list         argptr;
+       char            string[1024];
+
+       va_start (argptr,error);
+       vsprintf (string,error,argptr);
+       va_end (argptr);
+
+       PR_PrintStatement (pr_statements + pr_xstatement);
+       PR_StackTrace ();
+       Con_Printf ("%s\n", string);
+       
+       pr_depth = 0;           // dump the stack so host_error can shutdown functions
+
+       Host_Error ("Program error");
+}
+
+/*
+============================================================================
+PR_ExecuteProgram
+
+The interpretation main loop
+============================================================================
+*/
+
+/*
+====================
+PR_EnterFunction
+
+Returns the new program statement counter
+====================
+*/
+int PR_EnterFunction (dfunction_t *f)
+{
+       int             i, j, c, o;
+
+       pr_stack[pr_depth].s = pr_xstatement;
+       pr_stack[pr_depth].f = pr_xfunction;    
+       pr_depth++;
+       if (pr_depth >= MAX_STACK_DEPTH)
+               PR_RunError ("stack overflow");
+
+// save off any locals that the new function steps on
+       c = f->locals;
+       if (localstack_used + c > LOCALSTACK_SIZE)
+               PR_RunError ("PR_ExecuteProgram: locals stack overflow\n");
+
+       for (i=0 ; i < c ; i++)
+               localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];
+       localstack_used += c;
+
+// copy parameters
+       o = f->parm_start;
+       for (i=0 ; i<f->numparms ; i++)
+       {
+               for (j=0 ; j<f->parm_size[i] ; j++)
+               {
+                       ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];
+                       o++;
+               }
+       }
+
+       pr_xfunction = f;
+       return f->first_statement - 1;  // offset the s++
+}
+
+/*
+====================
+PR_LeaveFunction
+====================
+*/
+int PR_LeaveFunction (void)
+{
+       int             i, c;
+
+       if (pr_depth <= 0)
+               Sys_Error ("prog stack underflow");
+
+// restore locals from the stack
+       c = pr_xfunction->locals;
+       localstack_used -= c;
+       if (localstack_used < 0)
+               PR_RunError ("PR_ExecuteProgram: locals stack underflow\n");
+
+       for (i=0 ; i < c ; i++)
+               ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i];
+
+// up stack
+       pr_depth--;
+       pr_xfunction = pr_stack[pr_depth].f;
+       return pr_stack[pr_depth].s;
+}
+
+
+/*
+====================
+PR_ExecuteProgram
+====================
+*/
+/*
+void PR_ExecuteProgram (func_t fnum)
+{
+       eval_t  *a, *b, *c;
+       int                     s;
+       dstatement_t    *st;
+       dfunction_t     *f, *newf;
+       int             runaway;
+       int             i;
+       edict_t *ed;
+       int             exitdepth;
+       eval_t  *ptr;
+
+       if (!fnum || fnum >= progs->numfunctions)
+       {
+               if (pr_global_struct->self)
+                       ED_Print (PROG_TO_EDICT(pr_global_struct->self));
+               Host_Error ("PR_ExecuteProgram: NULL function");
+       }
+       
+       f = &pr_functions[fnum];
+
+       runaway = 100000;
+       pr_trace = false;
+
+// make a stack frame
+       exitdepth = pr_depth;
+
+       s = PR_EnterFunction (f);
+       
+while (1)
+{
+       s++;    // next statement
+
+       st = &pr_statements[s];
+       // LordHavoc: fix for 32768 QC def limit (just added unsigned short typecast)
+       a = (eval_t *)&pr_globals[(unsigned short) st->a];
+       b = (eval_t *)&pr_globals[(unsigned short) st->b];
+       c = (eval_t *)&pr_globals[(unsigned short) st->c];
+       
+       if (!--runaway)
+               PR_RunError ("runaway loop error");
+               
+       pr_xfunction->profile++;
+       pr_xstatement = s;
+       
+       if (pr_trace)
+               PR_PrintStatement (st);
+               
+       switch (st->op)
+       {
+       case OP_ADD_F:
+               c->_float = a->_float + b->_float;
+               break;
+       case OP_ADD_V:
+               c->vector[0] = a->vector[0] + b->vector[0];
+               c->vector[1] = a->vector[1] + b->vector[1];
+               c->vector[2] = a->vector[2] + b->vector[2];
+               break;
+               
+       case OP_SUB_F:
+               c->_float = a->_float - b->_float;
+               break;
+       case OP_SUB_V:
+               c->vector[0] = a->vector[0] - b->vector[0];
+               c->vector[1] = a->vector[1] - b->vector[1];
+               c->vector[2] = a->vector[2] - b->vector[2];
+               break;
+
+       case OP_MUL_F:
+               c->_float = a->_float * b->_float;
+               break;
+       case OP_MUL_V:
+               c->_float = a->vector[0]*b->vector[0]
+                               + a->vector[1]*b->vector[1]
+                               + a->vector[2]*b->vector[2];
+               break;
+       case OP_MUL_FV:
+               c->vector[0] = a->_float * b->vector[0];
+               c->vector[1] = a->_float * b->vector[1];
+               c->vector[2] = a->_float * b->vector[2];
+               break;
+       case OP_MUL_VF:
+               c->vector[0] = b->_float * a->vector[0];
+               c->vector[1] = b->_float * a->vector[1];
+               c->vector[2] = b->_float * a->vector[2];
+               break;
+
+       case OP_DIV_F:
+               c->_float = a->_float / b->_float;
+               break;
+       
+       case OP_BITAND:
+               c->_float = (int)a->_float & (int)b->_float;
+               break;
+       
+       case OP_BITOR:
+               c->_float = (int)a->_float | (int)b->_float;
+               break;
+       
+               
+       case OP_GE:
+               c->_float = a->_float >= b->_float;
+               break;
+       case OP_LE:
+               c->_float = a->_float <= b->_float;
+               break;
+       case OP_GT:
+               c->_float = a->_float > b->_float;
+               break;
+       case OP_LT:
+               c->_float = a->_float < b->_float;
+               break;
+       case OP_AND:
+               c->_float = a->_float && b->_float;
+               break;
+       case OP_OR:
+               c->_float = a->_float || b->_float;
+               break;
+               
+       case OP_NOT_F:
+               c->_float = !a->_float;
+               break;
+       case OP_NOT_V:
+               c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2];
+               break;
+       case OP_NOT_S:
+               c->_float = !a->string || !pr_strings[a->string];
+               break;
+       case OP_NOT_FNC:
+               c->_float = !a->function;
+               break;
+       case OP_NOT_ENT:
+               c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts);
+               break;
+
+       case OP_EQ_F:
+               c->_float = a->_float == b->_float;
+               break;
+       case OP_EQ_V:
+               c->_float = (a->vector[0] == b->vector[0]) &&
+                                       (a->vector[1] == b->vector[1]) &&
+                                       (a->vector[2] == b->vector[2]);
+               break;
+       case OP_EQ_S:
+               c->_float = !strcmp(pr_strings+a->string,pr_strings+b->string);
+               break;
+       case OP_EQ_E:
+               c->_float = a->_int == b->_int;
+               break;
+       case OP_EQ_FNC:
+               c->_float = a->function == b->function;
+               break;
+
+
+       case OP_NE_F:
+               c->_float = a->_float != b->_float;
+               break;
+       case OP_NE_V:
+               c->_float = (a->vector[0] != b->vector[0]) ||
+                                       (a->vector[1] != b->vector[1]) ||
+                                       (a->vector[2] != b->vector[2]);
+               break;
+       case OP_NE_S:
+               c->_float = strcmp(pr_strings+a->string,pr_strings+b->string);
+               break;
+       case OP_NE_E:
+               c->_float = a->_int != b->_int;
+               break;
+       case OP_NE_FNC:
+               c->_float = a->function != b->function;
+               break;
+
+//==================
+       case OP_STORE_F:
+       case OP_STORE_ENT:
+       case OP_STORE_FLD:              // integers
+       case OP_STORE_S:
+       case OP_STORE_FNC:              // pointers
+               b->_int = a->_int;
+               break;
+       case OP_STORE_V:
+               b->vector[0] = a->vector[0];
+               b->vector[1] = a->vector[1];
+               b->vector[2] = a->vector[2];
+               break;
+               
+       case OP_STOREP_F:
+       case OP_STOREP_ENT:
+       case OP_STOREP_FLD:             // integers
+       case OP_STOREP_S:
+       case OP_STOREP_FNC:             // pointers
+               ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+               ptr->_int = a->_int;
+               break;
+       case OP_STOREP_V:
+               ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+               ptr->vector[0] = a->vector[0];
+               ptr->vector[1] = a->vector[1];
+               ptr->vector[2] = a->vector[2];
+               break;
+               
+       case OP_ADDRESS:
+               ed = PROG_TO_EDICT(a->edict);
+#ifdef PARANOID
+               NUM_FOR_EDICT(ed);              // make sure it's in range
+#endif
+               if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
+                       PR_RunError ("assignment to world entity");
+               c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts;
+               break;
+               
+       case OP_LOAD_F:
+       case OP_LOAD_FLD:
+       case OP_LOAD_ENT:
+       case OP_LOAD_S:
+       case OP_LOAD_FNC:
+               ed = PROG_TO_EDICT(a->edict);
+#ifdef PARANOID
+               NUM_FOR_EDICT(ed);              // make sure it's in range
+#endif
+               a = (eval_t *)((int *)&ed->v + b->_int);
+               c->_int = a->_int;
+               break;
+
+       case OP_LOAD_V:
+               ed = PROG_TO_EDICT(a->edict);
+#ifdef PARANOID
+               NUM_FOR_EDICT(ed);              // make sure it's in range
+#endif
+               a = (eval_t *)((int *)&ed->v + b->_int);
+               c->vector[0] = a->vector[0];
+               c->vector[1] = a->vector[1];
+               c->vector[2] = a->vector[2];
+               break;
+               
+//==================
+
+       case OP_IFNOT:
+               if (!a->_int)
+                       s += st->b - 1; // offset the s++
+               break;
+               
+       case OP_IF:
+               if (a->_int)
+                       s += st->b - 1; // offset the s++
+               break;
+               
+       case OP_GOTO:
+               s += st->a - 1; // offset the s++
+               break;
+               
+       case OP_CALL0:
+       case OP_CALL1:
+       case OP_CALL2:
+       case OP_CALL3:
+       case OP_CALL4:
+       case OP_CALL5:
+       case OP_CALL6:
+       case OP_CALL7:
+       case OP_CALL8:
+               pr_argc = st->op - OP_CALL0;
+               if (!a->function)
+                       PR_RunError ("NULL function");
+
+               newf = &pr_functions[a->function];
+
+               if (newf->first_statement < 0)
+               {       // negative statements are built in functions
+                       i = -newf->first_statement;
+                       if (i >= pr_numbuiltins)
+                               PR_RunError ("Bad builtin call number");
+                       pr_builtins[i] ();
+                       break;
+               }
+
+               s = PR_EnterFunction (newf);
+               break;
+
+       case OP_DONE:
+       case OP_RETURN:
+               pr_globals[OFS_RETURN] = pr_globals[st->a];
+               pr_globals[OFS_RETURN+1] = pr_globals[st->a+1];
+               pr_globals[OFS_RETURN+2] = pr_globals[st->a+2];
+       
+               s = PR_LeaveFunction ();
+               if (pr_depth == exitdepth)
+                       return;         // all done
+               break;
+               
+       case OP_STATE:
+               ed = PROG_TO_EDICT(pr_global_struct->self);
+#ifdef FPS_20
+               ed->v.nextthink = pr_global_struct->time + 0.05;
+#else
+               ed->v.nextthink = pr_global_struct->time + 0.1;
+#endif
+               //if (a->_float != ed->v.frame) // LordHavoc: this was silly
+               //{
+                       ed->v.frame = a->_float;
+               //}
+               ed->v.think = b->function;
+               break;
+               
+       default:
+               PR_RunError ("Bad opcode %i", st->op);
+       }
+}
+
+}
+*/
+
+// LordHavoc: optimized
+#define OPA ((eval_t *)&pr_globals[(unsigned short) st->a])
+#define OPB ((eval_t *)&pr_globals[(unsigned short) st->b])
+#define OPC ((eval_t *)&pr_globals[(unsigned short) st->c])
+extern cvar_t pr_boundscheck;
+void PR_ExecuteProgram (func_t fnum)
+{
+       dstatement_t    *st;
+       dfunction_t     *f, *newf;
+       edict_t *ed;
+       int             exitdepth;
+       eval_t  *ptr;
+       int             profile, startprofile;
+
+       if (!fnum || fnum >= progs->numfunctions)
+       {
+               if (pr_global_struct->self)
+                       ED_Print (PROG_TO_EDICT(pr_global_struct->self));
+               Host_Error ("PR_ExecuteProgram: NULL function");
+       }
+       
+       f = &pr_functions[fnum];
+
+       pr_trace = false;
+
+// make a stack frame
+       exitdepth = pr_depth;
+
+       st = &pr_statements[PR_EnterFunction (f)];
+       startprofile = profile = 0;
+
+       if (pr_boundscheck.value)
+       {
+               while (1)
+               {
+                       st++;
+                       if (++profile > 100000)
+                       {
+                               pr_xstatement = st - pr_statements;
+                               PR_RunError ("runaway loop error");
+                       }
+                       
+                       if (pr_trace)
+                               PR_PrintStatement (st);
+
+                       switch (st->op)
+                       {
+                       case OP_ADD_F:
+                               OPC->_float = OPA->_float + OPB->_float;
+                               break;
+                       case OP_ADD_V:
+                               OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
+                               OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
+                               OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
+                               break;
+                       case OP_SUB_F:
+                               OPC->_float = OPA->_float - OPB->_float;
+                               break;
+                       case OP_SUB_V:
+                               OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
+                               OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
+                               OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
+                               break;
+                       case OP_MUL_F:
+                               OPC->_float = OPA->_float * OPB->_float;
+                               break;
+                       case OP_MUL_V:
+                               OPC->_float = OPA->vector[0]*OPB->vector[0] + OPA->vector[1]*OPB->vector[1] + OPA->vector[2]*OPB->vector[2];
+                               break;
+                       case OP_MUL_FV:
+                               OPC->vector[0] = OPA->_float * OPB->vector[0];
+                               OPC->vector[1] = OPA->_float * OPB->vector[1];
+                               OPC->vector[2] = OPA->_float * OPB->vector[2];
+                               break;
+                       case OP_MUL_VF:
+                               OPC->vector[0] = OPB->_float * OPA->vector[0];
+                               OPC->vector[1] = OPB->_float * OPA->vector[1];
+                               OPC->vector[2] = OPB->_float * OPA->vector[2];
+                               break;
+                       case OP_DIV_F:
+                               OPC->_float = OPA->_float / OPB->_float;
+                               break;
+                       case OP_BITAND:
+                               OPC->_float = (int)OPA->_float & (int)OPB->_float;
+                               break;
+                       case OP_BITOR:
+                               OPC->_float = (int)OPA->_float | (int)OPB->_float;
+                               break;
+                       case OP_GE:
+                               OPC->_float = OPA->_float >= OPB->_float;
+                               break;
+                       case OP_LE:
+                               OPC->_float = OPA->_float <= OPB->_float;
+                               break;
+                       case OP_GT:
+                               OPC->_float = OPA->_float > OPB->_float;
+                               break;
+                       case OP_LT:
+                               OPC->_float = OPA->_float < OPB->_float;
+                               break;
+                       case OP_AND:
+                               OPC->_float = OPA->_float && OPB->_float;
+                               break;
+                       case OP_OR:
+                               OPC->_float = OPA->_float || OPB->_float;
+                               break;
+                       case OP_NOT_F:
+                               OPC->_float = !OPA->_float;
+                               break;
+                       case OP_NOT_V:
+                               OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2];
+                               break;
+                       case OP_NOT_S:
+                               OPC->_float = !OPA->string || !pr_strings[OPA->string];
+                               break;
+                       case OP_NOT_FNC:
+                               OPC->_float = !OPA->function;
+                               break;
+                       case OP_NOT_ENT:
+                               OPC->_float = (PROG_TO_EDICT(OPA->edict) == sv.edicts);
+                               break;
+                       case OP_EQ_F:
+                               OPC->_float = OPA->_float == OPB->_float;
+                               break;
+                       case OP_EQ_V:
+                               OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]);
+                               break;
+                       case OP_EQ_S:
+                               OPC->_float = !strcmp(pr_strings+OPA->string,pr_strings+OPB->string);
+                               break;
+                       case OP_EQ_E:
+                               OPC->_float = OPA->_int == OPB->_int;
+                               break;
+                       case OP_EQ_FNC:
+                               OPC->_float = OPA->function == OPB->function;
+                               break;
+                       case OP_NE_F:
+                               OPC->_float = OPA->_float != OPB->_float;
+                               break;
+                       case OP_NE_V:
+                               OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]);
+                               break;
+                       case OP_NE_S:
+                               OPC->_float = strcmp(pr_strings+OPA->string,pr_strings+OPB->string);
+                               break;
+                       case OP_NE_E:
+                               OPC->_float = OPA->_int != OPB->_int;
+                               break;
+                       case OP_NE_FNC:
+                               OPC->_float = OPA->function != OPB->function;
+                               break;
+
+               //==================
+                       case OP_STORE_F:
+                       case OP_STORE_ENT:
+                       case OP_STORE_FLD:              // integers
+                       case OP_STORE_S:
+                       case OP_STORE_FNC:              // pointers
+                               OPB->_int = OPA->_int;
+                               break;
+                       case OP_STORE_V:
+                               OPB->vector[0] = OPA->vector[0];
+                               OPB->vector[1] = OPA->vector[1];
+                               OPB->vector[2] = OPA->vector[2];
+                               break;
+                               
+                       case OP_STOREP_F:
+                       case OP_STOREP_ENT:
+                       case OP_STOREP_FLD:             // integers
+                       case OP_STOREP_S:
+                       case OP_STOREP_FNC:             // pointers
+                               if (OPB->_int < 0 || OPB->_int + 4 > pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to write to an out of bounds edict\n");
+                                       return;
+                               }
+                               if (OPB->_int % pr_edict_size < ((byte *)&sv.edicts->v - (byte *)sv.edicts))
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to write to an engine edict field\n");
+                                       return;
+                               }
+                               ptr = (eval_t *)((byte *)sv.edicts + OPB->_int);
+                               ptr->_int = OPA->_int;
+                               break;
+                       case OP_STOREP_V:
+                               if (OPB->_int < 0 || OPB->_int + 12 > pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to write to an out of bounds edict\n");
+                                       return;
+                               }
+                               ptr = (eval_t *)((byte *)sv.edicts + OPB->_int);
+                               ptr->vector[0] = OPA->vector[0];
+                               ptr->vector[1] = OPA->vector[1];
+                               ptr->vector[2] = OPA->vector[2];
+                               break;
+                               
+                       case OP_ADDRESS:
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to address an out of bounds edict\n");
+                                       return;
+                               }
+                               if (OPA->edict == 0 && sv.state == ss_active)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError ("assignment to world entity");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int >= progs->entityfields)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to address an invalid field in an edict\n");
+                                       return;
+                               }
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = (byte *)((int *)&ed->v + OPB->_int) - (byte *)sv.edicts;
+                               break;
+                               
+                       case OP_LOAD_F:
+                       case OP_LOAD_FLD:
+                       case OP_LOAD_ENT:
+                       case OP_LOAD_S:
+                       case OP_LOAD_FNC:
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an out of bounds edict number\n");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int >= progs->entityfields)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an invalid field in an edict\n");
+                                       return;
+                               }
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = ((eval_t *)((int *)&ed->v + OPB->_int))->_int;
+                               break;
+
+                       case OP_LOAD_V:
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an out of bounds edict number\n");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int + 2 >= progs->entityfields)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an invalid field in an edict\n");
+                                       return;
+                               }
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->vector[0] = ((eval_t *)((int *)&ed->v + OPB->_int))->vector[0];
+                               OPC->vector[1] = ((eval_t *)((int *)&ed->v + OPB->_int))->vector[1];
+                               OPC->vector[2] = ((eval_t *)((int *)&ed->v + OPB->_int))->vector[2];
+                               break;
+                               
+               //==================
+
+                       case OP_IFNOT:
+                               if (!OPA->_int)
+                                       st += st->b - 1;        // offset the s++
+                               break;
+                               
+                       case OP_IF:
+                               if (OPA->_int)
+                                       st += st->b - 1;        // offset the s++
+                               break;
+                               
+                       case OP_GOTO:
+                               st += st->a - 1;        // offset the s++
+                               break;
+                               
+                       case OP_CALL0:
+                       case OP_CALL1:
+                       case OP_CALL2:
+                       case OP_CALL3:
+                       case OP_CALL4:
+                       case OP_CALL5:
+                       case OP_CALL6:
+                       case OP_CALL7:
+                       case OP_CALL8:
+                               pr_xfunction->profile += profile - startprofile;
+                               startprofile = profile;
+                               pr_xstatement = st - pr_statements;
+                               pr_argc = st->op - OP_CALL0;
+                               if (!OPA->function)
+                                       PR_RunError ("NULL function");
+
+                               newf = &pr_functions[OPA->function];
+
+                               if (newf->first_statement < 0)
+                               {       // negative statements are built in functions
+                                       int i = -newf->first_statement;
+                                       if (i >= pr_numbuiltins)
+                                               PR_RunError ("Bad builtin call number");
+                                       pr_builtins[i] ();
+                                       break;
+                               }
+
+                               st = &pr_statements[PR_EnterFunction (newf)];
+                               break;
+
+                       case OP_DONE:
+                       case OP_RETURN:
+                               pr_globals[OFS_RETURN] = pr_globals[(unsigned short) st->a];
+                               pr_globals[OFS_RETURN+1] = pr_globals[(unsigned short) st->a+1];
+                               pr_globals[OFS_RETURN+2] = pr_globals[(unsigned short) st->a+2];
+                       
+                               st = &pr_statements[PR_LeaveFunction ()];
+                               if (pr_depth == exitdepth)
+                                       return;         // all done
+                               break;
+                               
+                       case OP_STATE:
+                               ed = PROG_TO_EDICT(pr_global_struct->self);
+               #ifdef FPS_20
+                               ed->v.nextthink = pr_global_struct->time + 0.05;
+               #else
+                               ed->v.nextthink = pr_global_struct->time + 0.1;
+               #endif
+                               ed->v.frame = OPA->_float;
+                               ed->v.think = OPB->function;
+                               break;
+                               
+                       default:
+                               pr_xstatement = st - pr_statements;
+                               PR_RunError ("Bad opcode %i", st->op);
+                       }
+               }
+       }
+       else
+       {
+               while (1)
+               {
+                       st++;
+                       if (++profile > 100000)
+                       {
+                               pr_xstatement = st - pr_statements;
+                               PR_RunError ("runaway loop error");
+                       }
+                       
+                       if (pr_trace)
+                               PR_PrintStatement (st);
+
+                       switch (st->op)
+                       {
+                       case OP_ADD_F:
+                               OPC->_float = OPA->_float + OPB->_float;
+                               break;
+                       case OP_ADD_V:
+                               OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
+                               OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
+                               OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
+                               break;
+                       case OP_SUB_F:
+                               OPC->_float = OPA->_float - OPB->_float;
+                               break;
+                       case OP_SUB_V:
+                               OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
+                               OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
+                               OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
+                               break;
+                       case OP_MUL_F:
+                               OPC->_float = OPA->_float * OPB->_float;
+                               break;
+                       case OP_MUL_V:
+                               OPC->_float = OPA->vector[0]*OPB->vector[0] + OPA->vector[1]*OPB->vector[1] + OPA->vector[2]*OPB->vector[2];
+                               break;
+                       case OP_MUL_FV:
+                               OPC->vector[0] = OPA->_float * OPB->vector[0];
+                               OPC->vector[1] = OPA->_float * OPB->vector[1];
+                               OPC->vector[2] = OPA->_float * OPB->vector[2];
+                               break;
+                       case OP_MUL_VF:
+                               OPC->vector[0] = OPB->_float * OPA->vector[0];
+                               OPC->vector[1] = OPB->_float * OPA->vector[1];
+                               OPC->vector[2] = OPB->_float * OPA->vector[2];
+                               break;
+                       case OP_DIV_F:
+                               OPC->_float = OPA->_float / OPB->_float;
+                               break;
+                       case OP_BITAND:
+                               OPC->_float = (int)OPA->_float & (int)OPB->_float;
+                               break;
+                       case OP_BITOR:
+                               OPC->_float = (int)OPA->_float | (int)OPB->_float;
+                               break;
+                       case OP_GE:
+                               OPC->_float = OPA->_float >= OPB->_float;
+                               break;
+                       case OP_LE:
+                               OPC->_float = OPA->_float <= OPB->_float;
+                               break;
+                       case OP_GT:
+                               OPC->_float = OPA->_float > OPB->_float;
+                               break;
+                       case OP_LT:
+                               OPC->_float = OPA->_float < OPB->_float;
+                               break;
+                       case OP_AND:
+                               OPC->_float = OPA->_float && OPB->_float;
+                               break;
+                       case OP_OR:
+                               OPC->_float = OPA->_float || OPB->_float;
+                               break;
+                       case OP_NOT_F:
+                               OPC->_float = !OPA->_float;
+                               break;
+                       case OP_NOT_V:
+                               OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2];
+                               break;
+                       case OP_NOT_S:
+                               OPC->_float = !OPA->string || !pr_strings[OPA->string];
+                               break;
+                       case OP_NOT_FNC:
+                               OPC->_float = !OPA->function;
+                               break;
+                       case OP_NOT_ENT:
+                               OPC->_float = (PROG_TO_EDICT(OPA->edict) == sv.edicts);
+                               break;
+                       case OP_EQ_F:
+                               OPC->_float = OPA->_float == OPB->_float;
+                               break;
+                       case OP_EQ_V:
+                               OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]);
+                               break;
+                       case OP_EQ_S:
+                               OPC->_float = !strcmp(pr_strings+OPA->string,pr_strings+OPB->string);
+                               break;
+                       case OP_EQ_E:
+                               OPC->_float = OPA->_int == OPB->_int;
+                               break;
+                       case OP_EQ_FNC:
+                               OPC->_float = OPA->function == OPB->function;
+                               break;
+                       case OP_NE_F:
+                               OPC->_float = OPA->_float != OPB->_float;
+                               break;
+                       case OP_NE_V:
+                               OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]);
+                               break;
+                       case OP_NE_S:
+                               OPC->_float = strcmp(pr_strings+OPA->string,pr_strings+OPB->string);
+                               break;
+                       case OP_NE_E:
+                               OPC->_float = OPA->_int != OPB->_int;
+                               break;
+                       case OP_NE_FNC:
+                               OPC->_float = OPA->function != OPB->function;
+                               break;
+
+               //==================
+                       case OP_STORE_F:
+                       case OP_STORE_ENT:
+                       case OP_STORE_FLD:              // integers
+                       case OP_STORE_S:
+                       case OP_STORE_FNC:              // pointers
+                               OPB->_int = OPA->_int;
+                               break;
+                       case OP_STORE_V:
+                               OPB->vector[0] = OPA->vector[0];
+                               OPB->vector[1] = OPA->vector[1];
+                               OPB->vector[2] = OPA->vector[2];
+                               break;
+                               
+                       case OP_STOREP_F:
+                       case OP_STOREP_ENT:
+                       case OP_STOREP_FLD:             // integers
+                       case OP_STOREP_S:
+                       case OP_STOREP_FNC:             // pointers
+                               if (OPB->_int < 0 || OPB->_int + 4 > pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to write to an out of bounds edict\n");
+                                       return;
+                               }
+                               if (OPB->_int % pr_edict_size < ((byte *)&sv.edicts->v - (byte *)sv.edicts))
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to write to an engine edict field\n");
+                                       return;
+                               }
+                               ptr = (eval_t *)((byte *)sv.edicts + OPB->_int);
+                               ptr->_int = OPA->_int;
+                               break;
+                       case OP_STOREP_V:
+                               if (OPB->_int < 0 || OPB->_int + 12 > pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to write to an out of bounds edict\n");
+                                       return;
+                               }
+                               ptr = (eval_t *)((byte *)sv.edicts + OPB->_int);
+                               ptr->vector[0] = OPA->vector[0];
+                               ptr->vector[1] = OPA->vector[1];
+                               ptr->vector[2] = OPA->vector[2];
+                               break;
+                               
+                       case OP_ADDRESS:
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to address an out of bounds edict\n");
+                                       return;
+                               }
+                               if (OPA->edict == 0 && sv.state == ss_active)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError ("assignment to world entity");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int >= progs->entityfields)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to address an invalid field in an edict\n");
+                                       return;
+                               }
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = (byte *)((int *)&ed->v + OPB->_int) - (byte *)sv.edicts;
+                               break;
+                               
+                       case OP_LOAD_F:
+                       case OP_LOAD_FLD:
+                       case OP_LOAD_ENT:
+                       case OP_LOAD_S:
+                       case OP_LOAD_FNC:
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an out of bounds edict number\n");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int >= progs->entityfields)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an invalid field in an edict\n");
+                                       return;
+                               }
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->_int = ((eval_t *)((int *)&ed->v + OPB->_int))->_int;
+                               break;
+
+                       case OP_LOAD_V:
+                               if (OPA->edict < 0 || OPA->edict >= pr_edictareasize)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an out of bounds edict number\n");
+                                       return;
+                               }
+                               if (OPB->_int < 0 || OPB->_int + 2 >= progs->entityfields)
+                               {
+                                       pr_xstatement = st - pr_statements;
+                                       PR_RunError("Progs attempted to read an invalid field in an edict\n");
+                                       return;
+                               }
+                               ed = PROG_TO_EDICT(OPA->edict);
+                               OPC->vector[0] = ((eval_t *)((int *)&ed->v + OPB->_int))->vector[0];
+                               OPC->vector[1] = ((eval_t *)((int *)&ed->v + OPB->_int))->vector[1];
+                               OPC->vector[2] = ((eval_t *)((int *)&ed->v + OPB->_int))->vector[2];
+                               break;
+                               
+               //==================
+
+                       case OP_IFNOT:
+                               if (!OPA->_int)
+                                       st += st->b - 1;        // offset the s++
+                               break;
+                               
+                       case OP_IF:
+                               if (OPA->_int)
+                                       st += st->b - 1;        // offset the s++
+                               break;
+                               
+                       case OP_GOTO:
+                               st += st->a - 1;        // offset the s++
+                               break;
+                               
+                       case OP_CALL0:
+                       case OP_CALL1:
+                       case OP_CALL2:
+                       case OP_CALL3:
+                       case OP_CALL4:
+                       case OP_CALL5:
+                       case OP_CALL6:
+                       case OP_CALL7:
+                       case OP_CALL8:
+                               pr_xfunction->profile += profile - startprofile;
+                               startprofile = profile;
+                               pr_xstatement = st - pr_statements;
+                               pr_argc = st->op - OP_CALL0;
+                               if (!OPA->function)
+                                       PR_RunError ("NULL function");
+
+                               newf = &pr_functions[OPA->function];
+
+                               if (newf->first_statement < 0)
+                               {       // negative statements are built in functions
+                                       int i = -newf->first_statement;
+                                       if (i >= pr_numbuiltins)
+                                               PR_RunError ("Bad builtin call number");
+                                       pr_builtins[i] ();
+                                       break;
+                               }
+
+                               st = &pr_statements[PR_EnterFunction (newf)];
+                               break;
+
+                       case OP_DONE:
+                       case OP_RETURN:
+                               pr_globals[OFS_RETURN] = pr_globals[(unsigned short) st->a];
+                               pr_globals[OFS_RETURN+1] = pr_globals[(unsigned short) st->a+1];
+                               pr_globals[OFS_RETURN+2] = pr_globals[(unsigned short) st->a+2];
+                       
+                               st = &pr_statements[PR_LeaveFunction ()];
+                               if (pr_depth == exitdepth)
+                                       return;         // all done
+                               break;
+                               
+                       case OP_STATE:
+                               ed = PROG_TO_EDICT(pr_global_struct->self);
+               #ifdef FPS_20
+                               ed->v.nextthink = pr_global_struct->time + 0.05;
+               #else
+                               ed->v.nextthink = pr_global_struct->time + 0.1;
+               #endif
+                               ed->v.frame = OPA->_float;
+                               ed->v.think = OPB->function;
+                               break;
+                               
+                       default:
+                               pr_xstatement = st - pr_statements;
+                               PR_RunError ("Bad opcode %i", st->op);
+                       }
+               }
+       }
+}
diff --git a/progdefs.h b/progdefs.h
new file mode 100644 (file)
index 0000000..633e6d4
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+/* file generated by qcc, do not modify */
+
+typedef struct
+{
+       int     pad[28];
+       int     self;
+       int     other;
+       int     world;
+       float   time;
+       float   frametime;
+       float   force_retouch;
+       string_t        mapname;
+       float   deathmatch;
+       float   coop;
+       float   teamplay;
+       float   serverflags;
+       float   total_secrets;
+       float   total_monsters;
+       float   found_secrets;
+       float   killed_monsters;
+       float   parm1;
+       float   parm2;
+       float   parm3;
+       float   parm4;
+       float   parm5;
+       float   parm6;
+       float   parm7;
+       float   parm8;
+       float   parm9;
+       float   parm10;
+       float   parm11;
+       float   parm12;
+       float   parm13;
+       float   parm14;
+       float   parm15;
+       float   parm16;
+       vec3_t  v_forward;
+       vec3_t  v_up;
+       vec3_t  v_right;
+       float   trace_allsolid;
+       float   trace_startsolid;
+       float   trace_fraction;
+       vec3_t  trace_endpos;
+       vec3_t  trace_plane_normal;
+       float   trace_plane_dist;
+       int     trace_ent;
+       float   trace_inopen;
+       float   trace_inwater;
+       int     msg_entity;
+       func_t  main;
+       func_t  StartFrame;
+       func_t  PlayerPreThink;
+       func_t  PlayerPostThink;
+       func_t  ClientKill;
+       func_t  ClientConnect;
+       func_t  PutClientInServer;
+       func_t  ClientDisconnect;
+       func_t  SetNewParms;
+       func_t  SetChangeParms;
+} globalvars_t;
+
+typedef struct
+{
+       float   modelindex;
+       vec3_t  absmin;
+       vec3_t  absmax;
+       float   ltime;
+       float   movetype;
+       float   solid;
+       vec3_t  origin;
+       vec3_t  oldorigin;
+       vec3_t  velocity;
+       vec3_t  angles;
+       vec3_t  avelocity;
+       vec3_t  punchangle;
+       string_t        classname;
+       string_t        model;
+       float   frame;
+       float   skin;
+       float   effects;
+       vec3_t  mins;
+       vec3_t  maxs;
+       vec3_t  size;
+       func_t  touch;
+       func_t  use;
+       func_t  think;
+       func_t  blocked;
+       float   nextthink;
+       int     groundentity;
+       float   health;
+       float   frags;
+       float   weapon;
+       string_t        weaponmodel;
+       float   weaponframe;
+       float   currentammo;
+       float   ammo_shells;
+       float   ammo_nails;
+       float   ammo_rockets;
+       float   ammo_cells;
+       float   items;
+       float   takedamage;
+       int     chain;
+       float   deadflag;
+       vec3_t  view_ofs;
+       float   button0;
+       float   button1;
+       float   button2;
+       float   impulse;
+       float   fixangle;
+       vec3_t  v_angle;
+       float   idealpitch;
+       string_t        netname;
+       int     enemy;
+       float   flags;
+       float   colormap;
+       float   team;
+       float   max_health;
+       float   teleport_time;
+       float   armortype;
+       float   armorvalue;
+       float   waterlevel;
+       float   watertype;
+       float   ideal_yaw;
+       float   yaw_speed;
+       int     aiment;
+       int     goalentity;
+       float   spawnflags;
+       string_t        target;
+       string_t        targetname;
+       float   dmg_take;
+       float   dmg_save;
+       int     dmg_inflictor;
+       int     owner;
+       vec3_t  movedir;
+       string_t        message;
+       float   sounds;
+       string_t        noise;
+       string_t        noise1;
+       string_t        noise2;
+       string_t        noise3;
+} entvars_t;
+
+#define PROGHEADER_CRC 5927
diff --git a/progs.h b/progs.h
new file mode 100644 (file)
index 0000000..f17adfd
--- /dev/null
+++ b/progs.h
@@ -0,0 +1,180 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "pr_comp.h"                   // defs shared with qcc
+#include "progdefs.h"                  // generated by program cdefs
+
+typedef union eval_s
+{
+       string_t                string;
+       float                   _float;
+       float                   vector[3];
+       func_t                  function;
+       int                             _int;
+       int                             edict;
+} eval_t;      
+
+// LordHavoc: increased number of leafs per entity limit from 16 to 64
+#define        MAX_ENT_LEAFS   64
+typedef struct edict_s
+{
+       qboolean        free;
+       link_t          area;                           // linked to a division node or leaf
+       
+       int                     num_leafs;
+       short           leafnums[MAX_ENT_LEAFS];
+
+       entity_state_t  baseline;
+       entity_state_t  deltabaseline; // LordHavoc: previous frame
+       
+       float           freetime;                       // sv.time when the object was freed
+       // LordHavoc: for MOVETYPE_STEP interpolation
+       vec3_t          steporigin;
+       vec3_t          stepangles;
+       vec3_t          stepoldorigin;
+       vec3_t          stepoldangles;
+       float           steplerptime;
+       // LordHavoc: delta compression
+       float           nextfullupdate; // every second a full update is forced
+       entvars_t       v;                                      // C exported fields from progs
+// other fields from progs come immediately after
+} edict_t;
+#define        EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
+
+// LordHavoc: in an effort to eliminate time wasted on GetEdictFieldValue...  see pr_edict.c for the functions which use these.
+extern int eval_gravity;
+extern int eval_button3;
+extern int eval_button4;
+extern int eval_button5;
+extern int eval_button6;
+extern int eval_button7;
+extern int eval_button8;
+extern int eval_glow_size;
+extern int eval_glow_trail;
+extern int eval_glow_color;
+extern int eval_items2;
+extern int eval_scale;
+extern int eval_alpha;
+extern int eval_fullbright;
+extern int eval_ammo_shells1;
+extern int eval_ammo_nails1;
+extern int eval_ammo_lava_nails;
+extern int eval_ammo_rockets1;
+extern int eval_ammo_multi_rockets;
+extern int eval_ammo_cells1;
+extern int eval_ammo_plasma;
+extern int eval_idealpitch;
+extern int eval_pitch_speed;
+extern int eval_viewmodelforclient;
+extern int eval_nodrawtoclient;
+extern int eval_drawonlytoclient;
+extern int eval_colormod;
+extern int eval_ping;
+extern int eval_movement;
+
+#define GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t*)((char*)&ed->v + fieldoffset) : NULL)
+
+//============================================================================
+
+extern dprograms_t             *progs;
+extern dfunction_t             *pr_functions;
+extern char                    *pr_strings;
+extern ddef_t                  *pr_globaldefs;
+extern ddef_t                  *pr_fielddefs;
+extern dstatement_t    *pr_statements;
+extern globalvars_t    *pr_global_struct;
+extern float                   *pr_globals;                    // same as pr_global_struct
+
+extern int                             pr_edict_size;  // in bytes
+extern int                             pr_edictareasize; // LordHavoc: for bounds checking
+
+//============================================================================
+
+void PR_Init (void);
+
+void PR_ExecuteProgram (func_t fnum);
+void PR_LoadProgs (void);
+
+void PR_Profile_f (void);
+
+edict_t *ED_Alloc (void);
+void ED_Free (edict_t *ed);
+
+char   *ED_NewString (char *string);
+// returns a copy of the string allocated from the server's string heap
+
+void ED_Print (edict_t *ed);
+void ED_Write (FILE *f, edict_t *ed);
+char *ED_ParseEdict (char *data, edict_t *ent);
+
+void ED_WriteGlobals (FILE *f);
+void ED_ParseGlobals (char *data);
+
+void ED_LoadFromFile (char *data);
+
+edict_t *EDICT_NUM_ERROR(int n);
+#define EDICT_NUM(n) (n >= 0 ? (n < sv.max_edicts ? (edict_t *)((byte *)sv.edicts + (n) * pr_edict_size) : EDICT_NUM_ERROR(n)) : EDICT_NUM_ERROR(n))
+//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))
+//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size)
+
+//edict_t *EDICT_NUM(int n);
+int NUM_FOR_EDICT(edict_t *e);
+
+#define        NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size))
+
+#define        EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts)
+#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e))
+
+//============================================================================
+
+#define        G_FLOAT(o) (pr_globals[o])
+#define        G_INT(o) (*(int *)&pr_globals[o])
+#define        G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o]))
+#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
+#define        G_VECTOR(o) (&pr_globals[o])
+#define        G_STRING(o) (pr_strings + *(string_t *)&pr_globals[o])
+#define        G_FUNCTION(o) (*(func_t *)&pr_globals[o])
+
+#define        E_FLOAT(e,o) (((float*)&e->v)[o])
+#define        E_INT(e,o) (*(int *)&((float*)&e->v)[o])
+#define        E_VECTOR(e,o) (&((float*)&e->v)[o])
+#define        E_STRING(e,o) (pr_strings + *(string_t *)&((float*)&e->v)[o])
+
+extern int             type_size[8];
+
+typedef void (*builtin_t) (void);
+extern builtin_t *pr_builtins;
+extern int pr_numbuiltins;
+
+extern int             pr_argc;
+
+extern qboolean        pr_trace;
+extern dfunction_t     *pr_xfunction;
+extern int                     pr_xstatement;
+
+extern unsigned short          pr_crc;
+
+void PR_RunError (char *error, ...);
+
+void ED_PrintEdicts (void);
+void ED_PrintNum (int ent);
+
+//eval_t *GetEdictFieldValue(edict_t *ed, char *field);
+
diff --git a/protocol.h b/protocol.h
new file mode 100644 (file)
index 0000000..027871e
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// protocol.h -- communications protocols
+
+#define        PROTOCOL_VERSION        15
+
+// model effects
+#define        EF_ROCKET       1                       // leave a trail
+#define        EF_GRENADE      2                       // leave a trail
+#define        EF_GIB          4                       // leave a trail
+#define        EF_ROTATE       8                       // rotate (bonus items)
+#define        EF_TRACER       16                      // green split trail
+#define        EF_ZOMGIB       32                      // small blood trail
+#define        EF_TRACER2      64                      // orange split trail + rotate
+#define        EF_TRACER3      128                     // purple trail
+// entity effects
+#define        EF_BRIGHTFIELD                  1
+#define        EF_MUZZLEFLASH                  2
+#define        EF_BRIGHTLIGHT                  4
+#define        EF_DIMLIGHT                     8
+#define        EF_NODRAW                               16
+#define EF_ADDITIVE                            32
+#define EF_BLUE                                        64
+#define EF_RED                                 128
+#define EF_DELTA                               8388608 // LordHavoc: entity is delta compressed to save network bandwidth
+// effects/model (can be used as model flags or entity effects)
+#define        EF_REFLECTIVE                   256             // LordHavoc: shiny metal objects :)
+#define EF_FULLBRIGHT                  512             // LordHavoc: fullbright
+
+// if the high bit of the servercmd is set, the low bits are fast update flags:
+#define        U_MOREBITS      (1<<0)
+#define        U_ORIGIN1       (1<<1)
+#define        U_ORIGIN2       (1<<2)
+#define        U_ORIGIN3       (1<<3)
+#define        U_ANGLE2        (1<<4)
+// LordHavoc: U_NOLERP was only ever used for monsters, so I renamed it U_STEP
+#define        U_STEP          (1<<5)
+#define        U_FRAME         (1<<6)
+// just differentiates from other updates
+#define U_SIGNAL       (1<<7)
+
+// svc_update can pass all of the fast update bits, plus more
+#define        U_ANGLE1        (1<<8)
+#define        U_ANGLE3        (1<<9)
+#define        U_MODEL         (1<<10)
+#define        U_COLORMAP      (1<<11)
+#define        U_SKIN          (1<<12)
+#define        U_EFFECTS       (1<<13)
+#define        U_LONGENTITY    (1<<14)
+
+// LordHavoc: protocol extension
+#define        U_EXTEND1       (1<<15)
+// LordHavoc: first extend byte
+#define        U_DELTA         (1<<16) // no data, while this is set the entity is delta compressed (uses previous frame as a baseline, meaning only things that have changed from the previous frame are sent, except for the forced full update every half second)
+#define U_ALPHA                (1<<17) // 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 1, and the entity is not sent if <=0 unless it has effects (model effects are checked as well)
+#define        U_SCALE         (1<<18) // 1 byte, scale / 16 positive, not sent if 1.0
+#define        U_EFFECTS2      (1<<19) // 1 byte, this is .effects & 0xFF00 (second byte)
+#define U_GLOWSIZE     (1<<20) // 1 byte, encoding is float/8.0, signed (negative is darklight), not sent if 0
+#define        U_GLOWCOLOR     (1<<21) // 1 byte, palette index, default is 254 (white), this IS used for darklight (allowing colored darklight), however the particles from a darklight are always black, not sent if default value (even if glowsize or glowtrail is set)
+#define U_COLORMOD     (1<<22) // 1 byte, 3 bit red, 3 bit green, 2 bit blue, this lets you tint an object artifically, so you could make a red rocket, or a blue fiend...
+#define        U_EXTEND2       (1<<23) // another byte to follow
+// LordHavoc: second extend byte
+#define U_GLOWTRAIL    (1<<24) // leaves a trail of particles (of color .glowcolor, or black if it is a negative glowsize)
+#define U_VIEWMODEL    (1<<25) // attachs the model to the view (origin and angles become relative to it), only shown to owner, a more powerful alternative to .weaponmodel and such
+#define U_FRAME2       (1<<26) // 1 byte, this is .frame & 0xFF00 (second byte)
+#define U_UNUSED27     (1<<27) // future expansion
+#define U_UNUSED28     (1<<28) // future expansion
+#define U_UNUSED29     (1<<29) // future expansion
+#define U_UNUSED30     (1<<30) // future expansion
+#define U_EXTEND3      (1<<31) // another byte to follow, future expansion
+
+#define        SU_VIEWHEIGHT   (1<<0)
+#define        SU_IDEALPITCH   (1<<1)
+#define        SU_PUNCH1               (1<<2)
+#define        SU_PUNCH2               (1<<3)
+#define        SU_PUNCH3               (1<<4)
+#define        SU_VELOCITY1    (1<<5)
+#define        SU_VELOCITY2    (1<<6)
+#define        SU_VELOCITY3    (1<<7)
+//define       SU_AIMENT               (1<<8)  AVAILABLE BIT
+#define        SU_ITEMS                (1<<9)
+#define        SU_ONGROUND             (1<<10)         // no data follows, the bit is it
+#define        SU_INWATER              (1<<11)         // no data follows, the bit is it
+#define        SU_WEAPONFRAME  (1<<12)
+#define        SU_ARMOR                (1<<13)
+#define        SU_WEAPON               (1<<14)
+
+// a sound with no channel is a local only sound
+#define        SND_VOLUME              (1<<0)          // a byte
+#define        SND_ATTENUATION (1<<1)          // a byte
+#define        SND_LOOPING             (1<<2)          // a long
+
+
+// defaults for clientinfo messages
+#define        DEFAULT_VIEWHEIGHT      22
+
+
+// game types sent by serverinfo
+// these determine which intermission screen plays
+#define        GAME_COOP                       0
+#define        GAME_DEATHMATCH         1
+
+//==================
+// note that there are some defs.qc that mirror to these numbers
+// also related to svc_strings[] in cl_parse
+//==================
+
+//
+// server to client
+//
+#define        svc_bad                         0
+#define        svc_nop                         1
+#define        svc_disconnect          2
+#define        svc_updatestat          3       // [byte] [long]
+#define        svc_version                     4       // [long] server version
+#define        svc_setview                     5       // [short] entity number
+#define        svc_sound                       6       // <see code>
+#define        svc_time                        7       // [float] server time
+#define        svc_print                       8       // [string] null terminated string
+#define        svc_stufftext           9       // [string] stuffed into client's console buffer
+                                                               // the string should be \n terminated
+#define        svc_setangle            10      // [angle3] set the view angle to this absolute value
+       
+#define        svc_serverinfo          11      // [long] version
+                                               // [string] signon string
+                                               // [string]..[0]model cache
+                                               // [string]...[0]sounds cache
+#define        svc_lightstyle          12      // [byte] [string]
+#define        svc_updatename          13      // [byte] [string]
+#define        svc_updatefrags         14      // [byte] [short]
+#define        svc_clientdata          15      // <shortbits + data>
+#define        svc_stopsound           16      // <see code>
+#define        svc_updatecolors        17      // [byte] [byte]
+#define        svc_particle            18      // [vec3] <variable>
+#define        svc_damage                      19
+       
+#define        svc_spawnstatic         20
+//     svc_spawnbinary         21
+#define        svc_spawnbaseline       22
+       
+#define        svc_temp_entity         23
+
+#define        svc_setpause            24      // [byte] on / off
+#define        svc_signonnum           25      // [byte]  used for the signon sequence
+
+#define        svc_centerprint         26      // [string] to put in center of the screen
+
+#define        svc_killedmonster       27
+#define        svc_foundsecret         28
+
+#define        svc_spawnstaticsound    29      // [coord3] [byte] samp [byte] vol [byte] aten
+
+#define        svc_intermission        30              // [string] music
+#define        svc_finale                      31              // [string] music [string] text
+
+#define        svc_cdtrack                     32              // [byte] track [byte] looptrack
+#define svc_sellscreen         33
+
+#define svc_cutscene           34
+
+#define        svc_showlmp                     35              // [string] slotname [string] lmpfilename [coord] x [coord] y
+#define        svc_hidelmp                     36              // [string] slotname
+#define        svc_skybox                      37              // [string] skyname
+
+#define svc_skyboxsize         50              // [coord] size (default is 4096)
+#define svc_fog                                51              // [byte] enable <optional past this point, only included if enable is true> [float] density [byte] red [byte] green [byte] blue
+
+//
+// client to server
+//
+#define        clc_bad                 0
+#define        clc_nop                 1
+#define        clc_disconnect  2
+#define        clc_move                3                       // [usercmd_t]
+#define        clc_stringcmd   4               // [string] message
+
+
+//
+// temp entity events
+//
+#define        TE_SPIKE                        0 // [vector] origin
+#define        TE_SUPERSPIKE           1 // [vector] origin
+#define        TE_GUNSHOT                      2 // [vector] origin
+#define        TE_EXPLOSION            3 // [vector] origin
+#define        TE_TAREXPLOSION         4 // [vector] origin
+#define        TE_LIGHTNING1           5 // [entity] entity [vector] start [vector] end
+#define        TE_LIGHTNING2           6 // [entity] entity [vector] start [vector] end
+#define        TE_WIZSPIKE                     7 // [vector] origin
+#define        TE_KNIGHTSPIKE          8 // [vector] origin
+#define        TE_LIGHTNING3           9 // [entity] entity [vector] start [vector] end
+#define        TE_LAVASPLASH           10 // [vector] origin
+#define        TE_TELEPORT                     11 // [vector] origin
+#define TE_EXPLOSION2          12 // [vector] origin [byte] startcolor [byte] colorcount
+
+// PGM 01/21/97 
+#define TE_BEAM                                13 // [entity] entity [vector] start [vector] end
+// PGM 01/21/97 
+
+// Nehahra effects used in the movie (TE_EXPLOSION3 also got written up in a QSG tutorial, hence it's not marked NEH)
+#define        TE_EXPLOSION3           16 // [vector] origin [coord] red [coord] green [coord] blue
+#define TE_LIGHTNING4NEH       17 // [string] model [entity] entity [vector] start [vector] end
+
+// LordHavoc: added some TE_ codes (block1 - 50-60)
+#define        TE_BLOOD                        50 // [vector] origin [byte] xvel [byte] yvel [byte] zvel [byte] count
+#define        TE_SPARK                        51 // [vector] origin [byte] xvel [byte] yvel [byte] zvel [byte] count
+#define        TE_BLOODSHOWER          52 // [vector] min [vector] max [coord] explosionspeed [short] count
+#define        TE_EXPLOSIONRGB         53 // [vector] origin [byte] red [byte] green [byte] blue
+#define TE_PARTICLECUBE                54 // [vector] min [vector] max [vector] dir [short] count [byte] color [byte] gravity [coord] randomvel
+#define TE_PARTICLERAIN                55 // [vector] min [vector] max [vector] dir [short] count [byte] color
+#define TE_PARTICLESNOW                56 // [vector] min [vector] max [vector] dir [short] count [byte] color
+#define TE_GUNSHOTQUAD         57 // [vector] origin
+#define TE_SPIKEQUAD           58 // [vector] origin
+#define TE_SUPERSPIKEQUAD      59 // [vector] origin
+// LordHavoc: block2 - 70-80
+#define TE_EXPLOSIONQUAD       70 // [vector] origin
diff --git a/quakedef.h b/quakedef.h
new file mode 100644 (file)
index 0000000..7e0c869
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// quakedef.h -- primary header for client
+
+//#define      GLTEST                  // experimental stuff
+
+#define        QUAKE_GAME                      // as opposed to utilities
+
+#define DP_VERSION                     1.05
+#define        VERSION                         1.09
+#define        GLQUAKE_VERSION         1.00
+#define        D3DQUAKE_VERSION        0.01
+#define        WINQUAKE_VERSION        0.996
+#define        LINUX_VERSION           1.30
+#define        X11_VERSION                     1.10
+
+//define       PARANOID                        // speed sapping error checking
+
+#define        GAMENAME        "id1"
+
+#include <math.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+#define id386  0
+
+
+#if defined(_WIN32) && !defined(WINDED)
+
+//#if defined(_M_IX86)
+//#define __i386__     1
+//#endif
+
+void   VID_LockBuffer (void);
+void   VID_UnlockBuffer (void);
+
+#else
+
+#define        VID_LockBuffer()
+#define        VID_UnlockBuffer()
+
+#endif
+
+//#if defined __i386__ // && !defined __sun__
+//#define id386        1
+//#else
+//#define id386        0
+//#endif
+
+#if id386
+#define UNALIGNED_OK   1       // set to 0 if unaligned accesses are not supported
+#else
+#define UNALIGNED_OK   0
+#endif
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+#define CACHE_SIZE     32              // used to align key data structures
+
+#define UNUSED(x)      (x = x) // for pesky compiler / lint warnings
+
+#define        MINIMUM_MEMORY                  0x550000
+#define        MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000)
+
+#define MAX_NUM_ARGVS  50
+
+// up / down
+#define        PITCH   0
+
+// left / right
+#define        YAW             1
+
+// fall over
+#define        ROLL    2
+
+
+#define        MAX_QPATH               64                      // max length of a quake game pathname
+#define        MAX_OSPATH              128                     // max length of a filesystem pathname
+
+#define        ON_EPSILON              0.1                     // point on plane side epsilon
+
+// LordHavoc: these were 8000 and 1024 respectively, now 64000 and 8000
+#define        MAX_MSGLEN              64000           // max length of a reliable message
+#define        MAX_DATAGRAM    8000            // max length of unreliable message
+
+//
+// per-level limits
+//
+// LordHavoc: increased entity limit to 2048 from 600
+#define        MAX_EDICTS              2048            // FIXME: ouch! ouch! ouch!
+#define        MAX_LIGHTSTYLES 64
+#define        MAX_MODELS              256                     // these are sent over the net as bytes
+#define        MAX_SOUNDS              256                     // so they cannot be blindly increased
+
+#define        SAVEGAME_COMMENT_LENGTH 39
+
+#define        MAX_STYLESTRING 64
+
+//
+// stats are integers communicated to the client by the server
+//
+#define        MAX_CL_STATS            32
+#define        STAT_HEALTH                     0
+#define        STAT_FRAGS                      1
+#define        STAT_WEAPON                     2
+#define        STAT_AMMO                       3
+#define        STAT_ARMOR                      4
+#define        STAT_WEAPONFRAME        5
+#define        STAT_SHELLS                     6
+#define        STAT_NAILS                      7
+#define        STAT_ROCKETS            8
+#define        STAT_CELLS                      9
+#define        STAT_ACTIVEWEAPON       10
+#define        STAT_TOTALSECRETS       11
+#define        STAT_TOTALMONSTERS      12
+#define        STAT_SECRETS            13              // bumped on client side by svc_foundsecret
+#define        STAT_MONSTERS           14              // bumped by svc_killedmonster
+
+// stock defines
+
+#define        IT_SHOTGUN                              1
+#define        IT_SUPER_SHOTGUN                2
+#define        IT_NAILGUN                              4
+#define        IT_SUPER_NAILGUN                8
+#define        IT_GRENADE_LAUNCHER             16
+#define        IT_ROCKET_LAUNCHER              32
+#define        IT_LIGHTNING                    64
+#define IT_SUPER_LIGHTNING      128
+#define IT_SHELLS               256
+#define IT_NAILS                512
+#define IT_ROCKETS              1024
+#define IT_CELLS                2048
+#define IT_AXE                  4096
+#define IT_ARMOR1               8192
+#define IT_ARMOR2               16384
+#define IT_ARMOR3               32768
+#define IT_SUPERHEALTH          65536
+#define IT_KEY1                 131072
+#define IT_KEY2                 262144
+#define        IT_INVISIBILITY                 524288
+#define        IT_INVULNERABILITY              1048576
+#define        IT_SUIT                                 2097152
+#define        IT_QUAD                                 4194304
+#define IT_SIGIL1               (1<<28)
+#define IT_SIGIL2               (1<<29)
+#define IT_SIGIL3               (1<<30)
+#define IT_SIGIL4               (1<<31)
+
+//===========================================
+//rogue changed and added defines
+
+#define RIT_SHELLS              128
+#define RIT_NAILS               256
+#define RIT_ROCKETS             512
+#define RIT_CELLS               1024
+#define RIT_AXE                 2048
+#define RIT_LAVA_NAILGUN        4096
+#define RIT_LAVA_SUPER_NAILGUN  8192
+#define RIT_MULTI_GRENADE       16384
+#define RIT_MULTI_ROCKET        32768
+#define RIT_PLASMA_GUN          65536
+#define RIT_ARMOR1              8388608
+#define RIT_ARMOR2              16777216
+#define RIT_ARMOR3              33554432
+#define RIT_LAVA_NAILS          67108864
+#define RIT_PLASMA_AMMO         134217728
+#define RIT_MULTI_ROCKETS       268435456
+#define RIT_SHIELD              536870912
+#define RIT_ANTIGRAV            1073741824
+#define RIT_SUPERHEALTH         2147483648
+
+//MED 01/04/97 added hipnotic defines
+//===========================================
+//hipnotic added defines
+#define HIT_PROXIMITY_GUN_BIT 16
+#define HIT_MJOLNIR_BIT       7
+#define HIT_LASER_CANNON_BIT  23
+#define HIT_PROXIMITY_GUN   (1<<HIT_PROXIMITY_GUN_BIT)
+#define HIT_MJOLNIR         (1<<HIT_MJOLNIR_BIT)
+#define HIT_LASER_CANNON    (1<<HIT_LASER_CANNON_BIT)
+#define HIT_WETSUIT         (1<<(23+2))
+#define HIT_EMPATHY_SHIELDS (1<<(23+3))
+
+//===========================================
+
+// LordHavoc: increased player limit from 16 to 64
+#define        MAX_SCOREBOARD          64
+#define        MAX_SCOREBOARDNAME      32
+
+#define        SOUND_CHANNELS          8
+
+// This makes anyone on id's net privileged
+// Use for multiplayer testing only - VERY dangerous!!!
+// #define IDGODS
+
+#include "common.h"
+#include "bspfile.h"
+#include "vid.h"
+#include "sys.h"
+#include "zone.h"
+#include "mathlib.h"
+
+// LordHavoc: made this more compact, and added some more fields
+typedef struct
+{
+       vec3_t  origin;
+       vec3_t  angles;
+       int             effects;
+       short   modelindex;
+       short   frame;
+       byte    colormap;
+       byte    skin;
+       byte    alpha;
+       byte    scale;
+       byte    glowsize;
+       byte    glowcolor;
+       byte    colormod;
+} entity_state_t;
+
+
+#include "wad.h"
+#include "draw.h"
+#include "cvar.h"
+#include "screen.h"
+#include "net.h"
+#include "protocol.h"
+#include "cmd.h"
+#include "sbar.h"
+#include "sound.h"
+#include "render.h"
+#include "client.h"
+#include "progs.h"
+#include "server.h"
+
+#include "model_shared.h"
+
+#include "input.h"
+#include "world.h"
+#include "keys.h"
+#include "console.h"
+#include "view.h"
+#include "menu.h"
+#include "crc.h"
+#include "cdaudio.h"
+
+#include "glquake.h"
+
+//=============================================================================
+
+// the host system specifies the base of the directory tree, the
+// command line parms passed to the program, and the amount of memory
+// available for the program to use
+
+typedef struct
+{
+       char    *basedir;
+       char    *cachedir;              // for development over ISDN lines
+       int             argc;
+       char    **argv;
+       void    *membase;
+       int             memsize;
+} quakeparms_t;
+
+
+//=============================================================================
+
+
+
+extern qboolean noclip_anglehack;
+
+
+//
+// host
+//
+extern quakeparms_t host_parms;
+
+extern cvar_t          sys_ticrate;
+extern cvar_t          sys_nostdout;
+extern cvar_t          developer;
+
+extern qboolean        host_initialized;               // true if into command execution
+extern double          host_frametime;
+extern byte            *host_basepal;
+extern byte            *host_colormap;
+extern int                     host_framecount;        // incremented every frame, never reset
+extern double          realtime;                       // not bounded in any way, changed at
+                                                                               // start of every frame, never reset
+
+void Host_ClearMemory (void);
+void Host_ServerFrame (void);
+void Host_InitCommands (void);
+void Host_Init (quakeparms_t *parms);
+void Host_Shutdown(void);
+void Host_Error (char *error, ...);
+void Host_EndGame (char *message, ...);
+void Host_Frame (float time);
+void Host_Quit_f (void);
+void Host_ClientCommands (char *fmt, ...);
+void Host_ShutdownServer (qboolean crash);
+
+extern qboolean                msg_suppress_1;         // suppresses resolution and cache size console output
+                                                                               //  an fullscreen DIB focus gain/loss
+extern int                     current_skill;          // skill level for currently loaded level (in case
+                                                                               //  the user changes the cvar while the level is
+                                                                               //  running, this reflects the level actually in use)
+
+extern qboolean                isDedicated;
+
+extern int                     minimum_memory;
+
+//
+// chase
+//
+extern cvar_t  chase_active;
+
+void Chase_Init (void);
+void Chase_Reset (void);
+void Chase_Update (void);
diff --git a/r_light.c b/r_light.c
new file mode 100644 (file)
index 0000000..141d905
--- /dev/null
+++ b/r_light.c
@@ -0,0 +1,558 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_light.c
+
+#include "quakedef.h"
+
+cvar_t r_lightmodels = {"r_lightmodels", "1"};
+
+void rlight_init()
+{
+       Cvar_RegisterVariable(&r_lightmodels);
+}
+
+int    r_dlightframecount;
+
+/*
+==================
+R_AnimateLight
+==================
+*/
+void R_AnimateLight (void)
+{
+       int                     i,j,k;
+       
+//
+// light animations
+// 'm' is normal light, 'a' is no light, 'z' is double bright
+       i = (int)(cl.time*10);
+       for (j=0 ; j<MAX_LIGHTSTYLES ; j++)
+       {
+               if (!cl_lightstyle[j].length)
+               {
+                       d_lightstylevalue[j] = 256;
+                       continue;
+               }
+               k = i % cl_lightstyle[j].length;
+               k = cl_lightstyle[j].map[k] - 'a';
+               k = k*22;
+               d_lightstylevalue[j] = k;
+       }       
+}
+
+/*
+=============================================================================
+
+DYNAMIC LIGHTS
+
+=============================================================================
+*/
+
+/*
+=============
+R_MarkLights
+=============
+*/
+void R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex, mnode_t *node)
+{
+       float           dist, l, maxdist;
+       msurface_t      *surf;
+       int                     i, j, s, t;
+       vec3_t          impact;
+       
+loc0:
+       if (node->contents < 0)
+               return;
+
+       dist = DotProduct (lightorigin, node->plane->normal) - node->plane->dist;
+       
+       if (dist > light->radius)
+       {
+               if (node->children[0]->contents >= 0) // LordHavoc: save some time by not pushing another stack frame
+               {
+                       node = node->children[0];
+                       goto loc0;
+               }
+               return;
+       }
+       if (dist < -light->radius)
+       {
+               if (node->children[1]->contents >= 0) // LordHavoc: save some time by not pushing another stack frame
+               {
+                       node = node->children[1];
+                       goto loc0;
+               }
+               return;
+       }
+
+       maxdist = light->radius*light->radius;
+
+// mark the polygons
+       surf = cl.worldmodel->surfaces + node->firstsurface;
+       for (i=0 ; i<node->numsurfaces ; i++, surf++)
+       {
+               if (surf->flags & SURF_DRAWTURB) // water
+               {
+                       if (surf->dlightframe != r_dlightframecount) // not dynamic until now
+                       {
+                               surf->dlightbits[0] = surf->dlightbits[1] = surf->dlightbits[2] = surf->dlightbits[3] = surf->dlightbits[4] = surf->dlightbits[5] = surf->dlightbits[6] = surf->dlightbits[7] = 0;
+                               surf->dlightframe = r_dlightframecount;
+                       }
+                       surf->dlightbits[bitindex] |= bit;
+               }
+               // LordHavoc: MAJOR dynamic light speedup here, eliminates marking of surfaces that are too far away from light, thus preventing unnecessary uploads
+               else /*if (r_dynamicbothsides.value || (((surf->flags & SURF_PLANEBACK) && (dist < -BACKFACE_EPSILON)) || (!(surf->flags & SURF_PLANEBACK) && (dist > BACKFACE_EPSILON))))*/
+               {
+                       // passed the plane side check
+                       for (j=0 ; j<3 ; j++)
+                               impact[j] = lightorigin[j] - surf->plane->normal[j]*dist;
+
+                       // clamp center of light to corner and check brightness
+                       l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
+                       s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];
+                       s = l - s;
+                       l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
+                       t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];
+                       t = l - t;
+                       // compare to minimum light
+                       if ((s*s+t*t+dist*dist) < maxdist)
+                       {
+                               if (surf->dlightframe != r_dlightframecount) // not dynamic until now
+                               {
+                                       surf->dlightbits[0] = surf->dlightbits[1] = surf->dlightbits[2] = surf->dlightbits[3] = surf->dlightbits[4] = surf->dlightbits[5] = surf->dlightbits[6] = surf->dlightbits[7] = 0;
+                                       surf->dlightframe = r_dlightframecount;
+                               }
+                               surf->dlightbits[bitindex] |= bit;
+                       }
+               }
+       }
+
+       if (node->children[0]->contents >= 0)
+       {
+               if (node->children[1]->contents >= 0)
+               {
+                       R_MarkLights (lightorigin, light, bit, bitindex, node->children[0]);
+                       node = node->children[1];
+                       goto loc0;
+               }
+               else
+               {
+                       node = node->children[0];
+                       goto loc0;
+               }
+       }
+       else if (node->children[1]->contents >= 0)
+       {
+               node = node->children[1];
+               goto loc0;
+       }
+}
+
+
+/*
+=============
+R_PushDlights
+=============
+*/
+void R_PushDlights (void)
+{
+       int             i;
+       dlight_t        *l;
+
+       r_dlightframecount = r_framecount + 1;  // because the count hasn't advanced yet for this frame
+
+//     if (gl_flashblend.value || !r_dynamic.value)
+//             return;
+
+       l = cl_dlights;
+
+       for (i=0 ; i<MAX_DLIGHTS ; i++, l++)
+       {
+               if (l->die < cl.time || !l->radius)
+                       continue;
+               R_MarkLights (l->origin, l, 1<<(i&31), i >> 5, cl.worldmodel->nodes );
+       }
+}
+
+
+/*
+=============================================================================
+
+LIGHT SAMPLING
+
+=============================================================================
+*/
+
+mplane_t               *lightplane;
+vec3_t                 lightspot;
+
+int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end)
+{
+       float           front, back, frac;
+       vec3_t          mid;
+
+loc0:
+       if (node->contents < 0)
+               return false;           // didn't hit anything
+       
+// calculate mid point
+       front = PlaneDiff (start, node->plane);
+       back = PlaneDiff (end, node->plane);
+
+       // LordHavoc: optimized recursion
+       if ((back < 0) == (front < 0))
+//             return RecursiveLightPoint (color, node->children[front < 0], start, end);
+       {
+               node = node->children[front < 0];
+               goto loc0;
+       }
+       
+       frac = front / (front-back);
+       mid[0] = start[0] + (end[0] - start[0])*frac;
+       mid[1] = start[1] + (end[1] - start[1])*frac;
+       mid[2] = start[2] + (end[2] - start[2])*frac;
+       
+// go down front side
+       if (RecursiveLightPoint (color, node->children[front < 0], start, mid))
+               return true;    // hit something
+       else
+       {
+               int i, ds, dt;
+               msurface_t *surf;
+       // check for impact on this node
+               VectorCopy (mid, lightspot);
+               lightplane = node->plane;
+
+               surf = cl.worldmodel->surfaces + node->firstsurface;
+               for (i = 0;i < node->numsurfaces;i++, surf++)
+               {
+                       if (surf->flags & SURF_DRAWTILED)
+                               continue;       // no lightmaps
+
+                       ds = (int) ((float) DotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
+                       dt = (int) ((float) DotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
+
+                       if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
+                               continue;
+                       
+                       ds -= surf->texturemins[0];
+                       dt -= surf->texturemins[1];
+                       
+                       if (ds > surf->extents[0] || dt > surf->extents[1])
+                               continue;
+
+                       if (surf->samples)
+                       {
+                               byte *lightmap;
+                               int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
+                               float scale;
+                               line3 = ((surf->extents[0]>>4)+1)*3;
+
+                               lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
+
+                               for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
+                               {
+                                       scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0;
+                                       r00 += (float) lightmap[      0] * scale;g00 += (float) lightmap[      1] * scale;b00 += (float) lightmap[2] * scale;
+                                       r01 += (float) lightmap[      3] * scale;g01 += (float) lightmap[      4] * scale;b01 += (float) lightmap[5] * scale;
+                                       r10 += (float) lightmap[line3+0] * scale;g10 += (float) lightmap[line3+1] * scale;b10 += (float) lightmap[line3+2] * scale;
+                                       r11 += (float) lightmap[line3+3] * scale;g11 += (float) lightmap[line3+4] * scale;b11 += (float) lightmap[line3+5] * scale;
+                                       lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
+                               }
+
+                               color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)));
+                               color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)));
+                               color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)));
+                       }
+                       return true; // success
+               }
+
+       // go down back side
+               return RecursiveLightPoint (color, node->children[front >= 0], mid, end);
+       }
+}
+
+void R_LightPoint (vec3_t color, vec3_t p)
+{
+       vec3_t          end;
+       
+       if (r_fullbright.value || !cl.worldmodel->lightdata)
+       {
+               color[0] = color[1] = color[2] = 255;
+               return;
+       }
+       
+       end[0] = p[0];
+       end[1] = p[1];
+       end[2] = p[2] - 2048;
+
+       color[0] = color[1] = color[2] = 0;
+       RecursiveLightPoint (color, cl.worldmodel->nodes, p, end);
+}
+
+// LordHavoc: R_DynamicLightPoint - acumulates the dynamic lighting
+void R_DynamicLightPoint(vec3_t color, vec3_t org, int *dlightbits)
+{
+       int             i;
+       vec3_t  dist;
+       float   brightness, r, f;
+
+       if (/*gl_flashblend.value || !r_dynamic.value || */(!dlightbits[0] && !dlightbits[1] && !dlightbits[2] && !dlightbits[3] && !dlightbits[4] && !dlightbits[5] && !dlightbits[6] && !dlightbits[7]))
+               return;
+
+       for (i=0 ; i<MAX_DLIGHTS ; i++)
+       {
+               if (!((1 << (i&31)) & dlightbits[i>>5]))
+                       continue;
+               if (cl_dlights[i].die < cl.time || !cl_dlights[i].radius)
+                       continue;
+               VectorSubtract (org, cl_dlights[i].origin, dist);
+               if ((f = DotProduct(dist, dist) + 64.0) < (r = cl_dlights[i].radius*cl_dlights[i].radius))
+               {
+                       brightness = r * 16.0 / f;
+                       if (cl_dlights[i].dark)
+                               brightness = -brightness;
+                       color[0] += brightness * cl_dlights[i].color[0];
+                       color[1] += brightness * cl_dlights[i].color[1];
+                       color[2] += brightness * cl_dlights[i].color[2];
+               }
+       }
+}
+
+// same as above but no bitmask to check
+void R_DynamicLightPointNoMask(vec3_t color, vec3_t org)
+{
+       int             i;
+       vec3_t  dist;
+       float   brightness, r, f;
+
+//     if (gl_flashblend.value || !r_dynamic.value)
+//             return;
+
+       for (i=0 ; i<MAX_DLIGHTS ; i++)
+       {
+               if (cl_dlights[i].die < cl.time || !cl_dlights[i].radius)
+                       continue;
+               VectorSubtract (org, cl_dlights[i].origin, dist);
+               if ((f = DotProduct(dist, dist) + 64.0) < (r = cl_dlights[i].radius*cl_dlights[i].radius))
+               {
+                       brightness = r * 16.0 / f;
+                       if (cl_dlights[i].dark)
+                               brightness = -brightness;
+                       color[0] += brightness * cl_dlights[i].color[0];
+                       color[1] += brightness * cl_dlights[i].color[1];
+                       color[2] += brightness * cl_dlights[i].color[2];
+               }
+       }
+}
+
+extern float *aliasvert;
+extern float *aliasvertnorm;
+extern byte *aliasvertcolor;
+extern vec_t shadecolor[];
+extern float modelalpha;
+extern qboolean lighthalf;
+void R_LightModel(int numverts, vec3_t center)
+{
+       int i, j, nearlights = 0;
+       vec3_t dist;
+       float t, t1, t2, t3, *avn;
+       byte r,g,b,a, *avc;
+       struct
+       {
+               vec3_t color;
+               vec3_t origin;
+       } nearlight[MAX_DLIGHTS];
+       if (!lighthalf)
+       {
+               shadecolor[0] *= 2.0f;
+               shadecolor[1] *= 2.0f;
+               shadecolor[2] *= 2.0f;
+       }
+       avc = aliasvertcolor;
+       avn = aliasvertnorm;
+       a = (byte) bound((int) 0, (int) (modelalpha * 255.0f), (int) 255);
+       if (currententity->effects & EF_FULLBRIGHT)
+       {
+               if (lighthalf)
+               {
+                       r = (byte) ((float) (128.0f * currententity->colormod[0]));
+                       g = (byte) ((float) (128.0f * currententity->colormod[1]));
+                       b = (byte) ((float) (128.0f * currententity->colormod[2]));
+               }
+               else
+               {
+                       r = (byte) ((float) (255.0f * currententity->colormod[0]));
+                       g = (byte) ((float) (255.0f * currententity->colormod[1]));
+                       b = (byte) ((float) (255.0f * currententity->colormod[2]));
+               }
+               for (i = 0;i < numverts;i++)
+               {
+                       *avc++ = r;
+                       *avc++ = g;
+                       *avc++ = b;
+                       *avc++ = a;
+               }
+               return;
+       }
+       if (r_lightmodels.value)
+       {
+               for (i = 0;i < MAX_DLIGHTS;i++)
+               {
+                       if (cl_dlights[i].die < cl.time || !cl_dlights[i].radius)
+                               continue;
+                       VectorSubtract (center, cl_dlights[i].origin, dist);
+                       if ((t2 = DotProduct(dist,dist)) + 64.0f < (t1 = cl_dlights[i].radius*cl_dlights[i].radius))
+                       {
+                               VectorCopy(cl_dlights[i].origin, nearlight[nearlights].origin);
+                               nearlight[nearlights].color[0] = cl_dlights[i].color[0] * cl_dlights[i].radius * cl_dlights[i].radius * 0.5f;
+                               nearlight[nearlights].color[1] = cl_dlights[i].color[1] * cl_dlights[i].radius * cl_dlights[i].radius * 0.5f;
+                               nearlight[nearlights].color[2] = cl_dlights[i].color[2] * cl_dlights[i].radius * cl_dlights[i].radius * 0.5f;
+                               if (cl_dlights[i].dark)
+                               {
+                                       nearlight[nearlights].color[0] = -nearlight[nearlights].color[0];
+                                       nearlight[nearlights].color[1] = -nearlight[nearlights].color[1];
+                                       nearlight[nearlights].color[2] = -nearlight[nearlights].color[2];
+                               }
+                               if (lighthalf)
+                               {
+                                       nearlight[nearlights].color[0] *= 0.5f;
+                                       nearlight[nearlights].color[1] *= 0.5f;
+                                       nearlight[nearlights].color[2] *= 0.5f;
+                               }
+                               t1 = 1.0f / t2;
+                               shadecolor[0] += nearlight[nearlights].color[0] * t1;
+                               shadecolor[1] += nearlight[nearlights].color[1] * t1;
+                               shadecolor[2] += nearlight[nearlights].color[2] * t1;
+                               nearlight[nearlights].color[0] *= currententity->colormod[0];
+                               nearlight[nearlights].color[1] *= currententity->colormod[1];
+                               nearlight[nearlights].color[2] *= currententity->colormod[2];
+                               nearlights++;
+                       }
+               }
+       }
+       else
+       {
+               for (i = 0;i < MAX_DLIGHTS;i++)
+               {
+                       if (cl_dlights[i].die < cl.time || !cl_dlights[i].radius)
+                               continue;
+                       VectorSubtract (center, cl_dlights[i].origin, dist);
+                       if ((t2 = DotProduct(dist,dist)) + 64.0f < (t1 = cl_dlights[i].radius*cl_dlights[i].radius))
+                       {
+                               dist[0] = cl_dlights[i].color[0] * cl_dlights[i].radius * cl_dlights[i].radius * 0.5f;
+                               dist[1] = cl_dlights[i].color[1] * cl_dlights[i].radius * cl_dlights[i].radius * 0.5f;
+                               dist[2] = cl_dlights[i].color[2] * cl_dlights[i].radius * cl_dlights[i].radius * 0.5f;
+                               if (cl_dlights[i].dark)
+                               {
+                                       dist[0] = -dist[0];
+                                       dist[1] = -dist[1];
+                                       dist[2] = -dist[2];
+                               }
+                               if (lighthalf)
+                               {
+                                       dist[0] *= 0.5f;
+                                       dist[1] *= 0.5f;
+                                       dist[2] *= 0.5f;
+                               }
+                               t1 = 1.5f / t2;
+                               shadecolor[0] += dist[0] * t1;
+                               shadecolor[1] += dist[1] * t1;
+                               shadecolor[2] += dist[2] * t1;
+                       }
+               }
+       }
+       shadecolor[0] *= currententity->colormod[0];
+       shadecolor[1] *= currententity->colormod[1];
+       shadecolor[2] *= currententity->colormod[2];
+       t1 = bound(0, shadecolor[0], 255);r = (byte) t1;
+       t1 = bound(0, shadecolor[1], 255);g = (byte) t1;
+       t1 = bound(0, shadecolor[2], 255);b = (byte) t1;
+       if (nearlights)
+       {
+               int temp;
+               vec3_t v;
+               float *av;
+               av = aliasvert;
+               if (nearlights == 1)
+               {
+                       for (i = 0;i < numverts;i++)
+                       {
+                               VectorSubtract(nearlight[0].origin, av, v);
+                               t = DotProduct(avn,v);
+                               if (t > 0)
+                               {
+                                       t /= DotProduct(v,v);
+                                       temp = (int) ((float) (shadecolor[0] + nearlight[0].color[0] * t));if (temp < 0) temp = 0;else if (temp > 255) temp = 255;*avc++ = temp;
+                                       temp = (int) ((float) (shadecolor[1] + nearlight[0].color[1] * t));if (temp < 0) temp = 0;else if (temp > 255) temp = 255;*avc++ = temp;
+                                       temp = (int) ((float) (shadecolor[2] + nearlight[0].color[2] * t));if (temp < 0) temp = 0;else if (temp > 255) temp = 255;*avc++ = temp;
+                               }
+                               else
+                               {
+                                       *avc++ = r;
+                                       *avc++ = g;
+                                       *avc++ = b;
+                               }
+                               *avc++ = a;
+                               av+=3;
+                               avn+=3;
+                       }
+               }
+               else
+               {
+                       int i1, i2, i3;
+                       for (i = 0;i < numverts;i++)
+                       {
+                               t1 = shadecolor[0];
+                               t2 = shadecolor[1];
+                               t3 = shadecolor[2];
+                               for (j = 0;j < nearlights;j++)
+                               {
+                                       VectorSubtract(nearlight[j].origin, av, v);
+                                       t = DotProduct(avn,v);
+                                       if (t > 0)
+                                       {
+                                               t /= DotProduct(v,v);
+                                               t1 += nearlight[j].color[0] * t;
+                                               t2 += nearlight[j].color[1] * t;
+                                               t3 += nearlight[j].color[2] * t;
+                                       }
+                               }
+                               i1 = t1;if (i1 < 0) i1 = 0;else if (i1 > 255) i1 = 255;
+                               i2 = t2;if (i2 < 0) i2 = 0;else if (i2 > 255) i2 = 255;
+                               i3 = t3;if (i3 < 0) i3 = 0;else if (i3 > 255) i3 = 255;
+                               *avc++ = i1;
+                               *avc++ = i2;
+                               *avc++ = i3;
+                               *avc++ = a;
+                       }
+               }
+       }
+       else
+       {
+               for (i = 0;i < numverts;i++)
+               {
+                       *avc++ = r;
+                       *avc++ = g;
+                       *avc++ = b;
+                       *avc++ = a;
+               }
+       }
+}
diff --git a/r_part.c b/r_part.c
new file mode 100644 (file)
index 0000000..5c822d9
--- /dev/null
+++ b/r_part.c
@@ -0,0 +1,1550 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+#define MAX_PARTICLES                  2048    // default max # of particles at one
+                                                                               //  time
+#define ABSOLUTE_MIN_PARTICLES 512             // no fewer than this no matter what's
+                                                                               //  on the command line
+
+int            ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
+int            ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
+int            ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
+
+int            particletexture;
+int            smokeparticletexture;
+int            flareparticletexture;
+int            rainparticletexture;
+int            bloodcloudparticletexture;
+int            bubbleparticletexture;
+
+particle_t     *active_particles, *free_particles;
+
+particle_t     *particles;
+int                    r_numparticles;
+
+vec3_t                 r_pright, r_pup, r_ppn;
+
+//extern cvar_t r_particles/*, r_smoke*/, r_smokealpha;
+//cvar_t r_smokecolor = {"r_smokecolor", "0"};
+
+void fractalnoise(char *noise, int size);
+void fractalnoise_zeroedge(char *noise, int size);
+
+void R_InitParticleTexture (void)
+{
+       int             x,y,d;
+       float   dx, dy, dz, f, dot;
+       byte    data[64][64][4], noise1[64][64], noise2[64][64];
+       vec3_t  normal, light;
+
+       particletexture = texture_extension_number++;
+    glBindTexture(GL_TEXTURE_2D, particletexture);
+
+       for (x=0 ; x<64 ; x++)
+       {
+               for (y=0 ; y<64 ; y++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
+                       dx = x - 16;
+                       dy = y - 16;
+                       d = (255 - (dx*dx+dy*dy));
+                       if (d < 0) d = 0;
+                       data[y][x][3] = (byte) d;
+               }
+       }
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+
+       fractalnoise(&noise1[0][0], 64);
+       fractalnoise(&noise2[0][0], 64);
+       for (y = 0;y < 64;y++)
+               for (x = 0;x < 64;x++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
+                       dx = x - 16;
+                       dy = y - 16;
+                       d = (noise2[y][x] * (255 - (dx*dx+dy*dy))) * (1.0f / 255.0f);
+                       if (d < 0) d = 0;
+                       if (d > 255) d = 255;
+                       data[y][x][3] = (byte) d;
+               }
+
+       /*
+       for (x=0 ; x<34 ; x+=2)
+               for (y=0 ; y<34 ; y+=2)
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = (rand()%64)+192;
+       for (x=0 ; x<32 ; x+=2)
+               for (y=0 ; y<32 ; y+=2)
+               {
+                       data[y  ][x+1][0] = data[y  ][x+1][1] = data[y  ][x+1][2] = (int) (data[y  ][x  ][0] + data[y  ][x+2][0]) >> 1;
+                       data[y+1][x  ][0] = data[y+1][x  ][1] = data[y+1][x  ][2] = (int) (data[y  ][x  ][0] + data[y+2][x  ][0]) >> 1;
+                       data[y+1][x+1][0] = data[y+1][x+1][1] = data[y+1][x+1][2] = (int) (data[y  ][x  ][0] + data[y  ][x+2][0] + data[y+2][x  ][0] + data[y+2][x+2][0]) >> 2;
+               }
+       for (x=0 ; x<64 ; x++)
+       {
+               for (y=0 ; y<64 ; y++)
+               {
+                       //data[y][x][0] = data[y][x][1] = data[y][x][2] = (rand()%192)+64;
+                       dx = x - 16;
+                       dy = y - 16;
+                       d = (255 - (dx*dx+dy*dy));
+                       if (d < 0) d = 0;
+                       data[y][x][3] = (byte) d;
+               }
+       }
+       */
+       smokeparticletexture = texture_extension_number++;
+       glBindTexture(GL_TEXTURE_2D, smokeparticletexture);
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       fractalnoise(&noise1[0][0], 64);
+       fractalnoise(&noise2[0][0], 64);
+       for (y = 0;y < 64;y++)
+               for (x = 0;x < 64;x++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
+                       dx = x - 16;
+                       dy = y - 16;
+                       d = (noise2[y][x] * (255 - (dx*dx+dy*dy))) * (1.0f / 255.0f);
+                       if (d < 0) d = 0;
+                       if (d > 255) d = 255;
+                       data[y][x][3] = (byte) d;
+               }
+
+       bloodcloudparticletexture = texture_extension_number++;
+       glBindTexture(GL_TEXTURE_2D, bloodcloudparticletexture);
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       flareparticletexture = texture_extension_number++;
+    glBindTexture(GL_TEXTURE_2D, flareparticletexture);
+
+       for (x=0 ; x<64 ; x++)
+       {
+               for (y=0 ; y<64 ; y++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
+                       dx = x - 16;
+                       dy = y - 16;
+                       d = 2048 / (dx*dx+dy*dy+1) - 32;
+                       d = bound(0, d, 255);
+                       data[y][x][3] = (byte) d;
+               }
+       }
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       rainparticletexture = texture_extension_number++;
+    glBindTexture(GL_TEXTURE_2D, rainparticletexture);
+
+       for (x=0 ; x<64 ; x++)
+       {
+               for (y=0 ; y<64 ; y++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
+                       if (y < 24) // stretch the upper half to make a raindrop
+                       {
+                               dx = (x - 16)*2;
+                               dy = (y - 24)*2/3;
+                               d = (255 - (dx*dx+dy*dy))/2;
+                       }
+                       else
+                       {
+                               dx = (x - 16)*2;
+                               dy = (y - 24)*2;
+                               d = (255 - (dx*dx+dy*dy))/2;
+                       }
+                       if (d < 0) d = 0;
+                       data[y][x][3] = (byte) d;
+               }
+       }
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       bubbleparticletexture = texture_extension_number++;
+    glBindTexture(GL_TEXTURE_2D, bubbleparticletexture);
+
+       light[0] = 1;light[1] = 1;light[2] = 1;
+       VectorNormalize(light);
+       for (x=0 ; x<64 ; x++)
+       {
+               for (y=0 ; y<64 ; y++)
+               {
+                       data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
+                       dx = x * (1.0 / 16.0) - 1.0;
+                       dy = y * (1.0 / 16.0) - 1.0;
+                       if (dx*dx+dy*dy < 1) // it does hit the sphere
+                       {
+                               dz = 1 - (dx*dx+dy*dy);
+                               f = 0;
+                               // back side
+                               normal[0] = dx;normal[1] = dy;normal[2] = dz;
+                               VectorNormalize(normal);
+                               dot = DotProduct(normal, light);
+                               if (dot > 0.5) // interior reflection
+                                       f += ((dot *  2) - 1);
+                               else if (dot < -0.5) // exterior reflection
+                                       f += ((dot * -2) - 1);
+                               // front side
+                               normal[0] = dx;normal[1] = dy;normal[2] = -dz;
+                               VectorNormalize(normal);
+                               dot = DotProduct(normal, light);
+                               if (dot > 0.5) // interior reflection
+                                       f += ((dot *  2) - 1);
+                               else if (dot < -0.5) // exterior reflection
+                                       f += ((dot * -2) - 1);
+                               f *= 255;
+                               f = bound(0, f, 255);
+                               data[y][x][3] = (byte) d;
+                       }
+                       else
+                               data[y][x][3] = 0;
+               }
+       }
+       glTexImage2D (GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+/*
+===============
+R_InitParticles
+===============
+*/
+void R_InitParticles (void)
+{
+       int             i;
+
+       i = COM_CheckParm ("-particles");
+
+       if (i)
+       {
+               r_numparticles = (int)(atoi(com_argv[i+1]));
+               if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
+                       r_numparticles = ABSOLUTE_MIN_PARTICLES;
+       }
+       else
+       {
+               r_numparticles = MAX_PARTICLES;
+       }
+
+       particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles");
+
+//     Cvar_RegisterVariable (&r_smokecolor);
+       R_InitParticleTexture ();
+}
+
+/*
+===============
+R_EntityParticles
+===============
+*/
+
+#define NUMVERTEXNORMALS       162
+extern float   r_avertexnormals[NUMVERTEXNORMALS][3];
+vec3_t avelocities[NUMVERTEXNORMALS];
+float  beamlength = 16;
+vec3_t avelocity = {23, 7, 3};
+float  partstep = 0.01;
+float  timescale = 0.01;
+
+void R_EntityParticles (entity_t *ent)
+{
+       int                     count;
+       int                     i;
+       particle_t      *p;
+       float           angle;
+       float           sr, sp, sy, cr, cp, cy;
+       vec3_t          forward;
+       float           dist;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+       
+       dist = 64;
+       count = 50;
+
+if (!avelocities[0][0])
+{
+for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
+avelocities[0][i] = (rand()&255) * 0.01;
+}
+
+
+       for (i=0 ; i<NUMVERTEXNORMALS ; i++)
+       {
+               angle = cl.time * avelocities[i][0];
+               sy = sin(angle);
+               cy = cos(angle);
+               angle = cl.time * avelocities[i][1];
+               sp = sin(angle);
+               cp = cos(angle);
+               angle = cl.time * avelocities[i][2];
+               sr = sin(angle);
+               cr = cos(angle);
+       
+               forward[0] = cp*cy;
+               forward[1] = cp*sy;
+               forward[2] = -sp;
+
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = flareparticletexture;
+               p->scale = 2;
+               p->alpha = 255;
+               p->die = cl.time + 0.01;
+               p->color = 0x6f;
+               p->type = pt_explode;
+               
+               p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength;                       
+               p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength;                       
+               p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength;                       
+       }
+}
+
+
+/*
+===============
+R_ClearParticles
+===============
+*/
+void R_ClearParticles (void)
+{
+       int             i;
+       
+       free_particles = &particles[0];
+       active_particles = NULL;
+
+       for (i=0 ;i<r_numparticles ; i++)
+               particles[i].next = &particles[i+1];
+       particles[r_numparticles-1].next = NULL;
+}
+
+
+void R_ReadPointFile_f (void)
+{
+       FILE    *f;
+       vec3_t  org;
+       int             r;
+       int             c;
+       particle_t      *p;
+       char    name[MAX_OSPATH];
+       
+       sprintf (name,"maps/%s.pts", sv.name);
+
+       COM_FOpenFile (name, &f, false);
+       if (!f)
+       {
+               Con_Printf ("couldn't open %s\n", name);
+               return;
+       }
+       
+       Con_Printf ("Reading %s...\n", name);
+       c = 0;
+       for ( ;; )
+       {
+               r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
+               if (r != 3)
+                       break;
+               c++;
+               
+               if (!free_particles)
+               {
+                       Con_Printf ("Not enough free particles\n");
+                       break;
+               }
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+               
+               p->texnum = particletexture;
+               p->scale = 2;
+               p->alpha = 255;
+               p->die = 99999;
+               p->color = (-c)&15;
+               p->type = pt_static;
+               VectorCopy (vec3_origin, p->vel);
+               VectorCopy (org, p->org);
+       }
+
+       fclose (f);
+       Con_Printf ("%i points read\n", c);
+}
+
+/*
+===============
+R_ParseParticleEffect
+
+Parse an effect out of the server message
+===============
+*/
+void R_ParseParticleEffect (void)
+{
+       vec3_t          org, dir;
+       int                     i, count, msgcount, color;
+       
+       for (i=0 ; i<3 ; i++)
+               org[i] = MSG_ReadCoord ();
+       for (i=0 ; i<3 ; i++)
+               dir[i] = MSG_ReadChar () * (1.0/16);
+       msgcount = MSG_ReadByte ();
+       color = MSG_ReadByte ();
+
+if (msgcount == 255)
+       count = 1024;
+else
+       count = msgcount;
+       
+       R_RunParticleEffect (org, dir, color, count);
+}
+       
+/*
+===============
+R_ParticleExplosion
+
+===============
+*/
+void R_ParticleExplosion (vec3_t org, int smoke)
+{
+       int                     i, j;
+       particle_t      *p;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+       
+       for (i=0 ; i<1024 ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = flareparticletexture;
+               p->scale = 4+(rand()&7);
+               p->alpha = rand()&255;
+               p->die = cl.time + 5;
+               p->color = ramp1[0];
+               p->ramp = rand()&3;
+               /*
+               if (i & 1)
+                       p->type = pt_explode;
+               else
+                       p->type = pt_explode2;
+               */
+               p->color = ramp1[rand()&7];
+               p->type = pt_fallfadespark;
+               for (j=0 ; j<3 ; j++)
+               {
+                       p->org[j] = org[j] + ((rand()&63)-32);
+                       p->vel[j] = (rand()&511)-256;
+               }
+               p->vel[j] += 200;
+       }
+
+       if (smoke)
+       {
+               for (i=0 ; i<32 ; i++)
+               {
+                       if (!free_particles)
+                               return;
+                       p = free_particles;
+                       free_particles = p->next;
+                       p->next = active_particles;
+                       active_particles = p;
+
+                       p->texnum = smokeparticletexture;
+                       p->scale = 24;
+                       p->alpha = 80;
+                       p->die = cl.time + 2;
+                       p->type = pt_smoke;
+                       p->color = (rand()&7) + 8;
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               p->org[j] = org[j] + ((rand()%96)-48);
+                               p->vel[j] = (rand()&63)-32;
+                       }
+               }
+       }
+}
+
+/*
+===============
+R_ParticleExplosion2
+
+===============
+*/
+void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
+{
+       int                     i, j;
+       particle_t      *p;
+       int                     colorMod = 0;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       for (i=0; i<512; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = flareparticletexture;
+               p->scale = 8;
+               p->alpha = 255;
+               p->die = cl.time + 0.3;
+               p->color = colorStart + (colorMod % colorLength);
+               colorMod++;
+
+               p->type = pt_blob;
+               for (j=0 ; j<3 ; j++)
+               {
+                       p->org[j] = org[j] + ((rand()%32)-16);
+                       p->vel[j] = (rand()%512)-256;
+               }
+       }
+}
+
+/*
+===============
+R_BlobExplosion
+
+===============
+*/
+void R_BlobExplosion (vec3_t org)
+{
+       int                     i, j;
+       particle_t      *p;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+       
+       for (i=0 ; i<1024 ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = flareparticletexture;
+               p->scale = 8;
+               p->alpha = 255;
+               p->die = cl.time + 1 + (rand()&8)*0.05;
+
+               if (i & 1)
+               {
+                       p->type = pt_blob;
+                       p->color = 66 + rand()%6;
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               p->org[j] = org[j] + ((rand()%32)-16);
+                               p->vel[j] = (rand()%512)-256;
+                       }
+               }
+               else
+               {
+                       p->type = pt_blob2;
+                       p->color = 150 + rand()%6;
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               p->org[j] = org[j] + ((rand()%32)-16);
+                               p->vel[j] = (rand()%512)-256;
+                       }
+               }
+       }
+}
+
+/*
+===============
+R_RunParticleEffect
+
+===============
+*/
+void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
+{
+       int                     i, j;
+       particle_t      *p;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+       
+       for (i=0 ; i<count ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               if (count == 1024)
+               {       // rocket explosion
+                       p->texnum = flareparticletexture;
+                       p->scale = 8;
+                       p->alpha = 255;
+                       p->die = cl.time + 5;
+                       p->color = ramp1[0];
+                       p->ramp = rand()&3;
+                       if (i & 1)
+                       {
+                               p->type = pt_explode;
+                               for (j=0 ; j<3 ; j++)
+                               {
+                                       p->org[j] = org[j] + ((rand()%32)-16);
+                                       p->vel[j] = (rand()%512)-256;
+                               }
+                       }
+                       else
+                       {
+                               p->type = pt_explode2;
+                               for (j=0 ; j<3 ; j++)
+                               {
+                                       p->org[j] = org[j] + ((rand()%32)-16);
+                                       p->vel[j] = (rand()%512)-256;
+                               }
+                       }
+               }
+               else
+               {
+                       p->texnum = flareparticletexture;
+                       p->scale = 8;
+                       p->alpha = 255;
+                       p->die = cl.time + 0.1*(rand()%5);
+                       p->color = (color&~7) + (rand()&7);
+                       p->type = pt_static; //slowgrav;
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               p->org[j] = org[j] + ((rand()&15)-8);
+                               p->vel[j] = dir[j]*15;// + (rand()%300)-150;
+                       }
+               }
+       }
+}
+
+// LordHavoc: added this for spawning sparks/dust (which have strong gravity)
+/*
+===============
+R_SparkShower
+
+===============
+*/
+void R_SparkShower (vec3_t org, vec3_t dir, int count, int type)
+{
+       int                     i, j;
+       particle_t      *p;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       if (!free_particles)
+               return;
+       p = free_particles;
+       free_particles = p->next;
+       p->next = active_particles;
+       active_particles = p;
+       if (type == 0) // sparks
+       {
+               p->texnum = smokeparticletexture;
+               p->scale = 20;
+               p->alpha = 64;
+               p->color = (rand()&3)+12;
+               p->type = pt_bulletpuff;
+               p->die = cl.time + 1;
+               VectorCopy(org, p->org);
+               p->vel[0] = p->vel[1] = p->vel[2] = 0;
+       }
+       else // blood
+       {
+               p->texnum = bloodcloudparticletexture;
+               p->scale = 24;
+               p->alpha = 128;
+               p->color = (rand()&3)+68;
+               p->type = pt_bloodcloud;
+               p->die = cl.time + 0.5;
+               VectorCopy(org, p->org);
+               p->vel[0] = p->vel[1] = p->vel[2] = 0;
+               return;
+       }
+       for (i=0 ; i<count ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = flareparticletexture;
+               p->scale = 5;
+               p->alpha = 255;
+               p->die = cl.time + 0.0625 * (rand()&15);
+               /*
+               if (type == 0) // sparks
+               {
+               */
+                       p->type = pt_dust;
+                       p->ramp = (rand()&3);
+                       p->color = ramp1[(int)p->ramp];
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               p->org[j] = org[j] + ((rand()&7)-4);
+                               p->vel[j] = dir[j] + (rand()%192)-96;
+                       }
+               /*
+               }
+               else // blood
+               {
+                       p->type = pt_fadespark2;
+                       p->color = 67 + (rand()&3);
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               p->org[j] = org[j] + (rand()&7)-4;
+                               p->vel[j] = dir[j] + (rand()&63)-32;
+                       }
+               }
+               */
+       }
+}
+
+void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
+{
+       int                     i, j;
+       particle_t      *p;
+       vec3_t          diff;
+       vec3_t          center;
+       vec3_t          velscale;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       VectorSubtract(maxs, mins, diff);
+       center[0] = (mins[0] + maxs[0]) * 0.5;
+       center[1] = (mins[1] + maxs[1]) * 0.5;
+       center[2] = (mins[2] + maxs[2]) * 0.5;
+       velscale[0] = velspeed * 2.0 / diff[0];
+       velscale[1] = velspeed * 2.0 / diff[1];
+       velscale[2] = velspeed * 2.0 / diff[2];
+       
+       for (i=0 ; i<count ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = bloodcloudparticletexture;
+               p->scale = 24;
+               p->alpha = 96 + (rand()&63);
+               p->die = cl.time + 2; //0.015625 * (rand()%128);
+               p->type = pt_fadespark;
+               p->color = (rand()&3)+68;
+//             p->color = 67 + (rand()&3);
+               for (j=0 ; j<3 ; j++)
+               {
+                       p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
+                       p->vel[j] = (p->org[j] - center[j]) * velscale[j];
+               }
+       }
+}
+
+void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
+{
+       int                     i, j;
+       particle_t      *p;
+       vec3_t          diff;
+       float           t;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+       if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
+       if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
+       if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
+
+       VectorSubtract(maxs, mins, diff);
+       
+       for (i=0 ; i<count ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               p->texnum = flareparticletexture;
+               p->scale = 12;
+               p->alpha = 255;
+               p->die = cl.time + 1 + (rand()&15)*0.0625;
+               if (gravity)
+                       p->type = pt_grav;
+               else
+                       p->type = pt_static;
+               p->color = colorbase + (rand()&3);
+               for (j=0 ; j<3 ; j++)
+               {
+                       p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
+                       if (randomvel)
+                               p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
+                       else
+                               p->vel[j] = 0;
+               }
+       }
+}
+
+void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
+{
+       int                     i;
+       particle_t      *p;
+       vec3_t          diff;
+       vec3_t          org;
+       vec3_t          vel;
+       float           t, z;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+       if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
+       if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
+       if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
+       if (dir[2] < 0) // falling
+       {
+               t = (maxs[2] - mins[2]) / -dir[2];
+               z = maxs[2];
+       }
+       else // rising??
+       {
+               t = (maxs[2] - mins[2]) / dir[2];
+               z = mins[2];
+       }
+       if (t < 0 || t > 2) // sanity check
+               t = 2;
+       t += cl.time;
+
+       VectorSubtract(maxs, mins, diff);
+       
+       for (i=0 ; i<count ; i++)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+
+               vel[0] = dir[0] + (rand()&31) - 16;
+               vel[1] = dir[1] + (rand()&31) - 16;
+               vel[2] = dir[2] + (rand()&63) - 32;
+               org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
+               org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
+               org[2] = z;
+
+               p->scale = 6;
+               p->alpha = 255;
+               p->die = t;
+               if (type == 1)
+               {
+                       p->texnum = particletexture;
+                       p->type = pt_snow;
+               }
+               else // 0
+               {
+                       p->texnum = rainparticletexture;
+                       p->type = pt_static;
+               }
+               p->color = colorbase + (rand()&3);
+               VectorCopy(org, p->org);
+               VectorCopy(vel, p->vel);
+       }
+}
+
+
+/*
+===============
+R_LavaSplash
+
+===============
+*/
+void R_LavaSplash (vec3_t org)
+{
+       int                     i, j, k;
+       particle_t      *p;
+       float           vel;
+       vec3_t          dir;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       for (i=-16 ; i<16 ; i+=2)
+               for (j=-16 ; j<16 ; j+=2)
+                       for (k=0 ; k<1 ; k++)
+                       {
+                               if (!free_particles)
+                                       return;
+                               p = free_particles;
+                               free_particles = p->next;
+                               p->next = active_particles;
+                               active_particles = p;
+               
+                               p->texnum = flareparticletexture;
+                               p->scale = 24;
+                               p->alpha = 255;
+                               p->die = cl.time + 2 + (rand()&31) * 0.02;
+                               p->color = 224 + (rand()&7);
+                               p->type = pt_slowgrav;
+                               
+                               dir[0] = j*8 + (rand()&7);
+                               dir[1] = i*8 + (rand()&7);
+                               dir[2] = 256;
+       
+                               p->org[0] = org[0] + dir[0];
+                               p->org[1] = org[1] + dir[1];
+                               p->org[2] = org[2] + (rand()&63);
+       
+                               VectorNormalize (dir);                                          
+                               vel = 50 + (rand()&63);
+                               VectorScale (dir, vel, p->vel);
+                       }
+}
+
+/*
+===============
+R_TeleportSplash
+
+===============
+*/
+void R_TeleportSplash (vec3_t org)
+{
+       int                     i, j, k;
+       particle_t      *p;
+//     vec3_t          dir;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       /*
+       for (i=-16 ; i<16 ; i+=4)
+               for (j=-16 ; j<16 ; j+=4)
+                       for (k=-24 ; k<32 ; k+=4)
+                       {
+                               if (!free_particles)
+                                       return;
+                               p = free_particles;
+                               free_particles = p->next;
+                               p->next = active_particles;
+                               active_particles = p;
+               
+                               p->texnum = particletexture;
+                               p->scale = 2;
+                               p->alpha = 255;
+                               p->die = cl.time + 0.2 + (rand()&7) * 0.02;
+                               p->color = 7 + (rand()&7);
+                               p->type = pt_slowgrav;
+                               
+                               dir[0] = j*8;
+                               dir[1] = i*8;
+                               dir[2] = k*8;
+       
+                               p->org[0] = org[0] + i + (rand()&3);
+                               p->org[1] = org[1] + j + (rand()&3);
+                               p->org[2] = org[2] + k + (rand()&3);
+       
+                               VectorNormalize (dir);                                          
+                               vel = 50 + (rand()&63);
+                               VectorScale (dir, vel, p->vel);
+                       }
+       */
+
+       for (i=-24 ; i<24 ; i+=8)
+               for (j=-24 ; j<24 ; j+=8)
+                       for (k=-24 ; k<32 ; k+=8)
+                       {
+                               if (!free_particles)
+                                       return;
+                               p = free_particles;
+                               free_particles = p->next;
+                               p->next = active_particles;
+                               active_particles = p;
+               
+                               p->texnum = flareparticletexture;
+                               p->scale = 8;
+                               p->alpha = (1 + rand()&7) * 32;
+                               p->die = cl.time + 5;
+                               p->color = 254; //8 + (rand()&7);
+                               p->type = pt_fadespark;
+                               
+                               p->org[0] = org[0] + i + (rand()&7);
+                               p->org[1] = org[1] + j + (rand()&7);
+                               p->org[2] = org[2] + k + (rand()&7);
+       
+                               p->vel[0] = i*2 + (rand()%25) - 12;
+                               p->vel[1] = j*2 + (rand()%25) - 12;
+                               p->vel[2] = k*2 + (rand()%25) - 12 + 40;
+                       }
+}
+
+void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
+{
+       vec3_t          vec;
+       float           len, dec, t, nt, speed;
+       int                     j, contents, bubbles;
+       particle_t      *p;
+       static int      tracercount;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       t = cl.oldtime;
+       nt = cl.time;
+       if (ent->trail_leftover < 0)
+               ent->trail_leftover = 0;
+       t += ent->trail_leftover;
+       ent->trail_leftover -= (cl.time - cl.oldtime);
+       if (t >= cl.time)
+               return;
+
+       contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
+       if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
+               return;
+
+       VectorSubtract (end, start, vec);
+       len = VectorNormalizeLength (vec);
+       if (len <= 0.01f)
+               return;
+       speed = len / (nt - t);
+
+       bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
+
+       while (t < nt)
+       {
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+               
+               p->vel[0] = p->vel[1] = p->vel[2] = 0;
+               p->die = cl.time + 2;
+
+               switch (type)
+               {
+                       case 0: // rocket trail
+                       case 1: // grenade trail
+                               if (bubbles)
+                               {
+                                       dec = 0.01f;
+                                       p->texnum = bubbleparticletexture;
+                                       p->scale = 6+(rand()&3);
+                                       p->alpha = 255;
+//                                     if (r_smokecolor.value)
+//                                             p->color = r_smokecolor.value;
+//                                     else
+                                               p->color = (rand()&3)+12;
+                                       p->type = pt_bubble;
+                                       p->die = cl.time + 2;
+                                       for (j=0 ; j<3 ; j++)
+                                       {
+                                               p->vel[j] = (rand()&31)-16;
+                                               p->org[j] = start[j] + ((rand()&3)-2);
+                                       }
+                               }
+                               else
+                               {
+                                       dec = 0.03f;
+                                       p->texnum = smokeparticletexture;
+                                       p->scale = 12+(rand()&7);
+                                       p->alpha = 64 + (rand()&31);
+//                                     if (r_smokecolor.value)
+//                                             p->color = r_smokecolor.value;
+//                                     else
+                                               p->color = (rand()&3)+12;
+                                       p->type = pt_smoke;
+                                       p->die = cl.time + 2;
+                                       VectorCopy(start, p->org);
+                               }
+                               break;
+
+                               /*
+                       case 1: // smoke smoke
+                               dec = 0.016f;
+                               p->texnum = smokeparticletexture;
+                               p->scale = 12+rand()&7;
+                               p->alpha = 64;
+                               if (r_smokecolor.value)
+                                       p->color = r_smokecolor.value;
+                               else
+                                       p->color = (rand()&3)+12;
+                               p->type = pt_smoke;
+                               p->die = cl.time + 1;
+                               VectorCopy(start, p->org);
+                               break;
+                               */
+
+                       case 2: // blood
+                               dec = 0.03f;
+                               p->texnum = bloodcloudparticletexture;
+                               p->scale = 20+(rand()&7);
+                               p->alpha = 255;
+                               p->color = (rand()&3)+68;
+                               p->type = pt_bloodcloud;
+                               p->die = cl.time + 2;
+                               for (j=0 ; j<3 ; j++)
+                               {
+                                       p->vel[j] = (rand()&15)-8;
+                                       p->org[j] = start[j] + ((rand()&3)-2);
+                               }
+                               break;
+
+                       case 3:
+                       case 5: // tracer
+                               dec = 0.01f;
+                               p->texnum = flareparticletexture;
+                               p->scale = 4;
+                               p->alpha = 255;
+                               p->die = cl.time + 0.2; //5;
+                               p->type = pt_static;
+                               if (type == 3)
+                                       p->color = 52 + ((tracercount&4)<<1);
+                               else
+                                       p->color = 230 + ((tracercount&4)<<1);
+
+                               tracercount++;
+
+                               VectorCopy (start, p->org);
+                               if (tracercount & 1)
+                               {
+                                       p->vel[0] = 30*vec[1];
+                                       p->vel[1] = 30*-vec[0];
+                               }
+                               else
+                               {
+                                       p->vel[0] = 30*-vec[1];
+                                       p->vel[1] = 30*vec[0];
+                               }
+                               break;
+
+                       case 4: // slight blood
+                               dec = 0.03f; // sparse trail
+                               p->texnum = bloodcloudparticletexture;
+                               p->scale = 20+(rand()&7);
+                               p->alpha = 255;
+                               p->color = (rand()&3)+68;
+                               p->type = pt_fadespark2;
+                               p->die = cl.time + 2;
+                               for (j=0 ; j<3 ; j++)
+                               {
+                                       p->vel[j] = (rand()&15)-8;
+                                       p->org[j] = start[j] + ((rand()&3)-2);
+                               }
+                               break;
+
+                       case 6: // voor trail
+                               dec = 0.05f; // sparse trail
+                               p->texnum = flareparticletexture;
+                               p->scale = 20+(rand()&7);
+                               p->alpha = 255;
+                               p->color = 9*16 + 8 + (rand()&3);
+                               p->type = pt_fadespark2;
+                               p->die = cl.time + 2;
+                               for (j=0 ; j<3 ; j++)
+                               {
+                                       p->vel[j] = (rand()&15)-8;
+                                       p->org[j] = start[j] + ((rand()&3)-2);
+                               }
+                               break;
+
+                       case 7: // Nehahra smoke tracer
+                               dec = 0.14f;
+                               p->texnum = smokeparticletexture;
+                               p->scale = 12+(rand()&7);
+                               p->alpha = 64;
+                               p->color = (rand()&3)+12;
+                               p->type = pt_smoke;
+                               p->die = cl.time + 1;
+                               for (j=0 ; j<3 ; j++)
+                                       p->org[j] = start[j] + ((rand()&3)-2);
+                               break;
+               }
+               
+               t += dec;
+               dec *= speed;
+               VectorMA (start, dec, vec, start);
+       }
+       ent->trail_leftover = t - cl.time;
+}
+
+void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
+{
+       vec3_t          vec;
+       float           len;
+       particle_t      *p;
+       static int      tracercount;
+//     if (!r_particles.value) return; // LordHavoc: particles are optional
+
+       VectorSubtract (end, start, vec);
+       len = VectorNormalizeLength (vec);
+       while (len > 0)
+       {
+               len -= 3;
+
+               if (!free_particles)
+                       return;
+               p = free_particles;
+               free_particles = p->next;
+               p->next = active_particles;
+               active_particles = p;
+               
+               VectorCopy (vec3_origin, p->vel);
+
+               p->texnum = flareparticletexture;
+               p->scale = 16;
+               p->alpha = 192;
+               p->color = color;
+               p->type = pt_smoke;
+               p->die = cl.time + 1;
+               VectorCopy(start, p->org);
+//             for (j=0 ; j<3 ; j++)
+//                     p->org[j] = start[j] + ((rand()&15)-8);
+
+               VectorAdd (start, vec, start);
+       }
+}
+
+
+//extern qboolean isG200, isATI, isRagePro;
+extern qboolean lighthalf;
+
+/*
+===============
+R_DrawParticles
+===============
+*/
+extern cvar_t  sv_gravity;
+
+void R_DrawParticles (void)
+{
+       particle_t              *p, *kill;
+       int                             i, /*texnum, */r,g,b,a;
+       float                   grav, grav1, time1, time2, time3, dvel, frametime, scale, scale2;
+       byte                    *color24;
+       vec3_t                  up, right, uprightangles, forward2, up2, right2, v;
+
+       // LordHavoc: early out condition
+       if (!active_particles)
+               return;
+
+       /*
+       texnum = particletexture;
+    glBindTexture(GL_TEXTURE_2D, texnum);
+       glEnable (GL_BLEND);
+       // LordHavoc: Matrox G200 cards can't handle per pixel alpha at all...
+       // and ATI Rage Pro can't modulate a per pixel alpha texture
+       if (isG200 || isRagePro)
+               glEnable(GL_ALPHA_TEST);
+       else
+               glDisable(GL_ALPHA_TEST);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glDepthMask(0); // disable zbuffer updates
+       glShadeModel(GL_FLAT);
+       glBegin (GL_TRIANGLES);
+       */
+
+       VectorScale (vup, 1.5, up);
+       VectorScale (vright, 1.5, right);
+
+       uprightangles[0] = 0;
+       uprightangles[1] = r_refdef.viewangles[1];
+       uprightangles[2] = 0;
+       AngleVectors (uprightangles, forward2, right2, up2);
+
+       frametime = cl.time - cl.oldtime;
+       time3 = frametime * 15;
+       time2 = frametime * 10; // 15;
+       time1 = frametime * 5;
+       grav = (grav1 = frametime * sv_gravity.value) * 0.05;
+       dvel = 1+4*frametime;
+
+       for ( ;; ) 
+       {
+               kill = active_particles;
+               if (kill && kill->die < cl.time)
+               {
+                       active_particles = kill->next;
+                       kill->next = free_particles;
+                       free_particles = kill;
+                       continue;
+               }
+               break;
+       }
+
+       for (p=active_particles ; p ; p=p->next)
+       {
+               for ( ;; )
+               {
+                       kill = p->next;
+                       if (kill && kill->die < cl.time)
+                       {
+                               p->next = kill->next;
+                               kill->next = free_particles;
+                               free_particles = kill;
+                               continue;
+                       }
+                       break;
+               }
+               // LordHavoc: 'removed last in list' condition
+               if (!p)
+                       break;
+
+               VectorSubtract(p->org, r_refdef.vieworg, v);
+               if (DotProduct(v, v) >= 256.0f)
+               {
+                       scale = p->scale * -0.25;scale2 = p->scale * 0.75;
+                       /*
+                       if (p->texnum != texnum)
+                       {
+                               texnum = p->texnum;
+                               glEnd();
+                               glBindTexture(GL_TEXTURE_2D, texnum);
+                               glBegin(GL_TRIANGLES);
+                       }
+                       if (lighthalf)
+                       {
+                               color24 = (byte *)&d_8to24table[(int)p->color];
+                               if (p->texnum == smokeparticletexture)
+                                       glColor4ub((byte) (color24[0] >> 1), (byte) (color24[1] >> 1), (byte) (color24[2] >> 1), (byte) (p->alpha*r_smokealpha.value));
+                               else
+                                       glColor4ub((byte) (color24[0] >> 1), (byte) (color24[1] >> 1), (byte) (color24[2] >> 1), (byte) p->alpha);
+                       }
+                       else
+                       {
+                               color24 = (byte *) &d_8to24table[(int)p->color];
+                               if (p->texnum == smokeparticletexture)
+                                       glColor4ub(color24[0], color24[1], color24[2], (byte) (p->alpha*r_smokealpha.value));
+                               else
+                                       glColor4ub(color24[0], color24[1], color24[2], (byte) p->alpha);
+                       }
+                       if (p->texnum == rainparticletexture) // rain streak
+                       {
+                               glTexCoord2f (0,0);
+                               glVertex3f (p->org[0] + right2[0]*scale , p->org[1] + right2[1]*scale , p->org[2] + right2[2]*scale );
+                               glTexCoord2f (1,0);
+                               glVertex3f (p->org[0] + right2[0]*scale , p->org[1] + right2[1]*scale , p->org[2] + right2[2]*scale );
+                               glTexCoord2f (0,1);
+                               glVertex3f (p->org[0] + right2[0]*scale2, p->org[1] + right2[1]*scale2, p->org[2] + right2[2]*scale2);
+                       }
+                       else
+                       {
+                               glTexCoord2f (0,0);
+                               // LordHavoc: centered particle sprites
+                               glVertex3f (p->org[0] + up[0]*scale  + right[0]*scale , p->org[1] + up[1]*scale  + right[1]*scale , p->org[2] + up[2]*scale  + right[2]*scale );
+                               glTexCoord2f (1,0);
+                               glVertex3f (p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale );
+                               glTexCoord2f (0,1);
+                               glVertex3f (p->org[0] + up[0]*scale  + right[0]*scale2, p->org[1] + up[1]*scale  + right[1]*scale2, p->org[2] + up[2]*scale  + right[2]*scale2);
+                       }
+                       */
+                       color24 = (byte *) &d_8to24table[(int)p->color];
+                       r = color24[0];
+                       g = color24[1];
+                       b = color24[2];
+                       a = p->alpha;
+                       if (lighthalf)
+                       {
+                               r >>= 1;
+                               g >>= 1;
+                               b >>= 1;
+                       }
+                       transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
+                       if (p->texnum == rainparticletexture) // rain streak
+                       {
+                               transpolyvert(p->org[0] + right2[0]*scale , p->org[1] + right2[1]*scale , p->org[2] + right2[2]*scale , 0,0,r,g,b,a);
+                               transpolyvert(p->org[0] + right2[0]*scale , p->org[1] + right2[1]*scale , p->org[2] + right2[2]*scale , 1,0,r,g,b,a);
+                               transpolyvert(p->org[0] + right2[0]*scale2, p->org[1] + right2[1]*scale2, p->org[2] + right2[2]*scale2, 0,1,r,g,b,a);
+                       }
+                       else
+                       {
+                               transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale , p->org[1] + up[1]*scale  + right[1]*scale , p->org[2] + up[2]*scale  + right[2]*scale , 0,0,r,g,b,a);
+                               transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale , 1,0,r,g,b,a);
+                               transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale2, p->org[1] + up[1]*scale  + right[1]*scale2, p->org[2] + up[2]*scale  + right[2]*scale2, 0,1,r,g,b,a);
+                       }
+                       transpolyend();
+               }
+
+               p->org[0] += p->vel[0]*frametime;
+               p->org[1] += p->vel[1]*frametime;
+               p->org[2] += p->vel[2]*frametime;
+               
+               switch (p->type)
+               {
+               case pt_static:
+                       break;
+               case pt_fire:
+                       p->ramp += time1;
+                       if (p->ramp >= 6)
+                               p->die = -1;
+                       else
+                               p->color = ramp3[(int)p->ramp];
+                       p->vel[2] += grav;
+                       break;
+
+               case pt_explode:
+                       p->ramp += time2;
+                       if (p->ramp >=8)
+                               p->die = -1;
+                       else
+                               p->color = ramp1[(int)p->ramp];
+//                     p->vel[2] -= grav1; // LordHavoc: apply full gravity to explosion sparks
+                       for (i=0 ; i<3 ; i++)
+                               p->vel[i] *= dvel;
+//                     p->vel[2] -= grav;
+                       break;
+
+               case pt_explode2:
+                       p->ramp += time3;
+                       if (p->ramp >=8)
+                               p->die = -1;
+                       else
+                               p->color = ramp2[(int)p->ramp];
+//                     p->vel[2] -= grav1; // LordHavoc: apply full gravity to explosion sparks
+                       for (i=0 ; i<3 ; i++)
+                               p->vel[i] -= p->vel[i]*frametime;
+//                     p->vel[2] -= grav;
+                       break;
+
+               case pt_blob:
+                       for (i=0 ; i<3 ; i++)
+                               p->vel[i] += p->vel[i]*dvel;
+                       p->vel[2] -= grav;
+                       break;
+
+               case pt_blob2:
+                       for (i=0 ; i<2 ; i++)
+                               p->vel[i] -= p->vel[i]*dvel;
+                       p->vel[2] -= grav;
+                       break;
+
+               case pt_grav:
+                       p->vel[2] -= grav1;
+                       break;
+               case pt_slowgrav:
+                       p->vel[2] -= grav;
+                       break;
+// LordHavoc: gunshot spark showers
+               case pt_dust:
+                       p->ramp += time1;
+                       p->scale -= frametime * 4;
+                       if (p->ramp >= 8 || p->scale <= 0)
+                               p->die = -1;
+                       else
+                               p->color = ramp3[(int)p->ramp];
+                       p->vel[2] -= grav1;
+                       break;
+// LordHavoc: for smoke trails
+               case pt_smoke:
+                       p->scale += frametime * 16;
+                       p->alpha -= frametime * 64;
+                       p->vel[2] += grav;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_snow:
+                       if (cl.time > p->time2)
+                       {
+                               p->time2 = cl.time + 0.4;
+                               p->vel[0] = (rand()&63)-32;
+                               p->vel[1] = (rand()&63)-32;
+                       }
+                       break;
+               case pt_bulletpuff:
+                       p->scale -= frametime * 64;
+                       p->alpha -= frametime * 1024;
+                       p->vel[2] -= grav;
+                       if (p->alpha < 1 || p->scale < 1)
+                               p->die = -1;
+                       break;
+               case pt_bloodcloud:
+                       p->scale -= frametime * 24;
+                       p->alpha -= frametime * 128;
+                       p->vel[2] -= grav;
+                       if (p->alpha < 1 || p->scale < 1)
+                               p->die = -1;
+                       break;
+               case pt_fadespark:
+                       p->alpha -= frametime * 256;
+                       p->vel[2] -= grav;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_fadespark2:
+                       p->alpha -= frametime * 512;
+                       p->vel[2] -= grav;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_fallfadespark:
+                       p->alpha -= frametime * 256;
+                       p->vel[2] -= grav1;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_fallfadespark2:
+                       p->alpha -= frametime * 512;
+                       p->vel[2] -= grav1;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_bubble:
+                       p->vel[2] += grav1;
+                       if (p->vel[2] >= 100)
+                               p->vel[2] = 68+rand()&31;
+                       if (cl.time > p->time2)
+                       {
+                               p->time2 = cl.time + (rand()&7)*0.0625;
+                               p->vel[0] = (rand()&63)-32;
+                               p->vel[1] = (rand()&63)-32;
+                       }
+                       p->alpha -= frametime * 32;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               }
+       }
+
+       /*
+       glEnd ();
+       glShadeModel(GL_SMOOTH);
+       glDepthMask(1); // enable zbuffer updates
+       glDisable (GL_BLEND);
+       */
+}
+
diff --git a/render.h b/render.h
new file mode 100644 (file)
index 0000000..78289b5
--- /dev/null
+++ b/render.h
@@ -0,0 +1,177 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// refresh.h -- public interface to refresh functions
+
+#define        MAXCLIPPLANES   11
+
+#define        TOP_RANGE               16                      // soldier uniform colors
+#define        BOTTOM_RANGE    96
+
+//=============================================================================
+
+typedef struct efrag_s
+{
+       struct mleaf_s          *leaf;
+       struct efrag_s          *leafnext;
+       struct entity_s         *entity;
+       struct efrag_s          *entnext;
+} efrag_t;
+
+
+typedef struct entity_s
+{
+       qboolean                                forcelink;              // model changed
+
+       int                                             update_type;
+
+       entity_state_t                  baseline;               // to fill in defaults in updates
+       entity_state_t                  deltabaseline;  // LordHavoc: previous frame
+
+       double                                  msgtime;                // time of last update
+       vec3_t                                  msg_origins[2]; // last two updates (0 is newest)       
+       vec3_t                                  origin;
+       vec3_t                                  msg_angles[2];  // last two updates (0 is newest)
+       vec3_t                                  angles; 
+
+       // LordHavoc: added support for alpha transprency and other effects
+       float                                   alpha;                  // opacity (alpha) of the model
+       float                                   colormod[3];    // color tint for model
+       float                                   scale;                  // size the model is shown
+       int                                             draw_lastpose, draw_pose; // for interpolation
+       float                                   draw_lerpstart; // for interpolation
+       struct model_s                  *draw_lastmodel; // for interpolation
+       float                                   trail_leftover;
+       float                                   glowsize;               // how big the glow is
+       byte                                    glowcolor;              // color of glow and particle trail (paletted)
+       byte                                    glowtrail;              // leaves a trail of particles
+       byte                                    isviewmodel;    // attached to view
+
+       struct model_s                  *model;                 // NULL = no model
+       struct efrag_s                  *efrag;                 // linked list of efrags
+       int                                             frame;
+       float                                   syncbase;               // for client-side animations
+       byte                                    *colormap;
+       int                                             effects;                // light, particals, etc
+       int                                             skinnum;                // for Alias models
+       int                                             visframe;               // last frame this entity was
+                                                                                       //  found in an active leaf
+                                                                                       
+       int                                             dlightframe;    // dynamic lighting
+       int                                             dlightbits[8];
+       
+// FIXME: could turn these into a union
+       int                                             trivial_accept;
+       struct mnode_s                  *topnode;               // for bmodels, first world node
+                                                                                       //  that splits bmodel, or NULL if
+                                                                                       //  not split
+} entity_t;
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct
+{
+       vrect_t         vrect;                          // subwindow in video for refresh
+                                                                       // FIXME: not need vrect next field here?
+       vrect_t         aliasvrect;                     // scaled Alias version
+       int                     vrectright, vrectbottom;        // right & bottom screen coords
+       int                     aliasvrectright, aliasvrectbottom;      // scaled Alias versions
+       float           vrectrightedge;                 // rightmost right edge we care about,
+                                                                               //  for use in edge list
+       float           fvrectx, fvrecty;               // for floating-point compares
+       float           fvrectx_adj, fvrecty_adj; // left and top edges, for clamping
+       int                     vrect_x_adj_shift20;    // (vrect.x + 0.5 - epsilon) << 20
+       int                     vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20
+       float           fvrectright_adj, fvrectbottom_adj;
+                                                                               // right and bottom edges, for clamping
+       float           fvrectright;                    // rightmost edge, for Alias clamping
+       float           fvrectbottom;                   // bottommost edge, for Alias clamping
+       float           horizontalFieldOfView;  // at Z = 1.0, this many X is visible 
+                                                                               // 2.0 = 90 degrees
+       float           xOrigin;                        // should probably allways be 0.5
+       float           yOrigin;                        // between be around 0.3 to 0.5
+
+       vec3_t          vieworg;
+       vec3_t          viewangles;
+       
+       float           fov_x, fov_y;
+
+       int                     ambientlight;
+} refdef_t;
+
+
+//
+// refresh
+//
+extern int             reinit_surfcache;
+
+
+extern refdef_t        r_refdef;
+extern vec3_t  r_origin, vpn, vright, vup;
+
+extern struct texture_s        *r_notexture_mip;
+
+// LordHavoc: generic image loader
+byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight);
+int loadtextureimage (int texnum, char* filename, qboolean complain, int matchwidth, int matchheight);
+
+void R_Init (void);
+void R_InitTextures (void);
+void R_InitEfrags (void);
+void R_RenderView (void);              // must set r_refdef first
+void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect);
+                                                               // called whenever r_refdef or vid change
+// LordHavoc: changed this for sake of GLQuake
+void R_InitSky (byte *src, int bytesperpixel); // called at level load
+//void R_InitSky (struct texture_s *mt);       // called at level load
+
+void R_AddEfrags (entity_t *ent);
+void R_RemoveEfrags (entity_t *ent);
+
+void R_NewMap (void);
+
+
+void R_ParseParticleEffect (void);
+void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
+void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent);
+void R_RocketTrail2 (vec3_t start, vec3_t end, int type, entity_t *ent);
+void R_SparkShower (vec3_t org, vec3_t dir, int count, int type);
+
+void R_EntityParticles (entity_t *ent);
+void R_BlobExplosion (vec3_t org);
+void R_ParticleExplosion (vec3_t org, int smoke);
+void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength);
+void R_LavaSplash (vec3_t org);
+void R_TeleportSplash (vec3_t org);
+
+void R_PushDlights (void);
+
+
+//
+// surface cache related
+//
+extern int             reinit_surfcache;       // if 1, surface cache is currently empty and
+extern qboolean        r_cache_thrash; // set if thrashing the surface cache
+
+int    D_SurfaceCacheForRes (int width, int height);
+void D_FlushCaches (void);
+void D_DeleteSurfaceCache (void);
+void D_InitCaches (void *buffer, int size);
+void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj);
+
diff --git a/resource.h b/resource.h
new file mode 100644 (file)
index 0000000..afabb2e
--- /dev/null
@@ -0,0 +1,20 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by winquake.rc
+//
+#define IDS_STRING1                     1
+#define IDI_ICON2                       1
+#define IDD_DIALOG1                     108
+#define IDD_PROGRESS                    109
+#define IDC_PROGRESS                    1000
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        111
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1004
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/sbar.c b/sbar.c
new file mode 100644 (file)
index 0000000..9fe505a
--- /dev/null
+++ b/sbar.c
@@ -0,0 +1,1269 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sbar.c -- status bar code
+
+#include "quakedef.h"
+
+
+//int                  sb_updates;             // if >= vid.numpages, no update needed
+
+#define STAT_MINUS             10      // num frame for '-' stats digit
+qpic_t         *sb_nums[2][11];
+qpic_t         *sb_colon, *sb_slash;
+qpic_t         *sb_ibar;
+qpic_t         *sb_sbar;
+qpic_t         *sb_scorebar;
+
+qpic_t      *sb_weapons[7][8];   // 0 is active, 1 is owned, 2-5 are flashes
+qpic_t      *sb_ammo[4];
+qpic_t         *sb_sigil[4];
+qpic_t         *sb_armor[3];
+qpic_t         *sb_items[32];
+
+qpic_t *sb_faces[7][2];                // 0 is gibbed, 1 is dead, 2-6 are alive
+                                                       // 0 is static, 1 is temporary animation
+qpic_t *sb_face_invis;
+qpic_t *sb_face_quad;
+qpic_t *sb_face_invuln;
+qpic_t *sb_face_invis_invuln;
+
+qboolean       sb_showscores;
+
+int                    sb_lines;                       // scan lines to draw
+
+qpic_t      *rsb_invbar[2];
+qpic_t      *rsb_weapons[5];
+qpic_t      *rsb_items[2];
+qpic_t      *rsb_ammo[3];
+qpic_t      *rsb_teambord;             // PGM 01/19/97 - team color border
+
+//MED 01/04/97 added two more weapons + 3 alternates for grenade launcher
+qpic_t      *hsb_weapons[7][5];   // 0 is active, 1 is owned, 2-5 are flashes
+//MED 01/04/97 added array to simplify weapon parsing
+int         hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};
+//MED 01/04/97 added hipnotic items array
+qpic_t      *hsb_items[2];
+
+void Sbar_MiniDeathmatchOverlay (void);
+void Sbar_DeathmatchOverlay (void);
+void M_DrawPic (int x, int y, qpic_t *pic);
+
+/*
+===============
+Sbar_ShowScores
+
+Tab key down
+===============
+*/
+void Sbar_ShowScores (void)
+{
+       if (sb_showscores)
+               return;
+       sb_showscores = true;
+//     sb_updates = 0;
+}
+
+/*
+===============
+Sbar_DontShowScores
+
+Tab key up
+===============
+*/
+void Sbar_DontShowScores (void)
+{
+       sb_showscores = false;
+//     sb_updates = 0;
+}
+
+/*
+===============
+Sbar_Changed
+===============
+*/
+//void Sbar_Changed (void)
+//{
+//     sb_updates = 0; // update next frame
+//}
+
+/*
+===============
+Sbar_Init
+===============
+*/
+void Sbar_Init (void)
+{
+       int             i;
+
+       for (i=0 ; i<10 ; i++)
+       {
+               sb_nums[0][i] = Draw_PicFromWad (va("num_%i",i));
+               sb_nums[1][i] = Draw_PicFromWad (va("anum_%i",i));
+       }
+
+       sb_nums[0][10] = Draw_PicFromWad ("num_minus");
+       sb_nums[1][10] = Draw_PicFromWad ("anum_minus");
+
+       sb_colon = Draw_PicFromWad ("num_colon");
+       sb_slash = Draw_PicFromWad ("num_slash");
+
+       sb_weapons[0][0] = Draw_PicFromWad ("inv_shotgun");
+       sb_weapons[0][1] = Draw_PicFromWad ("inv_sshotgun");
+       sb_weapons[0][2] = Draw_PicFromWad ("inv_nailgun");
+       sb_weapons[0][3] = Draw_PicFromWad ("inv_snailgun");
+       sb_weapons[0][4] = Draw_PicFromWad ("inv_rlaunch");
+       sb_weapons[0][5] = Draw_PicFromWad ("inv_srlaunch");
+       sb_weapons[0][6] = Draw_PicFromWad ("inv_lightng");
+
+       sb_weapons[1][0] = Draw_PicFromWad ("inv2_shotgun");
+       sb_weapons[1][1] = Draw_PicFromWad ("inv2_sshotgun");
+       sb_weapons[1][2] = Draw_PicFromWad ("inv2_nailgun");
+       sb_weapons[1][3] = Draw_PicFromWad ("inv2_snailgun");
+       sb_weapons[1][4] = Draw_PicFromWad ("inv2_rlaunch");
+       sb_weapons[1][5] = Draw_PicFromWad ("inv2_srlaunch");
+       sb_weapons[1][6] = Draw_PicFromWad ("inv2_lightng");
+
+       for (i=0 ; i<5 ; i++)
+       {
+               sb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_shotgun",i+1));
+               sb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_sshotgun",i+1));
+               sb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_nailgun",i+1));
+               sb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_snailgun",i+1));
+               sb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_rlaunch",i+1));
+               sb_weapons[2+i][5] = Draw_PicFromWad (va("inva%i_srlaunch",i+1));
+               sb_weapons[2+i][6] = Draw_PicFromWad (va("inva%i_lightng",i+1));
+       }
+
+       sb_ammo[0] = Draw_PicFromWad ("sb_shells");
+       sb_ammo[1] = Draw_PicFromWad ("sb_nails");
+       sb_ammo[2] = Draw_PicFromWad ("sb_rocket");
+       sb_ammo[3] = Draw_PicFromWad ("sb_cells");
+
+       sb_armor[0] = Draw_PicFromWad ("sb_armor1");
+       sb_armor[1] = Draw_PicFromWad ("sb_armor2");
+       sb_armor[2] = Draw_PicFromWad ("sb_armor3");
+
+       sb_items[0] = Draw_PicFromWad ("sb_key1");
+       sb_items[1] = Draw_PicFromWad ("sb_key2");
+       sb_items[2] = Draw_PicFromWad ("sb_invis");
+       sb_items[3] = Draw_PicFromWad ("sb_invuln");
+       sb_items[4] = Draw_PicFromWad ("sb_suit");
+       sb_items[5] = Draw_PicFromWad ("sb_quad");
+
+       sb_sigil[0] = Draw_PicFromWad ("sb_sigil1");
+       sb_sigil[1] = Draw_PicFromWad ("sb_sigil2");
+       sb_sigil[2] = Draw_PicFromWad ("sb_sigil3");
+       sb_sigil[3] = Draw_PicFromWad ("sb_sigil4");
+
+       sb_faces[4][0] = Draw_PicFromWad ("face1");
+       sb_faces[4][1] = Draw_PicFromWad ("face_p1");
+       sb_faces[3][0] = Draw_PicFromWad ("face2");
+       sb_faces[3][1] = Draw_PicFromWad ("face_p2");
+       sb_faces[2][0] = Draw_PicFromWad ("face3");
+       sb_faces[2][1] = Draw_PicFromWad ("face_p3");
+       sb_faces[1][0] = Draw_PicFromWad ("face4");
+       sb_faces[1][1] = Draw_PicFromWad ("face_p4");
+       sb_faces[0][0] = Draw_PicFromWad ("face5");
+       sb_faces[0][1] = Draw_PicFromWad ("face_p5");
+
+       sb_face_invis = Draw_PicFromWad ("face_invis");
+       sb_face_invuln = Draw_PicFromWad ("face_invul2");
+       sb_face_invis_invuln = Draw_PicFromWad ("face_inv2");
+       sb_face_quad = Draw_PicFromWad ("face_quad");
+
+       Cmd_AddCommand ("+showscores", Sbar_ShowScores);
+       Cmd_AddCommand ("-showscores", Sbar_DontShowScores);
+
+       sb_sbar = Draw_PicFromWad ("sbar");
+       sb_ibar = Draw_PicFromWad ("ibar");
+       sb_scorebar = Draw_PicFromWad ("scorebar");
+
+//MED 01/04/97 added new hipnotic weapons
+       if (hipnotic)
+       {
+         hsb_weapons[0][0] = Draw_PicFromWad ("inv_laser");
+         hsb_weapons[0][1] = Draw_PicFromWad ("inv_mjolnir");
+         hsb_weapons[0][2] = Draw_PicFromWad ("inv_gren_prox");
+         hsb_weapons[0][3] = Draw_PicFromWad ("inv_prox_gren");
+         hsb_weapons[0][4] = Draw_PicFromWad ("inv_prox");
+
+         hsb_weapons[1][0] = Draw_PicFromWad ("inv2_laser");
+         hsb_weapons[1][1] = Draw_PicFromWad ("inv2_mjolnir");
+         hsb_weapons[1][2] = Draw_PicFromWad ("inv2_gren_prox");
+         hsb_weapons[1][3] = Draw_PicFromWad ("inv2_prox_gren");
+         hsb_weapons[1][4] = Draw_PicFromWad ("inv2_prox");
+
+         for (i=0 ; i<5 ; i++)
+         {
+                hsb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_laser",i+1));
+                hsb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_mjolnir",i+1));
+                hsb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_gren_prox",i+1));
+                hsb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_prox_gren",i+1));
+                hsb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_prox",i+1));
+         }
+
+         hsb_items[0] = Draw_PicFromWad ("sb_wsuit");
+         hsb_items[1] = Draw_PicFromWad ("sb_eshld");
+       }
+
+       if (rogue)
+       {
+               rsb_invbar[0] = Draw_PicFromWad ("r_invbar1");
+               rsb_invbar[1] = Draw_PicFromWad ("r_invbar2");
+
+               rsb_weapons[0] = Draw_PicFromWad ("r_lava");
+               rsb_weapons[1] = Draw_PicFromWad ("r_superlava");
+               rsb_weapons[2] = Draw_PicFromWad ("r_gren");
+               rsb_weapons[3] = Draw_PicFromWad ("r_multirock");
+               rsb_weapons[4] = Draw_PicFromWad ("r_plasma");
+
+               rsb_items[0] = Draw_PicFromWad ("r_shield1");
+        rsb_items[1] = Draw_PicFromWad ("r_agrav1");
+
+// PGM 01/19/97 - team color border
+        rsb_teambord = Draw_PicFromWad ("r_teambord");
+// PGM 01/19/97 - team color border
+
+               rsb_ammo[0] = Draw_PicFromWad ("r_ammolava");
+               rsb_ammo[1] = Draw_PicFromWad ("r_ammomulti");
+               rsb_ammo[2] = Draw_PicFromWad ("r_ammoplasma");
+       }
+}
+
+
+//=============================================================================
+
+// drawing routines are relative to the status bar location
+
+/*
+=============
+Sbar_DrawPic
+=============
+*/
+void Sbar_DrawPic (int x, int y, qpic_t *pic)
+{
+       if (cl.gametype == GAME_DEATHMATCH)
+               Draw_Pic (x /* + ((vid.width - 320)>>1)*/, y + (vid.height-SBAR_HEIGHT), pic);
+       else
+               Draw_Pic (x + ((vid.width - 320)>>1), y + (vid.height-SBAR_HEIGHT), pic);
+}
+
+/*
+=============
+Sbar_DrawTransPic
+=============
+*/
+void Sbar_DrawTransPic (int x, int y, qpic_t *pic)
+{
+       if (cl.gametype == GAME_DEATHMATCH)
+               Draw_TransPic (x /*+ ((vid.width - 320)>>1)*/, y + (vid.height-SBAR_HEIGHT), pic);
+       else
+               Draw_TransPic (x + ((vid.width - 320)>>1), y + (vid.height-SBAR_HEIGHT), pic);
+}
+
+/*
+================
+Sbar_DrawCharacter
+
+Draws one solid graphics character
+================
+*/
+void Sbar_DrawCharacter (int x, int y, int num)
+{
+       if (cl.gametype == GAME_DEATHMATCH)
+               Draw_Character ( x /*+ ((vid.width - 320)>>1) */ + 4 , y + vid.height-SBAR_HEIGHT, num);
+       else
+               Draw_Character ( x + ((vid.width - 320)>>1) + 4 , y + vid.height-SBAR_HEIGHT, num);
+}
+
+/*
+================
+Sbar_DrawString
+================
+*/
+void Sbar_DrawString (int x, int y, char *str)
+{
+       if (cl.gametype == GAME_DEATHMATCH)
+               Draw_String (x /*+ ((vid.width - 320)>>1)*/, y+ vid.height-SBAR_HEIGHT, str, 0);
+       else
+               Draw_String (x + ((vid.width - 320)>>1), y+ vid.height-SBAR_HEIGHT, str, 0);
+}
+
+/*
+=============
+Sbar_itoa
+=============
+*/
+int Sbar_itoa (int num, char *buf)
+{
+       char    *str;
+       int             pow10;
+       int             dig;
+
+       str = buf;
+
+       if (num < 0)
+       {
+               *str++ = '-';
+               num = -num;
+       }
+
+       for (pow10 = 10 ; num >= pow10 ; pow10 *= 10)
+       ;
+
+       do
+       {
+               pow10 /= 10;
+               dig = num/pow10;
+               *str++ = '0'+dig;
+               num -= dig*pow10;
+       } while (pow10 != 1);
+
+       *str = 0;
+
+       return str-buf;
+}
+
+
+/*
+=============
+Sbar_DrawNum
+=============
+*/
+void Sbar_DrawNum (int x, int y, int num, int digits, int color)
+{
+       char                    str[12];
+       char                    *ptr;
+       int                             l, frame;
+
+       l = Sbar_itoa (num, str);
+       ptr = str;
+       if (l > digits)
+               ptr += (l-digits);
+       if (l < digits)
+               x += (digits-l)*24;
+
+       while (*ptr)
+       {
+               if (*ptr == '-')
+                       frame = STAT_MINUS;
+               else
+                       frame = *ptr -'0';
+
+               Sbar_DrawTransPic (x,y,sb_nums[color][frame]);
+               x += 24;
+               ptr++;
+       }
+}
+
+//=============================================================================
+
+int            fragsort[MAX_SCOREBOARD];
+
+char   scoreboardtext[MAX_SCOREBOARD][20];
+int            scoreboardtop[MAX_SCOREBOARD];
+int            scoreboardbottom[MAX_SCOREBOARD];
+int            scoreboardcount[MAX_SCOREBOARD];
+int            scoreboardlines;
+
+/*
+===============
+Sbar_SortFrags
+===============
+*/
+void Sbar_SortFrags (void)
+{
+       int             i, j, k;
+
+// sort by frags
+       scoreboardlines = 0;
+       for (i=0 ; i<cl.maxclients ; i++)
+       {
+               if (cl.scores[i].name[0])
+               {
+                       fragsort[scoreboardlines] = i;
+                       scoreboardlines++;
+               }
+       }
+
+       for (i=0 ; i<scoreboardlines ; i++)
+               for (j=0 ; j<scoreboardlines-1-i ; j++)
+                       if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
+                       {
+                               k = fragsort[j];
+                               fragsort[j] = fragsort[j+1];
+                               fragsort[j+1] = k;
+                       }
+}
+
+int    Sbar_ColorForMap (int m)
+{
+       return m < 128 ? m + 8 : m + 8;
+}
+
+/*
+===============
+Sbar_UpdateScoreboard
+===============
+*/
+void Sbar_UpdateScoreboard (void)
+{
+       int             i, k;
+       int             top, bottom;
+       scoreboard_t    *s;
+
+       Sbar_SortFrags ();
+
+// draw the text
+       memset (scoreboardtext, 0, sizeof(scoreboardtext));
+
+       for (i=0 ; i<scoreboardlines; i++)
+       {
+               k = fragsort[i];
+               s = &cl.scores[k];
+               sprintf (&scoreboardtext[i][1], "%3i %s", s->frags, s->name);
+
+               top = s->colors & 0xf0;
+               bottom = (s->colors & 15) <<4;
+               scoreboardtop[i] = Sbar_ColorForMap (top);
+               scoreboardbottom[i] = Sbar_ColorForMap (bottom);
+       }
+}
+
+
+
+/*
+===============
+Sbar_SoloScoreboard
+===============
+*/
+void Sbar_SoloScoreboard (void)
+{
+       char    str[80];
+       int             minutes, seconds, tens, units;
+       int             l;
+
+       sprintf (str,"Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
+       Sbar_DrawString (8, 4, str);
+
+       sprintf (str,"Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
+       Sbar_DrawString (8, 12, str);
+
+// time
+       minutes = cl.time / 60;
+       seconds = cl.time - 60*minutes;
+       tens = seconds / 10;
+       units = seconds - 10*tens;
+       sprintf (str,"Time :%3i:%i%i", minutes, tens, units);
+       Sbar_DrawString (184, 4, str);
+
+// draw level name
+       l = strlen (cl.levelname);
+       Sbar_DrawString (232 - l*4, 12, cl.levelname);
+}
+
+/*
+===============
+Sbar_DrawScoreboard
+===============
+*/
+void Sbar_DrawScoreboard (void)
+{
+       Sbar_SoloScoreboard ();
+       if (cl.gametype == GAME_DEATHMATCH)
+               Sbar_DeathmatchOverlay ();
+#if 0
+       int             i, j, c;
+       int             x, y;
+       int             l;
+       int             top, bottom;
+       scoreboard_t    *s;
+
+       if (cl.gametype != GAME_DEATHMATCH)
+       {
+               Sbar_SoloScoreboard ();
+               return;
+       }
+
+       Sbar_UpdateScoreboard ();
+
+       l = scoreboardlines <= 6 ? scoreboardlines : 6;
+
+       for (i=0 ; i<l ; i++)
+       {
+               x = 20*(i&1);
+               y = i/2 * 8;
+
+               s = &cl.scores[fragsort[i]];
+               if (!s->name[0])
+                       continue;
+
+       // draw background
+               top = s->colors & 0xf0;
+               bottom = (s->colors & 15)<<4;
+               top = Sbar_ColorForMap (top);
+               bottom = Sbar_ColorForMap (bottom);
+
+               Draw_Fill ( x*8+10 + ((vid.width - 320)>>1), y + vid.height - SBAR_HEIGHT, 28, 4, top);
+               Draw_Fill ( x*8+10 + ((vid.width - 320)>>1), y+4 + vid.height - SBAR_HEIGHT, 28, 4, bottom);
+
+       // draw text
+               for (j=0 ; j<20 ; j++)
+               {
+                       c = scoreboardtext[i][j];
+                       if (c == 0 || c == ' ')
+                               continue;
+                       Sbar_DrawCharacter ( (x+j)*8, y, c);
+               }
+       }
+#endif
+}
+
+//=============================================================================
+
+/*
+===============
+Sbar_DrawInventory
+===============
+*/
+void Sbar_DrawInventory (void)
+{
+       int             i;
+       char    num[6];
+       float   time;
+       int             flashon;
+
+       if (rogue)
+       {
+               if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
+                       Sbar_DrawPic (0, -24, rsb_invbar[0]);
+               else
+                       Sbar_DrawPic (0, -24, rsb_invbar[1]);
+       }
+       else
+       {
+               Sbar_DrawPic (0, -24, sb_ibar);
+       }
+
+// weapons
+       for (i=0 ; i<7 ; i++)
+       {
+               if (cl.items & (IT_SHOTGUN<<i) )
+               {
+                       time = cl.item_gettime[i];
+                       flashon = (int)((cl.time - time)*10);
+                       if (flashon >= 10)
+                       {
+                               if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i)  )
+                                       flashon = 1;
+                               else
+                                       flashon = 0;
+                       }
+                       else
+                               flashon = (flashon%5) + 2;
+
+         Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]);
+
+//                     if (flashon > 1)
+//                             sb_updates = 0;         // force update to remove flash
+               }
+       }
+
+// MED 01/04/97
+// hipnotic weapons
+    if (hipnotic)
+    {
+      int grenadeflashing=0;
+      for (i=0 ; i<4 ; i++)
+      {
+         if (cl.items & (1<<hipweapons[i]) )
+         {
+            time = cl.item_gettime[hipweapons[i]];
+            flashon = (int)((cl.time - time)*10);
+            if (flashon >= 10)
+            {
+               if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i])  )
+                  flashon = 1;
+               else
+                  flashon = 0;
+            }
+            else
+               flashon = (flashon%5) + 2;
+
+            // check grenade launcher
+            if (i==2)
+            {
+               if (cl.items & HIT_PROXIMITY_GUN)
+               {
+                  if (flashon)
+                  {
+                     grenadeflashing = 1;
+                     Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
+                  }
+               }
+            }
+            else if (i==3)
+            {
+               if (cl.items & (IT_SHOTGUN<<4))
+               {
+                  if (flashon && !grenadeflashing)
+                  {
+                     Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
+                  }
+                  else if (!grenadeflashing)
+                  {
+                     Sbar_DrawPic (96, -16, hsb_weapons[0][3]);
+                  }
+               }
+               else
+                  Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
+            }
+            else
+               Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
+//            if (flashon > 1)
+//               sb_updates = 0;      // force update to remove flash
+         }
+      }
+    }
+
+       if (rogue)
+       {
+    // check for powered up weapon.
+               if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
+               {
+                       for (i=0;i<5;i++)
+                       {
+                               if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
+                               {
+                                       Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
+                               }
+                       }
+               }
+       }
+
+// ammo counts
+       for (i=0 ; i<4 ; i++)
+       {
+               sprintf (num, "%3i",cl.stats[STAT_SHELLS+i] );
+               if (num[0] != ' ')
+                       Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[0] - '0');
+               if (num[1] != ' ')
+                       Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[1] - '0');
+               if (num[2] != ' ')
+                       Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[2] - '0');
+       }
+
+       flashon = 0;
+   // items
+   for (i=0 ; i<6 ; i++)
+      if (cl.items & (1<<(17+i)))
+      {
+         time = cl.item_gettime[17+i];
+         if (time && time > cl.time - 2 && flashon )
+         {  // flash frame
+//            sb_updates = 0;
+         }
+         else
+         {
+         //MED 01/04/97 changed keys
+            if (!hipnotic || (i>1))
+            {
+               Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
+            }
+         }
+//         if (time && time > cl.time - 2)
+//            sb_updates = 0;
+      }
+   //MED 01/04/97 added hipnotic items
+   // hipnotic items
+   if (hipnotic)
+   {
+      for (i=0 ; i<2 ; i++)
+         if (cl.items & (1<<(24+i)))
+         {
+            time = cl.item_gettime[24+i];
+            if (time && time > cl.time - 2 && flashon )
+            {  // flash frame
+//               sb_updates = 0;
+            }
+            else
+            {
+               Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
+            }
+//            if (time && time > cl.time - 2)
+//               sb_updates = 0;
+         }
+   }
+
+       if (rogue)
+       {
+       // new rogue items
+               for (i=0 ; i<2 ; i++)
+               {
+                       if (cl.items & (1<<(29+i)))
+                       {
+                               time = cl.item_gettime[29+i];
+
+                               if (time &&     time > cl.time - 2 && flashon )
+                               {       // flash frame
+//                                     sb_updates = 0;
+                               }
+                               else
+                               {
+                                       Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
+                               }
+
+//                             if (time &&     time > cl.time - 2)
+//                                     sb_updates = 0;
+                       }
+               }
+       }
+       else
+       {
+       // sigils
+               for (i=0 ; i<4 ; i++)
+               {
+                       if (cl.items & (1<<(28+i)))
+                       {
+                               time = cl.item_gettime[28+i];
+                               if (time &&     time > cl.time - 2 && flashon )
+                               {       // flash frame
+//                                     sb_updates = 0;
+                               }
+                               else
+                                       Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
+//                             if (time &&     time > cl.time - 2)
+//                                     sb_updates = 0;
+                       }
+               }
+       }
+}
+
+//=============================================================================
+
+/*
+===============
+Sbar_DrawFrags
+===============
+*/
+void Sbar_DrawFrags (void)
+{
+       int                             i, k, l;
+       int                             top, bottom;
+       int                             x, y, f;
+       int                             xofs;
+       char                    num[12];
+       scoreboard_t    *s;
+
+       Sbar_SortFrags ();
+
+// draw the text
+       l = scoreboardlines <= 4 ? scoreboardlines : 4;
+
+       x = 23;
+       if (cl.gametype == GAME_DEATHMATCH)
+               xofs = 0;
+       else
+               xofs = (vid.width - 320)>>1;
+       y = vid.height - SBAR_HEIGHT - 23;
+
+       for (i=0 ; i<l ; i++)
+       {
+               k = fragsort[i];
+               s = &cl.scores[k];
+               if (!s->name[0])
+                       continue;
+
+       // draw background
+               top = s->colors & 0xf0;
+               bottom = (s->colors & 15)<<4;
+               top = Sbar_ColorForMap (top);
+               bottom = Sbar_ColorForMap (bottom);
+
+               Draw_Fill (xofs + x*8 + 10, y, 28, 4, top);
+               Draw_Fill (xofs + x*8 + 10, y+4, 28, 3, bottom);
+
+       // draw number
+               f = s->frags;
+               sprintf (num, "%3i",f);
+
+               Sbar_DrawCharacter ( (x+1)*8 , -24, num[0]);
+               Sbar_DrawCharacter ( (x+2)*8 , -24, num[1]);
+               Sbar_DrawCharacter ( (x+3)*8 , -24, num[2]);
+
+               if (k == cl.viewentity - 1)
+               {
+                       Sbar_DrawCharacter (x*8+2, -24, 16);
+                       Sbar_DrawCharacter ( (x+4)*8-4, -24, 17);
+               }
+               x+=4;
+       }
+}
+
+//=============================================================================
+
+
+/*
+===============
+Sbar_DrawFace
+===============
+*/
+void Sbar_DrawFace (void)
+{
+       int             f, anim;
+
+// PGM 01/19/97 - team color drawing
+// PGM 03/02/97 - fixed so color swatch only appears in CTF modes
+       if (rogue &&
+        (cl.maxclients != 1) &&
+        (teamplay.value>3) &&
+        (teamplay.value<7))
+       {
+               int                             top, bottom;
+               int                             xofs;
+               char                    num[12];
+               scoreboard_t    *s;
+               
+               s = &cl.scores[cl.viewentity - 1];
+               // draw background
+               top = s->colors & 0xf0;
+               bottom = (s->colors & 15)<<4;
+               top = Sbar_ColorForMap (top);
+               bottom = Sbar_ColorForMap (bottom);
+
+               if (cl.gametype == GAME_DEATHMATCH)
+                       xofs = 113;
+               else
+                       xofs = ((vid.width - 320)>>1) + 113;
+
+               Sbar_DrawPic (112, 0, rsb_teambord);
+               Draw_Fill (xofs, vid.height-SBAR_HEIGHT+3, 22, 9, top);
+               Draw_Fill (xofs, vid.height-SBAR_HEIGHT+12, 22, 9, bottom);
+
+               // draw number
+               f = s->frags;
+               sprintf (num, "%3i",f);
+
+               if (top==8)
+               {
+                       if (num[0] != ' ')
+                               Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
+                       if (num[1] != ' ')
+                               Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
+                       if (num[2] != ' ')
+                               Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
+               }
+               else
+               {
+                       Sbar_DrawCharacter ( 109, 3, num[0]);
+                       Sbar_DrawCharacter ( 116, 3, num[1]);
+                       Sbar_DrawCharacter ( 123, 3, num[2]);
+               }
+               
+               return;
+       }
+// PGM 01/19/97 - team color drawing
+
+       if ( (cl.items & (IT_INVISIBILITY | IT_INVULNERABILITY) )
+       == (IT_INVISIBILITY | IT_INVULNERABILITY) )
+       {
+               Sbar_DrawPic (112, 0, sb_face_invis_invuln);
+               return;
+       }
+       if (cl.items & IT_QUAD)
+       {
+               Sbar_DrawPic (112, 0, sb_face_quad );
+               return;
+       }
+       if (cl.items & IT_INVISIBILITY)
+       {
+               Sbar_DrawPic (112, 0, sb_face_invis );
+               return;
+       }
+       if (cl.items & IT_INVULNERABILITY)
+       {
+               Sbar_DrawPic (112, 0, sb_face_invuln);
+               return;
+       }
+
+       if (cl.stats[STAT_HEALTH] >= 100)
+               f = 4;
+       else
+               f = cl.stats[STAT_HEALTH] / 20;
+
+       if (cl.time <= cl.faceanimtime)
+       {
+               anim = 1;
+//             sb_updates = 0;         // make sure the anim gets drawn over
+       }
+       else
+               anim = 0;
+       Sbar_DrawPic (112, 0, sb_faces[f][anim]);
+}
+
+/*
+===============
+Sbar_Draw
+===============
+*/
+void Sbar_Draw (void)
+{
+       if (scr_con_current == vid.height)
+               return;         // console is full screen
+
+       // LordHavoc: always redraw
+       //if (sb_updates >= vid.numpages)
+       //      return;
+
+       scr_copyeverything = 1;
+
+//     sb_updates++;
+
+       if (sb_lines > 24)
+       {
+               Sbar_DrawInventory ();
+               if (cl.maxclients != 1)
+                       Sbar_DrawFrags ();
+       }
+
+       if (sb_showscores || cl.stats[STAT_HEALTH] <= 0)
+       {
+               Sbar_DrawPic (0, 0, sb_scorebar);
+               Sbar_DrawScoreboard ();
+//             sb_updates = 0;
+       }
+       else if (sb_lines)
+       {
+               Sbar_DrawPic (0, 0, sb_sbar);
+
+   // keys (hipnotic only)
+      //MED 01/04/97 moved keys here so they would not be overwritten
+      if (hipnotic)
+      {
+         if (cl.items & IT_KEY1)
+            Sbar_DrawPic (209, 3, sb_items[0]);
+         if (cl.items & IT_KEY2)
+            Sbar_DrawPic (209, 12, sb_items[1]);
+      }
+   // armor
+               if (cl.items & IT_INVULNERABILITY)
+               {
+                       Sbar_DrawNum (24, 0, 666, 3, 1);
+                       Sbar_DrawPic (0, 0, draw_disc);
+               }
+               else
+               {
+                       if (rogue)
+                       {
+                               Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3,
+                                                               cl.stats[STAT_ARMOR] <= 25);
+                               if (cl.items & RIT_ARMOR3)
+                                       Sbar_DrawPic (0, 0, sb_armor[2]);
+                               else if (cl.items & RIT_ARMOR2)
+                                       Sbar_DrawPic (0, 0, sb_armor[1]);
+                               else if (cl.items & RIT_ARMOR1)
+                                       Sbar_DrawPic (0, 0, sb_armor[0]);
+                       }
+                       else
+                       {
+                               Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3
+                               , cl.stats[STAT_ARMOR] <= 25);
+                               if (cl.items & IT_ARMOR3)
+                                       Sbar_DrawPic (0, 0, sb_armor[2]);
+                               else if (cl.items & IT_ARMOR2)
+                                       Sbar_DrawPic (0, 0, sb_armor[1]);
+                               else if (cl.items & IT_ARMOR1)
+                                       Sbar_DrawPic (0, 0, sb_armor[0]);
+                       }
+               }
+
+       // face
+               Sbar_DrawFace ();
+
+       // health
+               Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3
+               , cl.stats[STAT_HEALTH] <= 25);
+
+       // ammo icon
+               if (rogue)
+               {
+                       if (cl.items & RIT_SHELLS)
+                               Sbar_DrawPic (224, 0, sb_ammo[0]);
+                       else if (cl.items & RIT_NAILS)
+                               Sbar_DrawPic (224, 0, sb_ammo[1]);
+                       else if (cl.items & RIT_ROCKETS)
+                               Sbar_DrawPic (224, 0, sb_ammo[2]);
+                       else if (cl.items & RIT_CELLS)
+                               Sbar_DrawPic (224, 0, sb_ammo[3]);
+                       else if (cl.items & RIT_LAVA_NAILS)
+                               Sbar_DrawPic (224, 0, rsb_ammo[0]);
+                       else if (cl.items & RIT_PLASMA_AMMO)
+                               Sbar_DrawPic (224, 0, rsb_ammo[1]);
+                       else if (cl.items & RIT_MULTI_ROCKETS)
+                               Sbar_DrawPic (224, 0, rsb_ammo[2]);
+               }
+               else
+               {
+                       if (cl.items & IT_SHELLS)
+                               Sbar_DrawPic (224, 0, sb_ammo[0]);
+                       else if (cl.items & IT_NAILS)
+                               Sbar_DrawPic (224, 0, sb_ammo[1]);
+                       else if (cl.items & IT_ROCKETS)
+                               Sbar_DrawPic (224, 0, sb_ammo[2]);
+                       else if (cl.items & IT_CELLS)
+                               Sbar_DrawPic (224, 0, sb_ammo[3]);
+               }
+
+               Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3,
+                                         cl.stats[STAT_AMMO] <= 10);
+       }
+
+       if (vid.width > 320) {
+               if (cl.gametype == GAME_DEATHMATCH)
+                       Sbar_MiniDeathmatchOverlay ();
+       }
+}
+
+//=============================================================================
+
+/*
+==================
+Sbar_IntermissionNumber
+
+==================
+*/
+void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color)
+{
+       char                    str[12];
+       char                    *ptr;
+       int                             l, frame;
+
+       l = Sbar_itoa (num, str);
+       ptr = str;
+       if (l > digits)
+               ptr += (l-digits);
+       if (l < digits)
+               x += (digits-l)*24;
+
+       while (*ptr)
+       {
+               if (*ptr == '-')
+                       frame = STAT_MINUS;
+               else
+                       frame = *ptr -'0';
+
+               Draw_TransPic (x,y,sb_nums[color][frame]);
+               x += 24;
+               ptr++;
+       }
+}
+
+/*
+==================
+Sbar_DeathmatchOverlay
+
+==================
+*/
+void Sbar_DeathmatchOverlay (void)
+{
+       qpic_t                  *pic;
+       int                             i, k, l, top, bottom, x, y, total, n, minutes, tens, units, fph;
+       char                    num[128];
+       scoreboard_t    *s;
+
+       scr_copyeverything = 1;
+       scr_fullupdate = 0;
+
+       pic = Draw_CachePic ("gfx/ranking.lmp");
+       M_DrawPic ((320-pic->width)/2, 8, pic);
+
+// scores
+       Sbar_SortFrags ();
+
+// draw the text
+       l = scoreboardlines;
+
+       x = ((vid.width - 320)>>1) - 140;
+       y = 40;
+       for (i = 0;i < l;i++)
+       {
+               k = fragsort[i];
+               s = &cl.scores[k];
+               if (!s->name[0])
+                       continue;
+
+       // draw background
+               top = s->colors & 0xf0;
+               bottom = (s->colors & 15)<<4;
+               top = Sbar_ColorForMap (top);
+               bottom = Sbar_ColorForMap (bottom);
+
+               Draw_Fill ( x, y+1, 88, 3, top);
+               Draw_Fill ( x, y+4, 88, 3, bottom);
+
+               total = cl.time - s->entertime;
+               minutes = (int)total/60;
+               n = total - minutes*60;
+               tens = n/10;
+               units = n%10;
+
+               fph = total ? (int) ((float) s->frags * 3600.0 / total) : 0;
+               if (fph < -999) fph = -999;
+               if (fph > 9999) fph = 9999;
+               
+               // put it together
+               sprintf (num, "%c %4i:%4i  %3i:%i%i %s", k == cl.viewentity - 1 ? 12 : ' ', (int) s->frags, fph, minutes, tens, units, s->name);
+               Draw_String(x - 8, y, num, 0);
+
+               y += 8;
+       }
+}
+
+/*
+==================
+Sbar_DeathmatchOverlay
+
+==================
+*/
+void Sbar_MiniDeathmatchOverlay (void)
+{
+       int                             i, l, k, top, bottom, x, y, fph, numlines;
+       char                    num[128];
+       scoreboard_t    *s;
+
+       if (vid.width < 512 || !sb_lines)
+               return;
+
+       scr_copyeverything = 1;
+       scr_fullupdate = 0;
+
+// scores
+       Sbar_SortFrags ();
+
+// draw the text
+       l = scoreboardlines;
+       y = vid.height - sb_lines;
+       numlines = sb_lines/8;
+       if (numlines < 3)
+               return;
+
+       //find us
+       for (i = 0; i < scoreboardlines; i++)
+               if (fragsort[i] == cl.viewentity - 1)
+                       break;
+
+    if (i == scoreboardlines) // we're not there
+            i = 0;
+    else // figure out start
+            i = i - numlines/2;
+
+    if (i > scoreboardlines - numlines)
+            i = scoreboardlines - numlines;
+    if (i < 0)
+            i = 0;
+
+       x = 324;
+       for (;i < scoreboardlines && y < vid.height - 8;i++)
+       {
+               k = fragsort[i];
+               s = &cl.scores[k];
+               if (!s->name[0])
+                       continue;
+
+       // draw background
+               top = Sbar_ColorForMap (s->colors & 0xf0);
+               bottom = Sbar_ColorForMap ((s->colors & 15)<<4);
+
+               Draw_Fill ( x, y+1, 72, 3, top);
+               Draw_Fill ( x, y+4, 72, 3, bottom);
+
+               fph = (cl.time - s->entertime) ? (int) ((float) s->frags * 3600.0 / (cl.time - s->entertime)) : 0;
+               if (fph < -999) fph = -999;
+               if (fph > 9999) fph = 9999;
+               
+               // put it together
+               sprintf (num, "%c%4i:%4i%c %s", k == cl.viewentity - 1 ? 16 : ' ', (int) s->frags, fph, k == cl.viewentity - 1 ? 17 : ' ', s->name);
+               Draw_String(x - 8, y, num, 0);
+
+               y += 8;
+       }
+}
+
+/*
+==================
+Sbar_IntermissionOverlay
+
+==================
+*/
+void Sbar_IntermissionOverlay (void)
+{
+       qpic_t  *pic;
+       int             dig;
+       int             num;
+
+       scr_copyeverything = 1;
+       scr_fullupdate = 0;
+
+       if (cl.gametype == GAME_DEATHMATCH)
+       {
+               Sbar_DeathmatchOverlay ();
+               return;
+       }
+
+       pic = Draw_CachePic ("gfx/complete.lmp");
+       Draw_Pic (64, 24, pic);
+
+       pic = Draw_CachePic ("gfx/inter.lmp");
+       Draw_TransPic (0, 56, pic);
+
+// time
+       dig = cl.completed_time/60;
+       Sbar_IntermissionNumber (160, 64, dig, 3, 0);
+       num = cl.completed_time - dig*60;
+       Draw_TransPic (234,64,sb_colon);
+       Draw_TransPic (246,64,sb_nums[0][num/10]);
+       Draw_TransPic (266,64,sb_nums[0][num%10]);
+
+       Sbar_IntermissionNumber (160, 104, cl.stats[STAT_SECRETS], 3, 0);
+       Draw_TransPic (232,104,sb_slash);
+       Sbar_IntermissionNumber (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
+
+       Sbar_IntermissionNumber (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
+       Draw_TransPic (232,144,sb_slash);
+       Sbar_IntermissionNumber (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
+
+}
+
+
+/*
+==================
+Sbar_FinaleOverlay
+
+==================
+*/
+void Sbar_FinaleOverlay (void)
+{
+       qpic_t  *pic;
+
+       scr_copyeverything = 1;
+
+       pic = Draw_CachePic ("gfx/finale.lmp");
+       Draw_TransPic ( (vid.width-pic->width)/2, 16, pic);
+}
diff --git a/sbar.h b/sbar.h
new file mode 100644 (file)
index 0000000..286b3b6
--- /dev/null
+++ b/sbar.h
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// the status bar is only redrawn if something has changed, but if anything
+// does, the entire thing will be redrawn for the next vid.numpages frames.
+
+#define        SBAR_HEIGHT             24
+
+extern int                     sb_lines;                       // scan lines to draw
+
+void Sbar_Init (void);
+
+void Sbar_Changed (void);
+// call whenever any of the client stats represented on the sbar changes
+
+void Sbar_Draw (void);
+// called every frame by screen
+
+void Sbar_IntermissionOverlay (void);
+// called each frame after the level has been completed
+
+void Sbar_FinaleOverlay (void);
diff --git a/screen.h b/screen.h
new file mode 100644 (file)
index 0000000..64e0bc3
--- /dev/null
+++ b/screen.h
@@ -0,0 +1,55 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// screen.h
+
+void SCR_Init (void);
+
+void SCR_UpdateScreen (void);
+
+
+void SCR_SizeUp (void);
+void SCR_SizeDown (void);
+void SCR_BringDownConsole (void);
+void SCR_CenterPrint (char *str);
+
+void SCR_BeginLoadingPlaque (void);
+void SCR_EndLoadingPlaque (void);
+
+int SCR_ModalMessage (char *text);
+
+extern float           scr_con_current;
+extern float           scr_conlines;           // lines of console to display
+
+extern int                     scr_fullupdate; // set to 0 to force full redraw
+extern int                     sb_lines;
+
+extern int                     clearnotify;    // set to 0 whenever notify text is drawn
+extern qboolean        scr_disabled_for_loading;
+extern qboolean        scr_skipupdate;
+
+extern cvar_t          scr_viewsize;
+
+extern cvar_t scr_viewsize;
+
+// only the refresh window will be updated unless these variables are flagged 
+extern int                     scr_copytop;
+extern int                     scr_copyeverything;
+
+void SCR_UpdateWholeScreen (void);
diff --git a/server.h b/server.h
new file mode 100644 (file)
index 0000000..4e6635f
--- /dev/null
+++ b/server.h
@@ -0,0 +1,234 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// server.h
+
+typedef struct
+{
+       int                     maxclients;
+       int                     maxclientslimit;
+       struct client_s *clients;               // [maxclients]
+       int                     serverflags;            // episode completion information
+       qboolean        changelevel_issued;     // cleared when at SV_SpawnServer
+} server_static_t;
+
+//=============================================================================
+
+typedef enum {ss_loading, ss_active} server_state_t;
+
+typedef struct
+{
+       qboolean        active;                         // false if only a net client
+
+       qboolean        paused;
+       qboolean        loadgame;                       // handle connections specially
+
+       double          time;
+       
+       int                     lastcheck;                      // used by PF_checkclient
+       double          lastchecktime;
+       
+       char            name[64];                       // map name
+       char            modelname[64];          // maps/<name>.bsp, for model_precache[0]
+       struct model_s  *worldmodel;
+       char            *model_precache[MAX_MODELS];    // NULL terminated
+       struct model_s  *models[MAX_MODELS];
+       char            *sound_precache[MAX_SOUNDS];    // NULL terminated
+       char            *lightstyles[MAX_LIGHTSTYLES];
+       int                     num_edicts;
+       int                     max_edicts;
+       edict_t         *edicts;                        // can NOT be array indexed, because
+                                                                       // edict_t is variable sized, but can
+                                                                       // be used to reference the world ent
+       server_state_t  state;                  // some actions are only valid during load
+
+       sizebuf_t       datagram;
+       byte            datagram_buf[MAX_DATAGRAM];
+
+       sizebuf_t       reliable_datagram;      // copied to all clients at end of frame
+       byte            reliable_datagram_buf[MAX_DATAGRAM];
+
+       sizebuf_t       signon;
+       byte            signon_buf[32768]; // LordHavoc: increased signon message buffer from 8192 to 32768
+} server_t;
+
+
+#define        NUM_PING_TIMES          16
+#define        NUM_SPAWN_PARMS         16
+
+typedef struct client_s
+{
+       qboolean                active;                         // false = client is free
+       qboolean                spawned;                        // false = don't send datagrams
+       qboolean                dropasap;                       // has been told to go to another level
+       qboolean                privileged;                     // can execute any host command
+       qboolean                sendsignon;                     // only valid before spawned
+
+       double                  last_message;           // reliable messages must be sent
+                                                                               // periodically
+
+       struct qsocket_s *netconnection;        // communications handle
+
+       usercmd_t               cmd;                            // movement
+       vec3_t                  wishdir;                        // intended motion calced from cmd
+
+       sizebuf_t               message;                        // can be added to at any time,
+                                                                               // copied and clear once per frame
+       byte                    msgbuf[MAX_MSGLEN];
+       edict_t                 *edict;                         // EDICT_NUM(clientnum+1)
+       char                    name[32];                       // for printing to other people
+       int                             colors;
+               
+       float                   ping_times[NUM_PING_TIMES];
+       int                             num_pings;                      // ping_times[num_pings%NUM_PING_TIMES]
+
+// spawn parms are carried from level to level
+       float                   spawn_parms[NUM_SPAWN_PARMS];
+
+// client known data for deltas        
+       int                             old_frags;
+} client_t;
+
+
+//=============================================================================
+
+// edict->movetype values
+#define        MOVETYPE_NONE                   0               // never moves
+#define        MOVETYPE_ANGLENOCLIP    1
+#define        MOVETYPE_ANGLECLIP              2
+#define        MOVETYPE_WALK                   3               // gravity
+#define        MOVETYPE_STEP                   4               // gravity, special edge handling
+#define        MOVETYPE_FLY                    5
+#define        MOVETYPE_TOSS                   6               // gravity
+#define        MOVETYPE_PUSH                   7               // no clip to world, push and crush
+#define        MOVETYPE_NOCLIP                 8
+#define        MOVETYPE_FLYMISSILE             9               // extra size to monsters
+#define        MOVETYPE_BOUNCE                 10
+#define MOVETYPE_BOUNCEMISSILE 11              // bounce w/o gravity
+#define MOVETYPE_FOLLOW                        12              // track movement of aiment
+
+// edict->solid values
+#define        SOLID_NOT                               0               // no interaction with other objects
+#define        SOLID_TRIGGER                   1               // touch on edge, but not blocking
+#define        SOLID_BBOX                              2               // touch on edge, block
+#define        SOLID_SLIDEBOX                  3               // touch on edge, but not an onground
+#define        SOLID_BSP                               4               // bsp clip, touch on edge, block
+// LordHavoc: corpse code
+#define        SOLID_CORPSE                    5               // same as SOLID_BBOX, except it behaves as SOLID_NOT against SOLID_SLIDEBOX objects (players/monsters)
+
+// edict->deadflag values
+#define        DEAD_NO                                 0
+#define        DEAD_DYING                              1
+#define        DEAD_DEAD                               2
+
+#define        DAMAGE_NO                               0
+#define        DAMAGE_YES                              1
+#define        DAMAGE_AIM                              2
+
+// edict->flags
+#define        FL_FLY                                  1
+#define        FL_SWIM                                 2
+//#define      FL_GLIMPSE                              4
+#define        FL_CONVEYOR                             4
+#define        FL_CLIENT                               8
+#define        FL_INWATER                              16
+#define        FL_MONSTER                              32
+#define        FL_GODMODE                              64
+#define        FL_NOTARGET                             128
+#define        FL_ITEM                                 256
+#define        FL_ONGROUND                             512
+#define        FL_PARTIALGROUND                1024    // not all corners are valid
+#define        FL_WATERJUMP                    2048    // player jumping out of water
+#define        FL_JUMPRELEASED                 4096    // for jump debouncing
+
+// entity effects
+
+#define        EF_BRIGHTFIELD                  1
+#define        EF_MUZZLEFLASH                  2
+#define        EF_BRIGHTLIGHT                  4
+#define        EF_DIMLIGHT                     8
+// added EF_ effects:
+#define        EF_NODRAW                               16
+#define EF_ADDITIVE                            32  // LordHavoc: Additive Rendering
+#define EF_BLUE                                        64
+#define EF_RED                                 128
+
+#define        SPAWNFLAG_NOT_EASY                      256
+#define        SPAWNFLAG_NOT_MEDIUM            512
+#define        SPAWNFLAG_NOT_HARD                      1024
+#define        SPAWNFLAG_NOT_DEATHMATCH        2048
+
+//============================================================================
+
+extern cvar_t  teamplay;
+extern cvar_t  skill;
+extern cvar_t  deathmatch;
+extern cvar_t  coop;
+extern cvar_t  fraglimit;
+extern cvar_t  timelimit;
+
+extern server_static_t svs;                            // persistant server info
+extern server_t                sv;                                     // local server
+
+extern client_t        *host_client;
+
+extern jmp_buf         host_abortserver;
+
+extern double          host_time;
+
+extern edict_t         *sv_player;
+
+//===========================================================
+
+void SV_Init (void);
+
+void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count);
+void SV_StartSound (edict_t *entity, int channel, char *sample, int volume,
+    float attenuation);
+
+void SV_DropClient (qboolean crash);
+
+void SV_SendClientMessages (void);
+void SV_ClearDatagram (void);
+
+int SV_ModelIndex (char *name);
+
+void SV_SetIdealPitch (void);
+
+void SV_AddUpdates (void);
+
+void SV_ClientThink (void);
+void SV_AddClientToServer (struct qsocket_s    *ret);
+
+void SV_ClientPrintf (char *fmt, ...);
+void SV_BroadcastPrintf (char *fmt, ...);
+
+void SV_Physics (void);
+
+qboolean SV_CheckBottom (edict_t *ent);
+qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink);
+
+void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg);
+
+void SV_MoveToGoal (void);
+
+void SV_CheckForNewClients (void);
+void SV_RunClients (void);
+void SV_SaveSpawnparms ();
+void SV_SpawnServer (char *server);
diff --git a/snd_dma.c b/snd_dma.c
new file mode 100644 (file)
index 0000000..b04c1a3
--- /dev/null
+++ b/snd_dma.c
@@ -0,0 +1,1049 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_dma.c -- main control for any streaming sound output device
+
+#include "quakedef.h"
+
+#ifdef _WIN32
+#include "winquake.h"
+#endif
+
+void S_Play(void);
+void S_PlayVol(void);
+void S_SoundList(void);
+void S_Update_();
+void S_StopAllSounds(qboolean clear);
+void S_StopAllSoundsC(void);
+
+// =======================================================================
+// Internal sound data & structures
+// =======================================================================
+
+channel_t   channels[MAX_CHANNELS];
+int                    total_channels;
+
+int                            snd_blocked = 0;
+static qboolean        snd_ambient = 1;
+qboolean               snd_initialized = false;
+
+// pointer should go away
+volatile dma_t  *shm = 0;
+volatile dma_t sn;
+
+vec3_t         listener_origin;
+vec3_t         listener_forward;
+vec3_t         listener_right;
+vec3_t         listener_up;
+vec_t          sound_nominal_clip_dist=1000.0;
+
+int                    soundtime;              // sample PAIRS
+int            paintedtime;    // sample PAIRS
+
+
+//LordHavoc: increased the client sound limit from 512 to 4096 for the Nehahra movie
+#define        MAX_SFX         4096
+sfx_t          *known_sfx;             // hunk allocated [MAX_SFX]
+int                    num_sfx;
+
+sfx_t          *ambient_sfx[NUM_AMBIENTS];
+
+int            desired_speed = 11025;
+int            desired_bits = 16;
+
+int sound_started=0;
+
+cvar_t bgmvolume = {"bgmvolume", "1", true};
+cvar_t volume = {"volume", "0.7", true};
+
+cvar_t nosound = {"nosound", "0"};
+cvar_t precache = {"precache", "1"};
+cvar_t loadas8bit = {"loadas8bit", "0"};
+cvar_t bgmbuffer = {"bgmbuffer", "4096"};
+cvar_t ambient_level = {"ambient_level", "0.3"};
+cvar_t ambient_fade = {"ambient_fade", "100"};
+cvar_t snd_noextraupdate = {"snd_noextraupdate", "0"};
+cvar_t snd_show = {"snd_show", "0"};
+cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", true};
+cvar_t snd_swapstereo = {"snd_swapstereo", "0", true};
+
+
+// ====================================================================
+// User-setable variables
+// ====================================================================
+
+
+//
+// Fake dma is a synchronous faking of the DMA progress used for
+// isolating performance in the renderer.  The fakedma_updates is
+// number of times S_Update() is called per second.
+//
+
+qboolean fakedma = false;
+int fakedma_updates = 15;
+
+
+void S_AmbientOff (void)
+{
+       snd_ambient = false;
+}
+
+
+void S_AmbientOn (void)
+{
+       snd_ambient = true;
+}
+
+
+void S_SoundInfo_f(void)
+{
+       if (!sound_started || !shm)
+       {
+               Con_Printf ("sound system not started\n");
+               return;
+       }
+       
+    Con_Printf("%5d stereo\n", shm->channels - 1);
+    Con_Printf("%5d samples\n", shm->samples);
+    Con_Printf("%5d samplepos\n", shm->samplepos);
+    Con_Printf("%5d samplebits\n", shm->samplebits);
+    Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
+    Con_Printf("%5d speed\n", shm->speed);
+    Con_Printf("0x%x dma buffer\n", shm->buffer);
+       Con_Printf("%5d total_channels\n", total_channels);
+}
+
+
+/*
+================
+S_Startup
+================
+*/
+
+void S_Startup (void)
+{
+       int             rc;
+
+       if (!snd_initialized)
+               return;
+
+       if (!fakedma)
+       {
+               rc = SNDDMA_Init();
+
+               if (!rc)
+               {
+#ifndef        _WIN32
+                       Con_Printf("S_Startup: SNDDMA_Init failed.\n");
+#endif
+                       sound_started = 0;
+                       return;
+               }
+       }
+
+       sound_started = 1;
+}
+
+
+void S_Play2(void);
+
+/*
+================
+S_Init
+================
+*/
+void S_Init (void)
+{
+
+       Con_Printf("\nSound Initialization\n");
+
+       if (COM_CheckParm("-nosound"))
+               return;
+
+       if (COM_CheckParm("-simsound"))
+               fakedma = true;
+
+       Cmd_AddCommand("play", S_Play);
+       Cmd_AddCommand("play2", S_Play2);
+       Cmd_AddCommand("playvol", S_PlayVol);
+       Cmd_AddCommand("stopsound", S_StopAllSoundsC);
+       Cmd_AddCommand("soundlist", S_SoundList);
+       Cmd_AddCommand("soundinfo", S_SoundInfo_f);
+
+       Cvar_RegisterVariable(&nosound);
+       Cvar_RegisterVariable(&volume);
+       Cvar_RegisterVariable(&precache);
+       Cvar_RegisterVariable(&loadas8bit);
+       Cvar_RegisterVariable(&bgmvolume);
+       Cvar_RegisterVariable(&bgmbuffer);
+       Cvar_RegisterVariable(&ambient_level);
+       Cvar_RegisterVariable(&ambient_fade);
+       Cvar_RegisterVariable(&snd_noextraupdate);
+       Cvar_RegisterVariable(&snd_show);
+       Cvar_RegisterVariable(&_snd_mixahead);
+       Cvar_RegisterVariable(&snd_swapstereo); // LordHavoc: for people with backwards sound wiring
+
+       if (host_parms.memsize < 0x800000)
+       {
+               Cvar_Set ("loadas8bit", "1");
+               Con_Printf ("loading all sounds as 8bit\n");
+       }
+
+
+
+       snd_initialized = true;
+
+       S_Startup ();
+
+       SND_InitScaletable ();
+
+       known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t");
+       num_sfx = 0;
+
+// create a piece of DMA memory
+
+       if (fakedma)
+       {
+               shm = (void *) Hunk_AllocName(sizeof(*shm), "shm");
+               shm->splitbuffer = 0;
+               shm->samplebits = 16;
+               shm->speed = 22050;
+               shm->channels = 2;
+               shm->samples = 32768;
+               shm->samplepos = 0;
+               shm->soundalive = true;
+               shm->gamealive = true;
+               shm->submission_chunk = 1;
+               shm->buffer = Hunk_AllocName(1<<16, "shmbuf");
+       }
+
+       Con_Printf ("Sound sampling rate: %i\n", shm->speed);
+
+       // provides a tick sound until washed clean
+
+//     if (shm->buffer)
+//             shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging
+
+       ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav");
+       ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav");
+
+       S_StopAllSounds (true);
+}
+
+
+// =======================================================================
+// Shutdown sound engine
+// =======================================================================
+
+void S_Shutdown(void)
+{
+
+       if (!sound_started)
+               return;
+
+       if (shm)
+               shm->gamealive = 0;
+
+       shm = 0;
+       sound_started = 0;
+
+       if (!fakedma)
+       {
+               SNDDMA_Shutdown();
+       }
+}
+
+
+// =======================================================================
+// Load a sound
+// =======================================================================
+
+/*
+==================
+S_FindName
+
+==================
+*/
+sfx_t *S_FindName (char *name)
+{
+       int             i;
+       sfx_t   *sfx;
+
+       if (!name)
+               Sys_Error ("S_FindName: NULL\n");
+
+       if (strlen(name) >= MAX_QPATH)
+               Sys_Error ("Sound name too long: %s", name);
+
+// see if already loaded
+       for (i=0 ; i < num_sfx ; i++)
+               if (!strcmp(known_sfx[i].name, name))
+               {
+                       return &known_sfx[i];
+               }
+
+       if (num_sfx == MAX_SFX)
+               Sys_Error ("S_FindName: out of sfx_t");
+       
+       sfx = &known_sfx[i];
+       strcpy (sfx->name, name);
+
+       num_sfx++;
+       
+       return sfx;
+}
+
+
+/*
+==================
+S_TouchSound
+
+==================
+*/
+void S_TouchSound (char *name)
+{
+       sfx_t   *sfx;
+       
+       if (!sound_started)
+               return;
+
+       sfx = S_FindName (name);
+       Cache_Check (&sfx->cache);
+}
+
+/*
+==================
+S_PrecacheSound
+
+==================
+*/
+sfx_t *S_PrecacheSound (char *name)
+{
+       sfx_t   *sfx;
+
+       if (!sound_started || nosound.value)
+               return NULL;
+
+       sfx = S_FindName (name);
+       
+// cache it in
+       if (precache.value)
+               S_LoadSound (sfx);
+       
+       return sfx;
+}
+
+
+//=============================================================================
+
+/*
+=================
+SND_PickChannel
+=================
+*/
+channel_t *SND_PickChannel(int entnum, int entchannel)
+{
+    int ch_idx;
+    int first_to_die;
+    int life_left;
+
+// Check for replacement sound, or find the best one to replace
+    first_to_die = -1;
+    life_left = 0x7fffffff;
+    for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
+    {
+               if (entchannel != 0             // channel 0 never overrides
+               && channels[ch_idx].entnum == entnum
+               && (channels[ch_idx].entchannel == entchannel || entchannel == -1) )
+               {       // allways override sound from same entity
+                       first_to_die = ch_idx;
+                       break;
+               }
+
+               // don't let monster sounds override player sounds
+               if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx)
+                       continue;
+
+               if (channels[ch_idx].end - paintedtime < life_left)
+               {
+                       life_left = channels[ch_idx].end - paintedtime;
+                       first_to_die = ch_idx;
+               }
+   }
+
+       if (first_to_die == -1)
+               return NULL;
+
+       if (channels[first_to_die].sfx)
+               channels[first_to_die].sfx = NULL;
+
+    return &channels[first_to_die];    
+}       
+
+/*
+=================
+SND_Spatialize
+=================
+*/
+void SND_Spatialize(channel_t *ch)
+{
+    vec_t dot;
+    vec_t dist;
+    vec_t lscale, rscale, scale;
+    vec3_t source_vec;
+       sfx_t *snd;
+
+// anything coming from the view entity will allways be full volume
+// LordHavoc: make sounds with ATTN_NONE have no spatialization
+       if (ch->entnum == cl.viewentity || ch->dist_mult == 0)
+       {
+               ch->leftvol = ch->master_vol;
+               ch->rightvol = ch->master_vol;
+               return;
+       }
+
+// calculate stereo seperation and distance attenuation
+
+       snd = ch->sfx;
+       VectorSubtract(ch->origin, listener_origin, source_vec);
+       
+       dist = VectorNormalizeLength(source_vec) * ch->dist_mult;
+       
+       dot = DotProduct(listener_right, source_vec);
+
+       if (shm->channels == 1)
+       {
+               rscale = 1.0;
+               lscale = 1.0;
+       }
+       else
+       {
+               rscale = 1.0 + dot;
+               lscale = 1.0 - dot;
+       }
+
+// add in distance effect
+       scale = (1.0 - dist) * rscale;
+       ch->rightvol = (int) (ch->master_vol * scale);
+       if (ch->rightvol < 0)
+               ch->rightvol = 0;
+
+       scale = (1.0 - dist) * lscale;
+       ch->leftvol = (int) (ch->master_vol * scale);
+       if (ch->leftvol < 0)
+               ch->leftvol = 0;
+}           
+
+
+// =======================================================================
+// Start a sound effect
+// =======================================================================
+
+void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
+{
+       channel_t *target_chan, *check;
+       sfxcache_t      *sc;
+       int             vol;
+       int             ch_idx;
+       int             skip;
+
+       if (!sound_started)
+               return;
+
+       if (!sfx)
+               return;
+
+       if (nosound.value)
+               return;
+
+       vol = fvol*255;
+
+// pick a channel to play on
+       target_chan = SND_PickChannel(entnum, entchannel);
+       if (!target_chan)
+               return;
+               
+// spatialize
+       memset (target_chan, 0, sizeof(*target_chan));
+       VectorCopy(origin, target_chan->origin);
+       target_chan->dist_mult = attenuation / sound_nominal_clip_dist;
+       target_chan->master_vol = vol;
+       target_chan->entnum = entnum;
+       target_chan->entchannel = entchannel;
+       SND_Spatialize(target_chan);
+
+       if (!target_chan->leftvol && !target_chan->rightvol)
+               return;         // not audible at all
+
+// new channel
+       sc = S_LoadSound (sfx);
+       if (!sc)
+       {
+               target_chan->sfx = NULL;
+               return;         // couldn't load the sound's data
+       }
+
+       target_chan->sfx = sfx;
+       target_chan->pos = 0.0;
+    target_chan->end = paintedtime + sc->length;       
+
+// if an identical sound has also been started this frame, offset the pos
+// a bit to keep it from just making the first one louder
+       check = &channels[NUM_AMBIENTS];
+    for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++)
+    {
+               if (check == target_chan)
+                       continue;
+               if (check->sfx == sfx && !check->pos)
+               {
+                       skip = rand () % (int)(0.1*shm->speed);
+                       if (skip >= target_chan->end)
+                               skip = target_chan->end - 1;
+                       target_chan->pos += skip;
+                       target_chan->end -= skip;
+                       break;
+               }
+               
+       }
+}
+
+void S_StopSound(int entnum, int entchannel)
+{
+       int i;
+
+       for (i=0 ; i<MAX_DYNAMIC_CHANNELS ; i++)
+       {
+               if (channels[i].entnum == entnum
+                       && channels[i].entchannel == entchannel)
+               {
+                       channels[i].end = 0;
+                       channels[i].sfx = NULL;
+                       return;
+               }
+       }
+}
+
+void S_StopAllSounds(qboolean clear)
+{
+       int             i;
+
+       if (!sound_started)
+               return;
+
+       total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
+
+       for (i=0 ; i<MAX_CHANNELS ; i++)
+               if (channels[i].sfx)
+                       channels[i].sfx = NULL;
+
+       memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
+
+       if (clear)
+               S_ClearBuffer ();
+}
+
+void S_StopAllSoundsC (void)
+{
+       S_StopAllSounds (true);
+}
+
+void S_ClearBuffer (void)
+{
+       int             clear;
+               
+#ifdef _WIN32
+       if (!sound_started || !shm || (!shm->buffer && !pDSBuf))
+#else
+       if (!sound_started || !shm || !shm->buffer)
+#endif
+               return;
+
+       if (shm->samplebits == 8)
+               clear = 0x80;
+       else
+               clear = 0;
+
+#ifdef _WIN32
+       if (pDSBuf)
+       {
+               DWORD   dwSize;
+               DWORD   *pData;
+               int             reps;
+               HRESULT hresult;
+
+               reps = 0;
+
+               while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK)
+               {
+                       if (hresult != DSERR_BUFFERLOST)
+                       {
+                               Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n");
+                               S_Shutdown ();
+                               return;
+                       }
+
+                       if (++reps > 10000)
+                       {
+                               Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n");
+                               S_Shutdown ();
+                               return;
+                       }
+               }
+
+               memset(pData, clear, shm->samples * shm->samplebits/8);
+
+               pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0);
+       
+       }
+       else
+#endif
+       {
+               memset(shm->buffer, clear, shm->samples * shm->samplebits/8);
+       }
+}
+
+
+/*
+=================
+S_StaticSound
+=================
+*/
+void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
+{
+       channel_t       *ss;
+       sfxcache_t              *sc;
+
+       if (!sfx)
+               return;
+
+       if (total_channels == MAX_CHANNELS)
+       {
+               Con_Printf ("total_channels == MAX_CHANNELS\n");
+               return;
+       }
+
+       ss = &channels[total_channels];
+       total_channels++;
+
+       sc = S_LoadSound (sfx);
+       if (!sc)
+               return;
+
+       if (sc->loopstart == -1)
+       {
+               Con_Printf ("Sound %s not looped\n", sfx->name);
+               return;
+       }
+       
+       ss->sfx = sfx;
+       VectorCopy (origin, ss->origin);
+       ss->master_vol = vol;
+       ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist;
+    ss->end = paintedtime + sc->length;        
+       
+       SND_Spatialize (ss);
+}
+
+
+//=============================================================================
+
+/*
+===================
+S_UpdateAmbientSounds
+===================
+*/
+void S_UpdateAmbientSounds (void)
+{
+       mleaf_t         *l;
+       float           vol;
+       int                     ambient_channel;
+       channel_t       *chan;
+
+       if (!snd_ambient)
+               return;
+
+// calc ambient sound levels
+       if (!cl.worldmodel)
+               return;
+
+       l = Mod_PointInLeaf (listener_origin, cl.worldmodel);
+       if (!l || !ambient_level.value)
+       {
+               for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
+                       channels[ambient_channel].sfx = NULL;
+               return;
+       }
+
+       for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
+       {
+               chan = &channels[ambient_channel];      
+               chan->sfx = ambient_sfx[ambient_channel];
+       
+               vol = ambient_level.value * l->ambient_sound_level[ambient_channel];
+               if (vol < 8)
+                       vol = 0;
+
+       // don't adjust volume too fast
+               if (chan->master_vol < vol)
+               {
+                       chan->master_vol += host_frametime * ambient_fade.value;
+                       if (chan->master_vol > vol)
+                               chan->master_vol = vol;
+               }
+               else if (chan->master_vol > vol)
+               {
+                       chan->master_vol -= host_frametime * ambient_fade.value;
+                       if (chan->master_vol < vol)
+                               chan->master_vol = vol;
+               }
+               
+               chan->leftvol = chan->rightvol = chan->master_vol;
+       }
+}
+
+
+/*
+============
+S_Update
+
+Called once each time through the main loop
+============
+*/
+void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
+{
+       int                     i, j;
+       int                     total;
+       channel_t       *ch;
+       channel_t       *combine;
+
+       if (!sound_started || (snd_blocked > 0))
+               return;
+
+       VectorCopy(origin, listener_origin);
+       VectorCopy(forward, listener_forward);
+       VectorCopy(right, listener_right);
+       VectorCopy(up, listener_up);
+       
+// update general area ambient sound sources
+       S_UpdateAmbientSounds ();
+
+       combine = NULL;
+
+// update spatialization for static and dynamic sounds 
+       ch = channels+NUM_AMBIENTS;
+       for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++)
+       {
+               if (!ch->sfx)
+                       continue;
+               SND_Spatialize(ch);         // respatialize channel
+               if (!ch->leftvol && !ch->rightvol)
+                       continue;
+
+       // try to combine static sounds with a previous channel of the same
+       // sound effect so we don't mix five torches every frame
+       
+               if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS)
+               {
+               // see if it can just use the last one
+                       if (combine && combine->sfx == ch->sfx)
+                       {
+                               combine->leftvol += ch->leftvol;
+                               combine->rightvol += ch->rightvol;
+                               ch->leftvol = ch->rightvol = 0;
+                               continue;
+                       }
+               // search for one
+                       combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
+                       for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++)
+                               if (combine->sfx == ch->sfx)
+                                       break;
+                                       
+                       if (j == total_channels)
+                       {
+                               combine = NULL;
+                       }
+                       else
+                       {
+                               if (combine != ch)
+                               {
+                                       combine->leftvol += ch->leftvol;
+                                       combine->rightvol += ch->rightvol;
+                                       ch->leftvol = ch->rightvol = 0;
+                               }
+                               continue;
+                       }
+               }
+               
+               
+       }
+
+//
+// debugging output
+//
+       if (snd_show.value)
+       {
+               total = 0;
+               ch = channels;
+               for (i=0 ; i<total_channels; i++, ch++)
+                       if (ch->sfx && (ch->leftvol || ch->rightvol) )
+                       {
+                               //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
+                               total++;
+                       }
+               
+               Con_Printf ("----(%i)----\n", total);
+       }
+
+// mix some sound
+       S_Update_();
+}
+
+void GetSoundtime(void)
+{
+       int             samplepos;
+       static  int             buffers;
+       static  int             oldsamplepos;
+       int             fullsamples;
+       
+       fullsamples = shm->samples / shm->channels;
+
+// it is possible to miscount buffers if it has wrapped twice between
+// calls to S_Update.  Oh well.
+#ifdef __sun__
+       soundtime = SNDDMA_GetSamples();
+#else
+       samplepos = SNDDMA_GetDMAPos();
+
+
+       if (samplepos < oldsamplepos)
+       {
+               buffers++;                                      // buffer wrapped
+               
+               if (paintedtime > 0x40000000)
+               {       // time to chop things off to avoid 32 bit limits
+                       buffers = 0;
+                       paintedtime = fullsamples;
+                       S_StopAllSounds (true);
+               }
+       }
+       oldsamplepos = samplepos;
+
+       soundtime = buffers*fullsamples + samplepos/shm->channels;
+#endif
+}
+
+void IN_Accumulate (void);
+
+void S_ExtraUpdate (void)
+{
+
+#ifdef _WIN32
+       IN_Accumulate ();
+#endif
+
+       if (snd_noextraupdate.value)
+               return;         // don't pollute timings
+       S_Update_();
+}
+
+void S_Update_(void)
+{
+       unsigned        endtime;
+       int                             samps;
+       
+       if (!sound_started || (snd_blocked > 0))
+               return;
+
+// Updates DMA time
+       GetSoundtime();
+
+// check to make sure that we haven't overshot
+       if (paintedtime < soundtime)
+       {
+               //Con_Printf ("S_Update_ : overflow\n");
+               paintedtime = soundtime;
+       }
+
+// mix ahead of current position
+       endtime = soundtime + _snd_mixahead.value * shm->speed;
+       samps = shm->samples >> (shm->channels-1);
+       if (endtime - soundtime > samps)
+               endtime = soundtime + samps;
+
+#ifdef _WIN32
+// if the buffer was lost or stopped, restore it and/or restart it
+       {
+               DWORD   dwStatus;
+
+               if (pDSBuf)
+               {
+                       if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK)
+                               Con_Printf ("Couldn't get sound buffer status\n");
+                       
+                       if (dwStatus & DSBSTATUS_BUFFERLOST)
+                               pDSBuf->lpVtbl->Restore (pDSBuf);
+                       
+                       if (!(dwStatus & DSBSTATUS_PLAYING))
+                               pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+               }
+       }
+#endif
+
+       S_PaintChannels (endtime);
+
+       SNDDMA_Submit ();
+}
+
+/*
+===============================================================================
+
+console functions
+
+===============================================================================
+*/
+
+void S_Play(void)
+{
+       static int hash=345;
+       int     i;
+       char name[256];
+       sfx_t   *sfx;
+       
+       i = 1;
+       while (i<Cmd_Argc())
+       {
+               if (!strrchr(Cmd_Argv(i), '.'))
+               {
+                       strcpy(name, Cmd_Argv(i));
+                       strcat(name, ".wav");
+               }
+               else
+                       strcpy(name, Cmd_Argv(i));
+               sfx = S_PrecacheSound(name);
+               S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0);
+               i++;
+       }
+}
+
+void S_Play2(void)
+{
+       static int hash=345;
+       int     i;
+       char name[256];
+       sfx_t   *sfx;
+       
+       i = 1;
+       while (i<Cmd_Argc())
+       {
+               if (!strrchr(Cmd_Argv(i), '.'))
+               {
+                       strcpy(name, Cmd_Argv(i));
+                       strcat(name, ".wav");
+               }
+               else
+                       strcpy(name, Cmd_Argv(i));
+               sfx = S_PrecacheSound(name);
+               S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 0.0);
+               i++;
+       }
+}
+
+void S_PlayVol(void)
+{
+       static int hash=543;
+       int i;
+       float vol;
+       char name[256];
+       sfx_t   *sfx;
+       
+       i = 1;
+       while (i<Cmd_Argc())
+       {
+               if (!strrchr(Cmd_Argv(i), '.'))
+               {
+                       strcpy(name, Cmd_Argv(i));
+                       strcat(name, ".wav");
+               }
+               else
+                       strcpy(name, Cmd_Argv(i));
+               sfx = S_PrecacheSound(name);
+               vol = atof(Cmd_Argv(i+1));
+               S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0);
+               i+=2;
+       }
+}
+
+void S_SoundList(void)
+{
+       int             i;
+       sfx_t   *sfx;
+       sfxcache_t      *sc;
+       int             size, total;
+
+       total = 0;
+       for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
+       {
+               sc = Cache_Check (&sfx->cache);
+               if (!sc)
+                       continue;
+               size = sc->length*sc->width*(sc->stereo+1);
+               total += size;
+               if (sc->loopstart >= 0)
+                       Con_Printf ("L");
+               else
+                       Con_Printf (" ");
+               Con_Printf("(%2db) %6i : %s\n",sc->width*8,  size, sfx->name);
+       }
+       Con_Printf ("Total resident: %i\n", total);
+}
+
+
+void S_LocalSound (char *sound)
+{
+       sfx_t   *sfx;
+
+       if (nosound.value)
+               return;
+       if (!sound_started)
+               return;
+               
+       sfx = S_PrecacheSound (sound);
+       if (!sfx)
+       {
+               Con_Printf ("S_LocalSound: can't cache %s\n", sound);
+               return;
+       }
+       S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1);
+}
+
+
+void S_ClearPrecache (void)
+{
+}
+
+
+void S_BeginPrecaching (void)
+{
+}
+
+
+void S_EndPrecaching (void)
+{
+}
+
diff --git a/snd_mem.c b/snd_mem.c
new file mode 100644 (file)
index 0000000..5ddef73
--- /dev/null
+++ b/snd_mem.c
@@ -0,0 +1,403 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_mem.c: sound caching
+
+#include "quakedef.h"
+
+int                    cache_full_cycle;
+
+byte *S_Alloc (int size);
+
+/*
+================
+ResampleSfx
+================
+*/
+void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
+{
+       int             outcount;
+       int             srcsample;
+       float   stepscale;
+       int             i;
+       int             sample, samplefrac, fracstep;
+       sfxcache_t      *sc;
+       
+       sc = Cache_Check (&sfx->cache);
+       if (!sc)
+               return;
+
+       stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2
+
+       outcount = sc->length / stepscale;
+       sc->length = outcount;
+       if (sc->loopstart != -1)
+               sc->loopstart = sc->loopstart / stepscale;
+
+       sc->speed = shm->speed;
+       if (loadas8bit.value)
+               sc->width = 1;
+       else
+               sc->width = inwidth;
+//     sc->stereo = 0;
+
+// resample / decimate to the current source rate
+
+       if (stepscale == 1 && inwidth == 1 && sc->width == 1)
+       {
+// fast special case
+               // LordHavoc: I do not serve the readability gods...
+               int *indata, *outdata;
+               int count4, count1;
+               count1 = outcount << sc->stereo;
+               count4 = count1 >> 2;
+               indata = (void *)data;
+               outdata = (void *)sc->data;
+               while (count4--)
+                       *outdata++ = *indata++ ^ 0x80808080;
+               if (count1 & 2)
+                       ((short*)outdata)[0] = ((short*)indata)[0] ^ 0x8080;
+               if (count1 & 1)
+                       ((char*)outdata)[2] = ((char*)indata)[2] ^ 0x80;
+               /*
+               if (sc->stereo) // LordHavoc: stereo sound support
+               {
+                       for (i=0 ; i<(outcount<<1) ; i++)
+                               ((signed char *)sc->data)[i] = (int)( (unsigned char)(data[i]) - 128);
+               }
+               else
+               {
+                       for (i=0 ; i<outcount ; i++)
+                               ((signed char *)sc->data)[i] = (int)( (unsigned char)(data[i]) - 128);
+               }
+               */
+       }
+       else
+       {
+// general case
+               Con_DPrintf("ResampleSfx: resampling sound %s\n", sfx->name);
+               samplefrac = 0;
+               fracstep = stepscale*256;
+               if (sc->stereo) // LordHavoc: stereo sound support
+               {
+                       for (i=0 ; i<outcount ; i+=2)
+                       {
+                               srcsample = samplefrac >> 8;
+                               samplefrac += fracstep;
+                               srcsample <<= 1;
+                               // left
+                               if (inwidth == 2)
+                                       sample = LittleShort ( ((short *)data)[srcsample] );
+                               else
+                                       sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
+                               if (sc->width == 2)
+                                       ((short *)sc->data)[i] = sample;
+                               else
+                                       ((signed char *)sc->data)[i] = sample >> 8;
+                               // right
+                               srcsample++;
+                               if (inwidth == 2)
+                                       sample = LittleShort ( ((short *)data)[srcsample] );
+                               else
+                                       sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
+                               if (sc->width == 2)
+                                       ((short *)sc->data)[i+1] = sample;
+                               else
+                                       ((signed char *)sc->data)[i+1] = sample >> 8;
+                       }
+               }
+               else
+               {
+                       for (i=0 ; i<outcount ; i++)
+                       {
+                               srcsample = samplefrac >> 8;
+                               samplefrac += fracstep;
+                               if (inwidth == 2)
+                                       sample = LittleShort ( ((short *)data)[srcsample] );
+                               else
+                                       sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
+                               if (sc->width == 2)
+                                       ((short *)sc->data)[i] = sample;
+                               else
+                                       ((signed char *)sc->data)[i] = sample >> 8;
+                       }
+               }
+       }
+}
+
+//=============================================================================
+
+/*
+==============
+S_LoadSound
+==============
+*/
+sfxcache_t *S_LoadSound (sfx_t *s)
+{
+    char       namebuffer[256];
+       byte    *data;
+       wavinfo_t       info;
+       int             len;
+       float   stepscale;
+       sfxcache_t      *sc;
+       byte    stackbuf[1*1024];               // avoid dirtying the cache heap
+
+// see if still in memory
+       sc = Cache_Check (&s->cache);
+       if (sc)
+               return sc;
+
+//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
+// load it in
+       strcpy(namebuffer, "sound/");
+       strcat(namebuffer, s->name);
+
+//     Con_Printf ("loading %s\n",namebuffer);
+
+       data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf), false);
+
+       if (!data)
+       {
+               Con_Printf ("Couldn't load %s\n", namebuffer);
+               return NULL;
+       }
+
+       info = GetWavinfo (s->name, data, com_filesize);
+       // LordHavoc: stereo sounds are now allowed (intended for music)
+       if (info.channels < 1 || info.channels > 2)
+       {
+               Con_Printf ("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
+               return NULL;
+       }
+       /*
+       if (info.channels != 1)
+       {
+               Con_Printf ("%s is a stereo sample\n",s->name);
+               return NULL;
+       }
+       */
+
+       stepscale = (float)info.rate / shm->speed;      
+       len = info.samples / stepscale;
+
+       len = len * info.width * info.channels;
+
+       sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name);
+       if (!sc)
+               return NULL;
+       
+       sc->length = info.samples;
+       sc->loopstart = info.loopstart;
+       sc->speed = info.rate;
+       sc->width = info.width;
+       sc->stereo = info.channels == 2;
+
+       ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
+
+       return sc;
+}
+
+
+
+/*
+===============================================================================
+
+WAV loading
+
+===============================================================================
+*/
+
+
+byte   *data_p;
+byte   *iff_end;
+byte   *last_chunk;
+byte   *iff_data;
+int    iff_chunk_len;
+
+
+short GetLittleShort(void)
+{
+       short val = 0;
+       val = *data_p;
+       val = val + (*(data_p+1)<<8);
+       data_p += 2;
+       return val;
+}
+
+int GetLittleLong(void)
+{
+       int val = 0;
+       val = *data_p;
+       val = val + (*(data_p+1)<<8);
+       val = val + (*(data_p+2)<<16);
+       val = val + (*(data_p+3)<<24);
+       data_p += 4;
+       return val;
+}
+
+void FindNextChunk(char *name)
+{
+       while (1)
+       {
+               data_p=last_chunk;
+
+               if (data_p >= iff_end)
+               {       // didn't find the chunk
+                       data_p = NULL;
+                       return;
+               }
+               
+               data_p += 4;
+               iff_chunk_len = GetLittleLong();
+               if (iff_chunk_len < 0)
+               {
+                       data_p = NULL;
+                       return;
+               }
+//             if (iff_chunk_len > 1024*1024)
+//                     Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
+               data_p -= 8;
+               last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
+               if (!strncmp(data_p, name, 4))
+                       return;
+       }
+}
+
+void FindChunk(char *name)
+{
+       last_chunk = iff_data;
+       FindNextChunk (name);
+}
+
+
+void DumpChunks(void)
+{
+       char    str[5];
+       
+       str[4] = 0;
+       data_p=iff_data;
+       do
+       {
+               memcpy (str, data_p, 4);
+               data_p += 4;
+               iff_chunk_len = GetLittleLong();
+               Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
+               data_p += (iff_chunk_len + 1) & ~1;
+       } while (data_p < iff_end);
+}
+
+/*
+============
+GetWavinfo
+============
+*/
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
+{
+       wavinfo_t       info;
+       int     i;
+       int     format;
+       int             samples;
+
+       memset (&info, 0, sizeof(info));
+
+       if (!wav)
+               return info;
+               
+       iff_data = wav;
+       iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+       FindChunk("RIFF");
+       if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
+       {
+               Con_Printf("Missing RIFF/WAVE chunks\n");
+               return info;
+       }
+
+// get "fmt " chunk
+       iff_data = data_p + 12;
+// DumpChunks ();
+
+       FindChunk("fmt ");
+       if (!data_p)
+       {
+               Con_Printf("Missing fmt chunk\n");
+               return info;
+       }
+       data_p += 8;
+       format = GetLittleShort();
+       if (format != 1)
+       {
+               Con_Printf("Microsoft PCM format only\n");
+               return info;
+       }
+
+       info.channels = GetLittleShort();
+       info.rate = GetLittleLong();
+       data_p += 4+2;
+       info.width = GetLittleShort() / 8;
+
+// get cue chunk
+       FindChunk("cue ");
+       if (data_p)
+       {
+               data_p += 32;
+               info.loopstart = GetLittleLong();
+//             Con_Printf("loopstart=%d\n", sfx->loopstart);
+
+       // if the next chunk is a LIST chunk, look for a cue length marker
+               FindNextChunk ("LIST");
+               if (data_p)
+               {
+                       if (!strncmp (data_p + 28, "mark", 4))
+                       {       // this is not a proper parse, but it works with cooledit...
+                               data_p += 24;
+                               i = GetLittleLong ();   // samples in loop
+                               info.samples = info.loopstart + i;
+//                             Con_Printf("looped length: %i\n", i);
+                       }
+               }
+       }
+       else
+               info.loopstart = -1;
+
+// find data chunk
+       FindChunk("data");
+       if (!data_p)
+       {
+               Con_Printf("Missing data chunk\n");
+               return info;
+       }
+
+       data_p += 4;
+       samples = GetLittleLong () / info.width;
+
+       if (info.samples)
+       {
+               if (samples < info.samples)
+                       Sys_Error ("Sound %s has a bad loop length", name);
+       }
+       else
+               info.samples = samples;
+
+       info.dataofs = data_p - wav;
+       
+       return info;
+}
+
diff --git a/snd_mix.c b/snd_mix.c
new file mode 100644 (file)
index 0000000..92cf6cd
--- /dev/null
+++ b/snd_mix.c
@@ -0,0 +1,446 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_mix.c -- portable code to mix sounds for snd_dma.c
+
+#include "quakedef.h"
+
+#ifdef _WIN32
+#include "winquake.h"
+#else
+#define DWORD  unsigned long
+#endif
+
+#define        PAINTBUFFER_SIZE        512
+portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
+int            snd_scaletable[32][256];
+int    *snd_p, snd_linear_count, snd_vol;
+short  *snd_out;
+
+void Snd_WriteLinearBlastStereo16 (void);
+
+extern cvar_t snd_swapstereo;
+#if    !id386
+void Snd_WriteLinearBlastStereo16 (void)
+{
+       int             i;
+       int             val;
+
+       if (snd_swapstereo.value)
+       {
+               for (i=0 ; i<snd_linear_count ; i+=2)
+               {
+                       val = (snd_p[i+1]*snd_vol)>>8;
+                       if (val > 0x7fff)
+                               snd_out[i] = 0x7fff;
+                       else if (val < (short)0x8000)
+                               snd_out[i] = (short)0x8000;
+                       else
+                               snd_out[i] = val;
+
+                       val = (snd_p[i]*snd_vol)>>8;
+                       if (val > 0x7fff)
+                               snd_out[i+1] = 0x7fff;
+                       else if (val < (short)0x8000)
+                               snd_out[i+1] = (short)0x8000;
+                       else
+                               snd_out[i+1] = val;
+               }
+       }
+       else
+       {
+               for (i=0 ; i<snd_linear_count ; i+=2)
+               {
+                       val = (snd_p[i]*snd_vol)>>8;
+                       if (val > 0x7fff)
+                               snd_out[i] = 0x7fff;
+                       else if (val < (short)0x8000)
+                               snd_out[i] = (short)0x8000;
+                       else
+                               snd_out[i] = val;
+
+                       val = (snd_p[i+1]*snd_vol)>>8;
+                       if (val > 0x7fff)
+                               snd_out[i+1] = 0x7fff;
+                       else if (val < (short)0x8000)
+                               snd_out[i+1] = (short)0x8000;
+                       else
+                               snd_out[i+1] = val;
+               }
+       }
+}
+#endif
+
+void S_TransferStereo16 (int endtime)
+{
+       int             lpos;
+       int             lpaintedtime;
+       DWORD   *pbuf;
+#ifdef _WIN32
+       int             reps;
+       DWORD   dwSize,dwSize2;
+       DWORD   *pbuf2;
+       HRESULT hresult;
+#endif
+       
+       snd_vol = volume.value*256;
+
+       snd_p = (int *) paintbuffer;
+       lpaintedtime = paintedtime;
+
+#ifdef _WIN32
+       if (pDSBuf)
+       {
+               reps = 0;
+
+               while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, 
+                                                                          &pbuf2, &dwSize2, 0)) != DS_OK)
+               {
+                       if (hresult != DSERR_BUFFERLOST)
+                       {
+                               Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
+                               S_Shutdown ();
+                               S_Startup ();
+                               return;
+                       }
+
+                       if (++reps > 10000)
+                       {
+                               Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n");
+                               S_Shutdown ();
+                               S_Startup ();
+                               return;
+                       }
+               }
+       }
+       else
+#endif
+       {
+               pbuf = (DWORD *)shm->buffer;
+       }
+
+       while (lpaintedtime < endtime)
+       {
+       // handle recirculating buffer issues
+               lpos = lpaintedtime & ((shm->samples>>1)-1);
+
+               snd_out = (short *) pbuf + (lpos<<1);
+
+               snd_linear_count = (shm->samples>>1) - lpos;
+               if (lpaintedtime + snd_linear_count > endtime)
+                       snd_linear_count = endtime - lpaintedtime;
+
+               snd_linear_count <<= 1;
+
+       // write a linear blast of samples
+               Snd_WriteLinearBlastStereo16 ();
+
+               snd_p += snd_linear_count;
+               lpaintedtime += (snd_linear_count>>1);
+       }
+
+#ifdef _WIN32
+       if (pDSBuf)
+               pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
+#endif
+}
+
+void S_TransferPaintBuffer(int endtime)
+{
+       int     out_idx;
+       int     count;
+       int     out_mask;
+       int     *p;
+       int     step;
+       int             val;
+       int             snd_vol;
+       DWORD   *pbuf;
+#ifdef _WIN32
+       int             reps;
+       DWORD   dwSize,dwSize2;
+       DWORD   *pbuf2;
+       HRESULT hresult;
+#endif
+
+       if (shm->samplebits == 16 && shm->channels == 2)
+       {
+               S_TransferStereo16 (endtime);
+               return;
+       }
+       
+       p = (int *) paintbuffer;
+       count = (endtime - paintedtime) * shm->channels;
+       out_mask = shm->samples - 1; 
+       out_idx = paintedtime * shm->channels & out_mask;
+       step = 3 - shm->channels;
+       snd_vol = volume.value*256;
+
+#ifdef _WIN32
+       if (pDSBuf)
+       {
+               reps = 0;
+
+               while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, 
+                                                                          &pbuf2,&dwSize2, 0)) != DS_OK)
+               {
+                       if (hresult != DSERR_BUFFERLOST)
+                       {
+                               Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n");
+                               S_Shutdown ();
+                               S_Startup ();
+                               return;
+                       }
+
+                       if (++reps > 10000)
+                       {
+                               Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n");
+                               S_Shutdown ();
+                               S_Startup ();
+                               return;
+                       }
+               }
+       }
+       else
+#endif
+       {
+               pbuf = (DWORD *)shm->buffer;
+       }
+
+       if (shm->samplebits == 16)
+       {
+               short *out = (short *) pbuf;
+               while (count--)
+               {
+                       val = (*p * snd_vol) >> 8;
+                       p+= step;
+                       if (val > 0x7fff)
+                               val = 0x7fff;
+                       else if (val < (short)0x8000)
+                               val = (short)0x8000;
+                       out[out_idx] = val;
+                       out_idx = (out_idx + 1) & out_mask;
+               }
+       }
+       else if (shm->samplebits == 8)
+       {
+               unsigned char *out = (unsigned char *) pbuf;
+               while (count--)
+               {
+                       val = (*p * snd_vol) >> 8;
+                       p+= step;
+                       if (val > 0x7fff)
+                               val = 0x7fff;
+                       else if (val < (short)0x8000)
+                               val = (short)0x8000;
+                       out[out_idx] = (val>>8) + 128;
+                       out_idx = (out_idx + 1) & out_mask;
+               }
+       }
+
+#ifdef _WIN32
+       if (pDSBuf) {
+               DWORD dwNewpos, dwWrite;
+               int il = paintedtime;
+               int ir = endtime - paintedtime;
+               
+               ir += il;
+
+               pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
+
+               pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite);
+
+//             if ((dwNewpos >= il) && (dwNewpos <= ir))
+//                     Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos);
+       }
+#endif
+}
+
+
+/*
+===============================================================================
+
+CHANNEL MIXING
+
+===============================================================================
+*/
+
+void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
+void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
+
+void S_PaintChannels(int endtime)
+{
+       int     i;
+       int     end;
+       channel_t *ch;
+       sfxcache_t      *sc;
+       int             ltime, count;
+
+       while (paintedtime < endtime)
+       {
+       // if paintbuffer is smaller than DMA buffer
+               end = endtime;
+               if (endtime - paintedtime > PAINTBUFFER_SIZE)
+                       end = paintedtime + PAINTBUFFER_SIZE;
+
+       // clear the paint buffer
+               memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
+
+       // paint in the channels.
+               ch = channels;
+               for (i=0; i<total_channels ; i++, ch++)
+               {
+                       if (!ch->sfx)
+                               continue;
+                       if (!ch->leftvol && !ch->rightvol)
+                               continue;
+                       sc = S_LoadSound (ch->sfx);
+                       if (!sc)
+                               continue;
+
+                       ltime = paintedtime;
+
+                       while (ltime < end)
+                       {       // paint up to end
+                               if (ch->end < end)
+                                       count = ch->end - ltime;
+                               else
+                                       count = end - ltime;
+
+                               if (count > 0)
+                               {       
+                                       if (sc->width == 1)
+                                               SND_PaintChannelFrom8(ch, sc, count);
+                                       else
+                                               SND_PaintChannelFrom16(ch, sc, count);
+       
+                                       ltime += count;
+                               }
+
+                       // if at end of loop, restart
+                               if (ltime >= ch->end)
+                               {
+                                       if (sc->loopstart >= 0)
+                                       {
+                                               ch->pos = sc->loopstart;
+                                               ch->end = ltime + sc->length - ch->pos;
+                                       }
+                                       else                            
+                                       {       // channel just stopped
+                                               ch->sfx = NULL;
+                                               break;
+                                       }
+                               }
+                       }
+                                                                                                                         
+               }
+
+       // transfer out according to DMA format
+               S_TransferPaintBuffer(end);
+               paintedtime = end;
+       }
+}
+
+void SND_InitScaletable (void)
+{
+       int             i, j;
+       
+       for (i=0 ; i<32 ; i++)
+               for (j=0 ; j<256 ; j++)
+                       snd_scaletable[i][j] = ((signed char)j) * i * 8;
+}
+
+
+#if    !id386
+
+void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
+{
+//     int     data;
+       int             *lscale, *rscale;
+       unsigned char *sfx;
+       int             i;
+
+       if (ch->leftvol > 255)
+               ch->leftvol = 255;
+       if (ch->rightvol > 255)
+               ch->rightvol = 255;
+               
+       lscale = snd_scaletable[ch->leftvol >> 3];
+       rscale = snd_scaletable[ch->rightvol >> 3];
+       if (sc->stereo) // LordHavoc: stereo sound support, and optimizations
+       {
+               sfx = (unsigned char *)sc->data + ch->pos * 2;
+
+               for (i=0 ; i<count ; i++)
+               {
+                       paintbuffer[i].left += lscale[*sfx++];
+                       paintbuffer[i].right += rscale[*sfx++];
+               }
+               
+       }
+       else
+       {
+               sfx = (unsigned char *)sc->data + ch->pos;
+
+               for (i=0 ; i<count ; i++)
+               {
+                       paintbuffer[i].left += lscale[*sfx];
+                       paintbuffer[i].right += rscale[*sfx++];
+               }
+               
+       }
+       ch->pos += count;
+}
+
+#endif // !id386
+
+
+void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
+{
+//     int data;
+//     int left, right;
+       int leftvol, rightvol;
+       signed short *sfx;
+       int     i;
+
+       leftvol = ch->leftvol;
+       rightvol = ch->rightvol;
+       if (sc->stereo) // LordHavoc: stereo sound support, and optimizations
+       {
+               sfx = (signed short *)sc->data + ch->pos * 2;
+
+               for (i=0 ; i<count ; i++)
+               {
+                       paintbuffer[i].left += (short) ((int) (*sfx++ * leftvol) >> 8);
+                       paintbuffer[i].right += (short) ((int) (*sfx++ * rightvol) >> 8);
+               }
+       }
+       else
+       {
+               sfx = (signed short *)sc->data + ch->pos;
+
+               for (i=0 ; i<count ; i++)
+               {
+                       paintbuffer[i].left += (short) ((int) (*sfx * leftvol) >> 8);
+                       paintbuffer[i].right += (short) ((int) (*sfx++ * rightvol) >> 8);
+               }
+       }
+
+       ch->pos += count;
+}
+
diff --git a/snd_win.c b/snd_win.c
new file mode 100644 (file)
index 0000000..2aa42d6
--- /dev/null
+++ b/snd_win.c
@@ -0,0 +1,729 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include "quakedef.h"
+#include "winquake.h"
+
+#define iDirectSoundCreate(a,b,c)      pDirectSoundCreate(a,b,c)
+
+HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
+
+// 64K is > 1 second at 16-bit, 22050 Hz
+#define        WAV_BUFFERS                             64
+#define        WAV_MASK                                0x3F
+#define        WAV_BUFFER_SIZE                 0x0400
+#define SECONDARY_BUFFER_SIZE  0x10000
+
+typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
+
+static qboolean        wavonly;
+static qboolean        dsound_init;
+static qboolean        wav_init;
+static qboolean        snd_firsttime = true, snd_isdirect, snd_iswave;
+static qboolean        primary_format_set;
+
+static int     sample16;
+static int     snd_sent, snd_completed;
+
+
+/* 
+ * Global variables. Must be visible to window-procedure function 
+ *  so it can unlock and free the data block after it has been played. 
+ */ 
+
+HANDLE         hData;
+HPSTR          lpData, lpData2;
+
+HGLOBAL                hWaveHdr;
+LPWAVEHDR      lpWaveHdr;
+
+HWAVEOUT    hWaveOut; 
+
+WAVEOUTCAPS    wavecaps;
+
+DWORD  gSndBufSize;
+
+MMTIME         mmstarttime;
+
+LPDIRECTSOUND pDS;
+LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
+
+HINSTANCE hInstDS;
+
+qboolean SNDDMA_InitDirect (void);
+qboolean SNDDMA_InitWav (void);
+
+
+/*
+==================
+S_BlockSound
+==================
+*/
+void S_BlockSound (void)
+{
+
+// DirectSound takes care of blocking itself
+       if (snd_iswave)
+       {
+               snd_blocked++;
+
+               if (snd_blocked == 1)
+               {
+                       waveOutReset (hWaveOut);
+               }
+       }
+}
+
+
+/*
+==================
+S_UnblockSound
+==================
+*/
+void S_UnblockSound (void)
+{
+
+// DirectSound takes care of blocking itself
+       if (snd_iswave)
+       {
+               snd_blocked--;
+       }
+}
+
+
+/*
+==================
+FreeSound
+==================
+*/
+void FreeSound (void)
+{
+       int             i;
+
+       if (pDSBuf)
+       {
+               pDSBuf->lpVtbl->Stop(pDSBuf);
+               pDSBuf->lpVtbl->Release(pDSBuf);
+       }
+
+// only release primary buffer if it's not also the mixing buffer we just released
+       if (pDSPBuf && (pDSBuf != pDSPBuf))
+       {
+               pDSPBuf->lpVtbl->Release(pDSPBuf);
+       }
+
+       if (pDS)
+       {
+               pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
+               pDS->lpVtbl->Release(pDS);
+       }
+
+       if (hWaveOut)
+       {
+               waveOutReset (hWaveOut);
+
+               if (lpWaveHdr)
+               {
+                       for (i=0 ; i< WAV_BUFFERS ; i++)
+                               waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
+               }
+
+               waveOutClose (hWaveOut);
+
+               if (hWaveHdr)
+               {
+                       GlobalUnlock(hWaveHdr); 
+                       GlobalFree(hWaveHdr);
+               }
+
+               if (hData)
+               {
+                       GlobalUnlock(hData);
+                       GlobalFree(hData);
+               }
+
+       }
+
+       pDS = NULL;
+       pDSBuf = NULL;
+       pDSPBuf = NULL;
+       hWaveOut = 0;
+       hData = 0;
+       hWaveHdr = 0;
+       lpData = NULL;
+       lpWaveHdr = NULL;
+       dsound_init = false;
+       wav_init = false;
+}
+
+
+/*
+==================
+SNDDMA_InitDirect
+
+Direct-Sound support
+==================
+*/
+sndinitstat SNDDMA_InitDirect (void)
+{
+       DSBUFFERDESC    dsbuf;
+       DSBCAPS                 dsbcaps;
+       DWORD                   dwSize, dwWrite;
+       DSCAPS                  dscaps;
+       WAVEFORMATEX    format, pformat; 
+       HRESULT                 hresult;
+       int                             reps;
+
+       memset ((void *)&sn, 0, sizeof (sn));
+
+       shm = &sn;
+
+       shm->channels = 2;
+       shm->samplebits = 16;
+       shm->speed = 11025;
+
+       memset (&format, 0, sizeof(format));
+       format.wFormatTag = WAVE_FORMAT_PCM;
+    format.nChannels = shm->channels;
+    format.wBitsPerSample = shm->samplebits;
+    format.nSamplesPerSec = shm->speed;
+    format.nBlockAlign = format.nChannels
+               *format.wBitsPerSample / 8;
+    format.cbSize = 0;
+    format.nAvgBytesPerSec = format.nSamplesPerSec
+               *format.nBlockAlign; 
+
+       if (!hInstDS)
+       {
+               hInstDS = LoadLibrary("dsound.dll");
+               
+               if (hInstDS == NULL)
+               {
+                       Con_SafePrintf ("Couldn't load dsound.dll\n");
+                       return SIS_FAILURE;
+               }
+
+               pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
+
+               if (!pDirectSoundCreate)
+               {
+                       Con_SafePrintf ("Couldn't get DS proc addr\n");
+                       return SIS_FAILURE;
+               }
+       }
+
+       while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
+       {
+               if (hresult != DSERR_ALLOCATED)
+               {
+                       Con_SafePrintf ("DirectSound create failed\n");
+                       return SIS_FAILURE;
+               }
+
+               if (MessageBox (NULL,
+                                               "The sound hardware is in use by another app.\n\n"
+                                           "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
+                                               "Sound not available",
+                                               MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
+               {
+                       Con_SafePrintf ("DirectSoundCreate failure\n"
+                                                       "  hardware already in use\n");
+                       return SIS_NOTAVAIL;
+               }
+       }
+
+       dscaps.dwSize = sizeof(dscaps);
+
+       if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
+       {
+               Con_SafePrintf ("Couldn't get DS caps\n");
+       }
+
+       if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
+       {
+               Con_SafePrintf ("No DirectSound driver installed\n");
+               FreeSound ();
+               return SIS_FAILURE;
+       }
+
+       if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
+       {
+               Con_SafePrintf ("Set coop level failed\n");
+               FreeSound ();
+               return SIS_FAILURE;
+       }
+
+// get access to the primary buffer, if possible, so we can set the
+// sound hardware format
+       memset (&dsbuf, 0, sizeof(dsbuf));
+       dsbuf.dwSize = sizeof(DSBUFFERDESC);
+       dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
+       dsbuf.dwBufferBytes = 0;
+       dsbuf.lpwfxFormat = NULL;
+
+       memset(&dsbcaps, 0, sizeof(dsbcaps));
+       dsbcaps.dwSize = sizeof(dsbcaps);
+       primary_format_set = false;
+
+       if (!COM_CheckParm ("-snoforceformat"))
+       {
+               if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
+               {
+                       pformat = format;
+
+                       if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
+                       {
+                               if (snd_firsttime)
+                                       Con_SafePrintf ("Set primary sound buffer format: no\n");
+                       }
+                       else
+                       {
+                               if (snd_firsttime)
+                                       Con_SafePrintf ("Set primary sound buffer format: yes\n");
+
+                               primary_format_set = true;
+                       }
+               }
+       }
+
+       if (!primary_format_set || !COM_CheckParm ("-primarysound"))
+       {
+       // create the secondary buffer we'll actually work with
+               memset (&dsbuf, 0, sizeof(dsbuf));
+               dsbuf.dwSize = sizeof(DSBUFFERDESC);
+               dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
+               dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
+               dsbuf.lpwfxFormat = &format;
+
+               memset(&dsbcaps, 0, sizeof(dsbcaps));
+               dsbcaps.dwSize = sizeof(dsbcaps);
+
+               if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
+               {
+                       Con_SafePrintf ("DS:CreateSoundBuffer Failed");
+                       FreeSound ();
+                       return SIS_FAILURE;
+               }
+
+               shm->channels = format.nChannels;
+               shm->samplebits = format.wBitsPerSample;
+               shm->speed = format.nSamplesPerSec;
+
+               if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
+               {
+                       Con_SafePrintf ("DS:GetCaps failed\n");
+                       FreeSound ();
+                       return SIS_FAILURE;
+               }
+
+               if (snd_firsttime)
+                       Con_SafePrintf ("Using secondary sound buffer\n");
+       }
+       else
+       {
+               if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
+               {
+                       Con_SafePrintf ("Set coop level failed\n");
+                       FreeSound ();
+                       return SIS_FAILURE;
+               }
+
+               if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
+               {
+                       Con_Printf ("DS:GetCaps failed\n");
+                       return SIS_FAILURE;
+               }
+
+               pDSBuf = pDSPBuf;
+               Con_SafePrintf ("Using primary sound buffer\n");
+       }
+
+       // Make sure mixer is active
+       pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+
+       if (snd_firsttime)
+               Con_SafePrintf("   %d channel(s)\n"
+                              "   %d bits/sample\n"
+                                          "   %d bytes/sec\n",
+                                          shm->channels, shm->samplebits, shm->speed);
+       
+       gSndBufSize = dsbcaps.dwBufferBytes;
+
+// initialize the buffer
+       reps = 0;
+
+       while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
+       {
+               if (hresult != DSERR_BUFFERLOST)
+               {
+                       Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
+                       FreeSound ();
+                       return SIS_FAILURE;
+               }
+
+               if (++reps > 10000)
+               {
+                       Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
+                       FreeSound ();
+                       return SIS_FAILURE;
+               }
+
+       }
+
+       memset(lpData, 0, dwSize);
+//             lpData[4] = lpData[5] = 0x7f;   // force a pop for debugging
+
+       pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
+
+       /* we don't want anyone to access the buffer directly w/o locking it first. */
+       lpData = NULL; 
+
+       pDSBuf->lpVtbl->Stop(pDSBuf);
+       pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
+       pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+
+       shm->soundalive = true;
+       shm->splitbuffer = false;
+       shm->samples = gSndBufSize/(shm->samplebits/8);
+       shm->samplepos = 0;
+       shm->submission_chunk = 1;
+       shm->buffer = (unsigned char *) lpData;
+       sample16 = (shm->samplebits/8) - 1;
+
+       dsound_init = true;
+
+       return SIS_SUCCESS;
+}
+
+
+/*
+==================
+SNDDM_InitWav
+
+Crappy windows multimedia base
+==================
+*/
+qboolean SNDDMA_InitWav (void)
+{
+       WAVEFORMATEX  format; 
+       int                             i;
+       HRESULT                 hr;
+       
+       snd_sent = 0;
+       snd_completed = 0;
+
+       shm = &sn;
+
+       shm->channels = 2;
+       shm->samplebits = 16;
+       shm->speed = 11025;
+
+       memset (&format, 0, sizeof(format));
+       format.wFormatTag = WAVE_FORMAT_PCM;
+       format.nChannels = shm->channels;
+       format.wBitsPerSample = shm->samplebits;
+       format.nSamplesPerSec = shm->speed;
+       format.nBlockAlign = format.nChannels
+               *format.wBitsPerSample / 8;
+       format.cbSize = 0;
+       format.nAvgBytesPerSec = format.nSamplesPerSec
+               *format.nBlockAlign; 
+       
+       /* Open a waveform device for output using window callback. */ 
+       while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, 
+                                       &format, 
+                                       0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
+       {
+               if (hr != MMSYSERR_ALLOCATED)
+               {
+                       Con_SafePrintf ("waveOutOpen failed\n");
+                       return false;
+               }
+
+               if (MessageBox (NULL,
+                                               "The sound hardware is in use by another app.\n\n"
+                                           "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
+                                               "Sound not available",
+                                               MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
+               {
+                       Con_SafePrintf ("waveOutOpen failure;\n"
+                                                       "  hardware already in use\n");
+                       return false;
+               }
+       } 
+
+       /* 
+        * Allocate and lock memory for the waveform data. The memory 
+        * for waveform data must be globally allocated with 
+        * GMEM_MOVEABLE and GMEM_SHARE flags. 
+
+       */ 
+       gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
+       hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); 
+       if (!hData) 
+       { 
+               Con_SafePrintf ("Sound: Out of memory.\n");
+               FreeSound ();
+               return false; 
+       }
+       lpData = GlobalLock(hData);
+       if (!lpData)
+       { 
+               Con_SafePrintf ("Sound: Failed to lock.\n");
+               FreeSound ();
+               return false; 
+       } 
+       memset (lpData, 0, gSndBufSize);
+
+       /* 
+        * Allocate and lock memory for the header. This memory must 
+        * also be globally allocated with GMEM_MOVEABLE and 
+        * GMEM_SHARE flags. 
+        */ 
+       hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, 
+               (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); 
+
+       if (hWaveHdr == NULL)
+       { 
+               Con_SafePrintf ("Sound: Failed to Alloc header.\n");
+               FreeSound ();
+               return false; 
+       } 
+
+       lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); 
+
+       if (lpWaveHdr == NULL)
+       { 
+               Con_SafePrintf ("Sound: Failed to lock header.\n");
+               FreeSound ();
+               return false; 
+       }
+
+       memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
+
+       /* After allocation, set up and prepare headers. */ 
+       for (i=0 ; i<WAV_BUFFERS ; i++)
+       {
+               lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE; 
+               lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
+
+               if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
+                               MMSYSERR_NOERROR)
+               {
+                       Con_SafePrintf ("Sound: failed to prepare wave headers\n");
+                       FreeSound ();
+                       return false;
+               }
+       }
+
+       shm->soundalive = true;
+       shm->splitbuffer = false;
+       shm->samples = gSndBufSize/(shm->samplebits/8);
+       shm->samplepos = 0;
+       shm->submission_chunk = 1;
+       shm->buffer = (unsigned char *) lpData;
+       sample16 = (shm->samplebits/8) - 1;
+
+       wav_init = true;
+
+       return true;
+}
+
+/*
+==================
+SNDDMA_Init
+
+Try to find a sound device to mix for.
+Returns false if nothing is found.
+==================
+*/
+
+int SNDDMA_Init(void)
+{
+       sndinitstat     stat;
+
+       if (COM_CheckParm ("-wavonly"))
+               wavonly = true;
+
+       dsound_init = wav_init = 0;
+
+       stat = SIS_FAILURE;     // assume DirectSound won't initialize
+
+       /* Init DirectSound */
+       if (!wavonly)
+       {
+               if (snd_firsttime || snd_isdirect)
+               {
+                       stat = SNDDMA_InitDirect ();;
+
+                       if (stat == SIS_SUCCESS)
+                       {
+                               snd_isdirect = true;
+
+                               if (snd_firsttime)
+                                       Con_SafePrintf ("DirectSound initialized\n");
+                       }
+                       else
+                       {
+                               snd_isdirect = false;
+                               Con_SafePrintf ("DirectSound failed to init\n");
+                       }
+               }
+       }
+
+// if DirectSound didn't succeed in initializing, try to initialize
+// waveOut sound, unless DirectSound failed because the hardware is
+// already allocated (in which case the user has already chosen not
+// to have sound)
+       if (!dsound_init && (stat != SIS_NOTAVAIL))
+       {
+               if (snd_firsttime || snd_iswave)
+               {
+
+                       snd_iswave = SNDDMA_InitWav ();
+
+                       if (snd_iswave)
+                       {
+                               if (snd_firsttime)
+                                       Con_SafePrintf ("Wave sound initialized\n");
+                       }
+                       else
+                       {
+                               Con_SafePrintf ("Wave sound failed to init\n");
+                       }
+               }
+       }
+
+       snd_firsttime = false;
+
+       if (!dsound_init && !wav_init)
+       {
+//             if (snd_firsttime)
+//                     Con_SafePrintf ("No sound device initialized\n");
+
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+==============
+SNDDMA_GetDMAPos
+
+return the current sample position (in mono samples read)
+inside the recirculating dma buffer, so the mixing code will know
+how many sample are required to fill it up.
+===============
+*/
+int SNDDMA_GetDMAPos(void)
+{
+       MMTIME  mmtime;
+       int             s;
+       DWORD   dwWrite;
+
+       if (dsound_init) 
+       {
+               mmtime.wType = TIME_SAMPLES;
+               pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
+               s = mmtime.u.sample - mmstarttime.u.sample;
+       }
+       else if (wav_init)
+       {
+               s = snd_sent * WAV_BUFFER_SIZE;
+       }
+
+
+       s >>= sample16;
+
+       s &= (shm->samples-1);
+
+       return s;
+}
+
+/*
+==============
+SNDDMA_Submit
+
+Send sound to device if buffer isn't really the dma buffer
+===============
+*/
+void SNDDMA_Submit(void)
+{
+       LPWAVEHDR       h;
+       int                     wResult;
+
+       if (!wav_init)
+               return;
+
+       //
+       // find which sound blocks have completed
+       //
+       while (1)
+       {
+               if ( snd_completed == snd_sent )
+               {
+                       Con_DPrintf ("Sound overrun\n");
+                       break;
+               }
+
+               if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
+               {
+                       break;
+               }
+
+               snd_completed++;        // this buffer has been played
+       }
+
+       //
+       // submit two new sound blocks
+       //
+       while (((snd_sent - snd_completed) >> sample16) < 4)
+       {
+               h = lpWaveHdr + ( snd_sent&WAV_MASK );
+
+               snd_sent++;
+               /* 
+                * Now the data block can be sent to the output device. The 
+                * waveOutWrite function returns immediately and waveform 
+                * data is sent to the output device in the background. 
+                */ 
+               wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); 
+
+               if (wResult != MMSYSERR_NOERROR)
+               { 
+                       Con_SafePrintf ("Failed to write block to device\n");
+                       FreeSound ();
+                       return; 
+               } 
+       }
+}
+
+/*
+==============
+SNDDMA_Shutdown
+
+Reset the sound device for exiting
+===============
+*/
+void SNDDMA_Shutdown(void)
+{
+       FreeSound ();
+}
+
diff --git a/sound.h b/sound.h
new file mode 100644 (file)
index 0000000..4818755
--- /dev/null
+++ b/sound.h
@@ -0,0 +1,179 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sound.h -- client sound i/o functions
+
+#ifndef __SOUND__
+#define __SOUND__
+
+#define DEFAULT_SOUND_PACKET_VOLUME 255
+#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
+
+// !!! if this is changed, it much be changed in asm_i386.h too !!!
+typedef struct
+{
+       int left;
+       int right;
+} portable_samplepair_t;
+
+typedef struct sfx_s
+{
+       char    name[MAX_QPATH];
+       cache_user_t    cache;
+} sfx_t;
+
+// !!! if this is changed, it much be changed in asm_i386.h too !!!
+typedef struct
+{
+       int     length;
+       int     loopstart;
+       int     speed;
+       int     width;
+       int     stereo;
+       byte    data[1];                // variable sized
+} sfxcache_t;
+
+typedef struct
+{
+       qboolean                gamealive;
+       qboolean                soundalive;
+       qboolean                splitbuffer;
+       int                             channels;
+       int                             samples;                                // mono samples in buffer
+       int                             submission_chunk;               // don't mix less than this #
+       int                             samplepos;                              // in mono samples
+       int                             samplebits;
+       int                             speed;
+       unsigned char   *buffer;
+} dma_t;
+
+// !!! if this is changed, it much be changed in asm_i386.h too !!!
+typedef struct
+{
+       sfx_t   *sfx;                   // sfx number
+       int             leftvol;                // 0-255 volume
+       int             rightvol;               // 0-255 volume
+       int             end;                    // end time in global paintsamples
+       int     pos;                    // sample position in sfx
+       int             looping;                // where to loop, -1 = no looping
+       int             entnum;                 // to allow overriding a specific sound
+       int             entchannel;             //
+       vec3_t  origin;                 // origin of sound effect
+       vec_t   dist_mult;              // distance multiplier (attenuation/clipK)
+       int             master_vol;             // 0-255 master volume
+} channel_t;
+
+typedef struct
+{
+       int             rate;
+       int             width;
+       int             channels;
+       int             loopstart;
+       int             samples;
+       int             dataofs;                // chunk starts this many bytes from file start
+} wavinfo_t;
+
+void S_Init (void);
+void S_Startup (void);
+void S_Shutdown (void);
+void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol,  float attenuation);
+void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation);
+void S_StopSound (int entnum, int entchannel);
+void S_StopAllSounds(qboolean clear);
+void S_ClearBuffer (void);
+void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up);
+void S_ExtraUpdate (void);
+
+sfx_t *S_PrecacheSound (char *sample);
+void S_TouchSound (char *sample);
+void S_ClearPrecache (void);
+void S_BeginPrecaching (void);
+void S_EndPrecaching (void);
+void S_PaintChannels(int endtime);
+void S_InitPaintChannels (void);
+
+// picks a channel based on priorities, empty slots, number of channels
+channel_t *SND_PickChannel(int entnum, int entchannel);
+
+// spatializes a channel
+void SND_Spatialize(channel_t *ch);
+
+// initializes cycling through a DMA buffer and returns information on it
+qboolean SNDDMA_Init(void);
+
+// gets the current DMA position
+int SNDDMA_GetDMAPos(void);
+
+// shutdown the DMA xfer.
+void SNDDMA_Shutdown(void);
+
+// ====================================================================
+// User-setable variables
+// ====================================================================
+
+// LordHavoc: increased from 128 to 516 (4 for NUM_AMBIENTS)
+#define        MAX_CHANNELS                    516
+// LordHavoc: increased maximum sound channels from 8 to 128
+#define        MAX_DYNAMIC_CHANNELS    128
+
+
+extern channel_t   channels[MAX_CHANNELS];
+// 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds
+// MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc
+// MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds
+
+extern int                     total_channels;
+
+//
+// Fake dma is a synchronous faking of the DMA progress used for
+// isolating performance in the renderer.  The fakedma_updates is
+// number of times S_Update() is called per second.
+//
+
+extern qboolean                fakedma;
+extern int                     fakedma_updates;
+extern int             paintedtime;
+extern vec3_t listener_origin;
+extern vec3_t listener_forward;
+extern vec3_t listener_right;
+extern vec3_t listener_up;
+extern volatile dma_t *shm;
+extern volatile dma_t sn;
+extern vec_t sound_nominal_clip_dist;
+
+extern cvar_t loadas8bit;
+extern cvar_t bgmvolume;
+extern cvar_t volume;
+
+extern qboolean        snd_initialized;
+
+extern int             snd_blocked;
+
+void S_LocalSound (char *s);
+sfxcache_t *S_LoadSound (sfx_t *s);
+
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength);
+
+void SND_InitScaletable (void);
+void SNDDMA_Submit(void);
+
+void S_AmbientOff (void);
+void S_AmbientOn (void);
+
+#endif
diff --git a/spritegn.h b/spritegn.h
new file mode 100644 (file)
index 0000000..007a8bb
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// spritegn.h: header file for sprite generation program
+//
+
+// **********************************************************
+// * This file must be identical in the spritegen directory *
+// * and in the Quake directory, because it's used to       *
+// * pass data from one to the other via .spr files.        *
+// **********************************************************
+
+//-------------------------------------------------------
+// This program generates .spr sprite package files.
+// The format of the files is as follows:
+//
+// dsprite_t file header structure
+// <repeat dsprite_t.numframes times>
+//   <if spritegroup, repeat dspritegroup_t.numframes times>
+//     dspriteframe_t frame header structure
+//     sprite bitmap
+//   <else (single sprite frame)>
+//     dspriteframe_t frame header structure
+//     sprite bitmap
+// <endrepeat>
+//-------------------------------------------------------
+
+#define SPRITE_VERSION 1
+#define HALFLIFESPRITE_VERSION 2
+#define SPRITE32_VERSION       32
+
+// TODO: shorten these?
+typedef struct {
+       int                     ident;
+       int                     version;
+       int                     type;
+       float           boundingradius;
+       int                     width;
+       int                     height;
+       int                     numframes;
+       float           beamlength;
+       synctype_t      synctype;
+} dsprite_t;
+
+#define SPR_VP_PARALLEL_UPRIGHT                0
+#define SPR_FACING_UPRIGHT                     1
+#define SPR_VP_PARALLEL                                2
+#define SPR_ORIENTED                           3
+#define SPR_VP_PARALLEL_ORIENTED       4
+
+typedef struct {
+       int                     origin[2];
+       int                     width;
+       int                     height;
+} dspriteframe_t;
+
+typedef struct {
+       int                     numframes;
+} dspritegroup_t;
+
+typedef struct {
+       float   interval;
+} dspriteinterval_t;
+
+typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t;
+
+typedef struct {
+       spriteframetype_t       type;
+} dspriteframetype_t;
+
+#define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I')
+                                                                                                               // little-endian "IDSP"
+
diff --git a/sv_main.c b/sv_main.c
new file mode 100644 (file)
index 0000000..5fbc78d
--- /dev/null
+++ b/sv_main.c
@@ -0,0 +1,1356 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_main.c -- server main program
+
+#include "quakedef.h"
+
+server_t               sv;
+server_static_t        svs;
+
+char   localmodels[MAX_MODELS][5];                     // inline model names for precache
+
+//============================================================================
+
+/*
+===============
+SV_Init
+===============
+*/
+void SV_Init (void)
+{
+       int             i;
+       extern  cvar_t  sv_maxvelocity;
+       extern  cvar_t  sv_gravity;
+       extern  cvar_t  sv_nostep;
+       extern  cvar_t  sv_friction;
+       extern  cvar_t  sv_edgefriction;
+       extern  cvar_t  sv_stopspeed;
+       extern  cvar_t  sv_maxspeed;
+       extern  cvar_t  sv_accelerate;
+       extern  cvar_t  sv_idealpitchscale;
+       extern  cvar_t  sv_aim;
+
+       Cvar_RegisterVariable (&sv_maxvelocity);
+       Cvar_RegisterVariable (&sv_gravity);
+       Cvar_RegisterVariable (&sv_friction);
+       Cvar_RegisterVariable (&sv_edgefriction);
+       Cvar_RegisterVariable (&sv_stopspeed);
+       Cvar_RegisterVariable (&sv_maxspeed);
+       Cvar_RegisterVariable (&sv_accelerate);
+       Cvar_RegisterVariable (&sv_idealpitchscale);
+       Cvar_RegisterVariable (&sv_aim);
+       Cvar_RegisterVariable (&sv_nostep);
+
+       for (i=0 ; i<MAX_MODELS ; i++)
+               sprintf (localmodels[i], "*%i", i);
+}
+
+/*
+=============================================================================
+
+EVENT MESSAGES
+
+=============================================================================
+*/
+
+/*  
+==================
+SV_StartParticle
+
+Make sure the event gets sent to all clients
+==================
+*/
+void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
+{
+       int             i, v;
+
+       if (sv.datagram.cursize > MAX_DATAGRAM-16)
+               return; 
+       MSG_WriteByte (&sv.datagram, svc_particle);
+       MSG_WriteCoord (&sv.datagram, org[0]);
+       MSG_WriteCoord (&sv.datagram, org[1]);
+       MSG_WriteCoord (&sv.datagram, org[2]);
+       for (i=0 ; i<3 ; i++)
+       {
+               v = dir[i]*16;
+               if (v > 127)
+                       v = 127;
+               else if (v < -128)
+                       v = -128;
+               MSG_WriteChar (&sv.datagram, v);
+       }
+       MSG_WriteByte (&sv.datagram, count);
+       MSG_WriteByte (&sv.datagram, color);
+}           
+
+/*  
+==================
+SV_StartSound
+
+Each entity can have eight independant sound sources, like voice,
+weapon, feet, etc.
+
+Channel 0 is an auto-allocate channel, the others override anything
+allready running on that entity/channel pair.
+
+An attenuation of 0 will play full volume everywhere in the level.
+Larger attenuations will drop off.  (max 4 attenuation)
+
+==================
+*/  
+void SV_StartSound (edict_t *entity, int channel, char *sample, int volume,
+    float attenuation)
+{       
+    int         sound_num;
+    int field_mask;
+    int                        i;
+       int                     ent;
+       
+       if (volume < 0 || volume > 255)
+               Sys_Error ("SV_StartSound: volume = %i", volume);
+
+       if (attenuation < 0 || attenuation > 4)
+               Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
+
+       if (channel < 0 || channel > 7)
+               Sys_Error ("SV_StartSound: channel = %i", channel);
+
+       if (sv.datagram.cursize > MAX_DATAGRAM-16)
+               return; 
+
+// find precache number for sound
+    for (sound_num=1 ; sound_num<MAX_SOUNDS
+        && sv.sound_precache[sound_num] ; sound_num++)
+        if (!strcmp(sample, sv.sound_precache[sound_num]))
+            break;
+    
+    if ( sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num] )
+    {
+        Con_Printf ("SV_StartSound: %s not precached\n", sample);
+        return;
+    }
+    
+       ent = NUM_FOR_EDICT(entity);
+
+       channel = (ent<<3) | channel;
+
+       field_mask = 0;
+       if (volume != DEFAULT_SOUND_PACKET_VOLUME)
+               field_mask |= SND_VOLUME;
+       if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
+               field_mask |= SND_ATTENUATION;
+
+// directed messages go only to the entity the are targeted on
+       MSG_WriteByte (&sv.datagram, svc_sound);
+       MSG_WriteByte (&sv.datagram, field_mask);
+       if (field_mask & SND_VOLUME)
+               MSG_WriteByte (&sv.datagram, volume);
+       if (field_mask & SND_ATTENUATION)
+               MSG_WriteByte (&sv.datagram, attenuation*64);
+       MSG_WriteShort (&sv.datagram, channel);
+       MSG_WriteByte (&sv.datagram, sound_num);
+       for (i=0 ; i<3 ; i++)
+               MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]));
+}           
+
+/*
+==============================================================================
+
+CLIENT SPAWNING
+
+==============================================================================
+*/
+
+/*
+================
+SV_SendServerinfo
+
+Sends the first message from the server to a connected client.
+This will be sent on the initial connection and upon each server load.
+================
+*/
+void SV_SendServerinfo (client_t *client)
+{
+       char                    **s;
+       char                    message[2048];
+
+       MSG_WriteByte (&client->message, svc_print);
+#ifdef NEHAHRA
+       sprintf (message, "%c\nDPNEHAHRA VERSION %4.2f SERVER (%i CRC)", 2, DP_VERSION, pr_crc);
+#else
+       sprintf (message, "%c\nDARKPLACES VERSION %4.2f SERVER (%i CRC)", 2, DP_VERSION, pr_crc);
+#endif
+       MSG_WriteString (&client->message,message);
+
+       MSG_WriteByte (&client->message, svc_serverinfo);
+       MSG_WriteLong (&client->message, PROTOCOL_VERSION);
+       MSG_WriteByte (&client->message, svs.maxclients);
+
+       if (!coop.value && deathmatch.value)
+               MSG_WriteByte (&client->message, GAME_DEATHMATCH);
+       else
+               MSG_WriteByte (&client->message, GAME_COOP);
+
+       sprintf (message, pr_strings+sv.edicts->v.message);
+
+       MSG_WriteString (&client->message,message);
+
+       for (s = sv.model_precache+1 ; *s ; s++)
+               MSG_WriteString (&client->message, *s);
+       MSG_WriteByte (&client->message, 0);
+
+       for (s = sv.sound_precache+1 ; *s ; s++)
+               MSG_WriteString (&client->message, *s);
+       MSG_WriteByte (&client->message, 0);
+
+// send music
+       MSG_WriteByte (&client->message, svc_cdtrack);
+       MSG_WriteByte (&client->message, sv.edicts->v.sounds);
+       MSG_WriteByte (&client->message, sv.edicts->v.sounds);
+
+// set view    
+       MSG_WriteByte (&client->message, svc_setview);
+       MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict));
+
+       MSG_WriteByte (&client->message, svc_signonnum);
+       MSG_WriteByte (&client->message, 1);
+
+       client->sendsignon = true;
+       client->spawned = false;                // need prespawn, spawn, etc
+}
+
+/*
+================
+SV_ConnectClient
+
+Initializes a client_t for a new net connection.  This will only be called
+once for a player each game, not once for each level change.
+================
+*/
+void SV_ConnectClient (int clientnum)
+{
+       edict_t                 *ent;
+       client_t                *client;
+       int                             edictnum;
+       struct qsocket_s *netconnection;
+       int                             i;
+       float                   spawn_parms[NUM_SPAWN_PARMS];
+
+       client = svs.clients + clientnum;
+
+       Con_DPrintf ("Client %s connected\n", client->netconnection->address);
+
+       edictnum = clientnum+1;
+
+       ent = EDICT_NUM(edictnum);
+       
+// set up the client_t
+       netconnection = client->netconnection;
+       
+       if (sv.loadgame)
+               memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
+       memset (client, 0, sizeof(*client));
+       client->netconnection = netconnection;
+
+       strcpy (client->name, "unconnected");
+       client->active = true;
+       client->spawned = false;
+       client->edict = ent;
+       client->message.data = client->msgbuf;
+       client->message.maxsize = sizeof(client->msgbuf);
+       client->message.allowoverflow = true;           // we can catch it
+
+#ifdef IDGODS
+       client->privileged = IsID(&client->netconnection->addr);
+#else  
+       client->privileged = false;                             
+#endif
+
+       if (sv.loadgame)
+               memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
+       else
+       {
+       // call the progs to get default spawn parms for the new client
+               PR_ExecuteProgram (pr_global_struct->SetNewParms);
+               for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+                       client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
+       }
+
+       SV_SendServerinfo (client);
+}
+
+
+/*
+===================
+SV_CheckForNewClients
+
+===================
+*/
+void SV_CheckForNewClients (void)
+{
+       struct qsocket_s        *ret;
+       int                             i;
+               
+//
+// check for new connections
+//
+       while (1)
+       {
+               ret = NET_CheckNewConnections ();
+               if (!ret)
+                       break;
+
+       // 
+       // init a new client structure
+       //      
+               for (i=0 ; i<svs.maxclients ; i++)
+                       if (!svs.clients[i].active)
+                               break;
+               if (i == svs.maxclients)
+                       Sys_Error ("Host_CheckForNewClients: no free clients");
+               
+               svs.clients[i].netconnection = ret;
+               SV_ConnectClient (i);   
+       
+               net_activeconnections++;
+       }
+}
+
+
+
+/*
+===============================================================================
+
+FRAME UPDATES
+
+===============================================================================
+*/
+
+/*
+==================
+SV_ClearDatagram
+
+==================
+*/
+void SV_ClearDatagram (void)
+{
+       SZ_Clear (&sv.datagram);
+}
+
+/*
+=============================================================================
+
+The PVS must include a small area around the client to allow head bobbing
+or other small motion on the client side.  Otherwise, a bob might cause an
+entity that should be visible to not show up, especially when the bob
+crosses a waterline.
+
+=============================================================================
+*/
+
+int            fatbytes;
+byte   fatpvs[MAX_MAP_LEAFS/8];
+
+void SV_AddToFatPVS (vec3_t org, mnode_t *node)
+{
+       int             i;
+       byte    *pvs;
+       mplane_t        *plane;
+       float   d;
+
+       while (1)
+       {
+       // if this is a leaf, accumulate the pvs bits
+               if (node->contents < 0)
+               {
+                       if (node->contents != CONTENTS_SOLID)
+                       {
+                               pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel);
+                               for (i=0 ; i<fatbytes ; i++)
+                                       fatpvs[i] |= pvs[i];
+                       }
+                       return;
+               }
+       
+               plane = node->plane;
+               d = DotProduct (org, plane->normal) - plane->dist;
+               if (d > 8)
+                       node = node->children[0];
+               else if (d < -8)
+                       node = node->children[1];
+               else
+               {       // go down both
+                       SV_AddToFatPVS (org, node->children[0]);
+                       node = node->children[1];
+               }
+       }
+}
+
+/*
+=============
+SV_FatPVS
+
+Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
+given point.
+=============
+*/
+byte *SV_FatPVS (vec3_t org)
+{
+       fatbytes = (sv.worldmodel->numleafs+31)>>3;
+       memset (fatpvs, 0, fatbytes);
+       SV_AddToFatPVS (org, sv.worldmodel->nodes);
+       return fatpvs;
+}
+
+//=============================================================================
+
+
+/*
+=============
+SV_WriteEntitiesToClient
+
+=============
+*/
+void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg)
+{
+       int             e, i, clentnum, bits, alpha, glowcolor, glowsize, scale, colormod, modred, modgreen, modblue, dodelta, effects;
+       byte    *pvs;
+       vec3_t  org, origin, angles;
+       float   movelerp, moveilerp;
+       edict_t *ent;
+       eval_t  *val;
+       entity_state_t *baseline; // LordHavoc: delta or startup baseline
+
+// find the client's PVS
+       VectorAdd (clent->v.origin, clent->v.view_ofs, org);
+       pvs = SV_FatPVS (org);
+
+       clentnum = NUM_FOR_EDICT(clent); // LordHavoc: for comparison purposes
+// send over all entities (except the client) that touch the pvs
+       ent = NEXT_EDICT(sv.edicts);
+       for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+       {
+               bits = 0;
+               if (ent != clent) // LordHavoc: always send player
+               {
+                       if ((val = GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)) && val->edict)
+                       {
+                               if (val->edict != clentnum)
+                                       continue; // don't show to anyone else
+                               else
+                                       bits |= U_VIEWMODEL; // show relative to the view
+                       }
+                       else
+                       {
+                               // LordHavoc: never draw something told not to display to this client
+                               if ((val = GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)) && val->edict == clentnum)
+                                       continue;
+                               if ((val = GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)) && val->edict && val->edict != clentnum)
+                                       continue;
+                               // ignore if not touching a PV leaf
+                               for (i=0 ; i < ent->num_leafs ; i++)
+                                       if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))
+                                               break;
+                                       
+                               if (i == ent->num_leafs)
+                                       continue;               // not visible
+                       }
+               }
+
+               // don't send if flagged for NODRAW and there are no effects
+               alpha = 255;
+               scale = 16;
+               glowsize = 0;
+               glowcolor = 254;
+               colormod = 255;
+               effects = ent->v.effects;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_alpha))
+               if ((alpha = (int) (val->_float * 255.0)) == 0)
+                       alpha = 255;
+               if (alpha < 0) alpha = 0;
+               if (alpha > 255) alpha = 255;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_glow_size))
+                       glowsize = (int) val->_float >> 3;
+               if (glowsize > 127) glowsize = 127;
+               if (glowsize < -128) glowsize = -128;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_scale))
+               if ((scale = (int) (val->_float * 16.0)) == 0) scale = 16;
+               if (scale < 0) scale = 0;
+               if (scale > 255) scale = 255;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_glow_trail))
+               if (val->_float != 0)
+                       bits |= U_GLOWTRAIL;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_glow_color))
+               if (val->_float != 0)
+                       glowcolor = (int) val->_float;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_fullbright))
+               if (val->_float != 0)
+                       effects |= EF_FULLBRIGHT;
+
+               if (val = GETEDICTFIELDVALUE(ent, eval_colormod))
+               if (val->vector[0] != 0 || val->vector[1] != 0 || val->vector[2] != 0)
+               {
+                       modred = val->vector[0] * 8.0;if (modred < 0) modred = 0;if (modred > 7) modred = 7;
+                       modgreen = val->vector[1] * 8.0;if (modgreen < 0) modgreen = 0;if (modgreen > 7) modgreen = 7;
+                       modblue = val->vector[2] * 4.0;if (modblue < 0) modblue = 0;if (modblue > 3) modblue = 3;
+                       colormod = (modred << 5) | (modgreen << 2) | modblue;
+               }
+
+               if (ent != clent)
+               {
+                       if (glowsize == 0 && bits == 0) // no effects
+                       {
+                               if (ent->v.modelindex && pr_strings[ent->v.model]) // model
+                               {
+                                       if (sv.models[ (int)ent->v.modelindex ]->flags == 0 && (ent->v.effects == EF_NODRAW || scale <= 0 || alpha <= 0))
+                                               continue;
+                               }
+                               else // no model and no effects
+                                       continue;
+                       }
+               }
+
+               if (msg->maxsize - msg->cursize < 32) // LordHavoc: increased check from 16 to 32
+               {
+                       Con_Printf ("packet overflow\n");
+                       return;
+               }
+
+// send an update
+               bits = 0;
+
+               dodelta = FALSE;
+               if ((int)ent->v.effects & EF_DELTA)
+                       dodelta = cl.time < ent->nextfullupdate; // every half second a full update is forced
+
+               if (dodelta)
+               {
+                       bits |= U_DELTA;
+                       baseline = &ent->deltabaseline;
+               }
+               else
+               {
+                       ent->nextfullupdate = cl.time + 0.5;
+                       baseline = &ent->baseline;
+               }
+
+               if (e >= 256)
+                       bits |= U_LONGENTITY;
+               if (ent->v.movetype == MOVETYPE_STEP)
+                       bits |= U_STEP;
+               
+               if (ent->v.movetype == MOVETYPE_STEP && ((int) ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) // monsters have smoothed walking/flying/swimming movement
+               {
+                       if (!ent->steplerptime || ent->steplerptime > sv.time) // when the level just started...
+                       {
+                               ent->steplerptime = sv.time;
+                               VectorCopy(ent->v.origin, ent->stepoldorigin);
+                               VectorCopy(ent->v.angles, ent->stepoldangles);
+                               VectorCopy(ent->v.origin, ent->steporigin);
+                               VectorCopy(ent->v.angles, ent->stepangles);
+                       }
+                       VectorSubtract(ent->v.origin, ent->steporigin, origin);
+                       VectorSubtract(ent->v.angles, ent->stepangles, angles);
+                       if (DotProduct(origin, origin) >= 0.125 || DotProduct(angles, angles) >= 1.4)
+                       {
+                               // update lerp positions
+                               ent->steplerptime = sv.time;
+                               VectorCopy(ent->steporigin, ent->stepoldorigin);
+                               VectorCopy(ent->stepangles, ent->stepoldangles);
+                               VectorCopy(ent->v.origin, ent->steporigin);
+                               VectorCopy(ent->v.angles, ent->stepangles);
+                       }
+                       movelerp = (sv.time - ent->steplerptime) * 10.0;
+                       if (movelerp > 1) movelerp = 1;
+                       moveilerp = 1 - movelerp;
+                       origin[0] = ent->stepoldorigin[0] * moveilerp + ent->steporigin[0] * movelerp;
+                       origin[1] = ent->stepoldorigin[1] * moveilerp + ent->steporigin[1] * movelerp;
+                       origin[2] = ent->stepoldorigin[2] * moveilerp + ent->steporigin[2] * movelerp;
+                       // choose shortest rotate (to avoid 'spin around' situations)
+                       VectorSubtract(ent->stepangles, ent->stepoldangles, angles);
+                       if (angles[0] < -180) angles[0] += 360;if (angles[0] >= 180) angles[0] -= 360;
+                       if (angles[1] < -180) angles[1] += 360;if (angles[1] >= 180) angles[1] -= 360;
+                       if (angles[2] < -180) angles[2] += 360;if (angles[2] >= 180) angles[2] -= 360;
+                       angles[0] = angles[0] * movelerp + ent->stepoldangles[0];
+                       angles[1] = angles[1] * movelerp + ent->stepoldangles[1];
+                       angles[2] = angles[2] * movelerp + ent->stepoldangles[2];
+               }
+               else // copy as they are
+               {
+                       VectorCopy(ent->v.origin, origin);
+                       VectorCopy(ent->v.angles, angles);
+                       if (ent->v.movetype == MOVETYPE_STEP) // monster, but airborn, update lerp info
+                       {
+                               // update lerp positions
+                               ent->steplerptime = sv.time;
+                               VectorCopy(ent->v.origin, ent->stepoldorigin);
+                               VectorCopy(ent->v.angles, ent->stepoldangles);
+                               VectorCopy(ent->v.origin, ent->steporigin);
+                               VectorCopy(ent->v.angles, ent->stepangles);
+                       }
+               }
+
+               // LordHavoc: old stuff, but rewritten to have more exact tolerances
+               if ((int)(origin[0]*8.0) != (int)(baseline->origin[0]*8.0))                                             bits |= U_ORIGIN1;
+               if ((int)(origin[1]*8.0) != (int)(baseline->origin[1]*8.0))                                             bits |= U_ORIGIN2;
+               if ((int)(origin[2]*8.0) != (int)(baseline->origin[2]*8.0))                                             bits |= U_ORIGIN3;
+               if ((int)(angles[0]*(256.0/360.0)) != (int)(baseline->angles[0]*(256.0/360.0))) bits |= U_ANGLE1;
+               if ((int)(angles[1]*(256.0/360.0)) != (int)(baseline->angles[1]*(256.0/360.0))) bits |= U_ANGLE2;
+               if ((int)(angles[2]*(256.0/360.0)) != (int)(baseline->angles[2]*(256.0/360.0))) bits |= U_ANGLE3;
+               if (baseline->colormap != (int) ent->v.colormap)                                                                bits |= U_COLORMAP;
+               if (baseline->skin != (int) ent->v.skin)                                                                                bits |= U_SKIN;
+               if ((baseline->frame & 0x00FF) != ((int) ent->v.frame & 0x00FF))                                bits |= U_FRAME;
+               if ((baseline->effects & 0x00FF) != ((int) ent->v.effects & 0x00FF))                    bits |= U_EFFECTS;
+               if (baseline->modelindex != (int) ent->v.modelindex)                                                    bits |= U_MODEL;
+
+               // LordHavoc: new stuff
+               if (baseline->alpha != alpha)                                                                                                   bits |= U_ALPHA;
+               if (baseline->scale != scale)                                                                                                   bits |= U_SCALE;
+               if (((int) baseline->effects & 0xFF00) != ((int) ent->v.effects & 0xFF00))              bits |= U_EFFECTS2;
+               if (baseline->glowsize != glowsize)                                                                                             bits |= U_GLOWSIZE;
+               if (baseline->glowcolor != glowcolor)                                                                                   bits |= U_GLOWCOLOR;
+               if (baseline->colormod != colormod)                                                                                             bits |= U_COLORMOD;
+               if (((int) baseline->frame & 0xFF00) != ((int) ent->v.frame & 0xFF00))                  bits |= U_FRAME2;
+
+               // update delta baseline
+               VectorCopy(ent->v.origin, ent->deltabaseline.origin);
+               VectorCopy(ent->v.angles, ent->deltabaseline.angles);
+               ent->deltabaseline.colormap = ent->v.colormap;
+               ent->deltabaseline.skin = ent->v.skin;
+               ent->deltabaseline.frame = ent->v.frame;
+               ent->deltabaseline.effects = ent->v.effects;
+               ent->deltabaseline.modelindex = ent->v.modelindex;
+               ent->deltabaseline.alpha = alpha;
+               ent->deltabaseline.scale = scale;
+               ent->deltabaseline.glowsize = glowsize;
+               ent->deltabaseline.glowcolor = glowcolor;
+               ent->deltabaseline.colormod = colormod;
+
+               // write the message
+               if (bits >= 16777216)
+                       bits |= U_EXTEND2;
+               if (bits >= 65536)
+                       bits |= U_EXTEND1;
+               if (bits >= 256)
+                       bits |= U_MOREBITS;
+               bits |= U_SIGNAL;
+
+               MSG_WriteByte (msg, bits);
+               if (bits & U_MOREBITS)
+                       MSG_WriteByte (msg, bits>>8);
+               // LordHavoc: extend bytes have to be written here due to delta compression
+               if (bits & U_EXTEND1)
+                       MSG_WriteByte (msg, bits>>16);
+               if (bits & U_EXTEND2)
+                       MSG_WriteByte (msg, bits>>24);
+
+               // LordHavoc: old stuff
+               if (bits & U_LONGENTITY)
+                       MSG_WriteShort (msg,e);
+               else
+                       MSG_WriteByte (msg,e);
+               if (bits & U_MODEL)             MSG_WriteByte (msg,     ent->v.modelindex);
+               if (bits & U_FRAME)             MSG_WriteByte (msg, ent->v.frame);
+               if (bits & U_COLORMAP)  MSG_WriteByte (msg, ent->v.colormap);
+               if (bits & U_SKIN)              MSG_WriteByte (msg, ent->v.skin);
+               if (bits & U_EFFECTS)   MSG_WriteByte (msg, ent->v.effects);
+               if (bits & U_ORIGIN1)   MSG_WriteCoord (msg, origin[0]);                
+               if (bits & U_ANGLE1)    MSG_WriteAngle(msg, angles[0]);
+               if (bits & U_ORIGIN2)   MSG_WriteCoord (msg, origin[1]);
+               if (bits & U_ANGLE2)    MSG_WriteAngle(msg, angles[1]);
+               if (bits & U_ORIGIN3)   MSG_WriteCoord (msg, origin[2]);
+               if (bits & U_ANGLE3)    MSG_WriteAngle(msg, angles[2]);
+
+               // LordHavoc: new stuff
+               if (bits & U_ALPHA)             MSG_WriteByte(msg, alpha);
+               if (bits & U_SCALE)             MSG_WriteByte(msg, scale);
+               if (bits & U_EFFECTS2)  MSG_WriteByte(msg, (int)ent->v.effects >> 8);
+               if (bits & U_GLOWSIZE)  MSG_WriteByte(msg, glowsize);
+               if (bits & U_GLOWCOLOR) MSG_WriteByte(msg, glowcolor);
+               if (bits & U_COLORMOD)  MSG_WriteByte(msg, colormod);
+               if (bits & U_FRAME2)    MSG_WriteByte(msg, (int)ent->v.frame >> 8);
+       }
+}
+
+/*
+=============
+SV_CleanupEnts
+
+=============
+*/
+void SV_CleanupEnts (void)
+{
+       int             e;
+       edict_t *ent;
+       
+       ent = NEXT_EDICT(sv.edicts);
+       for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+       {
+               ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
+       }
+
+}
+
+/*
+==================
+SV_WriteClientdataToMessage
+
+==================
+*/
+void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
+{
+       int             bits;
+       int             i;
+       edict_t *other;
+       int             items;
+       eval_t  *val;
+
+//
+// send a damage message
+//
+       if (ent->v.dmg_take || ent->v.dmg_save)
+       {
+               other = PROG_TO_EDICT(ent->v.dmg_inflictor);
+               MSG_WriteByte (msg, svc_damage);
+               MSG_WriteByte (msg, ent->v.dmg_save);
+               MSG_WriteByte (msg, ent->v.dmg_take);
+               for (i=0 ; i<3 ; i++)
+                       MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]));
+       
+               ent->v.dmg_take = 0;
+               ent->v.dmg_save = 0;
+       }
+
+//
+// send the current viewpos offset from the view entity
+//
+       SV_SetIdealPitch ();            // how much to look up / down ideally
+
+// a fixangle might get lost in a dropped packet.  Oh well.
+       if ( ent->v.fixangle )
+       {
+               MSG_WriteByte (msg, svc_setangle);
+               for (i=0 ; i < 3 ; i++)
+                       MSG_WriteAngle (msg, ent->v.angles[i] );
+               ent->v.fixangle = 0;
+       }
+
+       bits = 0;
+       
+       if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT)
+               bits |= SU_VIEWHEIGHT;
+               
+       if (ent->v.idealpitch)
+               bits |= SU_IDEALPITCH;
+
+// stuff the sigil bits into the high bits of items for sbar, or else
+// mix in items2
+       val = GETEDICTFIELDVALUE(ent, eval_items2);
+
+       if (val)
+               items = (int)ent->v.items | ((int)val->_float << 23);
+       else
+               items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28);
+
+       bits |= SU_ITEMS;
+       
+       if ( (int)ent->v.flags & FL_ONGROUND)
+               bits |= SU_ONGROUND;
+       
+       if ( ent->v.waterlevel >= 2)
+               bits |= SU_INWATER;
+       
+       for (i=0 ; i<3 ; i++)
+       {
+               if (ent->v.punchangle[i])
+                       bits |= (SU_PUNCH1<<i);
+               if (ent->v.velocity[i])
+                       bits |= (SU_VELOCITY1<<i);
+       }
+       
+       if (ent->v.weaponframe)
+               bits |= SU_WEAPONFRAME;
+
+       if (ent->v.armorvalue)
+               bits |= SU_ARMOR;
+
+//     if (ent->v.weapon)
+               bits |= SU_WEAPON;
+
+// send the data
+
+       MSG_WriteByte (msg, svc_clientdata);
+       MSG_WriteShort (msg, bits);
+
+       if (bits & SU_VIEWHEIGHT)
+               MSG_WriteChar (msg, ent->v.view_ofs[2]);
+
+       if (bits & SU_IDEALPITCH)
+               MSG_WriteChar (msg, ent->v.idealpitch);
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (bits & (SU_PUNCH1<<i))
+                       MSG_WriteChar (msg, ent->v.punchangle[i]);
+               if (bits & (SU_VELOCITY1<<i))
+                       MSG_WriteChar (msg, ent->v.velocity[i]/16);
+       }
+
+// [always sent]       if (bits & SU_ITEMS)
+       MSG_WriteLong (msg, items);
+
+       if (bits & SU_WEAPONFRAME)
+               MSG_WriteByte (msg, ent->v.weaponframe);
+       if (bits & SU_ARMOR)
+               MSG_WriteByte (msg, ent->v.armorvalue);
+       if (bits & SU_WEAPON)
+               MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel));
+       
+       MSG_WriteShort (msg, ent->v.health);
+       MSG_WriteByte (msg, ent->v.currentammo);
+       MSG_WriteByte (msg, ent->v.ammo_shells);
+       MSG_WriteByte (msg, ent->v.ammo_nails);
+       MSG_WriteByte (msg, ent->v.ammo_rockets);
+       MSG_WriteByte (msg, ent->v.ammo_cells);
+
+       if (standard_quake)
+       {
+               MSG_WriteByte (msg, ent->v.weapon);
+       }
+       else
+       {
+               for(i=0;i<32;i++)
+               {
+                       if ( ((int)ent->v.weapon) & (1<<i) )
+                       {
+                               MSG_WriteByte (msg, i);
+                               break;
+                       }
+               }
+       }
+}
+
+/*
+=======================
+SV_SendClientDatagram
+=======================
+*/
+qboolean SV_SendClientDatagram (client_t *client)
+{
+       byte            buf[MAX_DATAGRAM];
+       sizebuf_t       msg;
+       
+       msg.data = buf;
+       msg.maxsize = sizeof(buf);
+       msg.cursize = 0;
+
+       MSG_WriteByte (&msg, svc_time);
+       MSG_WriteFloat (&msg, sv.time);
+
+// add the client specific data to the datagram
+       SV_WriteClientdataToMessage (client->edict, &msg);
+
+       SV_WriteEntitiesToClient (client->edict, &msg);
+
+// copy the server datagram if there is space
+       if (msg.cursize + sv.datagram.cursize < msg.maxsize)
+               SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
+
+// send the datagram
+       if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
+       {
+               SV_DropClient (true);// if the message couldn't send, kick off
+               return false;
+       }
+       
+       return true;
+}
+
+/*
+=======================
+SV_UpdateToReliableMessages
+=======================
+*/
+void SV_UpdateToReliableMessages (void)
+{
+       int                     i, j;
+       client_t *client;
+
+// check for changes to be sent over the reliable streams
+       for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+       {
+               if (host_client->old_frags != host_client->edict->v.frags)
+               {
+                       for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+                       {
+                               if (!client->active)
+                                       continue;
+                               MSG_WriteByte (&client->message, svc_updatefrags);
+                               MSG_WriteByte (&client->message, i);
+                               MSG_WriteShort (&client->message, host_client->edict->v.frags);
+                       }
+
+                       host_client->old_frags = host_client->edict->v.frags;
+               }
+       }
+       
+       for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+       {
+               if (!client->active)
+                       continue;
+               SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
+       }
+
+       SZ_Clear (&sv.reliable_datagram);
+}
+
+
+/*
+=======================
+SV_SendNop
+
+Send a nop message without trashing or sending the accumulated client
+message buffer
+=======================
+*/
+void SV_SendNop (client_t *client)
+{
+       sizebuf_t       msg;
+       byte            buf[4];
+       
+       msg.data = buf;
+       msg.maxsize = sizeof(buf);
+       msg.cursize = 0;
+
+       MSG_WriteChar (&msg, svc_nop);
+
+       if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
+               SV_DropClient (true);   // if the message couldn't send, kick off
+       client->last_message = realtime;
+}
+
+/*
+=======================
+SV_SendClientMessages
+=======================
+*/
+void SV_SendClientMessages (void)
+{
+       int                     i;
+       
+// update frags, names, etc
+       SV_UpdateToReliableMessages ();
+
+// build individual updates
+       for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+       {
+               if (!host_client->active)
+                       continue;
+
+               if (host_client->spawned)
+               {
+                       if (!SV_SendClientDatagram (host_client))
+                               continue;
+               }
+               else
+               {
+               // the player isn't totally in the game yet
+               // send small keepalive messages if too much time has passed
+               // send a full message when the next signon stage has been requested
+               // some other message data (name changes, etc) may accumulate 
+               // between signon stages
+                       if (!host_client->sendsignon)
+                       {
+                               if (realtime - host_client->last_message > 5)
+                                       SV_SendNop (host_client);
+                               continue;       // don't send out non-signon messages
+                       }
+               }
+
+               // check for an overflowed message.  Should only happen
+               // on a very fucked up connection that backs up a lot, then
+               // changes level
+               if (host_client->message.overflowed)
+               {
+                       SV_DropClient (true);
+                       host_client->message.overflowed = false;
+                       continue;
+               }
+                       
+               if (host_client->message.cursize || host_client->dropasap)
+               {
+                       if (!NET_CanSendMessage (host_client->netconnection))
+                       {
+//                             I_Printf ("can't write\n");
+                               continue;
+                       }
+
+                       if (host_client->dropasap)
+                               SV_DropClient (false);  // went to another level
+                       else
+                       {
+                               if (NET_SendMessage (host_client->netconnection
+                               , &host_client->message) == -1)
+                                       SV_DropClient (true);   // if the message couldn't send, kick off
+                               SZ_Clear (&host_client->message);
+                               host_client->last_message = realtime;
+                               host_client->sendsignon = false;
+                       }
+               }
+       }
+       
+       
+// clear muzzle flashes
+       SV_CleanupEnts ();
+}
+
+
+/*
+==============================================================================
+
+SERVER SPAWNING
+
+==============================================================================
+*/
+
+/*
+================
+SV_ModelIndex
+
+================
+*/
+int SV_ModelIndex (char *name)
+{
+       int             i;
+       
+       if (!name || !name[0])
+               return 0;
+
+       for (i=0 ; i<MAX_MODELS && sv.model_precache[i] ; i++)
+               if (!strcmp(sv.model_precache[i], name))
+                       return i;
+       if (i==MAX_MODELS || !sv.model_precache[i])
+               Sys_Error ("SV_ModelIndex: model %s not precached", name);
+       return i;
+}
+
+/*
+================
+SV_CreateBaseline
+
+================
+*/
+void SV_CreateBaseline (void)
+{
+       int                     i;
+       edict_t                 *svent;
+       int                             entnum; 
+               
+       for (entnum = 0; entnum < sv.num_edicts ; entnum++)
+       {
+       // get the current server version
+               svent = EDICT_NUM(entnum);
+               if (svent->free)
+                       continue;
+               if (entnum > svs.maxclients && !svent->v.modelindex)
+                       continue;
+
+       //
+       // create entity baseline
+       //
+               VectorCopy (svent->v.origin, svent->baseline.origin);
+               VectorCopy (svent->v.angles, svent->baseline.angles);
+               svent->baseline.frame = svent->v.frame;
+               svent->baseline.skin = svent->v.skin;
+               if (entnum > 0 && entnum <= svs.maxclients)
+               {
+                       svent->baseline.colormap = entnum;
+                       svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl");
+               }
+               else
+               {
+                       svent->baseline.colormap = 0;
+                       svent->baseline.modelindex =
+                               SV_ModelIndex(pr_strings + svent->v.model);
+               }
+               svent->baseline.alpha = 255;
+               svent->baseline.scale = 16;
+               svent->baseline.glowsize = 0;
+               svent->baseline.glowcolor = 254;
+               svent->baseline.colormod = 255;
+               
+       //
+       // add to the message
+       //
+               MSG_WriteByte (&sv.signon,svc_spawnbaseline);           
+               MSG_WriteShort (&sv.signon,entnum);
+
+               MSG_WriteByte (&sv.signon, svent->baseline.modelindex);
+               MSG_WriteByte (&sv.signon, svent->baseline.frame);
+               MSG_WriteByte (&sv.signon, svent->baseline.colormap);
+               MSG_WriteByte (&sv.signon, svent->baseline.skin);
+               for (i=0 ; i<3 ; i++)
+               {
+                       MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);
+                       MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);
+               }
+       }
+}
+
+
+/*
+================
+SV_SendReconnect
+
+Tell all the clients that the server is changing levels
+================
+*/
+void SV_SendReconnect (void)
+{
+       char    data[128];
+       sizebuf_t       msg;
+
+       msg.data = data;
+       msg.cursize = 0;
+       msg.maxsize = sizeof(data);
+
+       MSG_WriteChar (&msg, svc_stufftext);
+       MSG_WriteString (&msg, "reconnect\n");
+       NET_SendToAll (&msg, 5);
+       
+       if (cls.state != ca_dedicated)
+               Cmd_ExecuteString ("reconnect\n", src_command);
+}
+
+
+/*
+================
+SV_SaveSpawnparms
+
+Grabs the current state of each client for saving across the
+transition to another level
+================
+*/
+void SV_SaveSpawnparms (void)
+{
+       int             i, j;
+
+       svs.serverflags = pr_global_struct->serverflags;
+
+       for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+       {
+               if (!host_client->active)
+                       continue;
+
+       // call the progs to get default spawn parms for the new client
+               pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
+               PR_ExecuteProgram (pr_global_struct->SetChangeParms);
+               for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
+                       host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j];
+       }
+}
+
+qboolean isworldmodel;
+
+/*
+================
+SV_SpawnServer
+
+This is called at the start of each level
+================
+*/
+extern float           scr_centertime_off;
+
+void SV_SpawnServer (char *server)
+{
+       edict_t         *ent;
+       int                     i;
+
+       // let's not have any servers with no name
+       if (hostname.string[0] == 0)
+               Cvar_Set ("hostname", "UNNAMED");
+       scr_centertime_off = 0;
+
+       Con_DPrintf ("SpawnServer: %s\n",server);
+       svs.changelevel_issued = false;         // now safe to issue another
+
+//
+// tell all connected clients that we are going to a new level
+//
+       if (sv.active)
+       {
+               SV_SendReconnect ();
+       }
+
+//
+// make cvars consistant
+//
+       if (coop.value)
+               Cvar_SetValue ("deathmatch", 0);
+       current_skill = (int)(skill.value + 0.5);
+       if (current_skill < 0)
+               current_skill = 0;
+       if (current_skill > 3)
+               current_skill = 3;
+
+       Cvar_SetValue ("skill", (float)current_skill);
+       
+//
+// set up the new server
+//
+       Host_ClearMemory ();
+
+       memset (&sv, 0, sizeof(sv));
+
+       strcpy (sv.name, server);
+
+// load progs to get entity field count
+       PR_LoadProgs ();
+
+// allocate server memory
+       sv.max_edicts = MAX_EDICTS;
+       
+       sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts");
+
+       sv.datagram.maxsize = sizeof(sv.datagram_buf);
+       sv.datagram.cursize = 0;
+       sv.datagram.data = sv.datagram_buf;
+       
+       sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
+       sv.reliable_datagram.cursize = 0;
+       sv.reliable_datagram.data = sv.reliable_datagram_buf;
+       
+       sv.signon.maxsize = sizeof(sv.signon_buf);
+       sv.signon.cursize = 0;
+       sv.signon.data = sv.signon_buf;
+       
+// leave slots at start for clients only
+       sv.num_edicts = svs.maxclients+1;
+       for (i=0 ; i<svs.maxclients ; i++)
+       {
+               ent = EDICT_NUM(i+1);
+               svs.clients[i].edict = ent;
+       }
+       
+       sv.state = ss_loading;
+       sv.paused = false;
+
+       sv.time = 1.0;
+       
+       strcpy (sv.name, server);
+       sprintf (sv.modelname,"maps/%s.bsp", server);
+       isworldmodel = true; // LordHavoc: only load submodels on the world model
+       sv.worldmodel = Mod_ForName (sv.modelname, false);
+       isworldmodel = false;
+       if (!sv.worldmodel)
+       {
+               Con_Printf ("Couldn't spawn server %s\n", sv.modelname);
+               sv.active = false;
+               return;
+       }
+       sv.models[1] = sv.worldmodel;
+       
+//
+// clear world interaction links
+//
+       SV_ClearWorld ();
+       
+       sv.sound_precache[0] = pr_strings;
+
+       sv.model_precache[0] = pr_strings;
+       sv.model_precache[1] = sv.modelname;
+       for (i=1 ; i<sv.worldmodel->numsubmodels ; i++)
+       {
+               sv.model_precache[1+i] = localmodels[i];
+               sv.models[i+1] = Mod_ForName (localmodels[i], false);
+       }
+
+//
+// load the rest of the entities
+//     
+       ent = EDICT_NUM(0);
+       memset (&ent->v, 0, progs->entityfields * 4);
+       ent->free = false;
+       ent->v.model = sv.worldmodel->name - pr_strings;
+       ent->v.modelindex = 1;          // world model
+       ent->v.solid = SOLID_BSP;
+       ent->v.movetype = MOVETYPE_PUSH;
+       ent->v.angles[0] = ent->v.angles[1] = ent->v.angles[2] = 0;
+
+       if (coop.value)
+               pr_global_struct->coop = coop.value;
+       else
+               pr_global_struct->deathmatch = deathmatch.value;
+
+       pr_global_struct->mapname = sv.name - pr_strings;
+
+// serverflags are for cross level information (sigils)
+       pr_global_struct->serverflags = svs.serverflags;
+       
+       ED_LoadFromFile (sv.worldmodel->entities);
+       // LordHavoc: clear world angles (to fix e3m3.bsp)
+       sv.edicts->v.angles[0] = sv.edicts->v.angles[1] = sv.edicts->v.angles[2] = 0;
+
+       sv.active = true;
+
+// all setup is completed, any further precache statements are errors
+       sv.state = ss_active;
+       
+// run two frames to allow everything to settle
+       host_frametime = 0.1;
+       SV_Physics ();
+       SV_Physics ();
+
+// create a baseline for more efficient communications
+       SV_CreateBaseline ();
+
+// send serverinfo to all connected clients
+       for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+               if (host_client->active)
+                       SV_SendServerinfo (host_client);
+       
+       Con_DPrintf ("Server spawned.\n");
+}
+
+// LordHavoc: added light checking to the server
+int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end);
+void SV_LightPoint (vec3_t color, vec3_t p)
+{
+       vec3_t          end;
+       
+       if (!sv.worldmodel->lightdata)
+       {
+               color[0] = color[1] = color[2] = 255;
+               return;
+       }
+       
+       end[0] = p[0];
+       end[1] = p[1];
+       end[2] = p[2] - 2048;
+
+       color[0] = color[1] = color[2] = 0;
+       RecursiveLightPoint (color, sv.worldmodel->nodes, p, end);
+}
diff --git a/sv_move.c b/sv_move.c
new file mode 100644 (file)
index 0000000..87f3ed4
--- /dev/null
+++ b/sv_move.c
@@ -0,0 +1,419 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_move.c -- monster movement
+
+#include "quakedef.h"
+
+#define        STEPSIZE        18
+
+/*
+=============
+SV_CheckBottom
+
+Returns false if any part of the bottom of the entity is off an edge that
+is not a staircase.
+
+=============
+*/
+int c_yes, c_no;
+
+qboolean SV_CheckBottom (edict_t *ent)
+{
+       vec3_t  mins, maxs, start, stop;
+       trace_t trace;
+       int             x, y;
+       float   mid, bottom;
+       
+       VectorAdd (ent->v.origin, ent->v.mins, mins);
+       VectorAdd (ent->v.origin, ent->v.maxs, maxs);
+
+// if all of the points under the corners are solid world, don't bother
+// with the tougher checks
+// the corners must be within 16 of the midpoint
+       start[2] = mins[2] - 1;
+       for     (x=0 ; x<=1 ; x++)
+               for     (y=0 ; y<=1 ; y++)
+               {
+                       start[0] = x ? maxs[0] : mins[0];
+                       start[1] = y ? maxs[1] : mins[1];
+                       if (SV_PointContents (start) != CONTENTS_SOLID)
+                               goto realcheck;
+               }
+
+       c_yes++;
+       return true;            // we got out easy
+
+realcheck:
+       c_no++;
+//
+// check it for real...
+//
+       start[2] = mins[2];
+       
+// the midpoint must be within 16 of the bottom
+       start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
+       start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
+       stop[2] = start[2] - 2*STEPSIZE;
+       trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent);
+
+       if (trace.fraction == 1.0)
+               return false;
+       mid = bottom = trace.endpos[2];
+       
+// the corners must be within 16 of the midpoint       
+       for     (x=0 ; x<=1 ; x++)
+               for     (y=0 ; y<=1 ; y++)
+               {
+                       start[0] = stop[0] = x ? maxs[0] : mins[0];
+                       start[1] = stop[1] = y ? maxs[1] : mins[1];
+                       
+                       trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent);
+                       
+                       if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
+                               bottom = trace.endpos[2];
+                       if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
+                               return false;
+               }
+
+       c_yes++;
+       return true;
+}
+
+
+/*
+=============
+SV_movestep
+
+Called by monster program code.
+The move will be adjusted for slopes and stairs, but if the move isn't
+possible, no move is done, false is returned, and
+pr_global_struct->trace_normal is set to the normal of the blocking wall
+=============
+*/
+qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
+{
+       float           dz;
+       vec3_t          oldorg, neworg, end;
+       trace_t         trace;
+       int                     i;
+       edict_t         *enemy;
+
+// try the move        
+       VectorCopy (ent->v.origin, oldorg);
+       VectorAdd (ent->v.origin, move, neworg);
+
+// flying monsters don't step up
+       if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) )
+       {
+       // try one move with vertical motion, then one without
+               for (i=0 ; i<2 ; i++)
+               {
+                       VectorAdd (ent->v.origin, move, neworg);
+                       enemy = PROG_TO_EDICT(ent->v.enemy);
+                       if (i == 0 && enemy != sv.edicts)
+                       {
+                               dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2];
+                               if (dz > 40)
+                                       neworg[2] -= 8;
+                               if (dz < 30)
+                                       neworg[2] += 8;
+                       }
+                       trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent);
+       
+                       if (trace.fraction == 1)
+                       {
+                               if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY )
+                                       return false;   // swim monster left water
+       
+                               VectorCopy (trace.endpos, ent->v.origin);
+                               if (relink)
+                                       SV_LinkEdict (ent, true);
+                               return true;
+                       }
+                       
+                       if (enemy == sv.edicts)
+                               break;
+               }
+               
+               return false;
+       }
+
+// push down from a step height above the wished position
+       neworg[2] += STEPSIZE;
+       VectorCopy (neworg, end);
+       end[2] -= STEPSIZE*2;
+
+       trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent);
+
+       if (trace.allsolid)
+               return false;
+
+       if (trace.startsolid)
+       {
+               neworg[2] -= STEPSIZE;
+               trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent);
+               if (trace.allsolid || trace.startsolid)
+                       return false;
+       }
+       if (trace.fraction == 1)
+       {
+       // if monster had the ground pulled out, go ahead and fall
+               if ( (int)ent->v.flags & FL_PARTIALGROUND )
+               {
+                       VectorAdd (ent->v.origin, move, ent->v.origin);
+                       if (relink)
+                               SV_LinkEdict (ent, true);
+                       ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+//     Con_Printf ("fall down\n"); 
+                       return true;
+               }
+
+               return false;           // walked off an edge
+       }
+
+// check point traces down for dangling corners
+       VectorCopy (trace.endpos, ent->v.origin);
+       
+       if (!SV_CheckBottom (ent))
+       {
+               if ( (int)ent->v.flags & FL_PARTIALGROUND )
+               {       // entity had floor mostly pulled out from underneath it
+                       // and is trying to correct
+                       if (relink)
+                               SV_LinkEdict (ent, true);
+                       return true;
+               }
+               VectorCopy (oldorg, ent->v.origin);
+               return false;
+       }
+
+       if ( (int)ent->v.flags & FL_PARTIALGROUND )
+       {
+//             Con_Printf ("back on ground\n"); 
+               ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND;
+       }
+       ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+
+// the move is ok
+       if (relink)
+               SV_LinkEdict (ent, true);
+       return true;
+}
+
+
+//============================================================================
+
+/*
+======================
+SV_StepDirection
+
+Turns to the movement direction, and walks the current distance if
+facing it.
+
+======================
+*/
+void PF_changeyaw (void);
+qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
+{
+       vec3_t          move, oldorigin;
+       float           delta;
+       
+       ent->v.ideal_yaw = yaw;
+       PF_changeyaw();
+       
+       yaw = yaw*M_PI*2 / 360;
+       move[0] = cos(yaw)*dist;
+       move[1] = sin(yaw)*dist;
+       move[2] = 0;
+
+       VectorCopy (ent->v.origin, oldorigin);
+       if (SV_movestep (ent, move, false))
+       {
+               delta = ent->v.angles[YAW] - ent->v.ideal_yaw;
+               if (delta > 45 && delta < 315)
+               {               // not turned far enough, so don't take the step
+                       VectorCopy (oldorigin, ent->v.origin);
+               }
+               SV_LinkEdict (ent, true);
+               return true;
+       }
+       SV_LinkEdict (ent, true);
+               
+       return false;
+}
+
+/*
+======================
+SV_FixCheckBottom
+
+======================
+*/
+void SV_FixCheckBottom (edict_t *ent)
+{
+//     Con_Printf ("SV_FixCheckBottom\n");
+       
+       ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND;
+}
+
+
+
+/*
+================
+SV_NewChaseDir
+
+================
+*/
+#define        DI_NODIR        -1
+void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
+{
+       float           deltax,deltay;
+       float                   d[3];
+       float           tdir, olddir, turnaround;
+
+       olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 );
+       turnaround = anglemod(olddir - 180);
+
+       deltax = enemy->v.origin[0] - actor->v.origin[0];
+       deltay = enemy->v.origin[1] - actor->v.origin[1];
+       if (deltax>10)
+               d[1]= 0;
+       else if (deltax<-10)
+               d[1]= 180;
+       else
+               d[1]= DI_NODIR;
+       if (deltay<-10)
+               d[2]= 270;
+       else if (deltay>10)
+               d[2]= 90;
+       else
+               d[2]= DI_NODIR;
+
+// try direct route
+       if (d[1] != DI_NODIR && d[2] != DI_NODIR)
+       {
+               if (d[1] == 0)
+                       tdir = d[2] == 90 ? 45 : 315;
+               else
+                       tdir = d[2] == 90 ? 135 : 215;
+                       
+               if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
+                       return;
+       }
+
+// try other directions
+       if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
+       {
+               tdir=d[1];
+               d[1]=d[2];
+               d[2]=tdir;
+       }
+
+       if (d[1]!=DI_NODIR && d[1]!=turnaround 
+       && SV_StepDirection(actor, d[1], dist))
+                       return;
+
+       if (d[2]!=DI_NODIR && d[2]!=turnaround
+       && SV_StepDirection(actor, d[2], dist))
+                       return;
+
+/* there is no direct path to the player, so pick another direction */
+
+       if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
+                       return;
+
+       if (rand()&1)   /*randomly determine direction of search*/
+       {
+               for (tdir=0 ; tdir<=315 ; tdir += 45)
+                       if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
+                                       return;
+       }
+       else
+       {
+               for (tdir=315 ; tdir >=0 ; tdir -= 45)
+                       if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
+                                       return;
+       }
+
+       if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
+                       return;
+
+       actor->v.ideal_yaw = olddir;            // can't move
+
+// if a bridge was pulled out from underneath a monster, it may not have
+// a valid standing position at all
+
+       if (!SV_CheckBottom (actor))
+               SV_FixCheckBottom (actor);
+
+}
+
+/*
+======================
+SV_CloseEnough
+
+======================
+*/
+qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
+{
+       int             i;
+       
+       for (i=0 ; i<3 ; i++)
+       {
+               if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
+                       return false;
+               if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
+                       return false;
+       }
+       return true;
+}
+
+/*
+======================
+SV_MoveToGoal
+
+======================
+*/
+void SV_MoveToGoal (void)
+{
+       edict_t         *ent, *goal;
+       float           dist;
+
+       ent = PROG_TO_EDICT(pr_global_struct->self);
+       goal = PROG_TO_EDICT(ent->v.goalentity);
+       dist = G_FLOAT(OFS_PARM0);
+
+       if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
+       {
+               G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+
+// if the next step hits the enemy, return immediately
+       if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts &&  SV_CloseEnough (ent, goal, dist) )
+               return;
+
+// bump around...
+       if ( (rand()&3)==1 ||
+       !SV_StepDirection (ent, ent->v.ideal_yaw, dist))
+       {
+               SV_NewChaseDir (ent, goal, dist);
+       }
+}
+
diff --git a/sv_phys.c b/sv_phys.c
new file mode 100644 (file)
index 0000000..d6ad3d2
--- /dev/null
+++ b/sv_phys.c
@@ -0,0 +1,1444 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_phys.c
+
+#include "quakedef.h"
+
+/*
+
+
+pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
+
+onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects 
+
+doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
+bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
+corpses are SOLID_NOT and MOVETYPE_TOSS
+crates are SOLID_BBOX and MOVETYPE_TOSS
+walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
+flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
+
+solid_edge items only clip against bsp models.
+
+*/
+
+cvar_t sv_friction = {"sv_friction","4",false,true};
+cvar_t sv_stopspeed = {"sv_stopspeed","100"};
+cvar_t sv_gravity = {"sv_gravity","800",false,true};
+cvar_t sv_maxvelocity = {"sv_maxvelocity","2000"};
+cvar_t sv_nostep = {"sv_nostep","0"};
+
+static vec3_t  vec_origin = {0.0, 0.0, 0.0};
+
+#define        MOVE_EPSILON    0.01
+
+void SV_Physics_Toss (edict_t *ent);
+
+/*
+================
+SV_CheckAllEnts
+================
+*/
+void SV_CheckAllEnts (void)
+{
+       int                     e;
+       edict_t         *check;
+
+// see if any solid entities are inside the final position
+       check = NEXT_EDICT(sv.edicts);
+       for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+       {
+               if (check->free)
+                       continue;
+               if (check->v.movetype == MOVETYPE_PUSH
+               || check->v.movetype == MOVETYPE_NONE
+               || check->v.movetype == MOVETYPE_FOLLOW
+               || check->v.movetype == MOVETYPE_NOCLIP)
+                       continue;
+
+               if (SV_TestEntityPosition (check))
+                       Con_Printf ("entity in invalid position\n");
+       }
+}
+
+/*
+================
+SV_CheckVelocity
+================
+*/
+void SV_CheckVelocity (edict_t *ent)
+{
+       int             i;
+       float   wishspeed;
+
+//
+// bound velocity
+//
+       for (i=0 ; i<3 ; i++)
+       {
+               if (IS_NAN(ent->v.velocity[i]))
+               {
+                       Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname);
+                       ent->v.velocity[i] = 0;
+               }
+               if (IS_NAN(ent->v.origin[i]))
+               {
+                       Con_Printf ("Got a NaN origin on %s\n", pr_strings + ent->v.classname);
+                       ent->v.origin[i] = 0;
+               }
+               // LordHavoc: maxvelocity fix, see below
+/*
+               if (ent->v.velocity[i] > sv_maxvelocity.value)
+                       ent->v.velocity[i] = sv_maxvelocity.value;
+               else if (ent->v.velocity[i] < -sv_maxvelocity.value)
+                       ent->v.velocity[i] = -sv_maxvelocity.value;
+*/
+       }
+
+       // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
+       wishspeed = DotProduct(ent->v.velocity, ent->v.velocity);
+       if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
+       {
+               wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
+               ent->v.velocity[0] *= wishspeed;
+               ent->v.velocity[1] *= wishspeed;
+               ent->v.velocity[2] *= wishspeed;
+               wishspeed = sv_maxvelocity.value;
+       }
+}
+
+/*
+=============
+SV_RunThink
+
+Runs thinking code if time.  There is some play in the exact time the think
+function will be called, because it is called before any movement is done
+in a frame.  Not used for pushmove objects, because they must be exact.
+Returns false if the entity removed itself.
+=============
+*/
+qboolean SV_RunThink (edict_t *ent)
+{
+       float   thinktime;
+
+       thinktime = ent->v.nextthink;
+       if (thinktime <= 0 || thinktime > sv.time + host_frametime)
+               return true;
+               
+       if (thinktime < sv.time)
+               thinktime = sv.time;    // don't let things stay in the past.
+                                                               // it is possible to start that way
+                                                               // by a trigger with a local time.
+       ent->v.nextthink = 0;
+       pr_global_struct->time = thinktime;
+       pr_global_struct->self = EDICT_TO_PROG(ent);
+       pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+       PR_ExecuteProgram (ent->v.think);
+       return !ent->free;
+}
+
+/*
+==================
+SV_Impact
+
+Two entities have touched, so run their touch functions
+==================
+*/
+void SV_Impact (edict_t *e1, edict_t *e2)
+{
+       int             old_self, old_other;
+       
+       old_self = pr_global_struct->self;
+       old_other = pr_global_struct->other;
+       
+       pr_global_struct->time = sv.time;
+       if (e1->v.touch && e1->v.solid != SOLID_NOT)
+       {
+               pr_global_struct->self = EDICT_TO_PROG(e1);
+               pr_global_struct->other = EDICT_TO_PROG(e2);
+               PR_ExecuteProgram (e1->v.touch);
+       }
+       
+       if (e2->v.touch && e2->v.solid != SOLID_NOT)
+       {
+               pr_global_struct->self = EDICT_TO_PROG(e2);
+               pr_global_struct->other = EDICT_TO_PROG(e1);
+               PR_ExecuteProgram (e2->v.touch);
+       }
+
+       pr_global_struct->self = old_self;
+       pr_global_struct->other = old_other;
+}
+
+
+/*
+==================
+ClipVelocity
+
+Slide off of the impacting object
+returns the blocked flags (1 = floor, 2 = step / wall)
+==================
+*/
+#define        STOP_EPSILON    0.1
+
+int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
+{
+       float   backoff;
+       float   change;
+       int             i, blocked;
+       
+       blocked = 0;
+       if (normal[2] > 0)
+               blocked |= 1;           // floor
+       if (!normal[2])
+               blocked |= 2;           // step
+       
+       backoff = DotProduct (in, normal) * overbounce;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               change = normal[i]*backoff;
+               out[i] = in[i] - change;
+               if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
+                       out[i] = 0;
+       }
+       
+       return blocked;
+}
+
+
+/*
+============
+SV_FlyMove
+
+The basic solid body movement clip that slides along multiple planes
+Returns the clipflags if the velocity was modified (hit something solid)
+1 = floor
+2 = wall / step
+4 = dead stop
+If steptrace is not NULL, the trace of any vertical wall hit will be stored
+============
+*/
+#define        MAX_CLIP_PLANES 5
+int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
+{
+       int                     bumpcount, numbumps;
+       vec3_t          dir;
+       float           d;
+       int                     numplanes;
+       vec3_t          planes[MAX_CLIP_PLANES];
+       vec3_t          primal_velocity, original_velocity, new_velocity;
+       int                     i, j;
+       trace_t         trace;
+       vec3_t          end;
+       float           time_left;
+       int                     blocked;
+       
+       numbumps = 4;
+       
+       blocked = 0;
+       VectorCopy (ent->v.velocity, original_velocity);
+       VectorCopy (ent->v.velocity, primal_velocity);
+       numplanes = 0;
+       
+       time_left = time;
+
+       for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
+       {
+               if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2])
+                       break;
+
+               for (i=0 ; i<3 ; i++)
+                       end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];
+
+               trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+
+               if (trace.allsolid)
+               {       // entity is trapped in another solid
+                       VectorCopy (vec3_origin, ent->v.velocity);
+                       return 3;
+               }
+
+               if (trace.fraction > 0)
+               {       // actually covered some distance
+                       VectorCopy (trace.endpos, ent->v.origin);
+                       VectorCopy (ent->v.velocity, original_velocity);
+                       numplanes = 0;
+               }
+
+               if (trace.fraction == 1)
+                        break;         // moved the entire distance
+
+               if (!trace.ent)
+                       Sys_Error ("SV_FlyMove: !trace.ent");
+
+               if (trace.plane.normal[2] > 0.7)
+               {
+                       blocked |= 1;           // floor
+                       if (trace.ent->v.solid == SOLID_BSP)
+                       {
+                               ent->v.flags =  (int)ent->v.flags | FL_ONGROUND;
+                               ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+                       }
+               }
+               if (!trace.plane.normal[2])
+               {
+                       blocked |= 2;           // step
+                       if (steptrace)
+                               *steptrace = trace;     // save for player extrafriction
+               }
+
+//
+// run the impact function
+//
+               SV_Impact (ent, trace.ent);
+               if (ent->free)
+                       break;          // removed by the impact function
+
+               
+               time_left -= time_left * trace.fraction;
+               
+       // cliped to another plane
+               if (numplanes >= MAX_CLIP_PLANES)
+               {       // this shouldn't really happen
+                       VectorCopy (vec3_origin, ent->v.velocity);
+                       return 3;
+               }
+
+               VectorCopy (trace.plane.normal, planes[numplanes]);
+               numplanes++;
+
+//
+// modify original_velocity so it parallels all of the clip planes
+//
+               for (i=0 ; i<numplanes ; i++)
+               {
+                       ClipVelocity (original_velocity, planes[i], new_velocity, 1);
+                       for (j=0 ; j<numplanes ; j++)
+                               if (j != i)
+                               {
+                                       if (DotProduct (new_velocity, planes[j]) < 0)
+                                               break;  // not ok
+                               }
+                       if (j == numplanes)
+                               break;
+               }
+               
+               if (i != numplanes)
+               {       // go along this plane
+                       VectorCopy (new_velocity, ent->v.velocity);
+               }
+               else
+               {       // go along the crease
+                       if (numplanes != 2)
+                       {
+//                             Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
+                               VectorCopy (vec3_origin, ent->v.velocity);
+                               return 7;
+                       }
+                       CrossProduct (planes[0], planes[1], dir);
+                       d = DotProduct (dir, ent->v.velocity);
+                       VectorScale (dir, d, ent->v.velocity);
+               }
+
+//
+// if original velocity is against the original velocity, stop dead
+// to avoid tiny occilations in sloping corners
+//
+               if (DotProduct (ent->v.velocity, primal_velocity) <= 0)
+               {
+                       VectorCopy (vec3_origin, ent->v.velocity);
+                       return blocked;
+               }
+       }
+
+       return blocked;
+}
+
+
+/*
+============
+SV_AddGravity
+
+============
+*/
+void SV_AddGravity (edict_t *ent)
+{
+       float   ent_gravity;
+
+       eval_t  *val;
+
+       val = GETEDICTFIELDVALUE(ent, eval_gravity);
+       if (val && val->_float)
+               ent_gravity = val->_float;
+       else
+               ent_gravity = 1.0;
+       ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime;
+}
+
+
+/*
+===============================================================================
+
+PUSHMOVE
+
+===============================================================================
+*/
+
+/*
+============
+SV_PushEntity
+
+Does not change the entities velocity at all
+============
+*/
+trace_t SV_PushEntity (edict_t *ent, vec3_t push)
+{
+       trace_t trace;
+       vec3_t  end;
+               
+       VectorAdd (ent->v.origin, push, end);
+
+       if (ent->v.movetype == MOVETYPE_FLYMISSILE)
+               trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
+       else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)
+       // only clip against bmodels
+               trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
+       else
+               trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);       
+       
+       VectorCopy (trace.endpos, ent->v.origin);
+       SV_LinkEdict (ent, true);
+
+       if (trace.ent)
+               SV_Impact (ent, trace.ent);             
+
+       return trace;
+}                                      
+
+
+/*
+============
+SV_PushMove
+
+============
+*/
+void SV_PushMove (edict_t *pusher, float movetime)
+{
+       int                     i, e;
+       edict_t         *check, *block;
+       vec3_t          mins, maxs, move;
+       vec3_t          entorig, pushorig;
+       int                     num_moved;
+       edict_t         *moved_edict[MAX_EDICTS];
+       vec3_t          moved_from[MAX_EDICTS];
+       float           savesolid;
+
+       if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
+       {
+               pusher->v.ltime += movetime;
+               return;
+       }
+
+       for (i=0 ; i<3 ; i++)
+       {
+               move[i] = pusher->v.velocity[i] * movetime;
+               mins[i] = pusher->v.absmin[i] + move[i];
+               maxs[i] = pusher->v.absmax[i] + move[i];
+       }
+
+       VectorCopy (pusher->v.origin, pushorig);
+       
+// move the pusher to it's final position
+
+       VectorAdd (pusher->v.origin, move, pusher->v.origin);
+       pusher->v.ltime += movetime;
+       SV_LinkEdict (pusher, false);
+
+
+// see if any solid entities are inside the final position
+       num_moved = 0;
+       check = NEXT_EDICT(sv.edicts);
+       for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
+       {
+               if (check->free)
+                       continue;
+               if (check->v.movetype == MOVETYPE_PUSH
+                || check->v.movetype == MOVETYPE_NONE
+                || check->v.movetype == MOVETYPE_FOLLOW
+                || check->v.movetype == MOVETYPE_NOCLIP)
+                       continue;
+
+               // if the entity is standing on the pusher, it will definitely be moved
+               if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher))
+               {
+                       if (check->v.absmin[0] >= maxs[0]
+                        || check->v.absmin[1] >= maxs[1]
+                        || check->v.absmin[2] >= maxs[2]
+                        || check->v.absmax[0] <= mins[0]
+                        || check->v.absmax[1] <= mins[1]
+                        || check->v.absmax[2] <= mins[2])
+                               continue;
+
+                       // see if the ent's bbox is inside the pusher's final position
+                       if (!SV_TestEntityPosition (check))
+                               continue;
+               }
+
+               // remove the onground flag for non-players
+               if (check->v.movetype != MOVETYPE_WALK)
+                       check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
+               
+               VectorCopy (check->v.origin, entorig);
+               VectorCopy (check->v.origin, moved_from[num_moved]);
+               moved_edict[num_moved] = check;
+               num_moved++;
+
+               // LordHavoc: pusher fixes (teleport train bug, etc)
+               savesolid = pusher->v.solid;
+               if (savesolid == SOLID_BSP || savesolid == SOLID_BBOX || savesolid == SOLID_SLIDEBOX)
+               {
+                       // try moving the contacted entity
+                       pusher->v.solid = SOLID_NOT;
+                       SV_PushEntity (check, move);
+                       pusher->v.solid = savesolid; // was SOLID_BSP
+
+                       // if it is still inside the pusher, block
+                       if (block = SV_TestEntityPosition (check))
+                       {       // fail the move
+                               if (check->v.mins[0] == check->v.maxs[0])
+                                       continue;
+                               if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
+                               {       // corpse
+                                       check->v.mins[0] = check->v.mins[1] = 0;
+                                       VectorCopy (check->v.mins, check->v.maxs);
+                                       continue;
+                               }
+                               
+                               VectorCopy (entorig, check->v.origin);
+                               SV_LinkEdict (check, true);
+
+                               VectorCopy (pushorig, pusher->v.origin);
+                               SV_LinkEdict (pusher, false);
+                               pusher->v.ltime -= movetime;
+
+                               // if the pusher has a "blocked" function, call it
+                               // otherwise, just stay in place until the obstacle is gone
+                               if (pusher->v.blocked)
+                               {
+                                       pr_global_struct->self = EDICT_TO_PROG(pusher);
+                                       pr_global_struct->other = EDICT_TO_PROG(check);
+                                       PR_ExecuteProgram (pusher->v.blocked);
+                               }
+                               
+                               // move back any entities we already moved
+                               for (i=0 ; i<num_moved ; i++)
+                               {
+                                       VectorCopy (moved_from[i], moved_edict[i]->v.origin);
+                                       SV_LinkEdict (moved_edict[i], false);
+                               }
+                               return;
+                       }       
+               }
+       }
+
+       
+}
+
+/*
+============
+SV_PushRotate
+
+============
+*/
+void SV_PushRotate (edict_t *pusher, float movetime)
+{
+       int                     i, e;
+       edict_t         *check, *block;
+       vec3_t          move, a, amove;
+       vec3_t          entorig, pushorig;
+       int                     num_moved;
+       edict_t         *moved_edict[MAX_EDICTS];
+       vec3_t          moved_from[MAX_EDICTS];
+       vec3_t          org, org2;
+       vec3_t          forward, right, up;
+       float           savesolid;
+
+       if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
+       {
+               pusher->v.ltime += movetime;
+               return;
+       }
+
+       for (i=0 ; i<3 ; i++)
+               amove[i] = pusher->v.avelocity[i] * movetime;
+
+       VectorSubtract (vec3_origin, amove, a);
+       AngleVectors (a, forward, right, up);
+
+       VectorCopy (pusher->v.angles, pushorig);
+       
+// move the pusher to it's final position
+
+       VectorAdd (pusher->v.angles, amove, pusher->v.angles);
+       pusher->v.ltime += movetime;
+       SV_LinkEdict (pusher, false);
+
+
+// see if any solid entities are inside the final position
+       num_moved = 0;
+       check = NEXT_EDICT(sv.edicts);
+       for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+       {
+               if (check->free)
+                       continue;
+               if (check->v.movetype == MOVETYPE_PUSH
+               || check->v.movetype == MOVETYPE_NONE
+               || check->v.movetype == MOVETYPE_FOLLOW
+               || check->v.movetype == MOVETYPE_NOCLIP)
+                       continue;
+
+       // if the entity is standing on the pusher, it will definately be moved
+               if ( ! ( ((int)check->v.flags & FL_ONGROUND)
+               && PROG_TO_EDICT(check->v.groundentity) == pusher) )
+               {
+                       if ( check->v.absmin[0] >= pusher->v.absmax[0]
+                       || check->v.absmin[1] >= pusher->v.absmax[1]
+                       || check->v.absmin[2] >= pusher->v.absmax[2]
+                       || check->v.absmax[0] <= pusher->v.absmin[0]
+                       || check->v.absmax[1] <= pusher->v.absmin[1]
+                       || check->v.absmax[2] <= pusher->v.absmin[2] )
+                               continue;
+
+               // see if the ent's bbox is inside the pusher's final position
+                       if (!SV_TestEntityPosition (check))
+                               continue;
+               }
+
+       // remove the onground flag for non-players
+               if (check->v.movetype != MOVETYPE_WALK)
+                       check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
+               
+               VectorCopy (check->v.origin, entorig);
+               VectorCopy (check->v.origin, moved_from[num_moved]);
+               moved_edict[num_moved] = check;
+               num_moved++;
+
+               // calculate destination position
+               VectorSubtract (check->v.origin, pusher->v.origin, org);
+               org2[0] = DotProduct (org, forward);
+               org2[1] = -DotProduct (org, right);
+               org2[2] = DotProduct (org, up);
+               VectorSubtract (org2, org, move);
+
+               // try moving the contacted entity 
+               savesolid = pusher->v.solid; // LordHavoc: restore to correct solid type
+               pusher->v.solid = SOLID_NOT;
+               SV_PushEntity (check, move);
+               pusher->v.solid = savesolid; // LordHavoc: restore to correct solid type
+
+       // if it is still inside the pusher, block
+               block = SV_TestEntityPosition (check);
+               if (block)
+               {       // fail the move
+                       if (check->v.mins[0] == check->v.maxs[0])
+                               continue;
+                       if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
+                       {       // corpse
+                               check->v.mins[0] = check->v.mins[1] = 0;
+                               VectorCopy (check->v.mins, check->v.maxs);
+                               continue;
+                       }
+                       
+                       VectorCopy (entorig, check->v.origin);
+                       SV_LinkEdict (check, true);
+
+                       VectorCopy (pushorig, pusher->v.angles);
+                       SV_LinkEdict (pusher, false);
+                       pusher->v.ltime -= movetime;
+
+                       // if the pusher has a "blocked" function, call it
+                       // otherwise, just stay in place until the obstacle is gone
+                       if (pusher->v.blocked)
+                       {
+                               pr_global_struct->self = EDICT_TO_PROG(pusher);
+                               pr_global_struct->other = EDICT_TO_PROG(check);
+                               PR_ExecuteProgram (pusher->v.blocked);
+                       }
+                       
+               // move back any entities we already moved
+                       for (i=0 ; i<num_moved ; i++)
+                       {
+                               VectorCopy (moved_from[i], moved_edict[i]->v.origin);
+                               VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles);
+                               SV_LinkEdict (moved_edict[i], false);
+                       }
+                       return;
+               }
+               else
+               {
+                       VectorAdd (check->v.angles, amove, check->v.angles);
+               }
+       }
+
+       
+}
+//#endif
+
+/*
+================
+SV_Physics_Pusher
+
+================
+*/
+void SV_Physics_Pusher (edict_t *ent)
+{
+       float   thinktime;
+       float   oldltime;
+       float   movetime;
+
+       oldltime = ent->v.ltime;
+       
+       thinktime = ent->v.nextthink;
+       if (thinktime < ent->v.ltime + host_frametime)
+       {
+               movetime = thinktime - ent->v.ltime;
+               if (movetime < 0)
+                       movetime = 0;
+       }
+       else
+               movetime = host_frametime;
+
+       if (movetime)
+       {
+               if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
+                       SV_PushRotate (ent, movetime);
+               else
+                       SV_PushMove (ent, movetime);    // advances ent->v.ltime if not blocked
+       }
+               
+       if (thinktime > oldltime && thinktime <= ent->v.ltime)
+       {
+               ent->v.nextthink = 0;
+               pr_global_struct->time = sv.time;
+               pr_global_struct->self = EDICT_TO_PROG(ent);
+               pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+               PR_ExecuteProgram (ent->v.think);
+               if (ent->free)
+                       return;
+       }
+
+}
+
+
+/*
+===============================================================================
+
+CLIENT MOVEMENT
+
+===============================================================================
+*/
+
+/*
+=============
+SV_CheckStuck
+
+This is a big hack to try and fix the rare case of getting stuck in the world
+clipping hull.
+=============
+*/
+void SV_CheckStuck (edict_t *ent)
+{
+       int             i, j;
+       int             z;
+       vec3_t  org;
+
+       if (!SV_TestEntityPosition(ent))
+       {
+               VectorCopy (ent->v.origin, ent->v.oldorigin);
+               return;
+       }
+
+       VectorCopy (ent->v.origin, org);
+       VectorCopy (ent->v.oldorigin, ent->v.origin);
+       if (!SV_TestEntityPosition(ent))
+       {
+               Con_DPrintf ("Unstuck.\n");
+               SV_LinkEdict (ent, true);
+               return;
+       }
+       
+       for (z=0 ; z< 18 ; z++)
+               for (i=-1 ; i <= 1 ; i++)
+                       for (j=-1 ; j <= 1 ; j++)
+                       {
+                               ent->v.origin[0] = org[0] + i;
+                               ent->v.origin[1] = org[1] + j;
+                               ent->v.origin[2] = org[2] + z;
+                               if (!SV_TestEntityPosition(ent))
+                               {
+                                       Con_DPrintf ("Unstuck.\n");
+                                       SV_LinkEdict (ent, true);
+                                       return;
+                               }
+                       }
+                       
+       VectorCopy (org, ent->v.origin);
+       Con_DPrintf ("player is stuck.\n");
+}
+
+
+/*
+=============
+SV_CheckWater
+=============
+*/
+qboolean SV_CheckWater (edict_t *ent)
+{
+       vec3_t  point;
+       int             cont;
+
+       point[0] = ent->v.origin[0];
+       point[1] = ent->v.origin[1];
+       point[2] = ent->v.origin[2] + ent->v.mins[2] + 1;       
+       
+       ent->v.waterlevel = 0;
+       ent->v.watertype = CONTENTS_EMPTY;
+       cont = SV_PointContents (point);
+       if (cont <= CONTENTS_WATER)
+       {
+               ent->v.watertype = cont;
+               ent->v.waterlevel = 1;
+               point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5;
+               cont = SV_PointContents (point);
+               if (cont <= CONTENTS_WATER)
+               {
+                       ent->v.waterlevel = 2;
+                       point[2] = ent->v.origin[2] + ent->v.view_ofs[2];
+                       cont = SV_PointContents (point);
+                       if (cont <= CONTENTS_WATER)
+                               ent->v.waterlevel = 3;
+               }
+       }
+       
+       return ent->v.waterlevel > 1;
+}
+
+/*
+============
+SV_WallFriction
+
+============
+*/
+void SV_WallFriction (edict_t *ent, trace_t *trace)
+{
+       vec3_t          forward, right, up;
+       float           d, i;
+       vec3_t          into, side;
+       
+       AngleVectors (ent->v.v_angle, forward, right, up);
+       d = DotProduct (trace->plane.normal, forward);
+       
+       d += 0.5;
+       if (d >= 0)
+               return;
+               
+// cut the tangential velocity
+       i = DotProduct (trace->plane.normal, ent->v.velocity);
+       VectorScale (trace->plane.normal, i, into);
+       VectorSubtract (ent->v.velocity, into, side);
+       
+       ent->v.velocity[0] = side[0] * (1 + d);
+       ent->v.velocity[1] = side[1] * (1 + d);
+}
+
+/*
+=====================
+SV_TryUnstick
+
+Player has come to a dead stop, possibly due to the problem with limited
+float precision at some angle joins in the BSP hull.
+
+Try fixing by pushing one pixel in each direction.
+
+This is a hack, but in the interest of good gameplay...
+======================
+*/
+int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
+{
+       int             i;
+       vec3_t  oldorg;
+       vec3_t  dir;
+       int             clip;
+       trace_t steptrace;
+       
+       VectorCopy (ent->v.origin, oldorg);
+       VectorCopy (vec3_origin, dir);
+
+       for (i=0 ; i<8 ; i++)
+       {
+// try pushing a little in an axial direction
+               switch (i)
+               {
+                       case 0: dir[0] = 2; dir[1] = 0; break;
+                       case 1: dir[0] = 0; dir[1] = 2; break;
+                       case 2: dir[0] = -2; dir[1] = 0; break;
+                       case 3: dir[0] = 0; dir[1] = -2; break;
+                       case 4: dir[0] = 2; dir[1] = 2; break;
+                       case 5: dir[0] = -2; dir[1] = 2; break;
+                       case 6: dir[0] = 2; dir[1] = -2; break;
+                       case 7: dir[0] = -2; dir[1] = -2; break;
+               }
+               
+               SV_PushEntity (ent, dir);
+
+// retry the original move
+               ent->v.velocity[0] = oldvel[0];
+               ent->v. velocity[1] = oldvel[1];
+               ent->v. velocity[2] = 0;
+               clip = SV_FlyMove (ent, 0.1, &steptrace);
+
+               if ( fabs(oldorg[1] - ent->v.origin[1]) > 4
+               || fabs(oldorg[0] - ent->v.origin[0]) > 4 )
+               {
+//Con_DPrintf ("unstuck!\n");
+                       return clip;
+               }
+                       
+// go back to the original pos and try again
+               VectorCopy (oldorg, ent->v.origin);
+       }
+       
+       VectorCopy (vec3_origin, ent->v.velocity);
+       return 7;               // still not moving
+}
+
+/*
+=====================
+SV_WalkMove
+
+Only used by players
+======================
+*/
+#define        STEPSIZE        18
+void SV_WalkMove (edict_t *ent)
+{
+       vec3_t          upmove, downmove;
+       vec3_t          oldorg, oldvel;
+       vec3_t          nosteporg, nostepvel;
+       int                     clip;
+       int                     oldonground;
+       trace_t         steptrace, downtrace;
+       
+//
+// do a regular slide move unless it looks like you ran into a step
+//
+       oldonground = (int)ent->v.flags & FL_ONGROUND;
+       ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+       
+       VectorCopy (ent->v.origin, oldorg);
+       VectorCopy (ent->v.velocity, oldvel);
+       
+       clip = SV_FlyMove (ent, host_frametime, &steptrace);
+
+       if ( !(clip & 2) )
+               return;         // move didn't block on a step
+
+       if (!oldonground && ent->v.waterlevel == 0)
+               return;         // don't stair up while jumping
+       
+       if (ent->v.movetype != MOVETYPE_WALK)
+               return;         // gibbed by a trigger
+       
+       if (sv_nostep.value)
+               return;
+       
+       if ( (int)sv_player->v.flags & FL_WATERJUMP )
+               return;
+
+       VectorCopy (ent->v.origin, nosteporg);
+       VectorCopy (ent->v.velocity, nostepvel);
+
+//
+// try moving up and forward to go up a step
+//
+       VectorCopy (oldorg, ent->v.origin);     // back to start pos
+
+       VectorCopy (vec3_origin, upmove);
+       VectorCopy (vec3_origin, downmove);
+       upmove[2] = STEPSIZE;
+       downmove[2] = -STEPSIZE + oldvel[2]*host_frametime;
+
+// move up
+       SV_PushEntity (ent, upmove);    // FIXME: don't link?
+
+// move forward
+       ent->v.velocity[0] = oldvel[0];
+       ent->v. velocity[1] = oldvel[1];
+       ent->v. velocity[2] = 0;
+       clip = SV_FlyMove (ent, host_frametime, &steptrace);
+
+// check for stuckness, possibly due to the limited precision of floats
+// in the clipping hulls
+       if (clip)
+       {
+               if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125
+               && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )
+               {       // stepping up didn't make any progress
+                       clip = SV_TryUnstick (ent, oldvel);
+               }
+       }
+       
+// extra friction based on view angle
+       if ( clip & 2 )
+               SV_WallFriction (ent, &steptrace);
+
+// move down
+       downtrace = SV_PushEntity (ent, downmove);      // FIXME: don't link?
+
+       if (downtrace.plane.normal[2] > 0.7)
+       {
+               if (ent->v.solid == SOLID_BSP)
+               {
+                       ent->v.flags =  (int)ent->v.flags | FL_ONGROUND;
+                       ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
+               }
+       }
+       else
+       {
+// if the push down didn't end up on good ground, use the move without
+// the step up.  This happens near wall / slope combinations, and can
+// cause the player to hop up higher on a slope too steep to climb     
+               VectorCopy (nosteporg, ent->v.origin);
+               VectorCopy (nostepvel, ent->v.velocity);
+       }
+}
+
+
+/*
+================
+SV_Physics_Client
+
+Player character actions
+================
+*/
+void SV_Physics_Client (edict_t        *ent, int num)
+{
+       if ( ! svs.clients[num-1].active )
+               return;         // unconnected slot
+
+//
+// call standard client pre-think
+//     
+       pr_global_struct->time = sv.time;
+       pr_global_struct->self = EDICT_TO_PROG(ent);
+       PR_ExecuteProgram (pr_global_struct->PlayerPreThink);
+       
+//
+// do a move
+//
+       SV_CheckVelocity (ent);
+
+//
+// decide which move function to call
+//
+       switch ((int)ent->v.movetype)
+       {
+       case MOVETYPE_NONE:
+               if (!SV_RunThink (ent))
+                       return;
+               break;
+
+       case MOVETYPE_WALK:
+               if (!SV_RunThink (ent))
+                       return;
+               if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
+                       SV_AddGravity (ent);
+               SV_CheckStuck (ent);
+               SV_WalkMove (ent);
+               break;
+               
+       case MOVETYPE_TOSS:
+       case MOVETYPE_BOUNCE:
+               SV_Physics_Toss (ent);
+               break;
+
+       case MOVETYPE_FLY:
+               if (!SV_RunThink (ent))
+                       return;
+               SV_FlyMove (ent, host_frametime, NULL);
+               break;
+               
+       case MOVETYPE_NOCLIP:
+               if (!SV_RunThink (ent))
+                       return;
+               VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
+               break;
+               
+       default:
+               Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
+       }
+
+//
+// call standard player post-think
+//             
+       SV_LinkEdict (ent, true);
+
+       pr_global_struct->time = sv.time;
+       pr_global_struct->self = EDICT_TO_PROG(ent);
+       PR_ExecuteProgram (pr_global_struct->PlayerPostThink);
+}
+
+//============================================================================
+
+/*
+=============
+SV_Physics_None
+
+Non moving objects can only think
+=============
+*/
+void SV_Physics_None (edict_t *ent)
+{
+// regular thinking
+       SV_RunThink (ent);
+}
+
+/*
+=============
+SV_Physics_Follow
+
+Entities that are "stuck" to another entity
+=============
+*/
+void SV_Physics_Follow (edict_t *ent)
+{
+       vec3_t vf, vu, vr, angles;
+       edict_t *e;
+// regular thinking
+       SV_RunThink (ent);
+       // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
+       e = PROG_TO_EDICT(ent->v.aiment);
+       angles[0] = -e->v.angles[0];
+       angles[1] = e->v.angles[1];
+       angles[2] = e->v.angles[2];
+       AngleVectors (angles, vf, vr, vu);
+       VectorMA (e->v.origin, ent->v.view_ofs[0], vf, ent->v.origin);
+       VectorMA (ent->v.origin, ent->v.view_ofs[1], vr, ent->v.origin);
+       VectorMA (ent->v.origin, ent->v.view_ofs[2], vu, ent->v.origin);
+       VectorAdd (e->v.angles, ent->v.v_angle, ent->v.angles);
+//     VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin);
+       SV_LinkEdict (ent, true);
+}
+
+/*
+=============
+SV_Physics_Noclip
+
+A moving object that doesn't obey physics
+=============
+*/
+void SV_Physics_Noclip (edict_t *ent)
+{
+// regular thinking
+       if (!SV_RunThink (ent))
+               return;
+       
+       VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
+       VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
+
+       SV_LinkEdict (ent, false);
+}
+
+/*
+==============================================================================
+
+TOSS / BOUNCE
+
+==============================================================================
+*/
+
+/*
+=============
+SV_CheckWaterTransition
+
+=============
+*/
+void SV_CheckWaterTransition (edict_t *ent)
+{
+       int             cont;
+       cont = SV_PointContents (ent->v.origin);
+       if (!ent->v.watertype)
+       {       // just spawned here
+               ent->v.watertype = cont;
+               ent->v.waterlevel = 1;
+               return;
+       }
+       
+       if (cont <= CONTENTS_WATER)
+       {
+               if (ent->v.watertype == CONTENTS_EMPTY)
+               {       // just crossed into water
+                       SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
+               }               
+               ent->v.watertype = cont;
+               ent->v.waterlevel = 1;
+       }
+       else
+       {
+               if (ent->v.watertype != CONTENTS_EMPTY)
+               {       // just crossed into water
+                       SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
+               }               
+               ent->v.watertype = CONTENTS_EMPTY;
+               ent->v.waterlevel = cont;
+       }
+}
+
+/*
+=============
+SV_Physics_Toss
+
+Toss, bounce, and fly movement.  When onground, do nothing.
+=============
+*/
+void SV_Physics_Toss (edict_t *ent)
+{
+       trace_t trace;
+       vec3_t  move;
+       float   backoff;
+       // regular thinking
+       if (!SV_RunThink (ent))
+               return;
+
+// if onground, return without moving
+       if ( ((int)ent->v.flags & FL_ONGROUND) )
+               return;
+
+       SV_CheckVelocity (ent);
+
+// add gravity
+       if (ent->v.movetype != MOVETYPE_FLY
+       && ent->v.movetype != MOVETYPE_BOUNCEMISSILE // LordHavoc: enabled MOVETYPE_BOUNCEMISSILE
+       && ent->v.movetype != MOVETYPE_FLYMISSILE)
+               SV_AddGravity (ent);
+
+// move angles
+       VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
+
+// move origin
+       VectorScale (ent->v.velocity, host_frametime, move);
+       trace = SV_PushEntity (ent, move);
+       if (trace.fraction == 1)
+               return;
+       if (ent->free)
+               return;
+       
+       if (ent->v.movetype == MOVETYPE_BOUNCE)
+               backoff = 1.5;
+       else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE)
+               backoff = 2.0;
+       else
+               backoff = 1;
+
+       ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
+
+// stop if on ground
+       if (trace.plane.normal[2] > 0.7)
+       {               
+               if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE))
+               {
+                       ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
+                       ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+                       VectorCopy (vec3_origin, ent->v.velocity);
+                       VectorCopy (vec3_origin, ent->v.avelocity);
+               }
+       }
+       
+// check for in water
+       SV_CheckWaterTransition (ent);
+}
+
+/*
+===============================================================================
+
+STEPPING MOVEMENT
+
+===============================================================================
+*/
+
+/*
+=============
+SV_Physics_Step
+
+Monsters freefall when they don't have a ground entity, otherwise
+all movement is done with discrete steps.
+
+This is also used for objects that have become still on the ground, but
+will fall if the floor is pulled out from under them.
+=============
+*/
+void SV_Physics_Step (edict_t *ent)
+{
+       qboolean        hitsound;
+
+// freefall if not onground
+       if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )
+       {
+               if (ent->v.velocity[2] < sv_gravity.value*-0.1)
+                       hitsound = true;
+               else
+                       hitsound = false;
+
+               SV_AddGravity (ent);
+               SV_CheckVelocity (ent);
+               SV_FlyMove (ent, host_frametime, NULL);
+               SV_LinkEdict (ent, true);
+
+               if ( (int)ent->v.flags & FL_ONGROUND )  // just hit ground
+               {
+                       if (hitsound)
+                               SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
+               }
+       }
+
+// regular thinking
+       SV_RunThink (ent);
+       
+       SV_CheckWaterTransition (ent);
+}
+
+//============================================================================
+
+/*
+================
+SV_Physics
+
+================
+*/
+extern dfunction_t *EndFrameQC;
+void SV_Physics (void)
+{
+       int             i;
+       edict_t *ent;
+
+// let the progs know that a new frame has started
+       pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
+       pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+       pr_global_struct->time = sv.time;
+       PR_ExecuteProgram (pr_global_struct->StartFrame);
+
+//SV_CheckAllEnts ();
+
+//
+// treat each object in turn
+//
+       ent = sv.edicts;
+       for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
+       {
+               if (ent->free)
+                       continue;
+
+               if (pr_global_struct->force_retouch)
+               {
+                       SV_LinkEdict (ent, true);       // force retouch even for stationary
+               }
+
+               if (i > 0 && i <= svs.maxclients)
+                       SV_Physics_Client (ent, i);
+               else if (ent->v.movetype == MOVETYPE_PUSH)
+                       SV_Physics_Pusher (ent);
+               else if (ent->v.movetype == MOVETYPE_NONE)
+                       SV_Physics_None (ent);
+               else if (ent->v.movetype == MOVETYPE_FOLLOW)
+                       SV_Physics_Follow (ent);
+               else if (ent->v.movetype == MOVETYPE_NOCLIP)
+                       SV_Physics_Noclip (ent);
+               else if (ent->v.movetype == MOVETYPE_STEP)
+                       SV_Physics_Step (ent);
+               // LordHavoc: added support for MOVETYPE_WALK on normal entities! :)
+               else if (ent->v.movetype == MOVETYPE_WALK)
+               {
+                       if (SV_RunThink (ent))
+                       {
+                               if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
+                                       SV_AddGravity (ent);
+                               SV_CheckStuck (ent);
+                               SV_WalkMove (ent);
+                       }
+               }
+               else if (ent->v.movetype == MOVETYPE_TOSS 
+               || ent->v.movetype == MOVETYPE_BOUNCE
+               || ent->v.movetype == MOVETYPE_BOUNCEMISSILE
+               || ent->v.movetype == MOVETYPE_FLY
+               || ent->v.movetype == MOVETYPE_FLYMISSILE)
+                       SV_Physics_Toss (ent);
+               else
+                       Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);                        
+       }
+       
+       if (pr_global_struct->force_retouch)
+               pr_global_struct->force_retouch--;      
+
+       // LordHavoc: endframe support
+       if (EndFrameQC)
+       {
+               pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
+               pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+               pr_global_struct->time = sv.time;
+               PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions));
+       }
+
+       sv.time += host_frametime;
+}
+
+
+trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore)
+{
+       int i;
+       edict_t tempent, *tent;
+       trace_t trace;
+       vec3_t  move;
+       vec3_t  end;
+       double  save_frametime;
+
+       save_frametime = host_frametime;
+       host_frametime = 0.05;
+
+       memcpy(&tempent, ent, sizeof(edict_t));
+       tent = &tempent;
+
+       for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
+       {
+               SV_CheckVelocity (tent);
+               SV_AddGravity (tent);
+               VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles);
+               VectorScale (tent->v.velocity, host_frametime, move);
+               VectorAdd (tent->v.origin, move, end);
+               trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent);   
+               VectorCopy (trace.endpos, tent->v.origin);
+
+               if (trace.ent)
+                       if (trace.ent != ignore)
+                               break;
+       }
+       host_frametime = save_frametime;
+       return trace;
+}
diff --git a/sv_user.c b/sv_user.c
new file mode 100644 (file)
index 0000000..08b3119
--- /dev/null
+++ b/sv_user.c
@@ -0,0 +1,644 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_user.c -- server code for moving users
+
+#include "quakedef.h"
+
+edict_t        *sv_player;
+
+extern cvar_t  sv_friction;
+cvar_t sv_edgefriction = {"edgefriction", "2"};
+extern cvar_t  sv_stopspeed;
+
+static vec3_t          forward, right, up;
+
+vec3_t wishdir;
+float  wishspeed;
+
+// world
+float  *angles;
+float  *origin;
+float  *velocity;
+
+qboolean       onground;
+
+usercmd_t      cmd;
+
+cvar_t sv_idealpitchscale = {"sv_idealpitchscale","0.8"};
+
+
+/*
+===============
+SV_SetIdealPitch
+===============
+*/
+#define        MAX_FORWARD     6
+void SV_SetIdealPitch (void)
+{
+       float   angleval, sinval, cosval;
+       trace_t tr;
+       vec3_t  top, bottom;
+       float   z[MAX_FORWARD];
+       int             i, j;
+       int             step, dir, steps;
+
+       if (!((int)sv_player->v.flags & FL_ONGROUND))
+               return;
+               
+       angleval = sv_player->v.angles[YAW] * M_PI*2 / 360;
+       sinval = sin(angleval);
+       cosval = cos(angleval);
+
+       for (i=0 ; i<MAX_FORWARD ; i++)
+       {
+               top[0] = sv_player->v.origin[0] + cosval*(i+3)*12;
+               top[1] = sv_player->v.origin[1] + sinval*(i+3)*12;
+               top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2];
+               
+               bottom[0] = top[0];
+               bottom[1] = top[1];
+               bottom[2] = top[2] - 160;
+               
+               tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player);
+               if (tr.allsolid)
+                       return; // looking at a wall, leave ideal the way is was
+
+               if (tr.fraction == 1)
+                       return; // near a dropoff
+               
+               z[i] = top[2] + tr.fraction*(bottom[2]-top[2]);
+       }
+       
+       dir = 0;
+       steps = 0;
+       for (j=1 ; j<i ; j++)
+       {
+               step = z[j] - z[j-1];
+               if (step > -ON_EPSILON && step < ON_EPSILON)
+                       continue;
+
+               if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) )
+                       return;         // mixed changes
+
+               steps++;        
+               dir = step;
+       }
+       
+       if (!dir)
+       {
+               sv_player->v.idealpitch = 0;
+               return;
+       }
+       
+       if (steps < 2)
+               return;
+       sv_player->v.idealpitch = -dir * sv_idealpitchscale.value;
+}
+
+
+/*
+==================
+SV_UserFriction
+
+==================
+*/
+void SV_UserFriction (void)
+{
+       float   *vel;
+       float   speed, newspeed, control;
+       vec3_t  start, stop;
+       float   friction;
+       trace_t trace;
+       
+       vel = velocity;
+       
+       speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
+       if (!speed)
+               return;
+
+// if the leading edge is over a dropoff, increase friction
+       start[0] = stop[0] = origin[0] + vel[0]/speed*16;
+       start[1] = stop[1] = origin[1] + vel[1]/speed*16;
+       start[2] = origin[2] + sv_player->v.mins[2];
+       stop[2] = start[2] - 34;
+
+       trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player);
+
+       if (trace.fraction == 1.0)
+               friction = sv_friction.value*sv_edgefriction.value;
+       else
+               friction = sv_friction.value;
+
+// apply friction      
+       control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
+       newspeed = speed - host_frametime*control*friction;
+       
+       if (newspeed < 0)
+               newspeed = 0;
+       else
+               newspeed /= speed;
+
+       vel[0] = vel[0] * newspeed;
+       vel[1] = vel[1] * newspeed;
+       vel[2] = vel[2] * newspeed;
+}
+
+/*
+==============
+SV_Accelerate
+==============
+*/
+cvar_t sv_maxspeed = {"sv_maxspeed", "320", false, true};
+cvar_t sv_accelerate = {"sv_accelerate", "10"};
+#if 0
+void SV_Accelerate (vec3_t wishvel)
+{
+       int                     i;
+       float           addspeed, accelspeed;
+       vec3_t          pushvec;
+
+       if (wishspeed == 0)
+               return;
+
+       VectorSubtract (wishvel, velocity, pushvec);
+       addspeed = VectorNormalize (pushvec);
+
+       accelspeed = sv_accelerate.value*host_frametime*addspeed;
+       if (accelspeed > addspeed)
+               accelspeed = addspeed;
+       
+       for (i=0 ; i<3 ; i++)
+               velocity[i] += accelspeed*pushvec[i];   
+}
+#endif
+void SV_Accelerate (void)
+{
+       int                     i;
+       float           addspeed, accelspeed, currentspeed;
+
+       currentspeed = DotProduct (velocity, wishdir);
+       addspeed = wishspeed - currentspeed;
+       if (addspeed <= 0)
+               return;
+       accelspeed = sv_accelerate.value*host_frametime*wishspeed;
+       if (accelspeed > addspeed)
+               accelspeed = addspeed;
+       
+       for (i=0 ; i<3 ; i++)
+               velocity[i] += accelspeed*wishdir[i];   
+}
+
+void SV_AirAccelerate (vec3_t wishveloc)
+{
+       int                     i;
+       float           addspeed, wishspd, accelspeed, currentspeed;
+               
+       wishspd = VectorNormalizeLength (wishveloc);
+       if (wishspd > 30)
+               wishspd = 30;
+       currentspeed = DotProduct (velocity, wishveloc);
+       addspeed = wishspd - currentspeed;
+       if (addspeed <= 0)
+               return;
+//     accelspeed = sv_accelerate.value * host_frametime;
+       accelspeed = sv_accelerate.value*wishspeed * host_frametime;
+       if (accelspeed > addspeed)
+               accelspeed = addspeed;
+       
+       for (i=0 ; i<3 ; i++)
+               velocity[i] += accelspeed*wishveloc[i]; 
+}
+
+
+void DropPunchAngle (void)
+{
+       float   len;
+       
+       len = VectorNormalizeLength (sv_player->v.punchangle);
+       
+       len -= 10*host_frametime;
+       if (len < 0)
+               len = 0;
+       VectorScale (sv_player->v.punchangle, len, sv_player->v.punchangle);
+}
+
+/*
+===================
+SV_WaterMove
+
+===================
+*/
+void SV_WaterMove (void)
+{
+       int             i;
+       vec3_t  wishvel;
+       float   speed, newspeed, wishspeed, addspeed, accelspeed;
+
+//
+// user intentions
+//
+       AngleVectors (sv_player->v.v_angle, forward, right, up);
+
+       for (i=0 ; i<3 ; i++)
+               wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;
+
+       if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
+               wishvel[2] -= 60;               // drift towards bottom
+       else
+               wishvel[2] += cmd.upmove;
+
+       wishspeed = Length(wishvel);
+       if (wishspeed > sv_maxspeed.value)
+       {
+               VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel);
+               wishspeed = sv_maxspeed.value;
+       }
+       wishspeed *= 0.7;
+
+//
+// water friction
+//
+       speed = Length (velocity);
+       if (speed)
+       {
+               newspeed = speed - host_frametime * speed * sv_friction.value;
+               if (newspeed < 0)
+                       newspeed = 0;   
+               VectorScale (velocity, newspeed/speed, velocity);
+       }
+       else
+               newspeed = 0;
+       
+//
+// water acceleration
+//
+       if (!wishspeed)
+               return;
+
+       addspeed = wishspeed - newspeed;
+       if (addspeed <= 0)
+               return;
+
+       VectorNormalize (wishvel);
+       accelspeed = sv_accelerate.value * wishspeed * host_frametime;
+       if (accelspeed > addspeed)
+               accelspeed = addspeed;
+
+       for (i=0 ; i<3 ; i++)
+               velocity[i] += accelspeed * wishvel[i];
+}
+
+void SV_WaterJump (void)
+{
+       if (sv.time > sv_player->v.teleport_time
+       || !sv_player->v.waterlevel)
+       {
+               sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP;
+               sv_player->v.teleport_time = 0;
+       }
+       sv_player->v.velocity[0] = sv_player->v.movedir[0];
+       sv_player->v.velocity[1] = sv_player->v.movedir[1];
+}
+
+
+/*
+===================
+SV_AirMove
+
+===================
+*/
+void SV_AirMove (void)
+{
+       int                     i;
+       vec3_t          wishvel;
+       float           fmove, smove;
+
+       // LordHavoc: correct quake movement speed bug when looking up/down
+       wishvel[0] = wishvel[2] = 0;
+       wishvel[1] = sv_player->v.angles[1];
+       AngleVectors (wishvel, forward, right, up);
+//     AngleVectors (sv_player->v.angles, forward, right, up);
+
+       fmove = cmd.forwardmove;
+       smove = cmd.sidemove;
+       
+// hack to not let you back into teleporter
+       if (sv.time < sv_player->v.teleport_time && fmove < 0)
+               fmove = 0;
+               
+       for (i=0 ; i<3 ; i++)
+               wishvel[i] = forward[i]*fmove + right[i]*smove;
+
+       if ( (int)sv_player->v.movetype != MOVETYPE_WALK)
+               wishvel[2] = cmd.upmove;
+       else
+               wishvel[2] = 0;
+
+       VectorCopy (wishvel, wishdir);
+       wishspeed = VectorNormalizeLength(wishdir);
+       if (wishspeed > sv_maxspeed.value)
+       {
+               VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel);
+               wishspeed = sv_maxspeed.value;
+       }
+       
+       if ( sv_player->v.movetype == MOVETYPE_NOCLIP)
+       {       // noclip
+               VectorCopy (wishvel, velocity);
+       }
+       else if ( onground )
+       {
+               SV_UserFriction ();
+               SV_Accelerate ();
+       }
+       else
+       {       // not on ground, so little effect on velocity
+               SV_AirAccelerate (wishvel);
+       }               
+}
+
+/*
+===================
+SV_ClientThink
+
+the move fields specify an intended velocity in pix/sec
+the angle fields specify an exact angular motion in degrees
+===================
+*/
+void SV_ClientThink (void)
+{
+       vec3_t          v_angle;
+
+       if (sv_player->v.movetype == MOVETYPE_NONE)
+               return;
+       
+       onground = (int)sv_player->v.flags & FL_ONGROUND;
+
+       origin = sv_player->v.origin;
+       velocity = sv_player->v.velocity;
+
+       DropPunchAngle ();
+       
+//
+// if dead, behave differently
+//
+       if (sv_player->v.health <= 0)
+               return;
+
+//
+// angles
+// show 1/3 the pitch angle and all the roll angle
+       cmd = host_client->cmd;
+       angles = sv_player->v.angles;
+       
+       VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle);
+       angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4;
+       if (!sv_player->v.fixangle)
+       {
+               // LordHavoc: pitch was ugly to begin with...  removed except in water
+               if (sv_player->v.waterlevel >= 2)
+                       angles[PITCH] = -v_angle[PITCH]/3;
+               else
+                       angles[PITCH] = 0;
+               angles[YAW] = v_angle[YAW];
+       }
+
+       if ( (int)sv_player->v.flags & FL_WATERJUMP )
+       {
+               SV_WaterJump ();
+               return;
+       }
+//
+// walk
+//
+       if ( (sv_player->v.waterlevel >= 2)
+       && (sv_player->v.movetype != MOVETYPE_NOCLIP) )
+       {
+               SV_WaterMove ();
+               return;
+       }
+
+       SV_AirMove ();  
+}
+
+
+/*
+===================
+SV_ReadClientMove
+===================
+*/
+void SV_ReadClientMove (usercmd_t *move)
+{
+       int             i;
+       vec3_t  angle;
+       int             bits;
+       eval_t  *val;
+       float   total;
+       
+// read ping time
+       host_client->ping_times[host_client->num_pings%NUM_PING_TIMES]
+               = sv.time - MSG_ReadFloat ();
+       host_client->num_pings++;
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_ping))
+       {
+               for (i=0, total = 0;i < NUM_PING_TIMES;i++)
+                       total += host_client->ping_times[i];
+               val->_float = 1000.0 / NUM_PING_TIMES;
+       }
+
+// read current angles 
+       for (i=0 ; i<3 ; i++)
+               angle[i] = MSG_ReadAngle ();
+
+       VectorCopy (angle, host_client->edict->v.v_angle);
+               
+// read movement
+       move->forwardmove = MSG_ReadShort ();
+       move->sidemove = MSG_ReadShort ();
+       move->upmove = MSG_ReadShort ();
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_movement))
+       {
+               val->vector[0] = move->forwardmove;
+               val->vector[1] = move->sidemove;
+               val->vector[2] = move->upmove;
+       }
+       
+// read buttons
+       bits = MSG_ReadByte ();
+       host_client->edict->v.button0 = bits & 1;
+       host_client->edict->v.button2 = (bits & 2)>>1;
+       // LordHavoc: added 6 new buttons
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_button3)) val->_float = ((bits >> 2) & 1);
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_button4)) val->_float = ((bits >> 3) & 1);
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_button5)) val->_float = ((bits >> 4) & 1);
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_button6)) val->_float = ((bits >> 5) & 1);
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_button7)) val->_float = ((bits >> 6) & 1);
+       if (val = GETEDICTFIELDVALUE(host_client->edict, eval_button8)) val->_float = ((bits >> 7) & 1);
+
+       i = MSG_ReadByte ();
+       if (i)
+               host_client->edict->v.impulse = i;
+}
+
+/*
+===================
+SV_ReadClientMessage
+
+Returns false if the client should be killed
+===================
+*/
+qboolean SV_ReadClientMessage (void)
+{
+       int             ret;
+       int             cmd;
+       char            *s;
+       
+       do
+       {
+nextmsg:
+               ret = NET_GetMessage (host_client->netconnection);
+               if (ret == -1)
+               {
+                       Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n");
+                       return false;
+               }
+               if (!ret)
+                       return true;
+                                       
+               MSG_BeginReading ();
+               
+               while (1)
+               {
+                       if (!host_client->active)
+                               return false;   // a command caused an error
+
+                       if (msg_badread)
+                       {
+                               Sys_Printf ("SV_ReadClientMessage: badread\n");
+                               return false;
+                       }       
+       
+                       cmd = MSG_ReadChar ();
+                       
+                       switch (cmd)
+                       {
+                       case -1:
+                               goto nextmsg;           // end of message
+                               
+                       default:
+                               Sys_Printf ("SV_ReadClientMessage: unknown command char\n");
+                               return false;
+                                                       
+                       case clc_nop:
+//                             Sys_Printf ("clc_nop\n");
+                               break;
+                               
+                       case clc_stringcmd:     
+                               s = MSG_ReadString ();
+                               if (host_client->privileged)
+                                       ret = 2;
+                               else
+                                       ret = 0;
+                               if (Q_strncasecmp(s, "status", 6) == 0
+                                || Q_strncasecmp(s, "name", 4) == 0
+                                || Q_strncasecmp(s, "say", 3) == 0
+                                || Q_strncasecmp(s, "say_team", 8) == 0
+                                || Q_strncasecmp(s, "tell", 4) == 0
+                                || Q_strncasecmp(s, "color", 5) == 0
+                                || Q_strncasecmp(s, "kill", 4) == 0
+                                || Q_strncasecmp(s, "pause", 5) == 0
+                                || Q_strncasecmp(s, "spawn", 5) == 0
+                                || Q_strncasecmp(s, "begin", 5) == 0
+                                || Q_strncasecmp(s, "prespawn", 8) == 0
+                                || Q_strncasecmp(s, "kick", 4) == 0
+                                || Q_strncasecmp(s, "ping", 4) == 0
+                                || Q_strncasecmp(s, "ban", 3) == 0
+                                || (nehahra && (Q_strncasecmp(s, "max", 3) == 0 || Q_strncasecmp(s, "monster", 7) == 0 || Q_strncasecmp(s, "scrag", 5) == 0 || Q_strncasecmp(s, "gimme", 5) == 0 || Q_strncasecmp(s, "wraith", 6) == 0))
+                                || (!nehahra && (Q_strncasecmp(s, "god", 3) == 0 || Q_strncasecmp(s, "notarget", 8) == 0 || Q_strncasecmp(s, "fly", 3) == 0 || Q_strncasecmp(s, "give", 4) == 0 || Q_strncasecmp(s, "noclip", 6) == 0)))
+                                       ret = 1;
+                               if (ret == 2)
+                                       Cbuf_InsertText (s);
+                               else if (ret == 1)
+                                       Cmd_ExecuteString (s, src_client);
+                               else
+                                       Con_DPrintf("%s tried to %s\n", host_client->name, s);
+                               break;
+                               
+                       case clc_disconnect:
+//                             Sys_Printf ("SV_ReadClientMessage: client disconnected\n");
+                               return false;
+                       
+                       case clc_move:
+                               SV_ReadClientMove (&host_client->cmd);
+                               break;
+                       }
+               }
+       } while (ret == 1);
+       
+       return true;
+}
+
+
+/*
+==================
+SV_RunClients
+==================
+*/
+extern dfunction_t *SV_PlayerPhysicsQC;
+void SV_RunClients (void)
+{
+       int                             i;
+
+       for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+       {
+               if (!host_client->active)
+                       continue;
+       
+               sv_player = host_client->edict;
+
+               if (!SV_ReadClientMessage ())
+               {
+                       SV_DropClient (false);  // client misbehaved...
+                       continue;
+               }
+
+               if (!host_client->spawned)
+               {
+               // clear client movement until a new packet is received
+                       memset (&host_client->cmd, 0, sizeof(host_client->cmd));
+                       continue;
+               }
+
+// always pause in single player if in console or menus
+               if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+               {
+                       // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
+                       if (SV_PlayerPhysicsQC)
+                       {
+                               pr_global_struct->time = sv.time;
+                               pr_global_struct->self = EDICT_TO_PROG(sv_player);
+                               PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions));
+                       }
+                       else
+                               SV_ClientThink ();
+               }
+       }
+}
+
diff --git a/sys.h b/sys.h
new file mode 100644 (file)
index 0000000..c5d61a7
--- /dev/null
+++ b/sys.h
@@ -0,0 +1,71 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sys.h -- non-portable functions
+
+//
+// file IO
+//
+
+// returns the file size
+// return -1 if file is not present
+// the file should be in BINARY mode for stupid OSs that care
+int Sys_FileOpenRead (char *path, int *hndl);
+
+int Sys_FileOpenWrite (char *path);
+void Sys_FileClose (int handle);
+void Sys_FileSeek (int handle, int position);
+int Sys_FileRead (int handle, void *dest, int count);
+int Sys_FileWrite (int handle, void *data, int count);
+int    Sys_FileTime (char *path);
+void Sys_mkdir (char *path);
+
+//
+// memory protection
+//
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length);
+
+//
+// system IO
+//
+void Sys_DebugLog(char *file, char *fmt, ...);
+
+void Sys_Error (char *error, ...);
+// an error will cause the entire program to exit
+
+void Sys_Printf (char *fmt, ...);
+// send text to the console
+
+void Sys_Quit (void);
+
+double Sys_FloatTime (void);
+
+char *Sys_ConsoleInput (void);
+
+void Sys_Sleep (void);
+// called to yield for a little bit so as
+// not to hog cpu when paused or debugging
+
+void Sys_SendKeyEvents (void);
+// Perform Key_Event () callbacks until the input que is empty
+
+void Sys_LowFPPrecision (void);
+void Sys_HighFPPrecision (void);
+void Sys_SetFPCW (void);
+
diff --git a/sys_win.c b/sys_win.c
new file mode 100644 (file)
index 0000000..7a9d682
--- /dev/null
+++ b/sys_win.c
@@ -0,0 +1,929 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sys_win.c -- Win32 system interface code
+
+#include "quakedef.h"
+#include "winquake.h"
+#include "errno.h"
+#include "resource.h"
+#include "conproc.h"
+#include "direct.h"
+
+// LordHavoc: raised min to 24mb (was 8.5mb)
+#define MINIMUM_WIN_MEMORY             0x1800000
+// LordHavoc: raised max to 24mb (was 16mb)
+#define MAXIMUM_WIN_MEMORY             0x1800000
+
+#define CONSOLE_ERROR_TIMEOUT  60.0    // # of seconds to wait on Sys_Error running
+                                                                               //  dedicated before exiting
+#define PAUSE_SLEEP            50                              // sleep time on pause or minimization
+#define NOT_FOCUS_SLEEP        20                              // sleep time when not focus
+
+int                    starttime;
+qboolean       ActiveApp, Minimized;
+qboolean       WinNT;
+
+static double          pfreq;
+static double          curtime = 0.0;
+static double          lastcurtime = 0.0;
+static int                     lowshift;
+qboolean                       isDedicated;
+static qboolean                sc_return_on_enter = false;
+HANDLE                         hinput, houtput;
+
+static char                    *tracking_tag = "Clams & Mooses";
+
+static HANDLE  tevent;
+static HANDLE  hFile;
+static HANDLE  heventParent;
+static HANDLE  heventChild;
+
+void MaskExceptions (void);
+void Sys_InitFloatTime (void);
+void Sys_PushFPCW_SetHigh (void);
+void Sys_PopFPCW (void);
+
+volatile int                                   sys_checksum;
+
+
+/*
+================
+Sys_PageIn
+================
+*/
+void Sys_PageIn (void *ptr, int size)
+{
+       byte    *x;
+       int             m, n;
+
+// touch all the memory to make sure it's there. The 16-page skip is to
+// keep Win 95 from thinking we're trying to page ourselves in (we are
+// doing that, of course, but there's no reason we shouldn't)
+       x = (byte *)ptr;
+
+       for (n=0 ; n<4 ; n++)
+       {
+               for (m=0 ; m<(size - 16 * 0x1000) ; m += 4)
+               {
+                       sys_checksum += *(int *)&x[m];
+                       sys_checksum += *(int *)&x[m + 16 * 0x1000];
+               }
+       }
+}
+
+
+/*
+===============================================================================
+
+FILE IO
+
+===============================================================================
+*/
+
+// LordHavoc: 256 pak files (was 10)
+#define        MAX_HANDLES             256
+FILE   *sys_handles[MAX_HANDLES];
+
+int            findhandle (void)
+{
+       int             i;
+       
+       for (i=1 ; i<MAX_HANDLES ; i++)
+               if (!sys_handles[i])
+                       return i;
+       Sys_Error ("out of handles");
+       return -1;
+}
+
+/*
+================
+filelength
+================
+*/
+int filelength (FILE *f)
+{
+       int             pos;
+       int             end;
+       int             t;
+
+       t = VID_ForceUnlockedAndReturnState ();
+
+       pos = ftell (f);
+       fseek (f, 0, SEEK_END);
+       end = ftell (f);
+       fseek (f, pos, SEEK_SET);
+
+       VID_ForceLockState (t);
+
+       return end;
+}
+
+int Sys_FileOpenRead (char *path, int *hndl)
+{
+       FILE    *f;
+       int             i, retval;
+       int             t;
+
+       t = VID_ForceUnlockedAndReturnState ();
+
+       i = findhandle ();
+
+       f = fopen(path, "rb");
+
+       if (!f)
+       {
+               *hndl = -1;
+               retval = -1;
+       }
+       else
+       {
+               sys_handles[i] = f;
+               *hndl = i;
+               retval = filelength(f);
+       }
+
+       VID_ForceLockState (t);
+
+       return retval;
+}
+
+int Sys_FileOpenWrite (char *path)
+{
+       FILE    *f;
+       int             i;
+       int             t;
+
+       t = VID_ForceUnlockedAndReturnState ();
+       
+       i = findhandle ();
+
+       f = fopen(path, "wb");
+       if (!f)
+               Sys_Error ("Error opening %s: %s", path,strerror(errno));
+       sys_handles[i] = f;
+       
+       VID_ForceLockState (t);
+
+       return i;
+}
+
+void Sys_FileClose (int handle)
+{
+       int             t;
+
+       t = VID_ForceUnlockedAndReturnState ();
+       fclose (sys_handles[handle]);
+       sys_handles[handle] = NULL;
+       VID_ForceLockState (t);
+}
+
+void Sys_FileSeek (int handle, int position)
+{
+       int             t;
+
+       t = VID_ForceUnlockedAndReturnState ();
+       fseek (sys_handles[handle], position, SEEK_SET);
+       VID_ForceLockState (t);
+}
+
+int Sys_FileRead (int handle, void *dest, int count)
+{
+       int             t, x;
+
+       t = VID_ForceUnlockedAndReturnState ();
+       x = fread (dest, 1, count, sys_handles[handle]);
+       VID_ForceLockState (t);
+       return x;
+}
+
+int Sys_FileWrite (int handle, void *data, int count)
+{
+       int             t, x;
+
+       t = VID_ForceUnlockedAndReturnState ();
+       x = fwrite (data, 1, count, sys_handles[handle]);
+       VID_ForceLockState (t);
+       return x;
+}
+
+int    Sys_FileTime (char *path)
+{
+       FILE    *f;
+       int             t, retval;
+
+       t = VID_ForceUnlockedAndReturnState ();
+       
+       f = fopen(path, "rb");
+
+       if (f)
+       {
+               fclose(f);
+               retval = 1;
+       }
+       else
+       {
+               retval = -1;
+       }
+       
+       VID_ForceLockState (t);
+       return retval;
+}
+
+void Sys_mkdir (char *path)
+{
+       _mkdir (path);
+}
+
+
+/*
+===============================================================================
+
+SYSTEM IO
+
+===============================================================================
+*/
+
+/*
+================
+Sys_MakeCodeWriteable
+================
+*/
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+       DWORD  flOldProtect;
+
+       if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect))
+               Sys_Error("Protection change failed\n");
+}
+
+
+//#ifndef _M_IX86
+
+void Sys_SetFPCW (void)
+{
+}
+
+void Sys_PushFPCW_SetHigh (void)
+{
+}
+
+void Sys_PopFPCW (void)
+{
+}
+
+void MaskExceptions (void)
+{
+}
+
+//#endif
+
+/*
+================
+Sys_Init
+================
+*/
+void Sys_Init (void)
+{
+       LARGE_INTEGER   PerformanceFreq;
+       unsigned int    lowpart, highpart;
+       OSVERSIONINFO   vinfo;
+
+       MaskExceptions ();
+       Sys_SetFPCW ();
+
+       if (!QueryPerformanceFrequency (&PerformanceFreq))
+               Sys_Error ("No hardware timer available");
+
+// get 32 out of the 64 time bits such that we have around
+// 1 microsecond resolution
+       lowpart = (unsigned int)PerformanceFreq.LowPart;
+       highpart = (unsigned int)PerformanceFreq.HighPart;
+       lowshift = 0;
+
+       while (highpart || (lowpart > 2000000.0))
+       {
+               lowshift++;
+               lowpart >>= 1;
+               lowpart |= (highpart & 1) << 31;
+               highpart >>= 1;
+       }
+
+       pfreq = 1.0 / (double)lowpart;
+
+       Sys_InitFloatTime ();
+
+       vinfo.dwOSVersionInfoSize = sizeof(vinfo);
+
+       if (!GetVersionEx (&vinfo))
+               Sys_Error ("Couldn't get OS info");
+
+       if ((vinfo.dwMajorVersion < 4) ||
+               (vinfo.dwPlatformId == VER_PLATFORM_WIN32s))
+       {
+               Sys_Error ("WinQuake requires at least Win95 or NT 4.0");
+       }
+
+       if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
+               WinNT = true;
+       else
+               WinNT = false;
+}
+
+
+void Sys_Error (char *error, ...)
+{
+       va_list         argptr;
+       char            text[1024], text2[1024];
+       char            *text3 = "Press Enter to exit\n";
+       char            *text4 = "***********************************\n";
+       char            *text5 = "\n";
+       DWORD           dummy;
+       double          starttime;
+       static int      in_sys_error0 = 0;
+       static int      in_sys_error1 = 0;
+       static int      in_sys_error2 = 0;
+       static int      in_sys_error3 = 0;
+
+       if (!in_sys_error3)
+       {
+               in_sys_error3 = 1;
+               VID_ForceUnlockedAndReturnState ();
+       }
+
+       va_start (argptr, error);
+       vsprintf (text, error, argptr);
+       va_end (argptr);
+
+       if (isDedicated)
+       {
+               va_start (argptr, error);
+               vsprintf (text, error, argptr);
+               va_end (argptr);
+
+               sprintf (text2, "ERROR: %s\n", text);
+               WriteFile (houtput, text5, strlen (text5), &dummy, NULL);
+               WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
+               WriteFile (houtput, text2, strlen (text2), &dummy, NULL);
+               WriteFile (houtput, text3, strlen (text3), &dummy, NULL);
+               WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
+
+
+               starttime = Sys_FloatTime ();
+               sc_return_on_enter = true;      // so Enter will get us out of here
+
+               while (!Sys_ConsoleInput () &&
+                               ((Sys_FloatTime () - starttime) < CONSOLE_ERROR_TIMEOUT))
+               {
+               }
+       }
+       else
+       {
+       // switch to windowed so the message box is visible, unless we already
+       // tried that and failed
+               if (!in_sys_error0)
+               {
+                       in_sys_error0 = 1;
+                       VID_SetDefaultMode ();
+                       MessageBox(NULL, text, "Quake Error",
+                                          MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
+               }
+               else
+               {
+                       MessageBox(NULL, text, "Double Quake Error",
+                                          MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
+               }
+       }
+
+       if (!in_sys_error1)
+       {
+               in_sys_error1 = 1;
+               Host_Shutdown ();
+       }
+
+// shut down QHOST hooks if necessary
+       if (!in_sys_error2)
+       {
+               in_sys_error2 = 1;
+               DeinitConProc ();
+       }
+
+       exit (1);
+}
+
+void Sys_Printf (char *fmt, ...)
+{
+       va_list         argptr;
+       char            text[1024];
+       DWORD           dummy;
+       
+       if (isDedicated)
+       {
+               va_start (argptr,fmt);
+               vsprintf (text, fmt, argptr);
+               va_end (argptr);
+
+               WriteFile(houtput, text, strlen (text), &dummy, NULL);  
+       }
+}
+
+void Sys_Quit (void)
+{
+
+       VID_ForceUnlockedAndReturnState ();
+
+       Host_Shutdown();
+
+       if (tevent)
+               CloseHandle (tevent);
+
+       if (isDedicated)
+               FreeConsole ();
+
+// shut down QHOST hooks if necessary
+       DeinitConProc ();
+
+       exit (0);
+}
+
+
+/*
+================
+Sys_FloatTime
+================
+*/
+double Sys_FloatTime (void)
+{
+       static int                      sametimecount;
+       static unsigned int     oldtime;
+       static int                      first = 1;
+       LARGE_INTEGER           PerformanceCount;
+       unsigned int            temp, t2;
+       double                          time;
+
+       Sys_PushFPCW_SetHigh ();
+
+       QueryPerformanceCounter (&PerformanceCount);
+
+       temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) |
+                  ((unsigned int)PerformanceCount.HighPart << (32 - lowshift));
+
+       if (first)
+       {
+               oldtime = temp;
+               first = 0;
+       }
+       else
+       {
+       // check for turnover or backward time
+               if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000))
+               {
+                       oldtime = temp; // so we can't get stuck
+               }
+               else
+               {
+                       t2 = temp - oldtime;
+
+                       time = (double)t2 * pfreq;
+                       oldtime = temp;
+
+                       curtime += time;
+
+                       if (curtime == lastcurtime)
+                       {
+                               sametimecount++;
+
+                               if (sametimecount > 100000)
+                               {
+                                       curtime += 1.0;
+                                       sametimecount = 0;
+                               }
+                       }
+                       else
+                       {
+                               sametimecount = 0;
+                       }
+
+                       lastcurtime = curtime;
+               }
+       }
+
+       Sys_PopFPCW ();
+
+    return curtime;
+}
+
+
+/*
+================
+Sys_InitFloatTime
+================
+*/
+void Sys_InitFloatTime (void)
+{
+       int             j;
+
+       Sys_FloatTime ();
+
+       j = COM_CheckParm("-starttime");
+
+       if (j)
+       {
+               curtime = (double) (atof(com_argv[j+1]));
+       }
+       else
+       {
+               curtime = 0.0;
+       }
+
+       lastcurtime = curtime;
+}
+
+
+char *Sys_ConsoleInput (void)
+{
+       static char     text[256];
+       static int              len;
+       INPUT_RECORD    recs[1024];
+       int             dummy;
+       int             ch, numread, numevents;
+
+       if (!isDedicated)
+               return NULL;
+
+
+       for ( ;; )
+       {
+               if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
+                       Sys_Error ("Error getting # of console events");
+
+               if (numevents <= 0)
+                       break;
+
+               if (!ReadConsoleInput(hinput, recs, 1, &numread))
+                       Sys_Error ("Error reading console input");
+
+               if (numread != 1)
+                       Sys_Error ("Couldn't read console input");
+
+               if (recs[0].EventType == KEY_EVENT)
+               {
+                       if (!recs[0].Event.KeyEvent.bKeyDown)
+                       {
+                               ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
+
+                               switch (ch)
+                               {
+                                       case '\r':
+                                               WriteFile(houtput, "\r\n", 2, &dummy, NULL);    
+
+                                               if (len)
+                                               {
+                                                       text[len] = 0;
+                                                       len = 0;
+                                                       return text;
+                                               }
+                                               else if (sc_return_on_enter)
+                                               {
+                                               // special case to allow exiting from the error handler on Enter
+                                                       text[0] = '\r';
+                                                       len = 0;
+                                                       return text;
+                                               }
+
+                                               break;
+
+                                       case '\b':
+                                               WriteFile(houtput, "\b \b", 3, &dummy, NULL);   
+                                               if (len)
+                                               {
+                                                       len--;
+                                               }
+                                               break;
+
+                                       default:
+                                               if (ch >= ' ')
+                                               {
+                                                       WriteFile(houtput, &ch, 1, &dummy, NULL);       
+                                                       text[len] = ch;
+                                                       len = (len + 1) & 0xff;
+                                               }
+
+                                               break;
+
+                               }
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+void Sys_Sleep (void)
+{
+       Sleep (1);
+}
+
+
+void Sys_SendKeyEvents (void)
+{
+    MSG        msg;
+
+       while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
+       {
+       // we always update if there are any event, even if we're paused
+               scr_skipupdate = 0;
+
+               if (!GetMessage (&msg, NULL, 0, 0))
+                       Sys_Quit ();
+
+       TranslateMessage (&msg);
+       DispatchMessage (&msg);
+       }
+}
+
+
+/*
+==============================================================================
+
+ WINDOWS CRAP
+
+==============================================================================
+*/
+
+
+/*
+==================
+WinMain
+==================
+*/
+void SleepUntilInput (int time)
+{
+
+       MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
+}
+
+
+extern cvar_t maxfps;
+
+/*
+==================
+WinMain
+==================
+*/
+HINSTANCE      global_hInstance;
+int                    global_nCmdShow;
+char           *argv[MAX_NUM_ARGVS];
+static char    *empty_string = "";
+HWND           hwnd_dialog;
+
+
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+       quakeparms_t    parms;
+       double                  time, oldtime, newtime, timediff;
+       MEMORYSTATUS    lpBuffer;
+       static  char    cwd[1024];
+       int                             t;
+       RECT                    rect;
+
+    /* previous instances do not exist in Win32 */
+    if (hPrevInstance)
+        return 0;
+
+       global_hInstance = hInstance;
+       global_nCmdShow = nCmdShow;
+
+       lpBuffer.dwLength = sizeof(MEMORYSTATUS);
+       GlobalMemoryStatus (&lpBuffer);
+
+       if (!GetCurrentDirectory (sizeof(cwd), cwd))
+               Sys_Error ("Couldn't determine current directory");
+
+       if (cwd[strlen(cwd)-1] == '/')
+               cwd[strlen(cwd)-1] = 0;
+
+       parms.basedir = cwd;
+       parms.cachedir = NULL;
+
+       parms.argc = 1;
+       argv[0] = empty_string;
+
+       while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS))
+       {
+               while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+                       lpCmdLine++;
+
+               if (*lpCmdLine)
+               {
+                       argv[parms.argc] = lpCmdLine;
+                       parms.argc++;
+
+                       while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+                               lpCmdLine++;
+
+                       if (*lpCmdLine)
+                       {
+                               *lpCmdLine = 0;
+                               lpCmdLine++;
+                       }
+                       
+               }
+       }
+
+       parms.argv = argv;
+
+       COM_InitArgv (parms.argc, parms.argv);
+
+       parms.argc = com_argc;
+       parms.argv = com_argv;
+
+       isDedicated = (COM_CheckParm ("-dedicated") != 0);
+
+       if (!isDedicated)
+       {
+               hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL);
+
+               if (hwnd_dialog)
+               {
+                       if (GetWindowRect (hwnd_dialog, &rect))
+                       {
+                               if (rect.left > (rect.top * 2))
+                               {
+                                       SetWindowPos (hwnd_dialog, 0,
+                                               (rect.left / 2) - ((rect.right - rect.left) / 2),
+                                               rect.top, 0, 0,
+                                               SWP_NOZORDER | SWP_NOSIZE);
+                               }
+                       }
+
+                       ShowWindow (hwnd_dialog, SW_SHOWDEFAULT);
+                       UpdateWindow (hwnd_dialog);
+                       SetForegroundWindow (hwnd_dialog);
+               }
+       }
+
+// take the greater of all the available memory or half the total memory,
+// but at least 8 Mb and no more than 16 Mb, unless they explicitly
+// request otherwise
+       parms.memsize = lpBuffer.dwAvailPhys;
+
+       if (parms.memsize < MINIMUM_WIN_MEMORY)
+               parms.memsize = MINIMUM_WIN_MEMORY;
+
+       if (parms.memsize < (lpBuffer.dwTotalPhys >> 1))
+               parms.memsize = lpBuffer.dwTotalPhys >> 1;
+
+       if (parms.memsize > MAXIMUM_WIN_MEMORY)
+               parms.memsize = MAXIMUM_WIN_MEMORY;
+
+       if (COM_CheckParm ("-heapsize"))
+       {
+               t = COM_CheckParm("-heapsize") + 1;
+
+               if (t < com_argc)
+                       parms.memsize = atoi (com_argv[t]) * 1024;
+       }
+
+       if (COM_CheckParm ("-mem"))
+       {
+               t = COM_CheckParm("-mem") + 1;
+
+               if (t < com_argc)
+                       parms.memsize = atoi (com_argv[t]) * 1048576;
+       }
+
+       if (COM_CheckParm ("-winmem"))
+       {
+               t = COM_CheckParm("-winmem") + 1;
+
+               if (t < com_argc)
+                       parms.memsize = atoi (com_argv[t]) * 1048576;
+       }
+
+       parms.membase = malloc (parms.memsize);
+
+       if (!parms.membase)
+               Sys_Error ("Not enough memory free; check disk space\n");
+
+       Sys_PageIn (parms.membase, parms.memsize);
+
+       tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+       if (!tevent)
+               Sys_Error ("Couldn't create event");
+
+       if (isDedicated)
+       {
+               if (!AllocConsole ())
+               {
+                       Sys_Error ("Couldn't create dedicated server console");
+               }
+
+               hinput = GetStdHandle (STD_INPUT_HANDLE);
+               houtput = GetStdHandle (STD_OUTPUT_HANDLE);
+
+       // give QHOST a chance to hook into the console
+               if ((t = COM_CheckParm ("-HFILE")) > 0)
+               {
+                       if (t < com_argc)
+                               hFile = (HANDLE)atoi (com_argv[t+1]);
+               }
+                       
+               if ((t = COM_CheckParm ("-HPARENT")) > 0)
+               {
+                       if (t < com_argc)
+                               heventParent = (HANDLE)atoi (com_argv[t+1]);
+               }
+                       
+               if ((t = COM_CheckParm ("-HCHILD")) > 0)
+               {
+                       if (t < com_argc)
+                               heventChild = (HANDLE)atoi (com_argv[t+1]);
+               }
+
+               InitConProc (hFile, heventParent, heventChild);
+       }
+
+       Sys_Init ();
+
+// because sound is off until we become active
+       S_BlockSound ();
+
+       Sys_Printf ("Host_Init\n");
+       Host_Init (&parms);
+
+       oldtime = Sys_FloatTime ();
+
+    /* main window message loop */
+       while (1)
+       {
+               if (maxfps.value < 5) // LordHavoc: sanity checking
+                       maxfps.value = 5;
+               if (maxfps.value > 1000) // LordHavoc: sanity checking
+                       maxfps.value = 1000;
+               if (isDedicated)
+               {
+                       newtime = Sys_FloatTime ();
+                       time = newtime - oldtime;
+
+                       while (time < sys_ticrate.value )
+                       {
+                               Sys_Sleep();
+                               newtime = Sys_FloatTime ();
+                               time = newtime - oldtime;
+                       }
+               }
+               else
+               {
+               // yield the CPU for a little while when paused, minimized, or not the focus
+                       if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized)
+                       {
+                               SleepUntilInput (PAUSE_SLEEP);
+                               scr_skipupdate = 1;             // no point in bothering to draw
+                       }
+                       else if (!ActiveApp && !DDActive)
+                       {
+                               SleepUntilInput (NOT_FOCUS_SLEEP);
+                       }
+                       else if (!cls.timedemo && time < (timediff = 1.0 / maxfps.value))
+                       {
+                               newtime = Sys_FloatTime ();
+                               time = newtime - oldtime;
+
+                               while (time < timediff)
+                               {
+                                       Sys_Sleep();
+                                       newtime = Sys_FloatTime ();
+                                       time = newtime - oldtime;
+                               }
+                       }
+
+                       newtime = Sys_FloatTime ();
+                       time = newtime - oldtime;
+               }
+
+               Host_Frame (time);
+               oldtime = newtime;
+       }
+
+    /* return success of application */
+    return TRUE;
+}
+
diff --git a/sys_wind.c b/sys_wind.c
new file mode 100644 (file)
index 0000000..5e17373
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sys_null.h -- null system driver to aid porting efforts
+
+#include "quakedef.h"
+#include "winquake.h"
+#include "errno.h"
+#include <sys\types.h>
+#include <sys\timeb.h>
+
+
+/*
+===============================================================================
+
+FILE IO
+
+===============================================================================
+*/
+
+// LordHavoc: 256 pak files (was 10)
+#define MAX_HANDLES             256
+FILE   *sys_handles[MAX_HANDLES];
+
+int            findhandle (void)
+{
+       int             i;
+       
+       for (i=1 ; i<MAX_HANDLES ; i++)
+               if (!sys_handles[i])
+                       return i;
+       Sys_Error ("out of handles");
+       return -1;
+}
+
+/*
+================
+filelength
+================
+*/
+int filelength (FILE *f)
+{
+       int             pos;
+       int             end;
+
+       pos = ftell (f);
+       fseek (f, 0, SEEK_END);
+       end = ftell (f);
+       fseek (f, pos, SEEK_SET);
+
+       return end;
+}
+
+int Sys_FileOpenRead (char *path, int *hndl)
+{
+       FILE    *f;
+       int             i;
+       
+       i = findhandle ();
+
+       f = fopen(path, "rb");
+       if (!f)
+       {
+               *hndl = -1;
+               return -1;
+       }
+       sys_handles[i] = f;
+       *hndl = i;
+       
+       return filelength(f);
+}
+
+int Sys_FileOpenWrite (char *path)
+{
+       FILE    *f;
+       int             i;
+       
+       i = findhandle ();
+
+       f = fopen(path, "wb");
+       if (!f)
+               Sys_Error ("Error opening %s: %s", path,strerror(errno));
+       sys_handles[i] = f;
+       
+       return i;
+}
+
+void Sys_FileClose (int handle)
+{
+       fclose (sys_handles[handle]);
+       sys_handles[handle] = NULL;
+}
+
+void Sys_FileSeek (int handle, int position)
+{
+       fseek (sys_handles[handle], position, SEEK_SET);
+}
+
+int Sys_FileRead (int handle, void *dest, int count)
+{
+       return fread (dest, 1, count, sys_handles[handle]);
+}
+
+int Sys_FileWrite (int handle, void *data, int count)
+{
+       return fwrite (data, 1, count, sys_handles[handle]);
+}
+
+int    Sys_FileTime (char *path)
+{
+       FILE    *f;
+       
+       f = fopen(path, "rb");
+       if (f)
+       {
+               fclose(f);
+               return 1;
+       }
+       
+       return -1;
+}
+
+void Sys_mkdir (char *path)
+{
+}
+
+
+/*
+===============================================================================
+
+SYSTEM IO
+
+===============================================================================
+*/
+
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+}
+
+
+void Sys_DebugLog(char *file, char *fmt, ...)
+{
+}
+
+void Sys_Error (char *error, ...)
+{
+       va_list         argptr;
+       char            text[1024];
+
+       va_start (argptr,error);
+       vsprintf (text, error,argptr);
+       va_end (argptr);
+
+//    MessageBox(NULL, text, "Error", 0 /* MB_OK */ );
+       printf ("ERROR: %s\n", text);
+
+       exit (1);
+}
+
+void Sys_Printf (char *fmt, ...)
+{
+       va_list         argptr;
+       
+       va_start (argptr,fmt);
+       vprintf (fmt,argptr);
+       va_end (argptr);
+}
+
+void Sys_Quit (void)
+{
+       exit (0);
+}
+
+double Sys_FloatTime (void)
+{
+       double t;
+    struct _timeb tstruct;
+       static int      starttime;
+
+       _ftime( &tstruct );
+       if (!starttime)
+               starttime = tstruct.time;
+       t = (tstruct.time-starttime) + tstruct.millitm*0.001;
+       
+       return t;
+}
+
+void Sys_Sleep (void)
+{
+}
+
+
+void Sys_SendKeyEvents (void)
+{
+}
+
+void Sys_HighFPPrecision (void)
+{
+}
+
+void Sys_LowFPPrecision (void)
+{
+}
+
+char *Sys_ConsoleInput (void)
+{
+       static char     text[256];
+       static int              len;
+       INPUT_RECORD    recs[1024];
+       int             count;
+       int             i;
+       int             c;
+
+       // read a line out
+       while (_kbhit())
+       {
+               c = _getch();
+               putch (c);
+               if (c == '\r')
+               {
+                       text[len] = 0;
+                       putch ('\n');
+                       len = 0;
+                       return text;
+               }
+               if (c == 8)
+               {
+                       putch (' ');
+                       putch (c);
+                       len--;
+                       text[len] = 0;
+                       continue;
+               }
+               text[len] = c;
+               len++;
+               text[len] = 0;
+               if (len == sizeof(text))
+                       len = 0;
+       }
+
+       return NULL;
+}
+
+
+
+/*
+==================
+main
+
+==================
+*/
+char   *newargv[256];
+
+int main (int argc, char **argv)
+{
+    MSG        msg;
+       quakeparms_t    parms;
+       double                  time, oldtime;
+       static  char    cwd[1024];
+
+       memset (&parms, 0, sizeof(parms));
+
+       parms.memsize = 16384*1024;
+       parms.membase = malloc (parms.memsize);
+
+       _getcwd (cwd, sizeof(cwd));
+       if (cwd[strlen(cwd)-1] == '\\')
+               cwd[strlen(cwd)-1] = 0;
+       parms.basedir = cwd; //"f:/quake";
+//     parms.basedir = "f:\\quake";
+
+       COM_InitArgv (argc, argv);
+
+       // dedicated server ONLY!
+       if (!COM_CheckParm ("-dedicated"))
+       {
+               memcpy (newargv, argv, argc*4);
+               newargv[argc] = "-dedicated";
+               argc++;
+               argv = newargv;
+               COM_InitArgv (argc, argv);
+       }
+
+       parms.argc = argc;
+       parms.argv = argv;
+
+       printf ("Host_Init\n");
+       Host_Init (&parms);
+
+       oldtime = Sys_FloatTime ();
+
+    /* main window message loop */
+       while (1)
+       {
+               time = Sys_FloatTime();
+               if (time - oldtime < sys_ticrate.value )
+               {
+                       Sleep(1);
+                       continue;
+               }
+
+               Host_Frame ( time - oldtime );
+               oldtime = time;
+       }
+
+    /* return success of application */
+    return TRUE;
+}
+
diff --git a/transform.c b/transform.c
new file mode 100644 (file)
index 0000000..b8db4c3
--- /dev/null
@@ -0,0 +1,146 @@
+// LordHavoc: transform code for purposes of transpoly, wallpoly, etc
+
+#include "quakedef.h"
+
+vec3_t softwaretransform_x;
+vec3_t softwaretransform_y;
+vec3_t softwaretransform_z;
+vec_t softwaretransform_scale;
+vec3_t softwaretransform_offset;
+
+// set to different transform code depending on complexity of transform
+void (*softwaretransform) (vec3_t in, vec3_t out);
+
+// the real deal
+void softwaretransform_dorotatescaletranslate (vec3_t in, vec3_t out)
+{
+       out[0] = (in[0] * softwaretransform_x[0] + in[1] * softwaretransform_y[0] + in[2] * softwaretransform_z[0]) * softwaretransform_scale + softwaretransform_offset[0];
+       out[1] = (in[0] * softwaretransform_x[1] + in[1] * softwaretransform_y[1] + in[2] * softwaretransform_z[1]) * softwaretransform_scale + softwaretransform_offset[1];
+       out[2] = (in[0] * softwaretransform_x[2] + in[1] * softwaretransform_y[2] + in[2] * softwaretransform_z[2]) * softwaretransform_scale + softwaretransform_offset[2];
+}
+
+void softwaretransform_doscaletranslate (vec3_t in, vec3_t out)
+{
+       out[0] = in[0] * softwaretransform_scale + softwaretransform_offset[0];
+       out[1] = in[1] * softwaretransform_scale + softwaretransform_offset[1];
+       out[2] = in[2] * softwaretransform_scale + softwaretransform_offset[2];
+}
+
+void softwaretransform_dorotatetranslate (vec3_t in, vec3_t out)
+{
+       out[0] = (in[0] * softwaretransform_x[0] + in[1] * softwaretransform_y[0] + in[2] * softwaretransform_z[0]) + softwaretransform_offset[0];
+       out[1] = (in[0] * softwaretransform_x[1] + in[1] * softwaretransform_y[1] + in[2] * softwaretransform_z[1]) + softwaretransform_offset[1];
+       out[2] = (in[0] * softwaretransform_x[2] + in[1] * softwaretransform_y[2] + in[2] * softwaretransform_z[2]) + softwaretransform_offset[2];
+}
+
+void softwaretransform_dotranslate (vec3_t in, vec3_t out)
+{
+       out[0] = in[0] + softwaretransform_offset[0];
+       out[1] = in[1] + softwaretransform_offset[1];
+       out[2] = in[2] + softwaretransform_offset[2];
+}
+
+void softwaretransform_dorotatescale (vec3_t in, vec3_t out)
+{
+       out[0] = (in[0] * softwaretransform_x[0] + in[1] * softwaretransform_y[0] + in[2] * softwaretransform_z[0]) * softwaretransform_scale;
+       out[1] = (in[0] * softwaretransform_x[1] + in[1] * softwaretransform_y[1] + in[2] * softwaretransform_z[1]) * softwaretransform_scale;
+       out[2] = (in[0] * softwaretransform_x[2] + in[1] * softwaretransform_y[2] + in[2] * softwaretransform_z[2]) * softwaretransform_scale;
+}
+
+void softwaretransform_doscale (vec3_t in, vec3_t out)
+{
+       out[0] = in[0] * softwaretransform_scale + softwaretransform_offset[0];
+       out[1] = in[1] * softwaretransform_scale + softwaretransform_offset[1];
+       out[2] = in[2] * softwaretransform_scale + softwaretransform_offset[2];
+}
+
+void softwaretransform_dorotate (vec3_t in, vec3_t out)
+{
+       out[0] = (in[0] * softwaretransform_x[0] + in[1] * softwaretransform_y[0] + in[2] * softwaretransform_z[0]);
+       out[1] = (in[0] * softwaretransform_x[1] + in[1] * softwaretransform_y[1] + in[2] * softwaretransform_z[1]);
+       out[2] = (in[0] * softwaretransform_x[2] + in[1] * softwaretransform_y[2] + in[2] * softwaretransform_z[2]);
+}
+
+void softwaretransform_docopy (vec3_t in, vec3_t out)
+{
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+}
+
+// to save time on transforms, choose the appropriate function
+void softwaretransform_classify()
+{
+       if (softwaretransform_offset[0] != 0 || softwaretransform_offset[1] != 0 || softwaretransform_offset[2] != 0)
+       {
+               if (softwaretransform_scale != 1)
+               {
+                       if (softwaretransform_x[0] != 1 || softwaretransform_x[1] != 0 || softwaretransform_x[2] != 0 ||
+                               softwaretransform_y[0] != 0 || softwaretransform_y[1] != 1 || softwaretransform_y[2] != 0 ||
+                               softwaretransform_z[0] != 0 || softwaretransform_z[1] != 0 || softwaretransform_z[2] != 1)
+                               softwaretransform = &softwaretransform_dorotatescaletranslate;
+                       else
+                               softwaretransform = &softwaretransform_doscaletranslate;
+               }
+               else
+               {
+                       if (softwaretransform_x[0] != 1 || softwaretransform_x[1] != 0 || softwaretransform_x[2] != 0 ||
+                               softwaretransform_y[0] != 0 || softwaretransform_y[1] != 1 || softwaretransform_y[2] != 0 ||
+                               softwaretransform_z[0] != 0 || softwaretransform_z[1] != 0 || softwaretransform_z[2] != 1)
+                               softwaretransform = &softwaretransform_dorotatetranslate;
+                       else
+                               softwaretransform = &softwaretransform_dotranslate;
+               }
+       }
+       else
+       {
+               if (softwaretransform_scale != 1)
+               {
+                       if (softwaretransform_x[0] != 1 || softwaretransform_x[1] != 0 || softwaretransform_x[2] != 0 ||
+                               softwaretransform_y[0] != 0 || softwaretransform_y[1] != 1 || softwaretransform_y[2] != 0 ||
+                               softwaretransform_z[0] != 0 || softwaretransform_z[1] != 0 || softwaretransform_z[2] != 1)
+                               softwaretransform = &softwaretransform_dorotatescale;
+                       else
+                               softwaretransform = &softwaretransform_doscale;
+               }
+               else
+               {
+                       if (softwaretransform_x[0] != 1 || softwaretransform_x[1] != 0 || softwaretransform_x[2] != 0 ||
+                               softwaretransform_y[0] != 0 || softwaretransform_y[1] != 1 || softwaretransform_y[2] != 0 ||
+                               softwaretransform_z[0] != 0 || softwaretransform_z[1] != 0 || softwaretransform_z[2] != 1)
+                               softwaretransform = &softwaretransform_dorotate;
+                       else
+                               softwaretransform = &softwaretransform_docopy;
+               }
+       }
+}
+
+void softwaretransformidentity ()
+{
+       softwaretransform_offset[0] = softwaretransform_offset[1] = softwaretransform_offset[2] = softwaretransform_x[1] = softwaretransform_x[2] = softwaretransform_y[0] = softwaretransform_y[2] = softwaretransform_z[0] = softwaretransform_z[1] = 0;
+       softwaretransform_x[0] = softwaretransform_y[1] = softwaretransform_z[2] = 1;
+       softwaretransform_scale = 1;
+       // we know what it is
+       softwaretransform = &softwaretransform_docopy;
+}
+
+void softwaretransformset (vec3_t origin, vec3_t angles, vec_t scale)
+{
+       VectorCopy(origin, softwaretransform_offset);
+       AngleVectors(angles, softwaretransform_x, softwaretransform_y, softwaretransform_z);
+       softwaretransform_y[0] = -softwaretransform_y[0];
+       softwaretransform_y[1] = -softwaretransform_y[1];
+       softwaretransform_y[2] = -softwaretransform_y[2];
+       softwaretransform_scale = scale;
+       // choose best transform code
+       softwaretransform_classify();
+}
+
+void softwaretransformforentity (entity_t *e)
+{
+       vec3_t angles;
+       angles[0] = -e->angles[0];
+       angles[1] = e->angles[1];
+       angles[2] = e->angles[2];
+       softwaretransformset(e->origin, angles, e->scale);
+}
diff --git a/transform.h b/transform.h
new file mode 100644 (file)
index 0000000..bee8692
--- /dev/null
@@ -0,0 +1,15 @@
+// LordHavoc: software transform support, intended for transpoly and wallpoly systems
+
+#define tft_translate 1
+#define tft_rotate 2
+
+extern vec3_t softwaretransform_offset;
+extern vec3_t softwaretransform_x;
+extern vec3_t softwaretransform_y;
+extern vec3_t softwaretransform_z;
+extern int softwaretransform_type;
+
+extern void softwaretransformforentity (entity_t *e);
+extern void softwaretransformidentity ();
+extern void softwaretransformset (vec3_t origin, vec3_t angles, vec_t scale);
+void (*softwaretransform) (vec3_t in, vec3_t out);
diff --git a/vid.h b/vid.h
new file mode 100644 (file)
index 0000000..d08d5dc
--- /dev/null
+++ b/vid.h
@@ -0,0 +1,80 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// vid.h -- video driver defs
+
+#define VID_CBITS      6
+#define VID_GRADES     (1 << VID_CBITS)
+
+// a pixel can be one, two, or four bytes
+typedef byte pixel_t;
+
+typedef struct vrect_s
+{
+       int                             x,y,width,height;
+       struct vrect_s  *pnext;
+} vrect_t;
+
+typedef struct
+{
+       pixel_t                 *buffer;                // invisible buffer
+       pixel_t                 *colormap;              // 256 * VID_GRADES size
+       unsigned short  *colormap16;    // 256 * VID_GRADES size
+       int                             fullbright;             // index of first fullbright color
+       unsigned                width;          
+       unsigned                height;
+       float                   aspect;         // width / height -- < 0 is taller than wide
+       int                             numpages;
+       int                             recalc_refdef;  // if true, recalc vid-based stuff
+       pixel_t                 *conbuffer;
+       unsigned                conwidth;
+       unsigned                conheight;
+       int                             maxwarpwidth;
+       int                             maxwarpheight;
+       pixel_t                 *direct;                // direct drawing to framebuffer, if not
+                                                                       //  NULL
+} viddef_t;
+
+extern viddef_t        vid;                            // global video state
+extern unsigned short  d_8to16table[256];
+extern unsigned        d_8to24table[256];
+extern void (*vid_menudrawfn)(void);
+extern void (*vid_menukeyfn)(int key);
+
+void   VID_SetPalette (unsigned char *palette);
+// called at startup
+
+void   VID_Init (unsigned char *palette);
+// Called at startup to set up translation tables, takes 256 8 bit RGB values
+// the palette data will go away after the call, so it must be copied off if
+// the video driver will need it again
+
+void   VID_Shutdown (void);
+// Called at shutdown
+
+void   VID_Update (vrect_t *rects);
+// flushes the given rectangles from the view buffer to the screen
+
+int VID_SetMode (int modenum, unsigned char *palette);
+// sets the mode; only used by the Quake engine for resetting to mode 0 (the
+// base mode) on memory allocation failures
+
+void VID_HandlePause (qboolean pause);
+// called only on Win32, when pause happens, so the mouse can be released
+
diff --git a/vid_shared.c b/vid_shared.c
new file mode 100644 (file)
index 0000000..0654fe1
--- /dev/null
@@ -0,0 +1,104 @@
+
+#include "quakedef.h"
+
+unsigned       d_8to24table[256];
+unsigned char d_15to8table[32768]; // LordHavoc: was 64k elements, now 32k like it should be
+
+void   VID_SetPalette (unsigned char *palette)
+{
+       byte    *out;
+       unsigned short i;
+
+       out = (byte *) d_8to24table; // d_8to24table is accessed as 32bit for speed reasons, but is created as 8bit bytes
+       for (i=0 ; i<255 ; i++)
+       {
+               *out++ = *palette++;
+               *out++ = *palette++;
+               *out++ = *palette++;
+               *out++ = 255;
+       }
+       d_8to24table[255] = 0;
+}
+
+void   VID_Setup15to8Palette ()
+{
+       byte    *pal;
+       unsigned r,g,b;
+       unsigned v;
+       int     r1,g1,b1;
+       int             j,k,l;
+       unsigned short i;
+
+       // JACK: 3D distance calcs - k is last closest, l is the distance.
+       // FIXME: Precalculate this and cache to disk.
+       for (i = 0;i < 32768;i++)
+       {
+               /* Maps
+                       000000000000000
+                       000000000011111 = Red  = 0x001F
+                       000001111100000 = Blue = 0x03E0
+                       111110000000000 = Grn  = 0x7C00
+               */
+               r = ((i & 0x001F) << 3)+4;
+               g = ((i & 0x03E0) >> 2)+4;
+               b = ((i & 0x7C00) >> 7)+4;
+               pal = (unsigned char *)d_8to24table;
+               for (v = 0, k = 0, l = 1000000000;v < 256;v++, pal += 4)
+               {
+                       r1 = r - pal[0];
+                       g1 = g - pal[1];
+                       b1 = b - pal[2];
+                       j = (r1*r1*2)+(g1*g1*3)+(b1*b1); // LordHavoc: weighting to tune for human eye (added *2 and *3)
+                       if (j < l)
+                       {
+                               k = v;
+                               l = j;
+                       }
+               }
+               d_15to8table[i] = k;
+       }
+}
+
+// LordHavoc: gamma correction does not belong in gl_vidnt.c
+byte gamma[256];
+static float vid_gamma = 1.0;
+
+void Check_Gamma (unsigned char *pal)
+{
+       float   inf;
+       int             i;
+
+       if (i = COM_CheckParm("-gamma"))
+               vid_gamma = atof(com_argv[i+1]);
+       else
+       {
+//             if ((gl_renderer && strstr(gl_renderer, "Voodoo")) ||
+//                     (gl_vendor && strstr(gl_vendor, "3Dfx")))
+                       vid_gamma = 1;
+//             else if (gl_vendor && strstr(gl_vendor, "ATI"))
+//                     vid_gamma = 1;
+//             else
+//                     vid_gamma = 0.7;
+       }
+
+       if (vid_gamma == 1) // LordHavoc: dodge the math
+       {
+               for (i = 0;i < 256;i++)
+                       gamma[i] = i;
+       }
+       else
+       {
+               for (i = 0;i < 256;i++)
+               {
+                       inf = pow((i+1)/256.0, vid_gamma)*255 + 0.5;
+                       if (inf < 0) inf = 0;
+                       if (inf > 255) inf = 255;
+                       gamma[i] = inf;
+               }
+       }
+
+       // gamma correct the palette
+       for (i=0 ; i<768 ; i++)
+               pal[i] = gamma[pal[i]];
+       // note: 32bit uploads are corrected by the upload functions
+}
diff --git a/vid_wgl.c b/vid_wgl.c
new file mode 100644 (file)
index 0000000..7f2247c
--- /dev/null
+++ b/vid_wgl.c
@@ -0,0 +1,1821 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// gl_vidnt.c -- NT GL vid component
+
+#include "quakedef.h"
+#include "winquake.h"
+#include "resource.h"
+#include <commctrl.h>
+
+#define MAX_MODE_LIST  30
+#define VID_ROW_SIZE   3
+#define WARP_WIDTH             320
+#define WARP_HEIGHT            200
+#define MAXWIDTH               10000
+#define MAXHEIGHT              10000
+#define BASEWIDTH              320
+#define BASEHEIGHT             200
+
+#define MODE_WINDOWED                  0
+#define NO_MODE                                        (MODE_WINDOWED - 1)
+#define MODE_FULLSCREEN_DEFAULT        (MODE_WINDOWED + 1)
+
+typedef struct {
+       modestate_t     type;
+       int                     width;
+       int                     height;
+       int                     modenum;
+       int                     dib;
+       int                     fullscreen;
+       int                     bpp;
+       int                     halfscreen;
+       char            modedesc[17];
+} vmode_t;
+
+typedef struct {
+       int                     width;
+       int                     height;
+} lmode_t;
+
+lmode_t        lowresmodes[] = {
+       {320, 200},
+       {320, 240},
+       {400, 300},
+       {512, 384},
+};
+
+const char *gl_vendor;
+const char *gl_renderer;
+const char *gl_version;
+const char *gl_extensions;
+
+qboolean               DDActive;
+qboolean               scr_skipupdate;
+
+static vmode_t modelist[MAX_MODE_LIST];
+static int             nummodes;
+static vmode_t *pcurrentmode;
+static vmode_t badmode;
+
+static DEVMODE gdevmode;
+static qboolean        vid_initialized = false;
+static qboolean        windowed, leavecurrentmode;
+static qboolean vid_canalttab = false;
+static qboolean vid_wassuspended = false;
+static int             windowed_mouse;
+extern qboolean        mouseactive;  // from in_win.c
+static HICON   hIcon;
+
+int                    DIBWidth, DIBHeight;
+RECT           WindowRect;
+DWORD          WindowStyle, ExWindowStyle;
+
+HWND   mainwindow, dibwindow;
+
+int                    vid_modenum = NO_MODE;
+int                    vid_realmode;
+int                    vid_default = MODE_WINDOWED;
+static int     windowed_default;
+unsigned char  vid_curpal[256*3];
+
+HGLRC  baseRC;
+HDC            maindc;
+
+glvert_t glv;
+
+HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);
+
+viddef_t       vid;                            // global video state
+
+float          gldepthmin, gldepthmax;
+
+modestate_t    modestate = MS_UNINIT;
+
+void VID_MenuDraw (void);
+void VID_MenuKey (int key);
+
+LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+void AppActivate(BOOL fActive, BOOL minimize);
+char *VID_GetModeDescription (int mode);
+void ClearAllStates (void);
+void VID_UpdateWindowStatus (void);
+void GL_Init (void);
+
+// LordHavoc: ARB multitexture support
+int gl_mtex_enum = 0;
+
+qboolean is8bit = false;
+qboolean isPermedia = false;
+qboolean isATI = false; // LordHavoc: special differences for ATI's broken drivers
+qboolean isG200 = false; // LordHavoc: the Matrox G200 can't do per pixel alpha, and it uses a D3D driver for GL... ugh...
+qboolean isRagePro = false; // LordHavoc: the ATI Rage Pro has limitations with per pixel alpha (the color scaler does not apply to per pixel alpha images...), although not as bad as a G200.
+qboolean gl_mtexable = false;
+qboolean gl_arrays = false;
+
+//====================================
+
+cvar_t         vid_mode = {"vid_mode","0", false};
+// Note that 0 is MODE_WINDOWED
+cvar_t         _vid_default_mode = {"_vid_default_mode","0", true};
+// Note that 3 is MODE_FULLSCREEN_DEFAULT
+cvar_t         _vid_default_mode_win = {"_vid_default_mode_win","3", true};
+cvar_t         vid_wait = {"vid_wait","0"};
+cvar_t         vid_nopageflip = {"vid_nopageflip","0", true};
+cvar_t         _vid_wait_override = {"_vid_wait_override", "0", true};
+cvar_t         vid_config_x = {"vid_config_x","800", true};
+cvar_t         vid_config_y = {"vid_config_y","600", true};
+cvar_t         vid_stretch_by_2 = {"vid_stretch_by_2","1", true};
+cvar_t         _windowed_mouse = {"_windowed_mouse","1", true};
+
+int                    window_center_x, window_center_y, window_x, window_y, window_width, window_height;
+RECT           window_rect;
+
+// direct draw software compatability stuff
+
+void VID_HandlePause (qboolean pause)
+{
+}
+
+void VID_ForceLockState (int lk)
+{
+}
+
+void VID_LockBuffer (void)
+{
+}
+
+void VID_UnlockBuffer (void)
+{
+}
+
+int VID_ForceUnlockedAndReturnState (void)
+{
+       return 0;
+}
+
+void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify)
+{
+    int     CenterX, CenterY;
+
+       CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
+       CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
+       if (CenterX > CenterY*2)
+               CenterX >>= 1;  // dual screens
+       CenterX = (CenterX < 0) ? 0: CenterX;
+       CenterY = (CenterY < 0) ? 0: CenterY;
+       SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0,
+                       SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+}
+
+qboolean VID_SetWindowedMode (int modenum)
+{
+       int                             lastmodestate, width, height;
+       RECT                    rect;
+
+       lastmodestate = modestate;
+
+       WindowRect.top = WindowRect.left = 0;
+
+       WindowRect.right = modelist[modenum].width;
+       WindowRect.bottom = modelist[modenum].height;
+
+       DIBWidth = modelist[modenum].width;
+       DIBHeight = modelist[modenum].height;
+
+       WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU |
+                                 WS_MINIMIZEBOX;
+       ExWindowStyle = 0;
+
+       rect = WindowRect;
+       AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0);
+
+       width = rect.right - rect.left;
+       height = rect.bottom - rect.top;
+
+       // Create the DIB window
+       dibwindow = CreateWindowEx (
+                ExWindowStyle,
+#ifdef NEHAHRA
+                "DPNehahraQuake",
+                "DPNehahraGLQuake",
+#else
+                "DarkPlacesQuake",
+                "DarkPlacesGLQuake",
+#endif
+                WindowStyle,
+                rect.left, rect.top,
+                width,
+                height,
+                NULL,
+                NULL,
+                global_hInstance,
+                NULL);
+
+       if (!dibwindow)
+               Sys_Error ("Couldn't create DIB window");
+
+       // Center and show the DIB window
+       CenterWindow(dibwindow, WindowRect.right - WindowRect.left,
+                                WindowRect.bottom - WindowRect.top, false);
+
+       ShowWindow (dibwindow, SW_SHOWDEFAULT);
+       UpdateWindow (dibwindow);
+
+       modestate = MS_WINDOWED;
+
+       // LordHavoc: using GDI functions on an OpenGL window?  bad idea
+       /*
+// because we have set the background brush for the window to NULL
+// (to avoid flickering when re-sizing the window on the desktop),
+// we clear the window to black when created, otherwise it will be
+// empty while Quake starts up.
+       hdc = GetDC(dibwindow);
+       PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS);
+       ReleaseDC(dibwindow, hdc);
+       */
+
+       if (vid.conheight > modelist[modenum].height)
+               vid.conheight = modelist[modenum].height;
+       if (vid.conwidth > modelist[modenum].width)
+               vid.conwidth = modelist[modenum].width;
+       vid.width = vid.conwidth;
+       vid.height = vid.conheight;
+
+       vid.numpages = 2;
+
+       mainwindow = dibwindow;
+
+       SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon);
+       SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon);
+
+       return true;
+}
+
+
+qboolean VID_SetFullDIBMode (int modenum)
+{
+       int                             lastmodestate, width, height;
+       RECT                    rect;
+
+       if (!leavecurrentmode)
+       {
+               gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+               gdevmode.dmBitsPerPel = modelist[modenum].bpp;
+               gdevmode.dmPelsWidth = modelist[modenum].width <<
+                                                          modelist[modenum].halfscreen;
+               gdevmode.dmPelsHeight = modelist[modenum].height;
+               gdevmode.dmSize = sizeof (gdevmode);
+
+               if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
+                       Sys_Error ("Couldn't set fullscreen DIB mode");
+       }
+
+       lastmodestate = modestate;
+       modestate = MS_FULLDIB;
+
+       WindowRect.top = WindowRect.left = 0;
+
+       WindowRect.right = modelist[modenum].width;
+       WindowRect.bottom = modelist[modenum].height;
+
+       DIBWidth = modelist[modenum].width;
+       DIBHeight = modelist[modenum].height;
+
+       WindowStyle = WS_POPUP;
+       ExWindowStyle = 0;
+
+       rect = WindowRect;
+       AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0);
+
+       width = rect.right - rect.left;
+       height = rect.bottom - rect.top;
+
+       // Create the DIB window
+       dibwindow = CreateWindowEx (
+                ExWindowStyle,
+#ifdef NEHAHRA
+                "DPNehahraQuake",
+                "DPNehahraGLQuake",
+#else
+                "DarkPlacesQuake",
+                "DarkPlacesGLQuake",
+#endif
+                WindowStyle,
+                rect.left, rect.top,
+                width,
+                height,
+                NULL,
+                NULL,
+                global_hInstance,
+                NULL);
+
+       if (!dibwindow)
+               Sys_Error ("Couldn't create DIB window");
+
+       ShowWindow (dibwindow, SW_SHOWDEFAULT);
+       UpdateWindow (dibwindow);
+
+       // LordHavoc: using GDI functions on an OpenGL window?  bad idea
+       /*
+       // Because we have set the background brush for the window to NULL
+       // (to avoid flickering when re-sizing the window on the desktop), we
+       // clear the window to black when created, otherwise it will be
+       // empty while Quake starts up.
+       hdc = GetDC(dibwindow);
+       PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS);
+       ReleaseDC(dibwindow, hdc);
+       */
+
+       if (vid.conheight > modelist[modenum].height)
+               vid.conheight = modelist[modenum].height;
+       if (vid.conwidth > modelist[modenum].width)
+               vid.conwidth = modelist[modenum].width;
+       vid.width = vid.conwidth;
+       vid.height = vid.conheight;
+
+       vid.numpages = 2;
+
+// needed because we're not getting WM_MOVE messages fullscreen on NT
+       window_x = 0;
+       window_y = 0;
+
+       mainwindow = dibwindow;
+
+       SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon);
+       SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon);
+
+       return true;
+}
+
+
+int VID_SetMode (int modenum, unsigned char *palette)
+{
+       int                             original_mode, temp;
+       qboolean                stat;
+    MSG                                msg;
+
+       if ((windowed && (modenum != 0)) ||
+               (!windowed && (modenum < 1)) ||
+               (!windowed && (modenum >= nummodes)))
+       {
+               Sys_Error ("Bad video mode\n");
+       }
+
+// so Con_Printfs don't mess us up by forcing vid and snd updates
+       temp = scr_disabled_for_loading;
+       scr_disabled_for_loading = true;
+
+       CDAudio_Pause ();
+
+       if (vid_modenum == NO_MODE)
+               original_mode = windowed_default;
+       else
+               original_mode = vid_modenum;
+
+       // Set either the fullscreen or windowed mode
+       if (modelist[modenum].type == MS_WINDOWED)
+       {
+               if (_windowed_mouse.value && key_dest == key_game)
+               {
+                       stat = VID_SetWindowedMode(modenum);
+                       IN_ActivateMouse ();
+                       IN_HideMouse ();
+               }
+               else
+               {
+                       IN_DeactivateMouse ();
+                       IN_ShowMouse ();
+                       stat = VID_SetWindowedMode(modenum);
+               }
+       }
+       else if (modelist[modenum].type == MS_FULLDIB)
+       {
+               stat = VID_SetFullDIBMode(modenum);
+               IN_ActivateMouse ();
+               IN_HideMouse ();
+       }
+       else
+       {
+               Sys_Error ("VID_SetMode: Bad mode type in modelist");
+       }
+
+       window_width = DIBWidth;
+       window_height = DIBHeight;
+       VID_UpdateWindowStatus ();
+
+       CDAudio_Resume ();
+       scr_disabled_for_loading = temp;
+
+       if (!stat)
+       {
+               Sys_Error ("Couldn't set video mode");
+       }
+
+// now we try to make sure we get the focus on the mode switch, because
+// sometimes in some systems we don't.  We grab the foreground, then
+// finish setting up, pump all our messages, and sleep for a little while
+// to let messages finish bouncing around the system, then we put
+// ourselves at the top of the z order, then grab the foreground again,
+// Who knows if it helps, but it probably doesn't hurt
+       SetForegroundWindow (mainwindow);
+       VID_SetPalette (palette);
+       vid_modenum = modenum;
+       Cvar_SetValue ("vid_mode", (float)vid_modenum);
+
+       while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+       {
+       TranslateMessage (&msg);
+       DispatchMessage (&msg);
+       }
+
+       Sleep (100);
+
+       SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0,
+                                 SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |
+                                 SWP_NOCOPYBITS);
+
+       SetForegroundWindow (mainwindow);
+
+// fix the leftover Alt from any Alt-Tab or the like that switched us away
+       ClearAllStates ();
+
+       if (!msg_suppress_1)
+               Con_SafePrintf ("Video mode %s initialized.\n", VID_GetModeDescription (vid_modenum));
+
+       VID_SetPalette (palette);
+
+       vid.recalc_refdef = 1;
+
+       return true;
+}
+
+
+/*
+================
+VID_UpdateWindowStatus
+================
+*/
+void VID_UpdateWindowStatus (void)
+{
+
+       window_rect.left = window_x;
+       window_rect.top = window_y;
+       window_rect.right = window_x + window_width;
+       window_rect.bottom = window_y + window_height;
+       window_center_x = (window_rect.left + window_rect.right) / 2;
+       window_center_y = (window_rect.top + window_rect.bottom) / 2;
+
+       IN_UpdateClipCursor ();
+}
+
+
+//====================================
+
+void CheckVertexArrays (void)
+{
+       if (COM_CheckParm("-novertex"))
+       {
+               Con_Printf("...vertex array support disabled\n");
+               return;
+       }
+       if ((qglArrayElement = (void *) wglGetProcAddress("glArrayElement"))
+        && (qglColorPointer = (void *) wglGetProcAddress("glColorPointer"))
+//      && (qglDrawArrays = (void *) wglGetProcAddress("glDrawArrays"))
+        && (qglDrawElements = (void *) wglGetProcAddress("glDrawElements"))
+//      && (qglInterleavedArrays = (void *) wglGetProcAddress("glInterleavedArrays"))
+        && (qglTexCoordPointer = (void *) wglGetProcAddress("glTexCoordPointer"))
+        && (qglVertexPointer = (void *) wglGetProcAddress("glVertexPointer"))
+               )
+       {
+               Con_Printf("...vertex array support detected\n");
+               gl_arrays = true;
+               return;
+       }
+
+       Con_Printf("...vertex array support disabled (not detected - get a better driver)\n");
+}
+
+int            texture_extension_number = 1;
+
+void CheckMultiTexture(void) 
+{
+       qglMTexCoord2f = NULL;
+       qglSelectTexture = NULL;
+       // Check to see if multitexture is disabled
+       if (COM_CheckParm("-nomtex"))
+       {
+               Con_Printf("...multitexture disabled\n");
+               return;
+       }
+       // Test for ARB_multitexture
+       if (!COM_CheckParm("-SGISmtex") && strstr(gl_extensions, "GL_ARB_multitexture "))
+       {
+               Con_Printf("...using GL_ARB_multitexture\n");
+               qglMTexCoord2f = (void *) wglGetProcAddress("glMultiTexCoord2fARB");
+               qglSelectTexture = (void *) wglGetProcAddress("glActiveTextureARB");
+               gl_mtexable = true;
+               gl_mtex_enum = GL_TEXTURE0_ARB;
+       }
+       else if (strstr(gl_extensions, "GL_SGIS_multitexture ")) // Test for SGIS_multitexture (if ARB_multitexture not found)
+       {
+               Con_Printf("...using GL_SGIS_multitexture\n");
+               qglMTexCoord2f = (void *) wglGetProcAddress("glMTexCoord2fSGIS");
+               qglSelectTexture = (void *) wglGetProcAddress("glSelectTextureSGIS");
+               gl_mtexable = true;
+               gl_mtex_enum = TEXTURE0_SGIS;
+       }
+       if (!gl_mtexable)
+               Con_Printf("...multitexture disabled (not detected)\n");
+}
+
+/*
+===============
+GL_Init
+===============
+*/
+extern char *QSG_EXTENSIONS;
+void GL_Init (void)
+{
+       gl_vendor = glGetString (GL_VENDOR);
+       Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
+       gl_renderer = glGetString (GL_RENDERER);
+       Con_Printf ("GL_RENDERER: %s\n", gl_renderer);
+
+       gl_version = glGetString (GL_VERSION);
+       Con_Printf ("GL_VERSION: %s\n", gl_version);
+       gl_extensions = glGetString (GL_EXTENSIONS);
+       Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);
+
+//     Con_Printf ("%s %s\n", gl_renderer, gl_version);
+
+    if (strnicmp(gl_renderer,"Permedia",8)==0)
+         isPermedia = true;
+
+       // LordHavoc: special differences for ATI (broken 8bit color when also using 32bit? weird!)
+    if (strnicmp(gl_vendor,"ATI",3)==0)
+       {
+               isATI = true;
+               if (strnicmp(gl_renderer,"Rage Pro",8)==0)
+                       isRagePro = true;
+       }
+       if (strnicmp(gl_renderer,"Matrox G200 Direct3D",20)==0) // a D3D driver for GL? sigh...
+               isG200 = true;
+
+       CheckMultiTexture ();
+       CheckVertexArrays ();
+
+       // LordHavoc: report supported extensions
+       Con_Printf ("\nQSG extensions: %s\n", QSG_EXTENSIONS);
+
+       // LordHavoc: set up state
+//     glEnable(GL_DEPTH_TEST);
+//     glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+//     glShadeModel(GL_SMOOTH);
+//     glEnable(GL_TEXTURE_2D);
+       glAlphaFunc(GL_GREATER, 0.5);
+//     if (isRagePro || isG200)
+//     {
+//             glEnable(GL_ALPHA_TEST);
+//             glAlphaFunc(GL_GREATER, 0.5);
+//     }
+//     else
+//             glDisable(GL_ALPHA_TEST);
+//     glDepthMask(1);
+//     glDisable(GL_DITHER); // LordHavoc: disable dithering
+//     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+//     glEnable(GL_BLEND);
+
+//     glClearColor (0,0,0,0); // LordHavoc: changed from red to black
+       glCullFace(GL_FRONT);
+
+//     glAlphaFunc(GL_GREATER, 0.666);
+}
+
+/*
+=================
+GL_BeginRendering
+
+=================
+*/
+void GL_BeginRendering (int *x, int *y, int *width, int *height)
+{
+//     extern cvar_t gl_clear;
+
+       *x = *y = 0;
+       *width = WindowRect.right - WindowRect.left;
+       *height = WindowRect.bottom - WindowRect.top;
+
+//     if (!wglMakeCurrent( maindc, baseRC ))
+//             Sys_Error ("wglMakeCurrent failed");
+
+//     glViewport (*x, *y, *width, *height);
+}
+
+
+void GL_EndRendering (void)
+{
+       if (!scr_skipupdate)
+               SwapBuffers(maindc);
+
+// handle the mouse state when windowed if that's changed
+       if (modestate == MS_WINDOWED)
+       {
+               if (!_windowed_mouse.value)
+               {
+                       if (windowed_mouse)
+                       {
+                               IN_DeactivateMouse ();
+                               IN_ShowMouse ();
+                               windowed_mouse = false;
+                       }
+               }
+               else
+               {
+                       windowed_mouse = true;
+                       if (key_dest == key_game && !mouseactive && ActiveApp)
+                       {
+                               IN_ActivateMouse ();
+                               IN_HideMouse ();
+                       }
+                       else if (mouseactive && key_dest != key_game)
+                       {
+                               IN_DeactivateMouse ();
+                               IN_ShowMouse ();
+                       }
+               }
+       }
+}
+
+void VID_SetDefaultMode (void)
+{
+       IN_DeactivateMouse ();
+}
+
+
+void   VID_Shutdown (void)
+{
+       HGLRC hRC;
+       HDC       hDC;
+       int i;
+       GLuint temp[8192];
+
+       if (vid_initialized)
+       {
+               vid_canalttab = false;
+               hRC = wglGetCurrentContext();
+       hDC = wglGetCurrentDC();
+
+       wglMakeCurrent(NULL, NULL);
+
+               // LordHavoc: free textures before closing (may help NVIDIA)
+               for (i = 0;i < 8192;i++) temp[i] = i;
+               glDeleteTextures(8192, temp);
+
+       if (hRC)
+           wglDeleteContext(hRC);
+
+               if (hDC && dibwindow)
+                       ReleaseDC(dibwindow, hDC);
+
+               if (modestate == MS_FULLDIB)
+                       ChangeDisplaySettings (NULL, 0);
+
+               if (maindc && dibwindow)
+                       ReleaseDC (dibwindow, maindc);
+
+               AppActivate(false, false);
+       }
+}
+
+
+//==========================================================================
+
+
+BOOL bSetupPixelFormat(HDC hDC)
+{
+    static PIXELFORMATDESCRIPTOR pfd = {
+       sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
+       1,                              // version number
+       PFD_DRAW_TO_WINDOW              // support window
+       |  PFD_SUPPORT_OPENGL   // support OpenGL
+       |  PFD_DOUBLEBUFFER ,   // double buffered
+       PFD_TYPE_RGBA,                  // RGBA type
+       24,                             // 24-bit color depth
+       0, 0, 0, 0, 0, 0,               // color bits ignored
+       0,                              // no alpha buffer
+       0,                              // shift bit ignored
+       0,                              // no accumulation buffer
+       0, 0, 0, 0,                     // accum bits ignored
+       32,                             // 32-bit z-buffer      
+       0,                              // no stencil buffer
+       0,                              // no auxiliary buffer
+       PFD_MAIN_PLANE,                 // main layer
+       0,                              // reserved
+       0, 0, 0                         // layer masks ignored
+    };
+    int pixelformat;
+
+    if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+    {
+        MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
+        return FALSE;
+    }
+
+    if (SetPixelFormat(hDC, pixelformat, &pfd) == FALSE)
+    {
+        MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+
+byte        scantokey[128] = 
+                                       { 
+//  0           1       2       3       4       5       6       7 
+//  8           9       A       B       C       D       E       F 
+       0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6', 
+       '7',    '8',    '9',    '0',    '-',    '=',    K_BACKSPACE, 9, // 0 
+       'q',    'w',    'e',    'r',    't',    'y',    'u',    'i', 
+       'o',    'p',    '[',    ']',    13 ,    K_CTRL,'a',  's',      // 1 
+       'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';', 
+       '\'' ,    '`',    K_SHIFT,'\\',  'z',    'x',    'c',    'v',      // 2 
+       'b',    'n',    'm',    ',',    '.',    '/',    K_SHIFT,'*', 
+       K_ALT,' ',   0  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3 
+       K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE  ,    0  , K_HOME, 
+       K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, //4 
+       K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11, 
+       K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0, 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0, 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7 
+                                       }; 
+
+byte        shiftscantokey[128] = 
+                                       { 
+//  0           1       2       3       4       5       6       7 
+//  8           9       A       B       C       D       E       F 
+       0  ,    27,     '!',    '@',    '#',    '$',    '%',    '^', 
+       '&',    '*',    '(',    ')',    '_',    '+',    K_BACKSPACE, 9, // 0 
+       'Q',    'W',    'E',    'R',    'T',    'Y',    'U',    'I', 
+       'O',    'P',    '{',    '}',    13 ,    K_CTRL,'A',  'S',      // 1 
+       'D',    'F',    'G',    'H',    'J',    'K',    'L',    ':', 
+       '"' ,    '~',    K_SHIFT,'|',  'Z',    'X',    'C',    'V',      // 2 
+       'B',    'N',    'M',    '<',    '>',    '?',    K_SHIFT,'*', 
+       K_ALT,' ',   0  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3 
+       K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE  ,    0  , K_HOME, 
+       K_UPARROW,K_PGUP,'_',K_LEFTARROW,'%',K_RIGHTARROW,'+',K_END, //4 
+       K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11, 
+       K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0, 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0, 
+       0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7 
+                                       }; 
+
+
+/*
+=======
+MapKey
+
+Map from windows to quake keynums
+=======
+*/
+int MapKey (int key)
+{
+       key = (key>>16)&255;
+       if (key > 127)
+               return 0;
+       if (scantokey[key] == 0)
+               Con_DPrintf("key 0x%02x has no translation\n", key);
+       return scantokey[key];
+}
+
+/*
+===================================================================
+
+MAIN WINDOW
+
+===================================================================
+*/
+
+/*
+================
+ClearAllStates
+================
+*/
+void ClearAllStates (void)
+{
+       int             i;
+       
+// send an up event for each key, to make sure the server clears them all
+       for (i=0 ; i<256 ; i++)
+       {
+               Key_Event (i, false);
+       }
+
+       Key_ClearStates ();
+       IN_ClearStates ();
+}
+
+void AppActivate(BOOL fActive, BOOL minimize)
+/****************************************************************************
+*
+* Function:     AppActivate
+* Parameters:   fActive - True if app is activating
+*
+* Description:  If the application is activating, then swap the system
+*               into SYSPAL_NOSTATIC mode so that our palettes will display
+*               correctly.
+*
+****************************************************************************/
+{
+       static BOOL     sound_active;
+
+       ActiveApp = fActive;
+       Minimized = minimize;
+
+// enable/disable sound on focus gain/loss
+       if (!ActiveApp && sound_active)
+       {
+               S_BlockSound ();
+               sound_active = false;
+       }
+       else if (ActiveApp && !sound_active)
+       {
+               S_UnblockSound ();
+               sound_active = true;
+       }
+
+       if (fActive)
+       {
+               if (modestate == MS_FULLDIB)
+               {
+                       IN_ActivateMouse ();
+                       IN_HideMouse ();
+                       if (vid_canalttab && vid_wassuspended) {
+                               vid_wassuspended = false;
+                               ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);
+                               ShowWindow(mainwindow, SW_SHOWNORMAL);
+                       }
+               }
+               else if ((modestate == MS_WINDOWED) && _windowed_mouse.value && key_dest == key_game)
+               {
+                       IN_ActivateMouse ();
+                       IN_HideMouse ();
+               }
+       }
+
+       if (!fActive)
+       {
+               if (modestate == MS_FULLDIB)
+               {
+                       IN_DeactivateMouse ();
+                       IN_ShowMouse ();
+                       if (vid_canalttab) { 
+                               ChangeDisplaySettings (NULL, 0);
+                               vid_wassuspended = true;
+                       }
+               }
+               else if ((modestate == MS_WINDOWED) && _windowed_mouse.value)
+               {
+                       IN_DeactivateMouse ();
+                       IN_ShowMouse ();
+               }
+       }
+}
+
+LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+/* main window procedure */
+LONG WINAPI MainWndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    LONG    lRet = 1;
+       int             fActive, fMinimized, temp;
+       extern unsigned int uiWheelMessage;
+
+       if ( uMsg == uiWheelMessage )
+               uMsg = WM_MOUSEWHEEL;
+
+    switch (uMsg)
+    {
+               case WM_KILLFOCUS:
+                       if (modestate == MS_FULLDIB)
+                               ShowWindow(mainwindow, SW_SHOWMINNOACTIVE);
+                       break;
+
+               case WM_CREATE:
+                       break;
+
+               case WM_MOVE:
+                       window_x = (int) LOWORD(lParam);
+                       window_y = (int) HIWORD(lParam);
+                       VID_UpdateWindowStatus ();
+                       break;
+
+               case WM_KEYDOWN:
+               case WM_SYSKEYDOWN:
+                       Key_Event (MapKey(lParam), true);
+                       break;
+                       
+               case WM_KEYUP:
+               case WM_SYSKEYUP:
+                       Key_Event (MapKey(lParam), false);
+                       break;
+
+               case WM_SYSCHAR:
+               // keep Alt-Space from happening
+                       break;
+
+       // this is complicated because Win32 seems to pack multiple mouse events into
+       // one update sometimes, so we always check all states and look for events
+               case WM_LBUTTONDOWN:
+               case WM_LBUTTONUP:
+               case WM_RBUTTONDOWN:
+               case WM_RBUTTONUP:
+               case WM_MBUTTONDOWN:
+               case WM_MBUTTONUP:
+               case WM_MOUSEMOVE:
+                       temp = 0;
+
+                       if (wParam & MK_LBUTTON)
+                               temp |= 1;
+
+                       if (wParam & MK_RBUTTON)
+                               temp |= 2;
+
+                       if (wParam & MK_MBUTTON)
+                               temp |= 4;
+
+                       IN_MouseEvent (temp);
+
+                       break;
+
+               // JACK: This is the mouse wheel with the Intellimouse
+               // Its delta is either positive or neg, and we generate the proper
+               // Event.
+               case WM_MOUSEWHEEL: 
+                       if ((short) HIWORD(wParam) > 0) {
+                               Key_Event(K_MWHEELUP, true);
+                               Key_Event(K_MWHEELUP, false);
+                       } else {
+                               Key_Event(K_MWHEELDOWN, true);
+                               Key_Event(K_MWHEELDOWN, false);
+                       }
+                       break;
+
+       case WM_SIZE:
+            break;
+
+           case WM_CLOSE:
+                       if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit",
+                                               MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)
+                       {
+                               Sys_Quit ();
+                       }
+
+               break;
+
+               case WM_ACTIVATE:
+                       fActive = LOWORD(wParam);
+                       fMinimized = (BOOL) HIWORD(wParam);
+                       AppActivate(!(fActive == WA_INACTIVE), fMinimized);
+
+               // fix the leftover Alt from any Alt-Tab or the like that switched us away
+                       ClearAllStates ();
+
+                       break;
+
+           case WM_DESTROY:
+        {
+                       if (dibwindow)
+                               DestroyWindow (dibwindow);
+
+            PostQuitMessage (0);
+        }
+        break;
+
+               case MM_MCINOTIFY:
+            lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
+                       break;
+
+       default:
+            /* pass all unhandled messages to DefWindowProc */
+            lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+        break;
+    }
+
+    /* return 1 if handled message, 0 if not */
+    return lRet;
+}
+
+
+/*
+=================
+VID_NumModes
+=================
+*/
+int VID_NumModes (void)
+{
+       return nummodes;
+}
+
+       
+/*
+=================
+VID_GetModePtr
+=================
+*/
+vmode_t *VID_GetModePtr (int modenum)
+{
+
+       if ((modenum >= 0) && (modenum < nummodes))
+               return &modelist[modenum];
+       else
+               return &badmode;
+}
+
+
+/*
+=================
+VID_GetModeDescription
+=================
+*/
+char *VID_GetModeDescription (int mode)
+{
+       char            *pinfo;
+       vmode_t         *pv;
+       static char     temp[100];
+
+       if ((mode < 0) || (mode >= nummodes))
+               return NULL;
+
+       if (!leavecurrentmode)
+       {
+               pv = VID_GetModePtr (mode);
+               pinfo = pv->modedesc;
+       }
+       else
+       {
+               sprintf (temp, "Desktop resolution (%dx%d)",
+                                modelist[MODE_FULLSCREEN_DEFAULT].width,
+                                modelist[MODE_FULLSCREEN_DEFAULT].height);
+               pinfo = temp;
+       }
+
+       return pinfo;
+}
+
+
+// KJB: Added this to return the mode driver name in description for console
+
+char *VID_GetExtModeDescription (int mode)
+{
+       static char     pinfo[40];
+       vmode_t         *pv;
+
+       if ((mode < 0) || (mode >= nummodes))
+               return NULL;
+
+       pv = VID_GetModePtr (mode);
+       if (modelist[mode].type == MS_FULLDIB)
+       {
+               if (!leavecurrentmode)
+               {
+                       sprintf(pinfo,"%s fullscreen", pv->modedesc);
+               }
+               else
+               {
+                       sprintf (pinfo, "Desktop resolution (%dx%d)",
+                                        modelist[MODE_FULLSCREEN_DEFAULT].width,
+                                        modelist[MODE_FULLSCREEN_DEFAULT].height);
+               }
+       }
+       else
+       {
+               if (modestate == MS_WINDOWED)
+                       sprintf(pinfo, "%s windowed", pv->modedesc);
+               else
+                       sprintf(pinfo, "windowed");
+       }
+
+       return pinfo;
+}
+
+
+/*
+=================
+VID_DescribeCurrentMode_f
+=================
+*/
+void VID_DescribeCurrentMode_f (void)
+{
+       Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum));
+}
+
+
+/*
+=================
+VID_NumModes_f
+=================
+*/
+void VID_NumModes_f (void)
+{
+
+       if (nummodes == 1)
+               Con_Printf ("%d video mode is available\n", nummodes);
+       else
+               Con_Printf ("%d video modes are available\n", nummodes);
+}
+
+
+/*
+=================
+VID_DescribeMode_f
+=================
+*/
+void VID_DescribeMode_f (void)
+{
+       int             t, modenum;
+       
+       modenum = atoi (Cmd_Argv(1));
+
+       t = leavecurrentmode;
+       leavecurrentmode = 0;
+
+       Con_Printf ("%s\n", VID_GetExtModeDescription (modenum));
+
+       leavecurrentmode = t;
+}
+
+
+/*
+=================
+VID_DescribeModes_f
+=================
+*/
+void VID_DescribeModes_f (void)
+{
+       int                     i, lnummodes, t;
+       char            *pinfo;
+       vmode_t         *pv;
+
+       lnummodes = VID_NumModes ();
+
+       t = leavecurrentmode;
+       leavecurrentmode = 0;
+
+       for (i=1 ; i<lnummodes ; i++)
+       {
+               pv = VID_GetModePtr (i);
+               pinfo = VID_GetExtModeDescription (i);
+               Con_Printf ("%2d: %s\n", i, pinfo);
+       }
+
+       leavecurrentmode = t;
+}
+
+
+void VID_InitDIB (HINSTANCE hInstance)
+{
+       WNDCLASS                wc;
+
+       /* Register the frame class */
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)MainWndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+       wc.hbrBackground = NULL;
+    wc.lpszMenuName  = 0;
+#ifdef NEHAHRA
+    wc.lpszClassName = "DPNehahraQuake";
+#else
+    wc.lpszClassName = "DarkPlacesQuake";
+#endif
+
+    if (!RegisterClass (&wc) )
+               Sys_Error ("Couldn't register window class");
+
+       modelist[0].type = MS_WINDOWED;
+
+       if (COM_CheckParm("-width"))
+               modelist[0].width = atoi(com_argv[COM_CheckParm("-width")+1]);
+       else
+               modelist[0].width = 640;
+
+       if (modelist[0].width < 320)
+               modelist[0].width = 320;
+
+       if (COM_CheckParm("-height"))
+               modelist[0].height= atoi(com_argv[COM_CheckParm("-height")+1]);
+       else
+               modelist[0].height = modelist[0].width * 240/320;
+
+       if (modelist[0].height < 240)
+               modelist[0].height = 240;
+
+       sprintf (modelist[0].modedesc, "%dx%d",
+                        modelist[0].width, modelist[0].height);
+
+       modelist[0].modenum = MODE_WINDOWED;
+       modelist[0].dib = 1;
+       modelist[0].fullscreen = 0;
+       modelist[0].halfscreen = 0;
+       modelist[0].bpp = 0;
+
+       nummodes = 1;
+}
+
+
+/*
+=================
+VID_InitFullDIB
+=================
+*/
+void VID_InitFullDIB (HINSTANCE hInstance)
+{
+       DEVMODE devmode;
+       int             i, modenum, originalnummodes, existingmode, numlowresmodes;
+       int             j, bpp, done;
+       BOOL    stat;
+
+// enumerate >8 bpp modes
+       originalnummodes = nummodes;
+       modenum = 0;
+
+       do
+       {
+               stat = EnumDisplaySettings (NULL, modenum, &devmode);
+
+               if ((devmode.dmBitsPerPel >= 15) &&
+                       (devmode.dmPelsWidth <= MAXWIDTH) &&
+                       (devmode.dmPelsHeight <= MAXHEIGHT) &&
+                       (nummodes < MAX_MODE_LIST))
+               {
+                       devmode.dmFields = DM_BITSPERPEL |
+                                                          DM_PELSWIDTH |
+                                                          DM_PELSHEIGHT;
+
+                       if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) ==
+                                       DISP_CHANGE_SUCCESSFUL)
+                       {
+                               modelist[nummodes].type = MS_FULLDIB;
+                               modelist[nummodes].width = devmode.dmPelsWidth;
+                               modelist[nummodes].height = devmode.dmPelsHeight;
+                               modelist[nummodes].modenum = 0;
+                               modelist[nummodes].halfscreen = 0;
+                               modelist[nummodes].dib = 1;
+                               modelist[nummodes].fullscreen = 1;
+                               modelist[nummodes].bpp = devmode.dmBitsPerPel;
+                               sprintf (modelist[nummodes].modedesc, "%dx%dx%d",
+                                                devmode.dmPelsWidth, devmode.dmPelsHeight,
+                                                devmode.dmBitsPerPel);
+
+                       // if the width is more than twice the height, reduce it by half because this
+                       // is probably a dual-screen monitor
+                               if (!COM_CheckParm("-noadjustaspect"))
+                               {
+                                       if (modelist[nummodes].width > (modelist[nummodes].height << 1))
+                                       {
+                                               modelist[nummodes].width >>= 1;
+                                               modelist[nummodes].halfscreen = 1;
+                                               sprintf (modelist[nummodes].modedesc, "%dx%dx%d",
+                                                                modelist[nummodes].width,
+                                                                modelist[nummodes].height,
+                                                                modelist[nummodes].bpp);
+                                       }
+                               }
+
+                               for (i=originalnummodes, existingmode = 0 ; i<nummodes ; i++)
+                               {
+                                       if ((modelist[nummodes].width == modelist[i].width)   &&
+                                               (modelist[nummodes].height == modelist[i].height) &&
+                                               (modelist[nummodes].bpp == modelist[i].bpp))
+                                       {
+                                               existingmode = 1;
+                                               break;
+                                       }
+                               }
+
+                               if (!existingmode)
+                               {
+                                       nummodes++;
+                               }
+                       }
+               }
+
+               modenum++;
+       } while (stat);
+
+// see if there are any low-res modes that aren't being reported
+       numlowresmodes = sizeof(lowresmodes) / sizeof(lowresmodes[0]);
+       bpp = 16;
+       done = 0;
+
+       do
+       {
+               for (j=0 ; (j<numlowresmodes) && (nummodes < MAX_MODE_LIST) ; j++)
+               {
+                       devmode.dmBitsPerPel = bpp;
+                       devmode.dmPelsWidth = lowresmodes[j].width;
+                       devmode.dmPelsHeight = lowresmodes[j].height;
+                       devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+
+                       if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) ==
+                                       DISP_CHANGE_SUCCESSFUL)
+                       {
+                               modelist[nummodes].type = MS_FULLDIB;
+                               modelist[nummodes].width = devmode.dmPelsWidth;
+                               modelist[nummodes].height = devmode.dmPelsHeight;
+                               modelist[nummodes].modenum = 0;
+                               modelist[nummodes].halfscreen = 0;
+                               modelist[nummodes].dib = 1;
+                               modelist[nummodes].fullscreen = 1;
+                               modelist[nummodes].bpp = devmode.dmBitsPerPel;
+                               sprintf (modelist[nummodes].modedesc, "%dx%dx%d",
+                                                devmode.dmPelsWidth, devmode.dmPelsHeight,
+                                                devmode.dmBitsPerPel);
+
+                               for (i=originalnummodes, existingmode = 0 ; i<nummodes ; i++)
+                               {
+                                       if ((modelist[nummodes].width == modelist[i].width)   &&
+                                               (modelist[nummodes].height == modelist[i].height) &&
+                                               (modelist[nummodes].bpp == modelist[i].bpp))
+                                       {
+                                               existingmode = 1;
+                                               break;
+                                       }
+                               }
+
+                               if (!existingmode)
+                               {
+                                       nummodes++;
+                               }
+                       }
+               }
+               switch (bpp)
+               {
+                       case 16:
+                               bpp = 32;
+                               break;
+
+                       case 32:
+                               bpp = 24;
+                               break;
+
+                       case 24:
+                               done = 1;
+                               break;
+               }
+       } while (!done);
+
+       if (nummodes == originalnummodes)
+               Con_SafePrintf ("No fullscreen DIB modes found\n");
+}
+
+qboolean VID_Is8bit()
+{
+       return is8bit;
+}
+
+#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB
+
+void VID_Init8bitPalette() 
+{
+       // Check for 8bit Extensions and initialize them.
+       int i;
+       char thePalette[256*3];
+       char *oldPalette, *newPalette;
+       // LordHavoc: 8bit texture support broke many things...  it now must be specifically stated on the commandline (-no8bit became -8bit)
+    if (!COM_CheckParm("-8bit"))
+               return;
+    if (strstr(gl_extensions, "GL_EXT_shared_texture_palette"))
+               return;
+    if (!(glColorTableEXT = (void *)wglGetProcAddress("glColorTableEXT")))
+               return;
+
+       Con_SafePrintf("8-bit GL extensions enabled.\n");
+    glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
+       oldPalette = (char *) d_8to24table;
+       newPalette = thePalette;
+       for (i=0;i<256;i++)
+       {
+               *newPalette++ = *oldPalette++;
+               *newPalette++ = *oldPalette++;
+               *newPalette++ = *oldPalette++;
+               oldPalette++;
+       }
+       glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette);
+       is8bit = TRUE;
+}
+
+extern void Check_Gamma (unsigned char *pal);
+void VID_Setup15to8Palette ();
+
+/*
+===================
+VID_Init
+===================
+*/
+void   VID_Init (unsigned char *palette)
+{
+       int             i, existingmode;
+       int             basenummodes, width, height, bpp, findbpp, done;
+//     char    gldir[MAX_OSPATH];
+       HDC             hdc;
+       DEVMODE devmode;
+
+       memset(&devmode, 0, sizeof(devmode));
+
+       Cvar_RegisterVariable (&vid_mode);
+       Cvar_RegisterVariable (&vid_wait);
+       Cvar_RegisterVariable (&vid_nopageflip);
+       Cvar_RegisterVariable (&_vid_wait_override);
+       Cvar_RegisterVariable (&_vid_default_mode);
+       Cvar_RegisterVariable (&_vid_default_mode_win);
+       Cvar_RegisterVariable (&vid_config_x);
+       Cvar_RegisterVariable (&vid_config_y);
+       Cvar_RegisterVariable (&vid_stretch_by_2);
+       Cvar_RegisterVariable (&_windowed_mouse);
+
+       Cmd_AddCommand ("vid_nummodes", VID_NumModes_f);
+       Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f);
+       Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f);
+       Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f);
+
+       hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2));
+
+       InitCommonControls();
+
+       VID_InitDIB (global_hInstance);
+       basenummodes = nummodes = 1;
+
+       VID_InitFullDIB (global_hInstance);
+
+       if (COM_CheckParm("-window"))
+       {
+               hdc = GetDC (NULL);
+
+               if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
+               {
+                       Sys_Error ("Can't run in non-RGB mode");
+               }
+
+               ReleaseDC (NULL, hdc);
+
+               windowed = true;
+
+               vid_default = MODE_WINDOWED;
+       }
+       else
+       {
+               if (nummodes == 1)
+                       Sys_Error ("No RGB fullscreen modes available");
+
+               windowed = false;
+
+               if (COM_CheckParm("-mode"))
+               {
+                       vid_default = atoi(com_argv[COM_CheckParm("-mode")+1]);
+               }
+               else
+               {
+                       if (COM_CheckParm("-current"))
+                       {
+                               modelist[MODE_FULLSCREEN_DEFAULT].width =
+                                               GetSystemMetrics (SM_CXSCREEN);
+                               modelist[MODE_FULLSCREEN_DEFAULT].height =
+                                               GetSystemMetrics (SM_CYSCREEN);
+                               vid_default = MODE_FULLSCREEN_DEFAULT;
+                               leavecurrentmode = 1;
+                       }
+                       else
+                       {
+                               if (COM_CheckParm("-width"))
+                               {
+                                       width = atoi(com_argv[COM_CheckParm("-width")+1]);
+                               }
+                               else
+                               {
+                                       width = 640;
+                               }
+
+                               if (COM_CheckParm("-bpp"))
+                               {
+                                       bpp = atoi(com_argv[COM_CheckParm("-bpp")+1]);
+                                       findbpp = 0;
+                               }
+                               else
+                               {
+                                       bpp = 15;
+                                       findbpp = 1;
+                               }
+
+                               if (COM_CheckParm("-height"))
+                                       height = atoi(com_argv[COM_CheckParm("-height")+1]);
+
+                       // if they want to force it, add the specified mode to the list
+                               if (COM_CheckParm("-force") && (nummodes < MAX_MODE_LIST))
+                               {
+                                       modelist[nummodes].type = MS_FULLDIB;
+                                       modelist[nummodes].width = width;
+                                       modelist[nummodes].height = height;
+                                       modelist[nummodes].modenum = 0;
+                                       modelist[nummodes].halfscreen = 0;
+                                       modelist[nummodes].dib = 1;
+                                       modelist[nummodes].fullscreen = 1;
+                                       modelist[nummodes].bpp = bpp;
+                                       sprintf (modelist[nummodes].modedesc, "%dx%dx%d",
+                                                        devmode.dmPelsWidth, devmode.dmPelsHeight,
+                                                        devmode.dmBitsPerPel);
+
+                                       for (i=nummodes, existingmode = 0 ; i<nummodes ; i++)
+                                       {
+                                               if ((modelist[nummodes].width == modelist[i].width)   &&
+                                                       (modelist[nummodes].height == modelist[i].height) &&
+                                                       (modelist[nummodes].bpp == modelist[i].bpp))
+                                               {
+                                                       existingmode = 1;
+                                                       break;
+                                               }
+                                       }
+
+                                       if (!existingmode)
+                                       {
+                                               nummodes++;
+                                       }
+                               }
+
+                               done = 0;
+
+                               do
+                               {
+                                       if (COM_CheckParm("-height"))
+                                       {
+                                               height = atoi(com_argv[COM_CheckParm("-height")+1]);
+
+                                               for (i=1, vid_default=0 ; i<nummodes ; i++)
+                                               {
+                                                       if ((modelist[i].width == width) &&
+                                                               (modelist[i].height == height) &&
+                                                               (modelist[i].bpp == bpp))
+                                                       {
+                                                               vid_default = i;
+                                                               done = 1;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               for (i=1, vid_default=0 ; i<nummodes ; i++)
+                                               {
+                                                       if ((modelist[i].width == width) && (modelist[i].bpp == bpp))
+                                                       {
+                                                               vid_default = i;
+                                                               done = 1;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+
+                                       if (!done)
+                                       {
+                                               if (findbpp)
+                                               {
+                                                       switch (bpp)
+                                                       {
+                                                       case 15:
+                                                               bpp = 16;
+                                                               break;
+                                                       case 16:
+                                                               bpp = 32;
+                                                               break;
+                                                       case 32:
+                                                               bpp = 24;
+                                                               break;
+                                                       case 24:
+                                                               done = 1;
+                                                               break;
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       done = 1;
+                                               }
+                                       }
+                               }
+                               while (!done);
+
+                               if (!vid_default)
+                               {
+                                       Sys_Error ("Specified video mode not available");
+                               }
+                       }
+               }
+       }
+
+       vid_initialized = true;
+
+       if ((i = COM_CheckParm("-conwidth")) != 0)
+               vid.conwidth = atoi(com_argv[i+1]);
+       else
+               vid.conwidth = 640;
+
+       vid.conwidth &= 0xfff8; // make it a multiple of eight
+
+       if (vid.conwidth < 320)
+               vid.conwidth = 320;
+
+       // pick a conheight that matches with correct aspect
+       vid.conheight = vid.conwidth*3 / 4;
+
+       if ((i = COM_CheckParm("-conheight")) != 0)
+               vid.conheight = atoi(com_argv[i+1]);
+       if (vid.conheight < 200)
+               vid.conheight = 200;
+
+       vid.maxwarpwidth = WARP_WIDTH;
+       vid.maxwarpheight = WARP_HEIGHT;
+       vid.colormap = host_colormap;
+       vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
+
+       DestroyWindow (hwnd_dialog);
+
+       VID_SetPalette (palette);
+
+       Check_Gamma(palette);
+
+       VID_SetMode (vid_default, palette);
+
+    maindc = GetDC(mainwindow);
+       bSetupPixelFormat(maindc);
+
+    baseRC = wglCreateContext( maindc );
+       if (!baseRC)
+               Sys_Error ("Could not initialize GL (wglCreateContext failed).\n\nMake sure you are in 65536 color mode, and try running -window.");
+    if (!wglMakeCurrent( maindc, baseRC ))
+               Sys_Error ("wglMakeCurrent failed");
+
+       GL_Init ();
+
+//     sprintf (gldir, "%s/glquake", com_gamedir);
+//     Sys_mkdir (gldir);
+
+       vid_realmode = vid_modenum;
+
+       VID_Init8bitPalette();
+
+       if (is8bit) // LordHavoc: avoid calculating 15to8 table if it won't be used
+               VID_Setup15to8Palette ();
+
+       vid_menudrawfn = VID_MenuDraw;
+       vid_menukeyfn = VID_MenuKey;
+
+       strcpy (badmode.modedesc, "Bad mode");
+       vid_canalttab = true;
+}
+
+
+//========================================================
+// Video menu stuff
+//========================================================
+
+extern void M_Menu_Options_f (void);
+extern void M_Print (int cx, int cy, char *str);
+extern void M_PrintWhite (int cx, int cy, char *str);
+extern void M_DrawCharacter (int cx, int line, int num);
+extern void M_DrawTransPic (int x, int y, qpic_t *pic);
+extern void M_DrawPic (int x, int y, qpic_t *pic);
+
+static int     vid_line, vid_wmodes;
+
+typedef struct
+{
+       int             modenum;
+       char    *desc;
+       int             iscur;
+} modedesc_t;
+
+#define MAX_COLUMN_SIZE                9
+#define MODE_AREA_HEIGHT       (MAX_COLUMN_SIZE + 2)
+#define MAX_MODEDESCS          (MAX_COLUMN_SIZE*3)
+
+static modedesc_t      modedescs[MAX_MODEDESCS];
+
+/*
+================
+VID_MenuDraw
+================
+*/
+void VID_MenuDraw (void)
+{
+       qpic_t          *p;
+       char            *ptr;
+       int                     lnummodes, i, k, column, row;
+       vmode_t         *pv;
+
+       p = Draw_CachePic ("gfx/vidmodes.lmp");
+       M_DrawPic ( (320-p->width)/2, 4, p);
+
+       vid_wmodes = 0;
+       lnummodes = VID_NumModes ();
+       
+       for (i=1 ; (i<lnummodes) && (vid_wmodes < MAX_MODEDESCS) ; i++)
+       {
+               ptr = VID_GetModeDescription (i);
+               pv = VID_GetModePtr (i);
+
+               k = vid_wmodes;
+
+               modedescs[k].modenum = i;
+               modedescs[k].desc = ptr;
+               modedescs[k].iscur = 0;
+
+               if (i == vid_modenum)
+                       modedescs[k].iscur = 1;
+
+               vid_wmodes++;
+
+       }
+
+       if (vid_wmodes > 0)
+       {
+               M_Print (2*8, 36+0*8, "Fullscreen Modes (WIDTHxHEIGHTxBPP)");
+
+               column = 8;
+               row = 36+2*8;
+
+               for (i=0 ; i<vid_wmodes ; i++)
+               {
+                       if (modedescs[i].iscur)
+                               M_PrintWhite (column, row, modedescs[i].desc);
+                       else
+                               M_Print (column, row, modedescs[i].desc);
+
+                       column += 13*8;
+
+                       if ((i % VID_ROW_SIZE) == (VID_ROW_SIZE - 1))
+                       {
+                               column = 8;
+                               row += 8;
+                       }
+               }
+       }
+
+       M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*2,
+                        "Video modes must be set from the");
+       M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*3,
+                        "command line with -width <width>");
+       M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*4,
+                        "and -bpp <bits-per-pixel>");
+       M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6,
+                        "Select windowed mode with -window");
+}
+
+
+/*
+================
+VID_MenuKey
+================
+*/
+void VID_MenuKey (int key)
+{
+       switch (key)
+       {
+       case K_ESCAPE:
+               S_LocalSound ("misc/menu1.wav");
+               M_Menu_Options_f ();
+               break;
+
+       default:
+               break;
+       }
+}
diff --git a/view.c b/view.c
new file mode 100644 (file)
index 0000000..1780fef
--- /dev/null
+++ b/view.c
@@ -0,0 +1,916 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// view.c -- player eye positioning
+
+#include "quakedef.h"
+
+/*
+
+The view is allowed to move slightly from it's true position for bobbing,
+but if it exceeds 8 pixels linear distance (spherical, not box), the list of
+entities sent from the server may not include everything in the pvs, especially
+when crossing a water boudnary.
+
+*/
+
+cvar_t scr_ofsx = {"scr_ofsx","0", false};
+cvar_t scr_ofsy = {"scr_ofsy","0", false};
+cvar_t scr_ofsz = {"scr_ofsz","0", false};
+
+cvar_t cl_rollspeed = {"cl_rollspeed", "200"};
+cvar_t cl_rollangle = {"cl_rollangle", "2.0"};
+
+cvar_t cl_bob = {"cl_bob","0.02", false};
+cvar_t cl_bobcycle = {"cl_bobcycle","0.6", false};
+cvar_t cl_bobup = {"cl_bobup","0.5", false};
+
+cvar_t v_kicktime = {"v_kicktime", "0.5", false};
+cvar_t v_kickroll = {"v_kickroll", "0.6", false};
+cvar_t v_kickpitch = {"v_kickpitch", "0.6", false};
+
+cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", false};
+cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", false};
+cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", false};
+cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", false};
+cvar_t v_iroll_level = {"v_iroll_level", "0.1", false};
+cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", false};
+
+cvar_t v_idlescale = {"v_idlescale", "0", false};
+
+cvar_t crosshair = {"crosshair", "0", true};
+cvar_t cl_crossx = {"cl_crossx", "0", false};
+cvar_t cl_crossy = {"cl_crossy", "0", false};
+
+cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100", false};
+
+float  v_dmg_time, v_dmg_roll, v_dmg_pitch;
+
+extern int                     in_forward, in_forward2, in_back;
+
+
+/*
+===============
+V_CalcRoll
+
+Used by view and sv_user
+===============
+*/
+vec3_t forward, right, up;
+
+float V_CalcRoll (vec3_t angles, vec3_t velocity)
+{
+       float   sign;
+       float   side;
+       float   value;
+       
+       AngleVectors (angles, forward, right, up);
+       side = DotProduct (velocity, right);
+       sign = side < 0 ? -1 : 1;
+       side = fabs(side);
+       
+       value = cl_rollangle.value;
+//     if (cl.inwater)
+//             value *= 6;
+
+       if (side < cl_rollspeed.value)
+               side = side * value / cl_rollspeed.value;
+       else
+               side = value;
+       
+       return side*sign;
+       
+}
+
+
+/*
+===============
+V_CalcBob
+
+===============
+*/
+float V_CalcBob (void)
+{
+       float   bob;
+       float   cycle;
+       
+       cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value;
+       cycle /= cl_bobcycle.value;
+       if (cycle < cl_bobup.value)
+               cycle = M_PI * cycle / cl_bobup.value;
+       else
+               cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
+
+// bob is proportional to velocity in the xy plane
+// (don't count Z, or jumping messes it up)
+
+       bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
+//Con_Printf ("speed: %5.1f\n", Length(cl.velocity));
+       bob = bob*0.3 + bob*0.7*sin(cycle);
+       if (bob > 4)
+               bob = 4;
+       else if (bob < -7)
+               bob = -7;
+       return bob;
+       
+}
+
+
+//=============================================================================
+
+
+cvar_t v_centermove = {"v_centermove", "0.15", false};
+cvar_t v_centerspeed = {"v_centerspeed","500"};
+
+
+void V_StartPitchDrift (void)
+{
+#if 1
+       if (cl.laststop == cl.time)
+       {
+               return;         // something else is keeping it from drifting
+       }
+#endif
+       if (cl.nodrift || !cl.pitchvel)
+       {
+               cl.pitchvel = v_centerspeed.value;
+               cl.nodrift = false;
+               cl.driftmove = 0;
+       }
+}
+
+void V_StopPitchDrift (void)
+{
+       cl.laststop = cl.time;
+       cl.nodrift = true;
+       cl.pitchvel = 0;
+}
+
+/*
+===============
+V_DriftPitch
+
+Moves the client pitch angle towards cl.idealpitch sent by the server.
+
+If the user is adjusting pitch manually, either with lookup/lookdown,
+mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
+
+Drifting is enabled when the center view key is hit, mlook is released and
+lookspring is non 0, or when 
+===============
+*/
+void V_DriftPitch (void)
+{
+       float           delta, move;
+
+       if (noclip_anglehack || !cl.onground || cls.demoplayback )
+       {
+               cl.driftmove = 0;
+               cl.pitchvel = 0;
+               return;
+       }
+
+// don't count small mouse motion
+       if (cl.nodrift)
+       {
+               if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
+                       cl.driftmove = 0;
+               else
+                       cl.driftmove += host_frametime;
+       
+               if ( cl.driftmove > v_centermove.value)
+               {
+                       V_StartPitchDrift ();
+               }
+               return;
+       }
+       
+       delta = cl.idealpitch - cl.viewangles[PITCH];
+
+       if (!delta)
+       {
+               cl.pitchvel = 0;
+               return;
+       }
+
+       move = host_frametime * cl.pitchvel;
+       cl.pitchvel += host_frametime * v_centerspeed.value;
+       
+//Con_Printf ("move: %f (%f)\n", move, host_frametime);
+
+       if (delta > 0)
+       {
+               if (move > delta)
+               {
+                       cl.pitchvel = 0;
+                       move = delta;
+               }
+               cl.viewangles[PITCH] += move;
+       }
+       else if (delta < 0)
+       {
+               if (move > -delta)
+               {
+                       cl.pitchvel = 0;
+                       move = -delta;
+               }
+               cl.viewangles[PITCH] -= move;
+       }
+}
+
+
+
+
+
+/*
+============================================================================== 
+                                               PALETTE FLASHES 
+============================================================================== 
+*/ 
+cshift_t       cshift_empty = { {130,80,50}, 0 };
+cshift_t       cshift_water = { {130,80,50}, 128 };
+cshift_t       cshift_slime = { {0,25,5}, 150 };
+cshift_t       cshift_lava = { {255,80,0}, 150 };
+
+byte           ramps[3][256];
+float          v_blend[4];             // rgba 0.0 - 1.0
+
+/*
+===============
+V_ParseDamage
+===============
+*/
+void V_ParseDamage (void)
+{
+       int             armor, blood;
+       vec3_t  from;
+       int             i;
+       vec3_t  forward, right, up;
+       entity_t        *ent;
+       float   side;
+       float   count;
+       
+       armor = MSG_ReadByte ();
+       blood = MSG_ReadByte ();
+       for (i=0 ; i<3 ; i++)
+               from[i] = MSG_ReadCoord ();
+
+       count = blood*0.5 + armor*0.5;
+       if (count < 10)
+               count = 10;
+
+       cl.faceanimtime = cl.time + 0.2;                // but sbar face into pain frame
+
+       cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
+       if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
+               cl.cshifts[CSHIFT_DAMAGE].percent = 0;
+       if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
+               cl.cshifts[CSHIFT_DAMAGE].percent = 150;
+
+       if (armor > blood)              
+       {
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
+       }
+       else if (armor)
+       {
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
+       }
+       else
+       {
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
+               cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
+       }
+
+//
+// calculate view angle kicks
+//
+       ent = &cl_entities[cl.viewentity];
+       
+       VectorSubtract (from, ent->origin, from);
+       VectorNormalize (from);
+       
+       AngleVectors (ent->angles, forward, right, up);
+
+       side = DotProduct (from, right);
+       v_dmg_roll = count*side*v_kickroll.value;
+       
+       side = DotProduct (from, forward);
+       v_dmg_pitch = count*side*v_kickpitch.value;
+
+       v_dmg_time = v_kicktime.value;
+}
+
+
+/*
+==================
+V_cshift_f
+==================
+*/
+void V_cshift_f (void)
+{
+       cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
+       cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
+       cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
+       cshift_empty.percent = atoi(Cmd_Argv(4));
+}
+
+
+/*
+==================
+V_BonusFlash_f
+
+When you run over an item, the server sends this command
+==================
+*/
+void V_BonusFlash_f (void)
+{
+       cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
+       cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
+       cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
+       cl.cshifts[CSHIFT_BONUS].percent = 50;
+}
+
+/*
+=============
+V_SetContentsColor
+
+Underwater, lava, etc each has a color shift
+=============
+*/
+void V_SetContentsColor (int contents)
+{
+       cshift_t* c;
+       c = &cl.cshifts[CSHIFT_CONTENTS]; // just to shorten the code below
+       switch (contents)
+       {
+       case CONTENTS_EMPTY:
+       case CONTENTS_SOLID:
+               //cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
+               c->destcolor[0] = cshift_empty.destcolor[0];
+               c->destcolor[1] = cshift_empty.destcolor[1];
+               c->destcolor[2] = cshift_empty.destcolor[2];
+               c->percent = cshift_empty.percent;
+               break;
+       case CONTENTS_LAVA:
+               //cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
+               c->destcolor[0] = cshift_lava.destcolor[0];
+               c->destcolor[1] = cshift_lava.destcolor[1];
+               c->destcolor[2] = cshift_lava.destcolor[2];
+               c->percent = cshift_lava.percent;
+               break;
+       case CONTENTS_SLIME:
+               //cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
+               c->destcolor[0] = cshift_slime.destcolor[0];
+               c->destcolor[1] = cshift_slime.destcolor[1];
+               c->destcolor[2] = cshift_slime.destcolor[2];
+               c->percent = cshift_slime.percent;
+               break;
+       default:
+               //cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
+               c->destcolor[0] = cshift_water.destcolor[0];
+               c->destcolor[1] = cshift_water.destcolor[1];
+               c->destcolor[2] = cshift_water.destcolor[2];
+               c->percent = cshift_water.percent;
+       }
+}
+
+/*
+=============
+V_CalcPowerupCshift
+=============
+*/
+void V_CalcPowerupCshift (void)
+{
+       if (cl.items & IT_QUAD)
+       {
+               cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
+               cl.cshifts[CSHIFT_POWERUP].percent = 30;
+       }
+       else if (cl.items & IT_SUIT)
+       {
+               cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
+               cl.cshifts[CSHIFT_POWERUP].percent = 20;
+       }
+       else if (cl.items & IT_INVISIBILITY)
+       {
+               cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
+               cl.cshifts[CSHIFT_POWERUP].percent = 100;
+       }
+       else if (cl.items & IT_INVULNERABILITY)
+       {
+               cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
+               cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
+               cl.cshifts[CSHIFT_POWERUP].percent = 30;
+       }
+       else
+               cl.cshifts[CSHIFT_POWERUP].percent = 0;
+}
+
+/*
+=============
+V_CalcBlend
+=============
+*/
+// LordHavoc: fixed V_CalcBlend
+void V_CalcBlend (void)
+{
+       float   r, g, b, a, a2;
+       int             j;
+
+       r = 0;
+       g = 0;
+       b = 0;
+       a = 0;
+
+       if (gl_cshiftpercent.value)
+       {
+               for (j=0 ; j<NUM_CSHIFTS ; j++) 
+               {
+                       a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
+
+                       if (!a2)
+                               continue;
+                       if (a2 > 1)
+                               a2 = 1;
+                       r += (cl.cshifts[j].destcolor[0]-r) * a2;
+                       g += (cl.cshifts[j].destcolor[1]-g) * a2;
+                       b += (cl.cshifts[j].destcolor[2]-b) * a2;
+                       a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
+               }
+               // saturate color (to avoid blending in black)
+               if (a)
+               {
+                       a2 = 1 / a;
+                       r *= a2;
+                       g *= a2;
+                       b *= a2;
+               }
+       }
+
+       v_blend[0] = bound(0, r * (1.0/255.0), 1);
+       v_blend[1] = bound(0, g * (1.0/255.0), 1);
+       v_blend[2] = bound(0, b * (1.0/255.0), 1);
+       v_blend[3] = bound(0, a              , 1);
+}
+
+/*
+=============
+V_UpdatePalette
+=============
+*/
+void V_UpdatePalette (void)
+{
+       int             i, j;
+       qboolean        new;
+
+       V_CalcPowerupCshift ();
+       
+       new = false;
+       
+       for (i=0 ; i<NUM_CSHIFTS ; i++)
+       {
+               if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
+               {
+                       new = true;
+                       cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
+               }
+               for (j=0 ; j<3 ; j++)
+                       if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
+                       {
+                               new = true;
+                               cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
+                       }
+       }
+       
+// drop the damage value
+       cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
+       if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
+               cl.cshifts[CSHIFT_DAMAGE].percent = 0;
+
+// drop the bonus value
+       cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
+       if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
+               cl.cshifts[CSHIFT_BONUS].percent = 0;
+
+       if (!new)
+               return;
+
+       V_CalcBlend ();
+}
+
+/* 
+============================================================================== 
+                                               VIEW RENDERING 
+============================================================================== 
+*/ 
+
+float angledelta (float a)
+{
+       a = anglemod(a);
+       if (a > 180)
+               a -= 360;
+       return a;
+}
+
+/*
+==================
+CalcGunAngle
+==================
+*/
+void CalcGunAngle (void)
+{      
+       float   yaw, pitch, move;
+       static float oldyaw = 0;
+       static float oldpitch = 0;
+       
+       yaw = r_refdef.viewangles[YAW];
+       pitch = -r_refdef.viewangles[PITCH];
+
+       yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
+       if (yaw > 10)
+               yaw = 10;
+       if (yaw < -10)
+               yaw = -10;
+       pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
+       if (pitch > 10)
+               pitch = 10;
+       if (pitch < -10)
+               pitch = -10;
+       move = host_frametime*20;
+       if (yaw > oldyaw)
+       {
+               if (oldyaw + move < yaw)
+                       yaw = oldyaw + move;
+       }
+       else
+       {
+               if (oldyaw - move > yaw)
+                       yaw = oldyaw - move;
+       }
+       
+       if (pitch > oldpitch)
+       {
+               if (oldpitch + move < pitch)
+                       pitch = oldpitch + move;
+       }
+       else
+       {
+               if (oldpitch - move > pitch)
+                       pitch = oldpitch - move;
+       }
+       
+       oldyaw = yaw;
+       oldpitch = pitch;
+
+       cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
+       cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
+
+       cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
+       cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
+       cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
+}
+
+/*
+==============
+V_BoundOffsets
+==============
+*/
+void V_BoundOffsets (void)
+{
+       entity_t        *ent;
+       
+       ent = &cl_entities[cl.viewentity];
+
+// absolutely bound refresh reletive to entity clipping hull
+// so the view can never be inside a solid wall
+
+       if (r_refdef.vieworg[0] < ent->origin[0] - 14)
+               r_refdef.vieworg[0] = ent->origin[0] - 14;
+       else if (r_refdef.vieworg[0] > ent->origin[0] + 14)
+               r_refdef.vieworg[0] = ent->origin[0] + 14;
+       if (r_refdef.vieworg[1] < ent->origin[1] - 14)
+               r_refdef.vieworg[1] = ent->origin[1] - 14;
+       else if (r_refdef.vieworg[1] > ent->origin[1] + 14)
+               r_refdef.vieworg[1] = ent->origin[1] + 14;
+       if (r_refdef.vieworg[2] < ent->origin[2] - 22)
+               r_refdef.vieworg[2] = ent->origin[2] - 22;
+       else if (r_refdef.vieworg[2] > ent->origin[2] + 30)
+               r_refdef.vieworg[2] = ent->origin[2] + 30;
+}
+
+/*
+==============
+V_AddIdle
+
+Idle swaying
+==============
+*/
+void V_AddIdle (void)
+{
+       r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
+       r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
+       r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
+}
+
+
+/*
+==============
+V_CalcViewRoll
+
+Roll is induced by movement and damage
+==============
+*/
+void V_CalcViewRoll (void)
+{
+       float           side;
+               
+       side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity);
+       r_refdef.viewangles[ROLL] += side;
+
+       if (v_dmg_time > 0)
+       {
+               r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
+               r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
+               v_dmg_time -= host_frametime;
+       }
+
+       if (cl.stats[STAT_HEALTH] <= 0)
+       {
+               r_refdef.viewangles[ROLL] = 80; // dead view angle
+               return;
+       }
+
+}
+
+
+/*
+==================
+V_CalcIntermissionRefdef
+
+==================
+*/
+void V_CalcIntermissionRefdef (void)
+{
+       entity_t        *ent, *view;
+       float           old;
+
+// ent is the player model (visible when out of body)
+       ent = &cl_entities[cl.viewentity];
+// view is the weapon model (only visible from inside body)
+       view = &cl.viewent;
+
+       VectorCopy (ent->origin, r_refdef.vieworg);
+       VectorCopy (ent->angles, r_refdef.viewangles);
+       view->model = NULL;
+
+// allways idle in intermission
+       old = v_idlescale.value;
+       v_idlescale.value = 1;
+       V_AddIdle ();
+       v_idlescale.value = old;
+}
+
+/*
+==================
+V_CalcRefdef
+
+==================
+*/
+void V_CalcRefdef (void)
+{
+       entity_t        *ent, *view;
+       int                     i;
+       vec3_t          forward, right, up;
+       vec3_t          angles;
+       float           bob;
+       static float oldz = 0;
+
+       V_DriftPitch ();
+
+// ent is the player model (visible when out of body)
+       ent = &cl_entities[cl.viewentity];
+// view is the weapon model (only visible from inside body)
+       view = &cl.viewent;
+       
+
+// transform the view offset by the model's matrix to get the offset from
+// model origin for the view
+       ent->angles[YAW] = cl.viewangles[YAW];  // the model should face the view dir
+       ent->angles[PITCH] = -cl.viewangles[PITCH];     // the model should face the view dir
+                                                                               
+       
+       bob = V_CalcBob ();
+       
+// refresh position
+       VectorCopy (ent->origin, r_refdef.vieworg);
+       r_refdef.vieworg[2] += cl.viewheight + bob;
+
+// never let it sit exactly on a node line, because a water plane can
+// dissapear when viewed with the eye exactly on it.
+// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
+       r_refdef.vieworg[0] += 1.0/32;
+       r_refdef.vieworg[1] += 1.0/32;
+       r_refdef.vieworg[2] += 1.0/32;
+
+       VectorCopy (cl.viewangles, r_refdef.viewangles);
+       V_CalcViewRoll ();
+       V_AddIdle ();
+
+// offsets
+       angles[PITCH] = -ent->angles[PITCH];    // because entity pitches are
+                                                                                       //  actually backward
+       angles[YAW] = ent->angles[YAW];
+       angles[ROLL] = ent->angles[ROLL];
+
+       AngleVectors (angles, forward, right, up);
+
+       for (i=0 ; i<3 ; i++)
+               r_refdef.vieworg[i] += scr_ofsx.value*forward[i]
+                       + scr_ofsy.value*right[i]
+                       + scr_ofsz.value*up[i];
+       
+       
+       V_BoundOffsets ();
+               
+// set up gun position
+       VectorCopy (cl.viewangles, view->angles);
+       
+       CalcGunAngle ();
+
+       VectorCopy (ent->origin, view->origin);
+       view->origin[2] += cl.viewheight;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               view->origin[i] += forward[i]*bob*0.4;
+//             view->origin[i] += right[i]*bob*0.4;
+//             view->origin[i] += up[i]*bob*0.8;
+       }
+       view->origin[2] += bob;
+
+// fudge position around to keep amount of weapon visible
+// roughly equal with different FOV
+
+#if 0
+       if (cl.model_precache[cl.stats[STAT_WEAPON]] && strcmp (cl.model_precache[cl.stats[STAT_WEAPON]]->name,  "progs/v_shot2.mdl"))
+#endif
+// LordHavoc: everyone hates the gun moving around
+/*
+       if (scr_viewsize.value == 110)
+               view->origin[2] += 1;
+       else if (scr_viewsize.value == 100)
+               view->origin[2] += 2;
+       else if (scr_viewsize.value == 90)
+               view->origin[2] += 1;
+       else if (scr_viewsize.value == 80)
+               view->origin[2] += 0.5;
+*/
+
+       view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
+       view->frame = cl.stats[STAT_WEAPONFRAME];
+       view->colormap = vid.colormap;
+
+// set up the refresh position
+       VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
+
+// smooth out stair step ups
+if (cl.onground && ent->origin[2] - oldz > 0)
+{
+       float steptime;
+       
+       steptime = cl.time - cl.oldtime;
+       if (steptime < 0)
+//FIXME                I_Error ("steptime < 0");
+               steptime = 0;
+
+       oldz += steptime * 80;
+       if (oldz > ent->origin[2])
+               oldz = ent->origin[2];
+       if (ent->origin[2] - oldz > 12)
+               oldz = ent->origin[2] - 12;
+       r_refdef.vieworg[2] += oldz - ent->origin[2];
+       view->origin[2] += oldz - ent->origin[2];
+}
+else
+       oldz = ent->origin[2];
+
+       if (chase_active.value)
+               Chase_Update ();
+}
+
+/*
+==================
+V_RenderView
+
+The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
+the entity origin, so any view position inside that will be valid
+==================
+*/
+extern vrect_t scr_vrect;
+
+void V_RenderView (void)
+{
+       if (con_forcedup)
+               return;
+
+// don't allow cheats in multiplayer
+       if (cl.maxclients > 1)
+       {
+               Cvar_Set ("scr_ofsx", "0");
+               Cvar_Set ("scr_ofsy", "0");
+               Cvar_Set ("scr_ofsz", "0");
+       }
+
+       if (cl.intermission)
+       {       // intermission / finale rendering
+               V_CalcIntermissionRefdef ();    
+       }
+       else
+       {
+               if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ )
+                       V_CalcRefdef ();
+       }
+
+       R_PushDlights ();
+
+       R_RenderView ();
+}
+
+//============================================================================
+
+/*
+=============
+V_Init
+=============
+*/
+void V_Init (void)
+{
+       Cmd_AddCommand ("v_cshift", V_cshift_f);        
+       Cmd_AddCommand ("bf", V_BonusFlash_f);
+       Cmd_AddCommand ("centerview", V_StartPitchDrift);
+
+       Cvar_RegisterVariable (&v_centermove);
+       Cvar_RegisterVariable (&v_centerspeed);
+
+       Cvar_RegisterVariable (&v_iyaw_cycle);
+       Cvar_RegisterVariable (&v_iroll_cycle);
+       Cvar_RegisterVariable (&v_ipitch_cycle);
+       Cvar_RegisterVariable (&v_iyaw_level);
+       Cvar_RegisterVariable (&v_iroll_level);
+       Cvar_RegisterVariable (&v_ipitch_level);
+
+       Cvar_RegisterVariable (&v_idlescale);
+       Cvar_RegisterVariable (&crosshair);
+       Cvar_RegisterVariable (&cl_crossx);
+       Cvar_RegisterVariable (&cl_crossy);
+       Cvar_RegisterVariable (&gl_cshiftpercent);
+
+       Cvar_RegisterVariable (&scr_ofsx);
+       Cvar_RegisterVariable (&scr_ofsy);
+       Cvar_RegisterVariable (&scr_ofsz);
+       Cvar_RegisterVariable (&cl_rollspeed);
+       Cvar_RegisterVariable (&cl_rollangle);
+       Cvar_RegisterVariable (&cl_bob);
+       Cvar_RegisterVariable (&cl_bobcycle);
+       Cvar_RegisterVariable (&cl_bobup);
+
+       Cvar_RegisterVariable (&v_kicktime);
+       Cvar_RegisterVariable (&v_kickroll);
+       Cvar_RegisterVariable (&v_kickpitch);   
+}
+
+
diff --git a/view.h b/view.h
new file mode 100644 (file)
index 0000000..985bc5f
--- /dev/null
+++ b/view.h
@@ -0,0 +1,31 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// view.h
+
+extern float v_blend[4];
+
+extern cvar_t brightness;
+
+
+void V_Init (void);
+void V_RenderView (void);
+float V_CalcRoll (vec3_t angles, vec3_t velocity);
+void V_UpdatePalette (void);
+
diff --git a/wad.c b/wad.c
new file mode 100644 (file)
index 0000000..48c5811
--- /dev/null
+++ b/wad.c
@@ -0,0 +1,380 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// wad.c
+
+#include "quakedef.h"
+
+int                    wad_numlumps;
+lumpinfo_t     *wad_lumps;
+byte           *wad_base;
+
+void SwapPic (qpic_t *pic);
+
+/*
+==================
+W_CleanupName
+
+Lowercases name and pads with spaces and a terminating 0 to the length of
+lumpinfo_t->name.
+Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time
+Space padding is so names can be printed nicely in tables.
+Can safely be performed in place.
+==================
+*/
+void W_CleanupName (char *in, char *out)
+{
+       int             i;
+       int             c;
+       
+       for (i=0 ; i<16 ; i++ )
+       {
+               c = in[i];
+               if (!c)
+                       break;
+                       
+               if (c >= 'A' && c <= 'Z')
+                       c += ('a' - 'A');
+               out[i] = c;
+       }
+       
+       for ( ; i< 16 ; i++ )
+               out[i] = 0;
+}
+
+
+
+/*
+====================
+W_LoadWadFile
+====================
+*/
+void W_LoadWadFile (char *filename)
+{
+       lumpinfo_t              *lump_p;
+       wadinfo_t               *header;
+       unsigned                i;
+       int                             infotableofs;
+       
+       wad_base = COM_LoadHunkFile (filename, false);
+       if (!wad_base)
+               Sys_Error ("W_LoadWadFile: couldn't load %s", filename);
+
+       header = (wadinfo_t *)wad_base;
+       
+       if (header->identification[0] != 'W'
+       || header->identification[1] != 'A'
+       || header->identification[2] != 'D'
+       || header->identification[3] != '2')
+               Sys_Error ("Wad file %s doesn't have WAD2 id\n",filename);
+               
+       wad_numlumps = LittleLong(header->numlumps);
+       infotableofs = LittleLong(header->infotableofs);
+       wad_lumps = (lumpinfo_t *)(wad_base + infotableofs);
+       
+       for (i=0, lump_p = wad_lumps ; i<wad_numlumps ; i++,lump_p++)
+       {
+               lump_p->filepos = LittleLong(lump_p->filepos);
+               lump_p->size = LittleLong(lump_p->size);
+               W_CleanupName (lump_p->name, lump_p->name);
+               if (lump_p->type == TYP_QPIC)
+                       SwapPic ( (qpic_t *)(wad_base + lump_p->filepos));
+       }
+}
+
+
+/*
+=============
+W_GetLumpinfo
+=============
+*/
+lumpinfo_t     *W_GetLumpinfo (char *name)
+{
+       int             i;
+       lumpinfo_t      *lump_p;
+       char    clean[16];
+       
+       W_CleanupName (name, clean);
+       
+       for (lump_p=wad_lumps, i=0 ; i<wad_numlumps ; i++,lump_p++)
+       {
+               if (!strcmp(clean, lump_p->name))
+                       return lump_p;
+       }
+       
+       Sys_Error ("W_GetLumpinfo: %s not found", name);
+       return NULL;
+}
+
+void *W_GetLumpName (char *name)
+{
+       lumpinfo_t      *lump;
+       
+       lump = W_GetLumpinfo (name);
+       
+       return (void *)(wad_base + lump->filepos);
+}
+
+void *W_GetLumpNum (int num)
+{
+       lumpinfo_t      *lump;
+       
+       if (num < 0 || num > wad_numlumps)
+               Sys_Error ("W_GetLumpNum: bad number: %i", num);
+               
+       lump = wad_lumps + num;
+       
+       return (void *)(wad_base + lump->filepos);
+}
+
+/*
+=============================================================================
+
+automatic byte swapping
+
+=============================================================================
+*/
+
+void SwapPic (qpic_t *pic)
+{
+       pic->width = LittleLong(pic->width);
+       pic->height = LittleLong(pic->height);  
+}
+
+// LordHavoc: added alternate WAD2/WAD3 system for HalfLife texture wads
+#define TEXWAD_MAXIMAGES 8192
+typedef struct
+{
+       char name[16];
+       FILE *file;
+       int position;
+       int size;
+} texwadlump_t;
+
+texwadlump_t   texwadlump[TEXWAD_MAXIMAGES];
+
+/*
+====================
+W_LoadTextureWadFile
+====================
+*/
+void W_LoadTextureWadFile (char *filename, int complain)
+{
+       lumpinfo_t              *lumps, *lump_p;
+       wadinfo_t               header;
+       unsigned                i, j;
+       int                             infotableofs;
+       FILE                    *file;
+       int                             numlumps;
+       
+       COM_FOpenFile (filename, &file, false);
+       if (!file)
+       {
+               if (complain)
+                       Con_Printf ("W_LoadTextureWadFile: couldn't find %s", filename);
+               return;
+       }
+
+       if (fread(&header, sizeof(wadinfo_t), 1, file) != 1)
+       {Con_Printf ("W_LoadTextureWadFile: unable to read wad header");return;}
+       
+       if(header.identification[0] != 'W'
+       || header.identification[1] != 'A'
+       || header.identification[2] != 'D'
+       || header.identification[3] != '3')
+       {Con_Printf ("W_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\n",filename);return;}
+
+       numlumps = LittleLong(header.numlumps);
+       if (numlumps < 1 || numlumps > TEXWAD_MAXIMAGES)
+       {Con_Printf ("W_LoadTextureWadFile: invalid number of lumps (%i)\n", numlumps);return;}
+       infotableofs = LittleLong(header.infotableofs);
+       if (fseek(file, infotableofs, SEEK_SET))
+       {Con_Printf ("W_LoadTextureWadFile: unable to seek to lump table");return;}
+       if (!(lumps = malloc(sizeof(lumpinfo_t)*numlumps)))
+       {Con_Printf ("W_LoadTextureWadFile: unable to allocate temporary memory for lump table");return;}
+
+       if (fread(lumps, sizeof(lumpinfo_t), numlumps, file) != numlumps)
+       {Con_Printf ("W_LoadTextureWadFile: unable to read lump table");return;}
+       
+       for (i=0, lump_p = lumps ; i<numlumps ; i++,lump_p++)
+       {
+               W_CleanupName (lump_p->name, lump_p->name);
+               for (j = 0;j < TEXWAD_MAXIMAGES;j++)
+               {
+                       if (texwadlump[j].name[0]) // occupied slot, check the name
+                       {
+                               if (!strcmp(lump_p->name, texwadlump[j].name)) // name match, replace old one
+                                       break;
+                       }
+                       else // empty slot
+                               break;
+               }
+               if (j >= TEXWAD_MAXIMAGES)
+                       break; // abort loading
+               W_CleanupName (lump_p->name, texwadlump[j].name);
+               texwadlump[j].file = file;
+               texwadlump[j].position = LittleLong(lump_p->filepos);
+               texwadlump[j].size = LittleLong(lump_p->disksize);
+       }
+       free(lumps);
+       // leaves the file open
+}
+
+
+byte hlpalette[768] =
+{
+       0x00,0x00,0x00,0x0F,0x0F,0x0F,0x1F,0x1F,0x1F,0x2F,0x2F,0x2F,0x3F,0x3F,0x3F,0x4B,
+       0x4B,0x4B,0x5B,0x5B,0x5B,0x6B,0x6B,0x6B,0x7B,0x7B,0x7B,0x8B,0x8B,0x8B,0x9B,0x9B,
+       0x9B,0xAB,0xAB,0xAB,0xBB,0xBB,0xBB,0xCB,0xCB,0xCB,0xDB,0xDB,0xDB,0xEB,0xEB,0xEB,
+       0x0F,0x0B,0x07,0x17,0x0F,0x0B,0x1F,0x17,0x0B,0x27,0x1B,0x0F,0x2F,0x23,0x13,0x37,
+       0x2B,0x17,0x3F,0x2F,0x17,0x4B,0x37,0x1B,0x53,0x3B,0x1B,0x5B,0x43,0x1F,0x63,0x4B,
+       0x1F,0x6B,0x53,0x1F,0x73,0x57,0x1F,0x7B,0x5F,0x23,0x83,0x67,0x23,0x8F,0x6F,0x23,
+       0x0B,0x0B,0x0F,0x13,0x13,0x1B,0x1B,0x1B,0x27,0x27,0x27,0x33,0x2F,0x2F,0x3F,0x37,
+       0x37,0x4B,0x3F,0x3F,0x57,0x47,0x47,0x67,0x4F,0x4F,0x73,0x5B,0x5B,0x7F,0x63,0x63,
+       0x8B,0x6B,0x6B,0x97,0x73,0x73,0xA3,0x7B,0x7B,0xAF,0x83,0x83,0xBB,0x8B,0x8B,0xCB,
+       0x00,0x00,0x00,0x07,0x07,0x00,0x0B,0x0B,0x00,0x13,0x13,0x00,0x1B,0x1B,0x00,0x23,
+       0x23,0x00,0x2B,0x2B,0x07,0x2F,0x2F,0x07,0x37,0x37,0x07,0x3F,0x3F,0x07,0x47,0x47,
+       0x07,0x4B,0x4B,0x0B,0x53,0x53,0x0B,0x5B,0x5B,0x0B,0x63,0x63,0x0B,0x6B,0x6B,0x0F,
+       0x07,0x00,0x00,0x0F,0x00,0x00,0x17,0x00,0x00,0x1F,0x00,0x00,0x27,0x00,0x00,0x2F,
+       0x00,0x00,0x37,0x00,0x00,0x3F,0x00,0x00,0x47,0x00,0x00,0x4F,0x00,0x00,0x57,0x00,
+       0x00,0x5F,0x00,0x00,0x67,0x00,0x00,0x6F,0x00,0x00,0x77,0x00,0x00,0x7F,0x00,0x00,
+       0x13,0x13,0x00,0x1B,0x1B,0x00,0x23,0x23,0x00,0x2F,0x2B,0x00,0x37,0x2F,0x00,0x43,
+       0x37,0x00,0x4B,0x3B,0x07,0x57,0x43,0x07,0x5F,0x47,0x07,0x6B,0x4B,0x0B,0x77,0x53,
+       0x0F,0x83,0x57,0x13,0x8B,0x5B,0x13,0x97,0x5F,0x1B,0xA3,0x63,0x1F,0xAF,0x67,0x23,
+       0x23,0x13,0x07,0x2F,0x17,0x0B,0x3B,0x1F,0x0F,0x4B,0x23,0x13,0x57,0x2B,0x17,0x63,
+       0x2F,0x1F,0x73,0x37,0x23,0x7F,0x3B,0x2B,0x8F,0x43,0x33,0x9F,0x4F,0x33,0xAF,0x63,
+       0x2F,0xBF,0x77,0x2F,0xCF,0x8F,0x2B,0xDF,0xAB,0x27,0xEF,0xCB,0x1F,0xFF,0xF3,0x1B,
+       0x0B,0x07,0x00,0x1B,0x13,0x00,0x2B,0x23,0x0F,0x37,0x2B,0x13,0x47,0x33,0x1B,0x53,
+       0x37,0x23,0x63,0x3F,0x2B,0x6F,0x47,0x33,0x7F,0x53,0x3F,0x8B,0x5F,0x47,0x9B,0x6B,
+       0x53,0xA7,0x7B,0x5F,0xB7,0x87,0x6B,0xC3,0x93,0x7B,0xD3,0xA3,0x8B,0xE3,0xB3,0x97,
+       0xAB,0x8B,0xA3,0x9F,0x7F,0x97,0x93,0x73,0x87,0x8B,0x67,0x7B,0x7F,0x5B,0x6F,0x77,
+       0x53,0x63,0x6B,0x4B,0x57,0x5F,0x3F,0x4B,0x57,0x37,0x43,0x4B,0x2F,0x37,0x43,0x27,
+       0x2F,0x37,0x1F,0x23,0x2B,0x17,0x1B,0x23,0x13,0x13,0x17,0x0B,0x0B,0x0F,0x07,0x07,
+       0xBB,0x73,0x9F,0xAF,0x6B,0x8F,0xA3,0x5F,0x83,0x97,0x57,0x77,0x8B,0x4F,0x6B,0x7F,
+       0x4B,0x5F,0x73,0x43,0x53,0x6B,0x3B,0x4B,0x5F,0x33,0x3F,0x53,0x2B,0x37,0x47,0x23,
+       0x2B,0x3B,0x1F,0x23,0x2F,0x17,0x1B,0x23,0x13,0x13,0x17,0x0B,0x0B,0x0F,0x07,0x07,
+       0xDB,0xC3,0xBB,0xCB,0xB3,0xA7,0xBF,0xA3,0x9B,0xAF,0x97,0x8B,0xA3,0x87,0x7B,0x97,
+       0x7B,0x6F,0x87,0x6F,0x5F,0x7B,0x63,0x53,0x6B,0x57,0x47,0x5F,0x4B,0x3B,0x53,0x3F,
+       0x33,0x43,0x33,0x27,0x37,0x2B,0x1F,0x27,0x1F,0x17,0x1B,0x13,0x0F,0x0F,0x0B,0x07,
+       0x6F,0x83,0x7B,0x67,0x7B,0x6F,0x5F,0x73,0x67,0x57,0x6B,0x5F,0x4F,0x63,0x57,0x47,
+       0x5B,0x4F,0x3F,0x53,0x47,0x37,0x4B,0x3F,0x2F,0x43,0x37,0x2B,0x3B,0x2F,0x23,0x33,
+       0x27,0x1F,0x2B,0x1F,0x17,0x23,0x17,0x0F,0x1B,0x13,0x0B,0x13,0x0B,0x07,0x0B,0x07,
+       0xFF,0xF3,0x1B,0xEF,0xDF,0x17,0xDB,0xCB,0x13,0xCB,0xB7,0x0F,0xBB,0xA7,0x0F,0xAB,
+       0x97,0x0B,0x9B,0x83,0x07,0x8B,0x73,0x07,0x7B,0x63,0x07,0x6B,0x53,0x00,0x5B,0x47,
+       0x00,0x4B,0x37,0x00,0x3B,0x2B,0x00,0x2B,0x1F,0x00,0x1B,0x0F,0x00,0x0B,0x07,0x00,
+       0x00,0x00,0xFF,0x0B,0x0B,0xEF,0x13,0x13,0xDF,0x1B,0x1B,0xCF,0x23,0x23,0xBF,0x2B,
+       0x2B,0xAF,0x2F,0x2F,0x9F,0x2F,0x2F,0x8F,0x2F,0x2F,0x7F,0x2F,0x2F,0x6F,0x2F,0x2F,
+       0x5F,0x2B,0x2B,0x4F,0x23,0x23,0x3F,0x1B,0x1B,0x2F,0x13,0x13,0x1F,0x0B,0x0B,0x0F,
+       0x2B,0x00,0x00,0x3B,0x00,0x00,0x4B,0x07,0x00,0x5F,0x07,0x00,0x6F,0x0F,0x00,0x7F,
+       0x17,0x07,0x93,0x1F,0x07,0xA3,0x27,0x0B,0xB7,0x33,0x0F,0xC3,0x4B,0x1B,0xCF,0x63,
+       0x2B,0xDB,0x7F,0x3B,0xE3,0x97,0x4F,0xE7,0xAB,0x5F,0xEF,0xBF,0x77,0xF7,0xD3,0x8B,
+       0xA7,0x7B,0x3B,0xB7,0x9B,0x37,0xC7,0xC3,0x37,0xE7,0xE3,0x57,0x00,0xFF,0x00,0xAB,
+       0xE7,0xFF,0xD7,0xFF,0xFF,0x67,0x00,0x00,0x8B,0x00,0x00,0xB3,0x00,0x00,0xD7,0x00,
+       0x00,0xFF,0x00,0x00,0xFF,0xF3,0x93,0xFF,0xF7,0xC7,0xFF,0xFF,0xFF,0x9F,0x5B,0x53,
+};
+
+byte *W_GetTexture(char *name, int matchwidth, int matchheight)
+{
+       int i, c, datasize;
+       short colorcount;
+       FILE *file;
+       struct
+       {
+               char name[16];
+               int width;
+               int height;
+               int ofs[4];
+       } t;
+       byte pal[256][3], *indata, *outdata, *data;
+       for (i = 0;i < TEXWAD_MAXIMAGES;i++)
+       {
+               if (texwadlump[i].name[0])
+               {
+                       if (!strcmp(name, texwadlump[i].name)) // found it
+                       {
+                               file = texwadlump[i].file;
+                               if (fseek(file, texwadlump[i].position, SEEK_SET))
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               if (fread(&t, sizeof(t), 1, file) != 1)
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               image_width = LittleLong(t.width);
+                               image_height = LittleLong(t.height);
+                               if (matchwidth && image_width != matchwidth)
+                                       continue;
+                               if (matchheight && image_height != matchheight)
+                                       continue;
+                               if (image_width & 15 || image_height & 15)
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               // allocate space for expanded image,
+                               // and load incoming image into upper area (overwritten as it expands)
+                               if (!(data = outdata = malloc(image_width*image_height*4)))
+                               {Con_Printf("W_GetTexture: out of memory");return FALSE;}
+                               indata = outdata + image_width*image_height*3;
+                               datasize = image_width*image_height*85/64;
+                               // read the image data
+                               if (fseek(file, texwadlump[i].position + sizeof(t), SEEK_SET))
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               if (fread(indata, 1, image_width*image_height, file) != image_width*image_height)
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               // read the number of colors used (always 256)
+                               if (fseek(file, texwadlump[i].position + sizeof(t) + datasize, SEEK_SET))
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               if (fread(&colorcount, 2, 1, file) != 1)
+                               {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               if (texwadlump[i].size > (datasize + sizeof(t)))
+                               {
+                                       colorcount = LittleShort(colorcount);
+                                       // sanity checking
+                                       if (colorcount < 0) colorcount = 0;
+                                       if (colorcount > 256) colorcount = 256;
+                                       // read the palette
+       //                              fseek(file, texwadlump[i].position + sizeof(t) + datasize + 2, SEEK_SET);
+                                       if (fread(&pal, 3, colorcount, file) != colorcount)
+                                       {Con_Printf("W_GetTexture: corrupt WAD3 file");return FALSE;}
+                               }
+                               else
+                                       memcpy(&pal, hlpalette, 768);
+                               // expand the image to 32bit RGBA
+                               for (i = 0;i < image_width*image_height;i++)
+                               {
+                                       c = *indata++;
+                                       if (c == 255) // transparent color
+                                       {
+                                               *outdata++ = 0;
+                                               *outdata++ = 0;
+                                               *outdata++ = 0;
+                                               *outdata++ = 0;
+                                       }
+                                       else
+                                       {
+                                               *outdata++ = pal[c][0];
+                                               *outdata++ = pal[c][1];
+                                               *outdata++ = pal[c][2];
+                                               *outdata++ = 255;
+                                       }
+                               }
+                               return data;
+                       }
+               }
+               else
+                       break;
+       }
+       image_width = image_height = 0;
+       return NULL;
+}
diff --git a/wad.h b/wad.h
new file mode 100644 (file)
index 0000000..51e6dec
--- /dev/null
+++ b/wad.h
@@ -0,0 +1,80 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// wad.h
+
+//===============
+//   TYPES
+//===============
+
+#define        CMP_NONE                0
+#define        CMP_LZSS                1
+
+#define        TYP_NONE                0
+#define        TYP_LABEL               1
+
+#define        TYP_LUMPY               64                              // 64 + grab command number
+#define        TYP_PALETTE             64
+#define        TYP_QTEX                65
+#define        TYP_QPIC                66
+#define        TYP_SOUND               67
+#define        TYP_MIPTEX              68
+
+typedef struct
+{
+       int                     width, height;
+       byte            data[4];                        // variably sized
+} qpic_t;
+
+
+
+typedef struct
+{
+       char            identification[4];              // should be WAD2 or 2DAW
+       int                     numlumps;
+       int                     infotableofs;
+} wadinfo_t;
+
+typedef struct
+{
+       int                     filepos;
+       int                     disksize;
+       int                     size;                                   // uncompressed
+       char            type;
+       char            compression;
+       char            pad1, pad2;
+       char            name[16];                               // must be null terminated
+} lumpinfo_t;
+
+extern int                     wad_numlumps;
+extern lumpinfo_t      *wad_lumps;
+extern byte            *wad_base;
+
+void   W_LoadWadFile (char *filename);
+void   W_CleanupName (char *in, char *out);
+lumpinfo_t     *W_GetLumpinfo (char *name);
+void   *W_GetLumpName (char *name);
+void   *W_GetLumpNum (int num);
+
+void SwapPic (qpic_t *pic);
+
+// LordHavoc: added alternate texture WAD2/WAD3 system for easier loading of HalfLife texture wads
+extern image_width, image_height;
+void   W_LoadTextureWadFile (char *filename, int complain);
+byte   *W_GetTexture (char *name, int matchwidth, int matchheight); // returns malloc'd image data, width and height are in image_width and image_height (yeah yeah so I'm lazy...)
diff --git a/winquake.h b/winquake.h
new file mode 100644 (file)
index 0000000..81ceeab
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// winquake.h: Win32-specific Quake header file
+
+#pragma warning( disable : 4229 )  // mgraph gets this
+
+#include <windows.h>
+#define WM_MOUSEWHEEL                   0x020A
+
+#ifndef SERVERONLY
+#include <ddraw.h>
+#include <dsound.h>
+#endif
+
+extern HINSTANCE       global_hInstance;
+extern int                     global_nCmdShow;
+
+#ifndef SERVERONLY
+
+extern LPDIRECTDRAW            lpDD;
+extern qboolean                        DDActive;
+extern LPDIRECTDRAWSURFACE     lpPrimary;
+extern LPDIRECTDRAWSURFACE     lpFrontBuffer;
+extern LPDIRECTDRAWSURFACE     lpBackBuffer;
+extern LPDIRECTDRAWPALETTE     lpDDPal;
+extern LPDIRECTSOUND pDS;
+extern LPDIRECTSOUNDBUFFER pDSBuf;
+
+extern DWORD gSndBufSize;
+//#define SNDBUFSIZE 65536
+
+void   VID_LockBuffer (void);
+void   VID_UnlockBuffer (void);
+
+#endif
+
+typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t;
+
+extern modestate_t     modestate;
+
+extern HWND                    mainwindow;
+extern qboolean                ActiveApp, Minimized;
+
+extern qboolean        WinNT;
+
+int VID_ForceUnlockedAndReturnState (void);
+void VID_ForceLockState (int lk);
+
+void IN_ShowMouse (void);
+void IN_DeactivateMouse (void);
+void IN_HideMouse (void);
+void IN_ActivateMouse (void);
+void IN_RestoreOriginalMouseState (void);
+void IN_SetQuakeMouseState (void);
+void IN_MouseEvent (int mstate);
+
+extern qboolean        winsock_lib_initialized;
+
+extern cvar_t          _windowed_mouse;
+
+extern int             window_center_x, window_center_y;
+extern RECT            window_rect;
+
+extern qboolean        mouseinitialized;
+extern HWND            hwnd_dialog;
+
+extern HANDLE  hinput, houtput;
+
+void IN_UpdateClipCursor (void);
+void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify);
+
+void S_BlockSound (void);
+void S_UnblockSound (void);
+
+void VID_SetDefaultMode (void);
+
+int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);
+int (PASCAL FAR *pWSACleanup)(void);
+int (PASCAL FAR *pWSAGetLastError)(void);
+SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol);
+int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp);
+int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname,
+                                                         const char FAR * optval, int optlen);
+int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags,
+                                                       struct sockaddr FAR *from, int FAR * fromlen);
+int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags,
+                                                 const struct sockaddr FAR *to, int tolen);
+int (PASCAL FAR *pclosesocket)(SOCKET s);
+int (PASCAL FAR *pgethostname)(char FAR * name, int namelen);
+struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name);
+struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr,
+                                                                                                 int len, int type);
+int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name,
+                                                          int FAR * namelen);
diff --git a/world.c b/world.c
new file mode 100644 (file)
index 0000000..3e2298c
--- /dev/null
+++ b/world.c
@@ -0,0 +1,998 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// world.c -- world query functions
+
+#include "quakedef.h"
+
+/*
+
+entities never clip against themselves, or their owner
+
+line of sight checks trace->crosscontent, but bullets don't
+
+*/
+
+
+typedef struct
+{
+       vec3_t          boxmins, boxmaxs;// enclose the test object along entire move
+       float           *mins, *maxs;   // size of the moving object
+       vec3_t          mins2, maxs2;   // size when clipping against mosnters
+       float           *start, *end;
+       trace_t         trace;
+       int                     type;
+       edict_t         *passedict;
+} moveclip_t;
+
+
+/*
+===============================================================================
+
+HULL BOXES
+
+===============================================================================
+*/
+
+
+static hull_t          box_hull;
+static dclipnode_t     box_clipnodes[6];
+static mplane_t        box_planes[6];
+
+/*
+===================
+SV_InitBoxHull
+
+Set up the planes and clipnodes so that the six floats of a bounding box
+can just be stored out and get a proper hull_t structure.
+===================
+*/
+void SV_InitBoxHull (void)
+{
+       int             i;
+       int             side;
+
+       box_hull.clipnodes = box_clipnodes;
+       box_hull.planes = box_planes;
+       box_hull.firstclipnode = 0;
+       box_hull.lastclipnode = 5;
+
+       for (i=0 ; i<6 ; i++)
+       {
+               box_clipnodes[i].planenum = i;
+               
+               side = i&1;
+               
+               box_clipnodes[i].children[side] = CONTENTS_EMPTY;
+               if (i != 5)
+                       box_clipnodes[i].children[side^1] = i + 1;
+               else
+                       box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
+               
+               box_planes[i].type = i>>1;
+               box_planes[i].normal[i>>1] = 1;
+       }
+       
+}
+
+
+/*
+===================
+SV_HullForBox
+
+To keep everything totally uniform, bounding boxes are turned into small
+BSP trees instead of being compared directly.
+===================
+*/
+hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs)
+{
+       box_planes[0].dist = maxs[0];
+       box_planes[1].dist = mins[0];
+       box_planes[2].dist = maxs[1];
+       box_planes[3].dist = mins[1];
+       box_planes[4].dist = maxs[2];
+       box_planes[5].dist = mins[2];
+
+       return &box_hull;
+}
+
+
+
+/*
+================
+SV_HullForEntity
+
+Returns a hull that can be used for testing or clipping an object of mins/maxs
+size.
+Offset is filled in to contain the adjustment that must be added to the
+testing object's origin to get a point to use with the returned hull.
+================
+*/
+hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset)
+{
+       model_t         *model;
+       vec3_t          size;
+       vec3_t          hullmins, hullmaxs;
+       hull_t          *hull;
+
+// decide which clipping hull to use, based on the size
+       if (ent->v.solid == SOLID_BSP)
+       {       // explicit hulls in the BSP model
+               if (ent->v.movetype != MOVETYPE_PUSH)
+                       Host_Error ("SOLID_BSP without MOVETYPE_PUSH");
+
+               model = sv.models[ (int)ent->v.modelindex ];
+
+               // LordHavoc: fixed SOLID_BSP error message
+               if (!model || model->type != mod_brush)
+               {
+                       Con_Printf ("SOLID_BSP with a non bsp model, entity dump:\n");
+                       ED_Print (ent);
+                       Host_Error ("SOLID_BSP with a non bsp model\n");
+               }
+
+               VectorSubtract (maxs, mins, size);
+               // LordHavoc: FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+               if (size[0] < 3)
+                       hull = &model->hulls[0];
+               else if (size[0] <= 32)
+                       hull = &model->hulls[1];
+               else
+                       hull = &model->hulls[2];
+
+// calculate an offset value to center the origin
+               VectorSubtract (hull->clip_mins, mins, offset);
+               VectorAdd (offset, ent->v.origin, offset);
+       }
+       else
+       {       // create a temp hull from bounding box sizes
+
+               VectorSubtract (ent->v.mins, maxs, hullmins);
+               VectorSubtract (ent->v.maxs, mins, hullmaxs);
+               hull = SV_HullForBox (hullmins, hullmaxs);
+               
+               VectorCopy (ent->v.origin, offset);
+       }
+
+
+       return hull;
+}
+
+/*
+===============================================================================
+
+ENTITY AREA CHECKING
+
+===============================================================================
+*/
+
+typedef struct areanode_s
+{
+       int             axis;           // -1 = leaf node
+       float   dist;
+       struct areanode_s       *children[2];
+       link_t  trigger_edicts;
+       link_t  solid_edicts;
+} areanode_t;
+
+#define        AREA_DEPTH      4
+#define        AREA_NODES      32
+
+static areanode_t      sv_areanodes[AREA_NODES];
+static int                     sv_numareanodes;
+
+/*
+===============
+SV_CreateAreaNode
+
+===============
+*/
+areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs)
+{
+       areanode_t      *anode;
+       vec3_t          size;
+       vec3_t          mins1, maxs1, mins2, maxs2;
+
+       anode = &sv_areanodes[sv_numareanodes];
+       sv_numareanodes++;
+
+       ClearLink (&anode->trigger_edicts);
+       ClearLink (&anode->solid_edicts);
+       
+       if (depth == AREA_DEPTH)
+       {
+               anode->axis = -1;
+               anode->children[0] = anode->children[1] = NULL;
+               return anode;
+       }
+       
+       VectorSubtract (maxs, mins, size);
+       if (size[0] > size[1])
+               anode->axis = 0;
+       else
+               anode->axis = 1;
+       
+       anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
+       VectorCopy (mins, mins1);       
+       VectorCopy (mins, mins2);       
+       VectorCopy (maxs, maxs1);       
+       VectorCopy (maxs, maxs2);       
+       
+       maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
+       
+       anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2);
+       anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1);
+
+       return anode;
+}
+
+/*
+===============
+SV_ClearWorld
+
+===============
+*/
+void SV_ClearWorld (void)
+{
+       SV_InitBoxHull ();
+       
+       memset (sv_areanodes, 0, sizeof(sv_areanodes));
+       sv_numareanodes = 0;
+       SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs);
+}
+
+
+/*
+===============
+SV_UnlinkEdict
+
+===============
+*/
+void SV_UnlinkEdict (edict_t *ent)
+{
+       if (!ent->area.prev)
+               return;         // not linked in anywhere
+       RemoveLink (&ent->area);
+       ent->area.prev = ent->area.next = NULL;
+}
+
+
+/*
+====================
+SV_TouchLinks
+====================
+*/
+void SV_TouchLinks ( edict_t *ent, areanode_t *node )
+{
+       link_t          *l, *next;
+       edict_t         *touch;
+       int                     old_self, old_other;
+
+loc0:
+// touch linked edicts
+       for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next)
+       {
+               next = l->next;
+               touch = EDICT_FROM_AREA(l);
+               if (touch == ent)
+                       continue;
+               if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER)
+                       continue;
+               if (ent->v.absmin[0] > touch->v.absmax[0]
+               || ent->v.absmin[1] > touch->v.absmax[1]
+               || ent->v.absmin[2] > touch->v.absmax[2]
+               || ent->v.absmax[0] < touch->v.absmin[0]
+               || ent->v.absmax[1] < touch->v.absmin[1]
+               || ent->v.absmax[2] < touch->v.absmin[2] )
+                       continue;
+               old_self = pr_global_struct->self;
+               old_other = pr_global_struct->other;
+
+               pr_global_struct->self = EDICT_TO_PROG(touch);
+               pr_global_struct->other = EDICT_TO_PROG(ent);
+               pr_global_struct->time = sv.time;
+               PR_ExecuteProgram (touch->v.touch);
+
+               pr_global_struct->self = old_self;
+               pr_global_struct->other = old_other;
+       }
+       
+// recurse down both sides
+       if (node->axis == -1)
+               return;
+       
+       // LordHavoc: optimized recursion
+//     if (ent->v.absmax[node->axis] > node->dist) SV_TouchLinks (ent, node->children[0]);
+//     if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks (ent, node->children[1]);
+       if (ent->v.absmax[node->axis] > node->dist)
+       {
+               if (ent->v.absmin[node->axis] < node->dist)
+                       SV_TouchLinks(ent, node->children[1]); // order reversed to reduce code
+               node = node->children[0];
+               goto loc0;
+       }
+       else
+       {
+               if (ent->v.absmin[node->axis] < node->dist)
+               {
+                       node = node->children[1];
+                       goto loc0;
+               }
+       }
+}
+
+
+/*
+===============
+SV_FindTouchedLeafs
+
+===============
+*/
+void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node)
+{
+       mplane_t        *splitplane;
+       mleaf_t         *leaf;
+       int                     sides;
+       int                     leafnum;
+
+loc0:
+       if (node->contents == CONTENTS_SOLID)
+               return;
+       
+// add an efrag if the node is a leaf
+
+       if ( node->contents < 0)
+       {
+               if (ent->num_leafs == MAX_ENT_LEAFS)
+                       return;
+
+               leaf = (mleaf_t *)node;
+               leafnum = leaf - sv.worldmodel->leafs - 1;
+
+               ent->leafnums[ent->num_leafs] = leafnum;
+               ent->num_leafs++;                       
+               return;
+       }
+       
+// NODE_MIXED
+
+       splitplane = node->plane;
+       sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane);
+       
+// recurse down the contacted sides
+       // LordHavoc: optimized recursion
+//     if (sides & 1) SV_FindTouchedLeafs (ent, node->children[0]);
+//     if (sides & 2) SV_FindTouchedLeafs (ent, node->children[1]);
+       switch (sides)
+       {
+       case 1:
+               node = node->children[0];
+               goto loc0;
+       case 2:
+               node = node->children[1];
+               goto loc0;
+       default: // 3
+               if (node->children[0]->contents != CONTENTS_SOLID)
+                       SV_FindTouchedLeafs (ent, node->children[0]);
+               node = node->children[1];
+               goto loc0;
+       }
+}
+
+/*
+===============
+SV_LinkEdict
+
+===============
+*/
+void SV_LinkEdict (edict_t *ent, qboolean touch_triggers)
+{
+       areanode_t      *node;
+
+       if (ent->area.prev)
+               SV_UnlinkEdict (ent);   // unlink from old position
+               
+       if (ent == sv.edicts)
+               return;         // don't add the world
+
+       if (ent->free)
+               return;
+
+// set the abs box
+
+// LordHavoc: enabling rotating bmodels
+       if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
+       {       // expand for rotation
+               float           max, v;
+               int                     i;
+
+               max = 0;
+               for (i=0 ; i<3 ; i++)
+               {
+                       v =fabs( ent->v.mins[i]);
+                       if (v > max)
+                               max = v;
+                       v =fabs( ent->v.maxs[i]);
+                       if (v > max)
+                               max = v;
+               }
+               for (i=0 ; i<3 ; i++)
+               {
+                       ent->v.absmin[i] = ent->v.origin[i] - max;
+                       ent->v.absmax[i] = ent->v.origin[i] + max;
+               }
+       }
+       else
+       {
+               VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);  
+               VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
+       }
+
+//
+// to make items easier to pick up and allow them to be grabbed off
+// of shelves, the abs sizes are expanded
+//
+       if ((int)ent->v.flags & FL_ITEM)
+       {
+               ent->v.absmin[0] -= 15;
+               ent->v.absmin[1] -= 15;
+               ent->v.absmax[0] += 15;
+               ent->v.absmax[1] += 15;
+       }
+       else
+       {       // because movement is clipped an epsilon away from an actual edge,
+               // we must fully check even when bounding boxes don't quite touch
+               ent->v.absmin[0] -= 1;
+               ent->v.absmin[1] -= 1;
+               ent->v.absmin[2] -= 1;
+               ent->v.absmax[0] += 1;
+               ent->v.absmax[1] += 1;
+               ent->v.absmax[2] += 1;
+       }
+       
+// link to PVS leafs
+       ent->num_leafs = 0;
+       if (ent->v.modelindex)
+               SV_FindTouchedLeafs (ent, sv.worldmodel->nodes);
+
+       if (ent->v.solid == SOLID_NOT)
+               return;
+
+// find the first node that the ent's box crosses
+       node = sv_areanodes;
+       while (1)
+       {
+               if (node->axis == -1)
+                       break;
+               if (ent->v.absmin[node->axis] > node->dist)
+                       node = node->children[0];
+               else if (ent->v.absmax[node->axis] < node->dist)
+                       node = node->children[1];
+               else
+                       break;          // crosses the node
+       }
+       
+// link it in  
+
+       if (ent->v.solid == SOLID_TRIGGER)
+               InsertLinkBefore (&ent->area, &node->trigger_edicts);
+       else
+               InsertLinkBefore (&ent->area, &node->solid_edicts);
+       
+// if touch_triggers, touch all entities at this node and descend for more
+       if (touch_triggers)
+               SV_TouchLinks ( ent, sv_areanodes );
+}
+
+
+
+/*
+===============================================================================
+
+POINT TESTING IN HULLS
+
+===============================================================================
+*/
+
+#if    !id386
+
+/*
+==================
+SV_HullPointContents
+
+==================
+*/
+int SV_HullPointContents (hull_t *hull, int num, vec3_t p)
+{
+       dclipnode_t     *node;
+       mplane_t        *plane;
+
+       while (num >= 0)
+       {
+               if (num < hull->firstclipnode || num > hull->lastclipnode)
+                       Sys_Error ("SV_HullPointContents: bad node number");
+       
+               node = hull->clipnodes + num;
+               plane = hull->planes + node->planenum;
+               
+// LordHavoc: optimized this slightly (probably no actual impact due to compiler optimization)
+               if (plane->type < 3)
+                       num = node->children[p[plane->type] < plane->dist];
+               else
+                       num = node->children[DotProduct (plane->normal, p) < plane->dist];
+       }
+       
+       return num;
+}
+
+#endif // !id386
+
+
+/*
+============
+SV_TestEntityPosition
+
+This could be a lot more efficient...
+============
+*/
+edict_t        *SV_TestEntityPosition (edict_t *ent)
+{
+       trace_t trace;
+
+       trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent);
+       
+       if (trace.startsolid)
+               return sv.edicts;
+               
+       return NULL;
+}
+
+
+/*
+===============================================================================
+
+LINE TESTING IN HULLS
+
+===============================================================================
+*/
+
+// 1/32 epsilon to keep floating point happy
+#define        DIST_EPSILON    (0.03125)
+
+/*
+==================
+SV_RecursiveHullCheck
+
+==================
+*/
+qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
+{
+       dclipnode_t     *node;
+       mplane_t        *plane;
+       float           t1, t2;
+       float           frac;
+       int                     i;
+       vec3_t          mid;
+       int                     side;
+       float           midf;
+
+loc0:
+// check for empty
+       if (num < 0)
+       {
+               if (num != CONTENTS_SOLID)
+               {
+                       trace->allsolid = false;
+                       if (num == CONTENTS_EMPTY)
+                               trace->inopen = true;
+                       else
+                               trace->inwater = true;
+               }
+               else
+                       trace->startsolid = true;
+               return true;            // empty
+       }
+
+       if (num < hull->firstclipnode || num > hull->lastclipnode)
+               Sys_Error ("SV_RecursiveHullCheck: bad node number");
+
+//
+// find the point distances
+//
+       node = hull->clipnodes + num;
+       plane = hull->planes + node->planenum;
+
+       if (plane->type < 3)
+       {
+               t1 = p1[plane->type] - plane->dist;
+               t2 = p2[plane->type] - plane->dist;
+       }
+       else
+       {
+               t1 = DotProduct (plane->normal, p1) - plane->dist;
+               t2 = DotProduct (plane->normal, p2) - plane->dist;
+       }
+       
+#if 1
+       if (t1 >= 0 && t2 >= 0)
+       // LordHavoc: optimized recursion
+//             return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
+       {
+               num = node->children[0];
+               goto loc0;
+       }
+       if (t1 < 0 && t2 < 0)
+//             return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
+       {
+               num = node->children[1];
+               goto loc0;
+       }
+#else
+       if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) )
+               return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
+       if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) )
+               return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
+#endif
+
+// put the crosspoint DIST_EPSILON pixels on the near side
+       if (t1 < 0)
+               frac = (t1 + DIST_EPSILON)/(t1-t2);
+       else
+               frac = (t1 - DIST_EPSILON)/(t1-t2);
+       if (frac < 0)
+               frac = 0;
+       if (frac > 1)
+               frac = 1;
+               
+       midf = p1f + (p2f - p1f)*frac;
+       for (i=0 ; i<3 ; i++)
+               mid[i] = p1[i] + frac*(p2[i] - p1[i]);
+
+       side = (t1 < 0);
+
+// move up to the node
+       if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) )
+               return false;
+
+#ifdef PARANOID
+       if (SV_HullPointContents (hull, node->children[side], mid) == CONTENTS_SOLID)
+       {
+               Con_Printf ("mid PointInHullSolid\n");
+               return false;
+       }
+#endif
+       
+       if (SV_HullPointContents (hull, node->children[side^1], mid) != CONTENTS_SOLID)
+// go past the node
+               return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);
+       // mid would need to be duplicated during recursion...
+       /*
+       {
+               p1f = midf;
+               p1 = mid;
+               num = node->children[side^1];
+               goto loc0;
+       }
+       */
+
+       if (trace->allsolid)
+               return false;           // never got out of the solid area
+       
+//==================
+// the other side of the node is solid, this is the impact point
+//==================
+       if (!side)
+       {
+               VectorCopy (plane->normal, trace->plane.normal);
+               trace->plane.dist = plane->dist;
+       }
+       else
+       {
+               // LordHavoc: unrolled vector operation because the compiler can't be sure vec3_origin is 0
+//             VectorSubtract (vec3_origin, plane->normal, trace->plane.normal);
+               trace->plane.normal[0] = -plane->normal[0];
+               trace->plane.normal[1] = -plane->normal[1];
+               trace->plane.normal[2] = -plane->normal[2];
+               trace->plane.dist = -plane->dist;
+       }
+
+       while (SV_HullPointContents (hull, hull->firstclipnode, mid) == CONTENTS_SOLID)
+       { // shouldn't really happen, but does occasionally
+               frac -= 0.1;
+               if (frac < 0)
+               {
+                       trace->fraction = midf;
+                       VectorCopy (mid, trace->endpos);
+                       Con_DPrintf ("backup past 0\n");
+                       return false;
+               }
+               midf = p1f + (p2f - p1f)*frac;
+               for (i=0 ; i<3 ; i++)
+                       mid[i] = p1[i] + frac*(p2[i] - p1[i]);
+       }
+
+       trace->fraction = midf;
+       VectorCopy (mid, trace->endpos);
+
+       return false;
+}
+
+
+/*
+==================
+SV_ClipMoveToEntity
+
+Handles selection or creation of a clipping hull, and offseting (and
+eventually rotation) of the end points
+==================
+*/
+trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
+{
+       trace_t         trace;
+       vec3_t          offset;
+       vec3_t          start_l, end_l;
+       hull_t          *hull;
+
+// fill in a default trace
+       memset (&trace, 0, sizeof(trace_t));
+       trace.fraction = 1;
+       trace.allsolid = true;
+       VectorCopy (end, trace.endpos);
+
+// get the clipping hull
+       hull = SV_HullForEntity (ent, mins, maxs, offset);
+
+       VectorSubtract (start, offset, start_l);
+       VectorSubtract (end, offset, end_l);
+
+// LordHavoc: enabling rotating bmodels
+       // rotate start and end into the models frame of reference
+       if (ent->v.solid == SOLID_BSP && 
+       (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
+       {
+               vec3_t  forward, right, up;
+               vec3_t  temp;
+
+               AngleVectors (ent->v.angles, forward, right, up);
+
+               VectorCopy (start_l, temp);
+               start_l[0] = DotProduct (temp, forward);
+               start_l[1] = -DotProduct (temp, right);
+               start_l[2] = DotProduct (temp, up);
+
+               VectorCopy (end_l, temp);
+               end_l[0] = DotProduct (temp, forward);
+               end_l[1] = -DotProduct (temp, right);
+               end_l[2] = DotProduct (temp, up);
+       }
+
+// trace a line through the apropriate clipping hull
+       SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace);
+
+// LordHavoc: enabling rotating bmodels
+       // rotate endpos back to world frame of reference
+       if (ent->v.solid == SOLID_BSP && 
+       (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
+       {
+               vec3_t  a;
+               vec3_t  forward, right, up;
+               vec3_t  temp;
+
+               if (trace.fraction != 1)
+               {
+                       VectorSubtract (vec3_origin, ent->v.angles, a);
+                       AngleVectors (a, forward, right, up);
+
+                       VectorCopy (trace.endpos, temp);
+                       trace.endpos[0] = DotProduct (temp, forward);
+                       trace.endpos[1] = -DotProduct (temp, right);
+                       trace.endpos[2] = DotProduct (temp, up);
+
+                       VectorCopy (trace.plane.normal, temp);
+                       trace.plane.normal[0] = DotProduct (temp, forward);
+                       trace.plane.normal[1] = -DotProduct (temp, right);
+                       trace.plane.normal[2] = DotProduct (temp, up);
+               }
+       }
+
+// fix trace up by the offset
+       if (trace.fraction != 1)
+               VectorAdd (trace.endpos, offset, trace.endpos);
+
+// did we clip the move?
+       if (trace.fraction < 1 || trace.startsolid  )
+               trace.ent = ent;
+
+       return trace;
+}
+
+//===========================================================================
+
+/*
+====================
+SV_ClipToLinks
+
+Mins and maxs enclose the entire area swept by the move
+====================
+*/
+void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip )
+{
+       link_t          *l, *next;
+       edict_t         *touch;
+       trace_t         trace;
+
+loc0:
+// touch linked edicts
+       for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
+       {
+               next = l->next;
+               touch = EDICT_FROM_AREA(l);
+               if (touch->v.solid == SOLID_NOT)
+                       continue;
+               if (touch == clip->passedict)
+                       continue;
+               if (touch->v.solid == SOLID_TRIGGER)
+                       Sys_Error ("Trigger in clipping list");
+
+               if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
+                       continue;
+
+               if (clip->boxmins[0] > touch->v.absmax[0]
+               || clip->boxmins[1] > touch->v.absmax[1]
+               || clip->boxmins[2] > touch->v.absmax[2]
+               || clip->boxmaxs[0] < touch->v.absmin[0]
+               || clip->boxmaxs[1] < touch->v.absmin[1]
+               || clip->boxmaxs[2] < touch->v.absmin[2] )
+                       continue;
+
+               if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])
+                       continue;       // points never interact
+
+       // might intersect, so do an exact clip
+               if (clip->trace.allsolid)
+                       return;
+               if (clip->passedict)
+               {
+                       if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
+                               continue;       // don't clip against own missiles
+                       if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
+                               continue;       // don't clip against owner
+                       // LordHavoc: corpse code
+                       if (clip->passedict->v.solid == SOLID_CORPSE && touch->v.solid == SOLID_SLIDEBOX)
+                               continue;
+                       if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)
+                               continue;
+               }
+
+               if ((int)touch->v.flags & FL_MONSTER)
+                       trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end);
+               else
+                       trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end);
+               if (trace.allsolid || trace.startsolid ||
+               trace.fraction < clip->trace.fraction)
+               {
+                       trace.ent = touch;
+                       if (clip->trace.startsolid)
+                       {
+                               clip->trace = trace;
+                               clip->trace.startsolid = true;
+                       }
+                       else
+                               clip->trace = trace;
+               }
+               else if (trace.startsolid)
+                       clip->trace.startsolid = true;
+       }
+       
+// recurse down both sides
+       if (node->axis == -1)
+               return;
+
+       // LordHavoc: optimized recursion
+//     if (clip->boxmaxs[node->axis] > node->dist) SV_ClipToLinks(node->children[0], clip);
+//     if (clip->boxmins[node->axis] < node->dist) SV_ClipToLinks(node->children[1], clip);
+       if (clip->boxmaxs[node->axis] > node->dist)
+       {
+               if (clip->boxmins[node->axis] < node->dist)
+                       SV_ClipToLinks(node->children[1], clip);
+               node = node->children[0];
+               goto loc0;
+       }
+       else if (clip->boxmins[node->axis] < node->dist)
+       {
+               node = node->children[1];
+               goto loc0;
+       }
+}
+
+
+/*
+==================
+SV_MoveBounds
+==================
+*/
+void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
+{
+#if 0
+// debug to test against everything
+boxmins[0] = boxmins[1] = boxmins[2] = -9999;
+boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;
+#else
+       int             i;
+       
+       for (i=0 ; i<3 ; i++)
+       {
+               if (end[i] > start[i])
+               {
+                       boxmins[i] = start[i] + mins[i] - 1;
+                       boxmaxs[i] = end[i] + maxs[i] + 1;
+               }
+               else
+               {
+                       boxmins[i] = end[i] + mins[i] - 1;
+                       boxmaxs[i] = start[i] + maxs[i] + 1;
+               }
+       }
+#endif
+}
+
+/*
+==================
+SV_Move
+==================
+*/
+trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict)
+{
+       moveclip_t      clip;
+       int                     i;
+
+       memset ( &clip, 0, sizeof ( moveclip_t ) );
+
+// clip to world
+       clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end );
+
+       clip.start = start;
+       clip.end = end;
+       clip.mins = mins;
+       clip.maxs = maxs;
+       clip.type = type;
+       clip.passedict = passedict;
+
+       if (type == MOVE_MISSILE)
+       {
+               for (i=0 ; i<3 ; i++)
+               {
+                       clip.mins2[i] = -15;
+                       clip.maxs2[i] = 15;
+               }
+       }
+       else
+       {
+               VectorCopy (mins, clip.mins2);
+               VectorCopy (maxs, clip.maxs2);
+       }
+       
+// create the bounding box of the entire move
+       SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );
+
+// clip to entities
+       SV_ClipToLinks ( sv_areanodes, &clip );
+
+       return clip.trace;
+}
diff --git a/world.h b/world.h
new file mode 100644 (file)
index 0000000..bf9507c
--- /dev/null
+++ b/world.h
@@ -0,0 +1,83 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// world.h
+
+typedef struct
+{
+       vec3_t  normal;
+       float   dist;
+} plane_t;
+
+typedef struct
+{
+       qboolean        allsolid;       // if true, plane is not valid
+       qboolean        startsolid;     // if true, the initial point was in a solid area
+       qboolean        inopen, inwater;
+       float   fraction;               // time completed, 1.0 = didn't hit anything
+       vec3_t  endpos;                 // final position
+       plane_t plane;                  // surface normal at impact
+       edict_t *ent;                   // entity the surface is on
+       // LordHavoc: added texture and lighting to traceline
+       char    *texturename;
+       vec3_t  light;
+} trace_t;
+
+
+#define        MOVE_NORMAL             0
+#define        MOVE_NOMONSTERS 1
+#define        MOVE_MISSILE    2
+
+
+void SV_ClearWorld (void);
+// called after the world model has been loaded, before linking any entities
+
+void SV_UnlinkEdict (edict_t *ent);
+// call before removing an entity, and before trying to move one,
+// so it doesn't clip against itself
+// flags ent->v.modified
+
+void SV_LinkEdict (edict_t *ent, qboolean touch_triggers);
+// Needs to be called any time an entity changes origin, mins, maxs, or solid
+// flags ent->v.modified
+// sets ent->v.absmin and ent->v.absmax
+// if touchtriggers, calls prog functions for the intersected triggers
+
+extern int SV_HullPointContents (hull_t *hull, int num, vec3_t p);
+// LordHavoc: waste of time to wrap it
+//int SV_PointContents (vec3_t p);
+#define SV_PointContents(testpoint) SV_HullPointContents(&sv.worldmodel->hulls[0], 0, (testpoint))
+// returns the CONTENTS_* value from the world at the given point.
+// does not check any entities at all
+// the non-true version remaps the water current contents to content_water
+
+edict_t        *SV_TestEntityPosition (edict_t *ent);
+
+trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict);
+// mins and maxs are reletive
+
+// if the entire move stays in a solid volume, trace.allsolid will be set
+
+// if the starting point is in a solid, it will be allowed to move out
+// to an open area
+
+// nomonsters is used for line of sight or edge testing, where mosnters
+// shouldn't be considered solid objects
+
+// passedict is explicitly excluded from clipping checks (normally NULL)
diff --git a/zone.c b/zone.c
new file mode 100644 (file)
index 0000000..77b9532
--- /dev/null
+++ b/zone.c
@@ -0,0 +1,949 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// Z_zone.c
+
+#include "quakedef.h"
+
+// LordHavoc: everyone used a -zone 512 anyway, so...
+#define        DYNAMIC_SIZE    0x80000
+//#define      DYNAMIC_SIZE    0xc000
+
+#define        ZONEID  0x1d4a11
+#define MINFRAGMENT    64
+
+typedef struct memblock_s
+{
+       int             size;           // including the header and possibly tiny fragments
+       int     tag;            // a tag of 0 is a free block
+       int     id;                     // should be ZONEID
+       struct memblock_s       *next, *prev;
+       int             pad;                    // pad to 64 bit boundary
+} memblock_t;
+
+typedef struct
+{
+       int             size;           // total bytes malloced, including header
+       memblock_t      blocklist;              // start / end cap for linked list
+       memblock_t      *rover;
+} memzone_t;
+
+void Cache_FreeLow (int new_low_hunk);
+void Cache_FreeHigh (int new_high_hunk);
+
+
+/*
+==============================================================================
+
+                                               ZONE MEMORY ALLOCATION
+
+There is never any space between memblocks, and there will never be two
+contiguous free memblocks.
+
+The rover can be left pointing at a non-empty block
+
+The zone calls are pretty much only used for small strings and structures,
+all big things are allocated on the hunk.
+==============================================================================
+*/
+
+memzone_t      *mainzone;
+
+void Z_ClearZone (memzone_t *zone, int size);
+
+
+/*
+========================
+Z_ClearZone
+========================
+*/
+void Z_ClearZone (memzone_t *zone, int size)
+{
+       memblock_t      *block;
+       
+// set the entire zone to one free block
+
+       zone->blocklist.next = zone->blocklist.prev = block =
+               (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
+       zone->blocklist.tag = 1;        // in use block
+       zone->blocklist.id = 0;
+       zone->blocklist.size = 0;
+       zone->rover = block;
+       
+       block->prev = block->next = &zone->blocklist;
+       block->tag = 0;                 // free block
+       block->id = ZONEID;
+       block->size = size - sizeof(memzone_t);
+}
+
+
+/*
+========================
+Z_Free
+========================
+*/
+void Z_Free (void *ptr)
+{
+       memblock_t      *block, *other;
+       
+       if (!ptr)
+               Sys_Error ("Z_Free: NULL pointer");
+
+       block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
+       if (block->id != ZONEID)
+               Sys_Error ("Z_Free: freed a pointer without ZONEID");
+       if (block->tag == 0)
+               Sys_Error ("Z_Free: freed a freed pointer");
+
+       block->tag = 0;         // mark as free
+       
+       other = block->prev;
+       if (!other->tag)
+       {       // merge with previous free block
+               other->size += block->size;
+               other->next = block->next;
+               other->next->prev = other;
+               if (block == mainzone->rover)
+                       mainzone->rover = other;
+               block = other;
+       }
+       
+       other = block->next;
+       if (!other->tag)
+       {       // merge the next free block onto the end
+               block->size += other->size;
+               block->next = other->next;
+               block->next->prev = block;
+               if (other == mainzone->rover)
+                       mainzone->rover = block;
+       }
+}
+
+
+/*
+========================
+Z_Malloc
+========================
+*/
+void *Z_Malloc (int size)
+{
+       void    *buf;
+       
+Z_CheckHeap ();        // DEBUG
+       buf = Z_TagMalloc (size, 1);
+       if (!buf)
+               Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size);
+       memset (buf, 0, size);
+
+       return buf;
+}
+
+void *Z_TagMalloc (int size, int tag)
+{
+       int             extra;
+       memblock_t      *start, *rover, *new, *base;
+
+       if (!tag)
+               Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
+
+//
+// scan through the block list looking for the first free block
+// of sufficient size
+//
+       size += sizeof(memblock_t);     // account for size of block header
+       size += 4;                                      // space for memory trash tester
+       size = (size + 7) & ~7;         // align to 8-byte boundary
+       
+       base = rover = mainzone->rover;
+       start = base->prev;
+       
+       do
+       {
+               if (rover == start)     // scaned all the way around the list
+                       return NULL;
+               if (rover->tag)
+                       base = rover = rover->next;
+               else
+                       rover = rover->next;
+       } while (base->tag || base->size < size);
+       
+//
+// found a block big enough
+//
+       extra = base->size - size;
+       if (extra >  MINFRAGMENT)
+       {       // there will be a free fragment after the allocated block
+               new = (memblock_t *) ((byte *)base + size );
+               new->size = extra;
+               new->tag = 0;                   // free block
+               new->prev = base;
+               new->id = ZONEID;
+               new->next = base->next;
+               new->next->prev = new;
+               base->next = new;
+               base->size = size;
+       }
+       
+       base->tag = tag;                                // no longer a free block
+       
+       mainzone->rover = base->next;   // next allocation will start looking here
+       
+       base->id = ZONEID;
+
+// marker for memory trash testing
+       *(int *)((byte *)base + base->size - 4) = ZONEID;
+
+       return (void *) ((byte *)base + sizeof(memblock_t));
+}
+
+
+/*
+========================
+Z_Print
+========================
+*/
+void Z_Print (memzone_t *zone)
+{
+       memblock_t      *block;
+       
+       Con_Printf ("zone size: %i  location: %p\n",mainzone->size,mainzone);
+       
+       for (block = zone->blocklist.next ; ; block = block->next)
+       {
+               Con_Printf ("block:%p    size:%7i    tag:%3i\n",
+                       block, block->size, block->tag);
+               
+               if (block->next == &zone->blocklist)
+                       break;                  // all blocks have been hit     
+               if ( (byte *)block + block->size != (byte *)block->next)
+                       Con_Printf ("ERROR: block size does not touch the next block\n");
+               if ( block->next->prev != block)
+                       Con_Printf ("ERROR: next block doesn't have proper back link\n");
+               if (!block->tag && !block->next->tag)
+                       Con_Printf ("ERROR: two consecutive free blocks\n");
+       }
+}
+
+
+/*
+========================
+Z_CheckHeap
+========================
+*/
+void Z_CheckHeap (void)
+{
+       memblock_t      *block;
+       
+       for (block = mainzone->blocklist.next ; ; block = block->next)
+       {
+               if (block->next == &mainzone->blocklist)
+                       break;                  // all blocks have been hit     
+               if ( (byte *)block + block->size != (byte *)block->next)
+                       Sys_Error ("Z_CheckHeap: block size does not touch the next block\n");
+               if ( block->next->prev != block)
+                       Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
+               if (!block->tag && !block->next->tag)
+                       Sys_Error ("Z_CheckHeap: two consecutive free blocks\n");
+       }
+}
+
+//============================================================================
+
+#define        HUNK_SENTINAL   0x1df001ed
+
+typedef struct
+{
+       int             sentinal;
+       int             size;           // including sizeof(hunk_t), -1 = not allocated
+       char    name[8];
+} hunk_t;
+
+byte   *hunk_base;
+int            hunk_size;
+
+int            hunk_low_used;
+int            hunk_high_used;
+
+qboolean       hunk_tempactive;
+int            hunk_tempmark;
+
+void R_FreeTextures (void);
+
+/*
+==============
+Hunk_Check
+
+Run consistancy and sentinal trahing checks
+==============
+*/
+void Hunk_Check (void)
+{
+       hunk_t  *h;
+       
+       for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; )
+       {
+               if (h->sentinal != HUNK_SENTINAL)
+                       Sys_Error ("Hunk_Check: trahsed sentinal");
+               if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
+                       Sys_Error ("Hunk_Check: bad size");
+               h = (hunk_t *)((byte *)h+h->size);
+       }
+}
+
+/*
+==============
+Hunk_Print
+
+If "all" is specified, every single allocation is printed.
+Otherwise, allocations with the same name will be totaled up before printing.
+==============
+*/
+void Hunk_Print (qboolean all)
+{
+       hunk_t  *h, *next, *endlow, *starthigh, *endhigh;
+       int             count, sum;
+       int             totalblocks;
+       char    name[9];
+
+       name[8] = 0;
+       count = 0;
+       sum = 0;
+       totalblocks = 0;
+       
+       h = (hunk_t *)hunk_base;
+       endlow = (hunk_t *)(hunk_base + hunk_low_used);
+       starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
+       endhigh = (hunk_t *)(hunk_base + hunk_size);
+
+       Con_Printf ("          :%8i total hunk size\n", hunk_size);
+       Con_Printf ("-------------------------\n");
+
+       while (1)
+       {
+       //
+       // skip to the high hunk if done with low hunk
+       //
+               if ( h == endlow )
+               {
+                       Con_Printf ("-------------------------\n");
+                       Con_Printf ("          :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used);
+                       Con_Printf ("-------------------------\n");
+                       h = starthigh;
+               }
+               
+       //
+       // if totally done, break
+       //
+               if ( h == endhigh )
+                       break;
+
+       //
+       // run consistancy checks
+       //
+               if (h->sentinal != HUNK_SENTINAL)
+                       Sys_Error ("Hunk_Check: trashed sentinal");
+               if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
+                       Sys_Error ("Hunk_Check: bad size");
+                       
+               next = (hunk_t *)((byte *)h+h->size);
+               count++;
+               totalblocks++;
+               sum += h->size;
+
+       //
+       // print the single block
+       //
+               memcpy (name, h->name, 8);
+               if (all)
+                       Con_Printf ("%8p :%8i %8s\n",h, h->size, name);
+                       
+       //
+       // print the total
+       //
+               if (next == endlow || next == endhigh || 
+               strncmp (h->name, next->name, 8) )
+               {
+                       if (!all)
+                               Con_Printf ("          :%8i %8s (TOTAL)\n",sum, name);
+                       count = 0;
+                       sum = 0;
+               }
+
+               h = next;
+       }
+
+       Con_Printf ("-------------------------\n");
+       Con_Printf ("%8i total blocks\n", totalblocks);
+       
+}
+
+/*
+===================
+Hunk_AllocName
+===================
+*/
+void *Hunk_AllocName (int size, char *name)
+{
+       hunk_t  *h;
+       
+#ifdef PARANOID
+       Hunk_Check ();
+#endif
+
+       if (size < 0)
+               Sys_Error ("Hunk_Alloc: bad size: %i", size);
+               
+       size = sizeof(hunk_t) + ((size+15)&~15);
+       
+       if (hunk_size - hunk_low_used - hunk_high_used < size)
+               Sys_Error ("Hunk_Alloc: failed on %i bytes",size);
+       
+       h = (hunk_t *)(hunk_base + hunk_low_used);
+       hunk_low_used += size;
+
+       Cache_FreeLow (hunk_low_used);
+
+       memset (h, 0, size);
+       
+       h->size = size;
+       h->sentinal = HUNK_SENTINAL;
+       strncpy (h->name, name, 8);
+       
+       return (void *)(h+1);
+}
+
+/*
+===================
+Hunk_Alloc
+===================
+*/
+void *Hunk_Alloc (int size)
+{
+       return Hunk_AllocName (size, "unknown");
+}
+
+int    Hunk_LowMark (void)
+{
+       return hunk_low_used;
+}
+
+void Hunk_FreeToLowMark (int mark)
+{
+       if (mark < 0 || mark > hunk_low_used)
+               Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark);
+       memset (hunk_base + mark, 0, hunk_low_used - mark);
+       hunk_low_used = mark;
+}
+
+int    Hunk_HighMark (void)
+{
+       if (hunk_tempactive)
+       {
+               hunk_tempactive = false;
+               Hunk_FreeToHighMark (hunk_tempmark);
+       }
+
+       return hunk_high_used;
+}
+
+void Hunk_FreeToHighMark (int mark)
+{
+       if (hunk_tempactive)
+       {
+               hunk_tempactive = false;
+               Hunk_FreeToHighMark (hunk_tempmark);
+       }
+       if (mark < 0 || mark > hunk_high_used)
+               Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark);
+       memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
+       hunk_high_used = mark;
+}
+
+
+/*
+===================
+Hunk_HighAllocName
+===================
+*/
+void *Hunk_HighAllocName (int size, char *name)
+{
+       hunk_t  *h;
+
+       if (size < 0)
+               Sys_Error ("Hunk_HighAllocName: bad size: %i", size);
+
+       if (hunk_tempactive)
+       {
+               Hunk_FreeToHighMark (hunk_tempmark);
+               hunk_tempactive = false;
+       }
+
+#ifdef PARANOID
+       Hunk_Check ();
+#endif
+
+       size = sizeof(hunk_t) + ((size+15)&~15);
+
+       if (hunk_size - hunk_low_used - hunk_high_used < size)
+       {
+               Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size);
+               return NULL;
+       }
+
+       hunk_high_used += size;
+       Cache_FreeHigh (hunk_high_used);
+
+       h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
+
+       memset (h, 0, size);
+       h->size = size;
+       h->sentinal = HUNK_SENTINAL;
+       strncpy (h->name, name, 8);
+
+       return (void *)(h+1);
+}
+
+
+/*
+=================
+Hunk_TempAlloc
+
+Return space from the top of the hunk
+=================
+*/
+void *Hunk_TempAlloc (int size)
+{
+       void    *buf;
+
+       size = (size+15)&~15;
+       
+       if (hunk_tempactive)
+       {
+               Hunk_FreeToHighMark (hunk_tempmark);
+               hunk_tempactive = false;
+       }
+       
+       hunk_tempmark = Hunk_HighMark ();
+
+       buf = Hunk_HighAllocName (size, "temp");
+
+       hunk_tempactive = true;
+
+       return buf;
+}
+
+/*
+===============================================================================
+
+CACHE MEMORY
+
+===============================================================================
+*/
+
+typedef struct cache_system_s
+{
+       int                                             size;           // including this header
+       cache_user_t                    *user;
+       char                                    name[16];
+       struct cache_system_s   *prev, *next;
+       struct cache_system_s   *lru_prev, *lru_next;   // for LRU flushing     
+} cache_system_t;
+
+cache_system_t *Cache_TryAlloc (int size, qboolean nobottom);
+
+cache_system_t cache_head;
+
+/*
+===========
+Cache_Move
+===========
+*/
+void Cache_Move ( cache_system_t *c)
+{
+       cache_system_t          *new;
+
+// we are clearing up space at the bottom, so only allocate it late
+       new = Cache_TryAlloc (c->size, true);
+       if (new)
+       {
+//             Con_Printf ("cache_move ok\n");
+
+               memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) );
+               new->user = c->user;
+               memcpy (new->name, c->name, sizeof(new->name));
+               Cache_Free (c->user);
+               new->user->data = (void *)(new+1);
+       }
+       else
+       {
+//             Con_Printf ("cache_move failed\n");
+
+               Cache_Free (c->user);           // tough luck...
+       }
+}
+
+/*
+============
+Cache_FreeLow
+
+Throw things out until the hunk can be expanded to the given point
+============
+*/
+void Cache_FreeLow (int new_low_hunk)
+{
+       cache_system_t  *c;
+       
+       while (1)
+       {
+               c = cache_head.next;
+               if (c == &cache_head)
+                       return;         // nothing in cache at all
+               if ((byte *)c >= hunk_base + new_low_hunk)
+                       return;         // there is space to grow the hunk
+               Cache_Move ( c );       // reclaim the space
+       }
+}
+
+/*
+============
+Cache_FreeHigh
+
+Throw things out until the hunk can be expanded to the given point
+============
+*/
+void Cache_FreeHigh (int new_high_hunk)
+{
+       cache_system_t  *c, *prev;
+       
+       prev = NULL;
+       while (1)
+       {
+               c = cache_head.prev;
+               if (c == &cache_head)
+                       return;         // nothing in cache at all
+               if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
+                       return;         // there is space to grow the hunk
+               if (c == prev)
+                       Cache_Free (c->user);   // didn't move out of the way
+               else
+               {
+                       Cache_Move (c); // try to move it
+                       prev = c;
+               }
+       }
+}
+
+void Cache_UnlinkLRU (cache_system_t *cs)
+{
+       if (!cs->lru_next || !cs->lru_prev)
+               Sys_Error ("Cache_UnlinkLRU: NULL link");
+
+       cs->lru_next->lru_prev = cs->lru_prev;
+       cs->lru_prev->lru_next = cs->lru_next;
+       
+       cs->lru_prev = cs->lru_next = NULL;
+}
+
+void Cache_MakeLRU (cache_system_t *cs)
+{
+       if (cs->lru_next || cs->lru_prev)
+               Sys_Error ("Cache_MakeLRU: active link");
+
+       cache_head.lru_next->lru_prev = cs;
+       cs->lru_next = cache_head.lru_next;
+       cs->lru_prev = &cache_head;
+       cache_head.lru_next = cs;
+}
+
+/*
+============
+Cache_TryAlloc
+
+Looks for a free block of memory between the high and low hunk marks
+Size should already include the header and padding
+============
+*/
+cache_system_t *Cache_TryAlloc (int size, qboolean nobottom)
+{
+       cache_system_t  *cs, *new;
+       
+// is the cache completely empty?
+
+       if (!nobottom && cache_head.prev == &cache_head)
+       {
+               if (hunk_size - hunk_high_used - hunk_low_used < size)
+                       Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size);
+
+               new = (cache_system_t *) (hunk_base + hunk_low_used);
+               memset (new, 0, sizeof(*new));
+               new->size = size;
+
+               cache_head.prev = cache_head.next = new;
+               new->prev = new->next = &cache_head;
+               
+               Cache_MakeLRU (new);
+               return new;
+       }
+       
+// search from the bottom up for space
+
+       new = (cache_system_t *) (hunk_base + hunk_low_used);
+       cs = cache_head.next;
+       
+       do
+       {
+               if (!nobottom || cs != cache_head.next)
+               {
+                       if ( (byte *)cs - (byte *)new >= size)
+                       {       // found space
+                               memset (new, 0, sizeof(*new));
+                               new->size = size;
+                               
+                               new->next = cs;
+                               new->prev = cs->prev;
+                               cs->prev->next = new;
+                               cs->prev = new;
+                               
+                               Cache_MakeLRU (new);
+       
+                               return new;
+                       }
+               }
+
+       // continue looking             
+               new = (cache_system_t *)((byte *)cs + cs->size);
+               cs = cs->next;
+
+       } while (cs != &cache_head);
+       
+// try to allocate one at the very end
+       if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size)
+       {
+               memset (new, 0, sizeof(*new));
+               new->size = size;
+               
+               new->next = &cache_head;
+               new->prev = cache_head.prev;
+               cache_head.prev->next = new;
+               cache_head.prev = new;
+               
+               Cache_MakeLRU (new);
+
+               return new;
+       }
+       
+       return NULL;            // couldn't allocate
+}
+
+/*
+============
+Cache_Flush
+
+Throw everything out, so new data will be demand cached
+============
+*/
+void Cache_Flush (void)
+{
+       while (cache_head.next != &cache_head)
+               Cache_Free ( cache_head.next->user );   // reclaim the space
+}
+
+
+/*
+============
+Cache_Print
+
+============
+*/
+void Cache_Print (void)
+{
+       cache_system_t  *cd;
+
+       for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next)
+       {
+               Con_Printf ("%8i : %s\n", cd->size, cd->name);
+       }
+}
+
+/*
+============
+Cache_Report
+
+============
+*/
+void Cache_Report (void)
+{
+       Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) );
+}
+
+/*
+============
+Cache_Compact
+
+============
+*/
+void Cache_Compact (void)
+{
+}
+
+/*
+============
+Cache_Init
+
+============
+*/
+void Cache_Init (void)
+{
+       cache_head.next = cache_head.prev = &cache_head;
+       cache_head.lru_next = cache_head.lru_prev = &cache_head;
+
+       Cmd_AddCommand ("flush", Cache_Flush);
+}
+
+/*
+==============
+Cache_Free
+
+Frees the memory and removes it from the LRU list
+==============
+*/
+void Cache_Free (cache_user_t *c)
+{
+       cache_system_t  *cs;
+
+       if (!c->data)
+               Sys_Error ("Cache_Free: not allocated");
+
+       cs = ((cache_system_t *)c->data) - 1;
+
+       cs->prev->next = cs->next;
+       cs->next->prev = cs->prev;
+       cs->next = cs->prev = NULL;
+
+       c->data = NULL;
+
+       Cache_UnlinkLRU (cs);
+}
+
+
+
+/*
+==============
+Cache_Check
+==============
+*/
+void *Cache_Check (cache_user_t *c)
+{
+       cache_system_t  *cs;
+
+       if (!c->data)
+               return NULL;
+
+       cs = ((cache_system_t *)c->data) - 1;
+
+// move to head of LRU
+       Cache_UnlinkLRU (cs);
+       Cache_MakeLRU (cs);
+       
+       return c->data;
+}
+
+
+/*
+==============
+Cache_Alloc
+==============
+*/
+void *Cache_Alloc (cache_user_t *c, int size, char *name)
+{
+       cache_system_t  *cs;
+
+       if (c->data)
+               Sys_Error ("Cache_Alloc: allready allocated");
+       
+       if (size <= 0)
+               Sys_Error ("Cache_Alloc: size %i", size);
+
+       size = (size + sizeof(cache_system_t) + 15) & ~15;
+
+// find memory for it  
+       while (1)
+       {
+               cs = Cache_TryAlloc (size, false);
+               if (cs)
+               {
+                       strncpy (cs->name, name, sizeof(cs->name)-1);
+                       c->data = (void *)(cs+1);
+                       cs->user = c;
+                       break;
+               }
+       
+       // free the least recently used cahedat
+               if (cache_head.lru_prev == &cache_head)
+                       Sys_Error ("Cache_Alloc: out of memory");
+                                                                                                       // not enough memory at all
+               Cache_Free ( cache_head.lru_prev->user );
+       } 
+       
+       return Cache_Check (c);
+}
+
+//============================================================================
+
+
+void HunkList_f(void)
+{
+       if (Cmd_Argc() == 2)
+               if (strcmp(Cmd_Argv(1), "all"))
+                       Con_Printf("usage: hunklist [all]\n");
+               else
+                       Hunk_Print(true);
+       else
+               Hunk_Print(false);
+}
+
+/*
+========================
+Memory_Init
+========================
+*/
+void Memory_Init (void *buf, int size)
+{
+       int p;
+       int zonesize = DYNAMIC_SIZE;
+
+       hunk_base = buf;
+       hunk_size = size;
+       hunk_low_used = 0;
+       hunk_high_used = 0;
+       
+       Cache_Init ();
+       p = COM_CheckParm ("-zone");
+       if (p)
+       {
+               if (p < com_argc-1)
+                       zonesize = atoi (com_argv[p+1]) * 1024;
+               else
+                       Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
+       }
+       mainzone = Hunk_AllocName (zonesize, "zone" );
+       Z_ClearZone (mainzone, zonesize);
+       Cmd_AddCommand ("hunklist", HunkList_f);
+}
+
diff --git a/zone.h b/zone.h
new file mode 100644 (file)
index 0000000..7655ad9
--- /dev/null
+++ b/zone.h
@@ -0,0 +1,131 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+/*
+ memory allocation
+
+
+H_??? The hunk manages the entire memory block given to quake.  It must be
+contiguous.  Memory can be allocated from either the low or high end in a
+stack fashion.  The only way memory is released is by resetting one of the
+pointers.
+
+Hunk allocations should be given a name, so the Hunk_Print () function
+can display usage.
+
+Hunk allocations are guaranteed to be 16 byte aligned.
+
+The video buffers are allocated high to avoid leaving a hole underneath
+server allocations when changing to a higher video mode.
+
+
+Z_??? Zone memory functions used for small, dynamic allocations like text
+strings from command input.  There is only about 48K for it, allocated at
+the very bottom of the hunk.
+
+Cache_??? Cache memory is for objects that can be dynamically loaded and
+can usefully stay persistant between levels.  The size of the cache
+fluctuates from level to level.
+
+To allocate a cachable object
+
+
+Temp_??? Temp memory is used for file loading and surface caching.  The size
+of the cache memory is adjusted so that there is a minimum of 512k remaining
+for temp memory.
+
+
+------ Top of Memory -------
+
+high hunk allocations
+
+<--- high hunk reset point held by vid
+
+video buffer
+
+z buffer
+
+surface cache
+
+<--- high hunk used
+
+cachable memory
+
+<--- low hunk used
+
+client and server low hunk allocations
+
+<-- low hunk reset point held by host
+
+startup hunk allocations
+
+Zone block
+
+----- Bottom of Memory -----
+
+
+
+*/
+
+void Memory_Init (void *buf, int size);
+
+void Z_Free (void *ptr);
+void *Z_Malloc (int size);                     // returns 0 filled memory
+void *Z_TagMalloc (int size, int tag);
+
+void Z_DumpHeap (void);
+void Z_CheckHeap (void);
+int Z_FreeMemory (void);
+
+void *Hunk_Alloc (int size);           // returns 0 filled memory
+void *Hunk_AllocName (int size, char *name);
+
+void *Hunk_HighAllocName (int size, char *name);
+
+int    Hunk_LowMark (void);
+void Hunk_FreeToLowMark (int mark);
+
+int    Hunk_HighMark (void);
+void Hunk_FreeToHighMark (int mark);
+
+void *Hunk_TempAlloc (int size);
+
+void Hunk_Check (void);
+
+typedef struct cache_user_s
+{
+       void    *data;
+} cache_user_t;
+
+void Cache_Flush (void);
+
+void *Cache_Check (cache_user_t *c);
+// returns the cached data, and moves to the head of the LRU list
+// if present, otherwise returns NULL
+
+void Cache_Free (cache_user_t *c);
+
+void *Cache_Alloc (cache_user_t *c, int size, char *name);
+// Returns NULL if all purgable data was tossed and there still
+// wasn't enough room.
+
+void Cache_Report (void);
+
+
+