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];