diff --git a/engine/common/asm_i386.h b/engine/common/asm_i386.h new file mode 100644 index 000000000..a521107a9 --- /dev/null +++ b/engine/common/asm_i386.h @@ -0,0 +1,98 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __ASM_I386__ +#define __ASM_I386__ + +#if defined(CYGORELF) || defined(ELF) +#define C(label) label +#else +#define C(label) _##label +#endif + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// plane_t structure +// !!! if this is changed, it must be changed in model.h too !!! +// !!! if the size of this is changed, the array lookup in SV_HullPointContents +// must be changed too !!! +#define pl_normal 0 +#define pl_dist 12 +#define pl_type 16 +#define pl_signbits 17 +#define pl_pad 18 +#define pl_size 20 + +// hull_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define hu_clipnodes 0 +#define hu_planes 4 +#define hu_firstclipnode 8 +#define hu_lastclipnode 12 +#define hu_clip_mins 16 +#define hu_clip_maxs 28 +#define hu_available 40 +#define hu_size 44 + +// dnode_t structure +// !!! if this is changed, it must be changed in bspfile.h too !!! +#define nd_planenum 0 +#define nd_children 4 +#define nd_mins 8 +#define nd_maxs 20 +#define nd_firstface 32 +#define nd_numfaces 36 +#define nd_size 40 + +// sfxcache_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define sfxc_length 0 +#define sfxc_loopstart 4 +#define sfxc_speed 8 +#define sfxc_width 12 +#define sfxc_stereo 16 +#define sfxc_data 20 + +// channel_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define MAXSOUNDCHANNELS 6 +#define ch_sfx 0 +#define ch_vol ch_sfx+4 +#define ch_end ch_vol+4*MAXSOUNDCHANNELS +#define ch_pos ch_end+4 +#define ch_looping ch_looping+4 +#define ch_entnum ch_entnum+4 +#define ch_entchannel ch_entchannel+4 +#define ch_origin ch_origin+4 +#define ch_dist_mult ch_dist_mult+4 +#define ch_master_vol ch_master_vol+4 +#define ch_size ch_size+4 + +// portable_samplepair_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define psp_left 0 +#define psp_right 4 +#define psp_size 8 + +#endif + diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h new file mode 100644 index 000000000..36ee52288 --- /dev/null +++ b/engine/common/bothdefs.h @@ -0,0 +1,447 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __BOTHDEFS_H +#define __BOTHDEFS_H + + +#ifdef __MINGW32_VERSION + #define MINGW +#endif + +#ifdef HAVE_CONFIG_H //if it was configured properly, then we have a more correct list of features we want to use. + #include "config.h" +#else + + //#define AVAIL_OGGVORBIS + #if !defined(__CYGWIN__) && !defined(MINGW) + #define AVAIL_PNGLIB + #define AVAIL_JPEGLIB + #define AVAIL_ZLIB + #endif + #define AVAIL_MASM + //#define AVAIL_DX7 + + +//set any additional defines or libs in win32 + #ifndef AVAIL_MASM + #ifdef _WIN32 + #define NOASM + #else + #undef AVAIL_MASM //fixme + #endif + #endif + + #ifndef AVAIL_OGGVORBIS + #define NO_OGGVORBIS + #endif + + #define SVRANKING + + #ifdef MINIMAL + + #undef AVAIL_MP3 //no mp3 support + #undef AVAIL_JPEGLIB //no jpeg support + #undef AVAIL_PNGLIB //no png support + #undef USE_MADLIB //no internal mp3 playing + #undef AVAIL_DX7 //no d3d support + #define NO_OGGVORBIS //don't use oggvorbis + #define NOMEDIA //NO playing of avis/cins/roqs + #define NOVOICECHAT //NO sound recording, tcp streaming and playback on a remote client. not finalised. + + #define MD3MODELS //we DO want to use quake3 alias models. This might be a minimal build, but we still want this. + + #else + + #define SIDEVIEWS 4 //enable secondary/reverse views. + #define SP2MODELS //quake2 sprite models + #define MD2MODELS //quake2 alias models + #define MD3MODELS //quake3 alias models + #define HUFFNETWORK //huffman network compression + #define HALFLIFEMODELS //halflife model support (experimental) + #define DOOMWADS //doom wad/map/sprite support + //#define WOLF3DSUPPORT //wolfenstein3d map support (not started yet) + #define Q2BSPS //quake 2 bsp support + #define Q3BSPS //quake 3 bsp support + #define SV_MASTER //starts up a master server + #define SVCHAT //serverside npc chatting. see sv_chat.c + #define Q2SERVER //server can run a q2 game dll and switches to q2 network and everything else. + #define Q2CLIENT //client can connect to q2 servers + #define NQPROT //server and client are capable of using quake1/netquake protocols. (qw is still prefered. uses the command 'nqconnect') + #define FISH //sw rendering only + #define VM_UI //support userinterfaces within Q3 Virtual Machines + //#define VM_CG //make work + #define ZLIB //zip/pk3 support + #define WEBSERVER //http/ftp servers + #define WEBCLIENT //http/ftp clients. + #define EMAILSERVER //smtp/pop3 server should you feel a need + #define EMAILCLIENT //smtp/pop3 clients (email notifications) + #define IRCCLIENT //connects to irc servers. + #define RUNTIMELIGHTING //calculate lit/lux files the first time the map is loaded and doesn't have a loadable lit. + #define QTERM //qterm... adds a console command that allows running programs from within quake - bit like xterm. + #define CL_MASTER //query master servers and stuff for a dynamic server listing. + #define SERIALMOUSE //means that the engine talks to a serial mouse directly via the com/serial port. Thus allowing duel mice with seperate inputs. + #define R_XFLIP //allow view to be flipped horizontally + #define IN_XFLIP //allow input to be flipped horizontally. + #define TEXTEDITOR + +// #define VOICECHAT //experimental + + #endif + +#endif + +//fix things a little... + +#ifdef MINGW + #undef ZLIB + #undef AVAIL_ZLIB +#endif + +#if defined(VM_UI) || defined(VM_CG) + #define VM_ANY +#endif + +#ifdef USE_MADLIB //global option. Specify on compiler command line. + #define AVAIL_MP3 //suposedly anti-gpl. don't use in a distributed binary +#endif + +#ifndef ZLIB + #undef AVAIL_ZLIB //no zip/pk3 support +#endif + +#ifndef _WIN32 + #undef QTERM +#endif + +#if (defined(Q2CLIENT) || defined(Q2SERVER)) + #ifndef Q2BSPS + #error "Q2 game support without Q2BSP support. doesn't make sense" + #endif + #if !defined(MD2MODELS) || !defined(SP2MODELS) + #error "Q2 game support without full Q2 model support. doesn't make sense" + #endif +#endif + + +#ifdef SERVERONLY //remove options that don't make sense on only a server + #undef Q2CLIENT + #undef WEBCLIENT + #undef IRCCLIENT + #undef EMAILCLIENT + #undef TEXTEDITOR + #undef RUNTIMELIGHTING +#endif +#ifdef CLIENTONLY //remove optional server componants that make no sence on a client only build. + #undef Q2SERVER + #undef WEBSERVER + #undef EMAILSERVER +#endif + +//remove any options that depend upon GL. +#ifndef SERVERONLY + #if defined(SWQUAKE) && !defined(RGLQUAKE) && !defined(GLQUAKE) + #undef DOOMWADS + #undef HALFLIFEMODELS + #undef Q3BSPS + #undef R_XFLIP + #undef RUNTIMELIGHTING + #endif +#endif + + +#define PROTOCOLEXTENSIONS + +#define PRE_SAYONE 2.487 //FIXME: remove. + +// defs common to client and server + +#define GLQUAKE_VERSION 1.01 +#define VERSION 2.55 +#define LINUX_VERSION 0.98 + +//#define VERSION3PART //add the 3rd decimal point of a more precise version. + +#define DISTRIBUTION "FTE" +#define DISTRIBUTIONLONG "Forethought Entertainment" + +#define ENGINEWEBSITE "http://fte.quakesrc.org/" + +#ifdef _WIN32 +#define PLATFORM "Win32" +#else +#define PLATFORM "Linux" +#endif + + +#if (defined(_M_IX86) || defined(__i386__)) && !defined(id386) +#define id386 1 +#else +#define id386 0 +#endif + +#if defined(NOASM) // no asm in dedicated server +#undef id386 +#endif + +#if id386 +#define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported +#else +#define UNALIGNED_OK 0 +#endif + +#ifdef _MSC_VER +#define VARGS __cdecl +#endif +#ifndef VARGS +#define VARGS +#endif + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CACHE_SIZE 32 // used to align key data structures + +#define UNUSED(x) (x = x) // for pesky compiler / lint warnings + +#define MINIMUM_MEMORY 0x550000 + +// up / down +#define PITCH 0 + +// left / right +#define YAW 1 + +// fall over +#define ROLL 2 + + +#define MAX_SCOREBOARD 16 // max numbers of players + +#define SOUND_CHANNELS 8 + + +#define MAX_QPATH 64 // max length of a quake game pathname +#define MAX_OSPATH 256 // max length of a filesystem pathname + +#define ON_EPSILON 0.1 // point on plane side epsilon + +#define MAX_NQMSGLEN 8000 // max length of a reliable message +#define MAX_Q2MSGLEN 1400 +#define MAX_QWMSGLEN 1450 +#define MAX_OVERALLMSGLEN MAX_NQMSGLEN +#define MAX_DATAGRAM 1450 // max length of unreliable message +#define MAX_Q2DATAGRAM MAX_Q2MSGLEN +#define MAX_NQDATAGRAM 1024 // max length of unreliable message +#define MAX_OVERALLDATAGRAM MAX_DATAGRAM + +#define MAX_BACKBUFLEN 1200 + +// +// per-level limits +// +#define MAX_EDICTS 2048 // FIXME: ouch! ouch! ouch! +#define MAX_LIGHTSTYLES 64 +#define MAX_MODELS 512 // these are sent over the net as bytes +#define MAX_SOUNDS 256 // so they cannot be blindly increased + +#define SAVEGAME_COMMENT_LENGTH 39 + +#define MAX_STYLESTRING 64 + +// +// stats are integers communicated to the client by the server +// +#define MAX_QW_STATS 32 +enum { +STAT_HEALTH = 0, +//STAT_FRAGS = 1, +STAT_WEAPON = 2, +STAT_AMMO = 3, +STAT_ARMOR = 4, +STAT_WEAPONFRAME = 5, +STAT_SHELLS = 6, +STAT_NAILS = 7, +STAT_ROCKETS = 8, +STAT_CELLS = 9, +STAT_ACTIVEWEAPON = 10, +STAT_TOTALSECRETS = 11, +STAT_TOTALMONSTERS = 12, +STAT_SECRETS = 13, // bumped on client side by svc_foundsecret +STAT_MONSTERS = 14, // bumped by svc_killedmonster +STAT_ITEMS = 15, +STAT_VIEWHEIGHT = 16, +STAT_TIME = 17, +#ifdef SIDEVIEWS +STAT_VIEW2 = 20, +#endif + +STAT_H2_LEVEL = 32, // changes stat bar +STAT_H2_INTELLIGENCE, // changes stat bar +STAT_H2_WISDOM, // changes stat bar +STAT_H2_STRENGTH, // changes stat bar +STAT_H2_DEXTERITY, // changes stat bar +STAT_H2_BLUEMANA, // changes stat bar +STAT_H2_GREENMANA, // changes stat bar +STAT_H2_EXPERIENCE, // changes stat bar +STAT_H2_CNT_TORCH, // changes stat bar +STAT_H2_CNT_H_BOOST, // changes stat bar +STAT_H2_CNT_SH_BOOST, // changes stat bar +STAT_H2_CNT_MANA_BOOST, // changes stat bar +STAT_H2_CNT_TELEPORT, // changes stat bar +STAT_H2_CNT_TOME, // changes stat bar +STAT_H2_CNT_SUMMON, // changes stat bar +STAT_H2_CNT_INVISIBILITY, // changes stat bar +STAT_H2_CNT_GLYPH, // changes stat bar +STAT_H2_CNT_HASTE, // changes stat bar +STAT_H2_CNT_BLAST, // changes stat bar +STAT_H2_CNT_POLYMORPH, // changes stat bar +STAT_H2_CNT_FLIGHT, // changes stat bar +STAT_H2_CNT_CUBEOFFORCE, // changes stat bar +STAT_H2_CNT_INVINCIBILITY, // changes stat bar +STAT_H2_ARTIFACT_ACTIVE, +STAT_H2_ARTIFACT_LOW, +STAT_H2_MOVETYPE, +STAT_H2_CAMERAMODE, +STAT_H2_HASTED, +STAT_H2_INVENTORY, +STAT_H2_RINGS_ACTIVE, + +STAT_H2_RINGS_LOW, +STAT_H2_AMULET, +STAT_H2_BRACER, +STAT_H2_BREASTPLATE, +STAT_H2_HELMET, +STAT_H2_FLIGHT_T, +STAT_H2_WATER_T, +STAT_H2_TURNING_T, +STAT_H2_REGEN_T, +STAT_H2_PUZZLE1A, +STAT_H2_PUZZLE1B, +STAT_H2_PUZZLE1C, +STAT_H2_PUZZLE1D, +STAT_H2_PUZZLE2A, +STAT_H2_PUZZLE2B, +STAT_H2_PUZZLE2C, +STAT_H2_PUZZLE2D, +STAT_H2_PUZZLE3A, +STAT_H2_PUZZLE3B, +STAT_H2_PUZZLE3C, +STAT_H2_PUZZLE3D, +STAT_H2_PUZZLE4A, +STAT_H2_PUZZLE4B, +STAT_H2_PUZZLE4C, +STAT_H2_PUZZLE4D, +STAT_H2_PUZZLE5A, +STAT_H2_PUZZLE5B, +STAT_H2_PUZZLE5C, +STAT_H2_PUZZLE5D, +STAT_H2_PUZZLE6A, +STAT_H2_PUZZLE6B, +STAT_H2_PUZZLE6C, +STAT_H2_PUZZLE6D, +STAT_H2_PUZZLE7A, +STAT_H2_PUZZLE7B, +STAT_H2_PUZZLE7C, +STAT_H2_PUZZLE7D, +STAT_H2_PUZZLE8A, +STAT_H2_PUZZLE8B, +STAT_H2_PUZZLE8C, +STAT_H2_PUZZLE8D, +STAT_H2_MAXHEALTH, +STAT_H2_MAXMANA, +STAT_H2_FLAGS, + +MAX_CL_STATS = 128 +}; + +// +// item flags +// +#define IT_SHOTGUN 1 +#define IT_SUPER_SHOTGUN 2 +#define IT_NAILGUN 4 +#define IT_SUPER_NAILGUN 8 + +#define IT_GRENADE_LAUNCHER 16 +#define IT_ROCKET_LAUNCHER 32 +#define IT_LIGHTNING 64 +#define IT_SUPER_LIGHTNING 128 + +#define IT_SHELLS 256 +#define IT_NAILS 512 +#define IT_ROCKETS 1024 +#define IT_CELLS 2048 + +#define IT_AXE 4096 + +#define IT_ARMOR1 8192 +#define IT_ARMOR2 16384 +#define IT_ARMOR3 32768 + +#define IT_SUPERHEALTH 65536 + +#define IT_KEY1 131072 +#define IT_KEY2 262144 + +#define IT_INVISIBILITY 524288 + +#define IT_INVULNERABILITY 1048576 +#define IT_SUIT 2097152 +#define IT_QUAD 4194304 + +#define IT_SIGIL1 (1<<28) + +#define IT_SIGIL2 (1<<29) +#define IT_SIGIL3 (1<<30) +#define IT_SIGIL4 (1<<31) + +// +// print flags +// +#define PRINT_LOW 0 // pickup messages +#define PRINT_MEDIUM 1 // death messages +#define PRINT_HIGH 2 // critical messages +#define PRINT_CHAT 3 // chat messages + + + +//split screen stuff +#define MAX_SPLITS 4 + + + + +//savegame vars +#define SAVEGAME_COMMENT_LENGTH 39 +#define SAVEGAME_VERSION 667 + + + + + +#define dem_cmd 0 +#define dem_read 1 +#define dem_set 2 +#define dem_multiple 3 +#define dem_single 4 +#define dem_stats 5 +#define dem_all 6 + + +#endif //ifndef __BOTHDEFS_H diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h new file mode 100644 index 000000000..408c74d91 --- /dev/null +++ b/engine/common/bspfile.h @@ -0,0 +1,732 @@ +/* +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_HULLSDQ1 4 +#define MAX_MAP_HULLSDH2 8 +#define MAX_MAP_HULLSM 16 + +#define MAX_MAP_MODELS 256 +#define MAX_MAP_BRUSHES 0x4000 +#define MAX_MAP_ENTITIES 1024 +#define MAX_MAP_ENTSTRING 65536 + +#define MAX_MAP_PLANES 8192 +#define MAX_MAP_NODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES 32767 // +#define MAX_MAP_LEAFS 32767 // +#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_MIPTEX 0x200000 +#define MAX_MAP_LIGHTING 0x100000 +#define MAX_MAP_VISIBILITY 0x200000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + + +//============================================================================= + + +#define BSPVERSION 29 +//HalfLife support +#define BSPVERSIONHL 30 + +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_HULLSDQ1]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} dq1model_t; + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; + int headnode[MAX_MAP_HULLSDH2]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} dh2model_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 Q1CONTENTS_EMPTY -1 +#define Q1CONTENTS_SOLID -2 +#define Q1CONTENTS_WATER -3 +#define Q1CONTENTS_SLIME -4 +#define Q1CONTENTS_LAVA -5 +#define Q1CONTENTS_SKY -6 + +#define FTECONTENTS_EMPTY 0 +#define FTECONTENTS_SOLID 1 +#define FTECONTENTS_WATER 2 +#define FTECONTENTS_SLIME 4 +#define FTECONTENTS_LAVA 8 +#define FTECONTENTS_SKY 16 +#define FTECONTENTS_LADDER 32 +#define FTECONTENTS_FLUID (FTECONTENTS_WATER|FTECONTENTS_SLIME|FTECONTENTS_LAVA|FTECONTENTS_SKY) //sky is a fluid for q1 code. + +// !!! 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 + qbyte 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 +#define NUM_MUSICS 2 // so the streaming media can just switch between the two, allowing it to seamlessly continue. + +// 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; + + qbyte ambient_level[NUM_AMBIENTS]; +} dleaf_t; + +//============================================================================ + +#ifndef QUAKE_GAME + +// 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 qbyte dvisdata[MAX_MAP_VISIBILITY]; + +extern int lightdatasize; +extern qbyte dlightdata[MAX_MAP_LIGHTING]; + +extern int texdatasize; +extern qbyte 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 LoadBSPFile (char *filename); +void WriteBSPFile (char *filename); +void PrintBSPFileSizes (void); + +#endif + + + + + + + + + + + + + + + + +#define MIPLEVELS 4 +typedef struct q2miptex_s +{ + char name[32]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored + char animname[32]; // next frame in animation chain + int flags; + int contents; + int value; +} q2miptex_t; + + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + +#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') + // little-endian "IBSP" + +#define Q2BSPVERSION 38 +#define Q3BSPVERSION 46 + + +// upper design bounds +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define MAX_Q2MAP_MODELS 1024 +#define MAX_Q2MAP_BRUSHES MAX_MAP_BRUSHES +#define MAX_Q2MAP_ENTITIES 2048 +#define MAX_Q2MAP_ENTSTRING 0x40000 +#define MAX_Q2MAP_TEXINFO 8192 + +#define MAX_Q2MAP_AREAS 256 +#define MAX_Q2MAP_AREAPORTALS 1024 +#define MAX_Q2MAP_PLANES 65536 +#define MAX_Q2MAP_NODES 65536 +#define MAX_Q2MAP_BRUSHSIDES 0x30000 +#define MAX_Q2MAP_LEAFS 65536 +#define MAX_Q2MAP_VERTS 65536 +#define MAX_Q2MAP_FACES 65536 +#define MAX_Q2MAP_LEAFFACES 65536 +#define MAX_Q2MAP_LEAFBRUSHES 65536 +#define MAX_Q2MAP_PORTALS 65536 +#define MAX_Q2MAP_EDGES 128000 +#define MAX_Q2MAP_SURFEDGES 256000 +#define MAX_Q2MAP_LIGHTING 0x200000 +#define MAX_Q2MAP_VISIBILITY MAX_MAP_VISIBILITY + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + + +#define Q2LUMP_ENTITIES 0 +#define Q2LUMP_PLANES 1 +#define Q2LUMP_VERTEXES 2 +#define Q2LUMP_VISIBILITY 3 +#define Q2LUMP_NODES 4 +#define Q2LUMP_TEXINFO 5 +#define Q2LUMP_FACES 6 +#define Q2LUMP_LIGHTING 7 +#define Q2LUMP_LEAFS 8 +#define Q2LUMP_LEAFFACES 9 +#define Q2LUMP_LEAFBRUSHES 10 +#define Q2LUMP_EDGES 11 +#define Q2LUMP_SURFEDGES 12 +#define Q2LUMP_MODELS 13 +#define Q2LUMP_BRUSHES 14 +#define Q2LUMP_BRUSHSIDES 15 +#define Q2LUMP_POP 16 +#define Q2LUMP_AREAS 17 +#define Q2LUMP_AREAPORTALS 18 +#define Q2HEADER_LUMPS 19 + +enum Q3LUMP +{ + Q3LUMP_ENTITIES =0, + Q3LUMP_SHADERS =1, + Q3LUMP_PLANES =2, + Q3LUMP_NODES =3, + Q3LUMP_LEAFS =4, + Q3LUMP_LEAFSURFACES =5, + Q3LUMP_LEAFBRUSHES =6, + Q3LUMP_MODELS =7, + Q3LUMP_BRUSHES =8, + Q3LUMP_BRUSHSIDES =9, + Q3LUMP_DRAWVERTS =10, + Q3LUMP_DRAWINDEXES =11, + Q3LUMP_FOGS =12, + Q3LUMP_SURFACES =13, + Q3LUMP_LIGHTMAPS =14, + Q3LUMP_LIGHTGRID =15, + Q3LUMP_VISIBILITY =16, + Q3LUMPS_TOTAL +}; + +typedef struct +{ + int ident; + int version; + lump_t lumps[Q2HEADER_LUMPS]; +} q2dheader_t; + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; // for sounds or lights + int headnode; + int firstface, numfaces; // submodels just draw faces + // without walking the bsp tree +} q2dmodel_t; + +typedef struct +{ + float mins[3]; + float maxs[3]; + int firstsurface; + int num_surfaces; + int firstbrush; + int num_brushes; +} q3dmodel_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 + + + + +// contents flags are seperate bits +// a given brush can contribute multiple content bits +// multiple brushes can be in a single leaf + +// these definitions also need to be in q_shared.h! + +// lower bits are stronger, and will eat weaker brushes completely +#define Q2CONTENTS_SOLID 1 // an eye is never valid in a solid +#define Q2CONTENTS_WINDOW 2 // translucent, but not watery +#define Q2CONTENTS_AUX 4 +#define Q2CONTENTS_LAVA 8 +#define Q2CONTENTS_SLIME 16 +#define Q2CONTENTS_WATER 32 +#define Q2CONTENTS_MIST 64 +#define Q2LAST_VISIBLE_CONTENTS 64 + +// remaining contents are non-visible, and don't eat brushes + +#define Q2CONTENTS_AREAPORTAL 0x8000 + +#define Q2CONTENTS_PLAYERCLIP 0x10000 +#define Q2CONTENTS_MONSTERCLIP 0x20000 + +// currents can be added to any other contents, and may be mixed +#define Q2CONTENTS_CURRENT_0 0x40000 +#define Q2CONTENTS_CURRENT_90 0x80000 +#define Q2CONTENTS_CURRENT_180 0x100000 +#define Q2CONTENTS_CURRENT_270 0x200000 +#define Q2CONTENTS_CURRENT_UP 0x400000 +#define Q2CONTENTS_CURRENT_DOWN 0x800000 + +#define Q2CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity + +#define Q2CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game +#define Q2CONTENTS_DEADMONSTER 0x4000000 +#define Q2CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs +#define Q2CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans +#define Q3CONTENTS_TRANSLUCENT 0x20000000 +#define Q2CONTENTS_LADDER 0x20000000 + + + +#define SURF_LIGHT 0x1 // value will hold the light strength + +#define SURF_SLICK 0x2 // effects game physics + +#define SURF_SKY 0x4 // don't draw, but add to skybox +#define SURF_WARP 0x8 // turbulent water warp +#define SURF_TRANS33 0x10 +#define SURF_TRANS66 0x20 +#define SURF_FLOWING 0x40 // scroll towards angle +#define SURF_NODRAW 0x80 // don't bother referencing the texture + +// content masks +#define MASK_ALL (-1) +#define MASK_SOLID (Q2CONTENTS_SOLID|Q2CONTENTS_WINDOW) +#define MASK_PLAYERSOLID (Q2CONTENTS_SOLID|Q2CONTENTS_PLAYERCLIP|Q2CONTENTS_WINDOW|Q2CONTENTS_MONSTER) +#define MASK_DEADSOLID (Q2CONTENTS_SOLID|Q2CONTENTS_PLAYERCLIP|Q2CONTENTS_WINDOW) +#define MASK_MONSTERSOLID (Q2CONTENTS_SOLID|Q2CONTENTS_MONSTERCLIP|Q2CONTENTS_WINDOW|Q2CONTENTS_MONSTER) +#define MASK_WATER (Q2CONTENTS_WATER|Q2CONTENTS_LAVA|Q2CONTENTS_SLIME) +#define MASK_OPAQUE (Q2CONTENTS_SOLID|Q2CONTENTS_SLIME|Q2CONTENTS_LAVA) +#define MASK_SHOT (Q2CONTENTS_SOLID|Q2CONTENTS_MONSTER|Q2CONTENTS_WINDOW|Q2CONTENTS_DEADMONSTER) +#define MASK_CURRENT (Q2CONTENTS_CURRENT_0|Q2CONTENTS_CURRENT_90|Q2CONTENTS_CURRENT_180|Q2CONTENTS_CURRENT_270|Q2CONTENTS_CURRENT_UP|Q2CONTENTS_CURRENT_DOWN) + + + +typedef struct +{ + int planenum; + int children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for frustom culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} q2dnode_t; + +typedef struct +{ + int plane; + int children[2]; + int mins[3]; + int maxs[3]; +} q3dnode_t; + + +typedef struct q2texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int flags; // miptex flags + overrides + int value; // light emission, etc + char texture[32]; // texture name (textures/ *.wal) + int nexttexinfo; // for animations, -1 = end of chain +} q2texinfo_t; + + + +typedef struct +{ + int contents; // OR of all brushes (not needed?) + + short cluster; + short area; + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; +} q2dleaf_t; + +typedef struct +{ + int cluster; + int area; + int mins[3]; + int maxs[3]; + int firstleafsurface; + int num_leafsurfaces; + int firstleafbrush; + int num_leafbrushes; +} q3dleaf_t; + + +typedef struct +{ + unsigned short planenum; // facing out of the leaf + short texinfo; +} q2dbrushside_t; + +typedef struct +{ + int planenum; + int texinfo; +} q3dbrushside_t; + +typedef struct +{ + int firstside; + int numsides; + int contents; +} q2dbrush_t; + + +typedef struct +{ + int firstside; + int num_sides; + int shadernum; +} q3dbrush_t; + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +// the visibility lump consists of a header with a count, then +// qbyte offsets for the PVS and PHS of each cluster, then the raw +// compressed bit vectors +#define DVIS_PVS 0 +#define DVIS_PHS 1 +typedef struct +{ + int numclusters; + int bitofs[8][2]; // bitofs[numclusters][2] +} q2dvis_t; + +typedef struct +{ + int numclusters; + int rowsize; + unsigned char data[1]; +} q3dvis_t; + +// each area has a list of portals that lead into other areas +// when portals are closed, other areas may not be visible or +// hearable even if the vis info says that it should be +typedef struct +{ + int portalnum; + int otherarea; +} q2dareaportal_t; + +typedef struct +{ + int numareaportals; + int firstareaportal; +} q2darea_t; + + + + + + + + + + + + + + + + + + + +typedef struct +{ + char shadername[64]; + int surfflags; + int contents; +} dq3shader_t; + +typedef struct +{ + float n[3]; + float d; +} Q3PLANE_t; + +struct Q3MODEL +{ + float mins[3]; + float maxs[3]; + int firstsurface; + int num_surfaces; + int firstbrush; + int num_brushes; +}; + + +typedef struct +{ + float point[3]; + float texcoords[2][2]; + float normal[3]; + unsigned char color[4]; +} q3dvertex_t; + +struct Q3FOG +{ + char shadername[64] ; + int brushnum; + int visibleside; +}; + +enum q3surfacetype +{ + MST_BAD=0, + MST_PLANAR=1, + MST_PATCH=2, + MST_TRIANGLE_SOUP=3, + MST_FLARE=4 +}; + +typedef struct +{ + int shadernum; + int fognum; + int facetype; + int firstvertex; + int num_vertices; + int firstindex; + int num_indexes; + int lightmapnum; + int lightmap_x; + int lightmap_y; + int lightmap_width; + int lightmap_height; + float lightmap_origin[3]; + float lightmap_vecs[2][3]; + float normal[3]; + int patchwidth; + int patchheight; +} q3dface_t; diff --git a/engine/common/cmd.c b/engine/common/cmd.c new file mode 100644 index 000000000..538a32b36 --- /dev/null +++ b/engine/common/cmd.c @@ -0,0 +1,2665 @@ +/* +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" + +cvar_t com_fs_cache = {"fs_cache", "0", NULL, CVAR_ARCHIVE}; +cvar_t rcon_level = {"rcon_level", "50"}; +cvar_t cmd_onestuffwonder = {"cmd_stuffcmdonce", "1"}; +cvar_t cmd_maxbuffersize = {"cmd_maxbuffersize", "65536"}; + +int Cmd_ExecLevel; + +void Cmd_ForwardToServer (void); + +#define MAX_ALIAS_NAME 32 + +typedef struct cmdalias_s +{ + struct cmdalias_s *next; + char name[MAX_ALIAS_NAME]; + char *value; + qbyte execlevel; + qbyte restriction; + int flags; +} cmdalias_t; + +#define ALIAS_FROMSERVER 1 + +cmdalias_t *cmd_alias; + +cvar_t cl_warncmd = {"cl_warncmd", "0"}; +cvar_t cl_aliasoverlap = {"cl_aliasoverlap", "1", NULL, CVAR_NOTFROMSERVER}; + + + + +//============================================================================= + + + + + + + + + + +#define MAX_MACROS 64 + +typedef struct { + char name[32]; + char *(*func) (void); +} macro_command_t; + +static macro_command_t macro_commands[MAX_MACROS]; +static int macro_count = 0; + +void Cmd_AddMacro(char *s, char *(*f)(void)) +{ + if (macro_count == MAX_MACROS) + Sys_Error("Cmd_AddMacro: macro_count == MAX_MACROS"); + Q_strncpyz(macro_commands[macro_count].name, s, sizeof(macro_commands[macro_count].name)); + macro_commands[macro_count++].func = f; +} + +char *TP_MacroString (char *s) +{ + int i; + macro_command_t *macro; + + for (i = 0; i < macro_count; i++) + { + macro = ¯o_commands[i]; + if (!Q_strcasecmp(s, macro->name)) + { + return macro->func(); + } + macro++; + } + return NULL; +} + +void Cmd_MacroList_f (void) +{ + int i; + + if (!macro_count) + { + Con_Printf("No macros!"); + return; + } + + for (i = 0; i < macro_count; i++) + Con_Printf ("$%s\n", macro_commands[i].name); +} + + + + + + + + +/* +============================================================================= + + COMMAND BUFFER + +============================================================================= +*/ + +struct { + sizebuf_t buf; + int noclear; + double waitattime; +} cmd_text[RESTRICT_MAX+1+MAX_SPLITS]; //max is local. + //RESTRICT_MAX+1 is the from sever buffer (max+2 is for second player...) + +/* +============ +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_text[Cmd_ExecLevel].waitattime = realtime; +} + +/* +============ +Cbuf_Init +============ +*/ +void Cbuf_Init (void) +{ + int level; + for (level = 0; level <= RESTRICT_MAX+1; level++) + cmd_text[level].waitattime = -1; +} + +/* +============ +Cbuf_AddText + +Adds command text at the end of the buffer +============ +*/ +void Cbuf_AddText (const char *text, int level) +{ + int l; + + if (level > sizeof(cmd_text)/sizeof(cmd_text[0]) || level < 0) + { + Con_Printf("Bad execution level\n"); + return; //reject. + } + + l = Q_strlen (text); + + if (!cmd_text[level].buf.maxsize) + { + cmd_text[level].buf.data = Z_Malloc(8192); + cmd_text[level].buf.maxsize = 8192; + } + if (cmd_text[level].buf.cursize + l >= cmd_text[level].buf.maxsize) + { + int newmax; + char *newbuf; + + newmax = cmd_text[level].buf.maxsize*2; + + if (newmax > cmd_maxbuffersize.value && cmd_maxbuffersize.value) + { + Con_TPrintf (TL_FUNCOVERFLOW, "Cbuf_AddText"); + return; + } + while (newmax < cmd_text[level].buf.cursize + l) + newmax*=2; + newbuf = Z_Malloc(newmax); + cmd_text[level].buf.data = Z_Malloc(newmax); + cmd_text[level].buf.maxsize = newmax; + } + SZ_Write (&cmd_text[level].buf, text, Q_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 (const char *text, int level) +{ + char *temp; + int templen; + + if (level > sizeof(cmd_text)/sizeof(cmd_text[0]) || level < 0) + { + Con_Printf("Bad execution level\n"); + return; //reject. + } + +// copy off any commands still remaining in the exec buffer + templen = cmd_text[level].buf.cursize; + if (templen) + { + temp = Z_Malloc (templen+1); + Q_memcpy (temp, cmd_text[level].buf.data, templen); + SZ_Clear (&cmd_text[level].buf); + } + else + temp = NULL; // shut up compiler + +// add the entire text of the file + Cbuf_AddText (text, level); + SZ_Write (&cmd_text[level].buf, "\n", 1); +// add the copied off data + if (templen) + { + temp[templen] = '\0'; + Cbuf_AddText(temp, level); +// SZ_Write (&cmd_text[level].buf, temp, templen); + Z_Free (temp); + } +} + +char *Cbuf_GetNext(int level) +{ + int i; + char *text; + int quotes; + static char line[1024]; + +start: + + text = (char *)cmd_text[level].buf.data; + + quotes = 0; + for (i=0 ; i< cmd_text[level].buf.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; + } + + if (i >= sizeof(line)-1) + { + Con_Printf("Statement too long\n"); + return ""; + } + + + 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[level].buf.cursize) + cmd_text[level].buf.cursize = 0; + else + { + i++; + cmd_text[level].buf.cursize -= i; + Q_memcpy (text, text+i, cmd_text[level].buf.cursize); + } + +// Con_Printf("Found \"%s\"\n", line); + text=line; + while(*text == ' ' || *text == '\t') + text++; + + if (!*text) + if (cmd_text[level].buf.cursize) + goto start; //should be a while. + + return text; +} + +char *Cbuf_StripText(int level) //remove all text in the command buffer and return it (so it can be readded later) +{ + char *buf; + buf = Z_Malloc(cmd_text[level].buf.cursize+1); + Q_memcpy (buf, cmd_text[level].buf.data, cmd_text[level].buf.cursize); + cmd_text[level].buf.cursize = 0; + return buf; +} + +void Cbuf_ExecuteLevel (int level) +{ + int i; + char *text; + char line[1024]; + int quotes; + + while (cmd_text[level].buf.cursize) + { + if (cmd_text[level].waitattime == realtime) + { // skip out while text still remains in buffer, leaving it + // for next frame + break; + } + +// find a \n or ; line break + text = (char *)cmd_text[level].buf.data; + + quotes = 0; + for (i=0 ; i< cmd_text[level].buf.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; + } + + if (i >= sizeof(line)) + i = sizeof(line)-1; + 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[level].buf.cursize) + cmd_text[level].buf.cursize = 0; + else + { + i++; + cmd_text[level].buf.cursize -= i; + Q_memcpy (text, text+i, cmd_text[level].buf.cursize); + } + +// execute the command line + Cmd_ExecuteString (line, level); + } +} + +/* +============ +Cbuf_Execute +============ +*/ +void Cbuf_Execute (void) +{ + int level; + + for (level = 0; level <= sizeof(cmd_text)/sizeof(cmd_text[0]); level++) + Cbuf_ExecuteLevel(level); +} + +/* +============================================================================== + + 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; + + static qboolean getstuffed = true; + + if (!getstuffed && cmd_onestuffwonder.value) + return; + + getstuffed = false; + +// build the combined string to parse from + s = 0; + for (i=1 ; inext) + { + if (a->flags & ALIAS_FROMSERVER) + Con_Printf ("%s : %s\n", a->name, a->value); + } + } + } + else + { + Con_TPrintf (TL_CURRENTALIASCOMMANDS); + for (a = cmd_alias ; a ; a=a->next) + { + /* extern int con_linewidth; + if (strlen(a->value)+strlen(a->name)+3 > con_linewidth) + Con_Printf ("%s ...\n", a->name); + else*/ + Con_Printf ("%s : %s\n", a->name, a->value); + } + } + return; + } + + s = Cmd_Argv(1); + if (strlen(s) >= MAX_ALIAS_NAME || !strcmp(s, "say")) //reject aliasing the say command. We use it as an easy way to warn that our player is cheating. + { + Con_TPrintf (TL_ALIASNAMETOOLONG); + return; + } + + if (!cl_aliasoverlap.value) + { + if (Cvar_FindVar (s)) + { + if (Cmd_FromServer()) + { + _snprintf(cmd, sizeof(cmd), "%s_a", s); + Con_Printf ("Can't register alias, %s is a cvar\nAlias has been named %s instead\n", s, cmd); + s = cmd; + } + else + { + Con_Printf ("Can't register alias, %s is a cvar\n", s); + return; + } + } + + // check for overlap with a command + if (Cmd_Exists (s)) + { + if (Cmd_FromServer()) + { + _snprintf(cmd, sizeof(cmd), "%s_a", s); + Con_Printf ("Can't register alias, %s is a command\nAlias has been named %s instead\n", s, cmd); + s = cmd; + } + else + { + Con_Printf ("Can't register alias, %s is a command\n", s); + return; + } + } + } + + // if the alias allready exists, reuse it + for (a = cmd_alias ; a ; a=a->next) + { + if (!strcmp(s, a->name)) + { + if ((a->restriction?a->restriction:rcon_level.value) > Cmd_ExecLevel) + { + Con_TPrintf (TL_ALIASRESTRICTIONLEVELERROR); + return; + } + Z_Free (a->value); + break; + } + } + + if (!a) + { + a = Z_Malloc (sizeof(cmdalias_t)); + a->next = cmd_alias; + cmd_alias = a; + } + if (Cmd_FromServer()) + a->flags |= ALIAS_FROMSERVER; + else + a->flags &= ~ALIAS_FROMSERVER; + + strcpy (a->name, s); + multiline = false; + if (Cmd_Argc() == 2) //check the next statement for being '{' + { + char *line, *end; + line = Cbuf_GetNext(Cmd_ExecLevel); + + while(*line <= ' ' && *line) //skip leading whitespace. + line++; + + for (end = line + strlen(line)-1; end >= line && *end <= ' '; end--) //skip trailing + *end = '\0'; + if (!strcmp(line, "{")) + multiline = true; + else + Cbuf_InsertText(line, Cmd_ExecLevel); //whoops. Stick the trimmed string back in to the cbuf. + } + else if (!strcmp(Cmd_Argv(2), "{")) + multiline = true; + + if (multiline) + { //fun! MULTILINE ALIASES!!!! + char *newv; + char *end; + int in = 1; + a->value = NULL; + for(;;) + { + s = Cbuf_GetNext(Cmd_ExecLevel); + if (!*s) + { + Con_Printf("^1WARNING:^0Multiline alias was not terminated\n"); + break; + } + while (*s <= ' ' && *s) + s++; + for (end = s + strlen(s)-1; end >= s && *end <= ' '; end--) + *end = '\0'; + if (!strcmp(s, "{")) + in++; + else if (!strcmp(s, "}")) + { + in--; + if (!in) + break; //phew + } + if (a->value) + { + newv = Z_Malloc(strlen(a->value) + strlen(s) + 2); + sprintf(newv, "%s;%s", a->value, s); + Z_Free(a->value); + a->value = newv; + } + else + a->value = CopyString(s); + } + + return; + } + +// 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, " "); + } + + if (!*cmd) //someone wants to wipe it. let them + { + if (a == cmd_alias) + { + cmd_alias = a->next; + Z_Free(a); + } + else + { + for (b = cmd_alias ; b ; b=b->next) + { + if (b->next == a) + { + b->next = a->next; + Z_Free(a); + return; + } + } + } + } + a->execlevel = 0; //run at users exec level + a->restriction = 1; //this is possibly a security risk if the admin also changes execlevel + a->value = CopyString (cmd); +} + +void Cmd_DeleteAlias(char *name) +{ + cmdalias_t *a, *b; + if (!strcmp(cmd_alias->name, name)) + { + a = cmd_alias; + cmd_alias = cmd_alias->next; + Z_Free(a); + return; + } + for (a = cmd_alias ; a ; a=a->next) + { + if (!strcmp(a->next->name, name)) + { + b = a->next; + a->next = b->next; + Z_Free(b); + return; + } + } +} + +char *Cmd_AliasExist(char *name, int restrictionlevel) +{ + cmdalias_t *a; + // if the alias allready exists, reuse it + for (a = cmd_alias ; a ; a=a->next) + { + if (!strcmp(name, a->name)) + { + if ((a->restriction?a->restriction:rcon_level.value) > restrictionlevel) + { + return NULL; //not at this level... + } + return a->value; + } + } + return NULL; +} + +void Cmd_AliasLevel_f (void) +{ + cmdalias_t *a; + char *s = Cmd_Argv(1); + int level; + if (Cmd_Argc() < 2 || Cmd_Argc() > 3) + { + Con_TPrintf(TL_ALIASLEVELCOMMANDUSAGE); + return; + } + + for (a = cmd_alias ; a ; a=a->next) + { + if (!strcmp(s, a->name)) + { + break; + } + } + if (!a) + { + Con_TPrintf(TL_ALIASNOTFOUND); + return; + } + + if (Cmd_Argc() == 3) + { + level = atoi(Cmd_Argv(2)); + if (level > RESTRICT_MAX) + { + level = RESTRICT_MAX; + } + else if (level < RESTRICT_MIN) + level = RESTRICT_MIN; + + if (level > Cmd_ExecLevel || (a->restriction?a->restriction:rcon_level.value) > Cmd_ExecLevel) + { + Con_TPrintf(TL_ALIASRAISELEVELERROR); + return; + } + + a->execlevel = level; + + if (a->restriction == 1) + Con_TPrintf(TL_ALIASRESTRICTIONLEVELWARN, a->name); + } + else + Con_TPrintf(TL_ALIASRESTRICTLEVEL, s, a->execlevel); +} + +//lists commands, also prints restriction level +void Cmd_AliasList_f (void) +{ + cmdalias_t *cmd; + int num=0; + for (cmd=cmd_alias ; cmd ; cmd=cmd->next) + { + if ((cmd->restriction?cmd->restriction:rcon_level.value) > Cmd_ExecLevel) + continue; + if (!num) + Con_TPrintf(TL_ALIASLIST); + if (cmd->execlevel) + Con_Printf("(%2i)(%2i) %s\n", (int)(cmd->restriction?cmd->restriction:rcon_level.value), cmd->execlevel, cmd->name); + else + Con_Printf("(%2i) %s\n", (int)(cmd->restriction?cmd->restriction:rcon_level.value), cmd->name); + num++; + } + if (num) + Con_Printf("\n"); +} + +void Alias_WriteAliases (FILE *f) +{ + cmdalias_t *cmd; + int num=0; + for (cmd=cmd_alias ; cmd ; cmd=cmd->next) + { +// if ((cmd->restriction?cmd->restriction:rcon_level.value) > Cmd_ExecLevel) +// continue; + if (!num) + fprintf(f, "//////////////////\n//Aliases\n"); + fprintf(f, "alias %s \"%s\"\n", cmd->name, cmd->value); + fprintf(f, "restrict %s %i\n", cmd->name, cmd->restriction); + fprintf(f, "aliaslevel %s %i\n", cmd->name, cmd->execlevel); + num++; + } +} + +void Alias_WipeStuffedAliaes(void) +{ + cmdalias_t *cmd, *n; + for (cmd=cmd_alias ; cmd ; ) + { + if (cmd->flags & ALIAS_FROMSERVER) + { + n = cmd->next; + Cmd_DeleteAlias(cmd->name); + cmd = n; + } + else + cmd=cmd->next; + } +} + +void Cvar_List_f (void); + +/* +============================================================================= + + COMMAND EXECUTION + +============================================================================= +*/ + +typedef struct cmd_function_s +{ + struct cmd_function_s *next; + char *name; + xcommand_t function; + + qbyte restriction; //restriction of admin level + qbyte zmalloced; +} 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; + + + +static cmd_function_t *cmd_functions; // possible commands to execute + +/* +============ +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 + +Returns a single string containing argv(1) to argv(argc()-1) +============ +*/ + +char *Cmd_Args (void) +{ + if (!cmd_args) + return ""; + return cmd_args; +} + + +/* +============ +Cmd_ShiftArgs + +Shifts Cmd_Argv results down one (killing first param) +============ +*/ +void Cmd_ShiftArgs (int ammount) +{ + int arg; + while (ammount>0 && cmd_argc) + { + arg=0; + cmd_argc--; + Z_Free(cmd_argv[0]); + while ( arg < cmd_argc ) + { + cmd_argv[arg] = cmd_argv[arg+1]; + arg++; + } + cmd_argv[arg]=NULL; + + ammount--; + + if (cmd_args) + { + cmd_args = COM_StringParse(cmd_args); + if (cmd_args) + while(*cmd_args == ' ') + cmd_args++; + } + } +} + +#ifdef ZQUAKETEAMPLAY +/* +================ +Cmd_ExpandString + +Expands all $cvar expressions to cvar values +If not SERVERONLY, also expands $macro expressions +Note: dest must point to a 1024 byte buffer +================ +*/ +char *TP_MacroString (char *s); +void Cmd_ExpandString (char *data, char *dest) +{ + unsigned int c; + char buf[255]; + int i, len; + cvar_t *var, *bestvar; + int quotes = 0; + char *str; + int name_length; +#ifndef SERVERONLY + extern int macro_length; +#endif + + len = 0; + + while ( (c = *data) != 0) + { + if (c == '"') + quotes++; + + if (c == '$' && !(quotes&1)) + { + data++; + + // Copy the text after '$' to a temp buffer + i = 0; + buf[0] = 0; + bestvar = NULL; + while ((c = *data) > 32) + { + if (c == '$') + break; + data++; + buf[i++] = c; + buf[i] = 0; + if ( (var = Cvar_FindVar(buf)) != NULL ) + bestvar = var; + } + +#ifndef SERVERONLY + if (dedicated) + str = NULL; + else { + str = TP_MacroString (buf); + name_length = macro_length; + } + if (bestvar && (!str || (strlen(bestvar->name) > macro_length))) { + str = bestvar->string; + name_length = strlen(bestvar->name); + } +#else + if (bestvar) { + str = bestvar->string; + name_length = strlen(bestvar->name); + } else + str = NULL; +#endif + + if (str) + { + // check buffer size + if (len + strlen(str) >= 1024-1) + break; + + strcpy(&dest[len], str); + len += strlen(str); + i = name_length; + while (buf[i]) + dest[len++] = buf[i++]; + } + else + { + // no matching cvar or macro + dest[len++] = '$'; + if (len + strlen(buf) >= 1024-1) + break; + strcpy (&dest[len], buf); + len += strlen(buf); + } + } + else + { + dest[len] = c; + data++; + len++; + if (len >= 1024-1) + break; + } + }; + + dest[len] = 0; +} + +#else + +/* +============ +Cmd_ExpandString + +Returns a string with all expanded macros. +============ +*/ +char *Cmd_ExpandString (char *input, int maxaccesslevel) +{ + static unsigned char buffer[8192]; + unsigned char *s; + int c, len; + + Q_strncpyz(buffer, input, sizeof(buffer)); + + len = strlen(buffer); + + //we check for macros. + for (s = buffer, c= 0; c < len; c++, s++) //this isn't a quoted token by the way. + { + if (*s == '$') + { + cvar_t *cvar; + char *macro; + char name[64]; + int i; + + for (i = 1; i < sizeof(name); i++) + { + if (s[i] <= ' ' || s[i] == '$' || s[i] == '\"') + break; + } + + Q_strncpyz(name, s+1, i); + i-=1; + name[i] = '\0'; + + if (!*name) + { + memmove(buffer+c, buffer+c+1, len-c); + c--; + len--; + } + else + while(*name) + { + cvar = Cvar_FindVar(name); + if (cvar && (cvar->restriction?cvar->restriction:rcon_level.value) <= maxaccesslevel) //got one... + { + memmove(s+strlen(cvar->string), s+i+1, len-c-i); + Q_strncpyS(s, cvar->string, strlen(cvar->string)); + s+=strlen(cvar->string); + len+=strlen(cvar->string)-(i+1); + break; + } + if (maxaccesslevel == RESTRICT_LOCAL) //don't expand without full rights. (prevents a few loopholes) + { + macro = TP_MacroString(name); + if (macro) + { + memmove(s+strlen(macro), s+i+1, len-c-i); + Q_strncpyS(s, macro, strlen(macro)); + s+=strlen(macro); + len+=strlen(macro)-(i+1); + break; + } + } + + i--; + name[i] = '\0'; + } + } + } + + if (strlen(buffer) >= sizeof(buffer)) + Sys_Error("Buffer was too small\n"); + + return (char *)buffer; +} + +#endif + +/* +============ +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 ; inext) + { + if (!Q_strcmp (cmd_name, cmd->name)) + { + Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + } + + cmd = Hunk_AllocName (sizeof(cmd_function_t), cmd_name); + cmd->name = cmd_name; + cmd->function = function; + cmd->next = cmd_functions; + cmd->restriction = 0; + cmd_functions = cmd; +} + +void Cmd_AddRemCommand (char *cmd_name, xcommand_t function) +{ + cmd_function_t *cmd; + +// 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 (!Q_strcmp (cmd_name, cmd->name)) + { + Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + } + + cmd = Z_Malloc (sizeof(cmd_function_t)); + cmd->name = cmd_name; + cmd->function = function; + cmd->next = cmd_functions; + cmd->restriction = 0; + cmd->zmalloced = true; + cmd_functions = cmd; +} + +void Cmd_RemoveCommand (char *cmd_name) +{ + cmd_function_t *cmd, **back; + + back = &cmd_functions; + while (1) + { + cmd = *back; + if (!cmd) + { + Con_Printf ("Cmd_RemoveCommand: %s not added\n", cmd_name); + return; + } + if (!strcmp (cmd_name, cmd->name)) + { + *back = cmd->next; + if (!cmd->zmalloced) + { + Con_Printf("Cmd_RemoveCommand: %s was not added dynamically\n", cmd_name); + return; + } + Z_Free (cmd); + return; + } + back = &cmd->next; + } +} + +void Cmd_RestrictCommand_f (void) +{ + cmdalias_t *a; + cvar_t *v; + cmd_function_t *cmd; + char *cmd_name = Cmd_Argv(1); + int level = atoi(Cmd_Argv(2)); + + if (Cmd_Argc() != 3 && Cmd_Argc() != 2) + { + Con_Printf("restrict [level]\n"); + return; + } + + if (Cmd_Argc() == 2) + { + if (level > RESTRICT_MAX) + { + level = RESTRICT_MAX; + if (level > Cmd_ExecLevel) + { + Con_TPrintf(TL_RESTRICTCOMMANDRAISE); + return; + } + } + else if (level < RESTRICT_MIN) + level = RESTRICT_MIN; + } +//commands + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (!Q_strcmp (cmd_name, cmd->name)) + { + if (Cmd_Argc() == 2) + { + if (cmd->restriction) + Con_TPrintf (TL_RESTRICTCURRENTLEVEL, cmd_name, (int)cmd->restriction); + else + Con_TPrintf (TL_RESTRICTCURRENTLEVELDEFAULT, cmd_name, (int)rcon_level.value); + } + else if ((cmd->restriction?cmd->restriction:rcon_level.value) > Cmd_ExecLevel) + Con_TPrintf(TL_RESTRICTCOMMANDTOOHIGH); + else + cmd->restriction = level; + return; + } + } + +//cvars + v = Cvar_FindVar(cmd_name); + if (v) + { + if (Cmd_Argc() == 2) + { + if (v->restriction) + Con_TPrintf (TL_RESTRICTCURRENTLEVEL, cmd_name, (int)v->restriction); + else + Con_TPrintf (TL_RESTRICTCURRENTLEVELDEFAULT, cmd_name, (int)rcon_level.value); + } + else if ((v->restriction?v->restriction:rcon_level.value) > Cmd_ExecLevel) + Con_TPrintf(TL_RESTRICTCOMMANDTOOHIGH); + else + v->restriction = level; + + return; + } + + // check alias + for (a=cmd_alias ; a ; a=a->next) + { + if (!Q_strcasecmp (cmd_name, a->name)) + { + if (Cmd_Argc() == 2) + { + if (a->restriction) + Con_TPrintf (TL_RESTRICTCURRENTLEVEL, cmd_name, (int)a->restriction); + else + Con_TPrintf (TL_RESTRICTCURRENTLEVELDEFAULT, cmd_name, (int)rcon_level.value); + } + else if ((a->restriction?a->restriction:rcon_level.value) > Cmd_ExecLevel) + Con_TPrintf(TL_RESTRICTCOMMANDTOOHIGH); + else + a->restriction = level; + return; + } + } + + Con_TPrintf (TL_RESTRICTNOTDEFINED, cmd_name); + return; +} + +int Cmd_Level(char *name) +{ + cmdalias_t *a; + cmd_function_t *cmds; + for (cmds = cmd_functions; cmds; cmds=cmds->next) + { + if (!strcmp(cmds->name, name)) + { + return cmds->restriction?cmds->restriction:rcon_level.value; + } + } + for (a=cmd_alias ; a ; a=a->next) + { + if (!strcmp(a->name, name)) + { + return a->restriction?a->restriction:Cmd_ExecLevel; + } + } + return -1; +} + +/* +============ +Cmd_Exists +============ +*/ +qboolean Cmd_Exists (char *cmd_name) +{ + cmd_function_t *cmd; + + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (!Q_strcmp (cmd_name,cmd->name)) + return true; + } + + return false; +} + + + +/* +============ +Cmd_CompleteCommand +============ +*/ +typedef struct { + int matchnum; + qboolean allowcutdown; + qboolean cutdown; + char result[256]; +} match_t; +void Cmd_CompleteCheck(char *check, match_t *match) //compare cumulative strings and join the result +{ + if (*match->result) + { + char *r; + if (match->allowcutdown) + { + for(r = match->result; *r == *check && *r; r++, check++) + ; + *r = '\0'; + match->cutdown = true; + if (match->matchnum > 0) + match->matchnum--; + } + else if (match->matchnum > 0) + { + strcpy(match->result, check); + match->matchnum--; + } + } + else + { + if (match->matchnum > 0) + { + strcpy(match->result, check); + match->matchnum--; + } + else + strcpy(match->result, check); + } +} +char *Cmd_CompleteCommand (char *partial, qboolean fullonly, int matchnum) +{ + extern cvar_group_t *cvar_groups; + cmd_function_t *cmd; + int len; + cmdalias_t *a; + + static match_t match; + + cvar_group_t *grp; + cvar_t *cvar; + + len = Q_strlen(partial); + + if (!len) + return NULL; + + if (matchnum == -1) + len++; + + match.allowcutdown = !fullonly; + match.cutdown = false; + if (matchnum) + match.matchnum = matchnum; + else + match.matchnum = 0; + + strcpy(match.result, ""); + +// check for partial match + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + if (!Q_strncmp (partial,cmd->name, len)) + Cmd_CompleteCheck(cmd->name, &match); + for (a=cmd_alias ; a ; a=a->next) + if (!Q_strncmp (partial, a->name, len)) + Cmd_CompleteCheck(a->name, &match); + for (grp=cvar_groups ; grp ; grp=grp->next) + for (cvar=grp->cvars ; cvar ; cvar=cvar->next) + if (!Q_strncmp (partial,cvar->name, len)) + Cmd_CompleteCheck(cvar->name, &match); + + if (match.matchnum>0) + return NULL; + if (!*match.result) + return NULL; + return match.result; +} + +//lists commands, also prints restriction level +void Cmd_List_f (void) +{ + cmd_function_t *cmd; + int num=0; + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if ((cmd->restriction?cmd->restriction:rcon_level.value) > Cmd_ExecLevel) + continue; + if (!num) + Con_TPrintf(TL_COMMANDLISTHEADER); + Con_Printf("(%2i) %s\n", (int)(cmd->restriction?cmd->restriction:rcon_level.value), cmd->name); + num++; + } + if (num) + Con_Printf("\n"); +} + + +#ifndef SERVERONLY // FIXME +/* +=================== +Cmd_ForwardToServer + +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_ForwardToServer (void) +{ + if (cls.state == ca_disconnected) + { + Con_TPrintf (TL_CANTXNOTCONNECTED, Cmd_Argv(0)); + return; + } + + if (cls.demoplayback) + return; // not really connected + +#ifdef Q2CLIENT + MSG_WriteByte (&cls.netchan.message, cls.q2server?clcq2_stringcmd:clc_stringcmd); +#else + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); +#endif + SZ_Print (&cls.netchan.message, Cmd_Argv(0)); + if (Cmd_Argc() > 1) + { + SZ_Print (&cls.netchan.message, " "); + SZ_Print (&cls.netchan.message, Cmd_Args()); + } +} + +// don't forward the first argument +void Cmd_ForwardToServer_f (void) +{ + if (cls.state == ca_disconnected) + { + Con_TPrintf (TL_CANTXNOTCONNECTED, Cmd_Argv(0)); + return; + } + + if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) { + if (SCR_RSShot()) + return; + } + + if (cls.demoplayback) + return; // not really connected + + if (Cmd_Argc() > 1) + { +#ifdef Q2CLIENT + MSG_WriteByte (&cls.netchan.message, cls.q2server?clcq2_stringcmd:clc_stringcmd); +#else + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); +#endif + SZ_Print (&cls.netchan.message, Cmd_Args()); + } +} +#else +void Cmd_ForwardToServer (void) +{ +} +#endif + +/* +============ +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, int level) +{ + cmd_function_t *cmd; + cmdalias_t *a; + + Cmd_ExecLevel = level; + + text = Cmd_ExpandString(text, level); + 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)) + { + if (strcmp (cmd_argv[0],cmd->name)) + break; //yes, I know we found it... (but it's the wrong case, go for an alias or cvar instead FIRST) + + if ((cmd->restriction?cmd->restriction:rcon_level.value) > level) + Con_TPrintf(TL_WASRESTIRCTED, cmd_argv[0]); + else if (!cmd->function) + Cmd_ForwardToServer (); + else + cmd->function (); + return; + } + } + +// check alias + for (a=cmd_alias ; a ; a=a->next) + { + if (!Q_strcasecmp (cmd_argv[0], a->name)) + { + int i; + + if ((a->restriction?a->restriction:rcon_level.value) > level) + { + Con_TPrintf(TL_WASRESTIRCTED, cmd_argv[0]); + return; + } + if (a->execlevel) + level = a->execlevel; + + Cbuf_InsertText ("\n", level); + Cbuf_InsertText (a->value, level); + + Cbuf_InsertText (va("set cmd_argc \"%i\"\n", cmd_argc), level); + + for (i = 1; i < cmd_argc; i++) + Cbuf_InsertText (va("set cmd_argv%i \"%s\"\n", i, cmd_argv[i]), level); + return; + } + } + +// check cvars + if (Cvar_Command (level)) + return; + + + + if (cmd) //go for skipped ones + { + if ((cmd->restriction?cmd->restriction:rcon_level.value) > level) + Con_TPrintf(TL_WASRESTIRCTED, cmd_argv[0]); + else if (!cmd->function) + Cmd_ForwardToServer (); + else + cmd->function (); + + return; + } + +#ifndef CLIENTONLY + if (sv.state) + { + if (PR_ConsoleCmd()) + return; + } +#endif + +#ifdef Q2CLIENT + if (cls.q2server) + Cmd_ForwardToServer(); + else +#endif + if (cl_warncmd.value || developer.value) + Con_TPrintf (TL_COMMANDNOTDEFINED, Cmd_Argv(0)); +} + + + +/* +================ +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; +} + + + + + + + + + + + + + + + + +typedef struct tempstack_s{ + struct tempstack_s *next; + char str[1]; +} tempstack_t; +tempstack_t *ifstack; + +void If_Token_Clear (tempstack_t *mark) +{ + tempstack_t *ois; + while(ifstack) + { + if (ifstack == mark) + break; + ois = ifstack; + ifstack = ifstack->next; + Z_Free(ois); + } +} + +tempstack_t *If_Token_GetMark (void) +{ + return ifstack; +} + + +char *retstring(char *s) +{ +// return s; + tempstack_t *ret; + ret = Z_Malloc(sizeof(tempstack_t)+strlen(s)); + ret->next = ifstack; + ifstack=ret; + strcpy(ret->str, s); + return ret->str; +} +char *retfloat(float f) +{ + char s[1024]; + tempstack_t *ret; + if (!f) + return ""; + sprintf(s, "%f", f); + ret = Z_Malloc(sizeof(tempstack_t)+strlen(s)); + ret->next = ifstack; + ifstack=ret; + strcpy(ret->str, s); + return ret->str; +} +qboolean is_numeric (char *c) +{ + return (*c >= '0' && *c <= '9') || + ((*c == '-' || *c == '+') && (c[1] == '.' || (c[1]>='0' && c[1]<='9'))) || + (*c == '.' && (c[1]>='0' && c[1]<='9')); +} +char *If_Token(char *func, char **end) +{ + char *s, *s2; + cvar_t *var; + int level; + while(*func <= ' ' && *func) + func++; + + s = COM_ParseToken(func); + + if (*com_token == '(') + { + s2 = s; + level=1; + while (*s2) + { + if (*s2 == ')') + { + level--; + if (!level) + break; + } + else if (*s2 == '(') + level++; + s2++; + } + func = If_Token(s, end); + *end = s2+1; + s = *end; + s2 = func; +// return func; + } + else if (*com_token == '!') + { + func = If_Token(s, end); + for (s = func; *s; s++); + if (func && *func) + return ""; + else + return "true"; + } + else if (!strcmp(com_token, "defined")) //functions + { + s = COM_ParseToken(s); + var = Cvar_FindVar(com_token); + *end = s; + return retstring((var != NULL)?"true":""); + } + else if (!strcmp(com_token, "random")) + { + s2 = retfloat((rand()&0x7fff) / (float)0x7fff); + } + else if (!strcmp(com_token, "vid")) //mostly for use with the menu system. + { + s = COM_ParseToken(s); +#ifndef SERVERONLY + if (qrenderer == QR_NONE) + s2 = ""; + else if (!strcmp(com_token, "width")) + s2 = retfloat(vid.width); + else if (!strcmp(com_token, "height")) + s2 = retfloat(vid.height); + else +#endif + s2 = ""; + } + else + { + if (*com_token == '$') + var = Cvar_FindVar(com_token+1); + else + var = Cvar_FindVar(com_token); //for consistancy. + if (var) + { + if ((var->restriction?var->restriction:rcon_level.value) > Cmd_ExecLevel) + s2 = "RESTRICTED"; + else + s2 = var->string; + } + else + s2 = retstring(com_token); + } + + *end = s; + + s = COM_ParseToken(s); + if (!strcmp(com_token, "=")) //comparisions + { + func=COM_ParseToken(s); + if (*com_token == '=') //lol. "=" == "==" + return retfloat(!strcmp(s2, If_Token(func, end))); + else + return retfloat(!strcmp(s2, If_Token(s, end))); + } + if (!strncmp(com_token, "equal", 5)) + return retfloat(!strcmp(s2, If_Token(s, end))); + if (!strcmp(com_token, "!")) + { + func=COM_ParseToken(s); + if (*com_token == '=') + { + s = If_Token(func, end); + if (!is_numeric(s) || !is_numeric(s2)) + { + if (strcmp(s2, s)) + return "true"; + else + return ""; + } + return retfloat(atof(s2)!=atof(s)); + } + else if (!strcmp(com_token, "isin")) + return retfloat(NULL==strstr(If_Token(s, end), s2)); + } + if (!strcmp(com_token, ">")) + { + func=COM_ParseToken(s); + if (*com_token == '=') + return retfloat(atof(s2)>=atof(If_Token(func, end))); + else if (*com_token == '<')//vb? + { + s = If_Token(func, end); + if (is_numeric(s) && is_numeric(s2)) + { + if (strcmp(s2, s)) + return "true"; + else + return ""; + } + return retfloat(atof(s2)!=atof(s)); + } + else + return retfloat(atof(s2)>atof(If_Token(s, end))); + } + if (!strcmp(com_token, "<")) + { + func=COM_ParseToken(s); + if (*com_token == '=') + return retfloat(atof(s2)<=atof(If_Token(func, end))); + else if (*com_token == '>')//vb? + return retfloat(atof(s2)!=atof(If_Token(func, end))); + else + return retfloat(atof(s2)= line && *end <= ' '; end--) //skip trailing + *end = '\0'; + + if (!strcmp(line, "{")) //multiline block + { + int indent = 1; + + for(;;) + { + line = Cbuf_GetNext(level); + + while(*line <= ' ' && *line) //skip leading whitespace. + line++; + + for (end = line + strlen(line)-1; end >= line && *end <= ' '; end--) //skip trailing + *end = '\0'; + + if (!strcmp(line, "{")) + indent++; + else if (!strcmp(line, "}")) + { + indent--; + if (!indent) + break; + } + else if (!*line) + { + Con_Printf("Unterminated block\n"); + break; + } + + if (exectext) + { + char *newv; + newv = Z_Malloc(strlen(exectext) + strlen(line) + 2); + sprintf(newv, "%s;%s", exectext, line); + Z_Free(exectext); + exectext = newv; + } + else + exectext = CopyString(line); +// Con_Printf("Exec \"%s\"\n", line); + } + } + else + { + exectext = CopyString(line); +// Con_Printf("Exec \"%s\"\n", line); + } + remainingcbuf = Cbuf_StripText(level); //this craziness is to prevent an if } from breaking the entire con text + Cbuf_AddText(exectext, level); + Z_Free(exectext); + Cbuf_ExecuteLevel(level); + Cbuf_AddText(remainingcbuf, level); + Z_Free(remainingcbuf); +} + +void Cbuf_SkipBlock(int level) +{ + char *line, *end; + line = Cbuf_GetNext(level); + + while(*line <= ' ' && *line) //skip leading whitespace. + line++; + + for (end = line + strlen(line)-1; end >= line && *end <= ' '; end--) //skip trailing + *end = '\0'; + + if (!strcmp(line, "{")) //multiline block + { + int indent = 1; + + for(;;) + { + line = Cbuf_GetNext(level); + + while(*line <= ' ' && *line) //skip leading whitespace. + line++; + + for (end = line + strlen(line)-1; end >= line && *end <= ' '; end--) //skip trailing + *end = '\0'; + + if (!strcmp(line, "{")) + indent++; + else if (!strcmp(line, "}")) + { + indent--; + if (!indent) + break; + } + else if (!*line) + { + Con_Printf("Unterminated block\n"); + break; + } +// Con_Printf("Skip \"%s\"\n", line); + } + } +// else +// Con_Printf("Skip \"%s\"\n", line); +} + +void Cmd_if_f(void) +{ + char *text = Cmd_Args(); + char *ret, *ws; + char *end; + int level; + qboolean trueblock=false; + tempstack_t *ts; + + if (Cmd_Argc()==1) + { + Con_TPrintf(TL_IFSYNTAX); + return; + } + + ts = If_Token_GetMark(); + level = Cmd_ExecLevel; + +elseif: +// Con_Printf("if %s\n", text); + for(ret = If_Token(text, &end); *ret; ret++) {if (*ret != '0' && *ret != '.')break;} + if (!end) + { + Con_TPrintf(TL_IFSYNTAXERROR); + If_Token_Clear(ts); + return; + } + +skipws: + while(*end <= ' ' && *end) //skip leading whitespace. + end++; + + for (ws = end + strlen(end)-1; ws >= end && *ws <= ' '; ws--) //skip trailing + *ws = '\0'; + + if (!strcmp(end, "then")) //sigh... trying to make fuhquake's ifs work. + { + end+=4; + goto skipws; + } + + if (!*end) + { + if (ret && *ret) //equation was true. + { + trueblock = true; + Cbuf_ExecBlock(level); + } + else //equation was false. + { +skipblock: + Cbuf_SkipBlock(level); + } + end = Cbuf_GetNext(level); + while(*end <= ' ' && *end) + end++; + if (!strncmp(end, "else", 4)) + { + end+=4; + while(*end <= ' ' && *end) //skip leading whitespace. + end++; + + if (!strncmp(end, "if", 2)) + { + text = end + 2; + if (trueblock) + goto skipblock; //we've had our true, all others are assumed to be false. + else + goto elseif; //and have annother go. + } + else + { //we got an else. This is the last block. Don't go through the normal way, cos that would let us follow up with a second else. + if (trueblock) + Cbuf_SkipBlock(level); + else + Cbuf_ExecBlock(level); + + If_Token_Clear(ts); + return; + } + } + //whoops. Too far. + Cbuf_InsertText(end, level); + If_Token_Clear(ts); + return; + } + + text = strstr(end, "else"); + if (ret && *ret) + { + if (text) //don't bother execing the else bit... + *text = '\0'; + Cbuf_InsertText(end, level); + } + else + { + if (text) + Cbuf_InsertText(text+4, level); //ironically, this will do elseif... + } + + If_Token_Clear(ts); +} + +void Cmd_set_f(void) +{ + cvar_t *var; + char *end; + char *text = Cmd_Args(); + if (Cmd_Argc()<3) + { + Con_TPrintf(TL_SETSYNTAX); + return; + } + + while(*text <= ' ') //first whitespace + text++; + while(*text > ' ') //first var + text++; + while(*text <= ' ') //second whitespace + text++; + //second var + text = If_Token(text, &end); + + var = Cvar_FindVar (Cmd_Argv(1)); + if (var) + Cvar_Set(var, text); + else + var = Cvar_Get(Cmd_Argv(1), text, 0, "User variables"); + + if (!stricmp(Cmd_Argv(0), "seta")) + var->flags |= CVAR_ARCHIVE|CVAR_USERCREATED; +} + + +void Cvar_Inc_f (void) +{ + int c; + cvar_t *var; + float delta; + + c = Cmd_Argc(); + if (c != 2 && c != 3) + { + Con_Printf ("inc [value]\n"); + return; + } + + var = Cvar_FindVar (Cmd_Argv(1)); + if (!var) + { + Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1)); + return; + } + delta = (c == 3) ? atof (Cmd_Argv(2)) : 1; + + Cvar_SetValue (var, var->value + delta); +} + +////////////////////////// +typedef struct msg_trigger_s { + char *match; + char *command; + struct msg_trigger_s *next; +} msg_trigger_t; +msg_trigger_t *msg_trigger; +void Cmd_MessageTrigger (char *message, int type) +{ + msg_trigger_t *trigger; + for (trigger = msg_trigger; trigger; trigger = trigger->next) + if (strstr(message, trigger->match)) + { + Cbuf_AddText(trigger->command, RESTRICT_LOCAL); + Cbuf_AddText("\n", RESTRICT_LOCAL); + } +} + +void Cmd_Msg_Trigger_f (void) +{ + char *command; + char *match; + char *parm; + qboolean dup=false; + int i; + msg_trigger_t *trigger, *parent; + + if (Cmd_Argc() == 1) + { + if (!msg_trigger) + { + Con_Printf("No message triggers are set\n"); + return; + } + Con_Printf("Message triggers are:\n"); + for (trigger = msg_trigger; trigger; trigger = trigger->next) + { + Con_Printf("%s: %s\n", trigger->command, trigger->match); + } + return; + } + if (Cmd_Argc() < 3) + { + Con_Printf("Usage: %s execcommand match [-d]\n", Cmd_Argv(0)); + return; + } + + command = Cmd_Argv(1); + match = Cmd_Argv(2); + for (i = 3; i < Cmd_Argc(); i++) + { + parm = Cmd_Argv(i); + if (!strcmp(parm, "-d")) + dup = true; + //print unknown parms? + } + + if (dup) + trigger = NULL; + else + { + for (trigger = msg_trigger; trigger; trigger = trigger->next) + { //find previous (by command text) + if (!strcmp(trigger->command, command)) + break; + } + } + if (!*match) //remove. + { + if (!trigger) + { + Con_Printf("Trigger not found\n"); + return; + } + if (msg_trigger == trigger) + { + parent = NULL; + msg_trigger = trigger->next; + } + else + { + for (parent = msg_trigger; parent; parent = parent->next) + { + if (parent->next == trigger) + break; + } + if (!parent) + { + Con_Printf("Something strange happening in Cmd_Msg_Trigger_f\n"); + return; + } + parent->next = trigger->next; + } + Z_Free(trigger->match); + Z_Free(trigger); + return; + } + if (!trigger) + { + trigger = Z_Malloc(sizeof(msg_trigger_t) + strlen(command) + 1); + trigger->next = msg_trigger; + msg_trigger = trigger; + + trigger->command = (char *)(trigger+1); + strcpy(trigger->command, command); + } + if (trigger->match) + Z_Free(trigger->match); + trigger->match = CopyString(match); +} + +#define MAX_FILTER_LEN 4 +typedef struct msg_filter_s { + struct msg_filter_s *next; + char *name; +} msg_filter_t; +msg_filter_t *msg_filter; +void Cmd_Msg_Filter_Add (char *name) +{ + msg_filter_t *f; + if (strchr(name, ' ')) + { + Con_Printf("Filter's may not contain spaces."); + return; + } + if (strchr(name, '#')) + { + Con_Printf("Filter's may not contain hashes internally."); + return; + } + + for (f = msg_filter; f; f = f->next) //check if it already exists + { + if (!strcmp(f->name, name)) + return; + } + f = Z_Malloc(sizeof(msg_filter_t) + strlen(name)+1); + f->next = msg_filter; + msg_filter = f; + f->name = (char *)(f+1); + strcpy(f->name, name); +} + +void Cmd_Msg_Filter_Remove (char *name) +{ + msg_filter_t *f; + msg_filter_t *old = NULL; + for (f = msg_filter; f; f = f->next) + { + if (!strcmp(f->name, name)) + { + if (old) + old->next = f->next; + else + msg_filter = f->next; + Z_Free(f); + return; + } + old = f; + } + Con_Printf("Couldn't remove filter \'%s\'.\n", name); +} + +void Cmd_Msg_Filter_ClearAll (void) +{ + msg_filter_t *old; + while(msg_filter) + { + old = msg_filter; + msg_filter = msg_filter->next; + Z_Free(old); + } +} + +void Cmd_Msg_Filter_f (void) +{ + int first, i; + msg_filter_t *f; + char *name; + qboolean clearem = true; + if (Cmd_Argc()==1) + { //print em + if (!msg_filter) + Con_Printf("Filtering not enabled\n"); + Con_Printf("Current filters:"); + for (f = msg_filter; f; f = f->next) + Con_Printf(" %s", f->name); + Con_Printf("\n"); + return; + } + first = 1; + if (!strcmp(Cmd_Argv(1), "clear")) + { + Cmd_Msg_Filter_ClearAll(); + first++; + } + + for(i = first; i < Cmd_Argc(); i++) + { + name = Cmd_Argv(i); + if (*name != '#') //go for ZQuake stylie. + { + clearem = false; + } + } + + if (clearem) + Cmd_Msg_Filter_ClearAll(); + + for(i = first; i < Cmd_Argc(); i++) + { + name = Cmd_Argv(i); + if (strchr(name, ' ')) + { + Con_Printf("Filter's may not contain spaces."); + continue; + } + if (*name == '#' || *name == '+') + Cmd_Msg_Filter_Add(name+1); + else if (*name == '-') + Cmd_Msg_Filter_Remove(name+1); + else + Cmd_Msg_Filter_Add(name); + } +} + +qboolean Cmd_FilterMessage (char *message, qboolean sameteam) //returns true if the message was filtered. +{ + msg_filter_t *f; + char *filter; + char *earliest, *end; + int len, msglen; + char trimmedfilter[MAX_FILTER_LEN+1]; + if (!msg_filter) //filtering is off. + return false; + + msglen = strlen(message); + for(filter = message+strlen(message)-1; filter>=message; filter--) + { + if (!*filter) //that's not right is it? + break; + if (*filter <= ' ') //ignore whitespace + msglen--; + else + break; + } + + len = msglen - MAX_FILTER_LEN; + if (len < 0) + len = 0; + earliest = message+len-1; + for(filter = message+msglen-1; filter>=earliest; filter--) + { + if (*filter == '#') //start of filter. + { + break; + } + if (*filter <= ' ') //not a filter + break; + } + if (filter<=earliest || *filter != '#') //not a filterable message, so don't filter it. + return false; + + filter++; + + Q_strncpyz(trimmedfilter, filter, sizeof(trimmedfilter)); //might have whitespace. + + for (end = trimmedfilter + strlen(filter)-1; end >= trimmedfilter && *end <= ' '; end--) //skip trailing + *end = '\0'; + + for (f = msg_filter; f; f = f->next) + { + if (!strcmp(trimmedfilter, f->name)) + { + //hide the filter part of the message. + memmove(filter-1, message + msglen, strlen(message)-msglen+1); + return false; //allow it. + } + } + + return true; //not on our list of allowed. +} + +void Cmd_WriteConfig_f(void) +{ + FILE *f; + char *filename; + + filename = Cmd_Argv(1); + if (!*filename) + filename = "fte"; + + if (!strncmp(filename, "../", 3)) + { + filename+=3; + if (strstr(filename, "..")) + { + Con_Printf ("Couldn't write config %s\n",filename); + return; + } + filename = va("%s/%s.cfg",com_gamedir, filename); + } + else + { + if (strstr(filename, "..")) + { + Con_Printf ("Couldn't write config %s\n",filename); + return; + } + + filename = va("%s/configs/%s.cfg",com_gamedir, filename); + } + COM_DefaultExtension(filename, ".cfg"); + COM_CreatePath(filename); + f = fopen (filename, "wb"); + if (!f) + { + Con_Printf ("Couldn't write config %s\n",filename); + return; + } + fprintf(f, "// FTE config file\n\n"); +#ifndef SERVERONLY + Key_WriteBindings (f); +#else + fprintf(f, "// Dedicated Server config\n\n"); +#endif + Alias_WriteAliases (f); + Cvar_WriteVariables (f, true); + fclose(f); +} + +/* +============ +Cmd_Init +============ +*/ +void Cmd_Init (void) +{ +// +// register our commands +// + Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); + Cmd_AddCommand ("cfg_load",Cmd_Exec_f); + Cmd_AddCommand ("exec",Cmd_Exec_f); + Cmd_AddCommand ("echo",Cmd_Echo_f); + Cmd_AddCommand ("alias",Cmd_Alias_f); + Cmd_AddCommand ("wait", Cmd_Wait_f); +#ifndef SERVERONLY + Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f); +#else + Cmd_AddCommand ("restrict", Cmd_RestrictCommand_f); + Cmd_AddCommand ("aliaslevel", Cmd_AliasLevel_f); +#endif + + Cmd_AddCommand ("msg_trigger", Cmd_Msg_Trigger_f); + Cmd_AddCommand ("filter", Cmd_Msg_Filter_f); + + Cmd_AddCommand ("cfg_save",Cmd_WriteConfig_f); + + + Cmd_AddCommand ("set", Cmd_set_f); + Cmd_AddCommand ("seta", Cmd_set_f); + Cmd_AddCommand ("inc", Cvar_Inc_f); + //FIXME: Add seta some time. + Cmd_AddCommand ("if", Cmd_if_f); + + Cmd_AddCommand ("cmdlist", Cmd_List_f); + Cmd_AddCommand ("aliaslist", Cmd_AliasList_f); + Cmd_AddCommand ("cvarlist", Cvar_List_f); + + Cmd_AddCommand ("fs_flush", COM_RefreshFSCache_f); + Cvar_Register(&com_fs_cache, "Filesystem"); + +#ifndef SERVERONLY + rcon_level.value = atof(rcon_level.string); //client is restricted to not be allowed to change restrictions. +#else + Cvar_Register(&rcon_level, "Access controls"); //server gains versatility. +#endif + rcon_level.restriction = RESTRICT_MAX; //default. Don't let anyone change this too easily. + cmd_maxbuffersize.restriction = RESTRICT_MAX; //filling this causes a loop for quite some time. + + Cvar_Register(&cmd_onestuffwonder, "Console"); + Cvar_Register(&cl_aliasoverlap, "Console"); + + //FIXME: go through quake.rc and parameters looking for sets and setas and setting them now. +} + diff --git a/engine/common/cmd.h b/engine/common/cmd.h new file mode 100644 index 000000000..dbed22bf9 --- /dev/null +++ b/engine/common/cmd.h @@ -0,0 +1,139 @@ +/* +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 (const char *text, int level); +// as new commands are generated from the console or keybindings, +// the text is added to the end of the command buffer. + +void Cbuf_InsertText (const char *text, int level); +// when a command wants to issue other commands immediately, the text is +// inserted at the beginning of the buffer, before any remaining unexecuted +// commands. + +char *Cbuf_GetNext(int level); + +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. + +*/ + +typedef void (*xcommand_t) (void); + +int Cmd_Level(char *name); + +void Cmd_Init (void); + +void Cmd_RemoveCommand (char *cmd_name); +void Cmd_AddRemCommand (char *cmd_name, xcommand_t function); //removable command +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 +// if function is NULL, the command will be forwarded to the server +// as a clc_stringcmd instead of executed locally + +qboolean Cmd_Exists (char *cmd_name); +// used by the cvar code to check for cvar / command name overlap + +char *Cmd_CompleteCommand (char *partial, qboolean fullonly, int matchnum); +// 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); +extern int Cmd_ExecLevel; +// 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 + +char *Cmd_AliasExist(char *name, int restrictionlevel); +void Alias_WipeStuffedAliaes(void); + +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, int restrictionlevel); + +#define RESTRICT_MAX 64 //1-64 it's all about bit size. This is max settable. servers are +1 +#define RESTRICT_DEFAULT 50 //rcon get's 63, local always gets 64 +#define RESTRICT_MIN 1 //rcon get's 63, local always gets 64 + +#define RESTRICT_LOCAL RESTRICT_MAX +#define RESTRICT_SERVER RESTRICT_MAX+1 +#define RESTRICT_RCON rcon_level.value +#define RESTRICT_PROGS RESTRICT_MAX-2 + +#define Cmd_FromServer() (Cmd_ExecLevel>=RESTRICT_SERVER) + +// Parses a single line of text into arguments and tries to execute it +// as if it was typed at the console + +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. + +qboolean Cmd_FilterMessage (char *message, qboolean sameteam); +void Cmd_MessageTrigger (char *message, int type); + +void Cmd_StuffCmds_f (void); + +void Cmd_ShiftArgs (int ammount); + +char *Cmd_ExpandString (char *input, int maxaccesslevel); + +extern cvar_t rcon_level; + diff --git a/engine/common/common.c b/engine/common/common.c new file mode 100644 index 000000000..ce71d2783 --- /dev/null +++ b/engine/common/common.c @@ -0,0 +1,4688 @@ +/* +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 + +#ifdef SERVERONLY +#include "qwsvdef.h" +#else +#include "quakedef.h" +#endif + + + + + + +#define ONE_FS_HASH //cause all filesystems files to be hased in one group. + //this is to prevent us from going into each directory and fopening and crazy stuff like that. + + +//#define HASH_FILESYSTEM + +#if defined(HASH_FILESYSTEM) || defined(ONE_FS_HASH) +//#include "hash.h" + +#define Hash_BytesForBuckets(b) (sizeof(bucket_t)*b) + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc +typedef struct bucket_s { + void *data; + char *keystring; + struct bucket_s *next; +} bucket_t; +typedef struct hashtable_s { + int numbuckets; + bucket_t **bucket; +} hashtable_t; + +void Hash_InitTable(hashtable_t *table, int numbucks, void *mem); //mem must be 0 filled. (memset(mem, 0, size)) +int Hash_Key(char *name, int modulus); +void *Hash_Get(hashtable_t *table, char *name); +void *Hash_GetKey(hashtable_t *table, int key); +void *Hash_GetNext(hashtable_t *table, char *name, void *old); +void *Hash_Add(hashtable_t *table, char *name, void *data); +void *Hash_Add2(hashtable_t *table, char *name, void *data, bucket_t *buck); +void *Hash_AddKey(hashtable_t *table, int key, void *data); +void Hash_Remove(hashtable_t *table, char *name); +#endif + + + + + +#ifdef ONE_FS_HASH +hashtable_t filesystemhash; +qboolean com_fschanged = true; + +extern cvar_t com_fs_cache; + +int active_fs_cachetype; +#endif + + + + + +#undef malloc +#undef free + + +#define NUM_SAFE_ARGVS 6 + +usercmd_t nullcmd; // guarenteed to be zero + +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"}; + +cvar_t registered = {"registered","0"}; + +qboolean com_modified; // set true if using non-id files + +int static_registered = 1; // only for startup check, then set + +qboolean msg_suppress_1 = 0; + +void COM_InitFilesystem (void); +void COM_Path_f (void); + + +// if a packfile directory differs from this, it is assumed to be hacked +#define PAK0_COUNT 339 +#define PAK0_CRC 52883 + +qboolean standard_quake = true, rogue, hipnotic; + +char gamedirfile[MAX_OSPATH]; + +// 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. + +*/ + +//============================================================================ + + +// 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 + +============================================================================ +*/ + +#if 0 +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>=2; + for (i=0 ; i= '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); +} + +#endif + +char *Q_strlwr(char *s) +{ + char *ret=s; + while(*s) + { + if (*s >= 'A' && *s <= 'Z') + *s=*s-'A'+'a'; + s++; + } + + return ret; +} + + +int Q_strwildcmp(char *s1, char *s2) //2 is wild +{ + while (1) + { + if (!*s1 && !*s2) + return 0; //yay, they are equivalent + if (!*s1 || !*s2) + return -1; //one ended too soon. + + if (*s2 == '*') + { + s2++; + while (1) + { + if (!*s1 && *s2) + return -1; + if (*s1 == '.' || *s1 == '/' || !*s1) + break; + s1++; + } + } + else if (*s2 != *s1 && *s2 != '?') + return -1; + else + { + s1++; + s2++; + } + } +} + +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; +} + +/* +============================================================================ + + qbyte ORDER FUNCTIONS + +============================================================================ +*/ + +qboolean bigendien; + +short (*BigShort) (short l); +short (*LittleShort) (short l); +int (*BigLong) (int l); +int (*LittleLong) (int l); +float (*BigFloat) (float l); +float (*LittleFloat) (float l); + +short ShortSwap (short l) +{ + qbyte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short ShortNoSwap (short l) +{ + return l; +} + +int LongSwap (int l) +{ + qbyte 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; + qbyte 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 qbyte ordering and avoids alignment errors +============================================================================== +*/ + +// +// writing functions +// + +void MSG_WriteChar (sizebuf_t *sb, int c) +{ + qbyte *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) +{ + qbyte *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) +{ + qbyte *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) +{ + qbyte *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, Q_strlen(s)+1); +} + +void MSG_WriteCoord (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int)(f*8)); +} +void MSG_WriteBigCoord (sizebuf_t *sb, float f) +{ + int o = f*8; + MSG_WriteShort (sb, o&0xffff); + MSG_WriteByte (sb, (o&0xff0000)>>16); +} + +void MSG_WriteAngle (sizebuf_t *sb, float f) +{ + MSG_WriteByte (sb, (int)(f*256/360) & 255); +} + +void MSG_WriteAngle16 (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int)(f*65536/360) & 65535); +} + +void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) +{ + int bits; + +// +// send the movement message +// + bits = 0; +#ifdef Q2CLIENT + if (cls.q2server) + { + if (cmd->angles[0] != from->angles[0]) + bits |= Q2CM_ANGLE1; + if (cmd->angles[1] != from->angles[1]) + bits |= Q2CM_ANGLE2; + if (cmd->angles[2] != from->angles[2]) + bits |= Q2CM_ANGLE3; + if (cmd->forwardmove != from->forwardmove) + bits |= Q2CM_FORWARD; + if (cmd->sidemove != from->sidemove) + bits |= Q2CM_SIDE; + if (cmd->upmove != from->upmove) + bits |= Q2CM_UP; + if (cmd->buttons != from->buttons) + bits |= Q2CM_BUTTONS; + if (cmd->impulse != from->impulse) + bits |= Q2CM_IMPULSE; + + MSG_WriteByte (buf, bits); + + if (bits & Q2CM_ANGLE1) + MSG_WriteShort (buf, cmd->angles[0]); + if (bits & Q2CM_ANGLE2) + MSG_WriteShort (buf, cmd->angles[1]); + if (bits & Q2CM_ANGLE3) + MSG_WriteShort (buf, cmd->angles[2]); + + if (bits & Q2CM_FORWARD) + MSG_WriteShort (buf, cmd->forwardmove); + if (bits & Q2CM_SIDE) + MSG_WriteShort (buf, cmd->sidemove); + if (bits & Q2CM_UP) + MSG_WriteShort (buf, cmd->upmove); + + if (bits & Q2CM_BUTTONS) + MSG_WriteByte (buf, cmd->buttons); + if (bits & Q2CM_IMPULSE) + MSG_WriteByte (buf, cmd->impulse); + MSG_WriteByte (buf, cmd->msec); + + MSG_WriteByte (buf, cmd->lightlevel); + + } + else +#endif + { + if (cmd->angles[0] != from->angles[0]) + bits |= CM_ANGLE1; + if (cmd->angles[1] != from->angles[1]) + bits |= CM_ANGLE2; + if (cmd->angles[2] != from->angles[2]) + bits |= CM_ANGLE3; + if (cmd->forwardmove != from->forwardmove) + bits |= CM_FORWARD; + if (cmd->sidemove != from->sidemove) + bits |= CM_SIDE; + if (cmd->upmove != from->upmove) + bits |= CM_UP; + if (cmd->buttons != from->buttons) + bits |= CM_BUTTONS; + if (cmd->impulse != from->impulse) + bits |= CM_IMPULSE; + + MSG_WriteByte (buf, bits); + + if (bits & CM_ANGLE1) + MSG_WriteShort (buf, cmd->angles[0]); + if (bits & CM_ANGLE2) + MSG_WriteShort (buf, cmd->angles[1]); + if (bits & CM_ANGLE3) + MSG_WriteShort (buf, cmd->angles[2]); + + if (bits & CM_FORWARD) + MSG_WriteShort (buf, cmd->forwardmove); + if (bits & CM_SIDE) + MSG_WriteShort (buf, cmd->sidemove); + if (bits & CM_UP) + MSG_WriteShort (buf, cmd->upmove); + + if (bits & CM_BUTTONS) + MSG_WriteByte (buf, cmd->buttons); + if (bits & CM_IMPULSE) + MSG_WriteByte (buf, cmd->impulse); + MSG_WriteByte (buf, cmd->msec); + } +} + + +// +// reading functions +// +int msg_readcount; +qboolean msg_badread; + +void MSG_BeginReading (void) +{ + msg_readcount = 0; + msg_badread = false; +} + +int MSG_GetReadCount(void) +{ + return msg_readcount; +} + +// returns -1 and sets msg_badread if no more characters are available +int MSG_ReadChar (void) +{ + int c; + + 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; + + 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 + { + qbyte b[4]; + float f; + int l; + } dat; + + if (msg_readcount+4 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + 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; +} + +char *MSG_ReadStringLine (void) +{ + static char string[2048]; + int l,c; + + l = 0; + do + { + c = MSG_ReadChar (); + if (c == -1 || c == 0 || c == '\n') + break; + string[l] = c; + l++; + } while (l < sizeof(string)-1); + + string[l] = 0; + + return string; +} + +float MSG_ReadCoord (void) +{ + return MSG_ReadShort() * (1.0/8); +} + +void MSG_ReadPos (vec3_t pos) +{ + pos[0] = MSG_ReadShort() * (1.0/8); + pos[1] = MSG_ReadShort() * (1.0/8); + pos[2] = MSG_ReadShort() * (1.0/8); +} + +#define Q2NUMVERTEXNORMALS 162 +vec3_t bytedirs[Q2NUMVERTEXNORMALS] = +{ +#include "../client/q2anorms.h" +}; +#ifndef SERVERONLY +void MSG_ReadDir (vec3_t dir) +{ + int b; + + b = MSG_ReadByte (); + if (b >= Q2NUMVERTEXNORMALS) + Host_EndGame ("MSG_ReadDir: out of range"); + VectorCopy (bytedirs[b], dir); +} +#endif +void MSG_WriteDir (sizebuf_t *sb, vec3_t dir) +{ + int i, best; + float d, bestd; + + if (!dir) + { + MSG_WriteByte (sb, 0); + return; + } + + bestd = 0; + best = 0; + for (i=0 ; i bestd) + { + bestd = d; + best = i; + } + } + MSG_WriteByte (sb, best); +} +float MSG_ReadAngle (void) +{ + return MSG_ReadChar() * (360.0/256); +} + +float MSG_ReadAngle16 (void) +{ + return MSG_ReadShort() * (360.0/65536); +} + +void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) +{ + int bits; + + memcpy (move, from, sizeof(*move)); + + bits = MSG_ReadByte (); + +// read current angles + if (bits & CM_ANGLE1) + move->angles[0] = MSG_ReadShort (); + if (bits & CM_ANGLE2) + move->angles[1] = MSG_ReadShort (); + if (bits & CM_ANGLE3) + move->angles[2] = MSG_ReadShort (); + +// read movement + if (bits & CM_FORWARD) + move->forwardmove = MSG_ReadShort (); + if (bits & CM_SIDE) + move->sidemove = MSG_ReadShort (); + if (bits & CM_UP) + move->upmove = MSG_ReadShort (); + +// read buttons + if (bits & CM_BUTTONS) + move->buttons = MSG_ReadByte (); + + if (bits & CM_IMPULSE) + move->impulse = MSG_ReadByte (); + +// read time to run command + move->msec = MSG_ReadByte (); +} + +void MSGQ2_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) +{ + int bits; + + memcpy (move, from, sizeof(*move)); + + bits = MSG_ReadByte (); + +// read current angles + if (bits & Q2CM_ANGLE1) + move->angles[0] = MSG_ReadShort (); + if (bits & Q2CM_ANGLE2) + move->angles[1] = MSG_ReadShort (); + if (bits & Q2CM_ANGLE3) + move->angles[2] = MSG_ReadShort (); + +// read movement + if (bits & Q2CM_FORWARD) + move->forwardmove = MSG_ReadShort (); + if (bits & Q2CM_SIDE) + move->sidemove = MSG_ReadShort (); + if (bits & Q2CM_UP) + move->upmove = MSG_ReadShort (); + +// read buttons + if (bits & Q2CM_BUTTONS) + move->buttons = MSG_ReadByte (); + + if (bits & Q2CM_IMPULSE) + move->impulse = MSG_ReadByte (); + +// read time to run command + move->msec = MSG_ReadByte (); + + move->lightlevel = MSG_ReadByte (); +} + +void MSG_ReadData (void *data, int len) +{ + int i; + + for (i=0 ; icursize = 0; + buf->overflowed = false; +} + +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 (%d)", buf->maxsize); + + if (length > buf->maxsize) + Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); + + Sys_Printf ("SZ_GetSpace: overflow\n"); // because Con_Printf may be redirected + SZ_Clear (buf); + buf->overflowed = true; + } + + data = buf->data + buf->cursize; + buf->cursize += length; + + return data; +} + +void SZ_Write (sizebuf_t *buf, const void *data, int length) +{ + Q_memcpy (SZ_GetSpace(buf,length),data,length); +} + +void SZ_Print (sizebuf_t *buf, const char *data) +{ + int len; + + len = Q_strlen(data)+1; + + if (!buf->cursize || buf->data[buf->cursize-1]) + Q_memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 + else + { + qbyte *msg; + msg = SZ_GetSpace(buf, len-1); + if (msg == buf->data) //whoops. SZ_GetSpace can return buf->data if it overflowed. + msg++; + Q_memcpy (msg-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 (in > s2) + s2 = in; + + if (s-s2 < 2) + strcpy (out,"?model?"); + else + { + s--; + Q_strncpyS (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); +} + +//============================================================================ + +#define TOKENSIZE sizeof(com_token) +char com_token[TOKENSIZE]; +int com_argc; +char **com_argv; + +com_tokentype_t com_tokentype; + + +/* +============== +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=='/') + { + if (data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + if (len >= TOKENSIZE-1) + return data; + + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse a regular word + do + { + if (len >= TOKENSIZE-1) + return data; + + com_token[len] = c; + data++; + len++; + c = *data; + } while (c>32); + + com_token[len] = 0; + return data; +} + +char *COM_ParseOut (char *data, char *out, int outlen) +{ + int c; + int len; + + len = 0; + out[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=='/') + { + if (data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + if (len >= outlen-1) + return data; + + c = *data++; + if (c=='\"' || !c) + { + out[len] = 0; + return data; + } + out[len] = c; + len++; + } + } + +// parse a regular word + do + { + if (len >= outlen-1) + return data; + + out[len] = c; + data++; + len++; + c = *data; + } while (c>32); + + out[len] = 0; + return data; +} + +//same as COM_Parse, but parses two quotes next to each other as a single quote as part of the string +char *COM_StringParse (char *data) +{ + int c; + int len; + unsigned char *s; + + 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=='/') + { + if (data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + if (len >= TOKENSIZE-1) + return data; + + + c = *data++; + if (c=='\"') + { + c = *(data); + if (c!='\"') + { + com_token[len] = 0; + return data; + } + while (c=='\"') + { + com_token[len] = c; + len++; + data++; + c = *(data+1); + } + } + if (!c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse a regular word + do + { + if (len >= TOKENSIZE-1) + return data; + + com_token[len] = c; + data++; + len++; + c = *data; + } while (c>32); + + com_token[len] = 0; +#ifndef CLIENTONLY + { + extern redirect_t sv_redirected; + if (sv_redirected) //servers shouldn't give the values of cvars to all clients... Like password... + return data; + } +#endif + //now we check for macros. + for (s = com_token, c= 0; c < len; c++, s++) //this isn't a quoted token by the way. + { + if (*s == '$') + { + cvar_t *macro; + char name[64]; + int i; + + for (i = 1; i < sizeof(name); i++) + { + if (s[i] <= ' ' || s[i] == '$') + break; + } + + Q_strncpyz(name, s+1, i); + i-=1; + + macro = Cvar_FindVar(name); + if (macro) //got one... + { + memmove(s+strlen(macro->string), s+i+1, len-c-i); + memcpy(s, macro->string, strlen(macro->string)); + s+=strlen(macro->string); + len+=strlen(macro->string)-(i+1); + } + } + } + + return data; +} + +char *COM_ParseToken (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=='/') + { + if (data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + else if (data[1] == '*') + { + data+=2; + while (*data && (*data != '*' || data[1] != '/')) + data++; + data+=2; + goto skipwhite; + } + } + + +// handle quoted strings specially + if (c == '\"') + { + com_tokentype = TTP_STRING; + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + + com_tokentype = TTP_UNKNOWN; + +// parse single characters + if (c==',' || c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c==';' || c == '=' || 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=='\'' || c==':' || c==';' || c == '=' || c == '!' || c == '>' || c == '<' || c == '&' || c == '|' || c == '+') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + +char *COM_ParseCString (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=='/') + { + if (data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (!c) + { + com_token[len] = 0; + return data; + } + if (c == '\\') + { + c = *data++; + switch(c) + { + case 'n': + c = '\n'; + break; + case '\\': + c = '\\'; + break; + case '"': + c = '"'; + com_token[len] = c; + len++; + continue; + default: + com_token[len] = 0; + return data; + } + } + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + } 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_CheckNextParm (char *parm, int last) +{ + int i = last+1; + + for (i=1 ; i 126))) + buffer++; + + if (*buffer) + { + argv[argc] = buffer; + argc++; + + while (*buffer && ((*buffer > 32) && (*buffer <= 126))) + buffer++; + + if (*buffer) + { + *buffer = 0; + buffer++; + } + + } + } + + + fclose(f); + } + + safe = false; + + for (com_argc=0 ; (com_argc=0) + fclose(h); + + return len; +} + +/* +============ +COM_Path_f + +============ +*/ +void COM_Path_f (void) +{ + searchpath_t *s; + + Con_TPrintf (TL_CURRENTSEARCHPATH); + for (s=com_searchpaths ; s ; s=s->next) + { + if (s == com_base_searchpaths) + Con_Printf ("----------\n"); + switch (s->type) + { + case SPT_PACK: + Con_TPrintf (TL_SERACHPATHISPACK, s->u.pack->filename, s->u.pack->numfiles); + break; +#ifdef ZLIB + case SPT_ZIP: + Con_TPrintf (TL_SERACHPATHISZIP, s->u.zip->filename, s->u.zip->numfiles); + break; +#endif + default: + 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) +{ + FILE *f; + char name[MAX_OSPATH]; + + sprintf (name, "%s/%s", com_gamedir, filename); + + f = fopen (name, "wb"); + if (!f) + { + Sys_mkdir(com_gamedir); + f = fopen (name, "wb"); + if (!f) + { + Con_Printf("Error opening %s\n", filename); + return; + } + } + + Sys_Printf ("COM_WriteFile: %s\n", name); + fwrite (data, 1, len, f); + fclose (f); + + com_fschanged=true; +} + + +/* +============ +COM_CreatePath + +Only used for CopyFile and download +============ +*/ +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) +{ + FILE *in, *out; + int remaining, count; + char buf[4096]; + + remaining = COM_FileOpenRead (netpath, &in); + COM_CreatePath (cachepath); // create directories up to the cache file + out = fopen(cachepath, "wb"); + if (!out) + Sys_Error ("Error opening %s", cachepath); + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + fread (buf, 1, count, in); + fwrite (buf, 1, count, out); + remaining -= count; + } + + fclose (in); + fclose (out); +} + +int fs_hash_dups; +int fs_hash_files; +int FS_RebuildOSFSHash(char *filename, int filesize, void *data) +{ + if (filename[strlen(filename)-1] == '/') + { //this is actually a directory + + char childpath[256]; + sprintf(childpath, "%s*.*", filename); + Sys_EnumerateFiles(((searchpath_t *)data)->filename, childpath, FS_RebuildOSFSHash, data); + sprintf(childpath, "%s*", filename); + Sys_EnumerateFiles(((searchpath_t *)data)->filename, childpath, FS_RebuildOSFSHash, data); + return true; + } + if (!Hash_Get(&filesystemhash, filename)) + { + bucket_t *bucket = BZ_Malloc(sizeof(bucket_t) + strlen(filename)+1); + strcpy((char *)(bucket+1), filename); +#ifdef _WIN32 + Q_strlwr((char *)(bucket+1)); +#endif + Hash_Add2(&filesystemhash, (char *)(bucket+1), data, bucket); + + fs_hash_files++; + } + else + fs_hash_dups++; + return true; +} + +void FS_RebuildFSHash(void) +{ + int i; + searchpath_t *search; + if (!filesystemhash.numbuckets) + { + filesystemhash.numbuckets = 1024; + filesystemhash.bucket = BZ_Malloc(Hash_BytesForBuckets(filesystemhash.numbuckets)); + } + else + memset(filesystemhash.bucket, 0, Hash_BytesForBuckets(filesystemhash.numbuckets)); + Hash_InitTable(&filesystemhash, filesystemhash.numbuckets, filesystemhash.bucket); + + fs_hash_dups = 0; + fs_hash_files = 0; + + for (search = com_searchpaths ; search ; search = search->next) + { + switch (search->type) + { + case SPT_PACK: + for (i = 0; i < search->u.pack->numfiles; i++) + { + if (!Hash_Get(&filesystemhash, search->u.pack->files[i].name)) + { + fs_hash_files++; + Hash_Add2(&filesystemhash, search->u.pack->files[i].name, &search->u.pack->files[i], &search->u.pack->files[i].bucket); + } + else + fs_hash_dups++; + } + break; +#ifdef ZLIB + case SPT_ZIP: + for (i = 0; i < search->u.zip->numfiles; i++) + { + if (!Hash_Get(&filesystemhash, search->u.zip->files[i].name)) + { + fs_hash_files++; + Hash_Add2(&filesystemhash, search->u.zip->files[i].name, &search->u.zip->files[i], &search->u.zip->files[i].bucket); + } + else + fs_hash_dups++; + } + break; +#endif + case SPT_OS: + Sys_EnumerateFiles(search->filename, "*.*", FS_RebuildOSFSHash, search); + break; + default: + Sys_Error("FS_RebuildFSHash: Bad searchpath type\n"); + break; + } + } + + com_fschanged = false; + + Con_Printf("%i unique files, %i duplicates\n", fs_hash_files, fs_hash_dups); +} + +/* +=========== +COM_FindFile + +Finds the file in the search path. +Sets com_filesize and one of handle or file +=========== +*/ +int file_from_pak; // global indicating file came from pack file ZOID +#if 0 +/* +int COM_FOpenFile2 (char *sensativename, FILE **file, qboolean compressedokay) +{ + searchpath_t *search; + char netpath[MAX_OSPATH]; + int findtime; + char filename[MAX_QPATH]; + + Q_strncpyz(filename, sensativename, sizeof(filename)); + Q_strlwr(filename); + + if (filename[strlen(filename)-1] == '/') + { + *file = NULL; + com_filesize = -1; +#ifdef ZLIB + com_pathforfile=NULL; +#endif + return -1; + } + + if (com_fs_cache.value && !developer.value) + { + if (com_fschanged) + FS_RebuildFSHash(); + + if (filesystemhash.numbuckets) + { + void *value; + value = Hash_Get(&filesystemhash, filename); + + if (!value) //we don't know about a file by this name + { + Con_DPrintf ("FindFile: don't know %s\n", filename); + + *file = NULL; + com_filesize = -1; +#ifdef ZLIB + com_pathforfile=NULL; +#endif + return -1; + } + + for (search = com_searchpaths ; search ; search = search->next) + { + switch (search->type) + { + case SPT_PACK: + { + pack_t *pak; + packfile_t *pf = value; + pak = search->u.pack; + if (pf >= pak->files && pf < pak->files+pak->numfiles) //is the range right? + { + //is in this pack file + Con_DPrintf ("PackFile: %s : %s\n",pak->filename, filename); + + // open a new file on the pakfile + *file = fopen (pak->filename, "rb"); + if (!*file) + Sys_Error ("Couldn't reopen %s", pak->filename); + fseek (*file, pf->filepos, SEEK_SET); + com_filesize = pf->filelen; + + file_from_pak = 1; +#ifdef ZLIB + com_pathforfile=NULL; +#endif + return com_filesize; + } + } + break; +#ifdef ZLIB + case SPT_ZIP: + { + zipfile_t *zip; + packfile_t *pfile = value; + zip = search->u.zip; + if (pfile >= zip->files && pfile < zip->files+zip->numfiles) //is the pointer to within the list? + { + Con_DPrintf ("ZipFile: %s : %s\n",zip->filename, filename); + + file_from_pak = 2; + com_filenum = pfile - zip->files; + com_filesize = pfile->filelen ; + if (!compressedokay) + { + unzLocateFileMy (zip->handle, com_filenum, zip->files[com_filenum].filepos); + if ((*file = unzOpenCurrentFileFile(zip->handle, search->filename))) //phew! + { + com_pathforfile = NULL; + return pfile->filelen; + } + Con_TPrintf(TL_COMPRESSEDFILEOPENFAILED, filename); + break; + } + else + { + *file = NULL; + com_pathforfile = search; + return pfile->filelen; + } + } + } + break; +#endif + case SPT_OS: + if (value == search) //hash tables refer to the searchpath. + { + sprintf (netpath, "%s/%s",search->filename, filename); + + findtime = Sys_FileTime (netpath); + if (findtime == -1) + break; + + Con_DPrintf ("FindFile: %s\n",netpath); + + *file = fopen (netpath, "rb"); + file_from_pak = 0; +#ifdef ZLIB + com_pathforfile=NULL; +#endif + return COM_filelength (*file); + } + break; + } + } + + Con_Printf ("FindFile: can't find %s\n", filename); + + *file = NULL; + com_filesize = -1; + return -1; + } + } + +// +// search through the path, one element at a time +// + for (search = com_searchpaths ; search ; search = search->next) + { + // is the element a pak file? + switch (search->type) + { + case SPT_PACK: + { + int i; + pack_t *pak; + + // look through all the pak file elements + pak = search->u.pack; + for (i=0 ; inumfiles ; i++) + { + if (!strcmp (pak->files[i].name, filename)) + { // found it! + Con_DPrintf ("PackFile: %s : %s\n",pak->filename, filename); + + // open a new file on the pakfile + *file = fopen (pak->filename, "rb"); + if (!*file) + Sys_Error ("Couldn't reopen %s", pak->filename); + fseek (*file, pak->files[i].filepos, SEEK_SET); + com_filesize = pak->files[i].filelen; + file_from_pak = 1; + #ifdef ZLIB + com_pathforfile=NULL; + #endif + return com_filesize; + } + } + } + break; +#ifdef ZLIB + case SPT_ZIP: + { + packfile_t *pfile; + zipfile_t *zip = search->u.zip; + if ((pfile = Com_FileInZip(zip, filename))) + { + file_from_pak = 2; + com_filenum = pfile - zip->files; + com_filesize = pfile->filelen ; + if (!compressedokay) + { + unzLocateFileMy (zip->handle, com_filenum, zip->files[com_filenum].filepos); + if ((*file = unzOpenCurrentFileFile(zip->handle, search->filename))) //phew! + { + com_pathforfile = NULL; + return pfile->filelen; + } + + //this code copies it to a temp file for ultimate hackage. + { + char *buf; + FILE *f = tmpfile(); + buf = BZ_Malloc(pfile->filelen); + Com_ReadFileInZip(zip, buf); + fwrite(buf, 1, pfile->filelen, f); + fseek(f, 0, SEEK_SET); + + *file = f; + com_pathforfile = search; + return pfile->filelen; + } + Con_TPrintf(TL_COMPRESSEDFILEOPENFAILED, filename); + continue; + } + *file = NULL; + com_pathforfile = search; + return pfile->filelen; + } + } + break; +#endif + case SPT_OS: + // 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; + } + + _snprintf (netpath, sizeof(netpath)-1, "%s/%s",search->filename, filename); + + findtime = Sys_FileTime (netpath); + if (findtime == -1) + continue; + + Con_DPrintf ("FindFile: %s\n",netpath); + + *file = fopen (netpath, "rb"); + file_from_pak = 0; +#ifdef ZLIB + com_pathforfile=NULL; +#endif + return COM_filelength (*file); + default: + Sys_Error("COM_FOpenFile2: bad searchpath type\n"); + break; + } + + } + + //Con_DPrintf ("FindFile: can't find %s\n", filename); + + *file = NULL; + com_filesize = -1; + return -1; +} +*/ +#endif +//if loc is valid, loc->search is always filled in, the others are filled on success. +//returns -1 if couldn't find. +int FS_FLocateFile(char *filename, FSLF_ReturnType_e returntype, flocation_t *loc) +{ + char netpath[MAX_OSPATH]; + int depth=0, len; + searchpath_t *search; + + packfile_t *pf; +//Con_Printf("Finding %s: ", filename); + + if (com_fs_cache.value) + { + if (com_fschanged) + FS_RebuildFSHash(); + pf = Hash_Get(&filesystemhash, filename); + if (!pf) + goto fail; + } + else + pf = NULL; +// +// search through the path, one element at a time +// + for (search = com_searchpaths ; search ; search = search->next) + { + // is the element a pak file? + switch (search->type) + { + case SPT_PACK: + { + int i; + pack_t *pak; + + if (returntype == FSLFRT_DEPTH_ANYPATH) + depth++; + + // look through all the pak file elements + pak = search->u.pack; + + if (pf) + { //is this a pointer to a file in this pak? + if (pf < pak->files || pf > pak->files + pak->numfiles) + continue; + } + else + { + for (i=0 ; inumfiles ; i++) //look for the file + { + if (!strcmp (pak->files[i].name, filename)) + { + pf = &pak->files[i]; + break; + } + } + } + + if (pf) + { + len = pf->filelen; + if (loc) + { + loc->search = search; + loc->index = pf - pak->files; + strcpy(loc->rawname, pak->filename); + loc->offset = pf->filepos; + loc->len = pf->filelen; + } + goto out; + } + } + break; +#ifdef ZLIB + case SPT_ZIP: + { + zipfile_t *zip = search->u.zip; + + if (returntype == FSLFRT_DEPTH_ANYPATH) + depth++; + + if (pf) + { + if (pf < zip->files || pf > zip->files + zip->numfiles) + continue; + } + else + pf = Com_FileInZip(zip, filename); + if (pf) + { + len = pf->filelen; + if (loc) + { + loc->search = search; + loc->index = pf - zip->files; + strcpy(loc->rawname, ""); //no rawname, as it needs special processing. + loc->offset = pf->filepos; + loc->len = pf->filelen; + + unzLocateFileMy (zip->handle, com_filenum, zip->files[com_filenum].filepos); + loc->offset = unzGetCurrentFileUncompressedPos(zip->handle); + if (loc->offset<0) + { //file not found... + *loc->rawname = '\0'; + loc->offset=0; + } + } + goto out; + } + } + break; +#endif + case SPT_OS: + { + FILE *f; + + if (pf && (void *)pf != (void *)search) + continue; + +/* + if (!static_registered) + { // if not a registered version, don't ever go beyond base + if ( strchr (filename, '/') || strchr (filename,'\\')) + continue; + } +*/ + + depth++; + // check a file in the directory tree + _snprintf (netpath, sizeof(netpath)-1, "%s/%s",search->filename, filename); + + f = fopen(netpath, "rb"); + if (!f) + continue; + + fseek(f, 0, SEEK_END); + len = ftell(f); + fclose(f); + if (loc) + { + loc->len = len; + loc->offset = 0; + loc->search = search; + loc->index = 0; + Q_strncpyz(loc->rawname, netpath, sizeof(loc->rawname)); + } + + goto out; + } + break; + + default: + Sys_Error("COM_FLocateFile: Bad filesystem type\n"); + break; + } + } +fail: + if (loc) + loc->search = NULL; + depth = 0x7fffffff; + len = -1; +out: + +/* if (len>=0) + { + if (loc) + Con_Printf("Found %s:%i\n", loc->rawname, loc->len); + else + Con_Printf("Found %s\n", filename); + } + else + Con_Printf("Failed\n"); +*/ if (returntype == FSLFRT_LENGTH) + return len; + else + return depth; +} +int COM_FOpenLocationFILE(flocation_t *loc, FILE **file) +{ + if (!*loc->rawname) + { + if (!loc->len) + { + *file = NULL; + return -1; + } + +#ifdef ZLIB + if (loc->search->type == SPT_ZIP) + {//create a new, temp file, bung the contents of the compressed file into it, then continue. + char *buf; + FILE *f = tmpfile(); + buf = BZ_Malloc(loc->len); + com_filenum = loc->index; + Com_ReadFileInZip(loc->search->u.zip, buf); + fwrite(buf, 1, loc->len, f); + BZ_Free(buf); + fseek(f, 0, SEEK_SET); + + *file = f; + com_pathforfile = loc->search; + return loc->len; + } +#endif + } +// Con_Printf("Opening %s\n", loc->rawname); + *file = fopen(loc->rawname, "rb"); + if (!*file) + return -1; + fseek(*file, loc->offset, SEEK_SET); +#ifdef ZLIB + com_pathforfile = loc->search; +#endif + return loc->len; +} + +int COM_FOpenFile(char *filename, FILE **file) +{ + flocation_t loc; + FS_FLocateFile(filename, FSLFRT_LENGTH, &loc); + + com_filesize = -1; + if (loc.search) + { + file_from_pak = loc.search->type; + com_filesize = COM_FOpenLocationFILE(&loc, file); + } + else + *file = NULL; + return com_filesize; +} +//int COM_FOpenFile (char *filename, FILE **file) {file_from_pak=0;return COM_FOpenFile2 (filename, file, false);} //FIXME: TEMPORARY + + +cache_user_t *loadcache; +qbyte *loadbuf; +int loadsize; + +/* +============ +COM_LoadFile + +Filename are reletive to the quake directory. +Allways appends a 0 qbyte to the loaded data. +============ +*/ +qbyte *COM_LoadFile (char *path, int usehunk) +{ + char *buf; + FILE *f; + int len; + char base[32]; + flocation_t loc; + FS_FLocateFile(path, FSLFRT_LENGTH, &loc); + + if (!loc.search) + return NULL; //wasn't found + + if (*loc.rawname) + { +// Con_Printf("Opening %s\n", loc.rawname); + f = fopen(loc.rawname, "rb"); + if (!f) + return NULL; + fseek(f, loc.offset, SEEK_SET); + } + else + f = NULL; + + com_filesize = len = loc.len; + // extract the filename base name for hunk tag + COM_FileBase (path, base); + + if (usehunk == 0) + buf = Z_Malloc (len+1); + else if (usehunk == 1) + buf = Hunk_AllocName (len+1, base); + else if (usehunk == 2) + buf = Hunk_TempAlloc (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 if (usehunk == 5) + buf = BZ_Malloc(len+1); + else if (usehunk == 6) + buf = Hunk_TempAllocMore (len+1); + else + { + Sys_Error ("COM_LoadFile: bad usehunk"); + buf = NULL; + } + + if (!buf) + Sys_Error ("COM_LoadFile: not enough space for %s", path); + + ((qbyte *)buf)[len] = 0; +#ifndef SERVERONLY + if (qrenderer) + if (Draw_BeginDisc) + Draw_BeginDisc (); +#endif + + if (f) + { + fread (buf, 1, len, f); + fclose (f); + } + else + { + switch(loc.search->type) + { +#ifdef ZLIB + case SPT_ZIP: + com_filenum = loc.index; + Com_ReadFileInZip(loc.search->u.zip, buf); + break; +#endif + default: + Sys_Error("COM_LoadFile: Bad search path type"); + break; + } + } + +#ifndef SERVERONLY + if (qrenderer) + if (Draw_EndDisc) + Draw_EndDisc (); +#endif + + return buf; +} + +/* +============ +COM_LoadFile + +Filename are reletive to the quake directory. +Allways appends a 0 qbyte to the loaded data. +============ +*/ +/* +cache_user_t *loadcache; +qbyte *loadbuf; +int loadsize; +qbyte *COM_LoadFile (char *path, int usehunk) +{ + FILE *h; + qbyte *buf; + char base[32]; + int len; + + buf = NULL; // quiet compiler warning + +// look for it in the filesystem or pack files + len = com_filesize = COM_FOpenFile2 (path, &h, true); + if (len<0) + 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 if (usehunk == 5) + buf = BZ_Malloc(len+1); + else + Sys_Error ("COM_LoadFile: bad usehunk"); + + if (!buf) + Sys_Error ("COM_LoadFile: not enough space for %s", path); + + ((qbyte *)buf)[len] = 0; +#ifndef SERVERONLY + if (qrenderer) + if (Draw_BeginDisc) + Draw_BeginDisc (); +#endif +#ifdef ZLIB + if (com_pathforfile) + Com_ReadFileInZip(((searchpath_t *)com_pathforfile)->u.zip, buf); + else +#endif + { + fread (buf, 1, len, h); + fclose (h); + } +#ifndef SERVERONLY + if (qrenderer) + if (Draw_EndDisc) + Draw_EndDisc (); +#endif + + return buf; +} +*/ + +qbyte *COM_LoadMallocFile (char *path) //used for temp info along side temp hunk +{ + return COM_LoadFile (path, 5); +} + +qbyte *COM_LoadHunkFile (char *path) +{ + return COM_LoadFile (path, 1); +} + +qbyte *COM_LoadTempFile (char *path) +{ + return COM_LoadFile (path, 2); +} +qbyte *COM_LoadTempFile2 (char *path) +{ + return COM_LoadFile (path, 6); +} + +void COM_LoadCacheFile (char *path, struct cache_user_s *cu) +{ + loadcache = cu; + COM_LoadFile (path, 3); +} + +// uses temp hunk if larger than bufsize +qbyte *COM_LoadStackFile (char *path, void *buffer, int bufsize) +{ + qbyte *buf; + + loadbuf = (qbyte *)buffer; + loadsize = bufsize; + buf = COM_LoadFile (path, 4); + + return buf; +} + + +int Q_strwildcmp(char *s1, char *s2); +#ifdef ZLIB +int COM_EnumerateZipFiles (zipfile_t *zip, char *match, int (*func)(char *, int, void *), void *parm); +#endif +int COM_EnumeratePackFiles (pack_t *zip, char *match, int (*func)(char *, int, void *), void *parm) +{ + int num; + + for (num = 0; num<(int)zip->numfiles; num++) + { + if (!Q_strwildcmp(zip->files[num].name, match)) + { + if (!func(zip->files[num].name, zip->files[num].filelen, parm)) + return false; + } + } + + return true; +} + +void COM_EnumerateFiles (char *match, int (*func)(char *, int, void *), void *parm) +{ + searchpath_t *search; + for (search = com_searchpaths; search ; search = search->next) + { + // is the element a pak file? + switch(search->type) + { + case SPT_PACK: + COM_EnumeratePackFiles(search->u.pack, match, func, parm); + break; +#ifdef ZLIB + case SPT_ZIP: + COM_EnumerateZipFiles(search->u.zip, match, func, parm); + break; +#endif + case SPT_OS: + Sys_EnumerateFiles(search->filename, match, func, parm); + break; + } + } +} + + +/* +================= +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; +// int j; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + FILE *packhandle; + dpackfile_t info; +// unsigned short crc; + + if (COM_FileOpenRead (packfile, &packhandle) == -1) + return NULL; + + fread (&header, 1, sizeof(header), packhandle); + if (header.id[0] != 'P' || header.id[1] != 'A' + || header.id[2] != 'C' || header.id[3] != 'K') + { + return NULL; +// 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 = Z_Malloc (numpackfiles * sizeof(packfile_t)); + + fseek (packhandle, header.dirofs, SEEK_SET); +// fread (&info, 1, header.dirlen, packhandle); + +// crc the directory to check for modifications +// crc = CRC_Block((qbyte *)info, header.dirlen); + + +// CRC_Init (&crc); + + pack = Z_Malloc (sizeof (pack_t)); +#ifdef HASH_FILESYSTEM + Hash_InitTable(&pack->hash, numpackfiles+1, Z_Malloc(Hash_BytesForBuckets(numpackfiles+1))); +#endif +// parse the directory + for (i=0 ; ihash, newfiles[i].name, &newfiles[i], &newfiles[i].bucket); +#endif + } +/* + if (crc != PAK0_CRC) + com_modified = true; +*/ + strcpy (pack->filename, packfile); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + + Con_TPrintf (TL_ADDEDPACKFILE, packfile, numpackfiles); + return pack; +} + +void COM_FlushTempoaryPacks(void) +{ + searchpath_t *next; + while (*com_searchpaths->filename == '*') + { + switch (com_searchpaths->type) + { + case SPT_PACK: + fclose (com_searchpaths->u.pack->handle); + Z_Free (com_searchpaths->u.pack->files); +#ifdef HASH_FILESYSTEM + Z_Free (com_searchpaths->u.pack->hash.bucket); +#endif + Z_Free (com_searchpaths->u.pack); + break; +#ifdef ZLIB + case SPT_ZIP: + unzClose(com_searchpaths->u.zip->handle); + Z_Free (com_searchpaths->u.zip->files); +#ifdef HASH_FILESYSTEM + Z_Free (com_searchpaths->u.zip->hash.bucket); +#endif + Z_Free (com_searchpaths->u.zip); + break; +#endif + case SPT_OS: + break; + } + next = com_searchpaths->next; + Z_Free (com_searchpaths); + com_searchpaths = next; + + com_fschanged = true; + } +} + +qboolean COM_LoadMapPackFile (char *filename, int ofs) +{ + dpackheader_t header; + int i; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + FILE *packhandle; + dpackfile_t info; + int fstart; + char *ballsup; + + searchpath_t *search; + flocation_t loc; + + FS_FLocateFile(filename, FSLFRT_LENGTH, &loc); + + if (!loc.search) + { + Con_Printf("Couldn't refind file\n"); + return false; + } + + if (!*loc.rawname) + { + Con_Printf("File %s is compressed\n"); + return false; + } + packhandle = fopen(loc.rawname, "rb"); + if (!packhandle) + { + Con_Printf("Couldn't reopen file\n"); + return -1; + } + fseek(packhandle, loc.offset, SEEK_SET); + + fstart = loc.offset; + fseek(packhandle, ofs+fstart, SEEK_SET); + + fread (&header, 1, sizeof(header), packhandle); + if (header.id[0] != 'P' || header.id[1] != 'A' + || header.id[2] != 'C' || header.id[3] != 'K') + { + return false; + } + header.dirofs = LittleLong (header.dirofs); + header.dirlen = LittleLong (header.dirlen); + + numpackfiles = header.dirlen / sizeof(dpackfile_t); + + newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t)); + + fseek (packhandle, header.dirofs+fstart, SEEK_SET); + + pack = Z_Malloc (sizeof (pack_t)); +#ifdef HASH_FILESYSTEM + Hash_InitTable(&pack->hash, numpackfiles+1, Z_Malloc(Hash_BytesForBuckets(numpackfiles+1))); +#endif +// parse the directory + for (i=0 ; ihash, newfiles[i].name, &newfiles[i], &newfiles[i].bucket); +#endif + } + + strcpy (pack->filename, loc.rawname); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + + Con_TPrintf (TL_ADDEDPACKFILE, filename, numpackfiles); + + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_PACK; + *search->filename = '*'; + strcpy (search->filename+1, filename); + search->u.pack = pack; + search->next = com_searchpaths; + com_searchpaths = search; + + com_fschanged = true; + return true; +} + +#ifdef DOOMWADS +qboolean COM_LoadWadFile (char *wadname) +{ + dwadheader_t header; + int i; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + FILE *packhandle; + dwadfile_t info; + int fstart; + + int section=0; + char sectionname[MAX_QPATH]; + char filename[52]; + char neatwadname[52]; + + searchpath_t *search; + flocation_t loc; + + FS_FLocateFile(wadname, FSLFRT_LENGTH, &loc); + + if (!loc.search) + { + return false; + } + + if (!*loc.rawname) + { + Con_Printf("File %s is compressed\n"); + return false; + } + packhandle = fopen(loc.rawname, "rb"); + if (!packhandle) + { + Con_Printf("Couldn't open file\n"); + return false; + } + fseek(packhandle, loc.offset, SEEK_SET); + + fstart = loc.offset; + fseek(packhandle, fstart, SEEK_SET); + + fread (&header, 1, sizeof(header), packhandle); + if (header.id[1] != 'W' + || header.id[2] != 'A' || header.id[3] != 'D') + { + return false; + } + if (header.id[0] == 'I') + *neatwadname = '\0'; + else if (header.id[0] == 'P') + { + COM_StripExtension(wadname, neatwadname); + strcat(neatwadname, ":"); + } + else + return false; + header.dirofs = LittleLong (header.dirofs); + header.dirlen = LittleLong (header.dirlen); + + numpackfiles = header.dirlen; + + newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t)); + + fseek (packhandle, header.dirofs+fstart, SEEK_SET); + + pack = Z_Malloc (sizeof (pack_t)); +#ifdef HASH_FILESYSTEM + Hash_InitTable(&pack->hash, numpackfiles+1, Z_Malloc(Hash_BytesForBuckets(numpackfiles+1))); +#endif +// parse the directory + for (i=0 ; ihash, newfiles[i].name, &newfiles[i], &newfiles[i].bucket); +#endif + } + + strcpy (pack->filename, loc.rawname); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + + Con_Printf ("Added wad file %s\n", wadname, numpackfiles); + + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_PACK; + strcpy (search->filename, wadname); + search->u.pack = pack; + search->next = com_searchpaths; + com_searchpaths = search; + + com_fschanged = true; + + COM_StripExtension(wadname, sectionname); + strcat(sectionname, ".gwa"); + if (strcmp(sectionname, wadname)) + COM_LoadWadFile(sectionname); + return true; +} +#endif + +#ifdef ZLIB +/* +================= +COM_LoadZipFile + +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. +================= +*/ +zipfile_t *COM_LoadZipFile (char *packfile) +{ + int i; + + zipfile_t *zip; + packfile_t *newfiles; + + unz_global_info globalinf; + unz_file_info file_info; + + FILE *packhandle; + if (COM_FileOpenRead (packfile, &packhandle) == -1) + return NULL; + fclose(packhandle); + + zip = Z_Malloc(sizeof(zipfile_t)); + Q_strncpyz(zip->filename, packfile, sizeof(zip->filename)); + zip->handle = unzOpen (packfile); + if (!zip->handle) + { + Con_TPrintf (TL_COULDNTOPENZIP, packfile); + return NULL; + } + + unzGetGlobalInfo (zip->handle, &globalinf); + + zip->numfiles = globalinf.number_entry; + + zip->files = newfiles = Z_Malloc (zip->numfiles * sizeof(packfile_t)); +#ifdef HASH_FILESYSTEM + Hash_InitTable(&zip->hash, zip->numfiles+1, Z_Malloc(Hash_BytesForBuckets(zip->numfiles+1))); +#endif + for (i = 0; i < zip->numfiles; i++) + { + unzGetCurrentFileInfo (zip->handle, &file_info, newfiles[i].name, sizeof(newfiles[i].name), NULL, 0, NULL, 0); + Q_strlwr(newfiles[i].name); + newfiles[i].filelen = file_info.uncompressed_size; + newfiles[i].filepos = file_info.c_offset; +#ifdef HASH_FILESYSTEM + Hash_Add2(&zip->hash, newfiles[i].name, &newfiles[i], &newfiles[i].bucket); +#endif + unzGoToNextFile (zip->handle); + } + + Con_TPrintf (TL_ADDEDZIPFILE, packfile, zip->numfiles); + return zip; +} + +packfile_t *Com_FileInZip(zipfile_t *zip, char *filename) +{ +// int err; + +#ifdef HASH_FILESYSTEM + return Hash_Get(&zip->hash, filename); +#else + int num; + + for (num = 0; num<(int)zip->numfiles; num++) + { + if (!stricmp(filename, zip->files[num].name)) + return &zip->files[num]; + } +#endif + return NULL; +} +char *Com_ReadFileInZip(zipfile_t *zip, char *buffer) +{ + int err; + + unzLocateFileMy (zip->handle, com_filenum, zip->files[com_filenum].filepos); + + unzOpenCurrentFile (zip->handle); + err = unzReadCurrentFile (zip->handle, buffer, zip->files[com_filenum].filelen); + unzCloseCurrentFile (zip->handle); + + if (err!=zip->files[com_filenum].filelen) + { + printf ("Can't extract file \"%s:%s\"", zip->filename, zip->files[com_filenum].name); + return 0; + } + + return 0; +} + +int Q_strwildcmp(char *s1, char *s2); +int COM_EnumerateZipFiles (zipfile_t *zip, char *match, int (*func)(char *, int, void *), void *parm) +{ + int num; + + for (num = 0; num<(int)zip->numfiles; num++) + { + if (!Q_strwildcmp(zip->files[num].name, match)) + { + if (!func(zip->files[num].name, zip->files[num].filelen, parm)) + return false; + } + } + + return true; +} +#endif + +#ifdef DOOMWADS +int COM_AddWad (char *descriptor) +{ + searchpath_t *search; + char pakfile[MAX_OSPATH]; + + sprintf (pakfile, descriptor, com_gamedir); + + for (search = com_searchpaths; search; search = search->next) + { + if (!stricmp(search->filename, pakfile)) + return true; //already loaded (base paths?) + } + + return COM_LoadWadFile (descriptor); +} +#endif + +void COM_AddPacks (char *descriptor) +{ + searchpath_t *search; + int i; + pack_t *pak; + char pakfile[MAX_OSPATH]; + for (i=0 ; ; i++) + { + sprintf (pakfile, descriptor, com_gamedir, i); + pak = COM_LoadPackFile (pakfile); + if (!pak) + break; + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_PACK; + strcpy (search->filename, pakfile); + search->u.pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + + com_fschanged = true; + } +} + +int COM_AddPacksWild (char *descriptor, int size, void *param) +{ + searchpath_t *search; + pack_t *pak; + char pakfile[MAX_OSPATH]; + + sprintf (pakfile, "%s/%s", com_gamedir, descriptor); + + for (search = com_searchpaths; search; search = search->next) + { + if (!stricmp(search->filename, pakfile)) + return true; //already loaded (base paths?) + } + + pak = COM_LoadPackFile (pakfile); + if (!pak) + return true; + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_PACK; + strcpy (search->filename, pakfile); + search->u.pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + + com_fschanged = true; + + return true; +} + +#ifdef ZLIB +void COM_AddZips (char *descriptor) +{ + searchpath_t *search; + int i; + zipfile_t *zip; + char pakfile[MAX_OSPATH]; + for (i=0 ; ; i++) + { + sprintf (pakfile, descriptor, com_gamedir, i); + zip = COM_LoadZipFile (pakfile); + if (!zip) + break; + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_ZIP; + Q_strncpyz (search->filename, pakfile, sizeof(search->filename)); + search->u.zip = zip; + search->next = com_searchpaths; + com_searchpaths = search; + + com_fschanged = true; + } +} + +int COM_AddZipsWild (char *descriptor, int size, void *param) +{ + searchpath_t *search; + zipfile_t *zip; + char pakfile[MAX_OSPATH]; + + sprintf (pakfile, "%s/%s", com_gamedir, descriptor); + + for (search = com_searchpaths; search; search = search->next) + { + if (!stricmp(search->filename, pakfile)) + return true; //already loaded (base paths?) + } + + zip = COM_LoadZipFile (pakfile); + if (!zip) + return true; + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_ZIP; + strcpy (search->filename, pakfile); + search->u.zip = zip; + search->next = com_searchpaths; + com_searchpaths = search; + + com_fschanged = true; + + return true; +} + +#endif + + +void COM_RefreshFSCache_f(void) +{ + com_fschanged=true; +} + +void COM_FlushFSCache(void) +{ + if (com_fs_cache.value != 2) + com_fschanged=true; +} +/* +================ +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) +{ + searchpath_t *search; + + char *p; + + if ((p = strrchr(dir, '/')) != NULL) + strcpy(gamedirfile, ++p); + else + strcpy(gamedirfile, p); + strcpy (com_gamedir, dir); + + for (search = com_searchpaths; search; search = search->next) + { + if (!strcmp(search->filename, com_gamedir)) + return; //already loaded (base paths?) + } + +// +// add the directory to the search path +// + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_OS; + strcpy (search->filename, dir); + search->next = com_searchpaths; + com_searchpaths = search; + + COM_AddPacks("%s/pak%i.pak"); +#ifdef ZLIB + COM_AddZips("%s/pak%i.zip"); + COM_AddZips("%s/pak%i.pk3"); +#endif + +#ifdef DOOMWADS + COM_AddWad("doom.wad"); + COM_AddWad("doom2.wad"); + COM_AddWad("heretic.wad"); + COM_AddWad("dv.wad"); +#endif + + + Sys_EnumerateFiles(com_gamedir, "*.pak", COM_AddPacksWild, NULL); +#ifdef ZLIB + Sys_EnumerateFiles(com_gamedir, "*.pk3", COM_AddZipsWild, NULL); + //don't do zips. we could, but don't. it's not a great idea. +#endif +} + +char *COM_NextPath (char *prevpath) +{ + searchpath_t *s; + char *prev; + + if (!prevpath) + return com_gamedir; + + prev = com_gamedir; + for (s=com_searchpaths ; s ; s=s->next) + { + if (s->type != SPT_OS) + continue; + + if (prevpath == prev) + return s->filename; + prev = s->filename; + } + + return NULL; +} + +/* +================ +COM_Gamedir + +Sets the gamedir and path to a different directory. +================ +*/ +void COM_Gamedir (char *dir) +{ + searchpath_t *search, *next; + + if (strstr(dir, "..") || strstr(dir, "/") + || strstr(dir, "\\") || strstr(dir, ":") ) + { + Con_TPrintf (TL_GAMEDIRAINTPATH); + return; + } + + if (!strcmp(gamedirfile, dir)) + return; // still the same + +#ifndef SERVERONLY + Host_WriteConfiguration(); //before we change anything. +#endif + + strcpy (gamedirfile, dir); + +#ifndef CLIENTONLY + sv.gamedirchanged = true; +#endif +#ifndef SERVERONLY + cl.gamedirchanged = true; +#endif + + // + // free up any current game dir info + // + while (com_searchpaths != com_base_searchpaths) + { + switch(com_searchpaths->type) + { + case SPT_PACK: + fclose (com_searchpaths->u.pack->handle); + Z_Free (com_searchpaths->u.pack->files); +#ifdef HASH_FILESYSTEM + Z_Free (com_searchpaths->u.pack->hash.bucket); +#endif + Z_Free (com_searchpaths->u.pack); + break; +#ifdef ZLIB + case SPT_ZIP: + unzClose(com_searchpaths->u.zip->handle); + Z_Free (com_searchpaths->u.zip->files); +#ifdef HASH_FILESYSTEM + Z_Free (com_searchpaths->u.zip->hash.bucket); +#endif + Z_Free (com_searchpaths->u.zip); + break; +#endif + case SPT_OS: + break; + } + next = com_searchpaths->next; + Z_Free (com_searchpaths); + com_searchpaths = next; + } + + com_fschanged = true; + + // + // flush all data, so it will be forced to reload + // + Cache_Flush (); + + sprintf (com_gamedir, "%s/%s", com_basedir, dir); + + for (search = com_searchpaths; search; search = search->next) //see if it's already loaded (base paths) + { + if (!strcmp(search->filename, com_gamedir)) + break; + } + + if (!search) //was already part of the basic. + { + // + // add the directory to the search path + // + search = Z_Malloc (sizeof(searchpath_t)); + search->type = SPT_OS; + strcpy (search->filename, com_gamedir); + search->next = com_searchpaths; + com_searchpaths = search; + + COM_AddPacks("%s/pak%i.pak"); + +#ifdef ZLIB + COM_AddZips("%s/pak%i.zip"); + COM_AddZips("%s/pak%i.pk3"); +#endif + +#ifdef DOOMWADS + COM_AddWad("doom.wad"); + COM_AddWad("doom2.wad"); + COM_AddWad("dv.wad"); +#endif + + Sys_EnumerateFiles(com_gamedir, "*.pak", COM_AddPacksWild, NULL); +#ifdef ZLIB + Sys_EnumerateFiles(com_gamedir, "*.pk3", COM_AddZipsWild, NULL); + //don't do zips. we could, but don't. it's not a great idea. +#endif + + com_fschanged = true; + } + +#ifndef SERVERONLY + { + char fn[MAX_OSPATH]; + FILE *f; + +// if (qrenderer) //only do this if we have already started the renderer +// Cbuf_InsertText("vid_restart\n", RESTRICT_LOCAL); + + sprintf(fn, "%s/%s", com_gamedir, "config.cfg"); + if ((f = fopen(fn, "r")) != NULL) + { + fclose(f); + Cbuf_InsertText("cl_warncmd 1\n", RESTRICT_LOCAL); + Cbuf_InsertText("exec frontend.cfg\n", RESTRICT_LOCAL); + Cbuf_InsertText("exec config.cfg\n", RESTRICT_LOCAL); + Cbuf_InsertText("cl_warncmd 0\n", RESTRICT_LOCAL); + } + } + + Validation_FlushFileList(); //prevent previous hacks from making a difference. + + //FIXME: load new palette, if different cause a vid_restart. + +#endif +} + +/* +================ +COM_InitFilesystem +================ +*/ +void COM_InitFilesystem (void) +{ + FILE *f; + int i; + +// +// -basedir +// Overrides the system supplied base directory (under id1) +// + i = COM_CheckParm ("-basedir"); + if (i && i < com_argc-1) + strcpy (com_basedir, com_argv[i+1]); + else + strcpy (com_basedir, host_parms.basedir); + +// +// start up with id1 by default +// + i = COM_CheckParm ("-basegame"); + if (i && i < com_argc-1) + { + do //use multiple -basegames + { + COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]) ); + + i = COM_CheckNextParm ("-basegame", i); + } + while (i && i < com_argc-1); + } + else + { + //if there is no pak0.pak file in id1, and baseq2 has one, use that instead. + f = fopen(va("%s/id1/pak0.pak", com_basedir), "rb"); + if (f) + { + fclose(f); + COM_AddGameDirectory (va("%s/id1", com_basedir) ); + } + else + { + f = fopen(va("%s/baseq2/pak0.pak", com_basedir), "rb"); + if (f) + { + fclose(f); + COM_AddGameDirectory (va("%s/baseq2", com_basedir) ); + } + else + { //hexen2. + f = fopen(va("%s/data1/pak0.pak", com_basedir), "rb"); + if (f) + { + fclose(f); + COM_AddGameDirectory (va("%s/data1", com_basedir) ); + } + else + COM_AddGameDirectory (va("%s/id1", com_basedir) ); + } + } + } + COM_AddGameDirectory (va("%s/qw", com_basedir) ); + + // any set gamedirs will be freed up to here + com_base_searchpaths = com_searchpaths; + + i = COM_CheckParm ("-game"); //effectivly replace with +gamedir x (But overridable) + if (i && i < com_argc-1) + { + COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]) ); + +#ifndef CLIENTONLY + Info_SetValueForStarKey (svs.info, "*gamedir", com_argv[i+1], MAX_SERVERINFO_STRING); +#endif + } +} + + + +/* +===================================================================== + + INFO STRINGS + +===================================================================== +*/ + +/* +=============== +Info_ValueForKey + +Searches the string for the given +key and returns the associated value, or an empty string. +=============== +*/ +char *Info_ValueForKey (char *s, char *key) +{ + char pkey[1024]; + static char value[4][1024]; // use two buffers so compares + // work without stomping on each other + static int valueindex; + char *o; + + valueindex = (valueindex + 1) % 4; + if (*s == '\\') + s++; + while (1) + { + o = pkey; + while (*s != '\\') + { + if (!*s) + { + *value[valueindex]='\0'; + return value[valueindex]; + } + *o++ = *s++; + } + *o = 0; + s++; + + o = value[valueindex]; + + while (*s != '\\' && *s) + { + if (!*s) + { + *value[valueindex]='\0'; + return value[valueindex]; + } + *o++ = *s++; + } + *o = 0; + + if (!strcmp (key, pkey) ) + return value[valueindex]; + + if (!*s) + { + *value[valueindex]='\0'; + return value[valueindex]; + } + s++; + } +} + +void Info_RemoveKey (char *s, char *key) +{ + char *start; + char pkey[1024]; + char value[1024]; + char *o; + + if (strstr (key, "\\")) + { + Con_TPrintf (TL_KEYHASSLASH); + return; + } + + while (1) + { + start = s; + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (!strcmp (key, pkey) ) + { + strcpy (start, s); // remove this part + return; + } + + if (!*s) + return; + } + +} + +void Info_RemovePrefixedKeys (char *start, char prefix) +{ + char *s; + char pkey[1024]; + char value[1024]; + char *o; + + s = start; + + while (1) + { + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (pkey[0] == prefix) + { + Info_RemoveKey (start, pkey); + s = start; + } + + if (!*s) + return; + } + +} + +void Info_RemoveNonStarKeys (char *start) +{ + char *s; + char pkey[1024]; + char value[1024]; + char *o; + + s = start; + + while (1) + { + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (pkey[0] != '*') + { + Info_RemoveKey (start, pkey); + s = start; + } + + if (!*s) + return; + } + +} + +void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize) +{ + char new[1024], *v; + int c; +#ifdef SERVERONLY + extern cvar_t sv_highchars; +#endif + + if (strstr (key, "\\") || strstr (value, "\\") ) + { + Con_TPrintf (TL_KEYHASSLASH); + return; + } + + if (strstr (key, "\"") || strstr (value, "\"") ) + { + Con_TPrintf (TL_KEYHASQUOTE); + return; + } + + if (strlen(key) >= MAX_INFO_KEY || strlen(value) >= MAX_INFO_KEY) + { + Con_TPrintf (TL_KEYTOOLONG); + return; + } + + // this next line is kinda trippy + if (*(v = Info_ValueForKey(s, key))) + { + // key exists, make sure we have enough room for new value, if we don't, + // don't change it! + if (strlen(value) - strlen(v) + strlen(s) + 1 > maxsize) + { + if (*Info_ValueForKey(s, "*ver")) //quick hack to kill off unneeded info on overflow. We can't simply increase the quantity of this stuff. + { + Info_RemoveKey(s, "*ver"); + Info_SetValueForStarKey (s, key, value, maxsize); + return; + } + Con_TPrintf (TL_INFOSTRINGTOOLONG); + return; + } + } + Info_RemoveKey (s, key); + if (!value || !strlen(value)) + return; + + sprintf (new, "\\%s\\%s", key, value); + + if ((int)(strlen(new) + strlen(s) + 1) > maxsize) + { + Con_TPrintf (TL_INFOSTRINGTOOLONG); + return; + } + + // only copy ascii values + s += strlen(s); + v = new; + while (*v) + { + c = (unsigned char)*v++; +#ifndef SERVERONLY + // client only allows highbits on name + if (stricmp(key, "name") != 0) { + c &= 127; + if (c < 32 || c > 127) + continue; + // auto lowercase team + if (stricmp(key, "team") == 0) + c = tolower(c); + } +#else + if (!sv_highchars.value) { + c &= 127; + if (c < 32 || c > 127) + continue; + } +#endif +// c &= 127; // strip high bits + if (c > 13) // && c < 127) + *s++ = c; + } + *s = 0; +} + +void Info_SetValueForKey (char *s, char *key, char *value, int maxsize) +{ + if (key[0] == '*') + { + Con_TPrintf (TL_STARKEYPROTECTED); + return; + } + + Info_SetValueForStarKey (s, key, value, maxsize); +} + +void Info_Print (char *s) +{ + char key[1024]; + char value[1024]; + char *o; + int l; + + if (*s == '\\') + s++; + while (*s) + { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + + l = o - key; + if (l < 20) + { + memset (o, ' ', 20-l); + key[20] = 0; + } + else + *o = 0; + Con_Printf ("%s", key); + + if (!*s) + { + Con_TPrintf (TL_KEYHASNOVALUE); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + Con_Printf ("%s\n", value); + } +} + +static qbyte chktbl[1024 + 4] = { +0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01, +0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a, +0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58, +0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3, +0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b, +0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36, +0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8, +0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27, +0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd, +0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63, +0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2, +0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d, +0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65, +0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f, +0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f, +0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42, +0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59, +0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9, +0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69, +0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba, +0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85, +0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4, +0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8, +0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa, +0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05, +0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7, +0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a, +0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb, +0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b, +0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d, +0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce, +0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3, + +// map checksum goes here +0x00,0x00,0x00,0x00 +}; + +#if 0 + +static qbyte chkbuf[16 + 60 + 4]; + +static unsigned last_mapchecksum = 0; + + +/* +==================== +COM_BlockSequenceCheckByte + +For proxy protecting +==================== +*/ +qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum) +{ + int checksum; + qbyte *p; + + if (last_mapchecksum != mapchecksum) { + last_mapchecksum = mapchecksum; + chktbl[1024] = (mapchecksum & 0xff000000) >> 24; + chktbl[1025] = (mapchecksum & 0x00ff0000) >> 16; + chktbl[1026] = (mapchecksum & 0x0000ff00) >> 8; + chktbl[1027] = (mapchecksum & 0x000000ff); + + Com_BlockFullChecksum (chktbl, sizeof(chktbl), chkbuf); + } + + p = chktbl + (sequence % (sizeof(chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy (chkbuf + 16, base, length); + + length += 16; + + chkbuf[length] = (sequence & 0xff) ^ p[0]; + chkbuf[length+1] = p[1]; + chkbuf[length+2] = ((sequence>>8) & 0xff) ^ p[2]; + chkbuf[length+3] = p[3]; + + length += 4; + + checksum = LittleLong(Com_BlockChecksum (chkbuf, length)); + + checksum &= 0xff; + + return checksum; +} +#endif + +/* +==================== +COM_BlockSequenceCRCByte + +For proxy protecting +==================== +*/ +qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence) +{ + unsigned short crc; + qbyte *p; + qbyte chkb[60 + 4]; + + p = chktbl + (sequence % (sizeof(chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy (chkb, base, length); + + chkb[length] = (sequence & 0xff) ^ p[0]; + chkb[length+1] = p[1]; + chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2]; + chkb[length+3] = p[3]; + + length += 4; + + crc = CRC_Block(chkb, length); + + crc &= 0xff; + + return crc; +} + + +#if defined(Q2CLIENT) || defined(Q2SERVER) +static qbyte q2chktbl[1024] = { +0x84, 0x47, 0x51, 0xc1, 0x93, 0x22, 0x21, 0x24, 0x2f, 0x66, 0x60, 0x4d, 0xb0, 0x7c, 0xda, +0x88, 0x54, 0x15, 0x2b, 0xc6, 0x6c, 0x89, 0xc5, 0x9d, 0x48, 0xee, 0xe6, 0x8a, 0xb5, 0xf4, +0xcb, 0xfb, 0xf1, 0x0c, 0x2e, 0xa0, 0xd7, 0xc9, 0x1f, 0xd6, 0x06, 0x9a, 0x09, 0x41, 0x54, +0x67, 0x46, 0xc7, 0x74, 0xe3, 0xc8, 0xb6, 0x5d, 0xa6, 0x36, 0xc4, 0xab, 0x2c, 0x7e, 0x85, +0xa8, 0xa4, 0xa6, 0x4d, 0x96, 0x19, 0x19, 0x9a, 0xcc, 0xd8, 0xac, 0x39, 0x5e, 0x3c, 0xf2, +0xf5, 0x5a, 0x72, 0xe5, 0xa9, 0xd1, 0xb3, 0x23, 0x82, 0x6f, 0x29, 0xcb, 0xd1, 0xcc, 0x71, +0xfb, 0xea, 0x92, 0xeb, 0x1c, 0xca, 0x4c, 0x70, 0xfe, 0x4d, 0xc9, 0x67, 0x43, 0x47, 0x94, +0xb9, 0x47, 0xbc, 0x3f, 0x01, 0xab, 0x7b, 0xa6, 0xe2, 0x76, 0xef, 0x5a, 0x7a, 0x29, 0x0b, +0x51, 0x54, 0x67, 0xd8, 0x1c, 0x14, 0x3e, 0x29, 0xec, 0xe9, 0x2d, 0x48, 0x67, 0xff, 0xed, +0x54, 0x4f, 0x48, 0xc0, 0xaa, 0x61, 0xf7, 0x78, 0x12, 0x03, 0x7a, 0x9e, 0x8b, 0xcf, 0x83, +0x7b, 0xae, 0xca, 0x7b, 0xd9, 0xe9, 0x53, 0x2a, 0xeb, 0xd2, 0xd8, 0xcd, 0xa3, 0x10, 0x25, +0x78, 0x5a, 0xb5, 0x23, 0x06, 0x93, 0xb7, 0x84, 0xd2, 0xbd, 0x96, 0x75, 0xa5, 0x5e, 0xcf, +0x4e, 0xe9, 0x50, 0xa1, 0xe6, 0x9d, 0xb1, 0xe3, 0x85, 0x66, 0x28, 0x4e, 0x43, 0xdc, 0x6e, +0xbb, 0x33, 0x9e, 0xf3, 0x0d, 0x00, 0xc1, 0xcf, 0x67, 0x34, 0x06, 0x7c, 0x71, 0xe3, 0x63, +0xb7, 0xb7, 0xdf, 0x92, 0xc4, 0xc2, 0x25, 0x5c, 0xff, 0xc3, 0x6e, 0xfc, 0xaa, 0x1e, 0x2a, +0x48, 0x11, 0x1c, 0x36, 0x68, 0x78, 0x86, 0x79, 0x30, 0xc3, 0xd6, 0xde, 0xbc, 0x3a, 0x2a, +0x6d, 0x1e, 0x46, 0xdd, 0xe0, 0x80, 0x1e, 0x44, 0x3b, 0x6f, 0xaf, 0x31, 0xda, 0xa2, 0xbd, +0x77, 0x06, 0x56, 0xc0, 0xb7, 0x92, 0x4b, 0x37, 0xc0, 0xfc, 0xc2, 0xd5, 0xfb, 0xa8, 0xda, +0xf5, 0x57, 0xa8, 0x18, 0xc0, 0xdf, 0xe7, 0xaa, 0x2a, 0xe0, 0x7c, 0x6f, 0x77, 0xb1, 0x26, +0xba, 0xf9, 0x2e, 0x1d, 0x16, 0xcb, 0xb8, 0xa2, 0x44, 0xd5, 0x2f, 0x1a, 0x79, 0x74, 0x87, +0x4b, 0x00, 0xc9, 0x4a, 0x3a, 0x65, 0x8f, 0xe6, 0x5d, 0xe5, 0x0a, 0x77, 0xd8, 0x1a, 0x14, +0x41, 0x75, 0xb1, 0xe2, 0x50, 0x2c, 0x93, 0x38, 0x2b, 0x6d, 0xf3, 0xf6, 0xdb, 0x1f, 0xcd, +0xff, 0x14, 0x70, 0xe7, 0x16, 0xe8, 0x3d, 0xf0, 0xe3, 0xbc, 0x5e, 0xb6, 0x3f, 0xcc, 0x81, +0x24, 0x67, 0xf3, 0x97, 0x3b, 0xfe, 0x3a, 0x96, 0x85, 0xdf, 0xe4, 0x6e, 0x3c, 0x85, 0x05, +0x0e, 0xa3, 0x2b, 0x07, 0xc8, 0xbf, 0xe5, 0x13, 0x82, 0x62, 0x08, 0x61, 0x69, 0x4b, 0x47, +0x62, 0x73, 0x44, 0x64, 0x8e, 0xe2, 0x91, 0xa6, 0x9a, 0xb7, 0xe9, 0x04, 0xb6, 0x54, 0x0c, +0xc5, 0xa9, 0x47, 0xa6, 0xc9, 0x08, 0xfe, 0x4e, 0xa6, 0xcc, 0x8a, 0x5b, 0x90, 0x6f, 0x2b, +0x3f, 0xb6, 0x0a, 0x96, 0xc0, 0x78, 0x58, 0x3c, 0x76, 0x6d, 0x94, 0x1a, 0xe4, 0x4e, 0xb8, +0x38, 0xbb, 0xf5, 0xeb, 0x29, 0xd8, 0xb0, 0xf3, 0x15, 0x1e, 0x99, 0x96, 0x3c, 0x5d, 0x63, +0xd5, 0xb1, 0xad, 0x52, 0xb8, 0x55, 0x70, 0x75, 0x3e, 0x1a, 0xd5, 0xda, 0xf6, 0x7a, 0x48, +0x7d, 0x44, 0x41, 0xf9, 0x11, 0xce, 0xd7, 0xca, 0xa5, 0x3d, 0x7a, 0x79, 0x7e, 0x7d, 0x25, +0x1b, 0x77, 0xbc, 0xf7, 0xc7, 0x0f, 0x84, 0x95, 0x10, 0x92, 0x67, 0x15, 0x11, 0x5a, 0x5e, +0x41, 0x66, 0x0f, 0x38, 0x03, 0xb2, 0xf1, 0x5d, 0xf8, 0xab, 0xc0, 0x02, 0x76, 0x84, 0x28, +0xf4, 0x9d, 0x56, 0x46, 0x60, 0x20, 0xdb, 0x68, 0xa7, 0xbb, 0xee, 0xac, 0x15, 0x01, 0x2f, +0x20, 0x09, 0xdb, 0xc0, 0x16, 0xa1, 0x89, 0xf9, 0x94, 0x59, 0x00, 0xc1, 0x76, 0xbf, 0xc1, +0x4d, 0x5d, 0x2d, 0xa9, 0x85, 0x2c, 0xd6, 0xd3, 0x14, 0xcc, 0x02, 0xc3, 0xc2, 0xfa, 0x6b, +0xb7, 0xa6, 0xef, 0xdd, 0x12, 0x26, 0xa4, 0x63, 0xe3, 0x62, 0xbd, 0x56, 0x8a, 0x52, 0x2b, +0xb9, 0xdf, 0x09, 0xbc, 0x0e, 0x97, 0xa9, 0xb0, 0x82, 0x46, 0x08, 0xd5, 0x1a, 0x8e, 0x1b, +0xa7, 0x90, 0x98, 0xb9, 0xbb, 0x3c, 0x17, 0x9a, 0xf2, 0x82, 0xba, 0x64, 0x0a, 0x7f, 0xca, +0x5a, 0x8c, 0x7c, 0xd3, 0x79, 0x09, 0x5b, 0x26, 0xbb, 0xbd, 0x25, 0xdf, 0x3d, 0x6f, 0x9a, +0x8f, 0xee, 0x21, 0x66, 0xb0, 0x8d, 0x84, 0x4c, 0x91, 0x45, 0xd4, 0x77, 0x4f, 0xb3, 0x8c, +0xbc, 0xa8, 0x99, 0xaa, 0x19, 0x53, 0x7c, 0x02, 0x87, 0xbb, 0x0b, 0x7c, 0x1a, 0x2d, 0xdf, +0x48, 0x44, 0x06, 0xd6, 0x7d, 0x0c, 0x2d, 0x35, 0x76, 0xae, 0xc4, 0x5f, 0x71, 0x85, 0x97, +0xc4, 0x3d, 0xef, 0x52, 0xbe, 0x00, 0xe4, 0xcd, 0x49, 0xd1, 0xd1, 0x1c, 0x3c, 0xd0, 0x1c, +0x42, 0xaf, 0xd4, 0xbd, 0x58, 0x34, 0x07, 0x32, 0xee, 0xb9, 0xb5, 0xea, 0xff, 0xd7, 0x8c, +0x0d, 0x2e, 0x2f, 0xaf, 0x87, 0xbb, 0xe6, 0x52, 0x71, 0x22, 0xf5, 0x25, 0x17, 0xa1, 0x82, +0x04, 0xc2, 0x4a, 0xbd, 0x57, 0xc6, 0xab, 0xc8, 0x35, 0x0c, 0x3c, 0xd9, 0xc2, 0x43, 0xdb, +0x27, 0x92, 0xcf, 0xb8, 0x25, 0x60, 0xfa, 0x21, 0x3b, 0x04, 0x52, 0xc8, 0x96, 0xba, 0x74, +0xe3, 0x67, 0x3e, 0x8e, 0x8d, 0x61, 0x90, 0x92, 0x59, 0xb6, 0x1a, 0x1c, 0x5e, 0x21, 0xc1, +0x65, 0xe5, 0xa6, 0x34, 0x05, 0x6f, 0xc5, 0x60, 0xb1, 0x83, 0xc1, 0xd5, 0xd5, 0xed, 0xd9, +0xc7, 0x11, 0x7b, 0x49, 0x7a, 0xf9, 0xf9, 0x84, 0x47, 0x9b, 0xe2, 0xa5, 0x82, 0xe0, 0xc2, +0x88, 0xd0, 0xb2, 0x58, 0x88, 0x7f, 0x45, 0x09, 0x67, 0x74, 0x61, 0xbf, 0xe6, 0x40, 0xe2, +0x9d, 0xc2, 0x47, 0x05, 0x89, 0xed, 0xcb, 0xbb, 0xb7, 0x27, 0xe7, 0xdc, 0x7a, 0xfd, 0xbf, +0xa8, 0xd0, 0xaa, 0x10, 0x39, 0x3c, 0x20, 0xf0, 0xd3, 0x6e, 0xb1, 0x72, 0xf8, 0xe6, 0x0f, +0xef, 0x37, 0xe5, 0x09, 0x33, 0x5a, 0x83, 0x43, 0x80, 0x4f, 0x65, 0x2f, 0x7c, 0x8c, 0x6a, +0xa0, 0x82, 0x0c, 0xd4, 0xd4, 0xfa, 0x81, 0x60, 0x3d, 0xdf, 0x06, 0xf1, 0x5f, 0x08, 0x0d, +0x6d, 0x43, 0xf2, 0xe3, 0x11, 0x7d, 0x80, 0x32, 0xc5, 0xfb, 0xc5, 0xd9, 0x27, 0xec, 0xc6, +0x4e, 0x65, 0x27, 0x76, 0x87, 0xa6, 0xee, 0xee, 0xd7, 0x8b, 0xd1, 0xa0, 0x5c, 0xb0, 0x42, +0x13, 0x0e, 0x95, 0x4a, 0xf2, 0x06, 0xc6, 0x43, 0x33, 0xf4, 0xc7, 0xf8, 0xe7, 0x1f, 0xdd, +0xe4, 0x46, 0x4a, 0x70, 0x39, 0x6c, 0xd0, 0xed, 0xca, 0xbe, 0x60, 0x3b, 0xd1, 0x7b, 0x57, +0x48, 0xe5, 0x3a, 0x79, 0xc1, 0x69, 0x33, 0x53, 0x1b, 0x80, 0xb8, 0x91, 0x7d, 0xb4, 0xf6, +0x17, 0x1a, 0x1d, 0x5a, 0x32, 0xd6, 0xcc, 0x71, 0x29, 0x3f, 0x28, 0xbb, 0xf3, 0x5e, 0x71, +0xb8, 0x43, 0xaf, 0xf8, 0xb9, 0x64, 0xef, 0xc4, 0xa5, 0x6c, 0x08, 0x53, 0xc7, 0x00, 0x10, +0x39, 0x4f, 0xdd, 0xe4, 0xb6, 0x19, 0x27, 0xfb, 0xb8, 0xf5, 0x32, 0x73, 0xe5, 0xcb, 0x32 +}; + +/* +==================== +COM_BlockSequenceCRCByte + +For proxy protecting +==================== +*/ +qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence) +{ + int n; + qbyte *p; + int x; + qbyte chkb[60 + 4]; + unsigned short crc; + + + if (sequence < 0) + Sys_Error("sequence < 0, this shouldn't happen\n"); + + p = q2chktbl + (sequence % (sizeof(q2chktbl) - 4)); + + if (length > 60) + length = 60; + memcpy (chkb, base, length); + + chkb[length] = p[0]; + chkb[length+1] = p[1]; + chkb[length+2] = p[2]; + chkb[length+3] = p[3]; + + length += 4; + + crc = CRC_Block(chkb, length); + + for (x=0, n=0; n 1) + { + b += 1; + } + + b -= 35778; // Dec 16 1998 + + return b; +} + diff --git a/engine/common/common.h b/engine/common/common.h new file mode 100644 index 000000000..ef6735676 --- /dev/null +++ b/engine/common/common.h @@ -0,0 +1,290 @@ +/* +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 + +typedef unsigned char qbyte; +#define _DEF_BYTE_ + +// KJB Undefined true and false defined in SciTech's DEBUG.H header +#undef true +#undef false + +#ifdef __cplusplus +typedef enum qboolean;//false and true are forcivly defined. +#else +typedef enum {false, true} qboolean; +#endif + +#define MAX_INFO_STRING 196 +#define MAX_SERVERINFO_STRING 512 +#define MAX_LOCALINFO_STRING 32768 + +//============================================================================ + +typedef struct sizebuf_s +{ + qboolean allowoverflow; // if false, do a Sys_Error + qboolean overflowed; // set to true if the buffer size failed + qbyte *data; + int maxsize; + int cursize; +} sizebuf_t; + +void SZ_Clear (sizebuf_t *buf); +void *SZ_GetSpace (sizebuf_t *buf, int length); +void SZ_Write (sizebuf_t *buf, const void *data, int length); +void SZ_Print (sizebuf_t *buf, const 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 *)((qbyte *)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) + +//============================================================================ + +extern qboolean bigendien; + +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); + +//============================================================================ + +struct usercmd_s; + +extern struct usercmd_s nullcmd; + +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_WriteBigCoord (sizebuf_t *sb, float f); +void MSG_WriteAngle (sizebuf_t *sb, float f); +void MSG_WriteAngle16 (sizebuf_t *sb, float f); +void MSG_WriteDeltaUsercmd (sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); +void MSG_WriteDir (sizebuf_t *sb, float *dir); + +extern int msg_readcount; +extern qboolean msg_badread; // set if a read goes beyond end of message + +void MSG_BeginReading (void); +int MSG_GetReadCount(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); +char *MSG_ReadStringLine (void); + +float MSG_ReadCoord (void); +void MSG_ReadPos (float *pos); +float MSG_ReadAngle (void); +float MSG_ReadAngle16 (void); +void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd); +void MSGQ2_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *move); +void MSG_ReadData (void *data, int len); + +//============================================================================ + +char *Q_strcpyline(char *out, char *in, int maxlen); //stops at '\n' (and '\r') + +#define Q_memset(d, f, c) memset((d), (f), (c)) +#define Q_memcpy(d, s, c) memcpy((d), (s), (c)) +#define Q_memcmp(m1, m2, c) memcmp((m1), (m2), (c)) +#define Q_strcpy(d, s) strcpy((d), (s)) +#define Q_strncpy(d, s, n) strncpy((d), (s), (n)) +#define Q_strlen(s) ((int)strlen(s)) +#define Q_strrchr(s, c) strrchr((s), (c)) +#define Q_strcat(d, s) strcat((d), (s)) +#define Q_strcmp(s1, s2) strcmp((s1), (s2)) +#define Q_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) + +#define Q_strncpyS(d, s, n) do{const char *____in=(s);char *____out=(d);int ____i; for (____i=0;*(____in); ____i++){if (____i == (n))break;*____out++ = *____in++;}if (____i < (n))*____out='\0';}while(0) //only use this when it should be used. If undiciided, use N +#define Q_strncpyN(d, s, n) do{if (n < 0)Sys_Error("Bad length in strncpyz");Q_strncpyS((d), (s), (n));((char *)(d))[n] = '\0';}while(0) //this'll stop me doing buffer overflows. (guarenteed to overflow if you tried the wrong size.) +#define Q_strncpyNCHECKSIZE(d, s, n) do{if (n < 1)Sys_Error("Bad length in strncpyz");Q_strncpyS((d), (s), (n));((char *)(d))[n-1] = '\0';((char *)(d))[n] = '255';}while(0) //This forces nothing else to be within the buffer. Should be used for testing and nothing else. +#define Q_strncpyz(d, s, n) Q_strncpyN(d, s, (n)-1) +//#define Q_strncpy Please remove all strncpys +/*#ifndef strncpy +#define strncpy Q_strncpy +#endif*/ + +#ifdef _WIN32 + +#define Q_strcasecmp(s1, s2) _stricmp((s1), (s2)) +#define Q_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) + +#else + +#define Q_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#define Q_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) + +#endif + +int Q_atoi (char *str); +float Q_atof (char *str); + + + +//============================================================================ + +extern char com_token[1024]; + +typedef enum {TTP_UNKNOWN, TTP_STRING} com_tokentype_t; +extern com_tokentype_t com_tokentype; + +extern qboolean com_eof; + +char *COM_Parse (char *data); +char *COM_ParseCString (char *data); +char *COM_StringParse (char *data); +char *COM_ParseToken (char *data); +char *COM_TrimString(char *str); + + +extern int com_argc; +extern char **com_argv; + +int COM_CheckParm (char *parm); +void COM_AddParm (char *parm); + +void COM_Init (void); +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 *COM_FileExtension (char *in); + +char *VARGS 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); + +typedef struct { + struct searchpath_s *search; + int index; + char rawname[MAX_OSPATH]; + int offset; + int len; +} flocation_t; + +typedef enum {FSLFRT_LENGTH, FSLFRT_DEPTH_OSONLY, FSLFRT_DEPTH_ANYPATH} FSLF_ReturnType_e; +//if loc is valid, loc->search is always filled in, the others are filled on success. +//returns -1 if couldn't find. +int FS_FLocateFile(char *filename, FSLF_ReturnType_e returntype, flocation_t *loc); + +int COM_FOpenFile (char *filename, FILE **file); + +void COM_CloseFile (FILE *h); + +#define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,ignorepacks?FSLFRT_DEPTH_OSONLY:FSLFRT_DEPTH_ANYPATH, NULL) +#define COM_FCheckExists(filename) (FS_FLocateFile(filename,FSLFRT_LENGTH, NULL)>0) + +int COM_filelength (FILE *f); +qbyte *COM_LoadStackFile (char *path, void *buffer, int bufsize); +qbyte *COM_LoadTempFile (char *path); +qbyte *COM_LoadTempFile2 (char *path); //allocates a little bit more without freeing old temp +qbyte *COM_LoadHunkFile (char *path); +qbyte *COM_LoadMallocFile (char *path); +void COM_LoadCacheFile (char *path, struct cache_user_s *cu); +void COM_CreatePath (char *path); +void COM_Gamedir (char *dir); +char *COM_NextPath (char *prevpath); +void COM_FlushFSCache(void); //a file was written using fopen +void COM_RefreshFSCache_f(void); + +qboolean COM_LoadMapPackFile(char *name, int offset); +void COM_FlushTempoaryPacks(void); + +void COM_EnumerateFiles (char *match, int (*func)(char *, int, void *), void *parm); + +extern struct cvar_s registered; +extern qboolean standard_quake, rogue, hipnotic; + +#define MAX_INFO_KEY 64 +char *Info_ValueForKey (char *s, char *key); +void Info_RemoveKey (char *s, char *key); +void Info_RemovePrefixedKeys (char *start, char prefix); +void Info_RemoveNonStarKeys (char *start); +void Info_SetValueForKey (char *s, char *key, char *value, int maxsize); +void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize); +void Info_Print (char *s); + +unsigned Com_BlockChecksum (void *buffer, int length); +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); +qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum); +qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); +qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); + +int build_number( void ); + + + +void TL_InitLanguages(void); +void T_FreeStrings(void); +char *T_GetString(int num); + diff --git a/engine/common/crc.c b/engine/common/crc.c new file mode 100644 index 000000000..b9e76d94e --- /dev/null +++ b/engine/common/crc.c @@ -0,0 +1,93 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/* crc.c */ + +#include "quakedef.h" +#include "crc.h" + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte(unsigned short *crcvalue, qbyte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} + +unsigned short CRC_Block (qbyte *start, int count) +{ + unsigned short crc; + + CRC_Init (&crc); + while (count--) + crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; + + return crc; +} + diff --git a/engine/common/crc.h b/engine/common/crc.h new file mode 100644 index 000000000..71fbe7bd1 --- /dev/null +++ b/engine/common/crc.h @@ -0,0 +1,25 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/* crc.h */ + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, qbyte data); +unsigned short CRC_Value(unsigned short crcvalue); +unsigned short CRC_Block (qbyte *start, int count); diff --git a/engine/common/cvar.c b/engine/common/cvar.c new file mode 100644 index 000000000..efa3f2214 --- /dev/null +++ b/engine/common/cvar.c @@ -0,0 +1,647 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// cvar.c -- dynamic variable tracking + +#ifdef SERVERONLY +#include "qwsvdef.h" +#else +#include "quakedef.h" +#endif + +cvar_group_t *cvar_groups; + +//cvar_t *cvar_vars; +char *cvar_null_string = ""; + +/* +============ +Cvar_FindVar +============ +*/ +cvar_t *Cvar_FindVar (char *var_name) +{ + cvar_group_t *grp; + cvar_t *var; + + for (grp=cvar_groups ; grp ; grp=grp->next) + for (var=grp->cvars ; var ; var=var->next) + if (!Q_strcasecmp (var_name, var->name)) + return var; + + for (grp=cvar_groups ; grp ; grp=grp->next) + for (var=grp->cvars ; var ; var=var->next) + if (var->name2 && !Q_strcasecmp (var_name, var->name2)) + return var; + + return NULL; +} + +cvar_group_t *Cvar_FindGroup (char *group_name) +{ + cvar_group_t *grp; + + for (grp=cvar_groups ; grp ; grp=grp->next) + if (!Q_strcasecmp (group_name, grp->name)) + return grp; + + return NULL; +} +cvar_group_t *Cvar_GetGroup(char *gname) +{ + cvar_group_t *g; + if (!gname) + gname = "Miscilaneous vars"; + g = Cvar_FindGroup(gname); + if (g) + return g; + + g = Z_Malloc(sizeof(cvar_group_t)); + g->name = gname; + g->next = NULL; + + g->next = cvar_groups; + cvar_groups = g; + + return g; +} + +//lists commands, also prints restriction level +void Cvar_List_f (void) +{ + cvar_group_t *grp; + cvar_t *cmd; + int num=0; + for (grp=cvar_groups ; grp ; grp=grp->next) + for (cmd=grp->cvars ; cmd ; cmd=cmd->next) + { + if ((cmd->restriction?cmd->restriction:rcon_level.value) > Cmd_ExecLevel) + continue; + if (!num) + Con_TPrintf(TL_CVARLISTHEADER); + Con_Printf("(%2i) %s\n", (int)(cmd->restriction?cmd->restriction:rcon_level.value), cmd->name); + num++; + } + if (num) + Con_Printf("\n"); +} + +/* +============ +Cvar_VariableValue +============ +*/ +float Cvar_VariableValue (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + return 0; + return Q_atof (var->string); +} + + +/* +============ +Cvar_VariableString +============ +*/ +char *Cvar_VariableString (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + return cvar_null_string; + return var->string; +} + + +/* +============ +Cvar_CompleteVariable +============ +*/ +/* moved to cmd_compleatevariable +char *Cvar_CompleteVariable (char *partial) +{ + cvar_group_t *grp; + cvar_t *cvar; + int len; + + len = Q_strlen(partial); + + if (!len) + return NULL; + + // check exact match + for (grp=cvar_groups ; grp ; grp=grp->next) + for (cvar=grp->cvars ; cvar ; cvar=cvar->next) + if (!strcmp (partial,cvar->name)) + return cvar->name; + + // check partial match + for (grp=cvar_groups ; grp ; grp=grp->next) + for (cvar=grp->cvars ; cvar ; cvar=cvar->next) + if (!Q_strncmp (partial,cvar->name, len)) + return cvar->name; + + return NULL; +} +*/ + + +#ifdef SERVERONLY +void SV_SendServerInfoChange(char *key, char *value); +#endif + +/* +============ +Cvar_Set +============ +*/ +cvar_t *Cvar_SetCore (cvar_t *var, char *value, qboolean force) +{ + char *latch=NULL; + + if (!var) + return NULL; + + if ((var->flags & CVAR_NOSET) && !force) + { + Con_Printf ("variable %s is readonly\n", var->name); + return NULL; + } + + if (var->flags & CVAR_SERVEROVERRIDE && !force) + latch = "variable %s is under server control - latched\n"; + else if (var->flags & CVAR_LATCH) + latch = "variable %s is latched\n"; + else if (var->flags & CVAR_RENDERERLATCH && qrenderer) + latch = "variable %s will be changed after a renderer restart\n"; +#ifndef SERVERONLY + else if (var->flags & CVAR_CHEAT && !cls.allow_cheats) + latch = "variable %s is a cheat variable - latched\n"; + else if (var->flags & CVAR_SEMICHEAT && !cls.allow_semicheats) + latch = "variable %s is a cheat variable - latched\n"; +#endif + + if (latch && !force) + { + if (cl_warncmd.value) + Con_Printf (latch, var->name); + + if (var->latched_string && !strcmp(var->latched_string, value)) //no point, this would force the same + return NULL; + if (var->latched_string) + Z_Free(var->latched_string); + if (!strcmp(var->string, value)) //latch to the origional value? remove the latch. + { + var->latched_string = NULL; + return NULL; + } + var->latched_string = Z_Malloc(strlen(value)+1); + strcpy(var->latched_string, value); + return NULL; + } + +#ifndef CLIENTONLY + if (var->flags & CVAR_SERVERINFO) + { + Info_SetValueForKey (svs.info, var->name, value, MAX_SERVERINFO_STRING); + SV_SendServerInfoChange(var->name, value); +// SV_BroadcastCommand ("fullserverinfo \"%s\"\n", svs.info); + } +#endif +#ifndef SERVERONLY + if (var->flags & CVAR_USERINFO) + { + Info_SetValueForKey (cls.userinfo, var->name, value, MAX_INFO_STRING); + if (cls.state >= ca_connected) + { +#ifdef Q2CLIENT + if (cls.q2server) //q2 just resends the lot. Kinda bad... + { + cls.resendinfo = true; + } + else +#endif + { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, va("setinfo \"%s\" \"%s\"\n", var->name, value)); + } + } + } +#endif + + if (var->string) + { + if (strcmp(var->string, value)) + var->modified++; //only modified if it changed. + + Z_Free (var->string); // free the old value string + } + + var->string = Z_Malloc (Q_strlen(value)+1); + Q_strcpy (var->string, value); + var->value = Q_atof (var->string); + + if (var->latched_string) //we may as well have this here. + { + Z_Free(var->latched_string); + var->latched_string = NULL; + } + + return var; +} + +void Cvar_ForceCheatVars(qboolean semicheats, qboolean absolutecheats) +{ //this either unlatches if the cheat type is allowed, or enforces a default for full cheats and blank for semicheats. + //this is clientside only. + //if a value is enforced, it is latched to the old value. + cvar_group_t *grp; + cvar_t *var; + + char *latch; + + for (grp=cvar_groups ; grp ; grp=grp->next) + for (var=grp->cvars ; var ; var=var->next) + { + if (!(var->flags & (CVAR_CHEAT|CVAR_SEMICHEAT))) + continue; + + latch = var->latched_string; + var->latched_string = NULL; + if (!latch) + { + latch = var->string; + var->string = NULL; + } + + if (var->flags & CVAR_CHEAT) + { + if (!absolutecheats) + Cvar_ForceSet(var, var->defaultstr); + else + Cvar_ForceSet(var, latch); + } + if (var->flags & CVAR_SEMICHEAT) + { + if (!semicheats) + Cvar_ForceSet(var, ""); + else + Cvar_ForceSet(var, latch); + } + + if (latch) + { + if (!strcmp(var->string, latch)) + Z_Free(latch); + else + var->latched_string = latch; + } + } +} + +void Cvar_ApplyLatches(int latchflag) +{ + cvar_group_t *grp; + cvar_t *var; + for (grp=cvar_groups ; grp ; grp=grp->next) + for (var=grp->cvars ; var ; var=var->next) + { + if (var->flags & latchflag) + { + if (var->latched_string) + { + var->flags &= ~CVAR_NOSET; + Cvar_ForceSet(var, var->latched_string); + } + } + } +} + +cvar_t *Cvar_Set (cvar_t *var, char *value) +{ + return Cvar_SetCore(var, value, false); +} +cvar_t *Cvar_ForceSet (cvar_t *var, char *value) +{ + return Cvar_SetCore(var, value, true); +} + +/* +============ +Cvar_SetValue +============ +*/ +void Cvar_SetValue (cvar_t *var, float value) +{ + char val[32]; + + if (value == (int)value) + sprintf (val, "%i",(int)value); //make it look nicer. + else + sprintf (val, "%f",value); + Cvar_Set (var, val); +} + +/* +============ +Cvar_RegisterVariable + +Adds a freestanding variable to the variable list. +============ +*/ +void Cvar_Register (cvar_t *variable, char *groupname) +{ + cvar_t *old; + cvar_group_t *group; + char value[512]; + +// first check to see if it has allready been defined + old = Cvar_FindVar (variable->name); + if (old) + { + if (old->flags & CVAR_POINTER) + { + cvar_t *prev; + group = Cvar_GetGroup(groupname); + + variable->next = old->next; + variable->latched_string = old->latched_string; + variable->string = old->string; + variable->modified = old->modified; + variable->value = old->value; + + //cheat prevention - engine set default is the one that stays. + Z_Free(variable->defaultstr); + variable->defaultstr = Z_Malloc (strlen(variable->string)+1); //give it it's default (for server controlled vars and things) + strcpy (variable->defaultstr, variable->string); + + if (group->cvars == old) + group->cvars = variable; + else + { + for (prev = group->cvars; prev; prev++) + { + if (prev->next == old) + { + prev->next = variable; + break; + } + } + if (!prev) //this should never happen + Sys_Error("Cvar was not linked\n"); + } + Z_Free(old); + return; + } + + Con_Printf ("Can't register variable %s, allready defined\n", variable->name); + return; + } + +// check for overlap with a command + if (Cmd_Exists (variable->name)) + { + Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name); + return; + } + + group = Cvar_GetGroup(groupname); + +// link the variable in + variable->next = group->cvars; + variable->restriction = 0; //exe registered vars + group->cvars = variable; + +// copy the value off, because future sets will Z_Free it + strcpy (value, variable->string); + variable->string = Z_Malloc (1); + + variable->defaultstr = Z_Malloc (strlen(value)+1); //give it it's default (for server controlled vars and things) + strcpy (variable->defaultstr, value); + +// set it through the function to be consistant + Cvar_SetCore (variable, value, true); +} +/* +void Cvar_RegisterVariable (cvar_t *variable) +{ + Cvar_Register(variable, NULL); +} +*/ +cvar_t *Cvar_Get(char *name, char *defaultvalue, int flags, char *group) +{ + cvar_t *var; + var = Cvar_FindVar(name); + + if (var) + { + //allow this to change all < cvar_latch values. + //this allows q2 dlls to apply different flags to a cvar without destroying our important ones (like cheat). + var->flags = (flags & (CVAR_LATCH-1)) | (var->flags & ~(CVAR_LATCH-1)); + return var; + } + + var = Z_Malloc(sizeof(cvar_t)+strlen(name)+1); + var->name = (char *)(var+1); + strcpy(var->name, name); + var->string = defaultvalue; + var->flags = flags|CVAR_POINTER; + + Cvar_Register(var, group); + + return var; +} + +//prevent the client from altering the cvar until they change map or the server resets the var to the default. +void Cvar_LockFromServer(cvar_t *var, char *str) +{ + char *oldlatch; + + var->flags |= CVAR_SERVEROVERRIDE; + + oldlatch = var->latched_string; + if (oldlatch) //maintaining control + var->latched_string = NULL; + else //taking control + { + oldlatch = Z_Malloc(strlen(var->string)+1); + strcpy(oldlatch, var->string); + } + + Cvar_SetCore (var, str, true); //will use all, quote included + + var->latched_string = oldlatch; //keep track of the origional value. +} + +/* +============ +Cvar_Command + +Handles variable inspection and changing from the console +============ +*/ +qboolean Cvar_Command (int level) +{ + cvar_t *v; + char *str; + +// check variables + v = Cvar_FindVar (Cmd_Argv(0)); + if (!v) + return false; + + if ((v->restriction?v->restriction:rcon_level.value) > level) + { + Con_Printf ("You do not have the priveledges for %s\n", v->name); + return true; + } + + if (v->flags & CVAR_NOTFROMSERVER && Cmd_FromServer()) + { + Con_Printf ("Server tried setting %s cvar\n", v->name); + return true; + } +// perform a variable print or set + if (Cmd_Argc() == 1) + { + Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); + if (v->latched_string) + Con_Printf ("Latched as \"%s\"\n", v->latched_string); + return true; + } + + if (Cmd_Argc() == 2) + str = Cmd_Argv(1); + else + str = Cmd_Args(); + + if (v->flags & CVAR_NOSET) + { + Con_Printf ("Cvar %s may not be set via the console\n", v->name); + return true; + } +#ifndef SERVERONLY + if (Cmd_ExecLevel > RESTRICT_SERVER) + { //directed at a secondary player. + char *msg; + msg = va("%i setinfo %s \"%s\"", Cmd_ExecLevel - RESTRICT_SERVER-1, v->name, str); + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, msg); + return true; + } + + if (v->flags & CVAR_SERVEROVERRIDE) + { + if (Cmd_FromServer()) + { + if (!strcmp(v->defaultstr, str)) //returning to default + { + v->flags &= ~CVAR_SERVEROVERRIDE; + if (v->latched_string) + str = v->latched_string; //set to the latched + } + else + { + Cvar_LockFromServer(v, str); + return true; + } + } + //let cvar_set latch if needed. + } + else if (Cmd_FromServer()) + { + Cvar_LockFromServer(v, str); + return true; + } +#endif + Cvar_Set (v, str); //will use all, quote included + return true; +} + + +/* +============ +Cvar_WriteVariables + +Writes lines containing "set variable value" for all variables +with the archive flag set to true. +============ +*/ +void Cvar_WriteVariables (FILE *f, qboolean all) +{ + qboolean writtengroupheader; + cvar_group_t *grp; + cvar_t *var; + char *val; + + for (grp=cvar_groups ; grp ; grp=grp->next) + { + writtengroupheader = false; + for (var = grp->cvars ; var ; var = var->next) + if (var->flags & CVAR_ARCHIVE || all) + { + if (!writtengroupheader) + { + writtengroupheader = true; + fprintf(f, "\n// %s\n", grp->name); + } + + val = var->string; //latched vars should act differently. + if (var->latched_string) + val = var->latched_string; + + if (var->flags & CVAR_USERCREATED) + { + if (var->flags & CVAR_ARCHIVE) + fprintf (f, "seta %s \"%s\"\n", var->name, val); + else + fprintf (f, "set %s \"%s\"\n", var->name, val); + } + else + fprintf (f, "%s \"%s\"\n", var->name, val); + } + } +} + +void Cvar_Shutdown(void) +{ + cvar_t *var; + cvar_group_t *grp; + while(cvar_groups) + { + while(cvar_groups->cvars) + { + var = cvar_groups->cvars; + cvar_groups->cvars = var->next; + + Z_Free(var->string); + if (var->flags & CVAR_POINTER) + Z_Free(var); + } + + grp = cvar_groups; + cvar_groups = grp->next; + Z_Free(grp); + } +} diff --git a/engine/common/cvar.h b/engine/common/cvar.h new file mode 100644 index 000000000..5be4b364e --- /dev/null +++ b/engine/common/cvar.h @@ -0,0 +1,145 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// cvar.h + +/* + +cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly +in C code. + +it is sufficient to initialize a cvar_t with just the first two fields, or +you can add a ,true flag for variables that you want saved to the configuration +file when the game is quit: + +cvar_t r_draworder = {"r_draworder","1"}; +cvar_t scr_screensize = {"screensize","1",true}; + +Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed: +Cvar_RegisterVariable (&host_framerate); + + +C code usually just references a cvar in place: +if ( r_draworder.value ) + +It could optionally ask for the value to be looked up for a string name: +if (Cvar_VariableValue ("r_draworder")) + +Interpreted prog code can access cvars with the cvar(name) or +cvar_set (name, value) internal functions: +teamplay = cvar("teamplay"); +cvar_set ("registered", "1"); + +The user can access cvars from the console in two ways: +r_draworder prints the current value +r_draworder 0 sets the current value to 0 +Cvars are restricted from having the same names as commands to keep this +interface from being ambiguous. +*/ + +typedef struct cvar_s +{ + //must match q2's definition + char *name; + char *string; + char *latched_string; // for CVAR_LATCH vars + int flags; + qboolean modified; // set each time the cvar is changed + float value; + struct cvar_s *next; + + //free style :) + char *name2; + char *defaultstr; //default + qbyte restriction; +} cvar_t; + +typedef struct cvar_group_s +{ + char *name; + struct cvar_group_s *next; + + cvar_t *cvars; +} cvar_group_t; + +//q2 constants +#define CVAR_ARCHIVE 1 // set to cause it to be saved to vars.rc +#define CVAR_USERINFO 2 // added to userinfo when changed +#define CVAR_SERVERINFO 4 // added to serverinfo when changed +#define CVAR_NOSET 8 // don't allow change from console at all, + // but can be set from the command line +#define CVAR_LATCH 16 // save changes until server restart + +//freestyle +#define CVAR_POINTER 32 // q2 style. May be converted to q1 if needed. These are often specified on the command line and then converted into q1 when registered properly. +#define CVAR_NOTFROMSERVER 64 +#define CVAR_USERCREATED 128 //write a 'set' or 'seta' in front of the var name. +#define CVAR_CHEAT 256 //latch from 0 unless cheats are enabled. +#define CVAR_SEMICHEAT 512 //if strict ruleset, force to 0. +#define CVAR_RENDERERLATCH 1024 +#define CVAR_SERVEROVERRIDE 2048 // + +#define CVAR_LATCHMASK (CVAR_LATCH|CVAR_RENDERERLATCH|CVAR_SERVEROVERRIDE|CVAR_CHEAT|CVAR_SEMICHEAT) //you're only allowed one of these. +#define CVAR_NEEDDEFAULT CVAR_CHEAT + +cvar_t *Cvar_Get (char *var_name, char *value, int flags, char *groupname); + + +void Cvar_Register (cvar_t *variable, char *cvargroup); +// registers a cvar that allready has the name, string, and optionally the +// archive elements set. + +//#define Cvar_RegisterVariable(x) Cvar_Register(x,__FILE__); + +cvar_t *Cvar_ForceSet (cvar_t *var, char *value); +cvar_t *Cvar_Set (cvar_t *var, char *value); +// equivelant to " " typed at the console + +void Cvar_SetValue (cvar_t *var, float value); +// expands value to a string and calls Cvar_Set + +void Cvar_ApplyLatches(int latchflag); +//sets vars to thier latched values + +float Cvar_VariableValue (char *var_name); +// returns 0 if not defined or non numeric + +char *Cvar_VariableString (char *var_name); +// returns an empty string if not defined + +char *Cvar_CompleteVariable (char *partial); +// attempts to match a partial variable name for command line completion +// returns NULL if nothing fits + +qboolean Cvar_Command (int level); +// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known +// command. Returns true if the command was a variable reference that +// was handled. (print or change) + +void Cvar_WriteVariables (FILE *f, qboolean all); +// Writes lines containing "set variable value" for all variables +// with the archive flag set to true. + +cvar_t *Cvar_FindVar (char *var_name); + +void Cvar_Shutdown(void); + +void Cvar_ForceCheatVars(qboolean semicheats, qboolean absolutecheats); //locks/unlocks cheat cvars depending on weather we are allowed them. + +//extern cvar_t *cvar_vars; diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c new file mode 100644 index 000000000..7a5fe9b96 --- /dev/null +++ b/engine/common/gl_q2bsp.c @@ -0,0 +1,4937 @@ +#include "quakedef.h" +#include "glquake.h" + +#define MAX_Q3MAP_INDICES 0x80000 +#define MAX_Q3MAP_VERTEXES 0x80000 +#define MAX_Q3MAP_BRUSHSIDES 0x30000 +#define MAX_CM_BRUSHSIDES (MAX_Q3MAP_BRUSHSIDES << 1) +#define MAX_CM_BRUSHES (MAX_Q2MAP_BRUSHES << 1) +#define MAX_CM_PATCH_VERTS (4096) +#define MAX_CM_FACES (MAX_Q2MAP_FACES) +#define MAX_CM_PATCHES (0x10000) +#define MAX_CM_LEAFFACES (MAX_Q2MAP_LEAFFACES) +#define MAX_CM_AREAS MAX_Q2MAP_AREAS + +#define Q3SURF_NODRAW 0x80 // don't generate a drawsurface at all +#define Q3SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes +#define Q3SURF_NONSOLID 0x4000 // don't collide against curves with this set + +#if Q3SURF_NODRAW != SURF_NODRAW +#error "nodraw isn't constant" +#endif + + +//these are in model.c (or gl_model.c) +void GLMod_LoadVertexes (lump_t *l); +void GLMod_LoadEdges (lump_t *l); +void GLMod_LoadMarksurfaces (lump_t *l); +void GLMod_LoadSurfedges (lump_t *l); +void GLMod_LoadLighting (lump_t *l); + +void SWMod_LoadVertexes (lump_t *l); +void SWMod_LoadEdges (lump_t *l); +void SWMod_LoadMarksurfaces (lump_t *l); +void SWMod_LoadSurfedges (lump_t *l); +void SWMod_LoadLighting (lump_t *l); + + +void Q2BSP_SetHullFuncs(hull_t *hull); + +qbyte areabits[MAX_Q2MAP_AREAS/8]; + + +extern char loadname[32]; +extern model_t *loadmodel; +float RadiusFromBounds (vec3_t mins, vec3_t maxs) +{ + int i; + vec3_t corner; + + for (i=0 ; i<3 ; i++) + { + corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); + } + + return Length (corner); +} + +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + mtexinfo_t *tex; + int bmins[2], bmaxs[2]; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = s->texinfo; + + for (i=0 ; inumedges ; i++) + { + e = loadmodel->surfedges[s->firstedge+i]; + if (e >= 0) + v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else + v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + bmins[i] = floor(mins[i]/16); + bmaxs[i] = ceil(maxs[i]/16); + + s->texturemins[i] = bmins[i] * 16; + s->extents[i] = (bmaxs[i] - bmins[i]) * 16; + +// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 ) //q2 uses 512. +// Sys_Error ("Bad surface extents"); + } +} + +void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs) +{ + int i; + vec_t val; + + for (i=0 ; i<3 ; i++) + { + val = v[i]; + if (val < mins[i]) + mins[i] = val; + if (val > maxs[i]) + maxs[i] = val; + } +} + +void ClearBounds (vec3_t mins, vec3_t maxs) +{ + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; +} + + +#ifdef Q2BSPS + +qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out); + +#ifdef SERVERONLY +#define Host_Error SV_Error +#endif + +extern model_t *loadmodel; +extern qbyte *mod_base; + + + + +unsigned char d_q28to24table[1024]; + + +/* +typedef struct q2csurface_s +{ + char name[16]; + int flags; + int value; +} q2csurface_t; +*/ + +typedef struct q2mapsurface_s // used internally due to name len probs //ZOID +{ + q2csurface_t c; + char rname[32]; +} q2mapsurface_t; + + + + + + + + + + + + + + + + + + + + + + + + + +typedef struct cmodel_s +{ + vec3_t mins, maxs; + vec3_t origin; // for sounds or lights + int headnode; + int numsurfaces; + int firstsurface; + + int firstbrush; //q3 submodels are considered small enough that you will never need to walk any sort of tree. + int num_brushes;//the brushes are checked instead. +} q2cmodel_t; + + + + + + + +typedef struct +{ + mplane_t *plane; + q2mapsurface_t *surface; +} q2cbrushside_t; + +typedef struct +{ + int contents; + int numsides; + int firstbrushside; + int checkcount; // to avoid repeated testings +} q2cbrush_t; + +typedef struct +{ + int numareaportals; + int firstareaportal; + int floodnum; // if two areas have equal floodnums, they are connected + int floodvalid; +} q2carea_t; + +typedef struct +{ + int numareaportals[MAX_CM_AREAS]; +} q3carea_t; + +typedef struct +{ + vec3_t absmins, absmaxs; + + int numbrushes; + q2cbrush_t *brushes; + + q2mapsurface_t *surface; + int checkcount; // to avoid repeated testings +} q3cpatch_t; + +typedef struct +{ + int facetype; + + int numverts; + int firstvert; + + int shadernum; + int patch_cp[2]; +} q3cface_t; + +int checkcount; + +//FIXME: Unlimit these. +char map_name[MAX_QPATH]; + +int numbrushsides; +q2cbrushside_t map_brushsides[MAX_Q2MAP_BRUSHSIDES]; + +int numtexinfo; +q2mapsurface_t map_surfaces[MAX_Q2MAP_TEXINFO]; + +int numplanes; +mplane_t map_planes[MAX_Q2MAP_PLANES+6]; // extra for box hull + +int numnodes; +mnode_t map_nodes[MAX_Q2MAP_NODES+6]; // extra for box hull + +int numleafs = 1; // allow leaf funcs to be called without a map +mleaf_t map_leafs[MAX_Q2MAP_LEAFS]; +int emptyleaf; + +int numleafbrushes; +int map_leafbrushes[MAX_Q2MAP_LEAFBRUSHES]; + +int numcmodels; +q2cmodel_t map_cmodels[MAX_Q2MAP_MODELS]; + +int numbrushes; +q2cbrush_t map_brushes[MAX_Q2MAP_BRUSHES]; + +int numvisibility; +qbyte map_visibility[MAX_Q2MAP_VISIBILITY]; +q2dvis_t *map_q2vis = (q2dvis_t *)map_visibility; +q3dvis_t *map_q3pvs = (q3dvis_t *)map_visibility; +qbyte map_hearability[MAX_Q2MAP_VISIBILITY]; +q3dvis_t *map_q3phs = (q3dvis_t *)map_hearability; + +int numentitychars; +char map_entitystring[MAX_Q2MAP_ENTSTRING]; + +int numareas = 1; +q2carea_t map_q2areas[MAX_Q2MAP_AREAS]; +q3carea_t map_q3areas[MAX_CM_AREAS]; + +int numareaportals; +q2dareaportal_t map_areaportals[MAX_Q2MAP_AREAPORTALS]; + +q3cpatch_t map_patches[MAX_CM_PATCHES]; +int numpatches; + +int map_leafpatches[MAX_CM_LEAFFACES]; +int numleafpatches; + +int numclusters = 1; + +q2mapsurface_t nullsurface; + +int floodvalid; + +qbyte portalopen[MAX_Q2MAP_AREAPORTALS]; //memset will work if it's a qbyte, really it should be a qboolean + + +static int mapisq3; +cvar_t map_noareas = {"map_noareas", "1"}; //1 for lack of mod support. +cvar_t map_noCurves = {"map_noCurves", "0", NULL, CVAR_CHEAT}; +cvar_t map_autoopenportals = {"map_autoopenportals", "1"}; //1 for lack of mod support. +cvar_t r_subdivisions = {"r_subdivisions", "1"}; + +int CM_NumInlineModels (void); +q2cmodel_t *CM_InlineModel (char *name); +void CM_InitBoxHull (void); +void FloodAreaConnections (void); + + +int c_pointcontents; +int c_traces, c_brush_traces; + + +vec4_t *map_verts; //3points + pad (I'm not entirly sure of the need for the pad, maybe it's just faster) +int numvertexes; + +vec2_t *map_vertstmexcoords; +vec2_t *map_vertlstmexcoords; + +q3cface_t *map_faces; +int numfaces; + +int *map_surfindexes; +int map_numsurfindexes; + +int *map_leaffaces; +int numleaffaces; + + + + + + + + + +int PlaneTypeForNormal ( vec3_t normal ) +{ + vec_t ax, ay, az; + +// NOTE: should these have an epsilon around 1.0? + if ( normal[0] >= 1.0) + return PLANE_X; + if ( normal[1] >= 1.0 ) + return PLANE_Y; + if ( normal[2] >= 1.0 ) + return PLANE_Z; + + ax = fabs( normal[0] ); + ay = fabs( normal[1] ); + az = fabs( normal[2] ); + + if ( ax >= ay && ax >= az ) + return PLANE_ANYX; + if ( ay >= ax && ay >= az ) + return PLANE_ANYY; + return PLANE_ANYZ; +} + +void CategorizePlane ( mplane_t *plane ) +{ + int i; + + plane->signbits = 0; + plane->type = PLANE_ANYZ; + for (i = 0; i < 3; i++) + { + if (plane->normal[i] < 0) + plane->signbits |= 1<normal[i] == 1.0f) + plane->type = i; + } + plane->type = PlaneTypeForNormal(plane->normal); +} + +void PlaneFromPoints ( vec3_t verts[3], mplane_t *plane ) +{ + vec3_t v1, v2; + + VectorSubtract( verts[1], verts[0], v1 ); + VectorSubtract( verts[2], verts[0], v2 ); + CrossProduct( v2, v1, plane->normal ); + VectorNormalize( plane->normal ); + plane->dist = DotProduct( verts[0], plane->normal ); +} + +qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2) +{ + return (mins1[0] <= maxs2[0] && mins1[1] <= maxs2[1] && mins1[2] <= maxs2[2] && + maxs1[0] >= mins2[0] && maxs1[1] >= mins2[1] && maxs1[2] >= mins2[2]); +} + + +#define VectorAvg(a,b,c) ((c)[0]=((a)[0]+(b)[0])*0.5f,(c)[1]=((a)[1]+(b)[1])*0.5f, (c)[2]=((a)[2]+(b)[2])*0.5f) +#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) +#define Vector4Scale(in,scale,out) ((out)[0]=(in)[0]*scale,(out)[1]=(in)[1]*scale,(out)[2]=(in)[2]*scale,(out)[3]=(in)[3]*scale) +#define Vector4Add(a,b,c) ((c)[0]=(((a[0])+(b[0]))),(c)[1]=(((a[1])+(b[1]))),(c)[2]=(((a[2])+(b[2]))),(c)[3]=(((a[3])+(b[3])))) + +/* +=============== +Patch_FlatnessTest +=============== +*/ +static int Patch_FlatnessTest ( float maxflat, vec4_t point0, vec4_t point1, vec4_t point2 ) +{ + vec3_t v1, v2, v3; + vec3_t t, n; + float dist, d, l; + int ft0, ft1; + + VectorSubtract ( point2, point0, n ); + l = VectorNormalize ( n ); + + if ( !l ) { + return 0; + } + + VectorSubtract ( point1, point0, t ); + d = -DotProduct ( t, n ); + VectorMA ( t, d, n, t ); + dist = VectorLength ( t ); + + if ( fabs(dist) <= maxflat ) { + return 0; + } + + VectorAvg ( point1, point0, v1 ); + VectorAvg ( point2, point1, v2 ); + VectorAvg ( v1, v2, v3 ); + + ft0 = Patch_FlatnessTest ( maxflat, point0, v1, v3 ); + ft1 = Patch_FlatnessTest ( maxflat, v3, v2, point2 ); + + return 1 + (int)floor( max ( ft0, ft1 ) + 0.5f ); +} + +/* +=============== +Patch_GetFlatness +=============== +*/ +void Patch_GetFlatness ( float maxflat, vec4_t *points, int *patch_cp, int *flat ) +{ + int i, p, u, v; + + flat[0] = flat[1] = 0; + for (v = 0; v < patch_cp[1] - 1; v += 2) + { + for (u = 0; u < patch_cp[0] - 1; u += 2) + { + p = v * patch_cp[0] + u; + + i = Patch_FlatnessTest ( maxflat, points[p], points[p+1], points[p+2] ); + flat[0] = max ( flat[0], i ); + i = Patch_FlatnessTest ( maxflat, points[p+patch_cp[0]], points[p+patch_cp[0]+1], points[p+patch_cp[0]+2] ); + flat[0] = max ( flat[0], i ); + i = Patch_FlatnessTest ( maxflat, points[p+2*patch_cp[0]], points[p+2*patch_cp[0]+1], points[p+2*patch_cp[0]+2] ); + flat[0] = max ( flat[0], i ); + + i = Patch_FlatnessTest ( maxflat, points[p], points[p+patch_cp[0]], points[p+2*patch_cp[0]] ); + flat[1] = max ( flat[1], i ); + i = Patch_FlatnessTest ( maxflat, points[p+1], points[p+patch_cp[0]+1], points[p+2*patch_cp[0]+1] ); + flat[1] = max ( flat[1], i ); + i = Patch_FlatnessTest ( maxflat, points[p+2], points[p+patch_cp[0]+2], points[p+2*patch_cp[0]+2] ); + flat[1] = max ( flat[1], i ); + } + } +} + +/* +=============== +Patch_Evaluate_QuadricBezier +=============== +*/ +static void Patch_Evaluate_QuadricBezier ( float t, vec4_t point0, vec4_t point1, vec3_t point2, vec4_t out ) +{ + float qt = t * t; + float dt = 2.0f * t, tt; + vec4_t tvec4; + + tt = 1.0f - dt + qt; + Vector4Scale ( point0, tt, out ); + + tt = dt - 2.0f * qt; + Vector4Scale ( point1, tt, tvec4 ); + Vector4Add ( out, tvec4, out ); + + Vector4Scale ( point2, qt, tvec4 ); + Vector4Add ( out, tvec4, out ); +} + +/* +=============== +Patch_Evaluate +=============== +*/ +void Patch_Evaluate ( vec4_t *p, int *numcp, int *tess, vec4_t *dest ) +{ + int num_patches[2], num_tess[2]; + int index[3], dstpitch, i, u, v, x, y; + float s, t, step[2]; + vec4_t *tvec, pv[3][3], v1, v2, v3; + + num_patches[0] = numcp[0] / 2; + num_patches[1] = numcp[1] / 2; + dstpitch = num_patches[0] * tess[0] + 1; + + step[0] = 1.0f / (float)tess[0]; + step[1] = 1.0f / (float)tess[1]; + + for ( v = 0; v < num_patches[1]; v++ ) + { + // last patch has one more row + if ( v < num_patches[1] - 1 ) { + num_tess[1] = tess[1]; + } else { + num_tess[1] = tess[1] + 1; + } + + for ( u = 0; u < num_patches[0]; u++ ) + { + // last patch has one more column + if ( u < num_patches[0] - 1 ) { + num_tess[0] = tess[0]; + } else { + num_tess[0] = tess[0] + 1; + } + + index[0] = (v * numcp[0] + u) * 2; + index[1] = index[0] + numcp[0]; + index[2] = index[1] + numcp[0]; + + // current 3x3 patch control points + for ( i = 0; i < 3; i++ ) + { + Vector4Copy ( p[index[0]+i], pv[i][0] ); + Vector4Copy ( p[index[1]+i], pv[i][1] ); + Vector4Copy ( p[index[2]+i], pv[i][2] ); + } + + t = 0.0f; + tvec = dest + v * tess[1] * dstpitch + u * tess[0]; + + for ( y = 0; y < num_tess[1]; y++, t += step[1] ) + { + Patch_Evaluate_QuadricBezier ( t, pv[0][0], pv[0][1], pv[0][2], v1 ); + Patch_Evaluate_QuadricBezier ( t, pv[1][0], pv[1][1], pv[1][2], v2 ); + Patch_Evaluate_QuadricBezier ( t, pv[2][0], pv[2][1], pv[2][2], v3 ); + + s = 0.0f; + for ( x = 0; x < num_tess[0]; x++, s += step[0] ) + { + Patch_Evaluate_QuadricBezier ( s, v1, v2, v3, tvec[x] ); + } + + tvec += dstpitch; + } + } + } +} + + + + +/* +=============================================================================== + + PATCH LOADING + +=============================================================================== +*/ + +#define cm_subdivlevel 15 + +void CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surface ) +{ + int i, j, k, sign; + vec3_t v1, v2; + vec3_t absmins, absmaxs; + q2cbrushside_t *side; + mplane_t *plane; + static mplane_t mainplane, patchplanes[20]; + qboolean skip[20]; + int numpatchplanes = 0; + + // calc absmins & absmaxs + ClearBounds ( absmins, absmaxs ); + for (i = 0; i < 3; i++) + AddPointToBounds ( verts[i], absmins, absmaxs ); + + PlaneFromPoints ( verts, &mainplane ); + + // front plane + plane = &patchplanes[numpatchplanes++]; + *plane = mainplane; + + // back plane + plane = &patchplanes[numpatchplanes++]; + VectorNegate (mainplane.normal, plane->normal); + plane->dist = -mainplane.dist; + + // axial planes + for ( i = 0; i < 3; i++ ) { + for (sign = -1; sign <= 1; sign += 2) { + plane = &patchplanes[numpatchplanes++]; + VectorClear ( plane->normal ); + plane->normal[i] = sign; + plane->dist = sign > 0 ? absmaxs[i] : -absmins[i]; + } + } + + // edge planes + for ( i = 0; i < 3; i++ ) { + vec3_t normal; + + VectorCopy (verts[i], v1); + VectorCopy (verts[(i + 1) % 3], v2); + + for ( k = 0; k < 3; k++ ) { + normal[k] = 0; + normal[(k+1)%3] = v1[(k+2)%3] - v2[(k+2)%3]; + normal[(k+2)%3] = -(v1[(k+1)%3] - v2[(k+1)%3]); + + if (VectorCompare (normal, vec3_origin)) + continue; + + plane = &patchplanes[numpatchplanes++]; + + VectorNormalize ( normal ); + VectorCopy ( normal, plane->normal ); + plane->dist = DotProduct (plane->normal, v1); + + if ( DotProduct(verts[(i + 2) % 3], normal) - plane->dist > 0 ) + { // invert + VectorInverse ( plane->normal ); + plane->dist = -plane->dist; + } + } + } + + // set plane->type and mark duplicate planes for removal + for (i = 0; i < numpatchplanes; i++) + { + CategorizePlane ( &patchplanes[i] ); + skip[i] = false; + + for (j = i + 1; j < numpatchplanes; j++) + if ( patchplanes[j].dist == patchplanes[i].dist + && VectorCompare (patchplanes[j].normal, patchplanes[i].normal) ) + { + skip[i] = true; + break; + } + } + + brush->numsides = 0; + brush->firstbrushside = numbrushsides; + + for (k = 0; k < 2; k++) { + for (i = 0; i < numpatchplanes; i++) { + if (skip[i]) + continue; + + // first, store all axially aligned planes + // then store everything else + // does it give a noticeable speedup? + if (!k && patchplanes[i].type >= 3) + continue; + + skip[i] = true; + + if (numplanes == MAX_Q2MAP_PLANES) + Host_Error ("CM_CreateBrush: numplanes == MAX_CM_PLANES"); + + plane = &map_planes[numplanes++]; + *plane = patchplanes[i]; + + if (numbrushsides == MAX_CM_BRUSHSIDES) + Host_Error ("CM_CreateBrush: numbrushsides == MAX_CM_BRUSHSIDES"); + + side = &map_brushsides[numbrushsides++]; + side->plane = plane; + + if (DotProduct(plane->normal, mainplane.normal) >= 0) + side->surface = surface; + else + side->surface = NULL; // don't clip against this side + + brush->numsides++; + } + } +} + +void CM_CreatePatch ( q3cpatch_t *patch, int numverts, vec4_t *verts, int *patch_cp ) +{ + int step[2], size[2], flat[2], i, u, v; + vec4_t points[MAX_CM_PATCH_VERTS]; + vec3_t tverts[4], tverts2[4]; + q2cbrush_t *brush; + mplane_t mainplane; + +// find the degree of subdivision in the u and v directions + Patch_GetFlatness ( cm_subdivlevel, verts, patch_cp, flat ); + + step[0] = (1 << flat[0]); + step[1] = (1 << flat[1]); + size[0] = (patch_cp[0] / 2) * step[0] + 1; + size[1] = (patch_cp[1] / 2) * step[1] + 1; + + if ( size[0] * size[1] > MAX_CM_PATCH_VERTS ) { + Host_Error ( "CM_CreatePatch: patch has too many vertices" ); + return; + } + +// fill in + Patch_Evaluate ( verts, patch_cp, step, points ); + + patch->brushes = brush = map_brushes + numbrushes; + patch->numbrushes = 0; + + ClearBounds (patch->absmins, patch->absmaxs); + +// create a set of brushes + for (v = 0; v < size[1]-1; v++) + { + for (u = 0; u < size[0]-1; u++) + { + if (numbrushes >= MAX_CM_BRUSHES) + Host_Error ("CM_CreatePatch: too many patch brushes"); + + i = v * size[0] + u; + VectorCopy (points[i], tverts[0]); + VectorCopy (points[i + size[0]], tverts[1]); + VectorCopy (points[i + 1], tverts[2]); + VectorCopy (points[i + size[0] + 1], tverts[3]); + + for (i = 0; i < 4; i++) + AddPointToBounds (tverts[i], patch->absmins, patch->absmaxs); + + PlaneFromPoints (tverts, &mainplane); + + // create two brushes + CM_CreateBrush (brush, tverts, patch->surface); + + brush->contents = patch->surface->c.value; + brush++; numbrushes++; patch->numbrushes++; + + VectorCopy (tverts[2], tverts2[0]); + VectorCopy (tverts[1], tverts2[1]); + VectorCopy (tverts[3], tverts2[2]); + CM_CreateBrush (brush, tverts2, patch->surface); + + brush->contents = patch->surface->c.value; + brush++; numbrushes++; patch->numbrushes++; + } + } +} + + +//====================================================== + +/* +================= +CM_CreatePatchesForLeafs +================= +*/ +void CM_CreatePatchesForLeafs (void) +{ + int i, j, k; + mleaf_t *leaf; + q3cface_t *face; + q2mapsurface_t *surf; + q3cpatch_t *patch; + int checkout[MAX_CM_FACES]; + + memset (checkout, -1, sizeof(int)*MAX_CM_FACES); + + for (i = 0, leaf = map_leafs; i < numleafs; i++, leaf++) + { + leaf->numleafpatches = 0; + leaf->firstleafpatch = numleafpatches; + + if (leaf->cluster == -1) + continue; + + for (j=0 ; jnumleaffaces ; j++) + { + k = leaf->firstleafface + j; + if (k >= numleaffaces) { + break; + } + + k = map_leaffaces[k]; + face = &map_faces[k]; + + if (face->facetype != MST_PATCH || face->numverts <= 0) + continue; + if (face->patch_cp[0] <= 0 || face->patch_cp[1] <= 0) + continue; + if (face->shadernum < 0 || face->shadernum >= numtexinfo) + continue; + + surf = &map_surfaces[face->shadernum]; + if ( !surf->c.value || (surf->c.flags & Q3SURF_NONSOLID) ) + continue; + + if ( numleafpatches >= MAX_CM_LEAFFACES ) + Host_Error ("CM_CreatePatchesForLeafs: map has too many faces"); + + // the patch was already built + if (checkout[k] != -1) + { + map_leafpatches[numleafpatches] = checkout[k]; + patch = &map_patches[checkout[k]]; + } + else + { + if (numpatches >= MAX_CM_PATCHES) + Host_Error ("CM_CreatePatchesForLeafs: map has too many patches"); + + patch = &map_patches[numpatches]; + patch->surface = surf; + map_leafpatches[numleafpatches] = numpatches; + checkout[k] = numpatches++; + + CM_CreatePatch ( patch, face->numverts, map_verts + face->firstvert, face->patch_cp ); + } + + leaf->contents |= patch->surface->c.value; + leaf->numleafpatches++; + + numleafpatches++; + } + } +} + + + +/* +=============================================================================== + + MAP LOADING + +=============================================================================== +*/ + +qbyte *cmod_base; + +/* +================= +CMod_LoadSubmodels +================= +*/ +void CMod_LoadSubmodels (lump_t *l) +{ + q2dmodel_t *in; + q2cmodel_t *out; + int i, j, count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no models"); + if (count > MAX_Q2MAP_MODELS) + Host_Error ("Map has too many models"); + + numcmodels = count; + + for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = LittleFloat (in->origin[j]); + } + out->headnode = LittleLong (in->headnode); + out->firstsurface = LittleLong (in->firstface); + out->numsurfaces = LittleLong (in->numfaces); + } +} + + +/* +================= +CMod_LoadSurfaces +================= +*/ +void CMod_LoadSurfaces (lump_t *l) +{ + q2texinfo_t *in; + q2mapsurface_t *out; + int i, count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + if (count < 1) + Host_Error ("Map with no surfaces"); + if (count > MAX_Q2MAP_TEXINFO) + Host_Error ("Map has too many surfaces"); + + numtexinfo = count; + out = map_surfaces; + + for ( i=0 ; ic.name, in->texture, sizeof(out->c.name)); + Q_strncpyz (out->rname, in->texture, sizeof(out->rname)); + out->c.flags = LittleLong (in->flags); + out->c.value = LittleLong (in->value); + } +} +#ifndef SERVERONLY +qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height); +qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, int asgrey); + + +qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, int asgrey); +qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height); +void *Mod_LoadWall(char *name) +{ + qbyte *in, *oin; + texture_t *tex; + q2miptex_t *wal; + int width, height; + char ln[32]; + + COM_FileBase(name, ln); + + wal = (void *)COM_LoadMallocFile (name); + if (!wal) + return NULL; + +//FIXME: Is this needed? + oin = in = ReadPCXFile((qbyte *)wal, com_filesize, &width, &height); + if (!in) + oin = in = ReadTargaFile((qbyte *)wal, com_filesize, &width, &height, false); + if (in) //this is a pcx. + { +#ifdef RGLQUAKE + if (qrenderer == QR_OPENGL) + { + tex = Hunk_AllocName(sizeof(texture_t), ln); + + tex->offsets[0] = sizeof(*tex); + tex->width = width; + tex->height = height; + + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + if (!(tex->gl_texturenum = Mod_LoadReplacementTexture(name, true, false))) + tex->gl_texturenum = GL_LoadTexture32 (name, width, height, (unsigned int *)in, true, false); + texture_mode = GL_LINEAR; + } + else +#endif +#ifdef SWQUAKE + if (qrenderer == QR_SOFTWARE) + { + int i, j; + qbyte *out; + + tex = Hunk_AllocName(sizeof(texture_t) + width*r_pixbytes*height/64*85, ln); + tex->pixbytes = r_pixbytes; + tex->width = width; + tex->height = height; + + tex->offsets[0] = sizeof(*tex); + tex->offsets[1] = tex->offsets[0] + width*height*r_pixbytes; + tex->offsets[2] = tex->offsets[1] + (width*height*r_pixbytes)/4; + tex->offsets[3] = tex->offsets[2] + (width*height*r_pixbytes)/(4*4); + + out = (qbyte *)(tex+1); + + if (tex->pixbytes == 4) + { + for (i = 0; i < width*height; i++) + { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + *out++ = in[3]; + in+=4; + } + } + else + { + for (i=0; i < tex->width*tex->height; i++) //downgrade colour + { + *out++ = GetPalette(in[0], in[1], in[2]); + in+=4; + } + + in = (qbyte *)tex+tex->offsets[0]; //shrink mips. + + for (j = 0; j < tex->height; j+=2) //we could convert mip[1], but shrinking is probably faster. + for (i = 0; i < tex->width; i+=2) + *out++ = in[i + tex->width*j]; + + for (j = 0; j < tex->height; j+=4) + for (i = 0; i < tex->width; i+=4) + *out++ = in[i + tex->width*j]; + + for (j = 0; j < tex->height; j+=8) + for (i = 0; i < tex->width; i+=8) + *out++ = in[i + tex->width*j]; + } + } + else +#endif + { + Sys_Error("Mod_LoadWall with bad renderer\n"); + tex = NULL; + } + + BZ_Free(oin); + BZ_Free(wal); + + return tex; + } + +#if defined(RGLQUAKE) + if (qrenderer == QR_OPENGL) + { + int j; + tex = Hunk_AllocName(sizeof(texture_t), ln); + + tex->offsets[0] = wal->offsets[0]; + tex->width = wal->width; + tex->height = wal->height; + + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + if (!(tex->gl_texturenum = Mod_LoadReplacementTexture(wal->name, true, false))) + tex->gl_texturenum = GL_LoadTexture8Pal24 (wal->name, tex->width, tex->height, (qbyte *)wal+wal->offsets[0], d_q28to24table, true, false); + + in = Hunk_TempAllocMore(wal->width*wal->height); + oin = (qbyte *)wal+wal->offsets[0]; + for (j = 0; j < wal->width*wal->height; j++) + in[j] = (d_q28to24table[oin[j]*3+0] + d_q28to24table[oin[j]*3+1] + d_q28to24table[oin[j]*3+2])/3; + tex->gl_texturenumbumpmap = GL_LoadTexture8Bump (va("%s_bump", wal->name), tex->width, tex->height, in, true); + texture_mode = GL_LINEAR; + } + else +#endif +#if defined(SWQUAKE) + if (qrenderer == QR_SOFTWARE) + { + int i, j; + qbyte *out; + + tex = Hunk_AllocName(sizeof(texture_t) + wal->width*r_pixbytes*wal->height/64*85, ln); + tex->width = wal->width; + tex->height = wal->height; + + tex->pixbytes = r_pixbytes; + for (i = 0; i < MIPLEVELS; i++) + tex->offsets[i] = (wal->offsets[i] - sizeof(*wal))*tex->pixbytes + sizeof(*tex); + + out = (qbyte *)(tex+1); + in = (qbyte *)wal+wal->offsets[0]; + + if (tex->pixbytes == 4) + { + for (i = 0; i < wal->width*wal->height/64*85; i++) + { + *out++ = d_q28to24table[*in*3+0]; + *out++ = d_q28to24table[*in*3+1]; + *out++ = d_q28to24table[*in*3+2]; + *out++ = 255; + in++; + } + } + else + { + for (i=0; i < tex->width*tex->height; i++) //downgrade colour + { + *out++ = GetPalette(d_q28to24table[*in*3+0], d_q28to24table[*in*3+1], d_q28to24table[*in*3+2]); + in++; + } + in = (qbyte *)tex+tex->offsets[0]; //shrink mips. + + for (j = 0; j < tex->height; j+=2) //we could convert mip[1], but shrinking is probably faster. + for (i = 0; i < tex->width; i+=2) + *out++ = in[i + tex->width*j]; + + for (j = 0; j < tex->height; j+=4) + for (i = 0; i < tex->width; i+=4) + *out++ = in[i + tex->width*j]; + + for (j = 0; j < tex->height; j+=8) + for (i = 0; i < tex->width; i+=8) + *out++ = in[i + tex->width*j]; + } + } + else +#endif + { + Sys_Error("Mod_LoadWall with bad renderer\n"); + tex = NULL; + } + + BZ_Free(wal); + + return tex; +} + +void CMod_LoadTexInfo (lump_t *l) //yes I know these load from the same place +{ + q2texinfo_t *in; + mtexinfo_t *out; + int i, j, count; + char name[MAX_QPATH]; + float len1, len2; + int texcount; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->textures = Hunk_AllocName(sizeof(texture_t *)*count, loadname); + texcount = 0; + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + +#if !defined(SERVERONLY) && defined(RGLQUAKE) + skytexturenum = -1; +#endif + + for ( i=0 ; iflags = LittleLong (in->flags); + + for (j=0 ; j<8 ; j++) + out->vecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (out->vecs[0]); + len2 = Length (out->vecs[1]); + len1 = (len1 + len2)/2; + if (len1 < 0.32) + out->mipadjust = 4; + else if (len1 < 0.49) + out->mipadjust = 3; + else if (len1 < 0.99) + out->mipadjust = 2; + else + out->mipadjust = 1; + + //damn q2... + for (j=0; j < texcount; j++) + { + if (!strcmp(in->texture, loadmodel->textures[j]->name)) + { + out->texture = loadmodel->textures[j]; + break; + } + } + if (j == texcount) //load a new one + { + _snprintf (name, sizeof(name), "textures/%s.wal", in->texture); + + out->texture = Mod_LoadWall (name); + if (!out->texture) + { + out->texture = Hunk_Alloc(sizeof(texture_t)); + + Con_Printf ("Couldn't load %s\n", name); + memcpy(out->texture, &r_notexture_mip, sizeof(r_notexture_mip)); + // out->texture = r_notexture_mip; // texture not found + // out->flags = 0; + } + Q_strncpyz(out->texture->name, in->texture, sizeof(out->texture->name)); + +#if !defined(SERVERONLY) && defined(RGLQUAKE) + if (out->flags & SURF_SKY) + skytexturenum = texcount; +#endif + loadmodel->textures[texcount++] = out->texture; + } +#if !defined(SERVERONLY) && defined(RGLQUAKE) + else if (out->flags & SURF_SKY) + out->texture = loadmodel->textures[skytexturenum]; +#endif + } + + loadmodel->numtextures = texcount; + + // count animation frames + /* + for (i=0 ; itexinfo[i]; +// out->numframes = 1; +// for (step = out->next ; step && step != out ; step=step->next) +// out->numframes++; + } + */ +} +#endif +/* +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + mtexinfo_t *tex; + int bmins[2], bmaxs[2]; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = s->texinfo; + + for (i=0 ; inumedges ; i++) + { + e = loadmodel->surfedges[s->firstedge+i]; + if (e >= 0) + v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else + v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + bmins[i] = floor(mins[i]/16); + bmaxs[i] = ceil(maxs[i]/16); + + s->texturemins[i] = bmins[i] * 16; + s->extents[i] = (bmaxs[i] - bmins[i]) * 16; + +// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 )// 256 ) +// Sys_Error ("Bad surface extents"); + } +}*/ + + +/* +================= +Mod_LoadFaces +================= +*/ +#ifndef SERVERONLY +void CMod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + int ti; + extern qboolean r_usinglits; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count+6)*sizeof(*out), loadname); //spare for skybox + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; +#if defined(RGLQUAKE) + out->polys = NULL; +#endif + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + ti = LittleShort (in->texinfo); + if (ti < 0 || ti >= loadmodel->numtexinfo) + Host_Error ("MOD_LoadBmodel: bad texinfo number"); + out->texinfo = loadmodel->texinfo + ti; + +#ifndef SERVERONLY + if (out->texinfo->flags & SURF_SKY) + { + out->flags |= SURF_DRAWSKY; + } +#endif + + CalcSurfaceExtents (out); + + // lighting info + + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; +#ifdef RGLQUAKE + else if (qrenderer == QR_OPENGL) + out->samples = loadmodel->lightdata + i; +#endif +#ifdef SWQUAKE + else if (r_usinglits) + out->samples = loadmodel->lightdata + i; +#endif + else + out->samples = loadmodel->lightdata + i/3; + + // set the drawing flags + /* + if (out->texinfo->flags & SURF_WARP) + { + out->flags |= SURF_DRAWTURB; + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + GL_SubdivideSurface (out); // cut up polygon for warps + } + */ + } +} +#endif + +void CMod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents != -1) + return; + CMod_SetParent (node->children[0], node); + CMod_SetParent (node->children[1], node); +} + +/* +================= +CMod_LoadNodes + +================= +*/ +void CMod_LoadNodes (lump_t *l) +{ + q2dnode_t *in; + int child; + mnode_t *out; + int i, j, count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map has no nodes"); + if (count > MAX_Q2MAP_NODES) + Host_Error ("Map has too many nodes"); + + out = map_nodes; + + numnodes = count; + + loadmodel->nodes = out; + loadmodel->numnodes = numnodes; + + for (i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + out->plane = map_planes + LittleLong(in->planenum); + + out->firstsurface = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + out->contents = -1; // differentiate from leafs + + for (j=0 ; j<2 ; j++) + { + child = LittleLong (in->children[j]); + out->childnum[j] = child; + if (child < 0) + out->children[j] = (mnode_t *)(map_leafs + -1-child); + else + out->children[j] = map_nodes + child; + } + } + + CMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +CMod_LoadBrushes + +================= +*/ +void CMod_LoadBrushes (lump_t *l) +{ + q2dbrush_t *in; + q2cbrush_t *out; + int i, count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_Q2MAP_BRUSHES) + Host_Error ("Map has too many brushes"); + + out = map_brushes; + + numbrushes = count; + + for (i=0 ; ifirstbrushside = LittleLong(in->firstside); + out->numsides = LittleLong(in->numsides); + out->contents = LittleLong(in->contents); + } + +} + +/* +================= +CMod_LoadLeafs +================= +*/ +void CMod_LoadLeafs (lump_t *l) +{ + int i, j; + mleaf_t *out; + q2dleaf_t *in; + int count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no leafs"); + // need to save space for box planes + if (count > MAX_Q2MAP_PLANES) + Host_Error ("Map has too many planes"); + + out = map_leafs; + numleafs = count; + numclusters = 0; + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + out->contents = LittleLong (in->contents); + out->cluster = LittleShort (in->cluster); + out->area = LittleShort (in->area); + out->firstleafbrush = LittleShort (in->firstleafbrush); + out->numleafbrushes = LittleShort (in->numleafbrushes); + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstleafface); + out->nummarksurfaces = LittleShort(in->numleaffaces); + + if (out->cluster >= numclusters) + numclusters = out->cluster + 1; + } + + if (map_leafs[0].contents != Q2CONTENTS_SOLID) + Host_Error ("Map leaf 0 is not CONTENTS_SOLID"); + + emptyleaf = -1; + for (i=1 ; ifileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no planes"); + // need to save space for box planes + if (count > MAX_Q2MAP_PLANES) + Host_Error ("Map has too many planes"); + + out = map_planes; + numplanes = count; + + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<dist = LittleFloat (in->dist); + out->type = LittleLong (in->type); + out->signbits = bits; + } +} + +/* +================= +CMod_LoadLeafBrushes +================= +*/ +void CMod_LoadLeafBrushes (lump_t *l) +{ + int i; + int *out; + unsigned short *in; + int count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no planes"); + // need to save space for box planes + if (count > MAX_Q2MAP_LEAFBRUSHES) + Host_Error ("Map has too many leafbrushes"); + + out = map_leafbrushes; + numleafbrushes = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + // need to save space for box planes + if (count > MAX_Q2MAP_BRUSHSIDES) + Host_Error ("Map has too many planes"); + + out = map_brushsides; + numbrushsides = count; + + for ( i=0 ; iplanenum); + out->plane = &map_planes[num]; + j = LittleShort (in->texinfo); + if (j >= numtexinfo) + Host_Error ("Bad brushside texinfo"); + out->surface = &map_surfaces[j]; + } +} + +/* +================= +CMod_LoadAreas +================= +*/ +void CMod_LoadAreas (lump_t *l) +{ + int i; + q2carea_t *out; + q2darea_t *in; + int count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_Q2MAP_AREAS) + Host_Error ("Map has too many areas"); + + out = map_q2areas; + numareas = count; + + for ( i=0 ; inumareaportals = LittleLong (in->numareaportals); + out->firstareaportal = LittleLong (in->firstareaportal); + out->floodvalid = 0; + out->floodnum = 0; + } +} + +/* +================= +CMod_LoadAreaPortals +================= +*/ +void CMod_LoadAreaPortals (lump_t *l) +{ + int i; + q2dareaportal_t *out; + q2dareaportal_t *in; + int count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_Q2MAP_AREAS) + Host_Error ("Map has too many areas"); + + out = map_areaportals; + numareaportals = count; + + for ( i=0 ; iportalnum = LittleLong (in->portalnum); + out->otherarea = LittleLong (in->otherarea); + } +} + +/* +================= +CMod_LoadVisibility +================= +*/ +void CMod_LoadVisibility (lump_t *l) +{ + int i; + + numvisibility = l->filelen; + if (l->filelen > MAX_Q2MAP_VISIBILITY) + Host_Error ("Map has too large visibility lump"); + + memcpy (map_visibility, cmod_base + l->fileofs, l->filelen); + + loadmodel->vis = map_q2vis; + + map_q2vis->numclusters = LittleLong (map_q2vis->numclusters); + for (i=0 ; inumclusters ; i++) + { + map_q2vis->bitofs[i][0] = LittleLong (map_q2vis->bitofs[i][0]); + map_q2vis->bitofs[i][1] = LittleLong (map_q2vis->bitofs[i][1]); + } +} + +/* +================= +CMod_LoadEntityString +================= +*/ +void CMod_LoadEntityString (lump_t *l) +{ + numentitychars = l->filelen; + if (l->filelen > MAX_Q2MAP_ENTSTRING) + Host_Error ("Map has too large entity lump"); + + memcpy (map_entitystring, cmod_base + l->fileofs, l->filelen); + + loadmodel->entities = map_entitystring; +} + + + + +void CModQ3_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + int *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +void CModQ3_LoadSubmodels (lump_t *l) +{ + q3dmodel_t *in; + q2cmodel_t *out; + int i, j, count; + int *leafbrush; + mleaf_t *bleaf; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no models"); + if (count > MAX_Q2MAP_MODELS) + Host_Error ("Map has too many models"); + + numcmodels = count; + + mapisq3 = true; + + for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = (out->maxs[j] + out->mins[j])/2; + } + if (!i) + out->headnode = 0; + else + { +//create a new leaf to hold the bruses and be directly clipped + out->headnode = -1 - numleafs; + out->firstsurface = LittleLong (in->firstsurface); + out->numsurfaces = LittleLong (in->num_surfaces); + +// out->firstbrush = LittleLong(in->firstbrush); +// out->num_brushes = LittleLong(in->num_brushes); + + bleaf = &map_leafs[numleafs++]; + bleaf->numleafbrushes = LittleLong ( in->num_brushes ); + bleaf->firstleafbrush = numleafbrushes; + bleaf->contents = 0; + + leafbrush = &map_leafbrushes[numleafbrushes]; + for ( j = 0; j < bleaf->numleafbrushes; j++, leafbrush++ ) { + *leafbrush = LittleLong ( in->firstbrush ) + j; + bleaf->contents |= map_brushes[*leafbrush].contents; + } + numleafbrushes += bleaf->numleafbrushes; + } + //submodels + } +} + +void CModQ3_LoadShaders (lump_t *l) +{ + dq3shader_t *in; + q2mapsurface_t *out; + int i, count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no shaders"); + else if (count > MAX_Q2MAP_TEXINFO) + Host_Error ("Map has too many shaders"); + + numtexinfo = count; + out = map_surfaces; + +#if !defined(SERVERONLY) && defined(RGLQUAKE) + skytexturenum = -1; +#endif + + loadmodel->texinfo = Hunk_Alloc(sizeof(mtexinfo_t)*count); + loadmodel->numtextures = count; + loadmodel->textures = Hunk_Alloc(sizeof(texture_t*)*count); + + for ( i=0 ; itexinfo[i].texture = Hunk_Alloc(sizeof(texture_t)); + Q_strncpyz(loadmodel->texinfo[i].texture->name, in->shadername, sizeof(loadmodel->texinfo[i].texture->name)); +#ifdef RGLQUAKE + if (qrenderer == QR_OPENGL) + { + loadmodel->texinfo[i].texture->gl_texturenum = Mod_LoadHiResTexture(in->shadername, true, false); + loadmodel->texinfo[i].texture->gl_texturenumfb = 0; + loadmodel->texinfo[i].texture->gl_texturenumbumpmap = 0; + } +#endif + loadmodel->textures[i] = loadmodel->texinfo[i].texture; + + out->c.flags = LittleLong ( in->surfflags ); + out->c.value = LittleLong ( in->contents ); + +#if !defined(SERVERONLY) && defined(RGLQUAKE) + if (!strncmp(in->shadername, "textures/skies/", 15)) + { + loadmodel->texinfo[i].flags |= SURF_SKY; + skytexturenum = i; + } +#endif + } +} + +void CModQ3_LoadVertexes (lump_t *l) +{ + q3dvertex_t *in; + vec4_t *out; + int i, count, j; + vec2_t *lmout, *stout; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("CMOD_LoadVertexes: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_Q3MAP_VERTEXES) + Host_Error ("Map has too many vertexes"); + + out = Hunk_Alloc ( count*sizeof(*out) ); + stout = Hunk_Alloc ( count*sizeof(*stout) ); + lmout = Hunk_Alloc ( count*sizeof(*lmout) ); + map_verts = out; + map_vertstmexcoords = stout; + map_vertlstmexcoords = lmout; + numvertexes = count; + + for ( i=0 ; ipoint[j] ); + } + for ( j=0 ; j < 2 ; j++) + { + stout[i][j] = LittleFloat ( ((float *)in->texcoords)[j] ); + lmout[i][j] = LittleFloat ( ((float *)in->texcoords)[j+2] ); + } + } +} + +void CModQ3_LoadIndexes (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + if (count < 1 || count >= MAX_Q3MAP_INDICES) + Host_Error ("MOD_LoadBmodel: bad surfedges count in %s: %i", + loadmodel->name, count); + + out = Hunk_AllocName ( count*sizeof(*out), loadmodel->name ); + + map_surfindexes = out; + map_numsurfindexes = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_MAP_FACES) + Host_Error ("Map has too many faces"); + + out = BZ_Malloc ( count*sizeof(*out) ); + map_faces = out; + numfaces = count; + + for ( i=0 ; ifacetype = LittleLong ( in->facetype ); + out->shadernum = LittleLong ( in->shadernum ); + + out->numverts = LittleLong ( in->num_vertices ); + out->firstvert = LittleLong ( in->firstvertex ); + + out->patch_cp[0] = LittleLong ( in->patchwidth ); + out->patch_cp[1] = LittleLong ( in->patchheight ); + } + + loadmodel->numsurfaces = i; +} + +#ifdef RGLQUAKE + +//Convert a patch in to a list of glpolys + +#define MAX_ARRAY_VERTS 2048 + + +glpoly_t *GL_MeshToGLPoly(mesh_t *mesh) +{ + int polysize = sizeof(glpoly_t) - (VERTEXSIZE)*sizeof(float); + int gv; + int v; + int rv; + glpoly_t *p, *ret; + + int numindx; + if (!mesh) + return NULL; + + numindx = mesh->numindexes; + ret = NULL; + + p = Hunk_Alloc(polysize * numindx/3); + + for (gv = 0; gv < numindx; ) + { + for (v = gv; v < gv+3; v++) + { + rv = mesh->indexes[v]; + p->verts[v%3][0] = mesh->xyz_array[rv][0]; + p->verts[v%3][1] = mesh->xyz_array[rv][1]; + p->verts[v%3][2] = mesh->xyz_array[rv][2]; + p->verts[v%3][3] = mesh->st_array[rv][0]; + p->verts[v%3][4] = mesh->st_array[rv][1]; + p->verts[v%3][5] = mesh->lmst_array[rv][0]; + p->verts[v%3][6] = mesh->lmst_array[rv][1]; + } + gv+=3; + + p->next = ret; + p->numverts = 3; + ret = p; + p = (glpoly_t *)((char *)p + polysize); + } + + return ret; +} + +#define Vector2Copy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];} + +vec_t VectorNormalize2 (vec3_t v, vec3_t out) +{ + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + + if (length) + { + length = sqrt (length); // FIXME + ilength = 1/length; + out[0] = v[0]*ilength; + out[1] = v[1]*ilength; + out[2] = v[2]*ilength; + } + else + { + VectorClear (out); + } + + return length; +} +float ColorNormalize (vec3_t in, vec3_t out) +{ + float f = max (max (in[0], in[1]), in[2]); + + if ( f > 1.0 ) { + f = 1.0 / f; + out[0] = in[0] * f; + out[1] = in[1] * f; + out[2] = in[2] * f; + } else { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + } + + return f; +} +int tempIndexesArray[MAX_ARRAY_VERTS*3]; +vec4_t tempxyz_array[MAX_ARRAY_VERTS]; //structure is used only at load. +vec3_t tempnormals_array[MAX_ARRAY_VERTS]; //so what harm is there in doing this? +vec2_t tempst_array[MAX_ARRAY_VERTS]; +vec2_t templmst_array[MAX_ARRAY_VERTS]; +byte_vec4_t tempcolors_array[MAX_ARRAY_VERTS]; + +mesh_t *GL_CreateMeshForPatch ( model_t *mod, q3dface_t *surf ) +{ + int numverts, numindexes, firstvert, patch_cp[2], step[2], size[2], flat[2], i, u, v, p; + vec4_t colors[MAX_ARRAY_VERTS], points[MAX_ARRAY_VERTS], normals[MAX_ARRAY_VERTS], + lm_st[MAX_ARRAY_VERTS], tex_st[MAX_ARRAY_VERTS]; + vec4_t c, colors2[MAX_ARRAY_VERTS], normals2[MAX_ARRAY_VERTS], lm_st2[MAX_ARRAY_VERTS], tex_st2[MAX_ARRAY_VERTS]; + mesh_t *mesh; + int *indexes; + float subdivlevel; + + patch_cp[0] = LittleLong ( surf->patchwidth ); + patch_cp[1] = LittleLong ( surf->patchheight ); + + if ( !patch_cp[0] || !patch_cp[1] ) { + return NULL; + } + + subdivlevel = r_subdivisions.value; + if ( subdivlevel < 1 ) + subdivlevel = 1; + + numverts = LittleLong ( surf->num_vertices ); + firstvert = LittleLong ( surf->firstvertex ); + for ( i = 0; i < numverts; i++ ) { + VectorCopy ( map_verts[firstvert + i], points[i] ); +// VectorCopy ( mod->bmodel->normals_array[firstvert + i], normals[i] ); +// Vector4Scale ( mod->bmodel->colors_array[firstvert + i], (1.0 / 255.0), colors[i] ); + Vector2Copy ( map_vertstmexcoords[firstvert + i], tex_st[i] ); + Vector2Copy ( map_vertlstmexcoords[firstvert + i], lm_st[i] ); + } + +// find the degree of subdivision in the u and v directions + Patch_GetFlatness ( subdivlevel, points, patch_cp, flat ); + +// allocate space for mesh + step[0] = (1 << flat[0]); + step[1] = (1 << flat[1]); + size[0] = (patch_cp[0] / 2) * step[0] + 1; + size[1] = (patch_cp[1] / 2) * step[1] + 1; + numverts = size[0] * size[1]; + + if ( numverts > MAX_ARRAY_VERTS ) { + return NULL; + } + + mesh = (mesh_t *)Hunk_TempAllocMore ( sizeof(mesh_t)); + + mesh->numvertexes = numverts; + mesh->xyz_array = Hunk_TempAllocMore ( numverts * sizeof(vec4_t)); + mesh->normals_array = Hunk_TempAllocMore ( numverts * sizeof(vec3_t)); + mesh->st_array = Hunk_TempAllocMore ( numverts * sizeof(vec2_t)); + mesh->lmst_array = Hunk_TempAllocMore ( numverts * sizeof(vec2_t)); + mesh->colors_array = Hunk_TempAllocMore ( numverts * sizeof(byte_vec4_t)); + + mesh->patchWidth = size[0]; + mesh->patchHeight = size[1]; + +// fill in + Patch_Evaluate ( points, patch_cp, step, mesh->xyz_array ); + Patch_Evaluate ( colors, patch_cp, step, colors2 ); + Patch_Evaluate ( normals, patch_cp, step, normals2 ); + Patch_Evaluate ( lm_st, patch_cp, step, lm_st2 ); + Patch_Evaluate ( tex_st, patch_cp, step, tex_st2 ); + + for (i = 0; i < numverts; i++) + { + VectorNormalize2 ( normals2[i], mesh->normals_array[i] ); + ColorNormalize ( colors2[i], c ); + Vector4Scale ( c, 255.0, mesh->colors_array[i] ); + Vector2Copy ( tex_st2[i], mesh->st_array[i] ); + Vector2Copy ( lm_st2[i], mesh->lmst_array[i] ); + } + +// compute new indexes avoiding adding invalid triangles + numindexes = 0; + indexes = tempIndexesArray; + for (v = 0, i = 0; v < size[1]-1; v++) + { + for (u = 0; u < size[0]-1; u++, i += 6) + { + indexes[0] = p = v * size[0] + u; + indexes[1] = p + size[0]; + indexes[2] = p + 1; + + if ( !VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) && + !VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) && + !VectorCompare(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) ) { + indexes += 3; + numindexes += 3; + } + + indexes[0] = p + 1; + indexes[1] = p + size[0]; + indexes[2] = p + size[0] + 1; + + if ( !VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) && + !VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) && + !VectorCompare(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) ) { + indexes += 3; + numindexes += 3; + } + } + } + +// allocate and fill index table + mesh->numindexes = numindexes; + + mesh->indexes = (int *)Hunk_TempAllocMore ( numindexes * sizeof(int)); + memcpy (mesh->indexes, tempIndexesArray, numindexes * sizeof(int) ); + + return mesh; +} + + + + +void CModQ3_LoadRFaces (lump_t *l) +{ + int polysize = sizeof(glpoly_t) - VERTEXSIZE*sizeof(float); + q3dface_t *in; + msurface_t *out; + mplane_t *pl; + + int count; + int surfnum; + + int numverts, numindexes; + int gv, v; + int rv; + int fv, fi; + + glpoly_t *p; + mesh_t *mesh; + + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadmodel->name ); + pl = Hunk_AllocName (count*sizeof(*p), loadmodel->name);//create a new array of planes for speed. + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for (surfnum = 0; surfnum < count; surfnum++, out++, in++, pl++) + { + out->plane = pl; + out->texinfo = loadmodel->texinfo + LittleLong(in->shadernum); + in->facetype = LittleLong(in->facetype); + out->lightmaptexturenum = in->lightmapnum; + out->light_s = in->lightmap_x; + out->light_t = in->lightmap_y; + out->extents[0] = (in->lightmap_width-1)<<4; + out->extents[1] = (in->lightmap_height-1)<<4; + out->samples = loadmodel->lightdata + 3*(out->light_s + out->light_t*128 + out->lightmaptexturenum*128*128); +if (out->lightmaptexturenum<0) +out->samples=NULL; + +fv = LittleLong(in->firstvertex); +{ + vec3_t v[3]; + VectorCopy(map_verts[fv+0], v[0]); + VectorCopy(map_verts[fv+1], v[1]); + VectorCopy(map_verts[fv+2], v[2]); + PlaneFromPoints(v, pl); + CategorizePlane(pl); +} +/* +if (in->fognum!=-1) +continue; +*/ + if (map_surfaces[in->shadernum].c.value == 0 || map_surfaces[in->shadernum].c.value & Q3CONTENTS_TRANSLUCENT) + //q3dm10's thingie is 0 + out->flags |= SURF_DRAWALPHA; + + if (loadmodel->texinfo[in->shadernum].flags & SURF_SKY) + out->flags |= SURF_DRAWSKY; + if (map_surfaces[in->shadernum].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP)) + { + out->mesh = NULL; + out->polys = NULL; + } + else if (in->facetype == MST_PATCH) + { + mesh = GL_CreateMeshForPatch(loadmodel, in); + out->polys = GL_MeshToGLPoly(mesh); + out->mesh = NULL; + } + else if (in->facetype == MST_PLANAR || in->facetype == MST_TRIANGLE_SOUP) + { + numindexes = LittleLong(in->num_indexes); + numverts = LittleLong(in->num_vertices); + if (numindexes%3) + Host_Error("mesh indexes should be multiples of 3"); +/* + out->mesh = Hunk_Alloc(sizeof(mesh_t) + (sizeof(vec3_t) + sizeof(byte_vec4_t)) * numverts); + out->mesh->normals_array= (vec3_t *)(out->mesh+1); + out->mesh->colors_array = (byte_vec4_t *)(out->mesh->normals_array + numverts); + + out->mesh->indexes = map_surfindexes + LittleLong(in->firstindex); + out->mesh->xyz_array = map_verts + LittleLong(in->firstvertex); + out->mesh->st_array = map_vertstmexcoords + LittleLong(in->firstvertex); + out->mesh->lmst_array = map_vertlstmexcoords + LittleLong(in->firstvertex); + + out->mesh->numindexes = numindexes; + out->mesh->numvertexes = numverts; +*/ + + p = Hunk_AllocName (polysize*numindexes/3, "SDList"); + fv = LittleLong(in->firstvertex); + fi = LittleLong(in->firstindex); + for (gv = 0; gv < numindexes; ) + { + for (v = gv; v < gv+3; v++) + { + rv = fv+map_surfindexes[fi+v]; + p->verts[v%3][0] = map_verts[rv][0]; + p->verts[v%3][1] = map_verts[rv][1]; + p->verts[v%3][2] = map_verts[rv][2]; + p->verts[v%3][3] = map_vertstmexcoords[rv][0]; + p->verts[v%3][4] = map_vertstmexcoords[rv][1]; + p->verts[v%3][5] = map_vertlstmexcoords[rv][0]; + p->verts[v%3][6] = map_vertlstmexcoords[rv][1]; + } + gv+=3; + + p->next = out->polys; + p->numverts = 3; + out->polys = p; + p = (glpoly_t *)((char *)p + polysize); + } + } + } +} +#endif + +void CModQ3_LoadLeafFaces (lump_t *l) +{ + int i, j, count; + int *in; + int *out; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_Q2MAP_LEAFFACES) + Host_Error ("Map has too many leaffaces"); + + out = BZ_Malloc ( count*sizeof(*out) ); + map_leaffaces = out; + numleaffaces = count; + + for ( i=0 ; i= numfaces) + Host_Error ("CMod_LoadLeafFaces: bad surface number"); + + out[i] = j; + } +} + +void CModQ3_LoadNodes (lump_t *l) +{ + int i, j, count, p; + q3dnode_t *in; + mnode_t *out; + //dnode_t + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = map_nodes;//Hunk_AllocName ( count*sizeof(*out), loadname); + + if (count > MAX_MAP_NODES) + Host_Error("Too many nodes on map"); + + numnodes = count; + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for ( i=0 ; iminmaxs[j] = LittleLong (in->mins[j]); + out->minmaxs[3+j] = LittleLong (in->maxs[j]); + } + + p = LittleLong(in->plane); + out->plane = loadmodel->planes + p; + + out->firstsurface = 0;//LittleShort (in->firstface); + out->numsurfaces = 0;//LittleShort (in->numfaces); + + out->contents = -1; + + for (j=0 ; j<2 ; j++) + { + p = LittleLong (in->children[j]); + out->childnum[j] = p; + if (p >= 0) + { + out->children[j] = loadmodel->nodes + p; + } + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + CMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +void CModQ3_LoadBrushes (lump_t *l) +{ + q3dbrush_t *in; + q2cbrush_t *out; + int i, count; + int shaderref; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_Q2MAP_BRUSHES) + Host_Error ("Map has too many brushes"); + + out = map_brushes; + + numbrushes = count; + + for (i=0 ; ishadernum ); + out->contents = map_surfaces[shaderref].c.value; + out->firstbrushside = LittleLong ( in->firstside ); + out->numsides = LittleLong ( in->num_sides ); + } +} + +void CModQ3_LoadLeafs (lump_t *l) +{ + int i, j; + mleaf_t *out; + q3dleaf_t *in; + int count; + q2cbrush_t *brush; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no leafs"); + // need to save space for box planes + if (count > MAX_Q2MAP_PLANES) + Host_Error ("Map has too many planes"); + + if (count > MAX_MAP_LEAFS) + Host_Error("Too many nodes on map"); + + out = map_leafs; + numleafs = count; + numclusters = 0; + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + emptyleaf = -1; + + for ( i=0 ; iminmaxs[0+j] = LittleFloat(in->mins[j]); + out->minmaxs[3+j] = LittleFloat(in->maxs[j]); + } + out->cluster = LittleLong ( in->cluster ); + out->area = LittleLong ( in->area ) + 1; + out->firstleafface = LittleLong ( in->firstleafsurface ); + out->numleaffaces = LittleLong ( in->num_leafsurfaces ); + out->contents = 0; + out->firstleafbrush = LittleLong ( in->firstleafbrush ); + out->numleafbrushes = LittleLong ( in->num_leafbrushes ); + + out->firstmarksurface = loadmodel->marksurfaces + + LittleLong(in->firstleafsurface); + out->nummarksurfaces = LittleLong(in->num_leafsurfaces); + + if (out->minmaxs[0] > out->minmaxs[3+0] || out->minmaxs[1] > out->minmaxs[3+1] || + out->minmaxs[2] > out->minmaxs[3+2] || VectorCompare (out->minmaxs, out->minmaxs+3)) + { + out->nummarksurfaces = 0; + } + + for ( j=0 ; jnumleafbrushes ; j++) + { + brush = &map_brushes[map_leafbrushes[out->firstleafbrush + j]]; + out->contents |= brush->contents; + } + + if ( out->area >= numareas ) { + numareas = out->area + 1; + } + + if ( !out->contents ) { + emptyleaf = i; + } + } + + // if map doesn't have an empty leaf - force one + if ( emptyleaf == -1 ) { + if (numleafs >= MAX_MAP_LEAFS-1) + Host_Error ("Map does not have an empty leaf"); + + out->cluster = -1; + out->area = -1; + out->numleafbrushes = 0; + out->contents = 0; + out->firstleafbrush = 0; + + Con_DPrintf ( "Forcing an empty leaf: %i\n", numleafs ); + emptyleaf = numleafs++; + } +} + +void CModQ3_LoadPlanes (lump_t *l) +{ + int i, j; + mplane_t *out; + Q3PLANE_t *in; + int count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = map_planes;//Hunk_AllocName ( count*2*sizeof(*out), loadname); + + numplanes = count; + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->n[j]); + } + out->dist = LittleFloat (in->d); + + CategorizePlane(out); + } +} + +void CModQ3_LoadLeafBrushes (lump_t *l) +{ + int i; + int *out; + int *in; + int count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Host_Error ("Map with no planes"); + // need to save space for box planes + if (count > MAX_Q2MAP_LEAFBRUSHES) + Host_Error ("Map has too many leafbrushes"); + + out = map_leafbrushes; + numleafbrushes = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size"); + count = l->filelen / sizeof(*in); + + // need to save space for box planes + if (count > MAX_Q2MAP_BRUSHSIDES) + Host_Error ("Map has too many planes"); + + out = map_brushsides; + numbrushsides = count; + + for ( i=0 ; iplanenum); + out->plane = &map_planes[num]; + j = LittleLong (in->texinfo); + if (j >= numtexinfo) + Host_Error ("Bad brushside texinfo"); + out->surface = &map_surfaces[j]; + } +} + +void CModQ3_LoadVisibility (lump_t *l) +{ + numvisibility = l->filelen; + if (l->filelen > MAX_Q2MAP_VISIBILITY+sizeof(int)*2) + Host_Error ("Map has too large visibility lump"); + + loadmodel->vis = (q2dvis_t *)map_q3pvs; + + memcpy (map_visibility, cmod_base + l->fileofs, l->filelen); + + numclusters = map_q3pvs->numclusters = LittleLong (map_q3pvs->numclusters); + map_q3pvs->rowsize = LittleLong (map_q3pvs->rowsize); +} + +#ifndef SERVERONLY +void CModQ3_LoadLightgrid (lump_t *l) +{ + dq3gridlight_t *in; + dq3gridlight_t *out; + q3lightgridinfo_t *grid; + int count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + grid = Hunk_AllocName (sizeof(q3lightgridinfo_t) + (count-1)*sizeof(*out), loadmodel->name ); + out = grid->lightgrid; + + loadmodel->lightgrid = grid; + grid->numlightgridelems = count; + + // lightgrid is all 8 bit + memcpy ( out, in, count*sizeof(*out) ); +} +#endif + + +#ifndef SERVERONLY +qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out); +int CM_GetQ2Palette (void) +{ + char *f = (void *)COM_LoadMallocFile("pics/colormap.pcx"); + if (!f) + { + Con_Printf ("Couldn't find pics/colormap.pcx"); + return -1; + } + if (!ReadPCXPalette(f, com_filesize, d_q28to24table)) + { + Con_Printf ("Couldn't read pics/colormap.pcx"); + BZ_Free(f); + return -1; + } + BZ_Free(f); + + +#ifdef RGLQUAKE + { + extern float vid_gamma; + float f, inf; + qbyte palette[768]; + qbyte *pal; + int i; + + pal = d_q28to24table; + + for (i=0 ; i<768 ; i++) + { + f = pow ( (pal[i]+1)/256.0 , vid_gamma ); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + memcpy (d_q28to24table, palette, sizeof(palette)); + } +#endif + return 0; +} +#endif + +void CM_OpenAllPortals(char *ents) //this is a compleate hack. About as compleate as possible. +{ //q2 levels contain a thingie called area portals. Basically, doors can seperate two areas and + //the engine knows when this portal is open, and weather to send ents from both sides of the door + //or not. It's not just ents, but also walls. We want to just open them by default and hope the + //progs knows how to close them. + + char style[8]; + char name[64]; + + if (!map_autoopenportals.value) + return; + + while(*ents) + { + if (*ents == '{') //an entity + { + ents++; + *style = '\0'; + *name = '\0'; + while (*ents) + { + ents = COM_Parse(ents); + + if (!strcmp(com_token, "classname")) + { + ents = COM_ParseOut(ents, name, sizeof(name)); + } + else if (!strcmp(com_token, "style")) + { + ents = COM_ParseOut(ents, style, sizeof(style)); + } + else if (*com_token == '}') + break; + else + ents = COM_Parse(ents); //other field + ents++; + } + + if (!strcmp(name, "func_areaportal")) + { + CMQ2_SetAreaPortalState(atoi(style), true); + } + } + + ents++; + } +} + + + +void CMQ3_CalcPHS (void) +{ + int rowbytes, rowwords; + int i, j, k, l, index; + int bitbyte; + unsigned *dest, *src; + qbyte *scan; + int count, vcount; + int numclusters; + + Con_DPrintf ("Building PHS...\n"); + + rowwords = map_q3pvs->rowsize / sizeof(long); + rowbytes = map_q3pvs->rowsize; + + memset ( map_q3phs, 0, MAX_Q2MAP_VISIBILITY ); + + map_q3phs->rowsize = map_q3pvs->rowsize; + map_q3phs->numclusters = numclusters = map_q3pvs->numclusters; + + vcount = 0; + for (i=0 ; i>3] & (1<<(j&7)) ) + { + vcount++; + } + } + } + + count = 0; + scan = (qbyte *)map_q3pvs->data; + dest = (unsigned *)((qbyte *)map_q3phs + 8); + + for (i=0 ; i= numclusters) + Host_Error ("CM_CalcPHS: Bad bit in PVS"); // pad bits should be 0 + src = (unsigned *)((qbyte*)map_q3pvs->data) + index*rowwords; + for (l=0 ; l>3] & (1<<(j&7)) ) + count++; + } + + Con_Printf ("Average clusters visible / hearable / total: %i / %i / %i\n" + , vcount/numclusters, count/numclusters, numclusters); +} + +qbyte *CM_LeafnumPVS (int leafnum, model_t *model, qbyte *buffer) +{ + return CM_ClusterPVS(CM_LeafCluster(leafnum), buffer); +} + +int CM_ModelPointLeafnum (vec3_t p, model_t *mdl) +{ + return CM_PointLeafnum(p); +} + +#ifndef SERVERONLY +#define GLQ2BSP_LightPointValues GLQ1BSP_LightPointValues +#define SWQ2BSP_LightPointValues SWQ1BSP_LightPointValues + +extern int r_dlightframecount; +void Q2BSP_MarkLights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents != -1) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + Q2BSP_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + Q2BSP_MarkLights (light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + Q2BSP_MarkLights (light, bit, node->children[0]); + Q2BSP_MarkLights (light, bit, node->children[1]); +} + +#ifndef SERVERONLY +#ifdef RGLQUAKE +void GLR_StainSurf (msurface_t *surf, float *parms); +void GLR_Q2BSP_StainNode (mnode_t *node, float *parms) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents != -1) + return; + + splitplane = node->plane; + dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist; + + if (dist > (*parms)) + { + GLR_Q2BSP_StainNode (node->children[0], parms); + return; + } + if (dist < (-*parms)) + { + GLR_Q2BSP_StainNode (node->children[1], parms); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK)) + continue; + GLR_StainSurf(surf, parms); + } + + GLR_Q2BSP_StainNode (node->children[0], parms); + GLR_Q2BSP_StainNode (node->children[1], parms); +} +#endif +#ifdef SWQUAKE +void SWR_StainSurf (msurface_t *surf, float *parms); +void SWR_Q2BSP_StainNode (mnode_t *node, float *parms) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents != -1) + return; + + splitplane = node->plane; + dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist; + + if (dist > (*parms)) + { + SWR_Q2BSP_StainNode (node->children[0], parms); + return; + } + if (dist < (-*parms)) + { + SWR_Q2BSP_StainNode (node->children[1], parms); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK)) + continue; + SWR_StainSurf(surf, parms); + } + + SWR_Q2BSP_StainNode (node->children[0], parms); + SWR_Q2BSP_StainNode (node->children[1], parms); +} +#endif +#endif + +#endif + +void Q2BSP_FatPVS (vec3_t org, qboolean add); +qboolean Q2BSP_EdictInFatPVS(edict_t *ent); +void Q2BSP_FindTouchedLeafs(edict_t *ent); +void GLQ2BSP_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir); +void SWQ2BSP_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir); + +/* +================== +CM_LoadMap + +Loads in the map and all submodels +================== +*/ +q2cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *checksum) +{ + unsigned *buf; + int i,j; + q2dheader_t header; + int length; + static unsigned last_checksum; + + // free old stuff + numplanes = 0; + numnodes = 0; + numleafs = 0; + numcmodels = 0; + numvisibility = 0; + numentitychars = 0; + map_entitystring[0] = 0; + map_name[0] = 0; + + loadmodel->type = mod_brush; + + if (!name || !name[0]) + { + numleafs = 1; + numclusters = 1; + numareas = 1; + *checksum = 0; + return &map_cmodels[0]; // cinematic servers won't have anything at all + } + + // + // load the file + // + buf = (unsigned *)filein; + length = com_filesize; + if (!buf) + Host_Error ("Couldn't load %s", name); + + last_checksum = LittleLong (Com_BlockChecksum (buf, length)); + *checksum = last_checksum; + + header = *(q2dheader_t *)(buf); + header.ident = LittleLong(header.ident); + header.version = LittleLong(header.version); + + if (header.version != Q2BSPVERSION && header.version != Q3BSPVERSION) + Host_Error ("CMod_LoadBrushModel: %s has wrong version number (%i should be %i or %i)" + , name, header.version, Q2BSPVERSION, Q3BSPVERSION); + + cmod_base = mod_base = (qbyte *)buf; + + switch(header.version) + { + default: + Sys_Error("Bad internal renderer on q2 map load\n"); + break; +#if 1 + case Q3BSPVERSION: + mapisq3 = true; + loadmodel->fromgame = fg_quake3; + for (i=0 ; ifuncs.FatPVS = Q2BSP_FatPVS; + loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs; + loadmodel->funcs.LeafPVS = CM_LeafnumPVS; + loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum; + +#if defined(RGLQUAKE) + loadmodel->funcs.LightPointValues = GLQ3_LightGrid; + loadmodel->funcs.StainNode = GLR_Q2BSP_StainNode; + loadmodel->funcs.MarkLights = Q2BSP_MarkLights; +#endif + +#ifndef SERVERONLY + //light grid info + if (loadmodel->lightgrid) + { + float maxs; + q3lightgridinfo_t *lg = loadmodel->lightgrid; + if ( lg->gridSize[0] < 1 || lg->gridSize[1] < 1 || lg->gridSize[2] < 1 ) + { + lg->gridSize[0] = 64; + lg->gridSize[1] = 64; + lg->gridSize[2] = 128; + } + + for ( i = 0; i < 3; i++ ) + { + lg->gridMins[i] = lg->gridSize[i] * ceil( (map_cmodels->mins[i] + 1) / lg->gridSize[i] ); + maxs = lg->gridSize[i] * floor( (map_cmodels->maxs[i] - 1) / lg->gridSize[i] ); + lg->gridBounds[i] = (maxs - lg->gridMins[i])/lg->gridSize[i] + 1; + } + + lg->gridBounds[3] = lg->gridBounds[1] * lg->gridBounds[0]; + } +#endif + + CM_CreatePatchesForLeafs (); //for clipping + + CMQ3_CalcPHS(); + +// BZ_Free(map_verts); + BZ_Free(map_faces); + BZ_Free(map_leaffaces); + + break; + default: + Sys_Error("Quake 3 maps are supported with dedicated servers and opengl only\n"); + } + break; +#endif + case Q2BSPVERSION: + mapisq3 = false; + for (i=0 ; ifuncs.FatPVS = Q2BSP_FatPVS; + loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs; + loadmodel->funcs.LightPointValues = NULL; + loadmodel->funcs.StainNode = NULL; + loadmodel->funcs.MarkLights = NULL; + loadmodel->funcs.LeafPVS = CM_LeafnumPVS; + loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum; + + break; +#if defined(RGLQUAKE) + case QR_OPENGL: + // load into heap + #ifndef SERVERONLY + GLMod_LoadVertexes (&header.lumps[Q2LUMP_VERTEXES]); + GLMod_LoadEdges (&header.lumps[Q2LUMP_EDGES]); + GLMod_LoadSurfedges (&header.lumps[Q2LUMP_SURFEDGES]); + GLMod_LoadLighting (&header.lumps[Q2LUMP_LIGHTING]); + #endif + CMod_LoadSurfaces (&header.lumps[Q2LUMP_TEXINFO]); + CMod_LoadLeafBrushes (&header.lumps[Q2LUMP_LEAFBRUSHES]); + CMod_LoadPlanes (&header.lumps[Q2LUMP_PLANES]); + #ifndef SERVERONLY + CMod_LoadTexInfo (&header.lumps[Q2LUMP_TEXINFO]); + CMod_LoadFaces (&header.lumps[Q2LUMP_FACES]); + GLMod_LoadMarksurfaces (&header.lumps[Q2LUMP_LEAFFACES]); + #endif + CMod_LoadVisibility (&header.lumps[Q2LUMP_VISIBILITY]); + CMod_LoadBrushes (&header.lumps[Q2LUMP_BRUSHES]); + CMod_LoadBrushSides (&header.lumps[Q2LUMP_BRUSHSIDES]); + CMod_LoadSubmodels (&header.lumps[Q2LUMP_MODELS]); + CMod_LoadLeafs (&header.lumps[Q2LUMP_LEAFS]); + CMod_LoadNodes (&header.lumps[Q2LUMP_NODES]); + CMod_LoadAreas (&header.lumps[Q2LUMP_AREAS]); + CMod_LoadAreaPortals (&header.lumps[Q2LUMP_AREAPORTALS]); + CMod_LoadEntityString (&header.lumps[Q2LUMP_ENTITIES]); + + loadmodel->funcs.FatPVS = Q2BSP_FatPVS; + loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs; + loadmodel->funcs.LightPointValues = GLQ2BSP_LightPointValues; + loadmodel->funcs.StainNode = GLR_Q2BSP_StainNode; + loadmodel->funcs.MarkLights = Q2BSP_MarkLights; + loadmodel->funcs.LeafPVS = CM_LeafnumPVS; + loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum; + break; +#endif +#if defined(SWQUAKE) + case QR_SOFTWARE: + // load into heap + #ifndef SERVERONLY + SWMod_LoadVertexes (&header.lumps[Q2LUMP_VERTEXES]); + SWMod_LoadEdges (&header.lumps[Q2LUMP_EDGES]); + SWMod_LoadSurfedges (&header.lumps[Q2LUMP_SURFEDGES]); + SWMod_LoadLighting (&header.lumps[Q2LUMP_LIGHTING]); + #endif + CMod_LoadSurfaces (&header.lumps[Q2LUMP_TEXINFO]); + CMod_LoadLeafBrushes (&header.lumps[Q2LUMP_LEAFBRUSHES]); + CMod_LoadPlanes (&header.lumps[Q2LUMP_PLANES]); + #ifndef SERVERONLY + CMod_LoadTexInfo (&header.lumps[Q2LUMP_TEXINFO]); + CMod_LoadFaces (&header.lumps[Q2LUMP_FACES]); + SWMod_LoadMarksurfaces (&header.lumps[Q2LUMP_LEAFFACES]); + #endif + CMod_LoadVisibility (&header.lumps[Q2LUMP_VISIBILITY]); + CMod_LoadBrushes (&header.lumps[Q2LUMP_BRUSHES]); + CMod_LoadBrushSides (&header.lumps[Q2LUMP_BRUSHSIDES]); + CMod_LoadSubmodels (&header.lumps[Q2LUMP_MODELS]); + CMod_LoadLeafs (&header.lumps[Q2LUMP_LEAFS]); + CMod_LoadNodes (&header.lumps[Q2LUMP_NODES]); + CMod_LoadAreas (&header.lumps[Q2LUMP_AREAS]); + CMod_LoadAreaPortals (&header.lumps[Q2LUMP_AREAPORTALS]); + CMod_LoadEntityString (&header.lumps[Q2LUMP_ENTITIES]); + + + loadmodel->funcs.FatPVS = Q2BSP_FatPVS; + loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs; + loadmodel->funcs.LightPointValues = SWQ2BSP_LightPointValues; + loadmodel->funcs.StainNode = SWR_Q2BSP_StainNode; + loadmodel->funcs.MarkLights = Q2BSP_MarkLights; + loadmodel->funcs.LeafPVS = CM_LeafnumPVS; + loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum; + break; +#endif + default: + Sys_Error("Bad internal renderer on q2 map load\n"); + } + } + +#ifndef SERVERONLY + Mod_ParseInfoFromEntityLump(loadmodel->entities); //only done for client's world model (or server if the server is loading it for client) +#endif + + CM_InitBoxHull (); + + if (map_autoopenportals.value) + memset (portalopen, 1, sizeof(portalopen)); //open them all. Used for progs that havn't got a clue. + else + memset (portalopen, 0, sizeof(portalopen)); //make them start closed. + FloodAreaConnections (); + + strcpy (map_name, name); + + + + + + + + loadmodel->numsubmodels = CM_NumInlineModels(); + { + model_t *mod = loadmodel; + + mod->hulls[0].firstclipnode = map_cmodels[0].headnode; + mod->hulls[0].available = true; + Q2BSP_SetHullFuncs(&mod->hulls[0]); + + for (j=1 ; jhulls[j].firstclipnode = map_cmodels[0].headnode; + mod->hulls[j].available = false; + Q2BSP_SetHullFuncs(&mod->hulls[j]); + } + + for (i=1 ; i< loadmodel->numsubmodels ; i++) + { + q2cmodel_t *bm; + + char name[10]; + + sprintf (name, "*%i", i); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + + bm = CM_InlineModel (name); + + + mod->hulls[0].firstclipnode = bm->headnode; + mod->hulls[j].available = true; + mod->nummodelsurfaces = bm->numsurfaces; + mod->firstmodelsurface = bm->firstsurface; + Q2BSP_SetHullFuncs(&mod->hulls[0]); + for (j=1 ; jhulls[j].firstclipnode = bm->headnode; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + mod->hulls[j].available = false; + Q2BSP_SetHullFuncs(&mod->hulls[j]); + } + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); +#ifndef SERVERONLY + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + R_DefaultTrail(mod); +#endif + } + } + + + return &map_cmodels[0]; +} + +/* +================== +CM_InlineModel +================== +*/ +q2cmodel_t *CM_InlineModel (char *name) +{ + int num; + + if (!name) + Host_Error("Bad model\n"); + else if (name[0] != '*') + Host_Error("Bad model\n"); + + num = atoi (name+1); + + if (num < 1 || num >= numcmodels) + Host_Error ("CM_InlineModel: bad number"); + + return &map_cmodels[num]; +} + +int CM_NumClusters (void) +{ + return numclusters; +} + +int CM_ClusterSize (void) +{ + return map_q3pvs->rowsize ? map_q3pvs->rowsize : MAX_Q2MAP_LEAFS / 8; +} + +int CM_NumInlineModels (void) +{ + return numcmodels; +} + +char *CM_EntityString (void) +{ + return map_entitystring; +} + +int CM_LeafContents (int leafnum) +{ + if (leafnum < 0 || leafnum >= numleafs) + Host_Error ("CM_LeafContents: bad number"); + return map_leafs[leafnum].contents; +} + +int CM_LeafCluster (int leafnum) +{ + if (leafnum < 0 || leafnum >= numleafs) + Host_Error ("CM_LeafCluster: bad number"); + return map_leafs[leafnum].cluster; +} + +int CM_LeafArea (int leafnum) +{ + if (leafnum < 0 || leafnum >= numleafs) + Host_Error ("CM_LeafArea: bad number"); + return map_leafs[leafnum].area; +} + +//======================================================================= + + +mplane_t *box_planes; +int box_headnode; +q2cbrush_t *box_brush; +mleaf_t *box_leaf; + +/* +=================== +CM_InitBoxHull + +Set up the planes and nodes so that the six floats of a bounding box +can just be stored out and get a proper clipping hull structure. +=================== +*/ +void CM_InitBoxHull (void) +{ + int i; + int side; + mnode_t *c; + mplane_t *p; + q2cbrushside_t *s; + + box_headnode = numnodes; + box_planes = &map_planes[numplanes]; + if (numnodes+6 > MAX_Q2MAP_NODES + || numbrushes+1 > MAX_Q2MAP_BRUSHES + || numleafbrushes+1 > MAX_Q2MAP_LEAFBRUSHES + || numbrushsides+6 > MAX_Q2MAP_BRUSHSIDES + || numplanes+12 > MAX_Q2MAP_PLANES) + Host_Error ("Not enough room for box tree"); + + box_brush = &map_brushes[numbrushes]; + box_brush->numsides = 6; + box_brush->firstbrushside = numbrushsides; + box_brush->contents = Q2CONTENTS_MONSTER; + + box_leaf = &map_leafs[numleafs]; + box_leaf->contents = Q2CONTENTS_MONSTER; + box_leaf->firstleafbrush = numleafbrushes; + box_leaf->numleafbrushes = 1; + + map_leafbrushes[numleafbrushes] = numbrushes; + + for (i=0 ; i<6 ; i++) + { + side = i&1; + + // brush sides + s = &map_brushsides[numbrushsides+i]; + s->plane = map_planes + (numplanes+i*2+side); + s->surface = &nullsurface; + + // nodes + c = &map_nodes[box_headnode+i]; + c->plane = map_planes + (numplanes+i*2); + c->childnum[side] = -1 - emptyleaf; + if (i != 5) + c->childnum[side^1] = box_headnode+i + 1; + else + c->childnum[side^1] = -1 - numleafs; + + // planes + p = &box_planes[i*2]; + p->type = i>>1; + p->signbits = 0; + VectorClear (p->normal); + p->normal[i>>1] = 1; + + p = &box_planes[i*2+1]; + p->type = 3 + (i>>1); + p->signbits = 0; + VectorClear (p->normal); + p->normal[i>>1] = -1; + } +} + + +/* +=================== +CM_HeadnodeForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +int CM_HeadnodeForBox (vec3_t mins, vec3_t maxs) +{ + box_planes[0].dist = maxs[0]; + box_planes[1].dist = -maxs[0]; + box_planes[2].dist = mins[0]; + box_planes[3].dist = -mins[0]; + box_planes[4].dist = maxs[1]; + box_planes[5].dist = -maxs[1]; + box_planes[6].dist = mins[1]; + box_planes[7].dist = -mins[1]; + box_planes[8].dist = maxs[2]; + box_planes[9].dist = -maxs[2]; + box_planes[10].dist = mins[2]; + box_planes[11].dist = -mins[2]; + + return box_headnode; +} + + +/* +================== +CM_PointLeafnum_r + +================== +*/ +int CM_PointLeafnum_r (vec3_t p, int num) +{ + float d; + mnode_t *node; + mplane_t *plane; + + while (num >= 0) + { + node = map_nodes + num; + plane = node->plane; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->childnum[1]; + else + num = node->childnum[0]; + } + + c_pointcontents++; // optimize counter + + return -1 - num; +} + +int CM_PointLeafnum (vec3_t p) +{ + if (!numplanes) + return 0; // sound may call this without map loaded + return CM_PointLeafnum_r (p, 0); +} + +/* +============= +CM_BoxLeafnums + +Fills in a list of all the leafs touched +============= +*/ +int leaf_count, leaf_maxcount; +int *leaf_list; +float *leaf_mins, *leaf_maxs; +int leaf_topnode; + +void CM_BoxLeafnums_r (int nodenum) +{ + mplane_t *plane; + mnode_t *node; + int s; + + while (1) + { + if (nodenum < 0) + { + if (leaf_count >= leaf_maxcount) + { +// Com_Printf ("CM_BoxLeafnums_r: overflow\n"); + return; + } + leaf_list[leaf_count++] = -1 - nodenum; + return; + } + + node = &map_nodes[nodenum]; + plane = node->plane; +// s = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane); + s = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane); + if (s == 1) + nodenum = node->childnum[0]; + else if (s == 2) + nodenum = node->childnum[1]; + else + { // go down both + if (leaf_topnode == -1) + leaf_topnode = nodenum; + CM_BoxLeafnums_r (node->childnum[0]); + nodenum = node->childnum[1]; + } + + } +} + +int CM_BoxLeafnums_headnode (vec3_t mins, vec3_t maxs, int *list, int listsize, int headnode, int *topnode) +{ + leaf_list = list; + leaf_count = 0; + leaf_maxcount = listsize; + leaf_mins = mins; + leaf_maxs = maxs; + + leaf_topnode = -1; + + CM_BoxLeafnums_r (headnode); + + if (topnode) + *topnode = leaf_topnode; + + return leaf_count; +} + +int CM_BoxLeafnums (vec3_t mins, vec3_t maxs, int *list, int listsize, int *topnode) +{ + return CM_BoxLeafnums_headnode (mins, maxs, list, + listsize, map_cmodels[0].headnode, topnode); +} + + + +/* +================== +CM_PointContents + +================== +*/ +#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist) +int CM_PointContents (vec3_t p, int headnode) +{ + int i, j, contents; + mleaf_t *leaf; + q2cbrush_t *brush; + q2cbrushside_t *brushside; + + if (!numnodes) // map not loaded + return 0; + + i = CM_PointLeafnum_r (p, headnode); + + if (!mapisq3) + return map_leafs[i].contents; //q2 is simple. + + leaf = &map_leafs[i]; + +// if ( leaf->contents & CONTENTS_NODROP ) { +// contents = CONTENTS_NODROP; +// } else { + contents = 0; +// } + + for (i = 0; i < leaf->numleafbrushes; i++) + { + brush = &map_brushes[map_leafbrushes[leaf->firstleafbrush + i]]; + + // check if brush actually adds something to contents + if ( (contents & brush->contents) == brush->contents ) { + continue; + } + + brushside = &map_brushsides[brush->firstbrushside]; + for ( j = 0; j < brush->numsides; j++, brushside++ ) + { + if ( PlaneDiff (p, brushside->plane) > 0 ) + break; + } + + if (j == brush->numsides) + contents |= brush->contents; + } + + return contents; +} + +/* +================== +CM_TransformedPointContents + +Handles offseting and rotation of the end points for moving and +rotating entities +================== +*/ +int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t angles) +{ + vec3_t p_l; + vec3_t temp; + vec3_t forward, right, up; + + // subtract origin offset + VectorSubtract (p, origin, p_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && + (angles[0] || angles[1] || angles[2]) ) + { + AngleVectors (angles, forward, right, up); + + VectorCopy (p_l, temp); + p_l[0] = DotProduct (temp, forward); + p_l[1] = -DotProduct (temp, right); + p_l[2] = DotProduct (temp, up); + } + + return CM_PointContents(p, headnode); +} + + +/* +=============================================================================== + +BOX TRACING + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +vec3_t trace_start, trace_end; +vec3_t trace_mins, trace_maxs; +vec3_t trace_extents; +vec3_t trace_absmins, trace_absmaxs; + +trace_t trace_trace; +int trace_contents; +qboolean trace_ispoint; // optimized case + +/* +================ +CM_ClipBoxToBrush +================ +*/ +void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, + trace_t *trace, q2cbrush_t *brush) +{ + int i, j; + mplane_t *plane, *clipplane; + float dist; + float enterfrac, leavefrac; + vec3_t ofs; + float d1, d2; + qboolean getout, startout; + float f; + q2cbrushside_t *side, *leadside; + + enterfrac = -1; + leavefrac = 1; + clipplane = NULL; + + if (!brush->numsides) + return; + + c_brush_traces++; + + getout = false; + startout = false; + leadside = NULL; + + for (i=0 ; inumsides ; i++) + { + side = &map_brushsides[brush->firstbrushside+i]; + plane = side->plane; + + // FIXME: special case for axial + + if (!trace_ispoint) + { // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct (ofs, plane->normal); + dist = plane->dist - dist; + } + else + { // special point case + dist = plane->dist; + } + + d1 = DotProduct (p1, plane->normal) - dist; + d2 = DotProduct (p2, plane->normal) - dist; + + if (d2 > 0) + getout = true; // endpoint is not in solid + if (d1 > 0) + startout = true; + + // if completely in front of face, no intersection + if (d1 > 0 && d2 >= d1) + return; + + if (d1 <= 0 && d2 <= 0) + continue; + + // crosses face + if (d1 > d2) + { // enter + f = (d1-DIST_EPSILON) / (d1-d2); + if (f > enterfrac) + { + enterfrac = f; + clipplane = plane; + leadside = side; + } + } + else + { // leave + f = (d1+DIST_EPSILON) / (d1-d2); + if (f < leavefrac) + leavefrac = f; + } + } + + if (!startout) + { // original point was inside brush + trace->startsolid = true; + if (!getout) + trace->allsolid = true; + return; + } + if (enterfrac < leavefrac) + { + if (enterfrac > -1 && enterfrac < trace->fraction) + { + if (enterfrac < 0) + enterfrac = 0; + trace->fraction = enterfrac; + trace->plane.dist = clipplane->dist; + VectorCopy(clipplane->normal, trace->plane.normal); + trace->surface = &(leadside->surface->c); + trace->contents = brush->contents; + } + } +} + +void CM_ClipBoxToPatch (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, + trace_t *trace, q2cbrush_t *brush) +{ + int i, j; + mplane_t *plane, *clipplane; + float enterfrac, leavefrac; + vec3_t ofs; + float d1, d2; + float dist; + qboolean startout; + float f; + q2cbrushside_t *side, *leadside; + + if (!brush->numsides) + return; + + c_brush_traces++; + + enterfrac = -1; + leavefrac = 1; + clipplane = NULL; + startout = false; + leadside = NULL; + + for (i=0 ; inumsides ; i++) + { + side = &map_brushsides[brush->firstbrushside+i]; + plane = side->plane; + + if (!trace_ispoint) + { // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct (ofs, plane->normal); + dist = plane->dist - dist; + } + else + { // special point case + dist = plane->dist; + } + + d1 = DotProduct (p1, plane->normal) - dist; + d2 = DotProduct (p2, plane->normal) - dist; + + if (d1 > 0) + startout = true; + + // if completely in front of face, no intersection + if (d1 > 0 && d2 >= d1) + return; + + if (d1 <= 0 && d2 <= 0) + continue; + + // crosses face + if (d1 > d2) + { // enter + f = (d1-DIST_EPSILON) / (d1-d2); + if (f > enterfrac) + { + enterfrac = f; + clipplane = plane; + leadside = side; + } + } + else + { // leave + f = (d1 /*+ DIST_EPSILON*/) / (d1-d2); + if (f < leavefrac) + leavefrac = f; + } + } + + if (!startout) + return; // original point is inside the patch + + if (enterfrac < leavefrac) + { + if (leadside && leadside->surface + && enterfrac < trace->fraction) + { + if (enterfrac < 0) + enterfrac = 0; + trace->fraction = enterfrac; + trace->plane.dist = clipplane->dist; + VectorCopy(clipplane->normal, trace->plane.normal); + trace->surface = &leadside->surface->c; + trace->contents = brush->contents; + } + } +} + + +/* +================ +CM_TestBoxInBrush +================ +*/ +void CM_TestBoxInBrush (vec3_t mins, vec3_t maxs, vec3_t p1, + trace_t *trace, q2cbrush_t *brush) +{ + int i, j; + mplane_t *plane; + float dist; + vec3_t ofs; + float d1; + q2cbrushside_t *side; + + if (!brush->numsides) + return; + + for (i=0 ; inumsides ; i++) + { + side = &map_brushsides[brush->firstbrushside+i]; + plane = side->plane; + + // FIXME: special case for axial + + // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct (ofs, plane->normal); + dist = plane->dist - dist; + + d1 = DotProduct (p1, plane->normal) - dist; + + // if completely in front of face, no intersection + if (d1 > 0) + return; + + } + + // inside this brush + trace->startsolid = trace->allsolid = true; + trace->fraction = 0; + trace->contents = brush->contents; +} + +void CM_TestBoxInPatch (vec3_t mins, vec3_t maxs, vec3_t p1, + trace_t *trace, q2cbrush_t *brush) +{ + int i, j; + mplane_t *plane; + vec3_t ofs; + float dist; + float d1, maxdist; + q2cbrushside_t *side; + + if (!brush->numsides) + return; + + maxdist = -9999; + + for (i=0 ; inumsides ; i++) + { + side = &map_brushsides[brush->firstbrushside+i]; + plane = side->plane; + + // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + + dist = DotProduct (ofs, plane->normal); + dist = plane->dist - dist; + + d1 = DotProduct (p1, plane->normal) - dist; + + // if completely in front of face, no intersection + if (d1 > 0) + return; + + if (side->surface && d1 > maxdist) + maxdist = d1; + } + +// FIXME + if (maxdist < -0.25) + return; // deep inside the patch + + // inside this patch + trace->startsolid = trace->allsolid = true; + trace->fraction = 0; + trace->contents = brush->contents; +} + + +/* +================ +CM_TraceToLeaf +================ +*/ +void CM_TraceToLeaf (int leafnum) +{ + int k, j; + int brushnum; + mleaf_t *leaf; + q2cbrush_t *b; + + int patchnum; + q3cpatch_t *patch; + + leaf = &map_leafs[leafnum]; + if ( !(leaf->contents & trace_contents)) + return; + // trace line against all brushes in the leaf + for (k=0 ; knumleafbrushes ; k++) + { + brushnum = map_leafbrushes[leaf->firstleafbrush+k]; + b = &map_brushes[brushnum]; + if (b->checkcount == checkcount) + continue; // already checked this brush in another leaf + b->checkcount = checkcount; + + if ( !(b->contents & trace_contents)) + continue; + CM_ClipBoxToBrush (trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, b); + if (!trace_trace.fraction) + return; + } + + if (!mapisq3 || map_noCurves.value) + return; + + // trace line against all patches in the leaf + for (k = 0; k < leaf->numleafpatches; k++) + { + patchnum = map_leafpatches[leaf->firstleafpatch+k]; + + patch = &map_patches[patchnum]; + if (patch->checkcount == checkcount) + continue; // already checked this patch in another leaf + patch->checkcount = checkcount; + if ( !(patch->surface->c.value & trace_contents) ) + continue; + if ( !BoundsIntersect(patch->absmins, patch->absmaxs, trace_absmins, trace_absmaxs) ) + continue; + for (j = 0; j < patch->numbrushes; j++) + { + CM_ClipBoxToPatch (trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, &patch->brushes[j]); + if (!trace_trace.fraction) + return; + } + } + +} + + +/* +================ +CM_TestInLeaf +================ +*/ +void CM_TestInLeaf (int leafnum) +{ + int k, j; + int brushnum; + int patchnum; + mleaf_t *leaf; + q2cbrush_t *b; + q3cpatch_t *patch; + + leaf = &map_leafs[leafnum]; + if ( !(leaf->contents & trace_contents)) + return; + // trace line against all brushes in the leaf + for (k=0 ; knumleafbrushes ; k++) + { + brushnum = map_leafbrushes[leaf->firstleafbrush+k]; + b = &map_brushes[brushnum]; + if (b->checkcount == checkcount) + continue; // already checked this brush in another leaf + b->checkcount = checkcount; + + if ( !(b->contents & trace_contents)) + continue; + CM_TestBoxInBrush (trace_mins, trace_maxs, trace_start, &trace_trace, b); + if (!trace_trace.fraction) + return; + } + + if (!mapisq3 || map_noCurves.value) + return; + + // trace line against all patches in the leaf + for (k = 0; k < leaf->numleafpatches; k++) + { + patchnum = map_leafpatches[leaf->firstleafpatch+k]; + + patch = &map_patches[patchnum]; + if (patch->checkcount == checkcount) + continue; // already checked this patch in another leaf + patch->checkcount = checkcount; + if ( !(patch->surface->c.value & trace_contents) ) + continue; + if ( !BoundsIntersect(patch->absmins, patch->absmaxs, trace_absmins, trace_absmaxs) ) + continue; + for (j = 0; j < patch->numbrushes; j++) + { + CM_TestBoxInPatch (trace_mins, trace_maxs, trace_start, &trace_trace, &patch->brushes[j]); + if (!trace_trace.fraction) + return; + } + } + +} + + +/* +================== +CM_RecursiveHullCheck + +================== +*/ +void CM_RecursiveHullCheck (int num, float p1f, float p2f, vec3_t p1, vec3_t p2) +{ + mnode_t *node; + mplane_t *plane; + float t1, t2, offset; + float frac, frac2; + float idist; + int i; + vec3_t mid; + int side; + float midf; + + if (trace_trace.fraction <= p1f) + return; // already hit something nearer + + // if < 0, we are in a leaf node + if (num < 0) + { + CM_TraceToLeaf (-1-num); + return; + } + + // + // find the point distances to the seperating plane + // and the offset for the size of the box + // + node = map_nodes + num; + plane = node->plane; + + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + offset = trace_extents[plane->type]; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + if (trace_ispoint) + offset = 0; + else + offset = fabs(trace_extents[0]*plane->normal[0]) + + fabs(trace_extents[1]*plane->normal[1]) + + fabs(trace_extents[2]*plane->normal[2]); + } + + +#if 0 +CM_RecursiveHullCheck (node->childnum[0], p1f, p2f, p1, p2); +CM_RecursiveHullCheck (node->childnum[1], p1f, p2f, p1, p2); +return; +#endif + + // see which sides we need to consider + if (t1 >= offset && t2 >= offset) + { + CM_RecursiveHullCheck (node->childnum[0], p1f, p2f, p1, p2); + return; + } + if (t1 < -offset && t2 < -offset) + { + CM_RecursiveHullCheck (node->childnum[1], p1f, p2f, p1, p2); + return; + } + + // put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < t2) + { + idist = 1.0/(t1-t2); + side = 1; + frac2 = (t1 + offset + DIST_EPSILON)*idist; + frac = (t1 - offset + DIST_EPSILON)*idist; + } + else if (t1 > t2) + { + idist = 1.0/(t1-t2); + side = 0; + frac2 = (t1 - offset - DIST_EPSILON)*idist; + frac = (t1 + offset + DIST_EPSILON)*idist; + } + else + { + side = 0; + frac = 1; + frac2 = 0; + } + + // move up to the node + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + CM_RecursiveHullCheck (node->childnum[side], p1f, midf, p1, mid); + + + // go past the node + if (frac2 < 0) + frac2 = 0; + if (frac2 > 1) + frac2 = 1; + + midf = p1f + (p2f - p1f)*frac2; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac2*(p2[i] - p1[i]); + + CM_RecursiveHullCheck (node->childnum[side^1], midf, p2f, mid, p2); +} + + +//====================================================================== + +/* +================== +CM_BoxTrace +================== +*/ +trace_t CM_BoxTrace (vec3_t start, vec3_t end, + vec3_t mins, vec3_t maxs, + int headnode, int brushmask) +{ + int i; + int moved; + vec3_t point; + + + checkcount++; // for multi-check avoidance + + c_traces++; // for statistics, may be zeroed + + // fill in a default trace + memset (&trace_trace, 0, sizeof(trace_trace)); + trace_trace.fraction = 1; + trace_trace.surface = &(nullsurface.c); + + if (!numnodes) // map not loaded + return trace_trace; + + trace_contents = brushmask; + VectorCopy (start, trace_start); + VectorCopy (end, trace_end); + VectorCopy (mins, trace_mins); + VectorCopy (maxs, trace_maxs); + + // build a bounding box of the entire move (for patches) + ClearBounds (trace_absmins, trace_absmaxs); + VectorAdd (start, trace_mins, point); + AddPointToBounds (point, trace_absmins, trace_absmaxs); + VectorAdd (start, trace_maxs, point); + AddPointToBounds (point, trace_absmins, trace_absmaxs); + VectorAdd (end, trace_mins, point); + AddPointToBounds (point, trace_absmins, trace_absmaxs); + VectorAdd (end, trace_maxs, point); + AddPointToBounds (point, trace_absmins, trace_absmaxs); + + // + // check for position test special case + // + if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) + { + int leafs[1024]; + int i, numleafs; + vec3_t c1, c2; + int topnode; + + VectorAdd (start, mins, c1); + VectorAdd (start, maxs, c2); + for (i=0 ; i<3 ; i++) + { + c1[i] -= 1; + c2[i] += 1; + } + + numleafs = CM_BoxLeafnums_headnode (c1, c2, leafs, 1024, headnode, &topnode); + for (i=0 ; i trace_maxs[0] ? -trace_mins[0] : trace_maxs[0]; + trace_extents[1] = -trace_mins[1] > trace_maxs[1] ? -trace_mins[1] : trace_maxs[1]; + trace_extents[2] = -trace_mins[2] > trace_maxs[2] ? -trace_mins[2] : trace_maxs[2]; + + if (-mins[2] != maxs[2]) //be prepared to move the thing up to counter the different min/max + { + moved = (trace_maxs[2] - trace_mins[2])/2; + trace_mins[2] = -moved; + trace_maxs[2] = moved; + trace_extents[2] = -trace_mins[2] > trace_maxs[2] ? -trace_mins[2] : trace_maxs[2]; + moved = (maxs[2] - trace_maxs[2]); + } + + trace_start[2]+=moved; + trace_end[2]+=moved; + } + + // + // general sweeping through world + // + CM_RecursiveHullCheck (headnode, 0, 1, trace_start, trace_end); + + if (trace_trace.fraction == 1) + { + VectorCopy (trace_end, trace_trace.endpos); + } + else + { + for (i=0 ; i<3 ; i++) + trace_trace.endpos[i] = trace_start[i] + trace_trace.fraction * (trace_end[i] - trace_start[i]); + } + + trace_trace.endpos[2] -= moved; + return trace_trace; +} + + +/* +================== +CM_TransformedBoxTrace + +Handles offseting and rotation of the end points for moving and +rotating entities +================== +*/ +#ifdef _WIN32 +#pragma optimize( "", off ) +#endif + + +trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end, + vec3_t mins, vec3_t maxs, + int headnode, int brushmask, + vec3_t origin, vec3_t angles) +{ + trace_t trace; + vec3_t start_l, end_l; + vec3_t a; + vec3_t forward, right, up; + vec3_t temp; + qboolean rotated; + + // subtract origin offset + VectorSubtract (start, origin, start_l); + VectorSubtract (end, origin, end_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && + (angles[0] || angles[1] || angles[2]) ) + rotated = true; + else + rotated = false; + + if (rotated) + { + AngleVectors (angles, forward, right, up); + + VectorCopy (start_l, temp); + start_l[0] = DotProduct (temp, forward); + start_l[1] = -DotProduct (temp, right); + start_l[2] = DotProduct (temp, up); + + VectorCopy (end_l, temp); + end_l[0] = DotProduct (temp, forward); + end_l[1] = -DotProduct (temp, right); + end_l[2] = DotProduct (temp, up); + } + + // sweep the box through the model + trace = CM_BoxTrace (start_l, end_l, mins, maxs, headnode, brushmask); + + if (rotated && trace.fraction != 1.0) + { + // FIXME: figure out how to do this with existing angles + VectorNegate (angles, a); + AngleVectors (a, forward, right, up); + + VectorCopy (trace.plane.normal, temp); + trace.plane.normal[0] = DotProduct (temp, forward); + trace.plane.normal[1] = -DotProduct (temp, right); + trace.plane.normal[2] = DotProduct (temp, up); + } + + trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]); + trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]); + trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]); + + return trace; +} + +#ifdef _WIN32 +#pragma optimize( "", on ) +#endif + + + +/* +=============================================================================== + +PVS / PHS + +=============================================================================== +*/ + +/* +=================== +CM_DecompressVis +=================== +*/ + +/* +qbyte *Mod_Q2DecompressVis (qbyte *in, model_t *model) +{ + static qbyte decompressed[MAX_MAP_LEAFS/8]; + int c; + qbyte *out; + int row; + + row = (model->vis->numclusters+7)>>3; + out = decompressed; + + if (!in) + { // no vis info, so make all visible + while (row) + { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); + + return decompressed; +} + +#define DVIS_PVS 0 +#define DVIS_PHS 1 +qbyte *Mod_ClusterPVS (int cluster, model_t *model) +{ + if (cluster == -1 || !model->vis) + return mod_novis; + return Mod_Q2DecompressVis ( (qbyte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], + model); +} +*/ +void CM_DecompressVis (qbyte *in, qbyte *out) +{ + int c; + qbyte *out_p; + int row; + + row = (numclusters+7)>>3; + out_p = out; + + if (!in || !numvisibility) + { // no vis info, so make all visible + while (row) + { + *out_p++ = 0xff; + row--; + } + return; + } + + do + { + if (*in) + { + *out_p++ = *in++; + continue; + } + + c = in[1]; + in += 2; + if ((out_p - out) + c > row) + { + c = row - (out_p - out); + Con_DPrintf ("warning: Vis decompression overrun\n"); + } + while (c) + { + *out_p++ = 0; + c--; + } + } while (out_p - out < row); +} + +qbyte pvsrow[MAX_Q2MAP_LEAFS/8]; +qbyte phsrow[MAX_Q2MAP_LEAFS/8]; + + + +qbyte *CM_ClusterPVS (int cluster, qbyte *buffer) +{ + if (!buffer) + buffer = pvsrow; + + if (mapisq3) + { + if (cluster != -1 && map_q3pvs->numclusters) + { + return (qbyte *)map_q3pvs->data + cluster * map_q3pvs->rowsize; + } + else + { + memset (buffer, 0, (numclusters+7)>>3); + return buffer; + } + } + + if (cluster == -1) + memset (buffer, 0, (numclusters+7)>>3); + else + CM_DecompressVis (map_visibility + map_q2vis->bitofs[cluster][DVIS_PVS], buffer); + return buffer; +} + +qbyte *CM_ClusterPHS (int cluster) +{ + if (mapisq3) //phs not working yet. + { + if (cluster != -1 && map_q3phs->numclusters) + { + return (qbyte *)map_q3phs->data + cluster * map_q3phs->rowsize; + } + else + { + memset (phsrow, 0, (numclusters+7)>>3); + return phsrow; + } + } + + if (cluster == -1) + memset (phsrow, 0, (numclusters+7)>>3); + else + CM_DecompressVis (map_visibility + map_q2vis->bitofs[cluster][DVIS_PHS], phsrow); + return phsrow; +} + + +/* +=============================================================================== + +AREAPORTALS + +=============================================================================== +*/ + +void FloodArea_r (q2carea_t *area, int floodnum) +{ + int i; + q2dareaportal_t *p; + + if (area->floodvalid == floodvalid) + { + if (area->floodnum == floodnum) + return; + Host_Error ("FloodArea_r: reflooded"); + } + + area->floodnum = floodnum; + area->floodvalid = floodvalid; + p = &map_areaportals[area->firstareaportal]; + for (i=0 ; inumareaportals ; i++, p++) + { + if (portalopen[p->portalnum]) + FloodArea_r (&map_q2areas[p->otherarea], floodnum); + } +} + +/* +==================== +FloodAreaConnections + + +==================== +*/ +void FloodAreaConnections (void) +{ + int i, j; + q2carea_t *area; + int floodnum; + + if (mapisq3) + { + // area 0 is not used + for (i=1 ; ifloodvalid == floodvalid) + continue; // already flooded into + floodnum++; + FloodArea_r (area, floodnum); + } + +} + +qboolean CMQ2_SetAreaPortalState (int portalnum, qboolean open) +{ + if (mapisq3) + Host_Error ("CMQ2_SetAreaPortalState on q3 map"); + if (portalnum > numareaportals) + Host_Error ("areaportal > numareaportals"); + + if (portalopen[portalnum] == open) + return false; + portalopen[portalnum] = open; + FloodAreaConnections (); + + return true; +} + +void CMQ3_SetAreaPortalState (int area1, int area2, qboolean open) +{ + if (!mapisq3) + Host_Error ("CMQ3_SetAreaPortalState on non-q3 map"); + + if (area1 > numareas || area2 > numareas) + Host_Error ("CMQ3_SetAreaPortalState: area > numareas"); + + if (open) + { + map_q3areas[area1].numareaportals[area2]++; + map_q3areas[area2].numareaportals[area1]++; + } + else + { + map_q3areas[area1].numareaportals[area2]--; + map_q3areas[area2].numareaportals[area1]--; + } +} + +qboolean CM_AreasConnected (int area1, int area2) +{ + if (map_noareas.value) + return true; + + if (area1 > numareas || area2 > numareas) + Host_Error ("area > numareas"); + + if (mapisq3) + { + int i; + for (i=1 ; i>3; + + if (map_noareas.value) + { // for debugging, send everything + memset (buffer, 255, bytes); + } + else + { + memset (buffer, 0, bytes); + + if (mapisq3) + { + for (i=0 ; i>3] |= 1<<(i&7); + } + } + else + { + floodnum = map_q2areas[area].floodnum; + for (i=0 ; i>3] |= 1<<(i&7); + } + } + } + + return bytes; +} + + +/* +=================== +CM_WritePortalState + +Writes the portal state to a savegame file +=================== +*/ +void CM_WritePortalState (FILE *f) +{ + fwrite (portalopen, sizeof(portalopen), 1, f); +} + +/* +=================== +CM_ReadPortalState + +Reads the portal state from a savegame file +and recalculates the area connections +=================== +*/ +void CM_ReadPortalState (FILE *f) +{ + fread (portalopen, 1, sizeof(portalopen), f); + FloodAreaConnections (); +} + +/* +============= +CM_HeadnodeVisible + +Returns true if any leaf under headnode has a cluster that +is potentially visible +============= +*/ +qboolean CM_HeadnodeVisible (int nodenum, qbyte *visbits) +{ + int leafnum; + int cluster; + mnode_t *node; + + if (nodenum < 0) + { + leafnum = -1-nodenum; + cluster = map_leafs[leafnum].cluster; + if (cluster == -1) + return false; + if (visbits[cluster>>3] & (1<<(cluster&7))) + return true; + return false; + } + + node = &map_nodes[nodenum]; + if (CM_HeadnodeVisible(node->childnum[0], visbits)) + return true; + return CM_HeadnodeVisible(node->childnum[1], visbits); +} + + +qboolean Q2BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +{ + trace_t ret = CM_BoxTrace(p1, p2, hull->clip_mins, hull->clip_maxs, hull->firstclipnode, MASK_SOLID); + memcpy(trace, &ret, sizeof(trace_t)); + if (ret.fraction==1) + return true; + return false; +} +int Q2BSP_HullPointContents(hull_t *hull, vec3_t p) +{ + int pc, ret = FTECONTENTS_EMPTY; + pc = CM_PointContents (p, hull->firstclipnode); + if (pc & (Q2CONTENTS_SOLID|Q2CONTENTS_WINDOW)) + ret |= FTECONTENTS_SOLID; + if (pc & Q2CONTENTS_LAVA) + ret |= FTECONTENTS_LAVA; + if (pc & Q2CONTENTS_SLIME) + ret |= FTECONTENTS_SLIME; + if (pc & Q2CONTENTS_WATER) + ret |= FTECONTENTS_WATER; + if (pc & Q2CONTENTS_LADDER) + ret |= FTECONTENTS_LADDER; + + return ret; +} +void Q2BSP_SetHullFuncs(hull_t *hull) +{ + hull->funcs.RecursiveHullCheck = Q2BSP_RecursiveHullCheck; + hull->funcs.HullPointContents = Q2BSP_HullPointContents; +} + + + + + + + + +int map_checksum; +void Mod_LoadQ2BrushModel (model_t *mod, void *buffer) +{ + mod->fromgame = fg_quake2; + CM_LoadMap(mod->name, buffer, true, &map_checksum); +} + +void CM_Init(void) //register cvars. +{ +#define MAPOPTIONS "Map Cvar Options" + Cvar_Register(&map_noareas, MAPOPTIONS); + Cvar_Register(&map_noCurves, MAPOPTIONS); + Cvar_Register(&map_autoopenportals, MAPOPTIONS); +} +#endif diff --git a/engine/common/huff.c b/engine/common/huff.c new file mode 100644 index 000000000..7b77dda31 --- /dev/null +++ b/engine/common/huff.c @@ -0,0 +1,812 @@ +/* +Q3Fusion - Quake III Clone Engine + +Copyright (C) 2003 Andrey Nazarov + +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. +*/ + +// +// huff.c - Huffman compression routines for data bitstream +// +#include "bothdefs.h" +#ifdef HUFFNETWORK + +#include "quakedef.h" +#define ID_INLINE + +#define VALUE(a) ((int )(a)) +#define NODE(a) ((void *)(a)) + +#define NODE_START NODE( 1) +#define NODE_NONE NODE(256) +#define NODE_NEXT NODE(257) + +#define NOT_REFERENCED 256 + +#define HUFF_TREE_SIZE 7175 +typedef void *tree_t[HUFF_TREE_SIZE]; + +// +// pre-defined frequency counts for all bytes [0..255] +// +static int q3huffCounts[256] = { + 0x3D1CB, 0x0A0E9, 0x01894, 0x01BC2, 0x00E92, 0x00EA6, 0x017DE, 0x05AF3, + 0x08225, 0x01B26, 0x01E9E, 0x025F2, 0x02429, 0x0436B, 0x00F6D, 0x006F2, + 0x02060, 0x00644, 0x00636, 0x0067F, 0x0044C, 0x004BD, 0x004D6, 0x0046E, + 0x006D5, 0x00423, 0x004DE, 0x0047D, 0x004F9, 0x01186, 0x00AF5, 0x00D90, + 0x0553B, 0x00487, 0x00686, 0x0042A, 0x00413, 0x003F4, 0x0041D, 0x0042E, + 0x006BE, 0x00378, 0x0049C, 0x00352, 0x003C0, 0x0030C, 0x006D8, 0x00CE0, + 0x02986, 0x011A2, 0x016F9, 0x00A7D, 0x0122A, 0x00EFD, 0x0082D, 0x0074B, + 0x00A18, 0x0079D, 0x007B4, 0x003AC, 0x0046E, 0x006FC, 0x00686, 0x004B6, + 0x01657, 0x017F0, 0x01C36, 0x019FE, 0x00E7E, 0x00ED3, 0x005D4, 0x005F4, + 0x008A7, 0x00474, 0x0054B, 0x003CB, 0x00884, 0x004E0, 0x00530, 0x004AB, + 0x006EA, 0x00436, 0x004F0, 0x004F2, 0x00490, 0x003C5, 0x00483, 0x004A2, + 0x00543, 0x004CC, 0x005F9, 0x00640, 0x00A39, 0x00800, 0x009F2, 0x00CCB, + 0x0096A, 0x00E01, 0x009C8, 0x00AF0, 0x00A73, 0x01802, 0x00E4F, 0x00B18, + 0x037AD, 0x00C5C, 0x008AD, 0x00697, 0x00C88, 0x00AB3, 0x00DB8, 0x012BC, + 0x00FFB, 0x00DBB, 0x014A8, 0x00FB0, 0x01F01, 0x0178F, 0x014F0, 0x00F54, + 0x0131C, 0x00E9F, 0x011D6, 0x012C7, 0x016DC, 0x01900, 0x01851, 0x02063, + 0x05ACB, 0x01E9E, 0x01BA1, 0x022E7, 0x0153D, 0x01183, 0x00E39, 0x01488, + 0x014C0, 0x014D0, 0x014FA, 0x00DA4, 0x0099A, 0x0069E, 0x0071D, 0x00849, + 0x0077C, 0x0047D, 0x005EC, 0x00557, 0x004D4, 0x00405, 0x004EA, 0x00450, + 0x004DD, 0x003EE, 0x0047D, 0x00401, 0x004D9, 0x003B8, 0x00507, 0x003E5, + 0x006B1, 0x003F1, 0x004A3, 0x0036F, 0x0044B, 0x003A1, 0x00436, 0x003B7, + 0x00678, 0x003A2, 0x00481, 0x00406, 0x004EE, 0x00426, 0x004BE, 0x00424, + 0x00655, 0x003A2, 0x00452, 0x00390, 0x0040A, 0x0037C, 0x00486, 0x003DE, + 0x00497, 0x00352, 0x00461, 0x00387, 0x0043F, 0x00398, 0x00478, 0x00420, + 0x00D86, 0x008C0, 0x0112D, 0x02F68, 0x01E4E, 0x00541, 0x0051B, 0x00CCE, + 0x0079E, 0x00376, 0x003FF, 0x00458, 0x00435, 0x00412, 0x00425, 0x0042F, + 0x005CC, 0x003E9, 0x00448, 0x00393, 0x0041C, 0x003E3, 0x0042E, 0x0036C, + 0x00457, 0x00353, 0x00423, 0x00325, 0x00458, 0x0039B, 0x0044F, 0x00331, + 0x0076B, 0x00750, 0x003D0, 0x00349, 0x00467, 0x003BC, 0x00487, 0x003B6, + 0x01E6F, 0x003BA, 0x00509, 0x003A5, 0x00467, 0x00C87, 0x003FC, 0x0039F, + 0x0054B, 0x00300, 0x00410, 0x002E9, 0x003B8, 0x00325, 0x00431, 0x002E4, + 0x003F5, 0x00325, 0x003F0, 0x0031C, 0x003E4, 0x00421, 0x02CC1, 0x034C0 +}; + +static int countinghuffCounts[256]; + + +// +// static Huffman tree +// +static tree_t huffTree; + +// +// received from MSG_* code +// +static int huffBitPos; + + +/* +======================================================================================= + + HUFFMAN TREE CONSTRUCTION + +======================================================================================= +*/ + +/* +============ +Huff_PrepareTree +============ +*/ +static ID_INLINE void Huff_PrepareTree( tree_t tree ) { + void **node; + + memset( tree, 0, sizeof( tree_t ) ); + + // create first node + node = &tree[263]; + VALUE( tree[0] )++; + + node[7] = NODE_NONE; + tree[2] = node; + tree[3] = node; + tree[4] = node; + tree[261] = node; +} + + + +/* +============ +Huff_GetNode +============ +*/ +static ID_INLINE void **Huff_GetNode( void **tree ) { + void **node; + int value; + + node = tree[262]; + if( !node ) { + value = VALUE( tree[1] )++; + node = &tree[value + 6407]; + return node; + } + + tree[262] = node[0]; + return node; +} + +/* +============ +Huff_Swap +============ +*/ +static ID_INLINE void Huff_Swap( void **tree1, void **tree2, void **tree3 ) { + void **a, **b; + + a = tree2[2]; + if( a ) { + if( a[0] == tree2 ) { + a[0] = tree3; + } else { + a[1] = tree3; + } + } else { + tree1[2] = tree3; + } + + b = tree3[2]; + + if( b ) { + if( b[0] == tree3 ) { + b[0] = tree2; + tree2[2] = b; + tree3[2] = a; + return; + } + + b[1] = tree2; + tree2[2] = b; + tree3[2] = a; + return; + } + + tree1[2] = tree2; + tree2[2] = NULL; + tree3[2] = a; +} + +/* +============ +Huff_SwapTrees +============ +*/ +static ID_INLINE void Huff_SwapTrees( void **tree1, void **tree2 ) { + void **temp; + + temp = tree1[3]; + tree1[3] = tree2[3]; + tree2[3] = temp; + + temp = tree1[4]; + tree1[4] = tree2[4]; + tree2[4] = temp; + + if( tree1[3] == tree1 ) { + tree1[3] = tree2; + } + + if( tree2[3] == tree2 ) { + tree2[3] = tree1; + } + + temp = tree1[3]; + if( temp ) { + temp[4] = tree1; + } + + temp = tree2[3]; + if( temp ) { + temp[4] = tree2; + } + + temp = tree1[4]; + if( temp ) { + temp[3] = tree1; + } + + temp = tree2[4]; + if( temp ) { + temp[3] = tree2; + } + +} + +/* +============ +Huff_DeleteNode +============ +*/ +static ID_INLINE void Huff_DeleteNode( void **tree1, void **tree2 ) { + tree2[0] = tree1[262]; + tree1[262] = tree2; +} + +/* +============ +Huff_IncrementFreq_r +============ +*/ +static void Huff_IncrementFreq_r( void **tree1, void **tree2 ) { + void **a, **b; + + if( !tree2 ) { + return; + } + + a = tree2[3]; + if( a ) { + a = a[6]; + if( a == tree2[6] ) { + b = tree2[5]; + if( b[0] != tree2[2] ) { + Huff_Swap( tree1, b[0], tree2 ); + } + Huff_SwapTrees( b[0], tree2 ); + } + } + + a = tree2[4]; + if( a && a[6] == tree2[6] ) { + b = tree2[5]; + b[0] = a; + } else { + a = tree2[5]; + a[0] = 0; + Huff_DeleteNode( tree1, tree2[5] ); + } + + + VALUE( tree2[6] )++; + a = tree2[3]; + if( a && a[6] == tree2[6] ) { + tree2[5] = a[5]; + } else { + a = Huff_GetNode( tree1 ); + tree2[5] = a; + a[0] = tree2; + } + + if( tree2[2] ) { + Huff_IncrementFreq_r( tree1, tree2[2] ); + + if( tree2[4] == tree2[2] ) { + Huff_SwapTrees( tree2, tree2[2] ); + a = tree2[5]; + + if( a[0] == tree2 ) { + a[0] = tree2[2]; + } + } + } +} + +/* +============ +Huff_AddReference + +Insert 'ch' into the tree or increment it's frequency +============ +*/ +static void Huff_AddReference( void **tree, int ch ) { + void **a, **b, **c, **d; + int value; + + ch &= 255; + if( tree[ch + 5] ) { + Huff_IncrementFreq_r( tree, tree[ch + 5] ); + return; // already added + } + + value = VALUE( tree[0] )++; + b = &tree[value * 8 + 263]; + + value = VALUE( tree[0] )++; + a = &tree[value * 8 + 263]; + + a[7] = NODE_NEXT; + a[6] = NODE_START; + d = tree[3]; + a[3] = d[3]; + if( a[3] ) { + d = a[3]; + d[4] = a; + d = a[3]; + if( d[6] == NODE_START ) { + a[5] = d[5]; + } else { + d = Huff_GetNode( tree ); + a[5] = d; + d[0] = a; + } + } else { + d = Huff_GetNode( tree ); + a[5] = d; + d[0] = a; + + } + + d = tree[3]; + d[3] = a; + a[4] = tree[3]; + b[7] = NODE( ch ); + b[6] = NODE_START; + d = tree[3]; + b[3] = d[3]; + if( b[3] ) { + d = b[3]; + d[4] = b; + if( d[6] == NODE_START ) { + b[5] = d[5]; + } else { + d = Huff_GetNode( tree ); + b[5] = d; + d[0] = a; + } + } else { + d = Huff_GetNode( tree ); + b[5] = d; + d[0] = b; + } + + d = tree[3]; + d[3] = b; + b[4] = tree[3]; + b[1] = NULL; + b[0] = NULL; + d = tree[3]; + c = d[2]; + if( c ) { + if( c[0] == tree[3] ) { + c[0] = a; + } else { + c[1] = a; + } + } else { + tree[2] = a; + } + + a[1] = b; + d = tree[3]; + a[0] = d; + a[2] = d[2]; + b[2] = a; + d = tree[3]; + d[2] = a; + tree[ch + 5] = b; + + Huff_IncrementFreq_r( tree, a[2] ); +} + +/* +======================================================================================= + + BITSTREAM I/O + +======================================================================================= +*/ + +/* +============ +Huff_EmitBit + +Put one bit into buffer +============ +*/ +static ID_INLINE void Huff_EmitBit( int bit, qbyte *buffer ) { + if( !(huffBitPos & 7) ) { + buffer[huffBitPos >> 3] = 0; + } + + buffer[huffBitPos >> 3] |= bit << (huffBitPos & 7); + huffBitPos++; +} + +/* +============ +Huff_GetBit + +Read one bit from buffer +============ +*/ +static ID_INLINE int Huff_GetBit( qbyte *buffer ) { + int bit; + + bit = buffer[huffBitPos >> 3] >> (huffBitPos & 7); + huffBitPos++; + + return (bit & 1); +} + +/* +============ +Huff_EmitPathToByte +============ +*/ +static ID_INLINE void Huff_EmitPathToByte( void **tree, void **subtree, qbyte *buffer ) { + if( tree[2] ) { + Huff_EmitPathToByte( tree[2], tree, buffer ); + } + + if( !subtree ) { + return; + } + + // + // emit tree walking control bits + // + if( tree[1] == subtree ) { + Huff_EmitBit( 1, buffer ); + } else { + Huff_EmitBit( 0, buffer ); + } +} + +/* +============ +Huff_GetByteFromTree + +Get one qbyte using dynamic or static tree +============ +*/ +static ID_INLINE int Huff_GetByteFromTree( void **tree, qbyte *buffer ) { + if( !tree ) { + return 0; + } + + // + // walk through the tree until we get a value + // + while( tree[7] == NODE_NEXT ) { + if( !Huff_GetBit( buffer ) ) { + tree = tree[0]; + } else { + tree = tree[1]; + } + + if( !tree ) { + return 0; + } + } + + return VALUE( tree[7] ); +} + +/* +============ +Huff_EmitByteDynamic + +Emit one qbyte using dynamic tree +============ +*/ +static void Huff_EmitByteDynamic( void **tree, int value, qbyte *buffer ) { + void **subtree; + int i; + + // + // if qbyte was already referenced, emit path to it + // + subtree = tree[value + 5]; + if( subtree ) { + if( subtree[2] ) { + Huff_EmitPathToByte( subtree[2], subtree, buffer ); + } + return; + } + + // + // qbyte was not referenced, just emit 8 bits + // + Huff_EmitByteDynamic( tree, NOT_REFERENCED, buffer ); + + for( i=7 ; i>=0 ; i-- ) { + Huff_EmitBit( (value >> i) & 1, buffer ); + } + +} + +/* +======================================================================================= + + PUBLIC INTERFACE + +======================================================================================= +*/ + +/* +============ +Huff_CompressPacket + +Compress message using dynamic Huffman tree, +beginning from specified offset +============ +*/ +void Huff_EncryptPacket( sizebuf_t *msg, int offset ) { + tree_t tree; + qbyte buffer[MAX_NQMSGLEN]; + qbyte *data; + int outLen; + int inLen; + int i; + + data = msg->data + offset; + inLen = msg->cursize - offset; + if( inLen <= 0 || inLen >= MAX_NQMSGLEN ) { + return; + } + + Huff_PrepareTree( tree ); + + buffer[0] = inLen >> 8; + buffer[1] = inLen & 0xFF; + huffBitPos = 16; + + for( i=0 ; i> 3) + 1; + + msg->cursize = offset + outLen; + memcpy( data, buffer, outLen ); + +} + +/* +============ +Huff_DecompressPacket + +Decompress message using dynamic Huffman tree, +beginning from specified offset +============ +*/ +void Huff_DecryptPacket( sizebuf_t *msg, int offset ) { + tree_t tree; + qbyte buffer[MAX_NQMSGLEN]; + qbyte *data; + int outLen; + int inLen; + int i, j; + int ch; + + data = msg->data + offset; + inLen = msg->cursize - offset; + if( inLen <= 0 ) { + return; + } + + Huff_PrepareTree( tree ); + + outLen = (data[0] << 8) + data[1]; + huffBitPos = 16; + + if( outLen > msg->maxsize - offset ) { + outLen = msg->maxsize - offset; + } + + for( i=0 ; i> 3) > inLen ) { + buffer[i] = 0; + break; + } + + ch = Huff_GetByteFromTree( tree[2], data ); + + if( ch == NOT_REFERENCED ) { + ch = 0; // just read 8 bits + for( j=0 ; j<8 ; j++ ) { + ch <<= 1; + ch |= Huff_GetBit( data ); + } + } + + buffer[i] = ch; + Huff_AddReference( tree, ch ); + } + + + msg->cursize = offset + outLen; + memcpy( data, buffer, outLen ); +} + +/* +============ +Huff_EmitByte +============ +*/ +void Huff_EmitByte( int ch, qbyte *buffer, int *count ) { + huffBitPos = *count; + Huff_EmitPathToByte( huffTree[ch + 5], NULL, buffer ); + *count = huffBitPos; +} + +/* +============ +Huff_GetByte +============ +*/ +int Huff_GetByte( qbyte *buffer, int *count ) { + int ch; + + huffBitPos = *count; + ch = Huff_GetByteFromTree( huffTree[2], buffer ); + *count = huffBitPos; + + return ch; +} + +static int madetable; +/* +============ +Huff_Init +============ +*/ +void Huff_Init( int *huffCounts ) { + int i, j; + + if (!huffCounts) + huffCounts = q3huffCounts; + + // build empty tree + Huff_PrepareTree( huffTree ); + + // add all pre-defined qbyte references + for( i=0 ; i<256 ; i++ ) { + for( j=0 ; jdata + offset; + inLen = msg->cursize - offset; + if( inLen <= 0 || inLen >= MAX_NQMSGLEN ) { + return; + } + + outLen = 0; + for( i=0 ; i> 3) + 1; + + if (outLen > inLen) + { + memmove( data+1, data, inLen ); + data[0] = 0x80; //this would have grown the packet. + msg->cursize+=1; + return; //cap it at only 1 qbyte growth. + } + + msg->cursize = offset + outLen; + { //add the bitcount + data[0] = (outLen<<3) - huffBitPos; + data+=1; + msg->cursize+=1; + } + if (msg->cursize > msg->maxsize) + Sys_Error("Compression became too large\n"); + memcpy( data, buffer, outLen ); +} + +/* +============ +Huff_DecompressPacket + +Decompress message using loaded Huffman tree, +beginning from specified offset +============ +*/ +void Huff_DecompressPacket( sizebuf_t *msg, int offset ) +{ + qbyte buffer[MAX_NQMSGLEN]; + qbyte *data; + int outLen; + int inLen; + int i; + + if (!madetable) + Huff_Init(NULL); + + data = msg->data + offset; + inLen = msg->cursize - offset; + if( inLen <= 0 || inLen >= MAX_NQMSGLEN ) { + return; + } + + inLen<<=3; + { //add the bitcount + inLen = inLen-8-data[0]; + if (data[0]&0x80) + { //packet would have grown. + msg->cursize -= 1; + memmove(data, data+1, msg->cursize); + return; //this never happened, okay? + } + data+=1; + } + + outLen = 0; + for( i=0 ; outLencursize = offset + i; + if (msg->cursize > msg->maxsize) + Sys_Error("Decompression became too large\n"); + memcpy( msg->data + offset, buffer, i ); +} + +#endif diff --git a/engine/common/math.s b/engine/common/math.s new file mode 100644 index 000000000..49b6ce6e2 --- /dev/null +++ b/engine/common/math.s @@ -0,0 +1,417 @@ +/* +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. + +*/ +// +// math.s +// x86 assembly-language math routines. + +#include "asm_i386.h" +#include "quakeasm.h" + + +#if id386 + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + +// TODO: rounding needed? +// stack parameter offset +#define val 4 + +.globl C(Invert24To16) +C(Invert24To16): + + movl val(%esp),%ecx + movl $0x100,%edx // 0x10000000000 as dividend + cmpl %edx,%ecx + jle LOutOfRange + + subl %eax,%eax + divl %ecx + + ret + +LOutOfRange: + movl $0xFFFFFFFF,%eax + ret + +#define in 4 +#define out 8 +#ifndef SERVERONLY + .align 2 +.globl C(TransformVector) +C(TransformVector): + movl in(%esp),%eax + movl out(%esp),%edx + + flds (%eax) // in[0] + fmuls C(vright) // in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vright[0] + fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] + fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] + + flds 4(%eax) // in[1] | ... + fmuls C(vright)+4 // in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vright[1] | ... + fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... + + faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... + faddp %st(0),%st(3) // in[1]*vpn[1] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + flds 8(%eax) // in[2] | ... + fmuls C(vright)+8 // in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vright[2] | ... + fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... + + faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... + faddp %st(0),%st(3) // in[2]*vpn[2] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + fstps 8(%edx) // out[2] + fstps 4(%edx) // out[1] + fstps (%edx) // out[0] + + ret +#endif + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpb $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + jmp Ljmptab(,%eax,4) + + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + call C(BOPS_Error) + +#endif // id386 diff --git a/engine/common/mathlib.c b/engine/common/mathlib.c new file mode 100644 index 000000000..ecbe03775 --- /dev/null +++ b/engine/common/mathlib.c @@ -0,0 +1,715 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// mathlib.c -- math primitives + +#include "quakedef.h" +#include + +vec3_t vec3_origin = {0,0,0}; +int nanmask = 255<<23; + +/*-----------------------------------------------------------------*/ + +#define DEG2RAD( a ) ( a * M_PI ) / 180.0F + +void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) +{ + float d; + vec3_t n; + float inv_denom; + + inv_denom = 1.0F / DotProduct( normal, normal ); + + d = DotProduct( normal, p ) * inv_denom; + + n[0] = normal[0] * inv_denom; + n[1] = normal[1] * inv_denom; + n[2] = normal[2] * inv_denom; + + dst[0] = p[0] - d * n[0]; + dst[1] = p[1] - d * n[1]; + dst[2] = p[2] - d * n[2]; +} + +/* +** assumes "src" is normalized +*/ +void PerpendicularVector( vec3_t dst, const vec3_t src ) +{ + int pos; + int i; + float minelem = 1.0F; + vec3_t tempvec; + + /* + ** find the smallest magnitude axially aligned vector + */ + for ( pos = 0, i = 0; i < 3; i++ ) + { + if ( fabs( src[i] ) < minelem ) + { + pos = i; + minelem = fabs( src[i] ); + } + } + tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; + tempvec[pos] = 1.0F; + + /* + ** project the point onto the plane defined by src + */ + ProjectPointOnPlane( dst, tempvec, src ); + + /* + ** normalize the result + */ + VectorNormalize( dst ); +} + +#ifdef _WIN32 +#pragma optimize( "", off ) +#endif + + +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ) +{ + float m[3][3]; + float im[3][3]; + float zrot[3][3]; + float tmpmat[3][3]; + float rot[3][3]; + int i; + vec3_t vr, vup, vf; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + PerpendicularVector( vr, dir ); + CrossProduct( vr, vf, vup ); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + memcpy( im, m, sizeof( im ) ); + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + memset( zrot, 0, sizeof( zrot ) ); + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + zrot[0][0] = cos( DEG2RAD( degrees ) ); + zrot[0][1] = sin( DEG2RAD( degrees ) ); + zrot[1][0] = -sin( DEG2RAD( degrees ) ); + zrot[1][1] = cos( DEG2RAD( degrees ) ); + + R_ConcatRotations( m, zrot, tmpmat ); + R_ConcatRotations( tmpmat, im, rot ); + + for ( i = 0; i < 3; i++ ) + { + dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } +} + +#ifdef _WIN32 +#pragma optimize( "", on ) +#endif + +/*-----------------------------------------------------------------*/ + +float anglemod(float a) +{ +#if 0 + if (a >= 0) + a -= 360*(int)(a/360); + else + a += 360*( 1 + (int)(-a/360) ); +#endif + a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); + return a; +} + +/* +================== +BOPS_Error + +Split out like this for ASM to call. +================== +*/ +void VARGS BOPS_Error (void) +{ + Sys_Error ("BoxOnPlaneSide: Bad signbits"); +} + +#if !id386 + +/* +================== +BoxOnPlaneSide + +Returns 1, 2, or 1 + 2 +================== +*/ +int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p) +{ + float dist1, dist2; + int sides; + +#if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this + // function +// fast axial cases + if (p->type < 3) + { + if (p->dist <= emins[p->type]) + return 1; + if (p->dist >= emaxs[p->type]) + return 2; + return 3; + } +#endif + +// general case + switch (p->signbits) + { + case 0: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + break; + case 1: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + break; + case 2: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + break; + case 3: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + break; + case 4: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + break; + case 5: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + break; + case 6: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + break; + case 7: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + break; + default: + dist1 = dist2 = 0; // shut up compiler + BOPS_Error (); + break; + } + +#if 0 + int i; + vec3_t corners[2]; + + for (i=0 ; i<3 ; i++) + { + if (plane->normal[i] < 0) + { + corners[0][i] = emins[i]; + corners[1][i] = emaxs[i]; + } + else + { + corners[1][i] = emins[i]; + corners[0][i] = emaxs[i]; + } + } + dist = DotProduct (plane->normal, corners[0]) - plane->dist; + dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; + sides = 0; + if (dist1 >= 0) + sides = 1; + if (dist2 < 0) + sides |= 2; + +#endif + + sides = 0; + if (dist1 >= p->dist) + sides = 1; + if (dist2 < p->dist) + sides |= 2; + +#ifdef PARANOID +if (sides == 0) + Sys_Error ("BoxOnPlaneSide: sides==0"); +#endif + + return sides; +} + +#endif + + + + +void VVPerpendicularVector(vec3_t dst, const vec3_t src) +{ + if (!src[0]) + { + dst[0] = 1; + dst[1] = dst[2] = 0; + } + else if (!src[1]) + { + dst[1] = 1; + dst[0] = dst[2] = 0; + } + else if (!src[2]) + { + dst[2] = 1; + dst[0] = dst[1] = 0; + } + else + { + dst[0] = -src[1]; + dst[1] = src[0]; + dst[2] = 0; + VectorNormalize(dst); + } +} +void VectorVectors(vec3_t forward, vec3_t right, vec3_t up) +{ + VVPerpendicularVector(right, forward); + CrossProduct(right, forward, up); +} + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = angles[YAW] * (M_PI*2 / 360); + sy = sin(angle); + cy = cos(angle); + angle = angles[PITCH] * (M_PI*2 / 360); + sp = sin(angle); + cp = cos(angle); + angle = angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + right[0] = (-1*sr*sp*cy+-1*cr*-sy); + right[1] = (-1*sr*sp*sy+-1*cr*cy); + right[2] = -1*sr*cp; + up[0] = (cr*sp*cy+-sr*-sy); + up[1] = (cr*sp*sy+-sr*cy); + up[2] = cr*cp; +} + +int VectorCompare (vec3_t v1, vec3_t v2) +{ + int i; + + for (i=0 ; i<3 ; i++) + if (v1[i] != v2[i]) + return 0; + + return 1; +} + +void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) +{ + vecc[0] = veca[0] + scale*vecb[0]; + vecc[1] = veca[1] + scale*vecb[1]; + vecc[2] = veca[2] + scale*vecb[2]; +} + + +vec_t _DotProduct (vec3_t v1, vec3_t v2) +{ + return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; +} + +void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out) +{ + out[0] = veca[0]-vecb[0]; + out[1] = veca[1]-vecb[1]; + out[2] = veca[2]-vecb[2]; +} + +void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out) +{ + out[0] = veca[0]+vecb[0]; + out[1] = veca[1]+vecb[1]; + out[2] = veca[2]+vecb[2]; +} + +void _VectorCopy (vec3_t in, vec3_t out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) +{ + cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; + cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; + cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +vec_t Length(vec3_t v) +{ + int i; + float length; + + length = 0; + for (i=0 ; i< 3 ; i++) + length += v[i]*v[i]; + length = sqrt (length); // FIXME + + return length; +} + +float VectorNormalize (vec3_t v) +{ + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + length = sqrt (length); // FIXME + + if (length) + { + ilength = 1/length; + v[0] *= ilength; + v[1] *= ilength; + v[2] *= ilength; + } + + return length; + +} + +void VectorInverse (vec3_t v) +{ + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; +} + +void VectorScale (vec3_t in, vec_t scale, vec3_t out) +{ + out[0] = in[0]*scale; + out[1] = in[1]*scale; + out[2] = in[2]*scale; +} + + +int Q_log2(int val) +{ + int answer=0; + while ((val>>=1) != 0) + answer++; + return answer; +} + + +/* +================ +R_ConcatRotations +================ +*/ +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; +} + + +/* +================ +R_ConcatTransforms +================ +*/ +void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + + in1[0][2] * in2[2][3] + in1[0][3]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + + in1[1][2] * in2[2][3] + in1[1][3]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + + in1[2][2] * in2[2][3] + in1[2][3]; +} + + +/* +=================== +FloorDivMod + +Returns mathematically correct (floor-based) quotient and remainder for +numer and denom, both of which should contain no fractional part. The +quotient must fit in 32 bits. +==================== +*/ + +void FloorDivMod (double numer, double denom, int *quotient, + int *rem) +{ + int q, r; + double x; + +#ifndef PARANOID + if (denom <= 0.0) + Sys_Error ("FloorDivMod: bad denominator %d\n", denom); + +// if ((floor(numer) != numer) || (floor(denom) != denom)) +// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n", +// numer, denom); +#endif + + if (numer >= 0.0) + { + + x = floor(numer / denom); + q = (int)x; + r = (int)floor(numer - (x * denom)); + } + else + { + // + // perform operations with positive values, and fix mod to make floor-based + // + x = floor(-numer / denom); + q = -(int)x; + r = (int)floor(-numer - (x * denom)); + if (r != 0) + { + q--; + r = (int)denom - r; + } + } + + *quotient = q; + *rem = r; +} + + +/* +=================== +GreatestCommonDivisor +==================== +*/ +int GreatestCommonDivisor (int i1, int i2) +{ + if (i1 > i2) + { + if (i2 == 0) + return (i1); + return GreatestCommonDivisor (i2, i1 % i2); + } + else + { + if (i1 == 0) + return (i2); + return GreatestCommonDivisor (i1, i2 % i1); + } +} + + +#if !id386 + +// TODO: move to nonintel.c + +/* +=================== +Invert24To16 + +Inverts an 8.24 value to a 16.16 value +==================== +*/ + +fixed16_t Invert24To16(fixed16_t val) +{ + if (val < 256) + return (0xFFFFFFFF); + + return (fixed16_t) + (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5); +} + +#endif + + + + + + + + +void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out) +{ + out[0] = DotProduct(in1, in2[0]) + in2[0][3]; + out[1] = DotProduct(in1, in2[1]) + in2[1][3]; + out[2] = DotProduct(in1, in2[2]) + in2[2][3]; +} + +#ifdef HALFLIFEMODELS + +void AngleQuaternion( const vec3_t angles, vec4_t quaternion ) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + // FIXME: rescale the inputs to 1/2 angle + angle = angles[2] * 0.5; + sy = sin(angle); + cy = cos(angle); + angle = angles[1] * 0.5; + sp = sin(angle); + cp = cos(angle); + angle = angles[0] * 0.5; + sr = sin(angle); + cr = cos(angle); + + quaternion[0] = sr*cp*cy-cr*sp*sy; // X + quaternion[1] = cr*sp*cy+sr*cp*sy; // Y + quaternion[2] = cr*cp*sy-sr*sp*cy; // Z + quaternion[3] = cr*cp*cy+sr*sp*sy; // W +} + +void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ) +{ + + matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2]; + matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2]; + matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1]; + + matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2]; + matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2]; + matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0]; + + matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1]; + matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0]; + matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1]; +} + +void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) +{ + int i; + float omega, cosom, sinom, sclp, sclq; + + // decide if one of the quaternions is backwards + float a = 0; + float b = 0; + for (i = 0; i < 4; i++) { + a += (p[i]-q[i])*(p[i]-q[i]); + b += (p[i]+q[i])*(p[i]+q[i]); + } + if (a > b) { + for (i = 0; i < 4; i++) { + q[i] = -q[i]; + } + } + + cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3]; + + if ((1.0 + cosom) > 0.00000001) { + if ((1.0 - cosom) > 0.00000001) { + omega = acos( cosom ); + sinom = sin( omega ); + sclp = sin( (1.0 - t)*omega) / sinom; + sclq = sin( t*omega ) / sinom; + } + else { + sclp = 1.0 - t; + sclq = t; + } + for (i = 0; i < 4; i++) { + qt[i] = sclp * p[i] + sclq * q[i]; + } + } + else { + qt[0] = -p[1]; + qt[1] = p[0]; + qt[2] = -p[3]; + qt[3] = p[2]; + sclp = sin( (1.0 - t) * 0.5 * M_PI); + sclq = sin( t * 0.5 * M_PI); + for (i = 0; i < 3; i++) { + qt[i] = sclp * p[i] + sclq * qt[i]; + } + } +} +#endif diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h new file mode 100644 index 000000000..efcf81912 --- /dev/null +++ b/engine/common/mathlib.h @@ -0,0 +1,99 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// mathlib.h + +typedef float vec_t; +typedef vec_t vec2_t[2]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; +typedef vec_t vec5_t[5]; + +typedef qbyte byte_vec4_t[4]; + +typedef int fixed4_t; +typedef int fixed8_t; +typedef int fixed16_t; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +struct mplane_s; + +extern vec3_t vec3_origin; +extern int nanmask; + +#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorClear(a) (a[0]=a[1]=a[2]=0) +#define VectorNegate(a,b) (b[0]=-a[0],b[1]=-a[1],b[2]=-a[2]) +#define VectorLength(a) Length(a) + +void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); + +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out); +void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); + +int VectorCompare (vec3_t v1, vec3_t v2); +vec_t Length (vec3_t v); +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); +float VectorNormalize (vec3_t v); // returns vector length +void VectorInverse (vec3_t v); +void VectorScale (vec3_t in, vec_t scale, vec3_t out); +int Q_log2(int val); + +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); +void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); + +void FloorDivMod (double numer, double denom, int *quotient, + int *rem); +fixed16_t Invert24To16(fixed16_t val); +fixed16_t Mul16_30(fixed16_t multiplier, fixed16_t multiplicand); +int GreatestCommonDivisor (int i1, int i2); + +void VectorVectors(vec3_t forward, vec3_t right, vec3_t up); +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); +int VARGS BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane); +float anglemod(float a); + +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); + + +#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ + (((p)->type < 3)? \ + ( \ + ((p)->dist <= (emins)[(p)->type])? \ + 1 \ + : \ + ( \ + ((p)->dist >= (emaxs)[(p)->type])?\ + 2 \ + : \ + 3 \ + ) \ + ) \ + : \ + BoxOnPlaneSide( (emins), (emaxs), (p))) diff --git a/engine/common/md4.c b/engine/common/md4.c new file mode 100644 index 000000000..7932d4a06 --- /dev/null +++ b/engine/common/md4.c @@ -0,0 +1,306 @@ +/* +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. + +*/ +/* GLOBAL.H - RSAREF types and constants */ + +#include + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +#ifdef __alpha__ +typedef unsigned int UINT4; +#else +typedef unsigned long int UINT4; +#endif + + +/* MD4.H - header file for MD4C.C */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. + +All rights reserved. + +License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this documentation and/or software. */ + +/* MD4 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD4_CTX; + +void MD4Init (MD4_CTX *); +void MD4Update (MD4_CTX *, unsigned char *, unsigned int); +void MD4Final (unsigned char [16], MD4_CTX *); + + + +/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */ +/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. + +License to copy and use this software is granted provided that it is identified as the +RSA Data Security, Inc. MD4 Message-Digest Algorithm + in all material mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such works are identified as +derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm +in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided +as is without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this documentation and/or software. */ + +/* Constants for MD4Transform routine. */ +#define S11 3 +#define S12 7 +#define S13 11 +#define S14 19 +#define S21 3 +#define S22 5 +#define S23 9 +#define S24 13 +#define S31 3 +#define S32 9 +#define S33 11 +#define S34 15 + +static void MD4Transform (UINT4 [4], unsigned char [64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, unsigned char *, unsigned int); + +static unsigned char PADDING[64] = { +0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G and H are basic MD4 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));} + +#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));} + +#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = ROTATE_LEFT ((a), (s));} + + +/* MD4 initialization. Begins an MD4 operation, writing a new context. */ +void MD4Init (MD4_CTX *context) +{ + context->count[0] = context->count[1] = 0; + +/* Load magic initialization constants.*/ +context->state[0] = 0x67452301; +context->state[1] = 0xefcdab89; +context->state[2] = 0x98badcfe; +context->state[3] = 0x10325476; +} + +/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */ +void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3)) + context->count[1]++; + + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible.*/ + if (inputLen >= partLen) + { + memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD4Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD4Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); +} + + +/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */ +void MD4Final (unsigned char digest[16], MD4_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64.*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD4Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD4Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information.*/ + memset ((POINTER)context, 0, sizeof (*context)); +} + + +/* MD4 basic transformation. Transforms state based on block. */ +static void MD4Transform (UINT4 state[4], unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + +/* Round 1 */ +FF (a, b, c, d, x[ 0], S11); /* 1 */ +FF (d, a, b, c, x[ 1], S12); /* 2 */ +FF (c, d, a, b, x[ 2], S13); /* 3 */ +FF (b, c, d, a, x[ 3], S14); /* 4 */ +FF (a, b, c, d, x[ 4], S11); /* 5 */ +FF (d, a, b, c, x[ 5], S12); /* 6 */ +FF (c, d, a, b, x[ 6], S13); /* 7 */ +FF (b, c, d, a, x[ 7], S14); /* 8 */ +FF (a, b, c, d, x[ 8], S11); /* 9 */ +FF (d, a, b, c, x[ 9], S12); /* 10 */ +FF (c, d, a, b, x[10], S13); /* 11 */ +FF (b, c, d, a, x[11], S14); /* 12 */ +FF (a, b, c, d, x[12], S11); /* 13 */ +FF (d, a, b, c, x[13], S12); /* 14 */ +FF (c, d, a, b, x[14], S13); /* 15 */ +FF (b, c, d, a, x[15], S14); /* 16 */ + +/* Round 2 */ +GG (a, b, c, d, x[ 0], S21); /* 17 */ +GG (d, a, b, c, x[ 4], S22); /* 18 */ +GG (c, d, a, b, x[ 8], S23); /* 19 */ +GG (b, c, d, a, x[12], S24); /* 20 */ +GG (a, b, c, d, x[ 1], S21); /* 21 */ +GG (d, a, b, c, x[ 5], S22); /* 22 */ +GG (c, d, a, b, x[ 9], S23); /* 23 */ +GG (b, c, d, a, x[13], S24); /* 24 */ +GG (a, b, c, d, x[ 2], S21); /* 25 */ +GG (d, a, b, c, x[ 6], S22); /* 26 */ +GG (c, d, a, b, x[10], S23); /* 27 */ +GG (b, c, d, a, x[14], S24); /* 28 */ +GG (a, b, c, d, x[ 3], S21); /* 29 */ +GG (d, a, b, c, x[ 7], S22); /* 30 */ +GG (c, d, a, b, x[11], S23); /* 31 */ +GG (b, c, d, a, x[15], S24); /* 32 */ + +/* Round 3 */ +HH (a, b, c, d, x[ 0], S31); /* 33 */ +HH (d, a, b, c, x[ 8], S32); /* 34 */ +HH (c, d, a, b, x[ 4], S33); /* 35 */ +HH (b, c, d, a, x[12], S34); /* 36 */ +HH (a, b, c, d, x[ 2], S31); /* 37 */ +HH (d, a, b, c, x[10], S32); /* 38 */ +HH (c, d, a, b, x[ 6], S33); /* 39 */ +HH (b, c, d, a, x[14], S34); /* 40 */ +HH (a, b, c, d, x[ 1], S31); /* 41 */ +HH (d, a, b, c, x[ 9], S32); /* 42 */ +HH (c, d, a, b, x[ 5], S33); /* 43 */ +HH (b, c, d, a, x[13], S34); /* 44 */ +HH (a, b, c, d, x[ 3], S31); /* 45 */ +HH (d, a, b, c, x[11], S32); /* 46 */ +HH (c, d, a, b, x[ 7], S33); /* 47 */ +HH (b, c, d, a, x[15], S34); /* 48 */ + +state[0] += a; +state[1] += b; +state[2] += c; +state[3] += d; + + /* Zeroize sensitive information.*/ + memset ((POINTER)x, 0, sizeof (x)); +} + + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ +static void Encode (unsigned char *output, UINT4 *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ +static void Decode (UINT4 *output, unsigned char *input, unsigned int len) +{ +unsigned int i, j; + +for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +//=================================================================== + +unsigned Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned val; + MD4_CTX ctx; + + MD4Init (&ctx); + MD4Update (&ctx, (unsigned char *)buffer, length); + MD4Final ( (unsigned char *)digest, &ctx); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} + +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf) +{ + MD4_CTX ctx; + + MD4Init (&ctx); + MD4Update (&ctx, (unsigned char *)buffer, len); + MD4Final ( outbuf, &ctx); +} diff --git a/engine/common/md5.c b/engine/common/md5.c new file mode 100644 index 000000000..c78749db7 --- /dev/null +++ b/engine/common/md5.c @@ -0,0 +1,422 @@ +//used by pop3. + + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "quakedef.h" + +#if defined(EMAILCLIENT) || defined(EMAILSERVER) + +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. +The following makes PROTOTYPES default to 0 if it has not already + + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + + + + + + + + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + + + + + + + + + + + + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + +*/ + memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + + + + + + +char *MD5_GetPop3APOPString(char *timestamp, char *secrit) +{ + static char ret[64]; + int digest[4]; + MD5_CTX ctx; + + MD5Init (&ctx); + MD5Update (&ctx, (unsigned char *)timestamp, strlen(timestamp)); + MD5Update (&ctx, (unsigned char *)secrit, strlen(secrit)); + MD5Final ( (unsigned char *)digest, &ctx); + + sprintf(ret, "%08x%08x%08x%08x", BigLong(digest[0]), BigLong(digest[1]), BigLong(digest[2]), BigLong(digest[3])); + return ret; +} + +#endif diff --git a/engine/common/net.h b/engine/common/net.h new file mode 100644 index 000000000..0a07f0e95 --- /dev/null +++ b/engine/common/net.h @@ -0,0 +1,165 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// net.h -- quake's interface to the networking layer + +#define PORT_ANY -1 + +typedef enum {NA_INVALID, NA_LOOPBACK, NA_IP, NA_IPV6, NA_IPX, NA_BROADCAST_IP, NA_BROADCAST_IP6, NA_BROADCAST_IPX} netadrtype_t; + +typedef enum {NS_CLIENT, NS_SERVER} netsrc_t; + +typedef struct +{ + netadrtype_t type; + + union { + qbyte ip[4]; + qbyte ip6[16]; + qbyte ipx[10]; + }; + + unsigned short port; +} netadr_t; + +struct sockaddr_qstorage +{ + short sa_family; + unsigned char sa_pad[6]; +#if defined(_MSC_VER) || defined(MINGW) + __int64 sa_align; +#else + int sa_align[2]; +#endif + unsigned char sa_pad2[112]; +}; + + +extern netadr_t net_local_ipadr; +extern netadr_t net_from; // address of who sent the packet +extern sizebuf_t net_message; +//#define MAX_UDP_PACKET (MAX_MSGLEN*2) // one more than msg + header +#define MAX_UDP_PACKET 8192 // one more than msg + header +extern qbyte net_message_buffer[MAX_UDP_PACKET]; + +extern cvar_t hostname; + +void NET_Init (void); +void NET_InitClient (void); +void NET_InitServer (void); +void NET_CloseServer (void); +void UDP_CloseSocket (int socket); +void NET_Shutdown (void); +qboolean NET_GetPacket (netsrc_t socket); +void NET_SendPacket (netsrc_t socket, int length, void *data, netadr_t to); + +qboolean NET_CompareAdr (netadr_t a, netadr_t b); +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); +char *NET_AdrToString (netadr_t a); +char *NET_BaseAdrToString (netadr_t a); +qboolean NET_StringToAdr (char *s, netadr_t *a); +qboolean NET_IsClientLegal(netadr_t *adr); + +qboolean NET_IsLoopBackAddress (netadr_t adr); + +//============================================================================ + +#define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) + +#define MAX_LATENT 32 + +typedef struct +{ + qboolean fatal_error; + +#ifdef NQPROT + qboolean isnqprotocol; + struct qsocket_s *qsocket; +#endif + + float last_received; // for timeouts + +// the statistics are cleared at each client begin, because +// the server connecting process gives a bogus picture of the data + float frame_latency; // rolling average + float frame_rate; + + int drop_count; // dropped packets, cleared each level + int good_count; // cleared each level + + netadr_t remote_address; + netsrc_t sock; + int qport; + +// bandwidth estimator + double cleartime; // if realtime > nc->cleartime, free to go + double rate; // seconds / qbyte + +// sequencing variables + int incoming_sequence; + int incoming_acknowledged; + int incoming_reliable_acknowledged; // single bit + + int incoming_reliable_sequence; // single bit, maintained local + + int outgoing_sequence; + int reliable_sequence; // single bit + int last_reliable_sequence; // sequence number of last send + +// reliable staging and holding areas + sizebuf_t message; // writing buffer to send to server + qbyte message_buf[MAX_OVERALLMSGLEN]; + + int reliable_length; + qbyte reliable_buf[MAX_OVERALLMSGLEN]; // unacked reliable message + +// time and size data to calculate bandwidth + int outgoing_size[MAX_LATENT]; + double outgoing_time[MAX_LATENT]; + qboolean compress; +} netchan_t; + +extern int net_drop; // packets dropped before this one + +void Netchan_Init (void); +void Netchan_Transmit (netchan_t *chan, int length, qbyte *data); +void Netchan_OutOfBand (netsrc_t sock, netadr_t adr, int length, qbyte *data); +void VARGS Netchan_OutOfBandPrint (netsrc_t sock, netadr_t adr, char *format, ...); +void VARGS Netchan_OutOfBandTPrintf (netsrc_t sock, netadr_t adr, int language, translation_t text, ...); +qboolean Netchan_Process (netchan_t *chan); +void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t adr, int qport); + +qboolean Netchan_CanPacket (netchan_t *chan); +qboolean Netchan_CanReliable (netchan_t *chan); + +#ifdef HUFFNETWORK +int Huff_PreferedCompressionCRC (void); +qboolean Huff_CompressionCRC(int crc); +void Huff_CompressPacket(sizebuf_t *msg, int offset); +void Huff_DecompressPacket(sizebuf_t *msg, int offset); +#endif + +#ifdef NQPROT +#include "../nqnet/nqnet.h" +#endif + +int UDP_OpenSocket (int port, qboolean bcast); +int IPX_OpenSocket (int port, qboolean bcast); +void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a); +qboolean NET_Sleep(int msec, qboolean stdinissocket); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c new file mode 100644 index 000000000..a339b338b --- /dev/null +++ b/engine/common/net_chan.c @@ -0,0 +1,534 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "quakedef.h" + +#ifdef _WIN32 +#include "winquake.h" +#else +#include "unistd.h" +#endif + +#define PACKET_HEADER 8 + +/* + +packet header +------------- +31 sequence +1 does this message contain a reliable payload +31 acknowledge sequence +1 acknowledge receipt of even/odd message +16 qport + +The remote connection never knows if it missed a reliable message, the +local side detects that it has been dropped by seeing a sequence acknowledge +higher thatn the last reliable sequence, but without the correct evon/odd +bit for the reliable set. + +If the sender notices that a reliable message has been dropped, it will be +retransmitted. It will not be retransmitted again until a message after +the retransmit has been acknowledged and the reliable still failed to get there. + +if the sequence number is -1, the packet should be handled without a netcon + +The reliable message can be added to at any time by doing +MSG_Write* (&netchan->message, ). + +If the message buffer is overflowed, either by a single message, or by +multiple frames worth piling up while the last reliable transmit goes +unacknowledged, the netchan signals a fatal error. + +Reliable messages are allways placed first in a packet, then the unreliable +message is included if there is sufficient room. + +To the receiver, there is no distinction between the reliable and unreliable +parts of the message, they are just processed out as a single larger message. + +Illogical packet sequence numbers cause the packet to be dropped, but do +not kill the connection. This, combined with the tight window of valid +reliable acknowledgement numbers provides protection against malicious +address spoofing. + +The qport field is a workaround for bad address translating routers that +sometimes remap the client's source port on a packet during gameplay. + +If the base part of the net address matches and the qport matches, then the +channel matches even if the IP port differs. The IP port should be updated +to the new value before sending out any replies. + + +*/ + +int net_drop; +cvar_t showpackets = {"showpackets", "0"}; +cvar_t showdrop = {"showdrop", "0"}; +cvar_t qport = {"qport", "0"}; + +/* +=============== +Netchan_Init + +=============== +*/ +void Netchan_Init (void) +{ + int port; + + // pick a port value that should be nice and random +#ifdef _WIN32 + port = ((int)(timeGetTime()*1000) * time(NULL)) & 0xffff; +#else + port = ((int)(getpid()+getuid()*1000) * time(NULL)) & 0xffff; +#endif + + Cvar_Register (&showpackets, "Networking"); + Cvar_Register (&showdrop, "Networking"); + Cvar_Register (&qport, "Networking"); + Cvar_SetValue(&qport, port); +} + +/* +=============== +Netchan_OutOfBand + +Sends an out-of-band datagram +================ +*/ +void Netchan_OutOfBand (netsrc_t sock, netadr_t adr, int length, qbyte *data) +{ + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN + PACKET_HEADER]; + +// write the packet header + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 0; + + MSG_WriteLong (&send, -1); // -1 sequence means out of band + SZ_Write (&send, data, length); + +// send the datagram + //zoid, no input in demo playback mode +#ifndef SERVERONLY + if (!cls.demoplayback) +#endif + NET_SendPacket (sock, send.cursize, send.data, adr); +} + +/* +=============== +Netchan_OutOfBandPrint + +Sends a text message in an out-of-band datagram +================ +*/ +void VARGS Netchan_OutOfBandPrint (netsrc_t sock, netadr_t adr, char *format, ...) +{ + va_list argptr; + static char string[8192]; // ??? why static? + + va_start (argptr, format); + _vsnprintf (string,sizeof(string)-1, format,argptr); + va_end (argptr); + + + Netchan_OutOfBand (sock, adr, strlen(string), (qbyte *)string); +} +#ifndef CLIENTONLY +void VARGS Netchan_OutOfBandTPrintf (netsrc_t sock, netadr_t adr, int language, translation_t text, ...) +{ + va_list argptr; + static char string[8192]; // ??? why static? + + char *format = langtext(text, language); + + string[0] = A2C_PRINT; + + va_start (argptr, text); + _vsnprintf (string+1,sizeof(string)-2, format,argptr); + va_end (argptr); + + + Netchan_OutOfBand (sock, adr, strlen(string), (qbyte *)string); +} +#endif +/* +============== +Netchan_Setup + +called to open a channel to a remote system +============== +*/ +void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t adr, int qport) +{ + memset (chan, 0, sizeof(*chan)); +#ifdef NQPROT + chan->qsocket = NULL; +#endif + chan->sock = sock; + chan->remote_address = adr; + chan->last_received = realtime; + + chan->message.data = chan->message_buf; + chan->message.allowoverflow = true; + chan->message.maxsize = MAX_QWMSGLEN;//sizeof(chan->message_buf); + + chan->qport = qport; + + chan->rate = 1.0/2500; +} + + +/* +=============== +Netchan_CanPacket + +Returns true if the bandwidth choke isn't active +================ +*/ +#define MAX_BACKUP 200 +qboolean Netchan_CanPacket (netchan_t *chan) +{ + if (chan->remote_address.type == NA_LOOPBACK) + return true; //don't ever drop packets due to possible routing problems when there is no routing. + if (chan->cleartime < realtime + MAX_BACKUP*chan->rate) + return true; + return false; +} + + +/* +=============== +Netchan_CanReliable + +Returns true if the bandwidth choke isn't +================ +*/ +qboolean Netchan_CanReliable (netchan_t *chan) +{ + if (chan->reliable_length) + return false; // waiting for ack + return Netchan_CanPacket (chan); +} + +#ifdef SERVERONLY +qboolean ServerPaused(void); +#endif + +/* +=============== +Netchan_Transmit + +tries to send an unreliable message to a connection, and handles the +transmition / retransmition of the reliable messages. + +A 0 length will still generate a packet and deal with the reliable messages. +================ +*/ +void Netchan_Transmit (netchan_t *chan, int length, qbyte *data) +{ + sizebuf_t send; + qbyte send_buf[MAX_OVERALLMSGLEN + PACKET_HEADER]; + qboolean send_reliable; + unsigned w1, w2; + int i; + +#ifdef NQPROT + if (chan->qsocket && !chan->qsocket->qwprotocol) + { + if (!NET_CanSendMessage (chan->qsocket)) + return; + if (!chan->reliable_length && chan->message.cursize) + { + memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); + chan->reliable_length = chan->message.cursize; + chan->message.cursize = 0; + chan->reliable_sequence ^= 1; + send_reliable = true; + } + + send.data = send_buf; + send.maxsize = MAX_NQMSGLEN + PACKET_HEADER; + send.cursize = 0; + + chan->outgoing_sequence++; + chan->last_reliable_sequence = chan->outgoing_sequence; + + SZ_Write (&send, chan->reliable_buf, chan->reliable_length); + + SZ_Write (&send, data, length); + + NET_SendMessage(chan->qsocket, &send); + + chan->message.cursize = 0; + chan->reliable_length = 0; + return; + } +#endif + +// check for message overflow + if (chan->message.overflowed) + { + chan->fatal_error = true; + Con_TPrintf (TL_OUTMESSAGEOVERFLOW + , NET_AdrToString (chan->remote_address)); + return; + } + +// if the remote side dropped the last reliable message, resend it + send_reliable = false; + + if (chan->incoming_acknowledged > chan->last_reliable_sequence + && chan->incoming_reliable_acknowledged != chan->reliable_sequence) + send_reliable = true; + +// if the reliable transmit buffer is empty, copy the current message out + if (!chan->reliable_length && chan->message.cursize) + { + memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); + chan->reliable_length = chan->message.cursize; + chan->message.cursize = 0; + chan->reliable_sequence ^= 1; + send_reliable = true; + } + +// write the packet header + send.data = send_buf; + send.maxsize = MAX_QWMSGLEN + PACKET_HEADER; //dmw wasn't quite true. + send.cursize = 0; + + w1 = chan->outgoing_sequence | (send_reliable<<31); + w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31); + + chan->outgoing_sequence++; + + MSG_WriteLong (&send, w1); + MSG_WriteLong (&send, w2); + + // send the qport if we are a client +#ifndef SERVERONLY + if (chan->sock == NS_CLIENT) + MSG_WriteShort (&send, cls.qport); +#endif + +// copy the reliable message to the packet first + if (send_reliable) + { + SZ_Write (&send, chan->reliable_buf, chan->reliable_length); + chan->last_reliable_sequence = chan->outgoing_sequence; + } + +// add the unreliable part if space is available + if (send.maxsize - send.cursize >= length) + SZ_Write (&send, data, length); + +// send the datagram + i = chan->outgoing_sequence & (MAX_LATENT-1); + chan->outgoing_size[i] = send.cursize; + chan->outgoing_time[i] = realtime; + +#ifdef HUFFNETWORK + if (chan->compress) + { + int oldsize = send.cursize; + Huff_CompressPacket(&send, (chan->sock == NS_CLIENT)?10:8); + Con_Printf("%i becomes %i\n", oldsize, send.cursize); +// Huff_DecompressPacket(&send, (chan->sock == NS_CLIENT)?10:8); + } +#endif + + //zoid, no input in demo playback mode +#ifndef SERVERONLY + if (!cls.demoplayback) +#endif + { +#ifdef NQPROT + if (chan->qsocket) + NET_SendUnreliableMessage(chan->qsocket, &send); //qw protocol adds it's own reliability. + else +#endif + NET_SendPacket (chan->sock, send.cursize, send.data, chan->remote_address); + } + + if (chan->cleartime < realtime) + chan->cleartime = realtime + send.cursize*chan->rate; + else + chan->cleartime += send.cursize*chan->rate; +#ifdef SERVERONLY + if (ServerPaused()) + chan->cleartime = realtime; +#endif + + if (showpackets.value) + Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n" + , chan->outgoing_sequence + , send_reliable + , chan->incoming_sequence + , chan->incoming_reliable_sequence + , send.cursize); + +} + +/* +================= +Netchan_Process + +called when the current net_message is from remote_address +modifies net_message so that it points to the packet payload +================= +*/ +qboolean Netchan_Process (netchan_t *chan) +{ + unsigned sequence, sequence_ack; + unsigned reliable_ack, reliable_message; +#ifndef CLIENTONLY + int qport; +#endif + + if ( +#ifndef SERVERONLY + !cls.demoplayback && +#endif + !NET_CompareAdr (net_from, chan->remote_address)) + return false; + +// get sequence numbers + MSG_BeginReading (); + sequence = MSG_ReadLong (); + sequence_ack = MSG_ReadLong (); + + // read the qport if we are a server +#ifndef CLIENTONLY + if (chan->sock == NS_SERVER) + qport = MSG_ReadShort (); +#endif + + reliable_message = sequence >> 31; + reliable_ack = sequence_ack >> 31; + + sequence &= ~(1<<31); + sequence_ack &= ~(1<<31); + + if (showpackets.value) + Con_Printf ("<-- s=%i(%i) a=%i(%i) %i\n" + , sequence + , reliable_message + , sequence_ack + , reliable_ack + , net_message.cursize); + +// get a rate estimation +#if 0 + if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) + { + int i; + double time, rate; + + i = sequence_ack & (MAX_LATENT - 1); + time = realtime - chan->outgoing_time[i]; + time -= 0.1; // subtract 100 ms + if (time <= 0) + { // gotta be a digital link for <100 ms ping + if (chan->rate > 1.0/5000) + chan->rate = 1.0/5000; + } + else + { + if (chan->outgoing_size[i] < 512) + { // only deal with small messages + rate = chan->outgoing_size[i]/time; + if (rate > 5000) + rate = 5000; + rate = 1.0/rate; + if (chan->rate > rate) + chan->rate = rate; + } + } + } +#endif + +// +// discard stale or duplicated packets +// + if (sequence <= (unsigned)chan->incoming_sequence) + { + if (showdrop.value) + Con_TPrintf (TL_OUTOFORDERPACKET + , NET_AdrToString (chan->remote_address) + , sequence + , chan->incoming_sequence); + return false; + } + +// +// dropped packets don't keep the message from being used +// + net_drop = sequence - (chan->incoming_sequence+1); + if (net_drop > 0) + { + chan->drop_count += 1; + + if (showdrop.value) + Con_TPrintf (TL_DROPPEDPACKETCOUNT + , NET_AdrToString (chan->remote_address) + , sequence-(chan->incoming_sequence+1) + , sequence); + } + +// +// if the current outgoing reliable message has been acknowledged +// clear the buffer to make way for the next +// + if (reliable_ack == (unsigned)chan->reliable_sequence) + chan->reliable_length = 0; // it has been received + +// +// if this message contains a reliable message, bump incoming_reliable_sequence +// + chan->incoming_sequence = sequence; + chan->incoming_acknowledged = sequence_ack; + chan->incoming_reliable_acknowledged = reliable_ack; + if (reliable_message) + chan->incoming_reliable_sequence ^= 1; + +// +// the message can now be read from the current message pointer +// update statistics counters +// + chan->frame_latency = chan->frame_latency*OLD_AVG + + (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG); + chan->frame_rate = chan->frame_rate*OLD_AVG + + (realtime-chan->last_received)*(1.0-OLD_AVG); + chan->good_count += 1; + + chan->last_received = realtime; + +#ifdef HUFFNETWORK + if (chan->compress) + { +// Huff_CompressPacket(&net_message, (chan->sock == NS_SERVER)?10:8); + Huff_DecompressPacket(&net_message, (chan->sock == NS_SERVER)?10:8); + } +#endif + + return true; +} + diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c new file mode 100644 index 000000000..784494f2f --- /dev/null +++ b/engine/common/net_wins.c @@ -0,0 +1,1260 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// net_wins.c +struct sockaddr; + +#include "quakedef.h" +#ifdef _WIN32 +#ifdef _MSC_VER +#define USEIPX +#endif +#include "winquake.h" +#ifdef USEIPX +#include "wsipx.h" +#endif +#ifdef IPPROTO_IPV6 +#include "ws2tcpip.h" +#endif + +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EMSGSIZE WSAEMSGSIZE +#define ECONNRESET WSAECONNRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNREFUSED WSAECONNREFUSED +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define EAFNOSUPPORT WSAEAFNOSUPPORT + +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef sun +#include +#endif + +#ifdef NeXT +#include +#endif + +#define closesocket close +#define ioctlsocket ioctl + +#endif + +#ifdef _WIN32 +#define qerrno WSAGetLastError() //windows errors are retrieved via WSAGetLastError +#else +#define qerrno errno //linux and single threaded oses are happy with errno as a global +#endif + +netadr_t net_local_ipadr; +netadr_t net_local_ip6adr; + +netadr_t net_from; +sizebuf_t net_message; + +//#define MAX_UDP_PACKET (MAX_MSGLEN*2) // one more than msg + header +#define MAX_UDP_PACKET 8192 // one more than msg + header +qbyte net_message_buffer[MAX_UDP_PACKET]; +#ifdef _WIN32 +WSADATA winsockdata; +#endif + +#ifdef IPPROTO_IPV6 +#ifdef _WIN32 +int (WINAPI *pgetaddrinfo) ( + const char* nodename, + const char* servname, + const struct addrinfo* hints, + struct addrinfo** res +); +void (WSAAPI *pfreeaddrinfo) (struct addrinfo*); +#else +#define pgetaddrinfo getaddrinfo +#define pfreeaddrinfo freeaddrinfo +/*int (*pgetaddrinfo) +( + const char* nodename, + const char* servname, + const struct addrinfo* hints, + struct addrinfo** res +); +void (*pfreeaddrinfo) (struct addrinfo*); +*/ +#endif +#endif + + +extern cvar_t sv_public; + + + +#define MAX_LOOPBACK 4 +typedef struct +{ + qbyte data[MAX_UDP_PACKET]; + int datalen; +} loopmsg_t; + +typedef struct +{ + loopmsg_t msgs[MAX_LOOPBACK]; + int get, send; +} loopback_t; + +loopback_t loopbacks[2]; +//============================================================================= + +void NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) +{ + switch(a->type) + { + case NA_BROADCAST_IP: + memset (s, 0, sizeof(struct sockaddr_in)); + ((struct sockaddr_in*)s)->sin_family = AF_INET; + + *(int *)&((struct sockaddr_in*)s)->sin_addr = INADDR_BROADCAST; + ((struct sockaddr_in*)s)->sin_port = a->port; + break; + + case NA_IP: + memset (s, 0, sizeof(struct sockaddr_in)); + ((struct sockaddr_in*)s)->sin_family = AF_INET; + + *(int *)&((struct sockaddr_in*)s)->sin_addr = *(int *)&a->ip; + ((struct sockaddr_in*)s)->sin_port = a->port; + break; +#ifdef IPPROTO_IPV6 + case NA_BROADCAST_IP6: + memset (s, 0, sizeof(struct sockaddr_in)); + ((struct sockaddr_in6*)s)->sin6_family = AF_INET6; + + memset((int *)&((struct sockaddr_in6*)s)->sin6_addr, 0, sizeof(*(int *)&((struct sockaddr_in6*)s)->sin6_addr)); + ((struct sockaddr_in6*)s)->sin6_addr.s6_addr[0] = 0xff; + ((struct sockaddr_in6*)s)->sin6_addr.s6_addr[1] = 0x02; + ((struct sockaddr_in6*)s)->sin6_addr.s6_addr[15] = 0x01; + ((struct sockaddr_in6*)s)->sin6_port = a->port; + break; + + case NA_IPV6: + memset (s, 0, sizeof(struct sockaddr_in)); + ((struct sockaddr_in6*)s)->sin6_family = AF_INET6; + + memcpy(&((struct sockaddr_in6*)s)->sin6_addr, &in6addr_any, sizeof(in6addr_any)); + ((struct sockaddr_in6*)s)->sin6_port = a->port; + break; +#endif +#ifdef USEIPX + case NA_IPX: + ((struct sockaddr_ipx *)s)->sa_family = AF_IPX; + memcpy(((struct sockaddr_ipx *)s)->sa_netnum, &a->ipx[0], 4); + memcpy(((struct sockaddr_ipx *)s)->sa_nodenum, &a->ipx[4], 6); + ((struct sockaddr_ipx *)s)->sa_socket = a->port; + break; + case NA_BROADCAST_IPX: + memset (s, 0, sizeof(struct sockaddr_ipx)); + ((struct sockaddr_ipx*)s)->sa_family = AF_IPX; + memset(&((struct sockaddr_ipx*)s)->sa_netnum, 0, 4); + memset(&((struct sockaddr_ipx*)s)->sa_nodenum, 0xff, 6); + ((struct sockaddr_ipx*)s)->sa_socket = a->port; + break; +#endif + default: + Sys_Error("Bad type - needs fixing"); + } +} + +void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a) +{ + switch (s->sa_family) + { + case AF_INET: + a->type = NA_IP; + *(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; + a->port = ((struct sockaddr_in *)s)->sin_port; + break; +#ifdef IPPROTO_IPV6 + case AF_INET6: + a->type = NA_IPV6; + memcpy((struct sockaddr_in6 *)&a->ip6, &((struct sockaddr_in6 *)s)->sin6_addr, sizeof(struct sockaddr_in6)); + a->port = ((struct sockaddr_in6 *)s)->sin6_port; + break; +#endif +#ifdef USEIPX + case AF_IPX: + a->type = NA_IPX; + *(int *)a->ip = 0xffffffff; + memcpy(&a->ipx[0], ((struct sockaddr_ipx *)s)->sa_netnum, 4); + memcpy(&a->ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6); + a->port = ((struct sockaddr_ipx *)s)->sa_socket; + break; +#endif + case AF_UNSPEC: + memset(a, 0, sizeof(*a)); + a->type = NA_INVALID; + break; + default: + Sys_Error("SockadrToNetadr: bad socket family"); + } +} + +qboolean NET_CompareAdr (netadr_t a, netadr_t b) +{ + if (a.type != b.type) + return false; + + if (a.type == NA_LOOPBACK) + return true; + + if (a.type == NA_IP || a.type == NA_BROADCAST_IP) + { + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) + return true; + return false; + } + +#ifdef IPPROTO_IPV6 + if (a.type == NA_IPV6 || a.type == NA_BROADCAST_IP6) + { + if ((memcmp(a.ip6, b.ip6, 16) == 0) && a.port == b.port) + return true; + return false; + } +#endif + + if (a.type == NA_IPX || a.type == NA_BROADCAST_IPX) + { + if ((memcmp(a.ipx, b.ipx, 10) == 0) && a.port == b.port) + return true; + return false; + } + + Sys_Error("NET_CompareAdr: Bad address type"); + return false; +} + +/* +=================== +NET_CompareBaseAdr + +Compares without the port +=================== +*/ +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + if (a.type != b.type) + return false; + + if (a.type == NA_LOOPBACK) + return true; + + if (a.type == NA_IP) + { + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) + return true; + return false; + } +#ifdef IPPROTO_IPV6 + if (a.type == NA_IPV6 || a.type == NA_BROADCAST_IP6) + { + if ((memcmp(a.ip6, b.ip6, 16) == 0)) + return true; + return false; + } +#endif + if (a.type == NA_IPX) + { + if ((memcmp(a.ipx, b.ipx, 10) == 0)) + return true; + return false; + } + + Sys_Error("NET_CompareBaseAdr: Bad address type"); + return false; +} + +char *NET_AdrToString (netadr_t a) +{ + static char s[64]; + + switch(a.type) + { + case NA_BROADCAST_IP: + case NA_IP: + sprintf (s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port)); + break; +#ifdef IPPROTO_IPV6 + case NA_BROADCAST_IP6: + case NA_IPV6: + sprintf (s, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%i", a.ip6[0], a.ip6[1], a.ip6[2], a.ip6[3], a.ip6[4], a.ip6[5], a.ip6[6], a.ip6[7], a.ip6[8], a.ip6[9], a.ip6[10], a.ip6[11], a.ip6[12], a.ip6[13], a.ip6[14], a.ip6[15], ntohs(a.port)); + break; +#endif +#ifdef USEIPX + case NA_BROADCAST_IPX: + case NA_IPX: + sprintf (s, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], ntohs(a.port)); + break; +#endif + case NA_LOOPBACK: + sprintf (s, "LocalHost"); + break; + default: + sprintf (s, "invalid netadr_t type"); +// Sys_Error("NET_AdrToString: Bad netadr_t type"); + } + + return s; +} + +char *NET_BaseAdrToString (netadr_t a) +{ + static char s[64]; + + switch(a.type) + { + case NA_BROADCAST_IP: + case NA_IP: + sprintf (s, "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); + break; +#ifdef IPPROTO_IPV6 + case NA_BROADCAST_IP6: + case NA_IPV6: + sprintf (s, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", a.ip6[0], a.ip6[1], a.ip6[2], a.ip6[3], a.ip6[4], a.ip6[5], a.ip6[6], a.ip6[7], a.ip6[8], a.ip6[9], a.ip6[10], a.ip6[11], a.ip6[12], a.ip6[13], a.ip6[14], a.ip6[15]); + break; +#endif +#ifdef USEIPX + case NA_BROADCAST_IPX: + case NA_IPX: + sprintf (s, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9]); + break; +#endif + case NA_LOOPBACK: + sprintf (s, "LocalHost"); + break; + default: + Sys_Error("NET_BaseAdrToString: Bad netadr_t type"); + } + + return s; +} + +/* +============= +NET_StringToAdr + +idnewt +idnewt:28000 +192.246.40.70 +192.246.40.70:28000 +any form of ipv6, including port number. +============= +*/ +#define DO(src,dest) \ + copy[0] = s[src]; \ + copy[1] = s[src + 1]; \ + sscanf (copy, "%x", &val); \ + ((struct sockaddr_ipx *)sadr)->dest = val + +qboolean NET_StringToSockaddr (char *s, struct sockaddr_qstorage *sadr) +{ + struct hostent *h; + char *colon; + char copy[128]; + + memset (sadr, 0, sizeof(*sadr)); + +#ifdef USEIPX + if ((strlen(s) >= 23) && (s[8] == ':') && (s[21] == ':')) // check for an IPX address + { + unsigned int val; + + ((struct sockaddr_ipx *)sadr)->sa_family = AF_IPX; + copy[2] = 0; + DO(0, sa_netnum[0]); + DO(2, sa_netnum[1]); + DO(4, sa_netnum[2]); + DO(6, sa_netnum[3]); + DO(9, sa_nodenum[0]); + DO(11, sa_nodenum[1]); + DO(13, sa_nodenum[2]); + DO(15, sa_nodenum[3]); + DO(17, sa_nodenum[4]); + DO(19, sa_nodenum[5]); + sscanf (&s[22], "%u", &val); + ((struct sockaddr_ipx *)sadr)->sa_socket = htons((unsigned short)val); + } + else +#endif +#ifdef IPPROTO_IPV6 + if (pgetaddrinfo) + { + struct addrinfo *addrinfo, *pos; + struct addrinfo udp6hint; + int error; + char *port; + + memset(&udp6hint, 0, sizeof(udp6hint)); + udp6hint.ai_family = 0;//Any... AF_INET6; + udp6hint.ai_socktype = SOCK_DGRAM; + udp6hint.ai_protocol = IPPROTO_UDP; + + port = s + strlen(s); + while(port >= s) + { + if (*port == ':') + break; + port--; + } + + if (port == s) + port = NULL; + if (port) + { + *port = '\0'; + error = pgetaddrinfo(s, port+1, &udp6hint, &addrinfo); + *port = ':'; + } + else + error = EAI_NONAME; + if (error) //failed, try string with no port. + error = pgetaddrinfo(s, NULL, &udp6hint, &addrinfo); //remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6) + if (error) + { + return false; + } + sadr->sa_family = 0; + for (pos = addrinfo; pos; pos = pos->ai_next) + { + switch(pos->ai_family) + { + case AF_INET6: + if (((struct sockaddr_in *)sadr)->sin_family == AF_INET6) + break; //first one should be best... + case AF_INET: + memcpy(sadr, addrinfo->ai_addr, addrinfo->ai_addrlen); + if (pos->ai_family == AF_INET) + goto dblbreak; //don't try finding any more, this is quake, they probably prefer ip4... + break; + } + } +dblbreak: + pfreeaddrinfo (addrinfo); + if (!sadr->sa_family) + return false; + } + else +#endif + { + ((struct sockaddr_in *)sadr)->sin_family = AF_INET; + + ((struct sockaddr_in *)sadr)->sin_port = 0; + + strcpy (copy, s); + // strip off a trailing :port if present + for (colon = copy ; *colon ; colon++) + if (*colon == ':') + { + *colon = 0; + ((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1)); + } + + if (copy[0] >= '0' && copy[0] <= '9') //this is the wrong way to test. a server name may start with a number. + { + *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy); + } + else + { + if (! (h = gethostbyname(copy)) ) + return 0; + if (h->h_addrtype != AF_INET) + return 0; + *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; + } + } + + return true; +} + +#undef DO + +qboolean NET_StringToAdr (char *s, netadr_t *a) +{ + struct sockaddr_qstorage sadr; + + if (!strcmp (s, "internalserver")) + { + memset (a, 0, sizeof(*a)); + a->type = NA_LOOPBACK; + return true; + } + + if (!NET_StringToSockaddr (s, &sadr)) + return false; + + SockadrToNetadr (&sadr, a); + + return true; +} + +// Returns true if we can't bind the address locally--in other words, +// the IP is NOT one of our interfaces. +qboolean NET_IsClientLegal(netadr_t *adr) +{ +#if 0 + struct sockaddr_in sadr; + int newsocket; + + if (adr->ip[0] == 127) + return false; // no local connections period + + NetadrToSockadr (adr, &sadr); + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("NET_IsClientLegal: socket:", strerror(qerrno)); + + sadr.sin_port = 0; + + if( bind (newsocket, (void *)&sadr, sizeof(sadr)) == -1) + { + // It is not a local address + close(newsocket); + return true; + } + close(newsocket); + return false; +#else + return true; +#endif +} + +qboolean NET_IsLoopBackAddress (netadr_t adr) +{ +// return (!strcmp(cls.servername, NET_AdrToString(net_local_adr)) || !strcmp(cls.servername, "local"); + return adr.type == NA_LOOPBACK; +} + +///////////////////////////////////////////// +//loopback stuff + +qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *from, sizebuf_t *message) +{ + int i; + loopback_t *loop; + + loop = &loopbacks[sock]; + + if (loop->send - loop->get > MAX_LOOPBACK) + loop->get = loop->send - MAX_LOOPBACK; + + if (loop->get >= loop->send) + return false; + + i = loop->get & (MAX_LOOPBACK-1); + loop->get++; + + if (message->maxsize < loop->msgs[i].datalen) + Sys_Error("NET_SendLoopPacket: Loopback buffer was too big"); + + memcpy (message->data, loop->msgs[i].data, loop->msgs[i].datalen); + message->cursize = loop->msgs[i].datalen; + memset (from, 0, sizeof(*from)); + from->type = NA_LOOPBACK; + return true; + +} + + +void NET_SendLoopPacket (netsrc_t sock, int length, void *data, netadr_t to) +{ + int i; + loopback_t *loop; + + loop = &loopbacks[sock^1]; + + i = loop->send & (MAX_LOOPBACK-1); + loop->send++; + + if (length > sizeof(loop->msgs[i].data)) + Sys_Error("NET_SendLoopPacket: Loopback buffer is too small"); + + memcpy (loop->msgs[i].data, data, length); + loop->msgs[i].datalen = length; +} +//============================================================================= + +qboolean NET_GetPacket (netsrc_t netsrc) +{ + int ret; + struct sockaddr_qstorage from; + int fromlen; + int i; + int socket; + + if (NET_GetLoopPacket(netsrc, &net_from, &net_message)) + return true; + + for (i = 0; i < 3; i++) + { + if (netsrc == NS_SERVER) + { +#ifdef CLIENTONLY + Sys_Error("NET_GetPacket: Bad netsrc"); + socket = 0; +#else + if (i == 0) + socket = svs.socketip; + else if (i == 1) + socket = svs.socketip6; + else + socket = svs.socketipx; +#endif + } + else + { +#ifdef SERVERONLY + Sys_Error("NET_GetPacket: Bad netsrc"); + socket = 0; +#else + if (i == 0) + socket = cls.socketip; + else if (i == 1) + socket = cls.socketip6; + else + socket = cls.socketipx; +#endif + } + if (!socket) + continue; + + fromlen = sizeof(from); + ret = recvfrom (socket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr*)&from, &fromlen); + + if (ret == -1) + { + if (qerrno == EWOULDBLOCK) + continue; + if (qerrno == EMSGSIZE) + { + SockadrToNetadr (&from, &net_from); + Con_TPrintf (TL_OVERSIZEPACKETFROM, + NET_AdrToString (net_from)); + continue; + } + if (qerrno == ECONNABORTED || qerrno == ECONNRESET) + { + Con_TPrintf (TL_CONNECTIONLOSTORABORTED); //server died/connection lost. +#ifndef SERVERONLY + if (cls.state != ca_disconnected && netsrc == NS_CLIENT) + { + Cbuf_AddText("disconnect\nreconnect\n", RESTRICT_LOCAL); //retry connecting. + break; + } +#endif + continue; + } + + + Con_TPrintf (TL_NETGETPACKETERROR, strerror(qerrno)); + continue; + } + SockadrToNetadr (&from, &net_from); + + net_message.cursize = ret; + if (net_message.cursize == sizeof(net_message_buffer) ) + { + Con_TPrintf (TL_OVERSIZEPACKETFROM, NET_AdrToString (net_from)); + return false; + } + + return ret; + } + return false; +} + +//============================================================================= + +void NET_SendPacket (netsrc_t netsrc, int length, void *data, netadr_t to) +{ + int ret; + struct sockaddr_qstorage addr; + int socket; + + if (to.type == NA_LOOPBACK) + { +// if ((rand()&15)==15) //simulate PL +// return; + NET_SendLoopPacket(netsrc, length, data, to); + return; + } + + if (netsrc == NS_SERVER) + { +#ifdef CLIENTONLY + Sys_Error("NET_SendPacket: bad netsrc"); + socket = 0; +#else +#ifdef USEIPX + if (to.type == NA_BROADCAST_IPX || to.type == NA_IPX) + socket = svs.socketipx; + else +#endif +#ifdef IPPROTO_IPV6 + if (to.type == NA_IPV6) + socket = svs.socketip6; + else +#endif + socket = svs.socketip; +#endif + } + else + { +#ifdef SERVERONLY + Sys_Error("NET_SendPacket: bad netsrc"); + socket = 0; +#else +#ifdef USEIPX + if (to.type == NA_BROADCAST_IPX || to.type == NA_IPX) + socket = cls.socketipx; + else +#endif +#ifdef IPPROTO_IPV6 + if (to.type == NA_BROADCAST_IP6 || to.type == NA_IPV6) + socket = cls.socketip6; + else +#endif + socket = cls.socketip; +#endif + } + + NetadrToSockadr (&to, &addr); + + ret = sendto (socket, data, length, 0, (struct sockaddr*)&addr, sizeof(addr) ); + if (ret == -1) + { +// wouldblock is silent + if (qerrno == EWOULDBLOCK) + return; + + if (qerrno == ECONNREFUSED) + return; + +#ifndef SERVERONLY + if (qerrno == EADDRNOTAVAIL) + Con_DPrintf("NET_SendPacket Warning: %i\n", qerrno); + else +#endif + Con_TPrintf (TL_NETSENDERROR, qerrno); + } +} + +//============================================================================= + +int UDP_OpenSocket (int port, qboolean bcast) +{ + int newsocket; + struct sockaddr_in address; + unsigned long _true = true; + int i; +int maxport = port + 100; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("UDP_OpenSocket: socket: %s", strerror(qerrno)); + + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); + + if (bcast) + { + _true = true; + if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) == -1) + { + Con_Printf("Cannot create broadcast socket\n"); + return 0; + } + } + + address.sin_family = AF_INET; +//ZOID -- check for interface binding option + if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) { + address.sin_addr.s_addr = inet_addr(com_argv[i+1]); + Con_TPrintf(TL_NETBINDINTERFACE, + inet_ntoa(address.sin_addr)); + } else + address.sin_addr.s_addr = INADDR_ANY; + + for(;;) + { + if (port == PORT_ANY) + address.sin_port = 0; + else + address.sin_port = htons((short)port); + + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + if (!port) + Sys_Error ("UDP_OpenSocket: bind: %s", strerror(qerrno)); + port++; + if (port > maxport) + Sys_Error ("UDP_OpenSocket: bind: %s", strerror(qerrno)); + } + else + break; + } + + return newsocket; +} + +#ifdef IPPROTO_IPV6 +int UDP6_OpenSocket (int port, qboolean bcast) +{ + int newsocket; + struct sockaddr_in6 address; + unsigned long _true = true; +// int i; +int maxport = port + 100; + + if ((newsocket = socket (PF_INET6, SOCK_DGRAM, 0)) == -1) + { + Con_Printf("IPV6 is not supported: %s\n", strerror(qerrno)); + return 0; + } + + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); + + if (bcast) + { +// address.sin6_addr +// _true = true; +// if (setsockopt(newsocket, SOL_SOCKET, IP_ADD_MEMBERSHIP, (char *)&_true, sizeof(_true)) == -1) +// { + Con_Printf("Cannot create broadcast socket\n"); + closesocket(newsocket); + return 0; +// } + } + + address.sin6_family = AF_INET6; +//ZOID -- check for interface binding option +// if ((i = COM_CheckParm("-ip6")) != 0 && i < com_argc) { +// address.sin6_addr = inet_addr(com_argv[i+1]); +/// Con_TPrintf(TL_NETBINDINTERFACE, +// inet_ntoa(address.sin6_addr)); +// } else + memcpy(&address.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + + for(;;) + { + if (port == PORT_ANY) + address.sin6_port = 0; + else + address.sin6_port = htons((short)port); + + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + if (!port) + Sys_Error ("UDP6_OpenSocket: bind: %s", strerror(qerrno)); + port++; + if (port > maxport) + Sys_Error ("UDP6_OpenSocket: bind: %s", strerror(qerrno)); + } + else + break; + } + + return newsocket; +} +#endif + +void UDP_CloseSocket (int socket) +{ + closesocket(socket); +} + +int IPX_OpenSocket (int port, qboolean bcast) +{ +#ifndef USEIPX + return 0; +#else + int newsocket; + struct sockaddr_ipx address; + u_long _true = 1; + + if ((newsocket = socket (PF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == -1) + { + if (qerrno != EAFNOSUPPORT) + Con_Printf ("WARNING: IPX_Socket: socket: %s\n", qerrno); + return 0; + } + + // make it non-blocking + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + { + Con_Printf ("WARNING: IPX_Socket: ioctl FIONBIO: %s\n", qerrno); + return 0; + } + + if (bcast) + { + // make it broadcast capable + if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) == -1) + { + Con_Printf ("WARNING: IPX_Socket: setsockopt SO_BROADCAST: %s\n", qerrno); + return 0; + } + } + + address.sa_family = AF_IPX; + memset (address.sa_netnum, 0, 4); + memset (address.sa_nodenum, 0, 6); + if (port == PORT_ANY) + address.sa_socket = 0; + else + address.sa_socket = htons((short)port); + + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + Con_Printf ("WARNING: IPX_Socket: bind: %i\n", qerrno); + closesocket (newsocket); + return 0; + } + + return newsocket; +#endif +} + +void IPX_CloseSocket (int socket) +{ +#ifdef USEIPX + closesocket(socket); +#endif +} + +// sleeps msec or until net socket is ready +//stdin can sometimes be a socket. As a result, +//we give the option to select it for nice console imput with timeouts. +qboolean NET_Sleep(int msec, qboolean stdinissocket) +{ + struct timeval timeout; + fd_set fdset; + int i; + + FD_ZERO(&fdset); + + if (stdinissocket) + FD_SET(0, &fdset); + + i = 0; + if (svs.socketip) + { + FD_SET(svs.socketip, &fdset); // network socket + i = svs.socketip; + } + if (svs.socketip6) + { + FD_SET(svs.socketip6, &fdset); // network socket + if (svs.socketip6 > i) + i = svs.socketip6; + i = svs.socketip6; + } +#ifdef USEIPX + if (svs.socketipx) + { + FD_SET(svs.socketipx, &fdset); // network socket + if (svs.socketipx > i) + i = svs.socketipx; + } +#endif + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000)*1000; + select(i+1, &fdset, NULL, NULL, &timeout); + + if (stdinissocket) + return FD_ISSET(0, &fdset); + return true; +} + +void NET_GetLocalIP6Address (int socket) +{ +// char buff[512]; + struct sockaddr_qstorage address; + int namelen; +// netadr_t adr; + +// gethostname(buff, 512); +// buff[512-1] = 0; + +// NET_StringToAdr (buff, &adr); + + namelen = sizeof(address); + if (getsockname (socket, (struct sockaddr *)&address, &namelen) == -1) + Sys_Error ("NET_Init: getsockname:", strerror(qerrno)); + + SockadrToNetadr(&address, &net_local_ip6adr); +/* if (!*(int*)net_local_ip6adr.ip) //socket was set to auto + *(int *)net_local_ip6adr.ip = *(int *)adr.ip; //change it to what the machine says it is, rather than the socket. +*/ + Con_TPrintf(TL_IPADDRESSIS, NET_AdrToString (net_local_ip6adr) ); +} + +void NET_GetLocalIPAddress (int socket) +{ + char buff[512]; + struct sockaddr_qstorage address; + int namelen; + netadr_t adr; + + gethostname(buff, 512); + buff[512-1] = 0; + + NET_StringToAdr (buff, &adr); + + namelen = sizeof(address); + if (getsockname (socket, (struct sockaddr *)&address, &namelen) == -1) + Sys_Error ("NET_Init: getsockname:", strerror(qerrno)); + + SockadrToNetadr(&address, &net_local_ipadr); + if (!*(int*)net_local_ipadr.ip) //socket was set to auto + *(int *)net_local_ipadr.ip = *(int *)adr.ip; //change it to what the machine says it is, rather than the socket. + + Con_TPrintf(TL_IPADDRESSIS, NET_AdrToString (net_local_ipadr) ); +} + +void NET_GetLocalIPXAddress (int socket) +{ + //we don't really care... this is for lans +/* char buff[512]; + struct sockaddr address; + int namelen; + netadr_t adr; + + gethostname(buff, 512); + buff[512-1] = 0; + + NET_StringToAdr (buff, &adr); + + namelen = sizeof(address); + if (getsockname (socket, (struct sockaddr *)&address, &namelen) == -1) + Sys_Error ("NET_Init: getsockname:", strerror(qerrno)); + + SockadrToNetadr(&address, &net_local_adr); + if (!*(int*)net_local_ipadr.ip) + *(int *)net_local_ipadr.ip = *(int *)adr.ip; + + if (net_local_adr.type == NA_IP) + Con_TPrintf(TL_IPADDRESSIS, NET_AdrToString (net_local_adr) ); + else + Con_Printf("IPX Address: %s\n", NET_AdrToString (net_local_adr) ); + */ +} + +/* +==================== +NET_Init +==================== +*/ +void NET_Init (void) +{ +#ifdef _WIN32 + WORD wVersionRequested; + int r; +#ifdef IPPROTO_IPV6 + HMODULE ws2_32dll; + ws2_32dll = LoadLibrary("ws2_32.dll"); + if (ws2_32dll) + { + pfreeaddrinfo = (void *)GetProcAddress(ws2_32dll, "freeaddrinfo"); + pgetaddrinfo = (void *)GetProcAddress(ws2_32dll, "getaddrinfo"); + if (!pgetaddrinfo || !pfreeaddrinfo) + { + pgetaddrinfo = NULL; + pfreeaddrinfo = NULL; + FreeLibrary(ws2_32dll); + } + } + else + pgetaddrinfo = NULL; +#endif + + wVersionRequested = MAKEWORD(1, 1); + + r = WSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + Sys_Error ("Winsock initialization failed."); +#endif + Con_TPrintf(TL_UDPINITED); + +#ifdef NQPROT + NQ_NET_Init(); +#endif +} +#ifndef SERVERONLY +void NET_InitClient(void) +{ + int port; + int p; + port = PORT_CLIENT; + p = COM_CheckParm ("-port"); + if (p && p < com_argc) + { + port = atoi(com_argv[p+1]); + } + // + // open the single socket to be used for all communications + // + cls.socketip = UDP_OpenSocket (port, false); +#ifdef IPPROTO_IPV6 + cls.socketip6 = UDP6_OpenSocket (port, false); +#endif +#ifdef USEIPX + cls.socketipx = IPX_OpenSocket (port, false); +#endif + + // + // init the message buffer + // + net_message.maxsize = sizeof(net_message_buffer); + net_message.data = net_message_buffer; + + // + // determine my name & address + // + NET_GetLocalIPAddress (cls.socketip); + + Con_TPrintf(TL_CLIENTPORTINITED); +} +#endif +#ifndef CLIENTONLY + +void NET_CloseServer(void) +{ + if (svs.socketip) + { + UDP_CloseSocket(svs.socketip); + svs.socketip = 0; + } + if (svs.socketip6) + { + UDP_CloseSocket(svs.socketip); + svs.socketip = 0; + } +#ifdef USEIPX + if (svs.socketipx) + { + IPX_CloseSocket(svs.socketipx); + svs.socketipx = 0; + } +#endif + + net_local_ipadr.type = NA_LOOPBACK; +} + +void NET_InitServer(void) +{ + int port; + int p; + port = PORT_SERVER; + + if (sv_public.value) + { + p = COM_CheckParm ("-svport"); + if (p && p < com_argc) + { + port = atoi(com_argv[p+1]); + } + + p = COM_CheckParm ("-port"); + if (p && p < com_argc) + { + port = atoi(com_argv[p+1]); + } + + // + // open the single socket to be used for all communications + // + if (!svs.socketip) + { + svs.socketip = UDP_OpenSocket (port, false); + NET_GetLocalIPAddress (svs.socketip); + } +#ifdef IPPROTO_IPV6 + if (!svs.socketip6) + { + svs.socketip6 = UDP6_OpenSocket (port, false); + if (svs.socketip6) + NET_GetLocalIP6Address (svs.socketip6); + } +#endif +#ifdef USEIPX + if (!svs.socketipx) + { + svs.socketipx = IPX_OpenSocket (port, false); + NET_GetLocalIPXAddress (svs.socketipx); + } +#endif + } + else + NET_CloseServer(); + + // + // init the message buffer + // + net_message.maxsize = sizeof(net_message_buffer); + net_message.data = net_message_buffer; +} +#endif +/* +==================== +NET_Shutdown +==================== +*/ +void NET_Shutdown (void) +{ +#ifndef CLIENTONLY + NET_CloseServer(); +#endif +#ifndef SERVERONLY + UDP_CloseSocket (cls.socketip); + UDP_CloseSocket (cls.socketip6); +#ifdef USEIPX + IPX_CloseSocket (cls.socketipx); +#endif +#endif +#ifdef NQPROT + NQ_NET_Shutdown(); +#endif +#ifdef _WIN32 +#ifdef SERVERTONLY + if (!serverthreadID) //running as subsystem of client. Don't close all of it's sockets too. +#endif + WSACleanup (); +#endif +} + diff --git a/engine/common/pmove.c b/engine/common/pmove.c new file mode 100644 index 000000000..1ddd3dbc0 --- /dev/null +++ b/engine/common/pmove.c @@ -0,0 +1,1060 @@ +/* +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" + +movevars_t movevars; +playermove_t pmove; + +float frametime; + +vec3_t forward, right, up; + +vec3_t player_mins = {-16, -16, -24}; +vec3_t player_maxs = {16, 16, 32}; + + +void PM_Init (void) +{ + PM_InitBoxHull(); +} + +#define MIN_STEP_NORMAL 0.7 // roughly 45 degrees + +#define pm_flyfriction 4 + +#define STOP_EPSILON 0.1 +#define BLOCKED_FLOOR 1 +#define BLOCKED_STEP 2 +#define BLOCKED_OTHER 4 +#define BLOCKED_ANY 7 + +/* +================== +PM_ClipVelocity + +Slide off of the impacting object +returns the blocked flags (1 = floor, 2 = step / wall) +================== +*/ + +int PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + float backoff; + float change; + int i, blocked; + + blocked = 0; + if (normal[2] > 0) + blocked |= BLOCKED_FLOOR; // floor + if (!normal[2]) + blocked |= BLOCKED_STEP; // step + + backoff = DotProduct (in, normal) * overbounce; + + for (i=0 ; i<3 ; i++) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } + + return blocked; +} + + +/* +============ +PM_SlideMove + +The basic solid body movement clip that slides along multiple planes +============ +*/ +#define MAX_CLIP_PLANES 5 + +int PM_SlideMove (void) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity; + int i, j; + trace_t trace; + vec3_t end; + float time_left; + int blocked; + + numbumps = 4; + + blocked = 0; + VectorCopy (pmove.velocity, original_velocity); + VectorCopy (pmove.velocity, primal_velocity); + numplanes = 0; + + time_left = frametime; + + for (bumpcount=0 ; bumpcount 0) + { // actually covered some distance + VectorCopy (trace.endpos, pmove.origin); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + // save entity for contact + if (pmove.numtouch < MAX_PHYSENTS) + { + pmove.touchindex[pmove.numtouch] = trace.entnum; + pmove.numtouch++; + } + + if (trace.plane.normal[2] >= MIN_STEP_NORMAL) + blocked |= BLOCKED_FLOOR; + else if (!trace.plane.normal[2]) + blocked |= BLOCKED_STEP; + else + blocked |= BLOCKED_OTHER; + + time_left -= time_left * trace.fraction; + + // cliped to another plane + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + VectorClear (pmove.velocity); + break; + } + + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + +// +// modify original_velocity so it parallels all of the clip planes +// + for (i=0 ; i= updist) + { +usedown: + VectorCopy (down, pmove.origin); + VectorCopy (downvel, pmove.velocity); + return; + } + + // copy z value from slide move + pmove.velocity[2] = downvel[2]; + + if (!pmove.onground && pmove.waterlevel < 2 && (blocked & BLOCKED_STEP)) { + float scale; + // in pm_airstep mode, walking up a 16 unit high step + // will kill 16% of horizontal velocity + scale = 1 - 0.01*(pmove.origin[2] - original[2]); + pmove.velocity[0] *= scale; + pmove.velocity[1] *= scale; + } +} + + + +/* +================== +PM_Friction + +Handles both ground friction and water friction +================== +*/ +void PM_Friction (void) +{ + float speed, newspeed, control; + float friction; + float drop; + vec3_t start, stop; + trace_t trace; + + if (pmove.waterjumptime) + return; + + speed = Length(pmove.velocity); + if (speed < 1) + { + pmove.velocity[0] = 0; + pmove.velocity[1] = 0; + if (pmove.pm_type == PM_FLY) + pmove.velocity[2] = 0; + return; + } + + if (pmove.waterlevel >= 2) + // apply water friction, even if in fly mode + drop = speed*movevars.waterfriction*pmove.waterlevel*frametime; + else if (pmove.pm_type == PM_FLY) { + // apply flymode friction + drop = speed * pm_flyfriction * frametime; + } + else if (pmove.onground) { + // apply ground friction + friction = movevars.friction; + + // if the leading edge is over a dropoff, increase friction + start[0] = stop[0] = pmove.origin[0] + pmove.velocity[0]/speed*16; + start[1] = stop[1] = pmove.origin[1] + pmove.velocity[1]/speed*16; + start[2] = pmove.origin[2] + player_mins[2]; + stop[2] = start[2] - 34; + trace = PM_PlayerTrace (start, stop); + if (trace.fraction == 1) + friction *= 2; + + control = speed < movevars.stopspeed ? movevars.stopspeed : speed; + drop = control*friction*frametime; + } + else if (pmove.onladder) + { + control = speed < movevars.stopspeed ? movevars.stopspeed : speed; + drop = control*movevars.friction*frametime*6; + } + else + return; // in air, no friction + +// scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + + VectorScale (pmove.velocity, newspeed / speed, pmove.velocity); +} + + +/* +============== +PM_Accelerate +============== +*/ +void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed; + + if (pmove.pm_type == PM_DEAD) + return; + if (pmove.waterjumptime) + return; + + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel*frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove.velocity[i] += accelspeed*wishdir[i]; +} + +void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed, wishspd = wishspeed; + float originalspeed, newspeed, speedcap; + + if (pmove.pm_type == PM_DEAD) + return; + if (pmove.waterjumptime) + return; + + if (movevars.bunnyspeedcap > 0) + { + originalspeed = sqrt(pmove.velocity[0]*pmove.velocity[0] + + pmove.velocity[1]*pmove.velocity[1]); + } + else + originalspeed = 0; //shh compiler. + + if (wishspd > 30) + wishspd = 30; + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * wishspeed * frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove.velocity[i] += accelspeed*wishdir[i]; + + if (movevars.bunnyspeedcap > 0) + { + newspeed = sqrt(pmove.velocity[0]*pmove.velocity[0] + + pmove.velocity[1]*pmove.velocity[1]); + if (newspeed > originalspeed) + { + speedcap = movevars.maxspeed * movevars.bunnyspeedcap; + if (newspeed > speedcap) + { + if (originalspeed < speedcap) + originalspeed = speedcap; + pmove.velocity[0] *= originalspeed / newspeed; + pmove.velocity[1] *= originalspeed / newspeed; + } + } + } +} + + + +/* +=================== +PM_WaterMove +=================== +*/ +void PM_WaterMove (void) +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + +// +// user intentions +// + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove; + + if (pmove.pm_type != PM_FLY && !pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if (wishspeed > movevars.maxspeed) { + VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + wishspeed *= 0.7; + +// +// water acceleration +// + PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate); + + PM_StepSlideMove (); +} + + +/* +*/ +void PM_FlyMove () +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove; + + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if (wishspeed > movevars.maxspeed) { + VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + + PM_Accelerate (wishdir, wishspeed, movevars.accelerate); + + PM_StepSlideMove (); +} + +void PM_LadderMove (void) +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + vec3_t start, dest; + trace_t trace; + +// +// user intentions +// + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove + up[i]*pmove.cmd.upmove; + + if (wishvel[2] > 100 || wishvel[2] < -100) //large up/down move + wishvel[2]*=10; + + if (pmove.cmd.buttons & 2) + wishvel[2]+=100; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if (wishspeed > movevars.maxspeed) + { + VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + + PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate); + +// assume it is a stair or a slope, so press down from stepheight above + VectorMA (pmove.origin, frametime, pmove.velocity, dest); + VectorCopy (dest, start); + start[2] += pm_stepheight + 1; + trace = PM_PlayerTrace (start, dest); + if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? + { // walked up the step + VectorCopy (trace.endpos, pmove.origin); + return; + } + + PM_FlyMove (); + +} + +/* +=================== +PM_AirMove + +=================== +*/ +void PM_AirMove (void) +{ + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + + fmove = pmove.cmd.forwardmove; + smove = pmove.cmd.sidemove; + + forward[2] = 0; + right[2] = 0; + VectorNormalize (forward); + VectorNormalize (right); + + for (i=0 ; i<2 ; i++) + wishvel[i] = forward[i]*fmove + right[i]*smove; + wishvel[2] = 0; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + +// +// clamp to server defined max speed +// + if (wishspeed > movevars.maxspeed) + { + VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + + if (pmove.onground) + { + if (pmove.velocity[2] > 0 || !movevars.slidefix) + pmove.velocity[2] = 0; + PM_Accelerate (wishdir, wishspeed, movevars.accelerate); + pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + + if (!movevars.slidefix) + pmove.velocity[2] = 0; + + if (!pmove.velocity[0] && !pmove.velocity[1]) + { + pmove.velocity[2] = 0; + return; + } + + PM_StepSlideMove (); + } + else + { // not on ground, so little effect on velocity + PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); + + // add gravity + pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + + if (movevars.airstep) + PM_StepSlideMove (); + else + PM_SlideMove (); + } +} + + +cplane_t groundplane; + +/* +============= +PM_CategorizePosition +============= +*/ +void PM_CategorizePosition (void) +{ + vec3_t point; + int cont; + trace_t trace; + +// if the player hull point one unit down is solid, the player +// is on ground + +// see if standing on something solid + point[0] = pmove.origin[0]; + point[1] = pmove.origin[1]; + point[2] = pmove.origin[2] - 1; + trace.startsolid = trace.allsolid = true; + VectorClear(trace.endpos); + if (pmove.velocity[2] > 180) + { + pmove.onground = false; + } + else + { + trace = PM_PlayerTrace (pmove.origin, point); + if (trace.fraction == 1 || trace.plane.normal[2] < MIN_STEP_NORMAL) + pmove.onground = false; + else + { + pmove.onground = true; + pmove.groundent = trace.entnum; + groundplane = trace.plane; + pmove.waterjumptime = 0; + } + + // standing on an entity other than the world + if (trace.entnum > 0) + { + if (pmove.numtouch < MAX_PHYSENTS) { + pmove.touchindex[pmove.numtouch] = trace.entnum; + pmove.numtouch++; + } + } + } + +// +// get waterlevel +// + pmove.waterlevel = 0; + pmove.watertype = FTECONTENTS_EMPTY; + + point[2] = pmove.origin[2] + player_mins[2] + 1; + cont = PM_PointContents (point); + + if (cont & FTECONTENTS_FLUID) + { + pmove.watertype = cont; + pmove.waterlevel = 1; + point[2] = pmove.origin[2] + (player_mins[2] + player_maxs[2])*0.5; + cont = PM_PointContents (point); + if (cont & FTECONTENTS_FLUID) + { + pmove.waterlevel = 2; + point[2] = pmove.origin[2] + player_mins[2]+24+DEFAULT_VIEWHEIGHT; + cont = PM_PointContents (point); + if (cont & FTECONTENTS_FLUID) + pmove.waterlevel = 3; + } + } + + //are we on a ladder? +#ifdef Q2BSPS + if (cont & FTECONTENTS_LADDER && pmove.physents[0].model->fromgame == fg_quake2) + { + trace_t t; + vec3_t flatforward, fwd1; + + flatforward[0] = forward[0]; + flatforward[1] = forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + VectorMA (pmove.origin, 24, flatforward, fwd1); + + //if we hit a wall when going forwards and we are in a ladder region, then we are on a ladder. + if (pmove.physents[0].model->fromgame == fg_quake2) + { + t = CM_BoxTrace(pmove.origin, fwd1, player_mins, player_maxs, pmove.physents[0].model->hulls[0].firstclipnode, MASK_PLAYERSOLID); + if (t.fraction < 1) + { + pmove.onladder = true; + pmove.onground = false; // too steep + } + } + } +#endif + + if (pmove.onground && pmove.pm_type != PM_FLY && pmove.waterlevel < 3) + { + // snap to ground so that we can't jump higher than we're supposed to + if (!trace.startsolid && !trace.allsolid) + VectorCopy (trace.endpos, pmove.origin); + } +} + + +/* +============= +PM_CheckJump +============= +*/ +void PM_CheckJump (void) +{ + if (pmove.pm_type == PM_FLY) + return; + + if (pmove.pm_type == PM_DEAD) + { + pmove.jump_held = true; // don't jump on respawn + return; + } + + if (!(pmove.cmd.buttons & BUTTON_JUMP)) + { + pmove.jump_held = false; + return; + } + + if (pmove.waterjumptime) + return; + + if (pmove.waterlevel >= 2) + { // swimming, not jumping + pmove.onground = false; + + if (pmove.watertype == FTECONTENTS_WATER) + pmove.velocity[2] = 100; + else if (pmove.watertype == FTECONTENTS_SLIME) + pmove.velocity[2] = 80; + else + pmove.velocity[2] = 50; + return; + } + + if (!pmove.onground) + return; // in air, so no effect + + if (pmove.jump_held && !pmove.jump_msec) + return; // don't pogo stick + + // check for jump bug + // groundplane normal was set in the call to PM_CategorizePosition + if (pmove.velocity[2] < 0 && + DotProduct(pmove.velocity, groundplane.normal) < -0.1) + { + // pmove.velocity is pointing into the ground, clip it + PM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1); + } + + pmove.onground = false; + pmove.velocity[2] += 270; + + if (movevars.ktjump > 0) + { + if (movevars.ktjump > 1) + movevars.ktjump = 1; + if (pmove.velocity[2] < 270) + pmove.velocity[2] = pmove.velocity[2] * (1 - movevars.ktjump) + + 270 * movevars.ktjump; + } + + pmove.jump_held = true; // don't jump again until released + + pmove.jump_msec = pmove.cmd.msec; +} + +/* +============= +PM_CheckWaterJump +============= +*/ +void PM_CheckWaterJump (void) +{ + vec3_t spot; + int cont; + vec3_t flatforward; + + if (pmove.waterjumptime>0) + return; + + // don't hop out if we just jumped in + if (pmove.velocity[2] < -180) + return; + + // see if near an edge + flatforward[0] = forward[0]; + flatforward[1] = forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + VectorMA (pmove.origin, 24, flatforward, spot); + spot[2] += 8 + 24+player_mins[2]; //hexen2 fix. calculated from the normal bottom of bbox + cont = PM_PointContents (spot); + if (!(cont & FTECONTENTS_SOLID)) + return; + spot[2] += 24; + cont = PM_PointContents (spot); + if (cont != FTECONTENTS_EMPTY) + return; + // jump out of water + VectorScale (flatforward, 50, pmove.velocity); + pmove.velocity[2] = 310; + pmove.waterjumptime = 2; // safety net + pmove.jump_held = true; // don't jump again until released +} + +/* +================= +PM_NudgePosition + +If pmove.origin is in a solid position, +try nudging slightly on all axis to +allow for the cut precision of the net coordinates +================= +*/ +void PM_NudgePosition (void) +{ + vec3_t base; + int x, y, z; + int i; + static int sign[3] = {0, -1, 1}; + + VectorCopy (pmove.origin, base); + + for (i=0 ; i<3 ; i++) + pmove.origin[i] = ((int)(pmove.origin[i]*8)) * 0.125; + + for (z=0 ; z<=2 ; z++) + { + for (x=0 ; x<=2 ; x++) + { + for (y=0 ; y<=2 ; y++) + { + pmove.origin[0] = base[0] + (sign[x] * 1.0/8); + pmove.origin[1] = base[1] + (sign[y] * 1.0/8); + pmove.origin[2] = base[2] + (sign[z] * 1.0/8); + if (PM_TestPlayerPosition (pmove.origin)) + return; + } + } + } + VectorCopy (base, pmove.origin); +// Com_DPrintf ("NudgePosition: stuck\n"); +} + +/* +=============== +PM_SpectatorMove +=============== +*/ +void PM_SpectatorMove (void) +{ + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + + // friction + + speed = Length (pmove.velocity); + if (speed < 1) + { + VectorClear (pmove.velocity); + } + else + { + drop = 0; + + friction = movevars.friction*1.5; // extra friction + control = speed < movevars.stopspeed ? movevars.stopspeed : speed; + drop += control*friction*frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pmove.velocity, newspeed, pmove.velocity); + } + + // accelerate + fmove = pmove.cmd.forwardmove; + smove = pmove.cmd.sidemove; + + VectorNormalize (forward); + VectorNormalize (right); + + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*fmove + right[i]*smove; + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > movevars.spectatormaxspeed) + { + VectorScale (wishvel, movevars.spectatormaxspeed/wishspeed, wishvel); + wishspeed = movevars.spectatormaxspeed; + } + + currentspeed = DotProduct(pmove.velocity, wishdir); + addspeed = wishspeed - currentspeed; + + // Buggy QW spectator mode, kept for compatibility + if (pmove.pm_type == PM_OLD_SPECTATOR) + { + if (addspeed <= 0) + return; + } + + if (addspeed > 0) { + accelspeed = movevars.accelerate*frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove.velocity[i] += accelspeed*wishdir[i]; + } + + // move + VectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin); +} + +/* +============= +PM_PlayerMove + +Returns with origin, angles, and velocity modified in place. + +Numtouch and touchindex[] will be set if any of the physents +were contacted during the move. +============= +*/ +void PM_PlayerMove (void) +{ + frametime = pmove.cmd.msec * 0.001; + pmove.numtouch = 0; + + if (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE) { + PM_CategorizePosition (); + return; + } + + // take angles directly from command + pmove.angles[0] = SHORT2ANGLE(pmove.cmd.angles[0]); + pmove.angles[1] = SHORT2ANGLE(pmove.cmd.angles[1]); + pmove.angles[2] = SHORT2ANGLE(pmove.cmd.angles[2]); + + AngleVectors (pmove.angles, forward, right, up); + + if (pmove.pm_type == PM_SPECTATOR || pmove.pm_type == PM_OLD_SPECTATOR) + { + PM_SpectatorMove (); + pmove.onground = false; + return; + } + + PM_NudgePosition (); + + // set onground, watertype, and waterlevel + PM_CategorizePosition (); + + if (pmove.waterlevel == 2 && pmove.pm_type != PM_FLY) + PM_CheckWaterJump (); + + if (pmove.velocity[2] < 0 || pmove.pm_type == PM_DEAD) + pmove.waterjumptime = 0; + + if (pmove.waterjumptime) + { + pmove.waterjumptime -= frametime; + if (pmove.waterjumptime < 0) + pmove.waterjumptime = 0; + } + + if (pmove.jump_msec) + { + pmove.jump_msec += pmove.cmd.msec; + if (pmove.jump_msec > 50) + pmove.jump_msec = 0; + } + + PM_CheckJump (); + + PM_Friction (); + + if (pmove.waterlevel >= 2) + PM_WaterMove (); + else if (pmove.pm_type == PM_FLY) + PM_FlyMove (); + else if (pmove.onladder) + PM_LadderMove (); + else + PM_AirMove (); + + // set onground, watertype, and waterlevel for final spot + PM_CategorizePosition (); + + // this is to make sure landing sound is not played twice + // and falling damage is calculated correctly + if (pmove.onground && pmove.velocity[2] < -300 + && DotProduct(pmove.velocity, groundplane.normal) < -0.1) + { + PM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1); + } +} diff --git a/engine/common/pmove.h b/engine/common/pmove.h new file mode 100644 index 000000000..89bfc5f10 --- /dev/null +++ b/engine/common/pmove.h @@ -0,0 +1,142 @@ +/* +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. + +*/ + +#define BUTTON_ATTACK 1 +#define BUTTON_JUMP 2 + +typedef enum { + PM_NORMAL, // normal ground movement + PM_OLD_SPECTATOR, // fly, no clip to world (QW bug) + PM_SPECTATOR, // fly, no clip to world + PM_DEAD, // no acceleration + PM_FLY, // fly, bump into walls + PM_NONE, // can't move + PM_FREEZE // can't move or look around (TODO) +} pmtype_t; + +#define MAX_PHYSENTS 128 +typedef struct +{ + vec3_t origin; + vec3_t angles; + model_t *model; // only for bsp models + vec3_t mins, maxs; // only for non-bsp models + int info; // for client or server to identify +} physent_t; + + +typedef struct +{ +/* int sequence; // just for debugging prints + + // player state + vec3_t origin; + vec3_t angles; + vec3_t velocity; + int oldbuttons; + float waterjumptime; + qboolean dead; + int spectator; + + int hullnum; + + // world state + int numphysent; + physent_t physents[MAX_PHYSENTS]; // 0 should be the world + + // input + usercmd_t cmd; + + // results + int numtouch; + int touchindex[MAX_PHYSENTS]; + qboolean onground; + int groundent; + + int waterlevel; + int watertype; + + pmtype_t pm_type;*/ + + + int sequence; // just for debugging prints + + // player state + vec3_t origin; + vec3_t angles; + vec3_t velocity; + qboolean jump_held; + int jump_msec; // msec since last jump + float waterjumptime; + int pm_type; + int hullnum; + + // world state + int numphysent; + physent_t physents[MAX_PHYSENTS]; // 0 should be the world + + // input + usercmd_t cmd; + + qboolean onladder; + + // results + int numtouch; + int touchindex[MAX_PHYSENTS]; + qboolean onground; + int groundent; // index in physents array, only valid + // when onground is true + int waterlevel; + int watertype; +} playermove_t; + +typedef struct { + float gravity; + float stopspeed; + float maxspeed; + float spectatormaxspeed; + float accelerate; + float airaccelerate; + float wateraccelerate; + float friction; + float waterfriction; + float entgravity; + float bunnyspeedcap; + float ktjump; + qboolean slidefix; + qboolean airstep; +} movevars_t; + + +extern movevars_t movevars; +extern playermove_t pmove; + +void PM_PlayerMove (void); +void PM_Init (void); +void PM_InitBoxHull (void); + +void PM_CategorizePosition (void); +int PM_HullPointContents (hull_t *hull, int num, vec3_t p); + +int PM_PointContents (vec3_t point); +qboolean PM_TestPlayerPosition (vec3_t point); +#ifndef __cplusplus +struct trace_s PM_PlayerTrace (vec3_t start, vec3_t stop); +#endif diff --git a/engine/common/pmovetst.c b/engine/common/pmovetst.c new file mode 100644 index 000000000..bf6796bd6 --- /dev/null +++ b/engine/common/pmovetst.c @@ -0,0 +1,344 @@ +/* +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" + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + +extern vec3_t player_mins; +extern vec3_t player_maxs; + +/* +=================== +PM_InitBoxHull + +Set up the planes and clipnodes so that the six floats of a bounding box +can just be stored out and get a proper hull_t structure. +=================== +*/ +void PM_InitBoxHull (void) +{ + int i; + int side; + + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; + box_hull.firstclipnode = 0; + box_hull.lastclipnode = 5; + + Q1BSP_SetHullFuncs(&box_hull); + + for (i=0 ; i<6 ; i++) + { + box_clipnodes[i].planenum = i; + + side = i&1; + + box_clipnodes[i].children[side] = Q1CONTENTS_EMPTY; + if (i != 5) + box_clipnodes[i].children[side^1] = i + 1; + else + box_clipnodes[i].children[side^1] = Q1CONTENTS_SOLID; + + box_planes[i].type = i>>1; + box_planes[i].normal[i>>1] = 1; + } + +} + + +/* +=================== +PM_HullForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +hull_t *PM_HullForBox (vec3_t mins, vec3_t maxs) +{ + box_planes[0].dist = maxs[0]; + box_planes[1].dist = mins[0]; + box_planes[2].dist = maxs[1]; + box_planes[3].dist = mins[1]; + box_planes[4].dist = maxs[2]; + box_planes[5].dist = mins[2]; + + return &box_hull; +} + +int PM_TransformedHullPointContents (hull_t *hull, vec3_t p, vec3_t origin, vec3_t angles) +{ + vec3_t p_l, forward, up, right, temp; + VectorSubtract (p, origin, p_l); + + if (!player_mins[2]) + p_l[2] -= hull->clip_mins[2]+0.1; + + // rotate start and end into the models frame of reference + if (hull != &box_hull && + (angles[0] || angles[1] || angles[2]) ) + { + AngleVectors (angles, forward, right, up); + + VectorCopy (p_l, temp); + p_l[0] = DotProduct (temp, forward); + p_l[1] = -DotProduct (temp, right); + p_l[2] = DotProduct (temp, up); + } + + return hull->funcs.HullPointContents(hull, p_l); +} + +/* +================== +PM_PointContents + +================== +*/ +int PM_PointContents (vec3_t p) +{ + hull_t *hull; + int num; + + int pc; + + hull = &pmove.physents[0].model->hulls[0]; + + pc = hull->funcs.HullPointContents(hull, p); + //we need this for e2m2 - waterjumping on to plats wouldn't work otherwise. + for (num = 1; num < pmove.numphysent; num++) + { + if (pmove.physents[num].model) + pc |= PM_TransformedHullPointContents(&pmove.physents[num].model->hulls[0], p, pmove.physents[num].origin, pmove.physents[num].angles); + } + + return pc; +} + +/* +=============================================================================== + +LINE TESTING IN HULLS + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +vec3_t trace_extents; + + +qboolean PM_TransformedHullCheck (hull_t *hull, vec3_t start, vec3_t end, trace_t *trace, vec3_t origin, vec3_t angles) +{ + vec3_t start_l, end_l; + vec3_t a; + vec3_t forward, right, up; + vec3_t temp; + qboolean rotated; + qboolean result; + + // subtract origin offset + VectorSubtract (start, origin, start_l); + VectorSubtract (end, origin, end_l); + + if (!player_mins[2]) //hmm.. hull bigger than player? Or is this hexen2? + { + start_l[2] -= hull->clip_mins[2]+0.1; + end_l[2] -= hull->clip_mins[2]+0.1; + } + + // rotate start and end into the models frame of reference + if (hull != &box_hull && + (angles[0] || angles[1] || angles[2]) ) + rotated = true; + else + rotated = false; + + if (rotated) + { + AngleVectors (angles, forward, right, up); + + VectorCopy (start_l, temp); + start_l[0] = DotProduct (temp, forward); + start_l[1] = -DotProduct (temp, right); + start_l[2] = DotProduct (temp, up); + + VectorCopy (end_l, temp); + end_l[0] = DotProduct (temp, forward); + end_l[1] = -DotProduct (temp, right); + end_l[2] = DotProduct (temp, up); + } + // sweep the box through the model + result = hull->funcs.RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, trace); + + if (rotated && trace->fraction != 1.0) + { + // FIXME: figure out how to do this with existing angles +// VectorNegate (angles, a); + a[0] = angles[0]; + a[1] = angles[1]; + a[2] = angles[2]; + AngleVectors (a, forward, right, up); + + VectorCopy (trace->plane.normal, temp); + trace->plane.normal[0] = DotProduct (temp, forward); + trace->plane.normal[1] = -DotProduct (temp, right); + trace->plane.normal[2] = DotProduct (temp, up); + } + + trace->endpos[0] = start[0] + trace->fraction * (end[0] - start[0]); + trace->endpos[1] = start[1] + trace->fraction * (end[1] - start[1]); + trace->endpos[2] = start[2] + trace->fraction * (end[2] - start[2]); +// if (!player_mins[2]) //hmm.. hull bigger than player? Or is this hexen2? +// trace->endpos[2] += hull->clip_mins[2]+0.1; + + return result; +} + +/* +================ +PM_TestPlayerPosition + +Returns false if the given player position is not valid (in solid) +================ +*/ +int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t angles); +qboolean PM_TestPlayerPosition (vec3_t pos) +{ + int i; + physent_t *pe; + vec3_t mins, maxs; + hull_t *hull; + + for (i=0 ; i< pmove.numphysent ; i++) + { + pe = &pmove.physents[i]; + + // get the clipping hull + if (pe->model) + { +#ifdef Q2BSPS + if (pe->model->fromgame == fg_quake2 || pe->model->fromgame == fg_quake3) + { + trace_t trace = CM_TransformedBoxTrace(pos, pos, player_mins, player_maxs, pe->model->hulls[0].firstclipnode, MASK_PLAYERSOLID, pe->origin, pe->angles); + if (trace.fraction == 0) + return false; + continue; + } +#endif + hull = &pe->model->hulls[pmove.hullnum]; + if (!hull->available || !hull->planes || !hull->clipnodes) + hull = &pe->model->hulls[1]; + } + else + { + VectorSubtract (pe->mins, player_maxs, mins); + VectorSubtract (pe->maxs, player_mins, maxs); + hull = PM_HullForBox (mins, maxs); + } + + if (PM_TransformedHullPointContents (hull, pos, pe->origin, pe->angles) & FTECONTENTS_SOLID) + return false; + } + + return true; +} + +/* +================ +PM_PlayerTrace +================ +*/ +trace_t PM_PlayerTrace (vec3_t start, vec3_t end) +{ + trace_t trace, total; + hull_t *hull; + int i; + physent_t *pe; + vec3_t mins, maxs; + +// fill in a default trace + memset (&total, 0, sizeof(trace_t)); + total.fraction = 1; + total.entnum = -1; + VectorCopy (end, total.endpos); + + for (i=0 ; i< pmove.numphysent ; i++) + { + pe = &pmove.physents[i]; + // get the clipping hull + if (pe->model) + { + hull = &pmove.physents[i].model->hulls[pmove.hullnum]; + if (!hull->available || !hull->clipnodes || !hull->planes) + hull = &pmove.physents[i].model->hulls[1]; //fallback + } + else + { + VectorSubtract (pe->mins, player_maxs, mins); + VectorSubtract (pe->maxs, player_mins, maxs); + hull = PM_HullForBox (mins, maxs); + } + + // fill in a default trace + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + trace.allsolid = true; +// trace.startsolid = true; + VectorCopy (end, trace.endpos); + + // trace a line through the apropriate clipping hull +#ifdef Q2BSPS + if (pmove.physents[i].model && (pmove.physents[i].model->fromgame == fg_quake2 || pmove.physents[i].model->fromgame == fg_quake3)) + { + trace_t tr; + tr = CM_TransformedBoxTrace (start, end, player_mins, player_maxs, pmove.physents[i].model->hulls[0].firstclipnode, MASK_PLAYERSOLID, pe->origin, pe->angles); + trace.startsolid = tr.startsolid; + trace.allsolid = tr.allsolid; + trace.fraction = tr.fraction; + memcpy(&trace.plane, &tr.plane, sizeof(tr.plane)); + VectorCopy(tr.endpos, trace.endpos); + } + else +#endif + PM_TransformedHullCheck (hull, start, end, &trace, pe->origin, pe->angles); + + if (trace.allsolid) + trace.startsolid = true; + if (trace.startsolid) + trace.fraction = 0; + + // did we clip the move? + if (trace.fraction < total.fraction) + { + // fix trace up by the offset + total = trace; + total.entnum = i; + } + + } + + return total; +} + + diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c new file mode 100644 index 000000000..3d00f7b2b --- /dev/null +++ b/engine/common/q1bsp.c @@ -0,0 +1,395 @@ +#include "quakedef.h" + +/* + +============================================================================ + +Physics functions (common) +*/ + +#if !id386 + +/* +================== +SV_HullPointContents + +================== +*/ +static int Q1_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_Error ("SV_HullPointContents: bad node number"); + + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} +#else +int VARGS Q1_HullPointContents (hull_t *hull, int num, vec3_t p); +#endif // !id386 + + + +#define DIST_EPSILON (0.03125) +qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +{ + dclipnode_t *node; + mplane_t *plane; + float t1, t2; + float frac; + int i; + vec3_t mid; + int side; + float midf; + +// check for empty + if (num < 0) + { + if (num != Q1CONTENTS_SOLID) + { + trace->allsolid = false; + if (num == Q1CONTENTS_EMPTY) + trace->inopen = true; + else + trace->inwater = true; + } + else + trace->startsolid = true; + return true; // empty + } + + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_Error ("Q1BSP_RecursiveHullCheck: bad node number"); + +// +// find the point distances +// + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + } + +#if 1 + if (t1 >= 0 && t2 >= 0) + return Q1BSP_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if (t1 < 0 && t2 < 0) + return Q1BSP_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#else + if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) + return Q1BSP_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) + return Q1BSP_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#endif + +// put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < 0) + frac = (t1 + DIST_EPSILON)/(t1-t2); + else + frac = (t1 - DIST_EPSILON)/(t1-t2); + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + side = (t1 < 0); + +// move up to the node + if (!Q1BSP_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) + return false; + +#ifdef PARANOID + if (Q1BSP_RecursiveHullCheck (sv_hullmodel, mid, node->children[side]) + == Q1CONTENTS_SOLID) + { + Con_Printf ("mid PointInHullSolid\n"); + return false; + } +#endif + + if (Q1_HullPointContents (hull, node->children[side^1], mid) + != Q1CONTENTS_SOLID) +// go past the node + return Q1BSP_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); + + if (trace->allsolid) + return false; // never got out of the solid area + +//================== +// the other side of the node is solid, this is the impact point +//================== + if (!side) + { + VectorCopy (plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; + } + else + { + VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; + } + + while (Q1_HullPointContents (hull, hull->firstclipnode, mid) + == Q1CONTENTS_SOLID) + { // shouldn't really happen, but does occasionally + frac -= 0.1; + if (frac < 0) + { + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + Con_DPrintf ("backup past 0\n"); + return false; + } + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + } + + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + + return false; +} + +int Q1BSP_HullPointContents(hull_t *hull, vec3_t p) +{ + switch(Q1_HullPointContents(hull, hull->firstclipnode, p)) + { + case Q1CONTENTS_EMPTY: + return FTECONTENTS_EMPTY; + case Q1CONTENTS_SOLID: + return FTECONTENTS_SOLID; + case Q1CONTENTS_WATER: + return FTECONTENTS_WATER; + case Q1CONTENTS_SLIME: + return FTECONTENTS_SLIME; + case Q1CONTENTS_LAVA: + return FTECONTENTS_LAVA; + case Q1CONTENTS_SKY: + return FTECONTENTS_SKY; + default: + Sys_Error("Q1_PointContents: Unknown contents type"); + return FTECONTENTS_SOLID; + } +} + +void Q1BSP_SetHullFuncs(hull_t *hull) +{ + hull->funcs.RecursiveHullCheck = Q1BSP_RecursiveHullCheck; + hull->funcs.HullPointContents = Q1BSP_HullPointContents; +} + +/* +Physics functions (common) + +============================================================================ + +Rendering functions (Client only) +*/ +#ifndef SERVERONLY + +extern int r_dlightframecount; + +//goes through the nodes marking the surfaces near the dynamic light as lit. +void Q1BSP_MarkLights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + Q1BSP_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + Q1BSP_MarkLights (light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + Q1BSP_MarkLights (light, bit, node->children[0]); + Q1BSP_MarkLights (light, bit, node->children[1]); +} + +#endif +/* +Rendering functions (Client only) + +============================================================================== + +Server only functions +*/ + + +extern int fatbytes; +extern qbyte fatpvs[(MAX_MAP_LEAFS+1)/4]; + +//does the recursive work of Q1BSP_FatPVS +void SV_Q1BSP_AddToFatPVS (vec3_t org, mnode_t *node) +{ + int i; + qbyte *pvs; + mplane_t *plane; + float d; + + while (1) + { + // if this is a leaf, accumulate the pvs bits + if (node->contents < 0) + { + if (node->contents != Q1CONTENTS_SOLID) + { + pvs = Mod_Q1LeafPVS ( (mleaf_t *)node, sv.worldmodel, NULL); + for (i=0 ; iplane; + d = DotProduct (org, plane->normal) - plane->dist; + if (d > 8) + node = node->children[0]; + else if (d < -8) + node = node->children[1]; + else + { // go down both + SV_Q1BSP_AddToFatPVS (org, node->children[0]); + node = node->children[1]; + } + } +} + +/* +============= +Q1BSP_FatPVS + +Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the +given point. +============= +*/ +void Q1BSP_FatPVS (vec3_t org, qboolean add) +{ + fatbytes = (sv.worldmodel->numleafs+31)>>3; + if (!add) + Q_memset (fatpvs, 0, fatbytes); + SV_Q1BSP_AddToFatPVS (org, sv.worldmodel->nodes); +} + +qboolean Q1BSP_EdictInFatPVS(edict_t *ent) +{ + int i; + for (i=0 ; i < ent->num_leafs ; i++) + if (fatpvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + return true; //we might be able to see this one. + + return false; //none of this ents leafs were visible, so neither is the ent. +} + +/* +=============== +SV_FindTouchedLeafs + +Links the edict to the right leafs so we can get it's potential visability. +=============== +*/ +void Q1BSP_RFindTouchedLeafs (edict_t *ent, mnode_t *node) +{ + mplane_t *splitplane; + mleaf_t *leaf; + int sides; + int leafnum; + + if (node->contents == Q1CONTENTS_SOLID) + return; + +// add an efrag if the node is a leaf + + if ( node->contents < 0) + { + if (ent->num_leafs == MAX_ENT_LEAFS) + return; + + leaf = (mleaf_t *)node; + leafnum = leaf - sv.worldmodel->leafs - 1; + + ent->leafnums[ent->num_leafs] = leafnum; + ent->num_leafs++; + return; + } + +// NODE_MIXED + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); + +// recurse down the contacted sides + if (sides & 1) + Q1BSP_RFindTouchedLeafs (ent, node->children[0]); + + if (sides & 2) + Q1BSP_RFindTouchedLeafs (ent, node->children[1]); +} +void Q1BSP_FindTouchedLeafs(edict_t *ent) +{ + ent->num_leafs = 0; + if (ent->v.modelindex) + Q1BSP_RFindTouchedLeafs (ent, sv.worldmodel->nodes); +} + +/* +Server only functions + +============================================================================== + +*/ + diff --git a/engine/common/q2pmove.c b/engine/common/q2pmove.c new file mode 100644 index 000000000..2c7efd34a --- /dev/null +++ b/engine/common/q2pmove.c @@ -0,0 +1,1370 @@ +/* +Copyright (C) 1997-2001 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" + +float pm_stepheight = 18; +#ifdef Q2BSPS + +#define Q2PMF_DUCKED 1 +#define Q2PMF_JUMP_HELD 2 +#define Q2PMF_ON_GROUND 4 +#define Q2PMF_TIME_WATERJUMP 8 // pm_time is waterjump +#define Q2PMF_TIME_LAND 16 // pm_time is time before rejump +#define Q2PMF_TIME_TELEPORT 32 // pm_time is non-moving time +#define Q2PMF_NO_PREDICTION 64 // temporarily disables prediction (used for grappling hook) + +// all of the locals will be zeroed before each +// pmove, just to make damn sure we don't have +// any differences when running on client or server + +typedef struct +{ + vec3_t origin; // full float precision + vec3_t velocity; // full float precision + + vec3_t forward, right, up; + float frametime; + + + q2csurface_t *groundsurface; + cplane_t groundplane; + int groundcontents; + + vec3_t previous_origin; + qboolean ladder; +} q2pml_t; + +q2pmove_t *q2pm; +static q2pml_t q2pml; + + +// movement parameters +float pm_stopspeed = 100; +float pm_maxspeed = 300; +float pm_duckspeed = 100; +float pm_accelerate = 10; +float pm_airaccelerate = 0; +float pm_wateraccelerate = 10; +float pm_friction = 6; +float pm_waterfriction = 1; +float pm_waterspeed = 400; +//float pm_stepheight; + +/* + + walking up a step should kill some velocity + +*/ + + +/* +================== +PMQ2_ClipVelocity + +Slide off of the impacting object +returns the blocked flags (1 = floor, 2 = step / wall) +================== +*/ +#define STOP_EPSILON 0.1 + +void PMQ2_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + float backoff; + float change; + int i; + + backoff = DotProduct (in, normal) * overbounce; + + for (i=0 ; i<3 ; i++) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } +} + + + + +/* +================== +PMQ2_StepSlideMove + +Each intersection will try to step over the obstruction instead of +sliding along it. + +Returns a new origin, velocity, and contact entity +Does not modify any world state? +================== +*/ +#define MIN_STEP_NORMAL 0.7 // can't step up onto very steep slopes +#define MAX_CLIP_PLANES 5 +void PMQ2_StepSlideMove_ (void) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity; + int i, j; + q2trace_t trace; + vec3_t end; + float time_left; + + numbumps = 4; + + VectorCopy (q2pml.velocity, primal_velocity); + numplanes = 0; + + time_left = q2pml.frametime; + + for (bumpcount=0 ; bumpcounttrace (q2pml.origin, q2pm->mins, q2pm->maxs, end); + + if (trace.allsolid) + { // entity is trapped in another solid + q2pml.velocity[2] = 0; // don't build up falling damage + return; + } + + if (trace.fraction > 0) + { // actually covered some distance + VectorCopy (trace.endpos, q2pml.origin); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + // save entity for contact + if (q2pm->numtouch < MAXTOUCH && trace.ent) + { + q2pm->touchents[q2pm->numtouch] = trace.ent; + q2pm->numtouch++; + } + + time_left -= time_left * trace.fraction; + + // slide along this plane + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + VectorCopy (vec3_origin, q2pml.velocity); + break; + } + + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + +#if 0 + float rub; + + // + // modify velocity so it parallels all of the clip planes + // + if (numplanes == 1) + { // go along this plane + VectorCopy (q2pml.velocity, dir); + VectorNormalize (dir); + rub = 1.0 + 0.5 * DotProduct (dir, planes[0]); + + // slide along the plane + PMQ2_ClipVelocity (q2pml.velocity, planes[0], q2pml.velocity, 1.01); + // rub some extra speed off on xy axis + // not on Z, or you can scrub down walls + q2pml.velocity[0] *= rub; + q2pml.velocity[1] *= rub; + q2pml.velocity[2] *= rub; + } + else if (numplanes == 2) + { // go along the crease + VectorCopy (q2pml.velocity, dir); + VectorNormalize (dir); + rub = 1.0 + 0.5 * DotProduct (dir, planes[0]); + + // slide along the plane + CrossProduct (planes[0], planes[1], dir); + d = DotProduct (dir, q2pml.velocity); + VectorScale (dir, d, q2pml.velocity); + + // rub some extra speed off + VectorScale (q2pml.velocity, rub, q2pml.velocity); + } + else + { +// Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy (vec3_origin, q2pml.velocity); + break; + } + +#else +// +// modify original_velocity so it parallels all of the clip planes +// + for (i=0 ; is.pm_time) + { + VectorCopy (primal_velocity, q2pml.velocity); + } +} + +/* +================== +PMQ2_StepSlideMove + +================== +*/ +void PMQ2_StepSlideMove (void) +{ + vec3_t start_o, start_v; + vec3_t down_o, down_v; + q2trace_t trace; + float down_dist, up_dist; +// vec3_t delta; + vec3_t up, down; + + VectorCopy (q2pml.origin, start_o); + VectorCopy (q2pml.velocity, start_v); + + PMQ2_StepSlideMove_ (); + + VectorCopy (q2pml.origin, down_o); + VectorCopy (q2pml.velocity, down_v); + + VectorCopy (start_o, up); + up[2] += pm_stepheight; + + trace = q2pm->trace (up, q2pm->mins, q2pm->maxs, up); + if (trace.allsolid) + return; // can't step up + + // try sliding above + VectorCopy (up, q2pml.origin); + VectorCopy (start_v, q2pml.velocity); + + PMQ2_StepSlideMove_ (); + + // push down the final amount + VectorCopy (q2pml.origin, down); + down[2] -= pm_stepheight; + trace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, down); + if (!trace.allsolid) + { + VectorCopy (trace.endpos, q2pml.origin); + } + +#if 0 + VectorSubtract (q2pml.origin, up, delta); + up_dist = DotProduct (delta, start_v); + + VectorSubtract (down_o, start_o, delta); + down_dist = DotProduct (delta, start_v); +#else + VectorCopy(q2pml.origin, up); + + // decide which one went farther + down_dist = (down_o[0] - start_o[0])*(down_o[0] - start_o[0]) + + (down_o[1] - start_o[1])*(down_o[1] - start_o[1]); + up_dist = (up[0] - start_o[0])*(up[0] - start_o[0]) + + (up[1] - start_o[1])*(up[1] - start_o[1]); +#endif + + if (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL) + { + VectorCopy (down_o, q2pml.origin); + VectorCopy (down_v, q2pml.velocity); + return; + } + //!! Special case + // if we were walking along a plane, then we need to copy the Z over + q2pml.velocity[2] = down_v[2]; +} + + +/* +================== +PMQ2_Friction + +Handles both ground friction and water friction +================== +*/ +void PMQ2_Friction (void) +{ + float *vel; + float speed, newspeed, control; + float friction; + float drop; + + vel = q2pml.velocity; + + speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]); + if (speed < 1) + { + vel[0] = 0; + vel[1] = 0; + return; + } + + drop = 0; + +// apply ground friction + if ((q2pm->groundentity && q2pml.groundsurface && !(q2pml.groundsurface->flags & SURF_SLICK) ) || (q2pml.ladder) ) + { + friction = pm_friction; + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control*friction*q2pml.frametime; + } + +// apply water friction + if (q2pm->waterlevel && !q2pml.ladder) + drop += speed*pm_waterfriction*q2pm->waterlevel*q2pml.frametime; + +// scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + { + newspeed = 0; + } + newspeed /= speed; + + vel[0] = vel[0] * newspeed; + vel[1] = vel[1] * newspeed; + vel[2] = vel[2] * newspeed; +} + + +/* +============== +PMQ2_Accelerate + +Handles user intended acceleration +============== +*/ +void PMQ2_Accelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed; + + currentspeed = DotProduct (q2pml.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel*q2pml.frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + q2pml.velocity[i] += accelspeed*wishdir[i]; +} + +void PMQ2_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed, wishspd = wishspeed; + + if (wishspd > 30) + wishspd = 30; + currentspeed = DotProduct (q2pml.velocity, wishdir); + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * wishspeed * q2pml.frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + q2pml.velocity[i] += accelspeed*wishdir[i]; +} + +/* +============= +PMQ2_AddCurrents +============= +*/ +void PMQ2_AddCurrents (vec3_t wishvel) +{ + vec3_t v; + float s; + + // + // account for ladders + // + + if (q2pml.ladder && fabs(q2pml.velocity[2]) <= 200) + { + if ((q2pm->viewangles[PITCH] <= -15) && (q2pm->cmd.forwardmove > 0)) + wishvel[2] = 200; + else if ((q2pm->viewangles[PITCH] >= 15) && (q2pm->cmd.forwardmove > 0)) + wishvel[2] = -200; + else if (q2pm->cmd.upmove > 0) + wishvel[2] = 200; + else if (q2pm->cmd.upmove < 0) + wishvel[2] = -200; + else + wishvel[2] = 0; + + // limit horizontal speed when on a ladder + if (wishvel[0] < -25) + wishvel[0] = -25; + else if (wishvel[0] > 25) + wishvel[0] = 25; + + if (wishvel[1] < -25) + wishvel[1] = -25; + else if (wishvel[1] > 25) + wishvel[1] = 25; + } + + + // + // add water currents + // + + if (q2pm->watertype & MASK_CURRENT) + { + memset(v, 0, sizeof(vec3_t)); + + if (q2pm->watertype & Q2CONTENTS_CURRENT_0) + v[0] += 1; + if (q2pm->watertype & Q2CONTENTS_CURRENT_90) + v[1] += 1; + if (q2pm->watertype & Q2CONTENTS_CURRENT_180) + v[0] -= 1; + if (q2pm->watertype & Q2CONTENTS_CURRENT_270) + v[1] -= 1; + if (q2pm->watertype & Q2CONTENTS_CURRENT_UP) + v[2] += 1; + if (q2pm->watertype & Q2CONTENTS_CURRENT_DOWN) + v[2] -= 1; + + s = pm_waterspeed; + if ((q2pm->waterlevel == 1) && (q2pm->groundentity)) + s /= 2; + + VectorMA (wishvel, s, v, wishvel); + } + + // + // add conveyor belt velocities + // + + if (q2pm->groundentity) + { + memset(v, 0, sizeof(vec3_t)); + + if (q2pml.groundcontents & Q2CONTENTS_CURRENT_0) + v[0] += 1; + if (q2pml.groundcontents & Q2CONTENTS_CURRENT_90) + v[1] += 1; + if (q2pml.groundcontents & Q2CONTENTS_CURRENT_180) + v[0] -= 1; + if (q2pml.groundcontents & Q2CONTENTS_CURRENT_270) + v[1] -= 1; + if (q2pml.groundcontents & Q2CONTENTS_CURRENT_UP) + v[2] += 1; + if (q2pml.groundcontents & Q2CONTENTS_CURRENT_DOWN) + v[2] -= 1; + + VectorMA (wishvel, 100 /* q2pm->groundentity->speed */, v, wishvel); + } +} + + +/* +=================== +PMQ2_WaterMove + +=================== +*/ +void PMQ2_WaterMove (void) +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + +// +// user intentions +// + for (i=0 ; i<3 ; i++) + wishvel[i] = q2pml.forward[i]*q2pm->cmd.forwardmove + q2pml.right[i]*q2pm->cmd.sidemove; + + if (!q2pm->cmd.forwardmove && !q2pm->cmd.sidemove && !q2pm->cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else + wishvel[2] += q2pm->cmd.upmove; + + PMQ2_AddCurrents (wishvel); + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if (wishspeed > pm_maxspeed) + { + VectorScale (wishvel, pm_maxspeed/wishspeed, wishvel); + wishspeed = pm_maxspeed; + } + wishspeed *= 0.5; + + PMQ2_Accelerate (wishdir, wishspeed, pm_wateraccelerate); + + PMQ2_StepSlideMove (); +} + + +/* +=================== +PMQ2_AirMove + +=================== +*/ +void PMQ2_AirMove (void) +{ + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + float maxspeed; + + fmove = q2pm->cmd.forwardmove; + smove = q2pm->cmd.sidemove; + +//!!!!! pitch should be 1/3 so this isn't needed??! +#if 0 + q2pml.forward[2] = 0; + q2pml.right[2] = 0; + VectorNormalize (q2pml.forward); + VectorNormalize (q2pml.right); +#endif + + for (i=0 ; i<2 ; i++) + wishvel[i] = q2pml.forward[i]*fmove + q2pml.right[i]*smove; + wishvel[2] = 0; + + PMQ2_AddCurrents (wishvel); + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + +// +// clamp to server defined max speed +// + maxspeed = (q2pm->s.pm_flags & Q2PMF_DUCKED) ? pm_duckspeed : pm_maxspeed; + + if (wishspeed > maxspeed) + { + VectorScale (wishvel, maxspeed/wishspeed, wishvel); + wishspeed = maxspeed; + } + + if ( q2pml.ladder ) + { + PMQ2_Accelerate (wishdir, wishspeed, pm_accelerate); + if (!wishvel[2]) + { + if (q2pml.velocity[2] > 0) + { + q2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime; + if (q2pml.velocity[2] < 0) + q2pml.velocity[2] = 0; + } + else + { + q2pml.velocity[2] += q2pm->s.gravity * q2pml.frametime; + if (q2pml.velocity[2] > 0) + q2pml.velocity[2] = 0; + } + } + PMQ2_StepSlideMove (); + } + else if ( q2pm->groundentity ) + { // walking on ground + q2pml.velocity[2] = 0; //!!! this is before the accel + PMQ2_Accelerate (wishdir, wishspeed, pm_accelerate); + +// PGM -- fix for negative trigger_gravity fields +// q2pml.velocity[2] = 0; + if(q2pm->s.gravity > 0) + q2pml.velocity[2] = 0; + else + q2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime; +// PGM + + if (!q2pml.velocity[0] && !q2pml.velocity[1]) + return; + PMQ2_StepSlideMove (); + } + else + { // not on ground, so little effect on velocity + if (pm_airaccelerate) + PMQ2_AirAccelerate (wishdir, wishspeed, pm_accelerate); + else + PMQ2_Accelerate (wishdir, wishspeed, 1); + // add gravity + q2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime; + PMQ2_StepSlideMove (); + } +} + + + +/* +============= +PMQ2_CatagorizePosition +============= +*/ +void PMQ2_CatagorizePosition (void) +{ + vec3_t point; + int cont; + q2trace_t trace; + int sample1; + int sample2; + +// if the player hull point one unit down is solid, the player +// is on ground + +// see if standing on something solid + point[0] = q2pml.origin[0]; + point[1] = q2pml.origin[1]; + point[2] = q2pml.origin[2] - 0.25; + if (q2pml.velocity[2] > 180) //!!ZOID changed from 100 to 180 (ramp accel) + { + q2pm->s.pm_flags &= ~Q2PMF_ON_GROUND; + q2pm->groundentity = NULL; + } + else + { + trace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, point); + q2pml.groundplane = trace.plane; + q2pml.groundsurface = trace.surface; + q2pml.groundcontents = trace.contents; + + if (!trace.ent || (trace.plane.normal[2] < 0.7 && !trace.startsolid) ) + { + q2pm->groundentity = NULL; + q2pm->s.pm_flags &= ~Q2PMF_ON_GROUND; + } + else + { + q2pm->groundentity = trace.ent; + + // hitting solid ground will end a waterjump + if (q2pm->s.pm_flags & Q2PMF_TIME_WATERJUMP) + { + q2pm->s.pm_flags &= ~(Q2PMF_TIME_WATERJUMP | Q2PMF_TIME_LAND | Q2PMF_TIME_TELEPORT); + q2pm->s.pm_time = 0; + } + + if (! (q2pm->s.pm_flags & Q2PMF_ON_GROUND) ) + { // just hit the ground + q2pm->s.pm_flags |= Q2PMF_ON_GROUND; + // don't do landing time if we were just going down a slope + if (q2pml.velocity[2] < -200) + { + q2pm->s.pm_flags |= Q2PMF_TIME_LAND; + // don't allow another jump for a little while + if (q2pml.velocity[2] < -400) + q2pm->s.pm_time = 25; + else + q2pm->s.pm_time = 18; + } + } + } + +#if 0 + if (trace.fraction < 1.0 && trace.ent && q2pml.velocity[2] < 0) + q2pml.velocity[2] = 0; +#endif + + if (q2pm->numtouch < MAXTOUCH && trace.ent) + { + q2pm->touchents[q2pm->numtouch] = trace.ent; + q2pm->numtouch++; + } + } + +// +// get waterlevel, accounting for ducking +// + q2pm->waterlevel = 0; + q2pm->watertype = 0; + + sample2 = q2pm->viewheight - q2pm->mins[2]; + sample1 = sample2 / 2; + + point[2] = q2pml.origin[2] + q2pm->mins[2] + 1; + cont = q2pm->pointcontents (point); + + if (cont & MASK_WATER) + { + q2pm->watertype = cont; + q2pm->waterlevel = 1; + point[2] = q2pml.origin[2] + q2pm->mins[2] + sample1; + cont = q2pm->pointcontents (point); + if (cont & MASK_WATER) + { + q2pm->waterlevel = 2; + point[2] = q2pml.origin[2] + q2pm->mins[2] + sample2; + cont = q2pm->pointcontents (point); + if (cont & MASK_WATER) + q2pm->waterlevel = 3; + } + } + +} + + +/* +============= +PMQ2_CheckJump +============= +*/ +void PMQ2_CheckJump (void) +{ + if (q2pm->s.pm_flags & Q2PMF_TIME_LAND) + { // hasn't been long enough since landing to jump again + return; + } + + if (q2pm->cmd.upmove < 10) + { // not holding jump + q2pm->s.pm_flags &= ~Q2PMF_JUMP_HELD; + return; + } + + // must wait for jump to be released + if (q2pm->s.pm_flags & Q2PMF_JUMP_HELD) + return; + + if (q2pm->s.pm_type == Q2PM_DEAD) + return; + + if (q2pm->waterlevel >= 2) + { // swimming, not jumping + q2pm->groundentity = NULL; + + if (q2pml.velocity[2] <= -300) + return; + + if (q2pm->watertype == Q2CONTENTS_WATER) + q2pml.velocity[2] = 100; + else if (q2pm->watertype == Q2CONTENTS_SLIME) + q2pml.velocity[2] = 80; + else + q2pml.velocity[2] = 50; + return; + } + + if (q2pm->groundentity == NULL) + return; // in air, so no effect + + q2pm->s.pm_flags |= Q2PMF_JUMP_HELD; + + q2pm->groundentity = NULL; + q2pml.velocity[2] += 270; + if (q2pml.velocity[2] < 270) + q2pml.velocity[2] = 270; +} + + +/* +============= +PMQ2_CheckSpecialMovement +============= +*/ +void PMQ2_CheckSpecialMovement (void) +{ + vec3_t spot; + int cont; + vec3_t flatforward; + q2trace_t trace; + + if (q2pm->s.pm_time) + return; + + q2pml.ladder = false; + + // check for ladder + flatforward[0] = q2pml.forward[0]; + flatforward[1] = q2pml.forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + VectorMA (q2pml.origin, 1, flatforward, spot); + trace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, spot); + if ((trace.fraction < 1) && (trace.contents & Q2CONTENTS_LADDER)) + q2pml.ladder = true; + + // check for water jump + if (q2pm->waterlevel != 2) + return; + + VectorMA (q2pml.origin, 30, flatforward, spot); + spot[2] += 4; + cont = q2pm->pointcontents (spot); + if (!(cont & Q2CONTENTS_SOLID)) + return; + + spot[2] += 16; + cont = q2pm->pointcontents (spot); + if (cont) + return; + // jump out of water + VectorScale (flatforward, 50, q2pml.velocity); + q2pml.velocity[2] = 350; + + q2pm->s.pm_flags |= Q2PMF_TIME_WATERJUMP; + q2pm->s.pm_time = 255; +} + + +/* +=============== +PMQ2_FlyMove +=============== +*/ +void PMQ2_FlyMove (qboolean doclip) +{ + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + vec3_t end; + q2trace_t trace; + + q2pm->viewheight = DEFAULT_VIEWHEIGHT; + + // friction + + speed = Length (q2pml.velocity); + if (speed < 1) + { + VectorCopy (vec3_origin, q2pml.velocity); + } + else + { + drop = 0; + + friction = pm_friction*1.5; // extra friction + control = speed < pm_stopspeed ? pm_stopspeed : speed; + drop += control*friction*q2pml.frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (q2pml.velocity, newspeed, q2pml.velocity); + } + + // accelerate + fmove = q2pm->cmd.forwardmove; + smove = q2pm->cmd.sidemove; + + VectorNormalize (q2pml.forward); + VectorNormalize (q2pml.right); + + for (i=0 ; i<3 ; i++) + wishvel[i] = q2pml.forward[i]*fmove + q2pml.right[i]*smove; + wishvel[2] += q2pm->cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > pm_maxspeed) + { + VectorScale (wishvel, pm_maxspeed/wishspeed, wishvel); + wishspeed = pm_maxspeed; + } + + + currentspeed = DotProduct(q2pml.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = pm_accelerate*q2pml.frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + q2pml.velocity[i] += accelspeed*wishdir[i]; + + if (doclip) { + for (i=0 ; i<3 ; i++) + end[i] = q2pml.origin[i] + q2pml.frametime * q2pml.velocity[i]; + + trace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, end); + + VectorCopy (trace.endpos, q2pml.origin); + } else { + // move + VectorMA (q2pml.origin, q2pml.frametime, q2pml.velocity, q2pml.origin); + } +} + + +/* +============== +PMQ2_CheckDuck + +Sets mins, maxs, and q2pm->viewheight +============== +*/ +void PMQ2_CheckDuck (void) +{ + q2trace_t trace; + + q2pm->mins[0] = -16; + q2pm->mins[1] = -16; + + q2pm->maxs[0] = 16; + q2pm->maxs[1] = 16; + + if (q2pm->s.pm_type == Q2PM_GIB) + { + q2pm->mins[2] = 0; + q2pm->maxs[2] = 16; + q2pm->viewheight = 8; + return; + } + + q2pm->mins[2] = -24; + + if (q2pm->s.pm_type == Q2PM_DEAD) + { + q2pm->s.pm_flags |= Q2PMF_DUCKED; + } + else if (q2pm->cmd.upmove < 0 && (q2pm->s.pm_flags & Q2PMF_ON_GROUND) ) + { // duck + q2pm->s.pm_flags |= Q2PMF_DUCKED; + } + else + { // stand up if possible + if (q2pm->s.pm_flags & Q2PMF_DUCKED) + { + // try to stand up + q2pm->maxs[2] = 32; + trace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, q2pml.origin); + if (!trace.allsolid) + q2pm->s.pm_flags &= ~Q2PMF_DUCKED; + } + } + + if (q2pm->s.pm_flags & Q2PMF_DUCKED) + { + q2pm->maxs[2] = 4; + q2pm->viewheight = -2; + } + else + { + q2pm->maxs[2] = 32; + q2pm->viewheight = DEFAULT_VIEWHEIGHT; + } +} + + +/* +============== +PMQ2_DeadMove +============== +*/ +void PMQ2_DeadMove (void) +{ + float forward; + + if (!q2pm->groundentity) + return; + + // extra friction + + forward = Length (q2pml.velocity); + forward -= 20; + if (forward <= 0) + { + memset(q2pml.velocity, 0, sizeof(vec3_t)); + } + else + { + VectorNormalize (q2pml.velocity); + VectorScale (q2pml.velocity, forward, q2pml.velocity); + } +} + + +qboolean PMQ2_GoodPosition (void) +{ + q2trace_t trace; + vec3_t origin, end; + int i; + + if (q2pm->s.pm_type == Q2PM_SPECTATOR) + return true; + + for (i=0 ; i<3 ; i++) + origin[i] = end[i] = q2pm->s.origin[i]*0.125; + trace = q2pm->trace (origin, q2pm->mins, q2pm->maxs, end); + + return !trace.allsolid; +} + +/* +================ +PMQ2_SnapPosition + +On exit, the origin will have a value that is pre-quantized to the 0.125 +precision of the network channel and in a valid position. +================ +*/ +void PMQ2_SnapPosition (void) +{ + int sign[3]; + int i, j, bits; + short base[3]; + // try all single bits first + static int jitterbits[8] = {0,4,1,2,3,5,6,7}; + + // snap velocity to eigths + for (i=0 ; i<3 ; i++) + q2pm->s.velocity[i] = (int)(q2pml.velocity[i]*8); + + for (i=0 ; i<3 ; i++) + { + if (q2pml.origin[i] >= 0) + sign[i] = 1; + else + sign[i] = -1; + q2pm->s.origin[i] = (int)(q2pml.origin[i]*8); + if (q2pm->s.origin[i]*0.125 == q2pml.origin[i]) + sign[i] = 0; + } + VectorCopy (q2pm->s.origin, base); + + // try all combinations + for (j=0 ; j<8 ; j++) + { + bits = jitterbits[j]; + VectorCopy (base, q2pm->s.origin); + for (i=0 ; i<3 ; i++) + if (bits & (1<s.origin[i] += sign[i]; + + if (PMQ2_GoodPosition ()) + return; + } + + // go back to the last position + VectorCopy (q2pml.previous_origin, q2pm->s.origin); +// Con_DPrintf ("using previous_origin\n"); +} + +#if 0 +//NO LONGER USED +/* +================ +PMQ2_InitialSnapPosition + +================ +*/ +void PMQ2_InitialSnapPosition (void) +{ + int x, y, z; + short base[3]; + + VectorCopy (q2pm->s.origin, base); + + for (z=1 ; z>=-1 ; z--) + { + q2pm->s.origin[2] = base[2] + z; + for (y=1 ; y>=-1 ; y--) + { + q2pm->s.origin[1] = base[1] + y; + for (x=1 ; x>=-1 ; x--) + { + q2pm->s.origin[0] = base[0] + x; + if (PMQ2_GoodPosition ()) + { + q2pml.origin[0] = q2pm->s.origin[0]*0.125; + q2pml.origin[1] = q2pm->s.origin[1]*0.125; + q2pml.origin[2] = q2pm->s.origin[2]*0.125; + VectorCopy (q2pm->s.origin, q2pml.previous_origin); + return; + } + } + } + } + + Con_DPrintf ("Bad InitialSnapPosition\n"); +} +#else +/* +================ +PMQ2_InitialSnapPosition + +================ +*/ +void PMQ2_InitialSnapPosition(void) +{ + int x, y, z; + short base[3]; + static int offset[3] = { 0, -1, 1 }; + + VectorCopy (q2pm->s.origin, base); + + for ( z = 0; z < 3; z++ ) { + q2pm->s.origin[2] = base[2] + offset[ z ]; + for ( y = 0; y < 3; y++ ) { + q2pm->s.origin[1] = base[1] + offset[ y ]; + for ( x = 0; x < 3; x++ ) { + q2pm->s.origin[0] = base[0] + offset[ x ]; + if (PMQ2_GoodPosition ()) { + q2pml.origin[0] = q2pm->s.origin[0]*0.125; + q2pml.origin[1] = q2pm->s.origin[1]*0.125; + q2pml.origin[2] = q2pm->s.origin[2]*0.125; + VectorCopy (q2pm->s.origin, q2pml.previous_origin); + return; + } + } + } + } + + Con_DPrintf ("Bad InitialSnapPosition\n"); +} + +#endif + +/* +================ +PMQ2_ClampAngles + +================ +*/ +void PMQ2_ClampAngles (void) +{ + short temp; + int i; + + if (q2pm->s.pm_flags & Q2PMF_TIME_TELEPORT) + { + q2pm->viewangles[YAW] = SHORT2ANGLE(q2pm->cmd.angles[YAW] + q2pm->s.delta_angles[YAW]); + q2pm->viewangles[PITCH] = 0; + q2pm->viewangles[ROLL] = 0; + } + else + { + // circularly clamp the angles with deltas + for (i=0 ; i<3 ; i++) + { + temp = q2pm->cmd.angles[i] + q2pm->s.delta_angles[i]; + q2pm->viewangles[i] = SHORT2ANGLE(temp); + } + + // don't let the player look up or down more than 90 degrees + if (q2pm->viewangles[PITCH] > 89 && q2pm->viewangles[PITCH] < 180) + q2pm->viewangles[PITCH] = 89; + else if (q2pm->viewangles[PITCH] < 271 && q2pm->viewangles[PITCH] >= 180) + q2pm->viewangles[PITCH] = 271; + } + AngleVectors (q2pm->viewangles, q2pml.forward, q2pml.right, q2pml.up); +} + +/* +================ +Pmove + +Can be called by either the server or the client +================ +*/ +void Q2_Pmove (q2pmove_t *pmove) +{ + q2pm = pmove; + + // clear results + q2pm->numtouch = 0; + memset (q2pm->viewangles, 0, sizeof(vec3_t)); + q2pm->viewheight = 0; + q2pm->groundentity = 0; + q2pm->watertype = 0; + q2pm->waterlevel = 0; + + // clear all pmove local vars + memset (&q2pml, 0, sizeof(q2pml)); + + // convert origin and velocity to float values + q2pml.origin[0] = q2pm->s.origin[0]*0.125; + q2pml.origin[1] = q2pm->s.origin[1]*0.125; + q2pml.origin[2] = q2pm->s.origin[2]*0.125; + + q2pml.velocity[0] = q2pm->s.velocity[0]*0.125; + q2pml.velocity[1] = q2pm->s.velocity[1]*0.125; + q2pml.velocity[2] = q2pm->s.velocity[2]*0.125; + + // save old org in case we get stuck + VectorCopy (q2pm->s.origin, q2pml.previous_origin); + + q2pml.frametime = q2pm->cmd.msec * 0.001; + + PMQ2_ClampAngles (); + + if (q2pm->s.pm_type == Q2PM_SPECTATOR) + { + PMQ2_FlyMove (false); + PMQ2_SnapPosition (); + return; + } + + if (q2pm->s.pm_type >= Q2PM_DEAD) + { + q2pm->cmd.forwardmove = 0; + q2pm->cmd.sidemove = 0; + q2pm->cmd.upmove = 0; + } + + if (q2pm->s.pm_type == Q2PM_FREEZE) + return; // no movement at all + + // set mins, maxs, and viewheight + PMQ2_CheckDuck (); + + if (q2pm->snapinitial) + PMQ2_InitialSnapPosition (); + + // set groundentity, watertype, and waterlevel + PMQ2_CatagorizePosition (); + + if (q2pm->s.pm_type == Q2PM_DEAD) + PMQ2_DeadMove (); + + PMQ2_CheckSpecialMovement (); + + // drop timing counter + if (q2pm->s.pm_time) + { + int msec; + + msec = q2pm->cmd.msec >> 3; + if (!msec) + msec = 1; + if ( msec >= q2pm->s.pm_time) + { + q2pm->s.pm_flags &= ~(Q2PMF_TIME_WATERJUMP | Q2PMF_TIME_LAND | Q2PMF_TIME_TELEPORT); + q2pm->s.pm_time = 0; + } + else + q2pm->s.pm_time -= msec; + } + + if (q2pm->s.pm_flags & Q2PMF_TIME_TELEPORT) + { // teleport pause stays exactly in place + } + else if (q2pm->s.pm_flags & Q2PMF_TIME_WATERJUMP) + { // waterjump has no control, but falls + q2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime; + if (q2pml.velocity[2] < 0) + { // cancel as soon as we are falling down again + q2pm->s.pm_flags &= ~(Q2PMF_TIME_WATERJUMP | Q2PMF_TIME_LAND | Q2PMF_TIME_TELEPORT); + q2pm->s.pm_time = 0; + } + + PMQ2_StepSlideMove (); + } + else + { + PMQ2_CheckJump (); + + PMQ2_Friction (); + + if (q2pm->waterlevel >= 2) + PMQ2_WaterMove (); + else { + vec3_t angles; + + VectorCopy(q2pm->viewangles, angles); + if (angles[PITCH] > 180) + angles[PITCH] = angles[PITCH] - 360; + angles[PITCH] /= 3; + + AngleVectors (angles, q2pml.forward, q2pml.right, q2pml.up); + + PMQ2_AirMove (); + } + } + + // set groundentity, watertype, and waterlevel for final spot + PMQ2_CatagorizePosition (); + + PMQ2_SnapPosition (); +} + + +#endif + diff --git a/engine/common/quakeasm.h b/engine/common/quakeasm.h new file mode 100644 index 000000000..cc02062e1 --- /dev/null +++ b/engine/common/quakeasm.h @@ -0,0 +1,289 @@ +/* +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. + +*/ +// +// quakeasm.h: general asm header file +// +#ifdef _WIN32 +#define __i386__ 1 +#endif + +#if defined(NOASM) +#define id386 0 +#else +#ifdef __i386__ +#define id386 1 +#else +#define id386 0 +#endif +#endif + + +#ifdef MQUAKE +#define SWQUAKE +#define GLQUAKE +#endif + +// !!! must be kept the same as in d_iface.h !!! +#define TRANSPARENT_COLOR 255 + +#ifndef NeXT +#ifdef SWQUAKE + .extern C(d_zistepu) + .extern C(d_pzbuffer) + .extern C(d_zistepv) + .extern C(d_zrowbytes) + .extern C(d_ziorigin) + .extern C(r_turb_s) + .extern C(r_turb_t) + .extern C(r_turb_pdest) + .extern C(r_turb_spancount) + .extern C(r_turb_turb) + .extern C(r_turb_pbase) + .extern C(r_turb_sstep) + .extern C(r_turb_tstep) + .extern C(r_bmodelactive) + .extern C(d_sdivzstepu) + .extern C(d_tdivzstepu) + .extern C(d_sdivzstepv) + .extern C(d_tdivzstepv) + .extern C(d_sdivzorigin) + .extern C(d_tdivzorigin) + .extern C(sadjust) + .extern C(tadjust) + .extern C(bbextents) + .extern C(bbextentt) + .extern C(cacheblock) + .extern C(d_viewbuffer) + .extern C(cachewidth) + .extern C(d_pzbuffer) + .extern C(d_zrowbytes) + .extern C(d_zwidth) + .extern C(d_scantable) + .extern C(r_lightptr) + .extern C(r_numvblocks) + .extern C(prowdestbase) + .extern C(pbasesource) + .extern C(r_lightwidth) + .extern C(lightright) + .extern C(lightrightstep) + .extern C(lightdeltastep) + .extern C(lightdelta) + .extern C(lightright) + .extern C(lightdelta) + .extern C(sourcetstep) + .extern C(surfrowbytes) + .extern C(lightrightstep) + .extern C(lightdeltastep) + .extern C(r_sourcemax) + .extern C(r_stepback) + .extern C(colormap) + .extern C(blocksize) + .extern C(sourcesstep) + .extern C(lightleft) + .extern C(blockdivshift) + .extern C(blockdivmask) + .extern C(lightleftstep) + .extern C(r_origin) + .extern C(r_ppn) + .extern C(r_pup) + .extern C(r_pright) + .extern C(ycenter) + .extern C(xcenter) + .extern C(d_vrectbottom_particle) + .extern C(d_vrectright_particle) + .extern C(d_vrecty) + .extern C(d_vrectx) + .extern C(d_pix_shift) + .extern C(d_pix_min) + .extern C(d_pix_max) + .extern C(d_y_aspect_shift) + .extern C(screenwidth) + .extern C(r_leftclipped) + .extern C(r_leftenter) + .extern C(r_rightclipped) + .extern C(r_rightenter) + .extern C(modelorg) + .extern C(xscale) + .extern C(r_refdef) + .extern C(yscale) + .extern C(r_leftexit) + .extern C(r_rightexit) + .extern C(r_lastvertvalid) + .extern C(cacheoffset) + .extern C(newedges) + .extern C(removeedges) + .extern C(r_pedge) + .extern C(r_framecount) + .extern C(r_u1) + .extern C(r_emitted) + .extern C(edge_p) + .extern C(surface_p) + .extern C(surfaces) + .extern C(r_lzi1) + .extern C(r_v1) + .extern C(r_ceilv1) + .extern C(r_nearzi) + .extern C(r_nearzionly) + .extern C(edge_aftertail) + .extern C(edge_tail) + .extern C(current_iv) + .extern C(edge_head_u_shift20) + .extern C(span_p) + .extern C(edge_head) + .extern C(fv) + .extern C(edge_tail_u_shift20) + + .extern C(r_apoldverts) //trivertex_t + .extern C(r_apnewverts) + .extern C(r_afrntlerp) //float of quantity. + .extern C(r_abacklerp) + + .extern C(r_anumverts) + .extern C(aliastransform) + .extern C(r_avertexnormals) + .extern C(r_plightvec) + .extern C(r_ambientlight) + .extern C(r_shadelight) + .extern C(aliasxcenter) + .extern C(aliasycenter) + .extern C(a_sstepxfrac) + .extern C(r_affinetridesc) + .extern C(acolormap) + .extern C(d_pcolormap) + .extern C(r_affinetridesc) + .extern C(d_sfrac) + .extern C(d_ptex) + .extern C(d_pedgespanpackage) + .extern C(d_tfrac) + .extern C(d_light) + .extern C(d_zi) + .extern C(d_pdest) + .extern C(d_pz) + .extern C(d_aspancount) + .extern C(erroradjustup) + .extern C(errorterm) + .extern C(d_xdenom) + .extern C(r_p0) + .extern C(r_p1) + .extern C(r_p2) + .extern C(a_tstepxfrac) + .extern C(r_sstepx) + .extern C(r_tstepx) + .extern C(a_ststepxwhole) + .extern C(zspantable) + .extern C(skintable) + .extern C(r_zistepx) + .extern C(erroradjustdown) + .extern C(d_countextrastep) + .extern C(ubasestep) + .extern C(a_ststepxwhole) + .extern C(a_tstepxfrac) + .extern C(r_lstepx) + .extern C(a_spans) + .extern C(erroradjustdown) + .extern C(d_pdestextrastep) + .extern C(d_pzextrastep) + .extern C(d_sfracextrastep) + .extern C(d_ptexextrastep) + .extern C(d_countextrastep) + .extern C(d_tfracextrastep) + .extern C(d_lightextrastep) + .extern C(d_ziextrastep) + .extern C(d_pdestbasestep) + .extern C(d_pzbasestep) + .extern C(d_sfracbasestep) + .extern C(d_ptexbasestep) + .extern C(ubasestep) + .extern C(d_tfracbasestep) + .extern C(d_lightbasestep) + .extern C(d_zibasestep) + .extern C(zspantable) + .extern C(r_lstepy) + .extern C(r_sstepy) + .extern C(r_tstepy) + .extern C(r_zistepy) + .extern C(D_PolysetSetEdgeTable) + .extern C(D_RasterizeAliasPolySmooth) + + .extern float_point5 + .extern Float2ToThe31nd + .extern izistep + .extern izi + .extern FloatMinus2ToThe31nd + .extern float_1 + .extern float_particle_z_clip + .extern float_minus_1 + .extern float_0 + .extern fp_16 + .extern fp_64k + .extern fp_1m + .extern fp_1m_minus_1 + .extern fp_8 + .extern entryvec_table + .extern advancetable + .extern sstep + .extern tstep + .extern pspantemp + .extern counttemp + .extern jumptemp + .extern reciprocal_table + .extern DP_Count + .extern DP_u + .extern DP_v + .extern DP_32768 + .extern DP_Color + .extern DP_Pix + .extern DP_EntryTable + .extern pbase + .extern s + .extern t + .extern sfracf + .extern tfracf + .extern snext + .extern tnext + .extern spancountminus1 + .extern zi16stepu + .extern sdivz16stepu + .extern tdivz16stepu + .extern zi8stepu + .extern sdivz8stepu + .extern tdivz8stepu + .extern reciprocal_table_16 + .extern entryvec_table_16 + .extern ceil_cw + .extern single_cw + .extern fp_64kx64k + .extern pz + .extern spr8entryvec_table +#endif + + .extern C(snd_scaletable) + .extern C(paintbuffer) +// .extern C(snd_linear_count) + .extern C(cursndcard) + .extern C(snd_p) + .extern C(snd_vol) + .extern C(snd_out) + .extern C(vright) + .extern C(vup) + .extern C(vpn) + .extern C(BOPS_Error) + +#endif diff --git a/engine/common/tlout.h b/engine/common/tlout.h new file mode 100644 index 000000000..23f8b4e93 --- /dev/null +++ b/engine/common/tlout.h @@ -0,0 +1,186 @@ +"STL_LANGUAGENAME \"English\"\n" +"STL_CLIENTCONNECTED \"client %s connected\\n\"\n" +"STL_SPECTATORCONNECTED \"spectator %s connected\\n\"\n" +"STL_RECORDEDCLIENTCONNECTED \"recorded client %s connected\\n\"\n" +"STL_RECORDEDSPECTATORCONNECTED \"recorded spectator %s connected\\n\"\n" +"STL_CLIENTWASBANNED \"%s was banned\\n\"\n" +"STL_YOUWEREBANNED \"You were banned\\n\"\n" +"STL_YOUAREBANNED \"You are still banned\\n\"\n" +"STL_CLIENTTIMEDOUT \"Client %s timed out\\n\"\n" +"STL_LOADZOMIBETIMEDOUT \"LoadZombie %s timed out\\n\"\n" +"STL_CLIENTWASKICKED \"%s was kicked\\n\"\n" +"STL_YOUWEREKICKED \"You were kicked\\n\"\n" +"STL_YOUWEREKICKEDNAMESPAM \"You were kicked for name spamming\\n\"\n" +"STL_CLIENTKICKEDNAMESPAM \"%s was kicked for name spamming\\n\"\n" +"STL_GODON \"godmode ON\\n\"\n" +"STL_GODOFF \"godmode OFF\\n\"\n" +"STL_NOCLIPON \"noclip ON\\n\"\n" +"STL_NOCLIPOFF \"noclip OFF\"\n" +"STL_CLIENTISCUFFEDPERMANENTLY \"%s is still cuffed\\n\"\n" +"STL_CLIENTISCUFFED \"%s is was cuffed\\n\"\n" +"STL_CLIENTISSTILLCUFFED \"%s is still cuffed\\n\"\n" +"STL_YOUWERECUFFED \"You were cuffed\\n\"\n" +"STL_YOUARNTCUFFED \"You are no longer cuffed\\n\"\n" +"STL_CLIENTISCRIPPLEDPERMANENTLY \"%s is now crippled permanently\\n\"\n" +"STL_CLIENTISCRIPPLED \"%s is crippled\\n\"\n" +"STL_CLIENTISSTILLCRIPPLED \"%s is still crippled\\n\"\n" +"STL_YOUWERECLIPPLED \"You have been crippled\\n\"\n" +"STL_YOUARNTCRIPPLED \"You are no longer crippled\\n\"\n" +"STL_CLIENTISMUTEDPERMANENTLY \"%s was muted permanently\\n\"\n" +"STL_CLIENTISMUTED \"%s was muted\\n\"\n" +"STL_CLIENTISSTILLMUTED \"%s is muted (still)\\n\"\n" +"STL_YOUAREMUTED \"%s is muted\\n\"\n" +"STL_YOUARNTMUTED \"You are no longer muted\\n\"\n" +"STL_NONAMEASMUTE \"Muted players may not change thier names\\n\"\n" +"STL_MUTEDVOTE \"Sorry, you cannot vote when muted as it may allow you to send a message.\\n\"\n" +"STL_MUTEDCHAT \"You cannot chat while muted\\n\"\n" +"STL_FLOODPROTACTIVE \"floodprot: You can't talk for %i seconds\\n\"\n" +"STL_FLOODPROTTIME \"You can't talk for %i more seconds\\n\"\n" +"STL_BUFFERPROTECTION \"buffer overflow protection: failiure\\n\"\n" +"STL_FIRSTGREETING \"Welcome %s. Your time on this server is being logged and ranked\\n\"\n" +"STL_SHORTGREETING \"Welcome back %s. You have previously spent %i mins connected\\n\"\n" +"STL_BIGGREETING \"Welcome back %s. You have previously spent %i:%i hours connected\\n\"\n" +"STL_POSSIBLEMODELCHEAT \"warning: %s eyes or player model does not match\\n\"\n" +"STL_MAPCHEAT \"Map model file does not match (%s), %i != %i/%i.\\nYou may need a new version of the map, or the proper install files.\\n\"\n" +"STL_INVALIDTRACKCLIENT \"invalid player to track\\n\"\n" +"STL_BADNAME \"Can't change name - new is invalid\\n\"\n" +"STL_CLIENTNAMECHANGE \"%s changed thier name to %s\\n\"\n" +"STL_SERVERPAUSED \"server is paused\\n\"\n" +"STL_UPLOADDENIED \"Upload denied\\n\"\n" +"STL_NAMEDCLIENTDOESNTEXIST \"client does not exist\\n\"\n" +"STL_NOSUISIDEWHENDEAD \"Can't suiside -- Already dead\\n\"\n" +"STL_CANTPAUSE \"Can't pause. Not allowed\\n\"\n" +"STL_CANTPAUSESPEC \"Spectators may not pause the game\\n\"\n" +"STL_CLIENTPAUSED \"%s paused the game\\n\"\n" +"STL_CLIENTUNPAUSED \"%s unpaused the game\\n\"\n" +"STL_CLIENTLESSUNPAUSE \"pause released due to empty server\\n\"\n" +"STL_CURRENTRATE \"current rate is %i\\n\"\n" +"STL_RATESETTO \"rate is changed to %i\\n\"\n" +"STL_CURRENTMSGLEVEL \"current msg level is %i\\n\"\n" +"STL_MSGLEVELSET \"new msg level set to %i\\n\"\n" +"STL_GAMESAVED \"Server has saved the game\\n\"\n" +"STL_CLIENTDROPPED \"%s dropped\\n\"\n" +"STL_SNAPREFUSED \"%s refused remote screenshot\\n\"\n" +"STL_FINALVOTE \"%s casts final vote for '%s'\\n\"\n" +"STL_VOTE \"%s casts a vote for '%s'\\n\"\n" +"STL_SPEEDCHEATKICKED \"%s was kicked for speedcheating (%s)\\n\"\n" +"STL_SPEEDCHEATPOSSIBLE \"Speed cheat possibility, analyzing:\\n %d %.1f %d for: %s\\n\"\n" +"STL_INITED \"======== QuakeWorld Initialized ========\\n\"\n" +"STL_BACKBUFSET \"WARNING %s: [SV_New] Back buffered (%d0, clearing)\\n\"\n" +"STL_MESSAGEOVERFLOW \"WARNING: backbuf [%d] reliable overflow for %s\\n\"\n" +"STL_BUILDINGPHS \"Building PHS...\\n\"\n" +"STL_PHSINFO \"Average leafs visible / hearable / total: %i / %i / %i\\n\"\n" +"STL_BREAKSTATEMENT \"Break Statement\\n\"\n" +"STL_BADSPRINT \"tried to sprint to a non-client\\n\"\n" +"STL_NOPRECACHE \"no precache: %s\\n\"\n" +"STL_CANTFREEWORLD \"cannot free world entity\\n\"\n" +"STL_CANTFREEPLAYERS \"cannot free player entities\\n\"\n" +"STL_COMPILEROVER \"Compile took %f secs\\n\"\n" +"STL_EDICTWASFREE \"%s edict was free\\n\"\n" +"STL_NOFREEEDICTS \"WARNING: no free edicts\\n\"\n" +"STL_NEEDCHEATPARM \"You must run the server with -cheats to enable this command.\\n\"\n" +"STL_USERDOESNTEXIST \"Couldn't find user number %s\\n\"\n" +"STL_MAPCOMMANDUSAGE \"map : continue game on a new level\\n\"\n" +"STL_NOVOTING \"Voting was dissallowed\\n\"\n" +"STL_BADVOTE \"You arn't allowed to vote for that\\n\"\n" +"STL_VOTESREMOVED \"All votes removed.\\n\"\n" +"STL_OLDVOTEREMOVED \"Old vote removed.\\n\"\n" +"TL_EXECING \"execing %s\\n\"\n" +"TL_EXECCOMMANDUSAGE \"exec : execute a script file\\n\"\n" +"TL_EXECFAILED \"couldn't exec %s\\n\"\n" +"TL_FUNCOVERFLOW \"%s: overflow\\n\"\n" +"TL_CURRENTALIASCOMMANDS \"Current alias commands:\\n\"\n" +"TL_ALIASNAMETOOLONG \"Alias name is too long\\n\"\n" +"TL_ALIASRESTRICTIONLEVELERROR \"Alias is already bound with a higher restriction\\n\"\n" +"TL_ALIASLEVELCOMMANDUSAGE \"aliaslevel [execlevel]\\n\"\n" +"TL_ALIASNOTFOUND \"Alias not found\\n\"\n" +"TL_ALIASRAISELEVELERROR \"You arn't allowed to raise a command above your own level\\n\"\n" +"TL_ALIASRESTRICTIONLEVELWARN \"WARNING: %s is available to all clients, any client will be able to use it at the new level.\\n\"\n" +"TL_ALIASRESTRICTLEVEL \"alias %s is set to run at the user level of %i\\n\"\n" +"TL_ALIASLIST \"Alias list:\\n\"\n" +"TL_COMMANDLISTHEADER \"Command list:\\n\"\n" +"TL_CVARLISTHEADER \"CVar list:\\n\"\n" +"TL_RESTRICTCOMMANDRAISE \"You arn't allowed to raise a command above your own level\\n\"\n" +"TL_RESTRICTCOMMANDTOOHIGH \"You arn't allowed to alter a level above your own\\n\"\n" +"TL_RESTRICTCURRENTLEVEL \"%s is restricted to %i\\n\"\n" +"TL_RESTRICTCURRENTLEVELDEFAULT \"%s is restricted to rcon_level (%i)\\n\"\n" +"TL_RESTRICTNOTDEFINED \"restrict: %s not defined\\n\"\n" +"TL_WASRESTIRCTED \"%s was restricted.\\n\"\n" +"TL_COMMANDNOTDEFINED \"Unknown command \\\"%s\\\"\\n\"\n" +"TL_IFSYNTAX \"if [elseif ] [...] [else ]\\n\"\n" +"TL_IFSYNTAXERROR \"Not terminated\\n\"\n" +"TL_SETSYNTAX \"set \\n\"\n" +"TL_CANTXNOTCONNECTED \"Can't \\\"%s\\\", not connected\\n\"\n" +"TL_SHAREWAREVERSION \"Playing shareware version.\\n\"\n" +"TL_REGISTEREDVERSION \"Playing registered version.\\n\"\n" +"TL_CURRENTSEARCHPATH \"Current search path:\\n\"\n" +"TL_SERACHPATHISPACK \"%s (%i files)\\n\"\n" +"TL_SERACHPATHISZIP \"%s (%i files)\\n\"\n" +"TL_COMPRESSEDFILEOPENFAILED \"Tried opening a handle to a compressed stream - %s\\n\"\n" +"TL_ADDEDPACKFILE \"Added packfile %s (%i files)\\n\"\n" +"TL_COULDNTOPENZIP \"Failed opening zipfile \\\"%s\\\" corrupt?\\n\"\n" +"TL_ADDEDZIPFILE \"Added zipfile %s (%i files)\\n\"\n" +"TL_GAMEDIRAINTPATH \"Gamedir should be a single filename, not a path\\n\"\n" +"TL_KEYHASSLASH \"Can't use a key with a \\\\\\n\"\n" +"TL_KEYHASQUOTE \"Can't use a key with a \\\"\\n\"\n" +"TL_KEYTOOLONG \"Keys and values must be < 64 characters.\\n\"\n" +"TL_INFOSTRINGTOOLONG \"Info string length exceeded\\n\"\n" +"TL_STARKEYPROTECTED \"Can't set * keys\\n\"\n" +"TL_KEYHASNOVALUE \"MISSING VALUE\\n\"\n" +"TL_OVERSIZEPACKETFROM \"Warning: Oversize packet from %s\\n\"\n" +"TL_CONNECTIONLOSTORABORTED \"Connection lost or aborted\\n\"\n" +"TL_NETGETPACKETERROR \"NET_GetPacket: %s\\n\"\n" +"TL_NETSENDERROR \"NET_SendPacket ERROR: %i\\n\"\n" +"TL_NETBINDINTERFACE \"Binding to IP Interface Address of %s\\n\"\n" +"TL_IPADDRESSIS \"IP address %s\\n\"\n" +"TL_UDPINITED \"UDP Initialized\\n\"\n" +"TL_SERVERPORTINITED \"Server port Initialized\\n\"\n" +"TL_CLIENTPORTINITED \"Client port Initialized\\n\"\n" +"TL_OUTMESSAGEOVERFLOW \"%s:Outgoing message overflow\\n\"\n" +"TL_OUTOFORDERPACKET \"%s:Out of order packet %i at %i\\n\"\n" +"TL_DROPPEDPACKETCOUNT \"%s:Dropped %i packets at %i\\n\"\n" +"STL_SERVERUNSPAWNED \"Server ended\\n\"\n" +"STL_SERVERSPAWNED \"Server spawned.\\n\"\n" +"TL_EXEDATETIME \"Exe: %s %s\\n\"\n" +"TL_HEAPSIZE \"%4.1f megabyte heap\\n\"\n" +"TL_VERSION2 \"\\nVersion %4.2f (Build %04d)\\n\\n\"\n" +"TL_SERVERVERSION2 \"\\n%s Version %4.2f (Build %04d)\\n\\n\"\n" +"STL_SAVESYNTAX \"save : save a game\\n\"\n" +"STL_NORELATIVEPATHS \"Relative pathnames are not allowed.\\n\"\n" +"STL_SAVEGAMETO \"Saving game to %s...\\n\"\n" +"STL_ERRORCOULDNTOPEN \"ERROR: couldn't open.\\n\"\n" +"STL_SAVEDONE \"done.\\n\"\n" +"STL_LOADSYNTAX \"load : load a game\\n\"\n" +"STL_LOADGAMEFROM \"Loading game from %s...\\n\"\n" +"STL_BADSAVEVERSION \"Savegame is version %i, not %i\\n\"\n" +"STL_LOADFAILED \"Couldn't load map\\n\"\n" +"STL_NOMATERMODE \"Setting nomaster mode.\\n\"\n" +"STL_MASTERAT \"Master server at %s\\n\"\n" +"STL_SENDINGPING \"Sending a ping.\\n\"\n" +"STL_SHUTTINGDOWN \"Shutting down.\\n\"\n" +"STL_LOGGINGOFF \"File logging off.\\n\"\n" +"STL_LOGGINGTO \"Logging text to %s.\\n\"\n" +"STL_FLOGGINGOFF \"Frag file logging off.\\n\"\n" +"STL_FLOGGINGFAILED \"Can't open any logfiles.\\n\"\n" +"STL_FLOGGINGTO \"Logging frags to %s.\\n\"\n" +"STL_USERIDNOTONSERVER \"Userid %i is not on the server\\n\"\n" +"STL_CANTFINDMAP \"Can't find %s\\n\"\n" +"STL_SERVERINFOSETTINGS \"Server info settings:\\n\"\n" +"STL_SERVERINFOSYNTAX \"usage: serverinfo [ ]\\n\"\n" +"STL_LOCALINFOSETTINGS \"Local info settings:\\n\"\n" +"STL_LOCALINFOSYNTAX \"usage: localinfo [ ]\\n\"\n" +"STL_USERINFOSYNTAX \"Usage: info \\n\"\n" +"STL_FLOODPROTSETTINGS \"Current floodprot settings: \\nAfter %d msgs per %d seconds, silence for %d seconds\\n\"\n" +"STL_FLOODPROTNOTON \"No floodprots enabled.\\n\"\n" +"STL_FLOODPROTSYNTAX \"Usage: floodprot <# of messages> \\nUse floodprotmsg to set a custom message to say to the flooder.\\n\"\n" +"STL_NONEGATIVEVALUES \"All values must be positive numbers\\n\"\n" +"STL_TRACK10PLUSSMESSAGES \"Can only track up to 10 messages.\\n\"\n" +"STL_FLOODPROTCURRENTMESSAGE \"Current msg: %s\\n\"\n" +"STL_FLOODPROTMESSAGESYNTAX \"Usage: floodprotmsg \\\"\\\"\\n\"\n" +"STL_CURRENTGAMEDIR \"Current gamedir: %s\\n\"\n" +"STL_SVGAMEDIRUSAGE \"Usage: sv_gamedir \\n\"\n" +"STL_GAMEDIRCANTBEPATH \"*Gamedir should be a single filename, not a path\\n\"\n" +"STL_GAMEDIRUSAGE \"Usage: gamedir \\n\"\n" +"STL_SNAPTOOMANYFILES \"Snap: Couldn't create a file, clean some out.\\n\"\n" +"STL_SNAPREQUEST \"Requesting snap from user %d...\\n\"\n" +"STL_SNAPUSAGE \"Usage: snap \\n\"\n" diff --git a/engine/common/zone.c b/engine/common/zone.c new file mode 100644 index 000000000..f5da1cc4b --- /dev/null +++ b/engine/common/zone.c @@ -0,0 +1,1508 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// Z_zone.c + +#include "quakedef.h" +#ifdef _WIN32 +#include "winquake.h" +#endif + +#undef malloc +#undef free + +#define NOZONE +#define NOCACHE + +void Cache_FreeLow (int new_low_hunk); +void Cache_FreeHigh (int new_high_hunk); + + + + +#ifdef NOZONE //zone memory is for small dynamic things. +/* +void *Z_TagMalloc(int size, int tag) +{ + return malloc(size); +} + +void *Z_Malloc(int size) +{ + qbyte *buf; + buf = Z_TagMalloc(size, 1); + if (!buf) + Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + Q_memset(buf, 0, size); + return buf; +} + +void Z_Free (void *buf) +{ + free(buf); +} + +void Z_FreeTags (void *buf) +{ + free(buf); +} + +*/ + + + + + + + + + + + + + + + + + + +#define ZONEID 0x1d4a11 + +#define ZONESENTINAL 0xdeadbeaf + +typedef struct zone_s { +// int sentinal1; + + struct zone_s *next; + struct zone_s *prev; + int size; + int tag; + +// int sentinal2; +} zone_t; +zone_t *zone_head; +/* +void Z_CheckSentinals(void) +{ + zone_t *zone; + for(zone = zone_head; zone; zone=zone->next) + { + if (zone->sentinal1 != ZONESENTINAL || zone->sentinal2 != ZONESENTINAL) + Sys_Error("Memory sentinal destroyed\n"); + } +}*/ + +int Z_Allocated(void) +{ + zone_t *zone; + int used = 0; + for(zone = zone_head; zone; zone=zone->next) + { + used += zone->size; + } + + return used; +} + +void Z_Free (void *c) +{ + zone_t *nz; + nz = ((zone_t *)c)-1; + +// Z_CheckSentinals(); + +// if (nz->sentinal1 != ZONESENTINAL || nz->sentinal2 != ZONESENTINAL) +// Sys_Error("zone was not z_malloced\n"); + + if (nz->next) + nz->next->prev = nz->prev; + if (nz->prev) + nz->prev->next = nz->next; + + if (nz == zone_head) + zone_head = nz->next; + + free(nz); +} + +void Z_FreeTags(int tag) +{ + zone_t *zone, *next; + for(zone = zone_head; zone; zone=next) + { + next = zone->next; + if (zone->tag == tag) + Z_Free(zone+1); + } +} + +#ifdef NAMEDMALLOCS +void *Z_BaseTagMalloc (int size, int tag, qboolean clear, char *descrip, ...) +#else +void *Z_BaseTagMalloc (int size, int tag, qboolean clear) +#endif +{ +#ifdef NAMEDMALLOCS + va_list argptr; + char buffer[512]; +#endif + void *buf; + zone_t *nt; + +// Z_CheckSentinals(); + + if (size <= 0) + Sys_Error ("Z_Malloc: size %i", size); + +#ifdef NAMEDMALLOCS + + va_start (argptr, descrip); + vsprintf (buffer, descrip,argptr); + va_end (argptr); + + nt = malloc(size + sizeof(zone_t)+strlen(buffer)+1); +#else + nt = malloc(size + sizeof(zone_t)); +#endif + if (!nt) + Sys_Error("Z_BaseTagMalloc: failed on allocation of %i bytes", size); + nt->next = zone_head; + nt->prev = NULL; + nt->size = size; + nt->tag = tag; +// nt->sentinal1 = ZONESENTINAL; +// nt->sentinal2 = ZONESENTINAL; + if (zone_head) + zone_head->prev = nt; + zone_head = nt; + buf = (void *)(nt+1); + if (clear) + Q_memset(buf, 0, size); + +#ifdef NAMEDMALLOCS + strcpy((char *)(nt+1) + nt->size, buffer); +#endif + return buf; +} +void *Z_TagMalloc (int size, int tag) +{ +#ifdef NAMEDMALLOCS + return Z_BaseTagMalloc(size, tag, true, ""); +#else + return Z_BaseTagMalloc(size, tag, true); +#endif +} + +#ifdef NAMEDMALLOCS +void *Z_MallocNamed (int size, char *file, int lineno) +{ + qbyte *buf; + buf = Z_BaseTagMalloc(size, 1, true, "%s: %i", file, lineno); + if (!buf) + Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + return buf; +} +#else +void *Z_Malloc(int size) +{ + qbyte *buf; + buf = Z_TagMalloc(size, 1); + if (!buf) + Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + return buf; +} + + + +void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensative data structures. +{ + void *data = Z_BaseTagMalloc(size, 1, true); + if (!data) + Sys_Error("BZ_Malloc failed on %i bytes", size); + + return data; +} +#endif +void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc. +{ +#ifdef NAMEDMALLOCS + return Z_BaseTagMalloc(size, 1, false, ""); +#else + return Z_BaseTagMalloc(size, 1, false); +#endif +} + +void *BZ_Realloc(void *data, int newsize) +{ + zone_t *oldzone; + void *newdata; + if (!data) + return BZ_Malloc(newsize); + oldzone = ((zone_t *)data)-1; + newdata = BZ_Malloc(newsize); + if (oldzone->size < newsize) + { + memcpy(newdata, data, oldzone->size); + memset((char *)newdata + oldzone->size, 0, newsize - oldzone->size); + } + else + memcpy(newdata, data, newsize); + BZ_Free(data); + + return newdata; +} + +void BZ_Free(void *data) +{ + Z_Free(data); +} + +void Zone_Print_f(void) +{ + int overhead=0; + int allocated = 0; + int blocks = 0; + int futurehide; + zone_t *zone; + if (*Cmd_Argv(1)) + futurehide = true; + else + futurehide = false; + for(zone = zone_head; zone; zone=zone->next) + { + blocks++; + allocated+= zone->size; +#ifdef NAMEDMALLOCS + if (*((char *)(zone+1)+zone->size)!='#') + { + Con_Printf("%i-%s\n", zone->size, (char *)(zone+1) + zone->size); + if (futurehide) + *((char *)(zone+1)+zone->size) = '#'; + +// Sleep(10); + } + overhead += sizeof(zone) + strlen((char *)(zone+1) + zone->size) +1; +#else + Con_Printf("%i-%i ", zone->size, zone->tag); + overhead += sizeof(zone); +#endif + } + Con_Printf("Zone:%i bytes in %i blocks\n", allocated, blocks); + Con_Printf("Overhead %i bytes\n", overhead); +} + +#else + + + + + + + +//dmw was 0x50000 19/12/02 - playing with dynamic sound system. +//was 0x80000 15/01/03 - playing with genuine pk3 files +#define DYNAMIC_SIZE 0x100000 + +#define ZONEID 0x1d4a11 +#define MINFRAGMENT 64 + +typedef struct memblock_s +{ + int size; // including the header and possibly tiny fragments + int tag; // a tag of 0 is a free block + int id; // should be ZONEID + struct memblock_s *next, *prev; + int pad; // pad to 64 bit boundary +} memblock_t; + +typedef struct +{ + int size; // total bytes malloced, including header + memblock_t blocklist; // start / end cap for linked list + memblock_t *rover; +} memzone_t; + +/* +============================================================================== + + ZONE MEMORY ALLOCATION + +There is never any space between memblocks, and there will never be two +contiguous free memblocks. + +The rover can be left pointing at a non-empty block + +The zone calls are pretty much only used for small strings and structures, +all big things are allocated on the hunk. +============================================================================== +*/ + +memzone_t *mainzone; + +void Z_ClearZone (memzone_t *zone, int size); + + +/* +======================== +Z_ClearZone +======================== +*/ +void Z_ClearZone (memzone_t *zone, int size) +{ + memblock_t *block; + +// set the entire zone to one free block + + zone->blocklist.next = zone->blocklist.prev = block = + (memblock_t *)( (qbyte *)zone + sizeof(memzone_t) ); + zone->blocklist.tag = 1; // in use block + zone->blocklist.id = 0; + zone->blocklist.size = 0; + zone->rover = block; + + block->prev = block->next = &zone->blocklist; + block->tag = 0; // free block + block->id = ZONEID; + block->size = size - sizeof(memzone_t); +} + + +/* +======================== +Z_Free +======================== +*/ +void Z_Free (void *ptr) +{ + memblock_t *block, *other; + + if (!ptr) + Sys_Error ("Z_Free: NULL pointer"); + + block = (memblock_t *) ( (qbyte *)ptr - sizeof(memblock_t)); + if (block->id != ZONEID) + Sys_Error ("Z_Free: freed a pointer without ZONEID"); + if (block->tag == 0) + Sys_Error ("Z_Free: freed a freed pointer"); + + block->tag = 0; // mark as free + + other = block->prev; + if (!other->tag) + { // merge with previous free block + other->size += block->size; + other->next = block->next; + other->next->prev = other; + if (block == mainzone->rover) + mainzone->rover = other; + block = other; + } + + other = block->next; + if (!other->tag) + { // merge the next free block onto the end + block->size += other->size; + block->next = other->next; + block->next->prev = block; + if (other == mainzone->rover) + mainzone->rover = block; + } +} + + +/* +======================== +Z_Malloc +======================== +*/ +#undef Z_Malloc +void *Z_Malloc (int size) +{ + void *buf; + +Z_CheckHeap (); // DEBUG + buf = Z_TagMalloc (size, 1); + if (!buf) + Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size); + Q_memset (buf, 0, size); + + return buf; +} + +void *Z_MallocNamed (int size, char *name) +{ + void *buf; + +Z_CheckHeap (); // DEBUG + buf = Z_TagMalloc (size, 1); + if (!buf) + Sys_Error ("Z_Malloc: %s failed on allocation of %i bytes", name, size); +// Sys_DebugLog("zmalloc.log", "%s allocates %i bytes\n", name, size); + Q_memset (buf, 0, size); + + return buf; +} + +void *Z_MallocNamed2 (int size, char *name, int line) +{ + void *buf; + +Z_CheckHeap (); // DEBUG + buf = Z_TagMalloc (size, 1); + if (!buf) + Sys_Error ("Z_Malloc: %s %i failed on allocation of %i bytes", name, line, size); +// Sys_DebugLog("zmalloc.log", "%s %i allocates %i bytes\n", name, line, size); + Q_memset (buf, 0, size); + + return buf; +} + +void *Z_TagMalloc (int size, int tag) +{ + int extra; + memblock_t *start, *rover, *new, *base; + + if (!tag) + Sys_Error ("Z_TagMalloc: tried to use a 0 tag"); + +// +// scan through the block list looking for the first free block +// of sufficient size +// + size += sizeof(memblock_t); // account for size of block header + size += 4; // space for memory trash tester + size = (size + 7) & ~7; // align to 8-qbyte boundary + + base = rover = mainzone->rover; + start = base->prev; + + do + { + if (rover == start) // scaned all the way around the list + return NULL; + if (rover->tag) + base = rover = rover->next; + else + rover = rover->next; + } while (base->tag || base->size < size); + +// +// found a block big enough +// + extra = base->size - size; + if (extra > MINFRAGMENT) + { // there will be a free fragment after the allocated block + new = (memblock_t *) ((qbyte *)base + size ); + new->size = extra; + new->tag = 0; // free block + new->prev = base; + new->id = ZONEID; + new->next = base->next; + new->next->prev = new; + base->next = new; + base->size = size; + } + + base->tag = tag; // no longer a free block + + mainzone->rover = base->next; // next allocation will start looking here + + base->id = ZONEID; + +// marker for memory trash testing + *(int *)((qbyte *)base + base->size - 4) = ZONEID; + + return (void *) ((qbyte *)base + sizeof(memblock_t)); +} + + +/* +======================== +Z_Print +======================== +*/ +void Z_Print (memzone_t *zone) +{ + memblock_t *block; + + Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone); + + for (block = zone->blocklist.next ; ; block = block->next) + { + Con_Printf ("block:%p size:%7i tag:%3i\n", + block, block->size, block->tag); + + if (block->next == &zone->blocklist) + break; // all blocks have been hit + if ( (qbyte *)block + block->size != (qbyte *)block->next) + Con_Printf ("ERROR: block size does not touch the next block\n"); + if ( block->next->prev != block) + Con_Printf ("ERROR: next block doesn't have proper back link\n"); + if (!block->tag && !block->next->tag) + Con_Printf ("ERROR: two consecutive free blocks\n"); + } +} + + +/* +======================== +Z_CheckHeap +======================== +*/ +void Z_CheckHeap (void) +{ + memblock_t *block; + + for (block = mainzone->blocklist.next ; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + break; // all blocks have been hit + if ( (qbyte *)block + block->size != (qbyte *)block->next) + Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); + if ( block->next->prev != block) + Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); + if (!block->tag && !block->next->tag) + Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); + } +} + + + + + + +void *BZ_Malloc(int size) +{ + void *data; + data = malloc(size); + memset(data, 0, size); + return data; +} + +void BZ_Free(void *data) +{ + free(data); +} + + + +#endif + + + + + + + +//============================================================================ + +#define HUNK_SENTINAL 0x1df001ed + +typedef struct +{ + int sentinal; + int size; // including sizeof(hunk_t), -1 = not allocated + char name[8]; +} hunk_t; + +qbyte *hunk_base; +int hunk_size; + +int hunk_low_used; +int hunk_high_used; + +qboolean hunk_tempactive; +int hunk_tempmark; + +void R_FreeTextures (void); + +/* +============== +Hunk_Check + +Run consistancy and sentinal trahing checks +============== +*/ +void Hunk_Check (void) +{ + hunk_t *h; + + for (h = (hunk_t *)hunk_base ; (qbyte *)h != hunk_base + hunk_low_used ; ) + { + if (h->sentinal != HUNK_SENTINAL) + Sys_Error ("Hunk_Check: trahsed sentinal"); + if (h->size < 16 || h->size + (qbyte *)h - hunk_base > hunk_size) + Sys_Error ("Hunk_Check: bad size"); + h = (hunk_t *)((qbyte *)h+h->size); + } +} + +/* +============== +Hunk_Print + +If "all" is specified, every single allocation is printed. +Otherwise, allocations with the same name will be totaled up before printing. +============== +*/ +void Hunk_Print (qboolean all) +{ + hunk_t *h, *next, *endlow, *starthigh, *endhigh; + int count, sum; + int totalblocks; + char name[9]; + + name[8] = 0; + count = 0; + sum = 0; + totalblocks = 0; + + h = (hunk_t *)hunk_base; + endlow = (hunk_t *)(hunk_base + hunk_low_used); + starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); + endhigh = (hunk_t *)(hunk_base + hunk_size); + + Con_Printf (" :%8i total hunk size\n", hunk_size); + Con_Printf ("-------------------------\n"); + + while (1) + { + // + // skip to the high hunk if done with low hunk + // + if ( h == endlow ) + { + Con_Printf ("-------------------------\n"); + Con_Printf (" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); + Con_Printf ("-------------------------\n"); + h = starthigh; + } + + // + // if totally done, break + // + if ( h == endhigh ) + break; + + // + // run consistancy checks + // + if (h->sentinal != HUNK_SENTINAL) + Sys_Error ("Hunk_Check: trahsed sentinal"); + if (h->size < 16 || h->size + (qbyte *)h - hunk_base > hunk_size) + Sys_Error ("Hunk_Check: bad size"); + + next = (hunk_t *)((qbyte *)h+h->size); + count++; + totalblocks++; + sum += h->size; + + // + // print the single block + // + memcpy (name, h->name, 8); + if (all) + Con_Printf ("%8p :%8i %8s\n",h, h->size, name); + + // + // print the total + // + if (next == endlow || next == endhigh || + strncmp (h->name, next->name, 8) ) + { + if (!all) + Con_Printf (" :%8i %8s (TOTAL)\n",sum, name); + count = 0; + sum = 0; + } + + h = next; + } + + Con_Printf ("-------------------------\n"); + Con_Printf ("%8i total blocks\n", totalblocks); + +} + +/* +=================== +Hunk_AllocName +=================== +*/ +void *Hunk_AllocName (int size, char *name) +{ + hunk_t *h; + +#ifdef PARANOID + Hunk_Check (); +#endif + + if (size < 0) + Sys_Error ("Hunk_Alloc: bad size: %i", size); + + size = sizeof(hunk_t) + ((size+15)&~15); + +#ifndef _WIN32 + if (hunk_size - hunk_low_used - hunk_high_used < size) +// Sys_Error ("Hunk_Alloc: failed on %i bytes",size); +#ifdef _WIN32 + Sys_Error ("Not enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize 16000\" on the QuakeWorld command line.", name); +#else + Sys_Error ("Not enough RAM allocated. Try starting using \"-mem 16\" on the QuakeWorld command line."); +#endif +#endif + + h = (hunk_t *)(hunk_base + hunk_low_used); + +#ifdef _WIN32 + if (!VirtualAlloc (hunk_base, hunk_low_used+size+sizeof(hunk_t), MEM_COMMIT, PAGE_READWRITE)) + { + char *buf; + Hunk_Print(true); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL); + Sys_Error ("VirtualCommit failed\nNot enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize 64000\" on the QuakeWorld command line.", name); + } +#endif + + hunk_low_used += size; + + Cache_FreeLow (hunk_low_used); + + memset (h, 0, size); + + h->size = size; + h->sentinal = HUNK_SENTINAL; + Q_strncpyz (h->name, COM_SkipPath(name), sizeof(h->name)); + + return (void *)(h+1); +} + +/* +=================== +Hunk_Alloc +=================== +*/ +void *Hunk_Alloc (int size) +{ + return Hunk_AllocName (size, "unknown"); +} + +int Hunk_LowMark (void) +{ + return hunk_low_used; +} + +int Hunk_LowMemAvailable(void) +{ + return hunk_size - hunk_low_used - hunk_high_used; +} + +void Hunk_FreeToLowMark (int mark) +{ + if (mark < 0 || mark > hunk_low_used) + Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark); + memset (hunk_base + mark, 0, hunk_low_used - mark); + hunk_low_used = mark; + +#ifdef _WIN32 + if (!VirtualAlloc (hunk_base, hunk_low_used+sizeof(hunk_t), MEM_COMMIT, PAGE_READWRITE)) + { + char *buf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL); + Sys_Error ("VirtualAlloc commit failed.\n%s", buf); + } +#endif +} + +int Hunk_HighMark (void) +{ + if (hunk_tempactive) + { + hunk_tempactive = false; + Hunk_FreeToHighMark (hunk_tempmark); + } + + return hunk_high_used; +} + +void Hunk_FreeToHighMark (int mark) +{ + if (hunk_tempactive) + { + hunk_tempactive = false; + Hunk_FreeToHighMark (hunk_tempmark); + } + if (mark < 0 || mark > hunk_high_used) + Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark); + memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); + hunk_high_used = mark; +} + + +/* +=================== +Hunk_HighAllocName +=================== +*/ +void *Hunk_HighAllocName (int size, char *name) +{ + hunk_t *h; + +#ifdef _WIN32 + Sys_Error("High hunk was disabled"); +#endif + + if (size < 0) + Sys_Error ("Hunk_HighAllocName: bad size: %i", size); + + if (hunk_tempactive) + { + Hunk_FreeToHighMark (hunk_tempmark); + hunk_tempactive = false; + } + +#ifdef PARANOID + Hunk_Check (); +#endif + + size = sizeof(hunk_t) + ((size+15)&~15); + + if (hunk_size - hunk_low_used - hunk_high_used < size) + { + Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size); + return NULL; + } + + hunk_high_used += size; + Cache_FreeHigh (hunk_high_used); + + h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); + + memset (h, 0, size); + h->size = size; + h->sentinal = HUNK_SENTINAL; + Q_strncpyz (h->name, name, sizeof(h->name)); + + return (void *)(h+1); +} + + +/* +================= +Hunk_TempAlloc + +Return space from the top of the hunk +clears old temp. +================= +*/ +#ifdef _WIN32 +typedef struct hnktemps_s { + struct hnktemps_s *next; +} hnktemps_t; +hnktemps_t *hnktemps; + +void Hunk_TempFree(void) +{ + hnktemps_t *nt; + + while (hnktemps) + { + nt = hnktemps->next; + free(hnktemps); + hnktemps = nt; + } +} +#endif + + +//allocates without clearing previous temp. +//safer than my hack that fuh moaned about... +void *Hunk_TempAllocMore (int size) +{ + void *buf; +#ifdef _WIN32 + hnktemps_t *nt; + nt = malloc(size + sizeof(hnktemps_t)); + nt->next = hnktemps; + hnktemps = nt; + buf = (void *)(nt+1); + memset(buf, 0, size); + return buf; +#else + + if (!hunk_tempactive) + return Hunk_TempAlloc(size); + + size = (size+15)&~15; + + hunk_tempactive = false; //so it doesn't wipe old temp. + buf = Hunk_HighAllocName (size, "mtmp"); + hunk_tempactive = true; + + return buf; +#endif +} + + +void *Hunk_TempAlloc (int size) +{ +#ifdef _WIN32 + + Hunk_TempFree(); + + return Hunk_TempAllocMore(size); +#else + void *buf; + + size = (size+15)&~15; + + if (hunk_tempactive) + { + Hunk_FreeToHighMark (hunk_tempmark); + hunk_tempactive = false; + } + + hunk_tempmark = Hunk_HighMark (); + + buf = Hunk_HighAllocName (size, "temp"); + + hunk_tempactive = true; + + return buf; +#endif +} + +/* +=============================================================================== + +CACHE MEMORY + +=============================================================================== +*/ +#ifdef NOCACHE + + +typedef struct cache_system_s { + cache_user_t *user; + struct cache_system_s *next; + struct cache_system_s *prev; + int size; + char name[16]; +} cache_system_t; +cache_system_t *cache_head; + +void Cache_Free (cache_user_t *c) +{ + cache_system_t *cs; + cs = ((cache_system_t *)c->data)-1; + + cs->user->data = NULL; + + if (cs->next) + cs->next->prev = cs->prev; + if (cs->prev) + cs->prev->next = cs->next; + + if (cs == cache_head) + cache_head = cs->next; + + free(cs); +} + +void *Cache_Check(cache_user_t *c) +{ + if (!c->data) + return NULL; + + return c->data; +} + +void Cache_Flush(void) +{ + while(cache_head) + { + Cache_Free(cache_head->user); + } +} + +void *Cache_Alloc (cache_user_t *c, int size, char *name) +{ + void *buf; + cache_system_t *nt; + + if (c->data) + Sys_Error ("Cache_Alloc: allready allocated"); + + if (size <= 0) + Sys_Error ("Cache_Alloc: size %i", size); + + size = (size + 15) & ~15; + + nt = malloc(size + sizeof(cache_system_t)); + if (!nt) + Sys_Error("Cache_Alloc: failed on allocation of %i bytes", size); + nt->next = cache_head; + nt->prev = NULL; + nt->user = c; + nt->size = size + sizeof(cache_system_t); + Q_strncpyz(nt->name, name, sizeof(nt->name)); + if (cache_head) + cache_head->prev = nt; + cache_head = nt; + nt->user->fake = false; + buf = (void *)(nt+1); + memset(buf, 0, size); + c->data = buf; + return c->data; +} + +void Cache_FreeLow(int newlow) +{ +} + +void Cache_FreeHigh(int newhigh) +{ +} + +void Cache_Report (void) +{ +} + +void Hunk_Print_f (void) +{ + cache_system_t *cs; + zone_t *zone; + int zoneblocks; + int cacheused; + int zoneused; + Hunk_Print(true); + + cacheused = 0; + zoneused = 0; + zoneblocks = 0; + for (cs = cache_head; cs; cs = cs->next) + { + cacheused += cs->size; + } + for(zone = zone_head; zone; zone=zone->next) + { + zoneused += zone->size + sizeof(zone_t); + zoneblocks++; + } + Con_Printf("Cache: %iKB\n", cacheused/1024); + Con_Printf("Zone: %i containing %iKB\n", zoneblocks, zoneused/1024); +} +void Cache_Init(void) +{ + Cmd_AddCommand ("flush", Cache_Flush); + Cmd_AddCommand ("hunkprint", Hunk_Print_f); + Cmd_AddCommand ("zoneprint", Zone_Print_f); +} + +#else +typedef struct cache_system_s +{ + int size; // including this header + cache_user_t *user; + char name[16]; + struct cache_system_s *prev, *next; + struct cache_system_s *lru_prev, *lru_next; // for LRU flushing +} cache_system_t; + +cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); + +cache_system_t cache_head; + +/* +=========== +Cache_Move +=========== +*/ +void Cache_Move ( cache_system_t *c) +{ + cache_system_t *new; + +// we are clearing up space at the bottom, so only allocate it late + new = Cache_TryAlloc (c->size, true); + if (new) + { +// Con_Printf ("cache_move ok\n"); + + Q_memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); + new->user = c->user; + Q_memcpy (new->name, c->name, sizeof(new->name)); + Cache_Free (c->user); + new->user->data = (void *)(new+1); + } + else + { +// Con_Printf ("cache_move failed\n"); + + Cache_Free (c->user); // tough luck... + } +} + +/* +============ +Cache_FreeLow + +Throw things out until the hunk can be expanded to the given point +============ +*/ +void Cache_FreeLow (int new_low_hunk) +{ + cache_system_t *c; + + while (1) + { + c = cache_head.next; + if (c == &cache_head) + return; // nothing in cache at all + if ((qbyte *)c >= hunk_base + new_low_hunk) + return; // there is space to grow the hunk + Cache_Move ( c ); // reclaim the space + } +} + +/* +============ +Cache_FreeHigh + +Throw things out until the hunk can be expanded to the given point +============ +*/ +void Cache_FreeHigh (int new_high_hunk) +{ + cache_system_t *c, *prev; + + prev = NULL; + while (1) + { + c = cache_head.prev; + if (c == &cache_head) + return; // nothing in cache at all + if ( (qbyte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) + return; // there is space to grow the hunk + if (c == prev) + Cache_Free (c->user); // didn't move out of the way + else + { + Cache_Move (c); // try to move it + prev = c; + } + } +} + +void Cache_UnlinkLRU (cache_system_t *cs) +{ + if (!cs->lru_next || !cs->lru_prev) + Sys_Error ("Cache_UnlinkLRU: NULL link"); + + cs->lru_next->lru_prev = cs->lru_prev; + cs->lru_prev->lru_next = cs->lru_next; + + cs->lru_prev = cs->lru_next = NULL; +} + +void Cache_MakeLRU (cache_system_t *cs) +{ + if (cs->lru_next || cs->lru_prev) + Sys_Error ("Cache_MakeLRU: active link"); + + cache_head.lru_next->lru_prev = cs; + cs->lru_next = cache_head.lru_next; + cs->lru_prev = &cache_head; + cache_head.lru_next = cs; +} + +/* +============ +Cache_TryAlloc + +Looks for a free block of memory between the high and low hunk marks +Size should already include the header and padding +============ +*/ +cache_system_t *Cache_TryAlloc (int size, qboolean nobottom) +{ + cache_system_t *cs, *new; + +// is the cache completely empty? + + if (!nobottom && cache_head.prev == &cache_head) + { + if (hunk_size - hunk_high_used - hunk_low_used < size) + Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size); + + new = (cache_system_t *) (hunk_base + hunk_low_used); + memset (new, 0, sizeof(*new)); + new->size = size; + + cache_head.prev = cache_head.next = new; + new->prev = new->next = &cache_head; + + Cache_MakeLRU (new); + return new; + } + +// search from the bottom up for space + + new = (cache_system_t *) (hunk_base + hunk_low_used); + cs = cache_head.next; + + do + { + if (!nobottom || cs != cache_head.next) + { + if ( (qbyte *)cs - (qbyte *)new >= size) + { // found space + memset (new, 0, sizeof(*new)); + new->size = size; + + new->next = cs; + new->prev = cs->prev; + cs->prev->next = new; + cs->prev = new; + + Cache_MakeLRU (new); + + return new; + } + } + + // continue looking + new = (cache_system_t *)((qbyte *)cs + cs->size); + cs = cs->next; + + } while (cs != &cache_head); + +// try to allocate one at the very end + if ( hunk_base + hunk_size - hunk_high_used - (qbyte *)new >= size) + { + memset (new, 0, sizeof(*new)); + new->size = size; + + new->next = &cache_head; + new->prev = cache_head.prev; + cache_head.prev->next = new; + cache_head.prev = new; + + Cache_MakeLRU (new); + + return new; + } + + return NULL; // couldn't allocate +} + +/* +============ +Cache_Flush + +Throw everything out, so new data will be demand cached +============ +*/ +void Cache_Flush (void) +{ + while (cache_head.next != &cache_head) + Cache_Free ( cache_head.next->user ); // reclaim the space +} + + +/* +============ +Cache_Print + +============ +*/ +void Cache_Print (void) +{ + cache_system_t *cd; + + for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) + { + Con_Printf ("%8i : %s\n", cd->size, cd->name); + } +} + +/* +============ +Cache_Report + +============ +*/ +void Cache_Report (void) +{ + Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); +} + +/* +============ +Cache_Compact + +============ +*/ +void Cache_Compact (void) +{ +} + +/* +============ +Cache_Init + +============ +*/ +void Hunk_Print_f (void) {Hunk_Print(true);} +void Cache_Init (void) +{ + cache_head.next = cache_head.prev = &cache_head; + cache_head.lru_next = cache_head.lru_prev = &cache_head; + + Cmd_AddCommand ("flush", Cache_Flush); + + Cmd_AddCommand ("hp", Hunk_Print_f); +} + +/* +============== +Cache_Free + +Frees the memory and removes it from the LRU list +============== +*/ +void Cache_Free (cache_user_t *c) +{ + cache_system_t *cs; + + if (!c->data) + Sys_Error ("Cache_Free: not allocated"); + + cs = ((cache_system_t *)c->data) - 1; + + cs->prev->next = cs->next; + cs->next->prev = cs->prev; + cs->next = cs->prev = NULL; + + c->data = NULL; + + Cache_UnlinkLRU (cs); +} + + + +/* +============== +Cache_Check +============== +*/ +void *Cache_Check (cache_user_t *c) +{ + cache_system_t *cs; + + if (!c->data) + return NULL; + + if (c->fake) //malloc or somesuch. + return c->data; + + cs = ((cache_system_t *)c->data) - 1; + +// move to head of LRU + Cache_UnlinkLRU (cs); + Cache_MakeLRU (cs); + + return c->data; +} + + +/* +============== +Cache_Alloc +============== +*/ +void *Cache_Alloc (cache_user_t *c, int size, char *name) +{ + cache_system_t *cs; + + if (c->data) + Sys_Error ("Cache_Alloc: allready allocated"); + + if (size <= 0) + Sys_Error ("Cache_Alloc: size %i", size); + + size = (size + sizeof(cache_system_t) + 15) & ~15; + +// find memory for it + while (1) + { + cs = Cache_TryAlloc (size, false); + if (cs) + { + strncpy (cs->name, name, sizeof(cs->name)-1); + c->data = (void *)(cs+1); + cs->user = c; + break; + } + + // free the least recently used cahedat + if (cache_head.lru_prev == &cache_head) + Sys_Error ("Cache_Alloc: out of memory"); + // not enough memory at all + Cache_Free ( cache_head.lru_prev->user ); + } + + return Cache_Check (c); +} +#endif +//============================================================================ + + +/* +======================== +Memory_Init +======================== +*/ +void Memory_Init (void *buf, int size) +{ +#ifndef NOZONE + int p; + int zonesize = DYNAMIC_SIZE; +#endif + + hunk_base = buf; + hunk_size = size; + hunk_low_used = 0; + hunk_high_used = 0; + + Cache_Init (); + +#ifndef NOZONE + p = COM_CheckParm ("-zone"); + if (p) + { + if (p < com_argc-1) + zonesize = Q_atoi (com_argv[p+1]) * 1024; + else + Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); + } + mainzone = Hunk_AllocName ( zonesize, "zone" ); + Z_ClearZone (mainzone, zonesize); +#endif +} + diff --git a/engine/common/zone.h b/engine/common/zone.h new file mode 100644 index 000000000..f453499bc --- /dev/null +++ b/engine/common/zone.h @@ -0,0 +1,157 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/* + memory allocation + + +H_??? The hunk manages the entire memory block given to quake. It must be +contiguous. Memory can be allocated from either the low or high end in a +stack fashion. The only way memory is released is by resetting one of the +pointers. + +Hunk allocations should be given a name, so the Hunk_Print () function +can display usage. + +Hunk allocations are guaranteed to be 16 byte aligned. + +The video buffers are allocated high to avoid leaving a hole underneath +server allocations when changing to a higher video mode. + + +Z_??? Zone memory functions used for small, dynamic allocations like text +strings from command input. There is only about 48K for it, allocated at +the very bottom of the hunk. + +Cache_??? Cache memory is for objects that can be dynamically loaded and +can usefully stay persistant between levels. The size of the cache +fluctuates from level to level. + +To allocate a cachable object + + +Temp_??? Temp memory is used for file loading and surface caching. The size +of the cache memory is adjusted so that there is a minimum of 512k remaining +for temp memory. + + +------ Top of Memory ------- + +high hunk allocations + +<--- high hunk reset point held by vid + +video buffer + +z buffer + +surface cache + +<--- high hunk used + +cachable memory + +<--- low hunk used + +client and server low hunk allocations + +<-- low hunk reset point held by host + +startup hunk allocations + +Zone block + +----- Bottom of Memory ----- + + + +*/ + +void Memory_Init (void *buf, int size); + +void Z_Free (void *ptr); +void *Z_Malloc (int size); // returns 0 filled memory +void *Z_MallocNamed (int size, char *, int); // returns 0 filled memory +//#define Z_Malloc(x) Z_MallocNamed2(x, __FILE__, __LINE__ ) +void *Z_TagMalloc (int size, int tag); +void Z_FreeTags(int tag); + +void Z_DumpHeap (void); +void Z_CheckHeap (void); +int Z_FreeMemory (void); + +int Z_Allocated(void); + +#ifdef _DEBUG +#define NAMEDMALLOCS +#endif + +//Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensative data structures. +//(this is a nicer name for malloc) +void *BZ_Malloc(int size); +void *BZF_Malloc(int size); +void *BZ_Realloc(void *ptr, int size); +void BZ_Free(void *ptr); + +#ifdef NAMEDMALLOCS +#define BZ_Malloc(size) Z_MallocNamed(size, __FILE__, __LINE__) + + +#define Z_Malloc(size) Z_MallocNamed(size, __FILE__, __LINE__) +#endif + +void *Hunk_Alloc (int size); // returns 0 filled memory +void *Hunk_AllocName (int size, char *name); + +void *Hunk_HighAllocName (int size, char *name); + +int Hunk_LowMark (void); +void Hunk_FreeToLowMark (int mark); +int Hunk_LowMemAvailable(void); + +int Hunk_HighMark (void); +void Hunk_FreeToHighMark (int mark); + +void *Hunk_TempAlloc (int size); +void *Hunk_TempAllocMore (int size); //Don't clear old temp + +void Hunk_Check (void); + +typedef struct cache_user_s +{ + void *data; + qboolean fake; +} cache_user_t; + +void Cache_Flush (void); + +void *Cache_Check (cache_user_t *c); +// returns the cached data, and moves to the head of the LRU list +// if present, otherwise returns NULL + +void Cache_Free (cache_user_t *c); + +void *Cache_Alloc (cache_user_t *c, int size, char *name); +// Returns NULL if all purgable data was tossed and there still +// wasn't enough room. + +void Cache_Report (void); + + +