diff --git a/Makefile b/Makefile index 9187cfacf..97231698f 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,6 @@ SERVER_OBJS := \ $(SRC_DIR)/pr2_cmds.o \ $(SRC_DIR)/pr2_edict.o \ $(SRC_DIR)/pr2_exec.o \ - $(SRC_DIR)/pr2_vm.o \ $(SRC_DIR)/sv_ccmds.o \ $(SRC_DIR)/sv_ents.o \ $(SRC_DIR)/sv_init.o \ @@ -240,7 +239,10 @@ SERVER_OBJS := \ $(SRC_DIR)/sv_demo_misc.o \ $(SRC_DIR)/sv_demo_qtv.o \ $(SRC_DIR)/sv_login.o \ - $(SRC_DIR)/sv_mod_frags.o + $(SRC_DIR)/sv_mod_frags.o \ + $(SRC_DIR)/vm.o \ + $(SRC_DIR)/vm_interpreted.o \ + $(SRC_DIR)/vm_x86.o HELP_OBJS := \ $(patsubst help_%.json,help_%.o,$(wildcard help_*.json)) diff --git a/ezQuake.vcxproj b/ezQuake.vcxproj index 65140541a..032864c9b 100644 --- a/ezQuake.vcxproj +++ b/ezQuake.vcxproj @@ -2133,9 +2133,6 @@ msversion.bat CppCode - - CppCode - CppCode @@ -3016,7 +3013,6 @@ msversion.bat - diff --git a/ezQuake.vcxproj.filters b/ezQuake.vcxproj.filters index 0f115246a..4b8ad0949 100644 --- a/ezQuake.vcxproj.filters +++ b/ezQuake.vcxproj.filters @@ -407,9 +407,6 @@ Source Files\Documentation - - Source Files\Server - Source Files\Server @@ -1384,9 +1381,6 @@ Source Files\Server - - Source Files\Server - Source Files\Server diff --git a/src/cmodel.c b/src/cmodel.c index 9f93fae92..bdbc74b5c 100644 --- a/src/cmodel.c +++ b/src/cmodel.c @@ -23,7 +23,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "common.h" #include "cvar.h" #endif -#include typedef struct cnode_s { // common with leaf @@ -78,8 +77,6 @@ static qbool map_halflife; static mphysicsnormal_t* map_physicsnormals; // must be same number as clipnodes to save reallocations in worst case scenario -static byte *cmod_base; // for CM_Load* functions - // lumps immediately follow: typedef struct { char lumpname[24]; @@ -87,8 +84,8 @@ typedef struct { int filelen; } bspx_lump_t; -void* Mod_BSPX_FindLump(bspx_header_t* bspx_header, char* lumpname, int* plumpsize, byte* mod_base); -bspx_header_t* Mod_LoadBSPX(int filesize, byte* mod_base); +static bspx_lump_t* CM_LoadBSPX(vfsfile_t* vf, dheader_t* header, bspx_header_t *xheader); +static byte* CM_BSPX_ReadLump(vfsfile_t* vf, bspx_header_t *xheader, bspx_lump_t* lump, char* lumpname, int* plumpsize); /* =============================================================================== @@ -600,14 +597,14 @@ int CM_FindTouchedLeafs (const vec3_t mins, const vec3_t maxs, int leafs[], int =============================================================================== */ -static void CM_LoadEntities (lump_t *l) +static void CM_LoadEntities (byte *buffer, int length) { - if (l->filelen <= 0 || l->filelen >= INT_MAX) { + if (!length) { map_entitystring = NULL; return; } - map_entitystring = (char *) Hunk_AllocName(l->filelen + 1, loadname); - memcpy(map_entitystring, cmod_base + l->fileofs, l->filelen); + map_entitystring = (char *) Hunk_AllocName (length, loadname); + memcpy (map_entitystring, buffer, length); } @@ -616,18 +613,18 @@ static void CM_LoadEntities (lump_t *l) CM_LoadSubmodels ================= */ -static void CM_LoadSubmodels (lump_t *l) +static void CM_LoadSubmodels (byte *buffer, int length) { dmodel_t *in; cmodel_t *out; int i, j, count; - in = (dmodel_t *)(cmod_base + l->fileofs); + in = (dmodel_t *) buffer; - if (l->filelen % sizeof(*in)) + if (length % sizeof(*in)) Host_Error ("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); + count = length / sizeof(*in); if (count < 1) Host_Error ("Map with no models"); @@ -685,19 +682,11 @@ static void CM_LoadSubmodels (lump_t *l) CM_SetParent ================= */ -static void CM_SetParent(cnode_t *node, cnode_t *parent) +static void CM_SetParent (cnode_t *node, cnode_t *parent) { - if (node->parent && node->parent == parent) { - Host_Error("Infinite loop detected in node list (node %p, base %p)\n", node, map_nodes); - return; - } - node->parent = parent; - - if (node->contents < 0) { + if (node->contents < 0) return; - } - CM_SetParent (node->children[0], node); CM_SetParent (node->children[1], node); } @@ -707,62 +696,48 @@ static void CM_SetParent(cnode_t *node, cnode_t *parent) CM_LoadNodes ================= */ -static void CM_LoadNodes (lump_t *l) +static void CM_LoadNodes (byte *buffer, int length) { int i, j, count, p; dnode_t *in; cnode_t *out; - const int max_nodes = (INT_MAX / sizeof(*out)); - in = (dnode_t *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (dnode_t *) buffer; + if (length % sizeof(*in)) Host_Error ("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_nodes) { - Host_Error("CM_LoadMap: invalid node count (%d vs 0-%d)", count, max_nodes); - } + count = length / sizeof(*in); out = (cnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); map_nodes = out; numnodes = count; - for (i = 0; i < count; i++, in++, out++) { + for (i = 0; i < count; i++, in++, out++) + { p = LittleLong(in->planenum); - if (p < 0 || p >= numplanes) { - Host_Error("Node %d has invalid plane# %d (0 to %d)\n", i, p, numplanes); - return; - } out->plane = map_planes + p; - for (j = 0; j < 2; j++) { - p = LittleShort(in->children[j]); - if (p < -numleafs || p >= numnodes) { - Host_Error("Node %d child[%d] #%d (%d to %d)", i, j, p, -numleafs, numnodes); - return; - } - out->children[j] = (p >= 0) ? (map_nodes + p) : ((cnode_t*)(map_leafs + (-1 - p))); + for (j=0 ; j<2 ; j++) + { + p = LittleShort (in->children[j]); + out->children[j] = (p >= 0) ? (map_nodes + p) : ((cnode_t *)(map_leafs + (-1 - p))); } } - CM_SetParent(map_nodes, NULL); // sets nodes and leafs + CM_SetParent (map_nodes, NULL); // sets nodes and leafs } -static void CM_LoadNodes29a(lump_t *l) +static void CM_LoadNodes29a(byte *buffer, int length) { int i, j, count, p; dnode29a_t *in; cnode_t *out; - const int max_nodes = (INT_MAX / sizeof(*out)); - in = (dnode29a_t *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (dnode29a_t *) buffer; + if (length % sizeof(*in)) Host_Error("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_nodes) { - Host_Error("CM_LoadMap: invalid node count (%d vs 0-%d)", count, max_nodes); - } + count = length / sizeof(*in); out = (cnode_t *)Hunk_AllocName(count * sizeof(*out), loadname); map_nodes = out; @@ -770,18 +745,10 @@ static void CM_LoadNodes29a(lump_t *l) for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->planenum); - if (p < 0 || p >= numplanes) { - Host_Error("Node %d has invalid plane# %d (0 to %d)\n", i, p, numplanes); - return; - } out->plane = map_planes + p; for (j = 0; j < 2; j++) { p = LittleLong(in->children[j]); - if (p < -numleafs || p >= numnodes) { - Host_Error("Node %d child[%d] #%d (%d to %d)", i, j, p, -numleafs, numnodes); - return; - } out->children[j] = (p >= 0) ? (map_nodes + p) : ((cnode_t *)(map_leafs + (-1 - p))); } } @@ -789,21 +756,17 @@ static void CM_LoadNodes29a(lump_t *l) CM_SetParent(map_nodes, NULL); // sets nodes and leafs } -static void CM_LoadNodesBSP2(lump_t *l) +static void CM_LoadNodesBSP2(byte *buffer, int length) { int i, j, count, p; dnode_bsp2_t *in; cnode_t *out; - const int max_nodes = (INT_MAX / sizeof(*out)); - in = (dnode_bsp2_t *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (dnode_bsp2_t *) buffer; + if (length % sizeof(*in)) Host_Error("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_nodes) { - Host_Error("CM_LoadMap: invalid node count (%d vs 0-%d)", count, max_nodes); - } + count = length / sizeof(*in); out = (cnode_t *)Hunk_AllocName(count * sizeof(*out), loadname); map_nodes = out; @@ -811,18 +774,10 @@ static void CM_LoadNodesBSP2(lump_t *l) for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->planenum); - if (p < 0) { - Host_Error("Node %d has negative plane# %d\n", i, in->planenum); - return; - } out->plane = map_planes + p; for (j = 0; j < 2; j++) { p = LittleLong(in->children[j]); - if (p < -numleafs || p >= numnodes) { - Host_Error("Node %d child[%d] #%d (%d to %d)", i, j, p, -numleafs, numnodes); - return; - } out->children[j] = (p >= 0) ? (map_nodes + p) : ((cnode_t *)(map_leafs + (-1 - p))); } } @@ -833,21 +788,17 @@ static void CM_LoadNodesBSP2(lump_t *l) /* ** CM_LoadLeafs */ -static void CM_LoadLeafs (lump_t *l) +static void CM_LoadLeafs (byte *buffer, int length) { dleaf_t *in; cleaf_t *out; int i, j, count, p; - int max_leafs = (INT_MAX / sizeof(*out)); - in = (dleaf_t *)(cmod_base + l->fileofs); + in = (dleaf_t *) buffer; - if (l->filelen % sizeof(*in)) + if (length % sizeof(*in)) Host_Error ("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_leafs) { - Host_Error("CM_LoadMap: invalid leaf count (%d vs 0-%d)", count, max_leafs); - } + count = length / sizeof(*in); out = (cleaf_t *) Hunk_AllocName ( count*sizeof(*out), loadname); map_leafs = out; @@ -861,22 +812,19 @@ static void CM_LoadLeafs (lump_t *l) } } -static void CM_LoadLeafs29a (lump_t *l) +static void CM_LoadLeafs29a (byte *buffer, int length) { dleaf29a_t *in; + cleaf_t *out; int i, j, count, p; - int max_leafs = (INT_MAX / sizeof(*out)); - in = (dleaf29a_t *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) { + in = (dleaf29a_t *) buffer; + if (length % sizeof(*in)) { Host_Error("CM_LoadMap: funny lump size"); } - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_leafs) { - Host_Error("CM_LoadMap: invalid leaf count (%d vs 0-%d)", count, max_leafs); - } + count = length / sizeof(*in); out = Hunk_AllocName ( count*sizeof(*out), loadname); map_leafs = out; @@ -885,29 +833,26 @@ static void CM_LoadLeafs29a (lump_t *l) for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->contents); out->contents = p; - for (j = 0; j < 4; j++) { + for (j = 0; j < 4; j++) out->ambient_sound_level[j] = in->ambient_level[j]; - } } + } -static void CM_LoadLeafsBSP2 (lump_t *l) +static void CM_LoadLeafsBSP2 (byte *buffer, int length) { dleaf_bsp2_t *in; + cleaf_t *out; int i, j, count, p; - int max_leafs = (INT_MAX / sizeof(*out)); - in = (dleaf_bsp2_t *)(cmod_base + l->fileofs); + in = (dleaf_bsp2_t *) buffer; - if (l->filelen % sizeof(*in)) { + if (length % sizeof(*in)) { Host_Error("CM_LoadMap: funny lump size"); } - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_leafs) { - Host_Error("CM_LoadMap: invalid leaf count (%d vs 0-%d)", count, max_leafs); - } + count = length / sizeof(*in); out = Hunk_AllocName ( count*sizeof(*out), loadname); map_leafs = out; @@ -926,20 +871,16 @@ static void CM_LoadLeafsBSP2 (lump_t *l) CM_LoadClipnodes ================= */ -static void CM_LoadClipnodes(lump_t *l) +static void CM_LoadClipnodes(byte *buffer, int length) { dclipnode_t *in; mclipnode_t *out; int i, count; - const int max_clipnodes = (INT_MAX / sizeof(*out)); - in = (dclipnode_t *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (dclipnode_t *) buffer; + if (length % sizeof(*in)) Host_Error("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_clipnodes) { - Host_Error("CM_LoadMap: invalid clipnode count (%d vs 0-%d)", count, max_clipnodes); - } + count = length / sizeof(*in); out = (mclipnode_t *)Hunk_AllocName(count * sizeof(*out), loadname); map_clipnodes = out; @@ -947,30 +888,21 @@ static void CM_LoadClipnodes(lump_t *l) for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); - if (out->planenum < 0 || out->planenum >= numplanes) { - Host_Error("CM_LoadClipNodes: node %d references plane %d (numplanes %d)", i, out->planenum, numplanes); - return; - } out->children[0] = LittleShort(in->children[0]); out->children[1] = LittleShort(in->children[1]); } } -static void CM_LoadClipnodesBSP2(lump_t *l) +static void CM_LoadClipnodesBSP2(byte *buffer, int length) { dclipnode29a_t *in; mclipnode_t *out; int i, count; - const int max_clipnodes = (INT_MAX / sizeof(*out)); - in = (void *)(cmod_base + l->fileofs); - if (l->filelen % sizeof(*in)) { + in = (void *) buffer; + if (length % sizeof(*in)) Host_Error("CM_LoadMap: funny lump size"); - } - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_clipnodes) { - Host_Error("CM_LoadMap: invalid clipnode count (%d vs 0-%d)", count, max_clipnodes); - } + count = length / sizeof(*in); out = Hunk_AllocName(count * sizeof(*out), loadname); map_clipnodes = out; @@ -978,10 +910,6 @@ static void CM_LoadClipnodesBSP2(lump_t *l) for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); - if (out->planenum < 0 || out->planenum >= numplanes) { - Host_Error("CM_LoadClipNodes: node %d references plane %d (numplanes %d)", i, out->planenum, numplanes); - return; - } out->children[0] = LittleLong(in->children[0]); out->children[1] = LittleLong(in->children[1]); } @@ -1015,12 +943,11 @@ static qbool CM_LoadPhysicsNormalsData(byte* data, int datalength) return true; } -static void CM_LoadPhysicsNormals(int filelen) +static void CM_LoadPhysicsNormals(byte *physnormals, int len_physnormals) { // Same logic as .lit file support: load from bspx, allow over-ride with .qpn files // As client-side movement prediction will be incorrect if physics normals don't // match, I strongly recommend the .bspx solution - bspx_header_t* bspx; int i; qbool bspx_loaded = false; @@ -1034,12 +961,8 @@ static void CM_LoadPhysicsNormals(int filelen) map_physicsnormals = Hunk_AllocName(numclipnodes * sizeof(map_physicsnormals[0]), loadname); // Try and load from BSPX lump - bspx = Mod_LoadBSPX(filelen, cmod_base); - if (bspx) { - int lumpsize = 0; - void* data = Mod_BSPX_FindLump(bspx, "MVDSV_PHYSICSNORMALS", &lumpsize, cmod_base); - - bspx_loaded = CM_LoadPhysicsNormalsData(data, lumpsize); + if (physnormals) { + bspx_loaded = CM_LoadPhysicsNormalsData(physnormals, len_physnormals); if (bspx_loaded) { Con_Printf("Loading BSPX physics normals\n"); } @@ -1087,14 +1010,9 @@ static void CM_MakeHull0(void) cnode_t *in, *child; mclipnode_t *out; int i, j, count; - int max_clipnodes = (INT_MAX / sizeof(*out)); in = map_nodes; count = numnodes; - if (count < 0 || count > max_clipnodes) { - Host_Error("CM_LoadMap: invalid clipnode count [MakeHull] (%d vs 0-%d)", count, max_clipnodes); - return; - } out = (mclipnode_t *)Hunk_AllocName(count * sizeof(*out), loadname); // fix up hull 0 in all cmodels @@ -1118,23 +1036,18 @@ static void CM_MakeHull0(void) CM_LoadPlanes ================= */ -static void CM_LoadPlanes(lump_t *l) +static void CM_LoadPlanes(byte *buffer, size_t length) { int i, j, count, bits; mplane_t *out; dplane_t *in; - const int max_planes = (INT_MAX / sizeof(*out)); - in = (dplane_t *)(cmod_base + l->fileofs); + in = (dplane_t *) buffer; - if (l->filelen % sizeof(*in)) + if (length % sizeof(*in)) Host_Error("CM_LoadMap: funny lump size"); - count = l->filelen / sizeof(*in); - if (count < 0 || count > max_planes) { - Host_Error("CM_LoadMap: invalid plane count (%d vs 0-%d)", count, max_planes); - return; - } + count = length / sizeof(*in); out = (mplane_t *)Hunk_AllocName(count * sizeof(*out), loadname); map_planes = out; @@ -1158,7 +1071,7 @@ static void CM_LoadPlanes(lump_t *l) /* ** DecompressVis */ -static byte *DecompressVis(byte *in, byte* limit) +static byte *DecompressVis(byte *in) { static byte decompressed[MAX_MAP_LEAFS / 8]; int c, row; @@ -1176,10 +1089,6 @@ static byte *DecompressVis(byte *in, byte* limit) } do { - if (in >= limit) { - return NULL; - } - if (*in) { *out++ = *in++; continue; @@ -1202,149 +1111,84 @@ static byte *DecompressVis(byte *in, byte* limit) ** ** Call after CM_LoadLeafs! */ -static void CM_BuildPVS(lump_t *lump_vis, lump_t *lump_leafs) +static void CM_BuildPVS(byte *visdata, int vis_len, byte *leaf_buf, int leaf_len) { - byte *visdata, *scan, *visdata_limit; + byte *scan; dleaf_t *in; int i; - int max_visleafs; map_vis_rowlongs = (visleafs + 31) >> 5; map_vis_rowbytes = map_vis_rowlongs * 4; - max_visleafs = (INT_MAX / map_vis_rowbytes); - if (visleafs < 0 || (visleafs > max_visleafs)) { - Host_Error("CM_LoadMap: invalid visleafs count [BuildPVS] (%d vs 0-%d)", visleafs, max_visleafs); - return; - } - map_pvs = (byte *)Hunk_Alloc(map_vis_rowbytes * visleafs); + map_pvs = (byte *)Hunk_AllocName(map_vis_rowbytes * visleafs, "pvs"); - if (lump_vis->filelen <= 0) { + if (!vis_len) { memset(map_pvs, 0xff, map_vis_rowbytes * visleafs); return; } // FIXME, add checks for lump_vis->filelen and leafs' visofs - visdata = cmod_base + lump_vis->fileofs; - visdata_limit = cmod_base + lump_vis->fileofs + lump_vis->filelen; // go through all leafs and decompress visibility data - in = (dleaf_t *)(cmod_base + lump_leafs->fileofs); + in = (dleaf_t *) leaf_buf; in++; // pvs row 0 is leaf 1 scan = map_pvs; for (i = 0; i < visleafs; i++, in++, scan += map_vis_rowbytes) { int p = LittleLong(in->visofs); - byte* source; - - if (p != -1 && p < 0) { - Host_Error("CM_BuildPVS: visleaf %d has invalid visofs %d", i, p); - } - if (p >= lump_vis->filelen) { - Host_Error("CM_BuildPVS: visleaf %d has out of range visofs %d (limit %d)", i, p, lump_vis->filelen); - } - - source = (p == -1) ? map_novis : DecompressVis(visdata + p, visdata_limit); - if (source == NULL) { - Host_Error("CM_BuildPVS: visleaf %d visofs %d caused invalid read, lumpsize %d", i, p, lump_vis->filelen); - return; - } - memcpy(scan, source, map_vis_rowbytes); + memcpy(scan, (p == -1) ? map_novis : DecompressVis(visdata + p), map_vis_rowbytes); } } -static void CM_BuildPVS29a(lump_t *lump_vis, lump_t *lump_leafs) +static void CM_BuildPVS29a(byte *visdata, int vis_len, byte *leaf_buf, int leaf_len) { - byte *visdata, *scan, *visdata_limit; + byte *scan; dleaf29a_t *in; int i; - int max_visleafs; map_vis_rowlongs = (visleafs + 31) >> 5; map_vis_rowbytes = map_vis_rowlongs * 4; - max_visleafs = (INT_MAX / map_vis_rowbytes); - if (visleafs < 0 || (visleafs > max_visleafs)) { - Host_Error("CM_LoadMap: invalid visleafs count [BuildPVS] (%d vs 0-%d)", visleafs, max_visleafs); - return; - } - map_pvs = (byte *)Hunk_Alloc(map_vis_rowbytes * visleafs); + map_pvs = (byte *)Hunk_AllocName(map_vis_rowbytes * visleafs, "pvs"); - if (lump_vis->filelen <= 0) { + if (!vis_len) { memset(map_pvs, 0xff, map_vis_rowbytes * visleafs); return; } - visdata = cmod_base + lump_vis->fileofs; - visdata_limit = cmod_base + lump_vis->fileofs + lump_vis->filelen; + // FIXME, add checks for lump_vis->filelen and leafs' visofs // go through all leafs and decompress visibility data - in = (dleaf29a_t *)(cmod_base + lump_leafs->fileofs); + in = (dleaf29a_t *) leaf_buf; in++; // pvs row 0 is leaf 1 scan = map_pvs; for (i = 0; i < visleafs; i++, in++, scan += map_vis_rowbytes) { int p = LittleLong(in->visofs); - byte* source; - - if (p != -1 && p < 0) { - Host_Error("CM_BuildPVS: visleaf %d has invalid visofs %d", i, p); - } - if (p >= lump_vis->filelen) { - Host_Error("CM_BuildPVS: visleaf %d has out of range visofs %d (limit %d)", i, p, lump_vis->filelen); - } - - source = (p == -1) ? map_novis : DecompressVis(visdata + p, visdata_limit); - if (source == NULL) { - Host_Error("CM_BuildPVS: visleaf %d visofs %d caused invalid read, lumpsize %d", i, p, lump_vis->filelen); - return; - } - memcpy(scan, source, map_vis_rowbytes); + memcpy(scan, (p == -1) ? map_novis : DecompressVis(visdata + p), map_vis_rowbytes); } } -static void CM_BuildPVSBSP2(lump_t *lump_vis, lump_t *lump_leafs) +static void CM_BuildPVSBSP2(byte *visdata, int vis_len, byte *leaf_buf, int leaf_len) { - byte *visdata, *scan, *visdata_limit; + byte *scan; dleaf_bsp2_t *in; int i; - int max_visleafs; map_vis_rowlongs = (visleafs + 31) >> 5; map_vis_rowbytes = map_vis_rowlongs * 4; - max_visleafs = (INT_MAX / map_vis_rowbytes); - if (visleafs < 0 || (visleafs > max_visleafs)) { - Host_Error("CM_LoadMap: invalid visleafs count [BuildPVS] (%d vs 0-%d)", visleafs, max_visleafs); - return; - } - map_pvs = (byte *)Hunk_Alloc(map_vis_rowbytes * visleafs); + map_pvs = (byte *)Hunk_AllocName(map_vis_rowbytes * visleafs, "pvs"); - if (lump_vis->filelen <= 0) { + if (!vis_len) { memset(map_pvs, 0xff, map_vis_rowbytes * visleafs); return; } // FIXME, add checks for lump_vis->filelen and leafs' visofs - visdata = cmod_base + lump_vis->fileofs; - visdata_limit = cmod_base + lump_vis->fileofs + lump_vis->filelen; // go through all leafs and decompress visibility data - in = (dleaf_bsp2_t *)(cmod_base + lump_leafs->fileofs); + in = (dleaf_bsp2_t *) leaf_buf; in++; // pvs row 0 is leaf 1 scan = map_pvs; for (i = 0; i < visleafs; i++, in++, scan += map_vis_rowbytes) { int p = LittleLong(in->visofs); - byte* source; - - if (p != -1 && p < 0) { - Host_Error("CM_BuildPVS: visleaf %d has invalid visofs %d", i, p); - } - if (p >= lump_vis->filelen) { - Host_Error("CM_BuildPVS: visleaf %d has out of range visofs %d (limit %d)", i, p, lump_vis->filelen); - } - - source = (p == -1) ? map_novis : DecompressVis(visdata + p, visdata_limit); - if (source == NULL) { - Host_Error("CM_BuildPVS: visleaf %d visofs %d caused invalid read, lumpsize %d", i, p, lump_vis->filelen); - return; - } - memcpy(scan, source, map_vis_rowbytes); + memcpy(scan, (p == -1) ? map_novis : DecompressVis(visdata + p), map_vis_rowbytes); } } @@ -1359,19 +1203,13 @@ static void CM_BuildPHS (void) int i, j, k, l, index1, bitbyte; unsigned *dest, *src; byte *scan; - int max_visleafs = (INT_MAX / map_vis_rowbytes); - - if (visleafs > max_visleafs) { - Host_Error("CM_LoadMap: invalid visleafs count [BuildPHS] (%d vs 0-%d)", visleafs, max_visleafs); - return; - } map_phs = NULL; if (map_vis_rowbytes * visleafs > 0x100000) { return; } - map_phs = (byte *) Hunk_Alloc (map_vis_rowbytes * visleafs); + map_phs = (byte *) Hunk_AllocName (map_vis_rowbytes * visleafs, "phs"); scan = map_pvs; dest = (unsigned *)map_phs; for (i = 0; i < visleafs; i++, dest += map_vis_rowlongs, scan += map_vis_rowbytes) @@ -1421,152 +1259,219 @@ void CM_InvalidateMap (void) map_physicsnormals = NULL; } -/* -** CM_LoadMap -*/ -typedef void(*BuildPVSFunction)(lump_t *lump_vis, lump_t *lump_leafs); -cmodel_t *CM_LoadMap (char *name, qbool clientload, unsigned *checksum, unsigned *checksum2) +static vfsfile_t *CM_OpenMap(char *name, dheader_t *header) { #ifndef CLIENTONLY extern cvar_t sv_bspversion, sv_halflifebsp; #endif + vfsfile_t *vf; + int i, read; - unsigned int i; - dheader_t *header; - unsigned int *buf; - unsigned int *padded_buf = NULL; - BuildPVSFunction cm_load_pvs_func = CM_BuildPVS; - qbool pad_lumps = false; - int required_length = 0; - int filelen = 0; - - if (map_name[0]) { - assert(!strcmp(name, map_name)); - - if (checksum) - *checksum = map_checksum; - *checksum2 = map_checksum2; - return &map_cmodels[0]; // still have the right version + vf = FS_OpenVFS(name, "rb", FS_ANY); // FIXME: should be FS_GAME. + if (!vf) { + Host_Error ("CM_OpenMap: %s not found", name); } - // load the file - buf = (unsigned int *) FS_LoadTempFile (name, &filelen); - if (!buf) - Host_Error ("CM_LoadMap: %s not found", name); - - COM_FileBase (name, loadname); + read = VFS_READ(vf, header, sizeof(dheader_t), NULL); + if (read != sizeof(dheader_t)) + { + Con_Printf("Failed to read BSP header, got %d of %d bytes\n", read, sizeof(dheader_t)); + VFS_CLOSE(vf); + return NULL; + } - header = (dheader_t *)buf; + for (i = 0; i < sizeof(dheader_t) / 4; i++) { + ((int *)header)[i] = LittleLong(((int *)header)[i]); + } - i = LittleLong (header->version); - if (i != Q1_BSPVERSION && i != HL_BSPVERSION && i != Q1_BSPVERSION2 && i != Q1_BSPVERSION29a) - Host_Error ("CM_LoadMap: %s has wrong version number (%i should be %i)", name, i, Q1_BSPVERSION); + switch (header->version) { + case Q1_BSPVERSION: + case HL_BSPVERSION: +#ifndef CLIENTONLY + Cvar_SetROM(&sv_bspversion, "1"); +#endif + break; + case Q1_BSPVERSION2: + case Q1_BSPVERSION29a: +#ifndef CLIENTONLY + Cvar_SetROM(&sv_bspversion, "2"); +#endif + break; + default: + VFS_CLOSE(vf); + Host_Error ("CM_OpenMap: %s has wrong version number (%i should be %i)", name, i, Q1_BSPVERSION); + break; + } - map_halflife = (i == HL_BSPVERSION); + map_halflife = (header->version == HL_BSPVERSION); #ifndef CLIENTONLY Cvar_SetROM(&sv_halflifebsp, map_halflife ? "1" : "0"); - Cvar_SetROM(&sv_bspversion, i == Q1_BSPVERSION || i == HL_BSPVERSION ? "1" : "2"); #endif - // swap all the lumps - for (i = 0; i < sizeof(dheader_t) / 4; i++) { - ((int *)header)[i] = LittleLong(((int *)header)[i]); - } + return vf; +} - // Align the lumps - for (i = 0; i < HEADER_LUMPS; ++i) { - pad_lumps |= (header->lumps[i].fileofs % 4) != 0; +static qbool CM_CalcChecksum(vfsfile_t *f, dheader_t *header, unsigned *checksum, unsigned *checksum2) +{ + byte *buf; + int i, read; - if (header->lumps[i].fileofs < 0 || header->lumps[i].filelen < 0) { - Host_Error("CM_LoadMap: %s has invalid lump definitions", name); + // checksum all of the map, except for entities + map_checksum = map_checksum2 = 0; + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + + if (VFS_SEEK(f, header->lumps[i].fileofs, SEEK_SET) < 0) + { + Con_Printf("Seek to BSP lump at %d failed\n", header->lumps[i].fileofs); + return false; } - if (header->lumps[i].fileofs + header->lumps[i].filelen > filelen || header->lumps[i].fileofs + header->lumps[i].filelen < 0) { - Host_Error("CM_LoadMap: %s has invalid lump definitions", name); + + buf = Hunk_TempAlloc (header->lumps[i].filelen); + + read = VFS_READ(f, buf, header->lumps[i].filelen, NULL); + if (read != header->lumps[i].filelen) + { + Con_Printf("Failed to read BSP lump, got %d of %d bytes\n", read, header->lumps[i].filelen); + return false; } - required_length += header->lumps[i].filelen; + map_checksum ^= LittleLong(Com_BlockChecksum(buf, header->lumps[i].filelen)); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + + map_checksum2 ^= LittleLong(Com_BlockChecksum(buf, header->lumps[i].filelen)); } - if (pad_lumps) { - int position = 0; - int required_size = sizeof(dheader_t) + required_length + HEADER_LUMPS * 4 + 1; - padded_buf = Q_malloc(required_size); + if (checksum) + *checksum = map_checksum; - // Copy header - memcpy(padded_buf, buf, sizeof(dheader_t)); - header = (dheader_t*)padded_buf; - position += sizeof(dheader_t); + if (checksum2) + *checksum2 = map_checksum2; - // Copy lumps: align on 4-byte boundary - for (i = 0; i < HEADER_LUMPS; ++i) { - if (position % 4) { - position += 4 - (position % 4); - } - if (position + header->lumps[i].filelen > required_size) { - Host_Error("CM_LoadMap: %s caused error while aligning lumps", name); - } - memcpy((byte*)padded_buf + position, ((byte*)buf) + header->lumps[i].fileofs, header->lumps[i].filelen); - header->lumps[i].fileofs = position; + return true; +} - position += header->lumps[i].filelen; - } +static byte *CM_ReadLump(vfsfile_t *vf, lump_t *lump) +{ + byte *out; + int read; + + if (VFS_SEEK(vf, lump->fileofs, SEEK_SET) < 0) + { + Con_Printf("Seek to BSP lump at %d failed\n", lump->fileofs); + return NULL; + } + out = Hunk_TempAllocMore(lump->filelen); - // Use the new buffer - buf = padded_buf; + read = VFS_READ(vf, out, lump->filelen, NULL); + if (read != lump->filelen) + { + Con_Printf("Failed to read BSP lump, got %d of %d bytes\n", read, lump->filelen); + return NULL; } - cmod_base = (byte *)header; + return out; +} - // checksum all of the map, except for entities - map_checksum = map_checksum2 = 0; - for (i = 0; i < HEADER_LUMPS; i++) { - if (i == LUMP_ENTITIES) - continue; - map_checksum ^= LittleLong(Com_BlockChecksum(cmod_base + header->lumps[i].fileofs, header->lumps[i].filelen)); +/* +** CM_LoadMap +*/ +typedef void(*BuildPVSFunction)(byte *vis_buf, int vis_len, byte *leaf_buf, int leaf_len); +cmodel_t *CM_LoadMap (char *name, qbool clientload, unsigned *checksum, unsigned *checksum2) +{ + dheader_t header = { 0 }; + BuildPVSFunction cm_load_pvs_func = CM_BuildPVS; + vfsfile_t *vf; + byte *l_planes, *l_leafs, *l_nodes, *l_clipnodes, *l_entities, *l_models, *l_vis; + bspx_header_t xheader = { 0 }; + bspx_lump_t *xlumps; + byte *l_physnormals = NULL; + int l_physnormals_len = 0; - if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) - continue; - map_checksum2 ^= LittleLong(Com_BlockChecksum(cmod_base + header->lumps[i].fileofs, header->lumps[i].filelen)); + if (map_name[0]) { + assert(!strcmp(name, map_name)); + + if (checksum) + *checksum = map_checksum; + *checksum2 = map_checksum2; + return &map_cmodels[0]; // still have the right version + } + + vf = CM_OpenMap(name, &header); + if (!vf) + { + return NULL; + } + + if (!CM_CalcChecksum(vf, &header, checksum, checksum2)) + { + VFS_CLOSE(vf); + return NULL; + } + + COM_FileBase (name, loadname); + + // Flush to temp zone to leave as much heap available to map loading as possible. + Hunk_TempFlush(); + + l_planes = CM_ReadLump(vf, &header.lumps[LUMP_PLANES]); + l_leafs = CM_ReadLump(vf, &header.lumps[LUMP_LEAFS]); + l_nodes = CM_ReadLump(vf, &header.lumps[LUMP_NODES]); + l_clipnodes = CM_ReadLump(vf, &header.lumps[LUMP_CLIPNODES]); + l_entities = CM_ReadLump(vf, &header.lumps[LUMP_ENTITIES]); + l_models = CM_ReadLump(vf, &header.lumps[LUMP_MODELS]); + l_vis = CM_ReadLump(vf, &header.lumps[LUMP_VISIBILITY]); + + xlumps = CM_LoadBSPX(vf, &header, &xheader); + if (xlumps) + l_physnormals = CM_BSPX_ReadLump(vf, &xheader, xlumps, "MVDSV_PHYSICSNORMALS", &l_physnormals_len); + + VFS_CLOSE(vf); + + if (!l_planes || !l_leafs || !l_nodes || !l_clipnodes || !l_entities || !l_models || !l_vis) + { + return NULL; } - if (checksum) - *checksum = map_checksum; - *checksum2 = map_checksum2; // load into heap - CM_LoadPlanes (&header->lumps[LUMP_PLANES]); - if (LittleLong(header->version) == Q1_BSPVERSION29a) { - CM_LoadLeafs29a(&header->lumps[LUMP_LEAFS]); - CM_LoadNodes29a(&header->lumps[LUMP_NODES]); - CM_LoadClipnodesBSP2(&header->lumps[LUMP_CLIPNODES]); + CM_LoadPlanes (l_planes, header.lumps[LUMP_PLANES].filelen); + if (LittleLong(header.version) == Q1_BSPVERSION29a) { + CM_LoadLeafs29a(l_leafs, header.lumps[LUMP_LEAFS].filelen); + CM_LoadNodes29a(l_nodes, header.lumps[LUMP_NODES].filelen); + CM_LoadClipnodesBSP2(l_clipnodes, header.lumps[LUMP_CLIPNODES].filelen); cm_load_pvs_func = CM_BuildPVS29a; } - else if (LittleLong(header->version) == Q1_BSPVERSION2) { - CM_LoadLeafsBSP2(&header->lumps[LUMP_LEAFS]); - CM_LoadNodesBSP2(&header->lumps[LUMP_NODES]); - CM_LoadClipnodesBSP2(&header->lumps[LUMP_CLIPNODES]); + else if (LittleLong(header.version) == Q1_BSPVERSION2) { + CM_LoadLeafsBSP2(l_leafs, header.lumps[LUMP_LEAFS].filelen); + CM_LoadNodesBSP2(l_nodes, header.lumps[LUMP_NODES].filelen); + CM_LoadClipnodesBSP2(l_clipnodes, header.lumps[LUMP_CLIPNODES].filelen); cm_load_pvs_func = CM_BuildPVSBSP2; } else { - CM_LoadLeafs(&header->lumps[LUMP_LEAFS]); - CM_LoadNodes(&header->lumps[LUMP_NODES]); - CM_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]); + CM_LoadLeafs(l_leafs, header.lumps[LUMP_LEAFS].filelen); + CM_LoadNodes(l_nodes, header.lumps[LUMP_NODES].filelen); + CM_LoadClipnodes(l_clipnodes, header.lumps[LUMP_CLIPNODES].filelen); cm_load_pvs_func = CM_BuildPVS; } - CM_LoadEntities (&header->lumps[LUMP_ENTITIES]); - CM_LoadSubmodels (&header->lumps[LUMP_MODELS]); + CM_LoadEntities (l_entities, header.lumps[LUMP_ENTITIES].filelen); + CM_LoadSubmodels (l_models, header.lumps[LUMP_MODELS].filelen); - CM_LoadPhysicsNormals(filelen); + CM_LoadPhysicsNormals(l_physnormals, l_physnormals_len); CM_MakeHull0 (); - cm_load_pvs_func (&header->lumps[LUMP_VISIBILITY], &header->lumps[LUMP_LEAFS]); + cm_load_pvs_func (l_vis, header.lumps[LUMP_VISIBILITY].filelen, l_leafs, header.lumps[LUMP_LEAFS].filelen); if (!clientload) // client doesn't need PHS CM_BuildPHS (); strlcpy (map_name, name, sizeof(map_name)); - Q_free(padded_buf); + // Flush temp zone to leave as much heap available to mods as possible. + Hunk_TempFlush(); return &map_cmodels[0]; } @@ -1631,6 +1536,41 @@ mphysicsnormal_t CM_PhysicsNormal(int num) return ret; } +static int CM_BSPX_FindOffset(dheader_t *header, int filesize) +{ + int i, xofs = 0; + + for (i = 0; i < HEADER_LUMPS; i++) { + xofs = max(xofs, header->lumps[i].fileofs + header->lumps[i].filelen); + } + + if (xofs + sizeof(bspx_header_t) > filesize) { + return -1; + } + + return xofs; +} + +static qbool CM_BSPX_LoadLumps(bspx_lump_t *lump, int numlumps, int filesize) +{ + int i; + + // byte-swap and check sanity + for (i = 0; i < numlumps; i++, lump++) { + lump->lumpname[sizeof(lump->lumpname) - 1] = '\0'; // make sure it ends with zero + lump->fileofs = LittleLong(lump->fileofs); + lump->filelen = LittleLong(lump->filelen); + if (lump->fileofs < 0 || lump->filelen < 0 || (unsigned)(lump->fileofs + lump->filelen) >(unsigned)filesize) { + Con_Printf("Invalid BSPX lump position, ofs: %d, len: %d, filelen: %d\n", lump->fileofs, lump->filelen, filesize); + return false; + } + } + + return true; +} + +#ifndef SERVERONLY +// Used by ezquake void* Mod_BSPX_FindLump(bspx_header_t* bspx_header, char* lumpname, int* plumpsize, byte* mod_base) { int i; @@ -1653,22 +1593,18 @@ void* Mod_BSPX_FindLump(bspx_header_t* bspx_header, char* lumpname, int* plumpsi return NULL; } +// Used by ezquake bspx_header_t* Mod_LoadBSPX(int filesize, byte* mod_base) { dheader_t* header; bspx_header_t* xheader; bspx_lump_t* lump; - int i; int xofs; // find end of last lump header = (dheader_t*)mod_base; - xofs = 0; - for (i = 0; i < HEADER_LUMPS; i++) { - xofs = max(xofs, header->lumps[i].fileofs + header->lumps[i].filelen); - } - - if (xofs + sizeof(bspx_header_t) > filesize) { + xofs = CM_BSPX_FindOffset(header, filesize); + if (xofs < 0) { return NULL; } @@ -1679,17 +1615,97 @@ bspx_header_t* Mod_LoadBSPX(int filesize, byte* mod_base) return NULL; } - // byte-swap and check sanity lump = (bspx_lump_t*)(xheader + 1); // lumps immediately follow the header - for (i = 0; i < xheader->numlumps; i++, lump++) { - lump->lumpname[sizeof(lump->lumpname) - 1] = '\0'; // make sure it ends with zero - lump->fileofs = LittleLong(lump->fileofs); - lump->filelen = LittleLong(lump->filelen); - if (lump->fileofs < 0 || lump->filelen < 0 || (unsigned)(lump->fileofs + lump->filelen) >(unsigned)filesize) { - return NULL; - } + if (!CM_BSPX_LoadLumps(lump, xheader->numlumps, filesize)) { + return NULL; } // success return xheader; } +#endif + +static byte* CM_BSPX_ReadLump(vfsfile_t *vf, bspx_header_t *xheader, bspx_lump_t* lump, char* lumpname, int* plumpsize) +{ + byte *buffer; + int i, read; + + if (!lump) { + return NULL; + } + + for (i = 0; i < xheader->numlumps; i++, lump++) { + if (!strcmp(lump->lumpname, lumpname)) { + if (plumpsize) { + *plumpsize = lump->filelen; + } + + buffer = Hunk_TempAllocMore(lump->filelen); + if (VFS_SEEK(vf, lump->fileofs, SEEK_SET) < 0) + { + Con_Printf("Seek to BSPX lump at %d failed\n", lump->fileofs); + return NULL; + } + read = VFS_READ(vf, buffer, lump->filelen, NULL); + if (read != lump->filelen) + { + Con_Printf("Failed to read BSPX lump, got %d of %d bytes\n", read, lump->filelen); + return NULL; + } + + return buffer; + } + } + + return NULL; +} + +static bspx_lump_t* CM_LoadBSPX(vfsfile_t *vf, dheader_t *header, bspx_header_t *xheader) +{ + bspx_lump_t* lump; + int xofs; + int read, lumpssize, filesize; + + filesize = VFS_GETLEN(vf); + + // find end of last lump + xofs = CM_BSPX_FindOffset(header, filesize); + if (xofs < 0) { + return NULL; + } + + if (VFS_SEEK(vf, xofs, SEEK_SET) < 0) + { + return NULL; + } + + read = VFS_READ(vf, xheader, sizeof(bspx_header_t), NULL); + if (read != sizeof(bspx_header_t)) + { + return NULL; + } + + xheader->numlumps = LittleLong(xheader->numlumps); + + lumpssize = sizeof(bspx_lump_t) * xheader->numlumps; + + if (xheader->numlumps < 0 || xofs + sizeof(bspx_header_t) + lumpssize > filesize) { + Con_Printf("Corrupt BSPX header\n"); + return NULL; + } + + lump = (bspx_lump_t*)Hunk_TempAllocMore(lumpssize); + + read = VFS_READ(vf, lump, lumpssize, NULL); + if (read != lumpssize) + { + Con_Printf("Failed to read BSPX lumps header, got %d of %d bytes\n", read, lumpssize); + return NULL; + } + + if (!CM_BSPX_LoadLumps(lump, xheader->numlumps, filesize)) { + return NULL; + } + + return lump; +} diff --git a/src/cmodel.h b/src/cmodel.h index 85c820ae9..7c7e64ec6 100644 --- a/src/cmodel.h +++ b/src/cmodel.h @@ -115,7 +115,9 @@ typedef struct bspx_header_s { int numlumps; } bspx_header_t; +#ifndef SERVERONLY void* Mod_BSPX_FindLump(bspx_header_t* bspx_header, char* lumpname, int* plumpsize, byte* mod_base); bspx_header_t* Mod_LoadBSPX(int filesize, byte* mod_base); +#endif #endif /* !__CMODEL_H__ */ diff --git a/src/g_public.h b/src/g_public.h index 483eb5d89..dfd2204c4 100644 --- a/src/g_public.h +++ b/src/g_public.h @@ -30,8 +30,35 @@ // // g_public.h -- game module information visible to server -#define GAME_API_VERSION 15 +#define GAME_API_VERSION 16 +/* + * Changes in GAME_API_VERSION 16: + * - server edict data removed from game edict: typedef struct shared_edict_s { entvars_t v;} edict_t; + * - SetSting works for PR1 only + * - ported VM from Q3 + * - QVM mods should get mapname and client netnames with infokey trap + * + * mod should get client netname in GAME_CLIENT_CONNECT call: + * self = PROG_TO_EDICT(g_globalvars.self); + * self->s.v.netname = netnames[NUM_FOR_EDICT(self)-1]; + * infokey( self, "netname", self->s.v.netname, 32); + * + * mod should get mapname in GAME_START_FRAME call: + * if (framecount == 0) + * { + * infokey(world, "mapname", mapname, sizeof(mapname)); + * infokey(world, "modelname", worldmodel, sizeof(worldmodel)); + * world->model = worldmodel; + * } + * + * infokey( world, "mapname", mapname, sizeof(mapname) ); + * + * - QVM GAME_CLIENT_USERINFO_CHANGED call now have integer paramater + * + * called with 0 before changing, with 1 after changing + * mod must update client netname in call with param 1 and key = "name" + */ //=============================================================== @@ -142,6 +169,8 @@ typedef enum G_SETPAUSE, G_SETUSERINFO, G_MOVETOGOAL, + G_VISIBLETO, + _G__LASTAPI } gameImport_t; // !!! new things comes to end of list !!! @@ -215,14 +244,30 @@ typedef struct typedef struct { - edict_t *ents; + int name; + int ofs; + int type; +} field_vm_t; + +typedef struct +{ + intptr_t ents; int sizeofent; - globalvars_t *global; - field_t *fields; + intptr_t global; + intptr_t fields; int APIversion; int maxentities; } gameData_t; +typedef struct +{ + int ents_p; + int sizeofent; + int global_p; + int fields_p; + int APIversion; + int maxentities; +} gameData_vm_t; typedef int fileHandle_t; typedef enum { diff --git a/src/pmove.c b/src/pmove.c index b36ea22c8..7e80f2350 100644 --- a/src/pmove.c +++ b/src/pmove.c @@ -29,9 +29,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. movevars_t movevars; playermove_t pmove; -static float pm_frametime; +static float pm_frametime; -static vec3_t pm_forward, pm_right; +static vec3_t pm_forward, pm_right; static vec3_t groundnormal; @@ -40,11 +40,11 @@ vec3_t player_maxs = {16, 16, 32}; #define STEPSIZE 18 -#define pm_flyfriction 4 +#define pm_flyfriction 4 -#define BLOCKED_FLOOR 1 -#define BLOCKED_STEP 2 -#define BLOCKED_OTHER 4 +#define BLOCKED_FLOOR 1 +#define BLOCKED_STEP 2 +#define BLOCKED_OTHER 4 #define BLOCKED_ANY 7 #define MAX_JUMPFIX_DOTPRODUCT -0.1 diff --git a/src/pr2.h b/src/pr2.h index 44190446e..e0eb152f4 100644 --- a/src/pr2.h +++ b/src/pr2.h @@ -23,11 +23,7 @@ #define __PR2_H__ -extern intptr_t sv_syscall(intptr_t arg, ...); -extern int sv_sys_callex(byte *data, unsigned int len, int fn, pr2val_t*arg); -typedef void (*pr2_trapcall_t)(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval); - -//extern int usedll; +intptr_t PR2_GameSystemCalls( intptr_t *args ); extern cvar_t sv_progtype; extern vm_t* sv_vm; @@ -68,7 +64,7 @@ void PR2_EdictThink(func_t f); #define PR_EdictThink PR2_EdictThink void PR2_EdictBlocked(func_t f); #define PR_EdictBlocked PR2_EdictBlocked -qbool PR2_UserInfoChanged(void); +qbool PR2_UserInfoChanged(int after); #define PR_UserInfoChanged PR2_UserInfoChanged void PR2_GameShutDown(void); #define PR_GameShutDown PR2_GameShutDown diff --git a/src/pr2_cmds.c b/src/pr2_cmds.c index 2553d058d..4ba2440c7 100644 --- a/src/pr2_cmds.c +++ b/src/pr2_cmds.c @@ -24,6 +24,8 @@ #ifdef USE_PR2 #include "qwsvdef.h" +#include "vm.h" +#include "vm_local.h" #define SETUSERINFO_STAR (1<<0) // allow set star keys @@ -32,8 +34,85 @@ #define Cbuf_ExecuteEx(x) Cbuf_Execute() #endif -const char* pr2_ent_data_ptr; -vm_t* sv_vm = NULL; +const char *pr2_ent_data_ptr; +vm_t *sv_vm = NULL; +extern gameData_t gamedata; + +static int PASSFLOAT(float f) +{ + floatint_t fi; + fi.f = f; + return fi.i; +} + +#if 0 // Provided for completness. +static float GETFLOAT(int i) +{ + floatint_t fi; + fi.i = i; + return fi.f; +} +#endif + +int NUM_FOR_GAME_EDICT(byte *e) +{ + int b; + + b = (byte *)e - (byte *)sv.game_edicts; + b /= pr_edict_size; + + if (b < 0 || b >= sv.num_edicts) + SV_Error("NUM_FOR_GAME_EDICT: bad pointer"); + + return b; +} + +intptr_t PR2_EntityStringLocation(string_t offset, int max_size); +void static PR2_SetEntityString_model(edict_t *ed, string_t *target, char *s) { + if (!sv_vm) { + PR1_SetString(target, s); + return; + } + + switch (sv_vm->type) { + case VMI_NONE: + PR1_SetString(target, s); + return; + + case VMI_NATIVE: + if (sv_vm->pr2_references) { + char **location = + (char **)PR2_EntityStringLocation(*target, sizeof(char *)); + if (location) { + *location = s; + } + } +#ifndef idx64 + else if (target) { + *target = (string_t)s; + } +#endif + return; + case VMI_BYTECODE: + case VMI_COMPILED: { + int off = VM_ExplicitPtr2VM(sv_vm, (byte *)s); + + if (sv_vm->pr2_references) { + string_t *location = + (string_t *)PR2_EntityStringLocation(*target, sizeof(string_t)); + + if (location) { + *location = off; + } + } else { + *target = off; + } + } + return; + } + + *target = 0; +} /* ============ @@ -52,8 +131,6 @@ void PR2_RunError(char *error, ...) va_end(argptr); sv_error = true; - if( sv_vm->type == VM_BYTECODE ) - QVM_StackTrace( (qvm_t *) sv_vm->hInst ); Con_Printf("%s\n", string); @@ -66,52 +143,13 @@ void PR2_CheckEmptyString(char *s) PR2_RunError("Bad string"); } -void PF2_GetApiVersion(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int = GAME_API_VERSION; -} - -void PF2_GetEntityToken(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - pr2_ent_data_ptr = COM_Parse(pr2_ent_data_ptr); - strlcpy((char*)VM_POINTER(base,mask,stack[0].string), com_token, stack[1]._int); - - retval->_int= pr2_ent_data_ptr != NULL; -} - -void PF2_DPrint(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - Con_DPrintf("%s", VM_POINTER(base,mask,stack[0].string)); -} - -void PF2_conprint(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - Sys_Printf("%s", VM_POINTER(base, mask, stack[0].string)); -} - -void PF2_Error(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - PR2_RunError((char *)VM_POINTER(base, mask, stack->string)); -} - -void PF2_Spawn(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int = NUM_FOR_EDICT( ED_Alloc() ); -} - -void PF2_Remove(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - ED_Free(EDICT_NUM(stack[0]._int)); -} - -void PF2_precache_sound(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_precache_sound(char *s) { int i; - char*s; + if (sv.state != ss_loading) PR2_RunError("PF_Precache_*: Precache can only be done in spawn " "functions"); - s = (char *) VM_POINTER(base,mask,stack[0].string); PR2_CheckEmptyString(s); for (i = 0; i < MAX_SOUNDS; i++) @@ -128,16 +166,14 @@ void PF2_precache_sound(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*re PR2_RunError ("PF_precache_sound: overflow"); } -void PF2_precache_model(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_precache_model(char *s) { int i; - char *s; if (sv.state != ss_loading) PR2_RunError("PF_Precache_*: Precache can only be done in spawn " "functions"); - s = (char *) VM_POINTER(base,mask,stack[0].string); PR2_CheckEmptyString(s); for (i = 0; i < MAX_MODELS; i++) @@ -154,16 +190,14 @@ void PF2_precache_model(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*re PR2_RunError ("PF_precache_model: overflow"); } -void PF2_precache_vwep_model(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_precache_vwep_model(char *s) { int i; - char *s; if (sv.state != ss_loading) PR2_RunError("PF_Precache_*: Precache can only be done in spawn " "functions"); - s = (char *) VM_POINTER(base,mask,stack[0].string); PR2_CheckEmptyString(s); // the strings are transferred via the stufftext mechanism, hence the stringency @@ -174,11 +208,11 @@ void PF2_precache_vwep_model(byte* base, uintptr_t mask, pr2val_t* stack, pr2val { if (!sv.vw_model_name[i]) { sv.vw_model_name[i] = s; - retval->_int = i; - return; + return i; } } PR2_RunError ("PF_precache_vwep_model: overflow"); + return 0; } /* @@ -191,18 +225,15 @@ setorigin (entity, origin) ================= */ -void PF2_setorigin(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_setorigin(edict_t *e, float x, float y, float z) { vec3_t origin; - edict_t *e; - e = EDICT_NUM(stack[0]._int); + origin[0] = x; + origin[1] = y; + origin[2] = z; - origin[0] = stack[1]._float; - origin[1] = stack[2]._float; - origin[2] = stack[3]._float; - - VectorCopy(origin, e->v.origin); + VectorCopy(origin, e->v->origin); SV_AntilagReset(e); SV_LinkEdict(e, false); } @@ -216,22 +247,18 @@ the size box is rotated by the current angle setsize (entity, minvector, maxvector) ================= */ -void PF2_setsize(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_setsize(edict_t *e, float x1, float y1, float z1, float x2, float y2, float z2) { - //vec3_t min, max; - edict_t *e ; - - e = EDICT_NUM(stack[0]._int); - - e->v.mins[0] = stack[1]._float; - e->v.mins[1] = stack[2]._float; - e->v.mins[2] = stack[3]._float; + // vec3_t min, max; + e->v->mins[0] = x1; + e->v->mins[1] = y1; + e->v->mins[2] = z1; - e->v.maxs[0] = stack[4]._float; - e->v.maxs[1] = stack[5]._float; - e->v.maxs[2] = stack[6]._float; + e->v->maxs[0] = x2; + e->v->maxs[1] = y2; + e->v->maxs[2] = z2; - VectorSubtract(e->v.maxs, e->v.mins, e->v.size); + VectorSubtract(e->v->maxs, e->v->mins, e->v->size); SV_LinkEdict(e, false); } @@ -244,17 +271,13 @@ setmodel(entity, model) Also sets size, mins, and maxs for inline bmodels ================= */ -void PF2_setmodel(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_setmodel(edict_t *e, char *m) { - edict_t *e; - char *m; char **check; int i; cmodel_t *mod; - e = EDICT_NUM(stack[0]._int); - m = (char *) VM_POINTER(base,mask,stack[1].string); - if(!m) + if (!m) m = ""; // check to see if model was properly precached for (i = 0, check = sv.model_precache; *check; i++, check++) @@ -264,35 +287,21 @@ void PF2_setmodel(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) if (!*check) PR2_RunError("no precache: %s\n", m); - PR2_SetEntityString(e, &e->v.model, m); - e->v.modelindex = i; + PR2_SetEntityString_model(e, &e->v->model, m); + e->v->modelindex = i; // if it is an inline model, get the size information for it if (m[0] == '*') { mod = CM_InlineModel (m); - VectorCopy(mod->mins, e->v.mins); - VectorCopy(mod->maxs, e->v.maxs); - VectorSubtract(mod->maxs, mod->mins, e->v.size); + VectorCopy(mod->mins, e->v->mins); + VectorCopy(mod->maxs, e->v->maxs); + VectorSubtract(mod->maxs, mod->mins, e->v->size); SV_LinkEdict(e, false); } } -/* -================= -PF2_bprint - -broadcast print to everyone on server - -bprint(value) -================= -*/ -void PF2_bprint(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - SV_BroadcastPrintfEx(stack[0]._int, stack[2]._int, "%s", VM_POINTER(base,mask,stack[1].string)); -} - /* ================= PF2_sprint @@ -306,15 +315,13 @@ sprint(clientent, value) // trap_SPrint() flags #define SPRINT_IGNOREINDEMO ( 1<<0) // do not put such message in mvd demo -void PF2_sprint(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_sprint(int entnum, int level, char *s, int flags) { client_t *client, *cl; - int entnum = stack[0]._int; - int level = stack[1]._int; - int flags = stack[3]._int; // using this atm just as hint to not put this message in mvd demo - char *s = (char *) VM_POINTER(base,mask,stack[2].string); int i; + if (gamedata.APIversion < 15) + flags = 0; if (entnum < 1 || entnum > MAX_CLIENTS) { Con_Printf("tried to sprint to a non-client %d \n", entnum); @@ -361,11 +368,9 @@ single print to a specific client centerprint(clientent, value) ================= */ -void PF2_centerprint(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_centerprint(int entnum, char *s) { client_t *cl, *spec; - int entnum = stack[0]._int; - char *s = (char *) VM_POINTER(base,mask,stack[1].string); int i; if (entnum < 1 || entnum > MAX_CLIENTS) @@ -413,24 +418,18 @@ PF2_ambientsound ================= */ -void PF2_ambientsound(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_ambientsound(float x, float y, float z, char *samp, float vol, float attenuation) { char **check; int i, soundnum; vec3_t pos; - char *samp; - float vol; - float attenuation; - pos[0] = stack[0]._float; - pos[1] = stack[1]._float; - pos[2] = stack[2]._float; + pos[0] = x; + pos[1] = y; + pos[2] = z; - samp = (char *) VM_POINTER(base,mask,stack[3].string); if( !samp ) samp = ""; - vol = stack[4]._float; - attenuation = stack[5]._float; // check to see if samp was properly precached for (soundnum = 0, check = sv.sound_precache; *check; check++, soundnum++) @@ -455,32 +454,6 @@ void PF2_ambientsound(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retv } -/* -================= -PF2_sound - -Each entity can have eight independant sound sources, like voice, -weapon, feet, etc. - -Channel 0 is an auto-allocate channel, the others override anything -already running on that entity/channel pair. - -An attenuation of 0 will play full volume everywhere in the level. -Larger attenuations will drop off. - -================= -*/ -void PF2_sound(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - edict_t *entity = EDICT_NUM(stack[0]._int); - int channel = stack[1]._int; - char *sample = (char *) VM_POINTER(base,mask,stack[2].string); - int volume = stack[3]._float * 255; - float attenuation = stack[4]._float; - - SV_StartSound(entity, channel, sample, volume, attenuation); -} - /* ================= PF2_traceline @@ -492,25 +465,22 @@ if the tryents flag is set. traceline (vector1, vector2, tryents) ================= */ -void PF2_traceline(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_traceline(float v1_x, float v1_y, float v1_z, + float v2_x, float v2_y, float v2_z, + int nomonsters, int entnum) { trace_t trace; edict_t *ent; vec3_t v1, v2; - int nomonsters, entnum; - - v1[0] = stack[0]._float; - v1[1] = stack[1]._float; - v1[2] = stack[2]._float; - - v2[0] = stack[3]._float; - v2[1] = stack[4]._float; - v2[2] = stack[5]._float; - - nomonsters = stack[6]._int; - entnum = stack[7]._int; ent = EDICT_NUM(entnum); + v1[0] = v1_x; + v1[1] = v1_y; + v1[2] = v1_z; + + v2[0] = v2_x; + v2[1] = v2_y; + v2[2] = v2_z; if (sv_antilag.value == 2) { @@ -543,41 +513,32 @@ PF2_TraceCapsule Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. - -void trap_TraceCapsule( float v1_x, float v1_y, float v1_z, - float v2_x, float v2_y, float v2_z, - int nomonst, int edn , - float min_x, float min_y, float min_z, - float max_x, float max_y, float max_z); - ================= */ -void PF2_TraceCapsule(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_TraceCapsule(float v1_x, float v1_y, float v1_z, + float v2_x, float v2_y, float v2_z, + int nomonsters, edict_t *ent, + float min_x, float min_y, float min_z, + float max_x, float max_y, float max_z) { trace_t trace; - edict_t *ent; vec3_t v1, v2, v3, v4; - int nomonsters;//, entnum; - - v1[0] = stack[0]._float; - v1[1] = stack[1]._float; - v1[2] = stack[2]._float; - - v2[0] = stack[3]._float; - v2[1] = stack[4]._float; - v2[2] = stack[5]._float; - nomonsters = stack[6]._int; + v1[0] = v1_x; + v1[1] = v1_y; + v1[2] = v1_z; - ent = EDICT_NUM(stack[7]._int); + v2[0] = v2_x; + v2[1] = v2_y; + v2[2] = v2_z; - v3[0] = stack[8]._float; - v3[1] = stack[9]._float; - v3[2] = stack[10]._float; + v3[0] = min_x; + v3[1] = min_y; + v3[2] = min_z; - v4[0] = stack[11]._float; - v4[1] = stack[12]._float; - v4[2] = stack[13]._float; + v4[0] = max_x; + v4[1] = max_y; + v4[2] = max_z; trace = SV_Trace(v1, v3, v4, v2, nomonsters, ent); @@ -641,11 +602,11 @@ int PF2_newcheckclient(int check) if (i == check) break; // didn't find anything else - if (ent->e->free) + if (ent->e.free) continue; - if (ent->v.health <= 0) + if (ent->v->health <= 0) continue; - if ((int) ent->v.flags & FL_NOTARGET) + if ((int) ent->v->flags & FL_NOTARGET) continue; // anything that is a client, or has a client as an enemy @@ -653,7 +614,7 @@ int PF2_newcheckclient(int check) } // get the PVS for the entity - VectorAdd (ent->v.origin, ent->v.view_ofs, org); + VectorAdd (ent->v->origin, ent->v->view_ofs, org); checkpvs = CM_LeafPVS (CM_PointInLeaf(org)); return i; @@ -661,7 +622,7 @@ int PF2_newcheckclient(int check) #define MAX_CHECK 16 -void PF2_checkclient(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_checkclient(void) { edict_t *ent, *self; int l; @@ -676,27 +637,22 @@ void PF2_checkclient(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva // return check if it might be visible ent = EDICT_NUM(sv.lastcheck); - if (ent->e->free || ent->v.health <= 0) + if (ent->e.free || ent->v->health <= 0) { // RETURN_EDICT(sv.edicts); - retval->_int = NUM_FOR_EDICT(sv.edicts); - return; + return NUM_FOR_EDICT(sv.edicts); } // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT(pr_global_struct->self); - VectorAdd(self->v.origin, self->v.view_ofs, view); + VectorAdd(self->v->origin, self->v->view_ofs, view); l = CM_Leafnum(CM_PointInLeaf(view)) - 1; if ((l < 0) || !(checkpvs[l >> 3] & (1 << (l & 7)))) { - retval->_int = NUM_FOR_EDICT(sv.edicts); - return; - + return NUM_FOR_EDICT(sv.edicts); } - retval->_int = NUM_FOR_EDICT(ent); - return; - + return NUM_FOR_EDICT(ent); } //============================================================================ @@ -715,15 +671,14 @@ stuffcmd (clientent, value) #define STUFFCMD_IGNOREINDEMO ( 1<<0) // do not put in mvd demo #define STUFFCMD_DEMOONLY ( 1<<1) // put in mvd demo only -void PF2_stuffcmd(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_stuffcmd(int entnum, char *str, int flags) { - char *str=NULL, *buf=NULL; + char *buf = NULL; client_t *cl, *spec; - int entnum = stack[0]._int; - int flags = stack[2]._int; // using this atm just as hint to not put this in mvd demo int j; - str = (char *) VM_POINTER(base,mask,stack[1].string); + if (gamedata.APIversion < 15) + flags = 0; if( !str ) PR2_RunError("PF2_stuffcmd: NULL pointer"); @@ -801,19 +756,10 @@ void PF2_stuffcmd(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) /* ================= -PF2_localcmd - -Sends text over to the server's execution buffer - -localcmd (string) +PF2_executecmd ================= */ -void PF2_localcmd(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - Cbuf_AddTextEx(&cbuf_server, (char *)VM_POINTER(base,mask,stack[0].string)); -} - -void PF2_executecmd(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_executecmd(void) { int old_other, old_self; // mod_consolecmd will be executed, so we need to store this @@ -834,19 +780,12 @@ void readmcmd (string str,string buff, int sizeofbuff) ================= */ -void PF2_readcmd (byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_readcmd(char *str, char *buf, int sizebuff) { - char *str; extern char outputbuf[]; - char *buf; - int sizebuff; extern redirect_t sv_redirected; redirect_t old; - str = (char *) VM_POINTER(base,mask,stack[0].string); - buf = (char *) VM_POINTER(base,mask,stack[1].string); - sizebuff = stack[2]._int; - Cbuf_ExecuteEx(&cbuf_server); Cbuf_AddTextEx(&cbuf_server, str); @@ -875,23 +814,17 @@ void redirectcmd (entity to, string str) ================= */ -void PF2_redirectcmd(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_redirectcmd(int entnum, char *str) { - char *str; - int entnum; - // redirect_t old; extern redirect_t sv_redirected; - str = (char *)VM_POINTER(base, mask, stack[1].string); if (sv_redirected) { Cbuf_AddTextEx(&cbuf_server, str); Cbuf_ExecuteEx(&cbuf_server); return; } - entnum = NUM_FOR_EDICT((edict_t *)VM_POINTER(base, mask, stack[0]._int)); - if (entnum < 1 || entnum > MAX_CLIENTS) { PR2_RunError("Parm 0 not a client"); } @@ -902,63 +835,6 @@ void PF2_redirectcmd(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva SV_EndRedirect(); } -/* -================= -PF2_cvar - -float trap_cvar( const char *var ); -================= -*/ -void PF2_cvar(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float = Cvar_Value((char *)VM_POINTER(base,mask,stack[0].string)); -} - -/* -================= -PF2_cvar_string - -void trap_cvar_string( const char *var, char *buffer, int bufsize ) -================= -*/ -void PF2_cvar_string(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - intptr_t buff_off = stack[1]._int; - intptr_t buffsize = stack[2]._int; - - if( ( buff_off ) &(~mask)) - return; - - if( ( buff_off + buffsize ) &(~mask)) - return; - - strlcpy((char *)VM_POINTER(base,mask,buff_off), - Cvar_String((char *)VM_POINTER(base,mask,stack[0].string)), buffsize); -} - -/* -================= -PF2_cvar_set - -void trap_cvar_set( const char *var, const char *val ); -================= -*/ -void PF2_cvar_set(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - Cvar_SetByName((char *) VM_POINTER(base,mask,stack[0].string), (char *) VM_POINTER(base,mask,stack[1].string)); -} -/* -================= -PF2_cvar_set_float - -void trap_cvar_set_float( const char *var, float val ); -================= -*/ -void PF2_cvar_set_float(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - Cvar_SetValueByName((char *)VM_POINTER(base,mask,stack[0].string), stack[1]._float); -} - /* ================= PF2_FindRadius @@ -968,36 +844,28 @@ gedict_t *findradius( gedict_t * start, vec3_t org, float rad ); ================= */ -void PF2_FindRadius( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retval ) +intptr_t PF2_FindRadius(int e, float *org, float rad) { - int e,j; + int j; edict_t *ed; - float* org; vec3_t eorg; - float rad; - - e = NUM_FOR_EDICT( (edict_t *) VM_POINTER( base, mask, stack[0]._int ) ); - org = (float *) VM_POINTER( base, mask, stack[1]._int ); - rad = stack[2]._float; for ( e++; e < sv.num_edicts; e++ ) { ed = EDICT_NUM( e ); - if (ed->e->free) + if (ed->e.free) continue; - if (ed->v.solid == SOLID_NOT) + if (ed->v->solid == SOLID_NOT) continue; for (j=0 ; j<3 ; j++) - eorg[j] = org[j] - (ed->v.origin[j] + (ed->v.mins[j] + ed->v.maxs[j])*0.5); + eorg[j] = org[j] - (ed->v->origin[j] + (ed->v->mins[j] + ed->v->maxs[j])*0.5); if (VectorLength(eorg) > rad) continue; - retval->_int = POINTER_TO_VM( base, mask, ed ); - return; + return VM_Ptr2VM((byte *)ed->v); } - retval->_int = 0; - return; + return 0; } /* @@ -1007,34 +875,15 @@ PF2_walkmove float(float yaw, float dist) walkmove =============== */ -void PF2_walkmove(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -//(int entn, float yaw, float dist) +int PF2_walkmove(edict_t *ent, float yaw, float dist) { - edict_t *ent; - float yaw, dist; vec3_t move; - // dfunction_t *oldf; int oldself; - // int ret; - // - - /*if( sv_vm->type == VM_BYTECODE)///FIXME !!! not worked yet - { - retval->_int = 0; - return; - }*/ - // ent = PROG_TO_EDICT(pr_global_struct->self); - // yaw = G_FLOAT(OFS_PARM0); - // dist = G_FLOAT(OFS_PARM1); - ent = EDICT_NUM(stack[0]._int); - yaw = stack[1]._float; - dist = stack[2]._float; + int ret; - if (!((int) ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) + if (!((int) ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) { - retval->_int = 0; - return; - + return 0; } yaw = yaw * M_PI * 2 / 360; @@ -1047,13 +896,12 @@ void PF2_walkmove(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) // oldf = pr_xfunction; oldself = pr_global_struct->self; - retval->_int = SV_movestep(ent, move, true); - + ret = SV_movestep(ent, move, true); // restore program state // pr_xfunction = oldf; pr_global_struct->self = oldself; - return; + return ret; } /* @@ -1064,26 +912,23 @@ float(float dist) PF2_MoveToGoal =============== */ -void PF2_MoveToGoal(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_MoveToGoal(float dist) { edict_t *ent, *goal; - float dist; // dfunction_t *oldf; int oldself; ent = PROG_TO_EDICT(pr_global_struct->self); - goal = PROG_TO_EDICT(ent->v.goalentity); - dist = stack[0]._float; + goal = PROG_TO_EDICT(ent->v->goalentity); - if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { - retval->_int = 0; return; } // if the next step hits the enemy, return immediately - if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) + if ( PROG_TO_EDICT(ent->v->enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) return; // save program state, because SV_movestep may call other progs @@ -1091,7 +936,7 @@ void PF2_MoveToGoal(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval oldself = pr_global_struct->self; // bump around... - if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v->ideal_yaw, dist)) { SV_NewChaseDir (ent, goal, dist); } @@ -1101,9 +946,6 @@ void PF2_MoveToGoal(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval pr_global_struct->self = oldself; } - - - /* =============== PF2_droptofloor @@ -1111,32 +953,27 @@ PF2_droptofloor void(entnum) droptofloor =============== */ -void PF2_droptofloor(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +int PF2_droptofloor(edict_t *ent) { - edict_t *ent; vec3_t end; trace_t trace; - ent = EDICT_NUM(stack[0]._int); - - VectorCopy(ent->v.origin, end); + VectorCopy(ent->v->origin, end); end[2] -= 256; - trace = SV_Trace(ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); + trace = SV_Trace(ent->v->origin, ent->v->mins, ent->v->maxs, end, false, ent); if (trace.fraction == 1 || trace.allsolid) { - retval->_int = 0; - return; + return 0; } else { - VectorCopy(trace.endpos, ent->v.origin); + VectorCopy(trace.endpos, ent->v->origin); SV_LinkEdict(ent, false); - ent->v.flags = (int) ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); - retval->_int = 1; - return; + ent->v->flags = (int) ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(trace.e.ent); + return 1; } } @@ -1147,14 +984,10 @@ PF2_lightstyle void(int style, string value) lightstyle =============== */ -void PF2_lightstyle(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_lightstyle(int style, char *val) { client_t *client; - int j,style; - char* val; - - style = stack[0]._int; - val = (char *) VM_POINTER(base,mask,stack[1]._int); + int j; // change the string in sv sv.lightstyles[style] = val; @@ -1181,57 +1014,45 @@ void PF2_lightstyle(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval } } } -/* -============= -PF2_checkbottom -============= -*/ -void PF2_checkbottom(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int = SV_CheckBottom(EDICT_NUM(stack[0]._int)); -} /* ============= PF2_pointcontents ============= */ -void PF2_pointcontents(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +int PF2_pointcontents(float x, float y, float z) { - vec3_t v; - v[0] = stack[0]._float; - v[1] = stack[1]._float; - v[2] = stack[2]._float; + vec3_t origin; + + origin[0] = x; + origin[1] = y; + origin[2] = z; - retval->_int = SV_PointContents(v); + return SV_PointContents(origin); } /* ============= PF2_nextent - + entity nextent(entity) ============= */ -void PF2_nextent(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_nextent(int i) { - int i; edict_t *ent; - i = stack[0]._int; while (1) { i++; if (i >= sv.num_edicts) { - retval->_int = 0; - return; + return 0; } ent = EDICT_NUM(i); - if (!ent->e->free) + if (!ent->e.free) { - retval->_int = i; - return; + return i; } } } @@ -1245,27 +1066,23 @@ fast walk over spawned clients entity nextclient(entity) ============= */ -void PF2_nextclient(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_nextclient(int i) { - int i; edict_t *ent; - i = NUM_FOR_EDICT((edict_t *) VM_POINTER(base,mask,stack[0]._int));; while (1) { i++; if (i < 1 || i > MAX_CLIENTS) { - retval->_int = 0; - return; + return 0; } ent = EDICT_NUM(i); - if (!ent->e->free) // actually that always true for clients edicts + if (!ent->e.free) // actually that always true for clients edicts { - if (svs.clients[i-1].state == cs_spawned) // client in game + if (svs.clients[i - 1].state == cs_spawned) // client in game { - retval->_int = POINTER_TO_VM(base,mask,ent); - return; + return VM_Ptr2VM((byte *)ent->v); } } } @@ -1278,43 +1095,34 @@ PF2_find entity find(start,fieldoff,str) ============= */ -void PF2_Find (byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_Find(int e, int fofs, char *str) { - int e; - int fofs; - char* str, *t; + char *t; edict_t *ed; - e = NUM_FOR_EDICT((edict_t *) VM_POINTER(base,mask,stack[0]._int)); - fofs = stack[1]._int; - - str = (char *) VM_POINTER(base,mask,stack[2].string); - if(!str) PR2_RunError ("PF2_Find: bad search string"); for (e++ ; e < sv.num_edicts ; e++) { ed = EDICT_NUM(e); - if (ed->e->free) + if (ed->e.free) continue; - if (!(intptr_t*)((byte*)ed + fofs)) + if (!(intptr_t *)((byte *)ed->v + fofs)) continue; - t = (char *) VM_POINTER(base,mask,*(intptr_t*)((byte*)ed + fofs)); + t = VM_ArgPtr(*(intptr_t *)((char *)ed->v + fofs)); if (!t) continue; if (!strcmp(t,str)) { - retval->_int = POINTER_TO_VM(base,mask,ed); - return; + return VM_Ptr2VM((byte *)ed->v); } } - retval->_int = 0; - return; + return 0; } /* @@ -1403,11 +1211,8 @@ static client_t *Write_GetClient(void) return &svs.clients[entnum - 1]; } -void PF2_WriteByte(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteByte(int to, int data) { - int to = stack[0]._int; - int data = stack[1]._int; - if (to == MSG_ONE) { client_t *cl = Write_GetClient(); @@ -1425,11 +1230,8 @@ void PF2_WriteByte(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) MSG_WriteByte(WriteDest2(to), data); } -void PF2_WriteChar(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteChar(int to, int data) { - int to = stack[0]._int; - int data = stack[1]._int; - if (to == MSG_ONE) { client_t *cl = Write_GetClient(); @@ -1447,11 +1249,8 @@ void PF2_WriteChar(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) MSG_WriteChar(WriteDest2(to), data); } -void PF2_WriteShort(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteShort(int to, int data) { - int to = stack[0]._int; - int data = stack[1]._int; - if (to == MSG_ONE) { client_t *cl = Write_GetClient(); @@ -1469,11 +1268,8 @@ void PF2_WriteShort(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval MSG_WriteShort(WriteDest2(to), data); } -void PF2_WriteLong(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteLong(int to, int data) { - int to = stack[0]._int; - int data = stack[1]._int; - if (to == MSG_ONE) { client_t *cl = Write_GetClient(); @@ -1491,11 +1287,8 @@ void PF2_WriteLong(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) MSG_WriteLong(WriteDest2(to), data); } -void PF2_WriteAngle(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteAngle(int to, float data) { - int to = stack[0]._int; - float data = stack[1]._float; - if (to == MSG_ONE) { #ifdef FTE_PEXT_FLOATCOORDS @@ -1518,11 +1311,8 @@ void PF2_WriteAngle(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval MSG_WriteAngle(WriteDest2(to), data); } -void PF2_WriteCoord(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteCoord(int to, float data) { - int to = stack[0]._int; - float data = stack[1]._float; - if (to == MSG_ONE) { #ifdef FTE_PEXT_FLOATCOORDS @@ -1545,11 +1335,8 @@ void PF2_WriteCoord(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval MSG_WriteCoord(WriteDest2(to), data); } -void PF2_WriteString(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteString(int to, char *data) { - int to = stack[0]._int; - char* data = (char *) VM_POINTER(base,mask,stack[1].string); - if (to == MSG_ONE) { client_t *cl = Write_GetClient(); @@ -1567,12 +1354,8 @@ void PF2_WriteString(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva MSG_WriteString(WriteDest2(to), data); } - -void PF2_WriteEntity(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_WriteEntity(int to, int data) { - int to = stack[0]._int; - int data = stack[1]._int; - if (to == MSG_ONE) { client_t *cl = Write_GetClient(); @@ -1600,12 +1383,10 @@ PF2_makestatic ================== */ -void PF2_makestatic(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_makestatic(edict_t *ent) { - entity_state_t* s; - edict_t *ent; + entity_state_t *s; - ent = EDICT_NUM(stack[0]._int); if (sv.static_entity_count >= sizeof(sv.static_entities) / sizeof(sv.static_entities[0])) { ED_Free (ent); return; @@ -1614,16 +1395,16 @@ void PF2_makestatic(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval s = &sv.static_entities[sv.static_entity_count]; memset(s, 0, sizeof(sv.static_entities[0])); s->number = sv.static_entity_count + 1; - s->modelindex = SV_ModelIndex(PR_GetEntityString(ent->v.model)); + s->modelindex = SV_ModelIndex(PR_GetEntityString(ent->v->model)); if (!s->modelindex) { ED_Free (ent); return; } - s->frame = ent->v.frame; - s->colormap = ent->v.colormap; - s->skinnum = ent->v.skin; - VectorCopy(ent->v.origin, s->origin); - VectorCopy(ent->v.angles, s->angles); + s->frame = ent->v->frame; + s->colormap = ent->v->colormap; + s->skinnum = ent->v->skin; + VectorCopy(ent->v->origin, s->origin); + VectorCopy(ent->v->angles, s->angles); ++sv.static_entity_count; // throw the entity away now @@ -1637,15 +1418,11 @@ void PF2_makestatic(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval PF2_setspawnparms ============== */ -void PF2_setspawnparms(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_setspawnparms(int entnum) { int i; - //edict_t *ent; - int entnum=stack[0]._int; client_t *client; - //ent = EDICT_NUM(entnum); - if (entnum < 1 || entnum > MAX_CLIENTS) PR2_RunError("Entity is not a client"); @@ -1661,13 +1438,13 @@ void PF2_setspawnparms(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*ret PF2_changelevel ============== */ -void PF2_changelevel(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_changelevel(char *s, char *entfile) { static int last_spawncount; - char *s = (char *) VM_POINTER(base,mask,stack[0].string); - char *entfile = (char *) VM_POINTER(base,mask,stack[1].string); char expanded[MAX_QPATH]; + if (gamedata.APIversion < 15) + entfile = ""; // check to make sure the level exists. // this is work around for bellow check about two changelevels, // which lock server in one map if we trying switch to map which does't exist @@ -1699,21 +1476,12 @@ PF2_logfrag logfrag (killer, killee) ============== */ -void PF2_logfrag(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_logfrag(int e1, int e2) { - // edict_t *ent1, *ent2; - int e1, e2; - char *s; + char *s; // -> scream time_t t; struct tm *tblock; - // <- - - //ent1 = G_EDICT(OFS_PARM0); - //ent2 = G_EDICT(OFS_PARM1); - - e1 = stack[0]._int; - e2 = stack[1]._int; if (e1 < 1 || e1 > MAX_CLIENTS || e2 < 1 || e2 > MAX_CLIENTS) return; @@ -1746,21 +1514,11 @@ PF2_getinfokey string(entity e, string key) infokey ============== */ -void PF2_infokey(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_infokey(int e1, char *key, char *valbuff, int sizebuff) //(int e1, char *key, char *valbuff, int sizebuff) { static char ov[256]; - - // edict_t *e; - int e1 = stack[0]._int; - char *key = (char *) VM_POINTER(base,mask,stack[1].string); - char *valbuff= (char *) VM_POINTER(base,mask,stack[2].string); char *value; - int sizebuff= stack[3]._int; - - // e = G_EDICT(OFS_PARM0); - // e1 = NUM_FOR_EDICT(e); - // key = G_STRING(OFS_PARM1); value = ov; @@ -1775,6 +1533,9 @@ void PF2_infokey(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) || !strcmp(key, "ip") || !strncmp(key, "realip", 7) || !strncmp(key, "download", 9) || !strcmp(key, "ping") || !strcmp(key, "*userid") || !strncmp(key, "login", 6) || !strcmp(key, "*VIP") || !strcmp(key, "*state") + || !strcmp(key, "netname") + || !strcmp(key, "mapname") || !strcmp(key, "modelname") + || !strcmp(key, "version") || !strcmp(key, "servername") ) value = "yes"; } @@ -1784,6 +1545,18 @@ void PF2_infokey(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) SV_TimeOfDay(&date, "%a %b %d, %H:%M:%S %Y"); snprintf(ov, sizeof(ov), "%s", date.str); } + else if (!strcmp(key, "mapname")) { + value = sv.mapname; + } + else if (!strcmp(key, "modelname")) { + value = sv.modelname; + } + else if (!strcmp(key, "version")) { + value = VersionStringFull(); + } + else if (!strcmp(key, "servername")) { + value = SERVER_NAME; + } else if ((value = Info_ValueForKey(svs.info, key)) == NULL || !*value) value = Info_Get(&_localinfo_, key); } @@ -1805,6 +1578,8 @@ void PF2_infokey(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) value = cl->login; else if (!strcmp(key, "*VIP")) // qqshka: also located in userinfo, but this is more safe/secure way, imo snprintf(ov, sizeof(ov), "%d", cl->vip); + else if (!strcmp(key, "netname")) + value = cl->name; else if (!strcmp(key, "*state")) { switch (cl->state) @@ -1838,16 +1613,14 @@ PF2_multicast void(vector where, float set) multicast ============== */ -void PF2_multicast(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_multicast(float x, float y, float z, int to) //(vec3_t o, int to) { vec3_t o; - int to; - o[0] = stack[0]._float; - o[1] = stack[1]._float; - o[2] = stack[2]._float; - to = stack[3]._int; + o[0] = x; + o[1] = y; + o[2] = z; SV_Multicast(o, to); } @@ -1858,12 +1631,10 @@ PF2_disable_updates void(entiny whom, float time) disable_updates ============== */ -void PF2_disable_updates(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_disable_updates(int entnum, float time1) //(int entnum, float time) { client_t *client; - int entnum = stack[0]._int; - float time1 = stack[1]._float; // entnum = G_EDICTNUM(OFS_PARM0); // time1 = G_FLOAT(OFS_PARM1); @@ -1879,119 +1650,6 @@ void PF2_disable_updates(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*r client->disable_updates_stop = realtime + time1; } -/* -============== -PR2_FlushSignon(); -============== -*/ -void PR2_FlushSignon(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - SV_FlushSignon(); -} - -/* -============== -PF2_cmdargc -============== -*/ -void PF2_cmdargc(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int = Cmd_Argc(); -} - -/* -============== -PF2_cmdargv -============== -*/ -void PF2_cmdargv(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -//(int arg, char *valbuff, int sizebuff) -{ - strlcpy((char *) VM_POINTER(base,mask,stack[1].string), Cmd_Argv(stack[0]._int), stack[2]._int); -} - -/* -============== -PF2_cmdargs -============== -*/ -void PF2_cmdargs(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -//(char *valbuff, int sizebuff) -{ - strlcpy((char *) VM_POINTER(base,mask,stack[0].string), Cmd_Args(), stack[1]._int); -} - -/* -============== -PF2_tokenize -============== -*/ -void PF2_tokenize(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -//(char *str) -{ - Cmd_TokenizeString((char *) VM_POINTER(base,mask,stack[0].string)); -} - -void PF2_fixme(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - PR2_RunError ("unimplemented bulitin"); -} - -void PF2_memset(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - memset(VM_POINTER(base, mask, stack[0].string), stack[1]._int, stack[2]._int); - - retval->_int = stack[0].string; -} - -void PF2_memcpy(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - memcpy(VM_POINTER(base, mask, stack[0].string), VM_POINTER(base, mask, stack[1].string), stack[2]._int); - - retval->_int = stack[0].string; -} -void PF2_strncpy(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - strncpy((char *)VM_POINTER(base, mask, stack[0].string), (char *)VM_POINTER(base, mask, stack[1].string), stack[2]._int); - - retval->_int = stack[0].string; -} - -void PF2_sin(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=sin(stack[0]._float); -} - -void PF2_cos(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=cos(stack[0]._float); -} - -void PF2_atan2(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=atan2(stack[0]._float,stack[1]._float); -} - -void PF2_sqrt(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=sqrt(stack[0]._float); -} - -void PF2_floor(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=floor(stack[0]._float); -} -void PF2_ceil(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=ceil(stack[0]._float); -} - -void PF2_acos(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_float=acos(stack[0]._float); -} - - #define MAX_PR2_FILES 8 typedef struct @@ -2006,21 +1664,14 @@ pr2_fopen_files_t pr2_fopen_files[MAX_PR2_FILES]; int pr2_num_open_files = 0; char* cmodes[]={"rb","r","wb","w","ab","a"}; -/* -int trap_FS_OpenFile(char*name, fileHandle_t* handle, fsMode_t fmode ); -*/ -//FIXME: read from paks -void PF2_FS_OpenFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +// FIXME: read from paks +int PF2_FS_OpenFile(char *name, fileHandle_t *handle, fsMode_t fmode) { - char *name=(char*)VM_POINTER(base,mask,stack[0].string); - fileHandle_t* handle=(fileHandle_t*)VM_POINTER(base,mask,stack[1]._int); - fsMode_t fmode = (fsMode_t) stack[2]._int; - int i; + int i, ret = -1; if(pr2_num_open_files >= MAX_PR2_FILES) { - retval->_int = -1; - return ; + return -1; } *handle = 0; @@ -2029,14 +1680,12 @@ void PF2_FS_OpenFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva break; if (i == MAX_PR2_FILES) //too many already open { - retval->_int = -1; - return ; + return -1; } if (FS_UnsafeFilename(name)) { // someone tried to be clever. - retval->_int = -1; - return ; + return -1; } strlcpy(pr2_fopen_files[i].name, name, sizeof(pr2_fopen_files[i].name)); pr2_fopen_files[i].accessmode = fmode; @@ -2052,13 +1701,12 @@ void PF2_FS_OpenFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva if(!pr2_fopen_files[i].handle) { - retval->_int = -1; - return ; + return -1; } Con_DPrintf( "PF2_FS_OpenFile %s\n", name ); - retval->_int = VFS_GETLEN(pr2_fopen_files[i].handle); + ret = VFS_GETLEN(pr2_fopen_files[i].handle); break; case FS_WRITE_BIN: @@ -2070,28 +1718,23 @@ void PF2_FS_OpenFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva pr2_fopen_files[i].handle = FS_OpenVFS(name, cmodes[fmode], FS_GAME_OS); if ( !pr2_fopen_files[i].handle ) { - retval->_int = -1; - return ; + return -1; } Con_DPrintf( "PF2_FS_OpenFile %s\n", name ); - retval->_int = VFS_TELL(pr2_fopen_files[i].handle); + ret = VFS_TELL(pr2_fopen_files[i].handle); break; default: - retval->_int = -1; - return ; - + return -1; } *handle = i+1; pr2_num_open_files++; + return ret; } -/* -void trap_FS_CloseFile( fileHandle_t handle ); -*/ -void PF2_FS_CloseFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) + +void PF2_FS_CloseFile(fileHandle_t fnum) { - fileHandle_t fnum = stack[0]._int; fnum--; if (fnum < 0 || fnum >= MAX_PR2_FILES) return; //out of range @@ -2108,105 +1751,68 @@ void PF2_FS_CloseFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retv int seek_origin[]={SEEK_CUR,SEEK_END,SEEK_SET}; -/* -int trap_FS_SeekFile( fileHandle_t handle, int offset, int type ); -*/ - -void PF2_FS_SeekFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_FS_SeekFile(fileHandle_t fnum, intptr_t offset, fsOrigin_t type) { - fileHandle_t fnum = stack[0]._int; - int offset = stack[1]._int; - fsOrigin_t type = (fsOrigin_t) stack[2]._int; fnum--; if (fnum < 0 || fnum >= MAX_PR2_FILES) - return; //out of range + return 0;//out of range if(!pr2_num_open_files) - return; + return 0; if(!(pr2_fopen_files[fnum].handle)) - return; + return 0; if(type < 0 || type >= sizeof(seek_origin) / sizeof(seek_origin[0])) - return; + return 0; - retval->_int = VFS_SEEK(pr2_fopen_files[fnum].handle, offset, seek_origin[type]); + return VFS_SEEK(pr2_fopen_files[fnum].handle, offset, seek_origin[type]); } -/* -int trap_FS_TellFile( fileHandle_t handle ); -*/ - -void PF2_FS_TellFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_FS_TellFile(fileHandle_t fnum) { - fileHandle_t fnum = stack[0]._int; fnum--; if (fnum < 0 || fnum >= MAX_PR2_FILES) - return; //out of range + return 0;//out of range if(!pr2_num_open_files) - return; + return 0; if(!(pr2_fopen_files[fnum].handle)) - return; + return 0; - retval->_int = VFS_TELL(pr2_fopen_files[fnum].handle); + return VFS_TELL(pr2_fopen_files[fnum].handle); } -/* -int trap_FS_WriteFile( char*src, int quantity, fileHandle_t handle ); -*/ -void PF2_FS_WriteFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_FS_WriteFile(char *dest, intptr_t quantity, fileHandle_t fnum) { - char*dest; - intptr_t memoffset = stack[0]._int; - intptr_t quantity = stack[1]._int; - fileHandle_t fnum = stack[2]._int; fnum--; if (fnum < 0 || fnum >= MAX_PR2_FILES) - return; //out of range + return 0;//out of range if(!pr2_num_open_files) - return; + return 0; if(!(pr2_fopen_files[fnum].handle)) - return; - if( (memoffset) &(~mask)) - return; - - if( (memoffset+quantity) &(~mask)) - return; + return 0; - dest = (char*)VM_POINTER(base,mask,memoffset); - retval->_int = VFS_WRITE(pr2_fopen_files[fnum].handle, dest, quantity); + return VFS_WRITE(pr2_fopen_files[fnum].handle, dest, quantity); } -/* -int trap_FS_ReadFile( char*dest, int quantity, fileHandle_t handle ); -*/ -void PF2_FS_ReadFile(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) + +intptr_t PF2_FS_ReadFile(char *dest, intptr_t quantity, fileHandle_t fnum) { - char*dest; - intptr_t memoffset = stack[0]._int; - intptr_t quantity = stack[1]._int; - fileHandle_t fnum = stack[2]._int; fnum--; if (fnum < 0 || fnum >= MAX_PR2_FILES) - return; //out of range + return 0;//out of range if(!pr2_num_open_files) - return; + return 0; if(!(pr2_fopen_files[fnum].handle)) - return; - if( (memoffset) &(~mask)) - return; - - if( (memoffset+quantity) &(~mask)) - return; + return 0; - dest = (char*)VM_POINTER(base,mask,memoffset); - retval->_int = VFS_READ(pr2_fopen_files[fnum].handle, dest, quantity, NULL); + return VFS_READ(pr2_fopen_files[fnum].handle, dest, quantity, NULL); } void PR2_FS_Restart(void) @@ -2231,10 +1837,6 @@ void PR2_FS_Restart(void) memset(pr2_fopen_files,0,sizeof(pr2_fopen_files)); } -/* -int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); -*/ - static int GetFileList_Compare (const void *p1, const void *p2) { return strcmp (*((char**)p1), *((char**)p2)); @@ -2244,7 +1846,8 @@ static int GetFileList_Compare (const void *p1, const void *p2) #define FILELIST_WITH_PATH (1<<1) // include path to file #define FILELIST_WITH_EXT (1<<2) // include extension of file -void PF2_FS_GetFileList(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_FS_GetFileList(char *path, char *ext, + char *listbuff, intptr_t buffsize, intptr_t flags) { // extern searchpath_t *com_searchpaths; // evil, because this must be used in fs.c only... char *gpath = NULL; @@ -2257,39 +1860,21 @@ void PF2_FS_GetFileList(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*re // searchpath_t *search; char netpath[MAX_OSPATH], *fullname; - char *path, *ext, *listbuff, *dirptr; - - intptr_t pathoffset = stack[0]._int; - intptr_t extoffset = stack[1]._int; - intptr_t listbuffoffset = stack[2]._int; - intptr_t buffsize = stack[3]._int; - intptr_t flags = stack[4]._int; + char *dirptr; int numfiles = 0; int i, j; - retval->_int = 0; - - if( ( listbuffoffset ) & (~mask)) - return; - if( ( listbuffoffset + buffsize ) & (~mask)) - return; - if( ( extoffset ) & (~mask)) - return; - if( ( pathoffset ) & (~mask)) - return; + if (gamedata.APIversion < 15) + flags = 0; memset(list, 0, sizeof(list)); - path = (char*)VM_POINTER(base,mask,pathoffset); - ext = (char*)VM_POINTER(base,mask,extoffset);; - - listbuff = (char*)VM_POINTER(base,mask,listbuffoffset); dirptr = listbuff; *dirptr = 0; if (strstr( path, ".." ) || strstr( path, "::" )) - return; // do not allow relative paths + return 0; // do not allow relative paths // search through the path, one element at a time for (i = 0, gpath = NULL; i < list_cnt && ( gpath = FS_NextPath( gpath ) ); ) @@ -2360,11 +1945,10 @@ void PF2_FS_GetFileList(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*re numfiles++; } - retval->_int = numfiles; - // free allocated mem for (i = 0; i < list_cnt; i++) Q_free(list[i]); + return numfiles; } /* @@ -2374,68 +1958,21 @@ void PF2_FS_GetFileList(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*re -1 not found -2 cannot map */ -extern int pr2_numAPI; -void PF2_Map_Extension(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +intptr_t PF2_Map_Extension(char *name, int mapto) { - int mapto = stack[1]._int; - - if( mapto < pr2_numAPI) + if (mapto < _G__LASTAPI) { - retval->_int = -2; - return; + return -2; } - retval->_int = -1; -} -//////////////////// -// -// timewaster functions -// -//////////////////// -void PF2_strcmp(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int= strcmp( (char *) VM_POINTER(base,mask,stack[0].string), - (char *) VM_POINTER(base,mask,stack[1].string)); -} - -void PF2_strncmp(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int= strncmp( (char *) VM_POINTER(base,mask,stack[0].string), - (char *) VM_POINTER(base,mask,stack[1].string),stack[2]._int); -} - -void PF2_stricmp(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int= strcasecmp( (char *) VM_POINTER(base,mask,stack[0].string), - (char *) VM_POINTER(base,mask,stack[1].string)); -} - -void PF2_strnicmp(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - retval->_int= strncasecmp( (char *) VM_POINTER(base,mask,stack[0].string), - (char *) VM_POINTER(base,mask,stack[1].string),stack[2]._int); -} - -void PF2_strlcpy(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ // (char *dst, char *src, size_t siz) - retval->_int = strlcpy( (char *) VM_POINTER(base,mask,stack[0].string), (char *) VM_POINTER(base,mask,stack[1].string), stack[2]._int ); + return -1; } - -void PF2_strlcat(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ // (char *dst, char *src, size_t siz) - retval->_int = strlcat( (char *) VM_POINTER(base,mask,stack[0].string), (char *) VM_POINTER(base,mask,stack[1].string), stack[2]._int ); -} - /////////Bot Functions extern cvar_t maxclients, maxspectators; -void PF2_Add_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retval ) +int PF2_Add_Bot(char *name, int bottomcolor, int topcolor, char *skin) { client_t *cl, *newcl = NULL; - char *name = (char *) VM_POINTER( base, mask, stack[0].string ); - int bottomcolor = stack[1]._int; - int topcolor = stack[2]._int; - char *skin = (char *) VM_POINTER( base, mask, stack[3].string ); int edictnum; int clients, spectators, i; extern char *shortinfotbl[]; @@ -2468,8 +2005,7 @@ void PF2_Add_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retv if ( clients >= ( int ) maxclients.value ) { - retval->_int = 0; - return; + return 0; } for ( i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++ ) { @@ -2481,8 +2017,7 @@ void PF2_Add_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retv } if ( !newcl ) { - retval->_int = 0; - return; + return 0; } memset(newcl, 0, sizeof(*newcl)); @@ -2512,22 +2047,22 @@ void PF2_Add_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retv strlcpy(newcl->name, name, sizeof(newcl->name)); newcl->entgravity = 1.0; - val = PR2_GetEdictFieldValue( ent, "gravity" ); + val = PR2_GetEdictFieldValue( ent, "gravity" ); // FIXME: do it similar to maxspeed if ( val ) val->_float = 1.0; sv_client->maxspeed = sv_maxspeed.value; - val = PR2_GetEdictFieldValue( ent, "maxspeed" ); - if ( val ) - val->_float = sv_maxspeed.value; + + if (fofs_maxspeed) + EdictFieldFloat(ent, fofs_maxspeed) = sv_maxspeed.value; newcl->edict = ent; - ent->v.colormap = edictnum; - val = PR2_GetEdictFieldValue( ent, "isBot" ); + ent->v->colormap = edictnum; + val = PR2_GetEdictFieldValue(ent, "isBot"); // FIXME: do it similar to maxspeed if( val ) val->_int = 1; // restore client name. - PR_SetEntityString(ent, ent->v.netname, newcl->name); + PR_SetEntityString(ent, ent->v->netname, newcl->name); memset( newcl->stats, 0, sizeof( newcl->stats ) ); SZ_InitEx (&newcl->netchan.message, newcl->netchan.message_buf, (int)sizeof(newcl->netchan.message_buf), true); @@ -2552,10 +2087,7 @@ void PF2_Add_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retv newcl->disable_updates_stop = -1.0; // Vladis - SV_FullClientUpdate( newcl, &sv.reliable_datagram ); - retval->_int = edictnum; - old_self = pr_global_struct->self; pr_global_struct->time = sv.time; @@ -2565,7 +2097,7 @@ void PF2_Add_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retv PR2_GamePutClientInServer(0); pr_global_struct->self = old_self; - + return edictnum; } void RemoveBot(client_t *cl) @@ -2579,7 +2111,7 @@ void RemoveBot(client_t *cl) PR2_GameClientDisconnect(0); cl->old_frags = 0; - cl->edict->v.frags = 0.0; + cl->edict->v->frags = 0.0; cl->name[0] = 0; cl->state = cs_free; @@ -2590,13 +2122,11 @@ void RemoveBot(client_t *cl) cl->isBot = 0; } -void PF2_Remove_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retval ) +void PF2_Remove_Bot(int entnum) { client_t *cl; int old_self; - int entnum = stack[0]._int; - if ( entnum < 1 || entnum > MAX_CLIENTS ) { Con_Printf( "tried to remove a non-botclient %d \n", entnum ); @@ -2616,16 +2146,16 @@ void PF2_Remove_Bot( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * r } -void PF2_SetBotUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retval ) +// FIXME: Why PR2_UserInfoChanged is not called here? Like for normal players. +// Why we need this special handling in the first place? +void PF2_SetBotUserInfo(int entnum, char *key, char *value, int flags) { client_t *cl; - int entnum = stack[0]._int; - char *key = (char *) VM_POINTER( base, mask, stack[1].string ); - char *value = (char *) VM_POINTER( base, mask, stack[2].string ); - int flags = stack[3]._int; int i; extern char *shortinfotbl[]; + if (gamedata.APIversion < 15) + flags = 0; if (strstr(key, "&c") || strstr(key, "&r") || strstr(value, "&c") || strstr(value, "&r")) return; @@ -2665,10 +2195,10 @@ void PF2_SetBotUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t } } -void PF2_SetBotCMD( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retval ) +void PF2_SetBotCMD(int entnum, int msec, float a1, float a2, float a3, + int forwardmove, int sidemove, int upmove, int buttons, int impulse) { client_t *cl; - int entnum = stack[0]._int; if ( entnum < 1 || entnum > MAX_CLIENTS ) { @@ -2681,20 +2211,21 @@ void PF2_SetBotCMD( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * re Con_Printf( "tried to set cmd a non-botclient %d \n", entnum ); return; } - cl->botcmd.msec = stack[1]._int; - cl->botcmd.angles[0] = stack[2]._float; - cl->botcmd.angles[1] = stack[3]._float; - cl->botcmd.angles[2] = stack[4]._float; - cl->botcmd.forwardmove = stack[5]._int; - cl->botcmd.sidemove = stack[6]._int; - cl->botcmd.upmove = stack[7]._int; - cl->botcmd.buttons = stack[8]._int; - cl->botcmd.impulse = stack[9]._int; - if ( cl->edict->v.fixangle) - { - VectorCopy(cl->edict->v.angles, cl->botcmd.angles); + cl->botcmd.msec = msec; + cl->botcmd.angles[0] = a1; + cl->botcmd.angles[1] = a2; + cl->botcmd.angles[2] = a3; + cl->botcmd.forwardmove = forwardmove; + cl->botcmd.sidemove = sidemove; + cl->botcmd.upmove = upmove; + cl->botcmd.buttons = buttons; + cl->botcmd.impulse = impulse; + + if (cl->edict->v->fixangle) + { + VectorCopy(cl->edict->v->angles, cl->botcmd.angles); cl->botcmd.angles[PITCH] *= -3; - cl->edict->v.fixangle = 0; + cl->edict->v->fixangle = 0; } } @@ -2707,22 +2238,15 @@ void PF2_SetBotCMD( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * re PF2_QVMstrftime ============== */ -void PF2_QVMstrftime(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -//(char *valbuff, int sizebuff, char *fmt, int offset) +int PF2_QVMstrftime(char *valbuff, int sizebuff, char *fmt, int offset) { - char *valbuff = (char *) VM_POINTER(base,mask,stack[0].string); - int sizebuff = stack[1]._int; - char *fmt = (char *) VM_POINTER(base,mask,stack[2].string); - int offset = stack[3]._int; - struct tm *newtime; time_t long_time; - - retval->_int = 0; + int ret; if (sizebuff <= 0 || !valbuff) { Con_DPrintf("PF2_QVMstrftime: wrong buffer\n"); - return; + return 0; } time(&long_time); @@ -2732,46 +2256,31 @@ void PF2_QVMstrftime(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retva if (!newtime) { valbuff[0] = 0; // or may be better set to "#bad date#" ? - return; + return 0; } - retval->_int = strftime(valbuff, sizebuff-1, fmt, newtime); + ret = strftime(valbuff, sizebuff-1, fmt, newtime); - if (!retval->_int) { + if (!ret) { valbuff[0] = 0; // or may be better set to "#bad date#" ? Con_DPrintf("PF2_QVMstrftime: buffer size too small\n"); - return; + return 0; } -} - -void PF2_makevectors(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) -{ - AngleVectors ((float *) VM_POINTER(base,mask,stack[0].string), - pr_global_struct->v_forward, - pr_global_struct->v_right, - pr_global_struct->v_up); + return ret; } // a la the ZQ_PAUSE QC extension -void PF2_setpause(byte* base, uintptr_t mask, pr2val_t* stack, pr2val_t*retval) +void PF2_setpause(int pause) { - int pause; - - pause = stack[0]._int ? 1 : 0; - if (pause != (sv.paused & 1)) SV_TogglePause (NULL, 1); } -void PF2_SetUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * retval ) +void PF2_SetUserInfo(int entnum, char *k, char *v, int flags) { client_t *cl; - int entnum = stack[0]._int; - char *k = (char *) VM_POINTER( base, mask, stack[1].string ); - char *v = (char *) VM_POINTER( base, mask, stack[2].string ); char key[MAX_KEY_STRING]; char value[MAX_KEY_STRING]; - int flags = stack[3]._int; char s[MAX_KEY_STRING * 4]; int i; extern char *shortinfotbl[]; @@ -2790,7 +2299,7 @@ void PF2_SetUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * // well, our API is weird if ( cl->isBot ) { - PF2_SetBotUserInfo( base, mask, stack, retval ); + PF2_SetBotUserInfo(entnum, k, v, flags); return; } @@ -2809,7 +2318,7 @@ void PF2_SetUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(cl->edict); - if( PR2_UserInfoChanged() ) + if (PR2_UserInfoChanged(0)) return; } @@ -2819,6 +2328,7 @@ void PF2_SetUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * Info_Set( &cl->_userinfo_ctx_, key, value ); SV_ExtractFromUserinfo( cl, !strcmp( key, "name" ) ); + PR2_UserInfoChanged(1); for ( i = 0; shortinfotbl[i] != NULL; i++ ) { @@ -2840,206 +2350,338 @@ void PF2_SetUserInfo( byte * base, uintptr_t mask, pr2val_t * stack, pr2val_t * } } -//=========================================================================== -// SysCalls -//=========================================================================== - -pr2_trapcall_t pr2_API[]= - { - PF2_GetApiVersion, //G_GETAPIVERSION - PF2_DPrint, //G_DPRINT - PF2_Error, //G_ERROR - PF2_GetEntityToken, //G_GetEntityToken, - PF2_Spawn, //G_SPAWN_ENT, - PF2_Remove, //G_REMOVE_ENT, - PF2_precache_sound, //G_PRECACHE_SOUND, - PF2_precache_model, //G_PRECACHE_MODEL, - PF2_lightstyle, //G_LIGHTSTYLE, - PF2_setorigin, //G_SETORIGIN, - PF2_setsize, //G_SETSIZE, - PF2_setmodel, //G_SETMODEL, - PF2_bprint, //G_BPRINT, - PF2_sprint, //G_SPRINT, - PF2_centerprint, //G_CENTERPRINT, - PF2_ambientsound, //G_AMBIENTSOUND, - PF2_sound, //G_SOUND, - PF2_traceline, //G_TRACELINE, - PF2_checkclient, //G_CHECKCLIENT, - PF2_stuffcmd, //G_STUFFCMD, - PF2_localcmd, //G_LOCALCMD, - PF2_cvar, //G_CVAR, - PF2_cvar_set, //G_CVAR_SET, - PF2_FindRadius, //G_FINDRADIUS - PF2_walkmove, - PF2_droptofloor, //G_DROPTOFLOOR, - PF2_checkbottom, //G_CHECKBOTTOM, - PF2_pointcontents, //G_POINTCONTENTS, - PF2_nextent, //G_NEXTENT, - PF2_fixme, //G_AIM, - PF2_makestatic, //G_MAKESTATIC, - PF2_setspawnparms, //G_SETSPAWNPARAMS, - PF2_changelevel, //G_CHANGELEVEL, - PF2_logfrag, //G_LOGFRAG, - PF2_infokey, //G_GETINFOKEY, - PF2_multicast, //G_MULTICAST, - PF2_disable_updates, //G_DISABLEUPDATES, - PF2_WriteByte, //G_WRITEBYTE, - PF2_WriteChar, //G_WRITECHAR, - PF2_WriteShort, //G_WRITESHORT, - PF2_WriteLong, //G_WRITELONG, - PF2_WriteAngle, //G_WRITEANGLE, - PF2_WriteCoord, //G_WRITECOORD, - PF2_WriteString, //G_WRITESTRING, - PF2_WriteEntity, //G_WRITEENTITY, - PR2_FlushSignon, //G_FLUSHSIGNON, - PF2_memset, //g_memset, - PF2_memcpy, //g_memcpy, - PF2_strncpy, //g_strncpy, - PF2_sin, //g_sin, - PF2_cos, //g_cos, - PF2_atan2, //g_atan2, - PF2_sqrt, //g_sqrt, - PF2_floor, //g_floor, - PF2_ceil, //g_ceil, - PF2_acos, //g_acos, - PF2_cmdargc, //G_CMD_ARGC, - PF2_cmdargv, //G_CMD_ARGV - PF2_TraceCapsule, - PF2_FS_OpenFile, - PF2_FS_CloseFile, - PF2_FS_ReadFile, - PF2_FS_WriteFile, - PF2_FS_SeekFile, - PF2_FS_TellFile, - PF2_FS_GetFileList, - PF2_cvar_set_float, - PF2_cvar_string, - PF2_Map_Extension, - PF2_strcmp, - PF2_strncmp, - PF2_stricmp, - PF2_strnicmp, - PF2_Find, - PF2_executecmd, - PF2_conprint, - PF2_readcmd, - PF2_redirectcmd, - PF2_Add_Bot, - PF2_Remove_Bot, - PF2_SetBotUserInfo, - PF2_SetBotCMD, - PF2_QVMstrftime, //G_QVMstrftime - PF2_cmdargs, //G_CMD_ARGS - PF2_tokenize, //G_CMD_TOKENIZE - PF2_strlcpy, //g_strlcpy - PF2_strlcat, //g_strlcat - PF2_makevectors, //G_MAKEVECTORS - PF2_nextclient, //G_NEXTCLIENT - PF2_precache_vwep_model,//G_PRECACHE_VWEP_MODEL - PF2_setpause, //G_SETPAUSE - PF2_SetUserInfo, //G_SETUSERINFO - PF2_MoveToGoal, //G_MOVETOGOAL - }; -int pr2_numAPI = sizeof(pr2_API)/sizeof(pr2_API[0]); - -intptr_t sv_syscall(intptr_t arg, ...) //must passed ints -{ - intptr_t args[20]; - va_list argptr; - pr2val_t ret; - - if( arg >= pr2_numAPI ) - PR2_RunError ("sv_syscall: Bad API call number"); - - va_start(argptr, arg); - args[0] =va_arg(argptr, intptr_t); - args[1] =va_arg(argptr, intptr_t); - args[2] =va_arg(argptr, intptr_t); - args[3] =va_arg(argptr, intptr_t); - args[4] =va_arg(argptr, intptr_t); - args[5] =va_arg(argptr, intptr_t); - args[6] =va_arg(argptr, intptr_t); - args[7] =va_arg(argptr, intptr_t); - args[8] =va_arg(argptr, intptr_t); - args[9] =va_arg(argptr, intptr_t); - args[10]=va_arg(argptr, intptr_t); - args[11]=va_arg(argptr, intptr_t); - args[12]=va_arg(argptr, intptr_t); - args[13]=va_arg(argptr, intptr_t); - args[14]=va_arg(argptr, intptr_t); - args[15]=va_arg(argptr, intptr_t); - args[16]=va_arg(argptr, intptr_t); - args[17]=va_arg(argptr, intptr_t); - args[18]=va_arg(argptr, intptr_t); - args[19]=va_arg(argptr, intptr_t); - va_end(argptr); - - pr2_API[arg] ( 0, (uintptr_t)~0, (pr2val_t*)args, &ret); - - return ret._int; -} - -int sv_sys_callex(byte *data, unsigned int mask, int fn, pr2val_t*arg) +void PF2_VisibleTo(int viewer, int first, int len, byte *visible) { - pr2val_t ret; - - if( fn >= pr2_numAPI ) - PR2_RunError ("sv_sys_callex: Bad API call number"); - - pr2_API[fn](data, mask, arg,&ret); - return ret._int; -} - -extern gameData_t *gamedata; -extern field_t *fields; - -#define GAME_API_VERSION_MIN 8 - -void PR2_InitProg(void) -{ - extern cvar_t sv_pr2references; - - Cvar_SetValue(&sv_pr2references, 0.0f); - - if ( !sv_vm ) { - PR1_InitProg(); - return; - } + int e, last = first + len; + edict_t *ent; + edict_t *viewer_ent = EDICT_NUM(viewer); + vec3_t org; + byte *pvs; - PR2_FS_Restart(); + if (last > sv.num_edicts) + last = sv.num_edicts; - gamedata = (gameData_t *) VM_Call(sv_vm, GAME_INIT, (int) (sv.time * 1000), - (int) (Sys_DoubleTime() * 100000), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - if (!gamedata) { - SV_Error("PR2_InitProg gamedata == NULL"); - } + VectorAdd(viewer_ent->v->origin, viewer_ent->v->view_ofs, org); + pvs = CM_FatPVS(org); - gamedata = (gameData_t *)PR2_GetString((intptr_t)gamedata); - if (gamedata->APIversion < GAME_API_VERSION_MIN || gamedata->APIversion > GAME_API_VERSION) { - if (GAME_API_VERSION_MIN == GAME_API_VERSION) { - SV_Error("PR2_InitProg: Incorrect API version (%i should be %i)", gamedata->APIversion, GAME_API_VERSION); + for (e = first, ent = EDICT_NUM(e); e < last; e++, ent = NEXT_EDICT(ent)) + { + int i; + if (ent->e.num_leafs < 0 || ent->e.free + || (e >= 1 && e <= MAX_CLIENTS && svs.clients[e - 1].state != cs_spawned)) { + continue; // Ignore free edicts or not active client. } - else { - SV_Error("PR2_InitProg: Incorrect API version (%i should be between %i and %i)", gamedata->APIversion, GAME_API_VERSION_MIN, GAME_API_VERSION); + for (i = 0; i < ent->e.num_leafs; i++) { + if (pvs[ent->e.leafnums[i] >> 3] & (1 << (ent->e.leafnums[i]&7))) { + visible[e - first] = true; // seems to be visible + break; + } } } +} - sv_vm->pr2_references = gamedata->APIversion >= 15 && (int)sv_pr2references.value; - - sv.edicts = (edict_t *)PR2_GetString((intptr_t)gamedata->ents); - pr_global_struct = (globalvars_t*)PR2_GetString((intptr_t)gamedata->global); - pr_globals = (float *) pr_global_struct; - fields = (field_t*)PR2_GetString((intptr_t)gamedata->fields); - pr_edict_size = gamedata->sizeofent; +//=========================================================================== +// SysCalls +//=========================================================================== - sv.max_edicts = MAX_EDICTS; - if (gamedata->APIversion >= 14) { - sv.max_edicts = min(sv.max_edicts, gamedata->maxentities); - } - else { - sv.max_edicts = min(sv.max_edicts, 512); +#define VMV(x) _vmf(args[x]), _vmf(args[(x) + 1]), _vmf(args[(x) + 2]) +#define VME(x) EDICT_NUM(args[x]) +intptr_t PR2_GameSystemCalls(intptr_t *args) { + switch (args[0]) { + case G_GETAPIVERSION: + return GAME_API_VERSION; + case G_DPRINT: + Con_DPrintf("%s", (const char *)VMA(1)); + return 0; + case G_ERROR: + PR2_RunError(VMA(1)); + return 0; + case G_GetEntityToken: + VM_CheckBounds(sv_vm, args[1], args[2]); + pr2_ent_data_ptr = COM_Parse(pr2_ent_data_ptr); + strlcpy(VMA(1), com_token, args[2]); + return pr2_ent_data_ptr != NULL; + case G_SPAWN_ENT: + return NUM_FOR_EDICT(ED_Alloc()); + case G_REMOVE_ENT: + ED_Free(VME(1)); + return 0; + case G_PRECACHE_SOUND: + PF2_precache_sound(VMA(1)); + return 0; + case G_PRECACHE_MODEL: + PF2_precache_model(VMA(1)); + return 0; + case G_LIGHTSTYLE: + PF2_lightstyle(args[1], VMA(2)); + return 0; + case G_SETORIGIN: + PF2_setorigin(VME(1), VMV(2)); + return 0; + case G_SETSIZE: + PF2_setsize(VME(1), VMV(2), VMV(5)); + return 0; + case G_SETMODEL: + PF2_setmodel(VME(1), VMA(2)); + return 0; + case G_BPRINT: { + int flags = args[3]; + if (gamedata.APIversion < 15) + flags = 0; + SV_BroadcastPrintfEx(args[1], flags, "%s", VMA(2)); + } + return 0; + case G_SPRINT: + PF2_sprint(args[1], args[2], VMA(3), args[4]); + return 0; + case G_CENTERPRINT: + PF2_centerprint(args[1], VMA(2)); + return 0; + case G_AMBIENTSOUND: + PF2_ambientsound(VMV(1), VMA(4), VMF(5), VMF(6)); + return 0; + case G_SOUND: + /* + ================= + PF2_sound + + Each entity can have eight independant sound sources, like voice, + weapon, feet, etc. + + Channel 0 is an auto-allocate channel, the others override anything + already running on that entity/channel pair. + + An attenuation of 0 will play full volume everywhere in the level. + Larger attenuations will drop off. + void sound( gedict_t * ed, int channel, char *samp, float vol, float att ) + ================= + */ + SV_StartSound(VME(1), args[2], VMA(3), VMF(4) * 255, VMF(5)); + return 0; + case G_TRACELINE: + PF2_traceline(VMV(1), VMV(4), args[7], args[8]); + return 0; + case G_CHECKCLIENT: + return PF2_checkclient(); + case G_STUFFCMD: + PF2_stuffcmd(args[1], VMA(2), args[3]); + return 0; + case G_LOCALCMD: + /* ================= + Sends text over to the server's execution buffer + + localcmd (string) + ================= */ + Cbuf_AddText(VMA(1)); + return 0; + case G_CVAR: + return PASSFLOAT(Cvar_Value(VMA(1))); + case G_CVAR_SET: + Cvar_SetByName(VMA(1), VMA(2)); + return 0; + case G_FINDRADIUS: + return PF2_FindRadius(NUM_FOR_GAME_EDICT(VMA(1)), (float *)VMA(2), VMF(3)); + case G_WALKMOVE: + return PF2_walkmove(VME(1), VMF(2), VMF(3)); + case G_DROPTOFLOOR: + return PF2_droptofloor(VME(1)); + case G_CHECKBOTTOM: + return SV_CheckBottom(VME(1)); + case G_POINTCONTENTS: + return PF2_pointcontents(VMV(1)); + case G_NEXTENT: + return PF2_nextent(args[1]); + case G_AIM: + return 0; + case G_MAKESTATIC: + PF2_makestatic(VME(1)); + return 0; + case G_SETSPAWNPARAMS: + PF2_setspawnparms(args[1]); + return 0; + case G_CHANGELEVEL: + PF2_changelevel(VMA(1), VMA(2)); + return 0; + case G_LOGFRAG: + PF2_logfrag(args[1], args[2]); + return 0; + case G_GETINFOKEY: + VM_CheckBounds(sv_vm, args[3], args[4]); + PF2_infokey(args[1], VMA(2), VMA(3), args[4]); + return 0; + case G_MULTICAST: + PF2_multicast(VMV(1), args[4]); + return 0; + case G_DISABLEUPDATES: + PF2_disable_updates(args[1], VMF(2)); + return 0; + case G_WRITEBYTE: + PF2_WriteByte(args[1], args[2]); + return 0; + case G_WRITECHAR: + PF2_WriteChar(args[1], args[2]); + return 0; + case G_WRITESHORT: + PF2_WriteShort(args[1], args[2]); + return 0; + case G_WRITELONG: + PF2_WriteLong(args[1], args[2]); + return 0; + case G_WRITEANGLE: + PF2_WriteAngle(args[1], VMF(2)); + return 0; + case G_WRITECOORD: + PF2_WriteCoord(args[1], VMF(2)); + return 0; + case G_WRITESTRING: + PF2_WriteString(args[1], VMA(2)); + return 0; + case G_WRITEENTITY: + PF2_WriteEntity(args[1], args[2]); + return 0; + case G_FLUSHSIGNON: + SV_FlushSignon(); + return 0; + case g_memset: + VM_CheckBounds(sv_vm, args[1], args[3]); + memset(VMA(1), args[2], args[3]); + return args[1]; + case g_memcpy: + VM_CheckBounds2(sv_vm, args[1], args[2], args[3]); + memcpy(VMA(1), VMA(2), args[3]); + return args[1]; + case g_strncpy: + VM_CheckBounds2(sv_vm, args[1], args[2], args[3]); + strncpy(VMA(1), VMA(2), args[3]); + return args[1]; + case g_sin: + return PASSFLOAT(sin(VMF(1))); + case g_cos: + return PASSFLOAT(cos(VMF(1))); + case g_atan2: + return PASSFLOAT(atan2(VMF(1), VMF(2))); + case g_sqrt: + return PASSFLOAT(sqrt(VMF(1))); + case g_floor: + return PASSFLOAT(floor(VMF(1))); + case g_ceil: + return PASSFLOAT(ceil(VMF(1))); + case g_acos: + return PASSFLOAT(acos(VMF(1))); + case G_CMD_ARGC: + return Cmd_Argc(); + case G_CMD_ARGV: + VM_CheckBounds(sv_vm, args[2], args[3]); + strlcpy(VMA(2), Cmd_Argv(args[1]), args[3]); + return 0; + case G_TraceCapsule: + PF2_TraceCapsule(VMV(1), VMV(4), args[7], VME(8), VMV(9), VMV(12)); + return 0; + case G_FSOpenFile: + return PF2_FS_OpenFile(VMA(1), (fileHandle_t *)VMA(2), (fsMode_t)args[3]); + case G_FSCloseFile: + PF2_FS_CloseFile((fileHandle_t)args[1]); + return 0; + case G_FSReadFile: + VM_CheckBounds(sv_vm, args[1], args[2]); + return PF2_FS_ReadFile(VMA(1), args[2], (fileHandle_t)args[3]); + case G_FSWriteFile: + VM_CheckBounds(sv_vm, args[1], args[2]); + return PF2_FS_WriteFile(VMA(1), args[2], (fileHandle_t)args[3]); + case G_FSSeekFile: + return PF2_FS_SeekFile((fileHandle_t)args[1], args[2], (fsOrigin_t)args[3]); + case G_FSTellFile: + return PF2_FS_TellFile((fileHandle_t)args[1]); + case G_FSGetFileList: + VM_CheckBounds(sv_vm, args[3], args[4]); + return PF2_FS_GetFileList(VMA(1), VMA(2), VMA(3), args[4], args[5]); + case G_CVAR_SET_FLOAT: + Cvar_SetValueByName(VMA(1), VMF(2)); + return 0; + case G_CVAR_STRING: + VM_CheckBounds(sv_vm, args[2], args[3]); + strlcpy(VMA(2), Cvar_String(VMA(1)), args[3]); + return 0; + case G_Map_Extension: + return PF2_Map_Extension(VMA(1), args[2]); + case G_strcmp: + return strcmp(VMA(1), VMA(2)); + case G_strncmp: + return strncmp(VMA(1), VMA(2), args[3]); + case G_stricmp: + return strcasecmp(VMA(1), VMA(2)); + case G_strnicmp: + return strncasecmp(VMA(1), VMA(2), args[3]); + case G_Find: + return PF2_Find(NUM_FOR_GAME_EDICT(VMA(1)), args[2], VMA(3)); + case G_executecmd: + PF2_executecmd(); + return 0; + case G_conprint: + Sys_Printf("%s", VMA(1)); + return 0; + case G_readcmd: + VM_CheckBounds(sv_vm, args[2], args[3]); + PF2_readcmd(VMA(1), VMA(2), args[3]); + return 0; + case G_redirectcmd: + PF2_redirectcmd(NUM_FOR_GAME_EDICT(VMA(1)), VMA(2)); + return 0; + case G_Add_Bot: + return PF2_Add_Bot(VMA(1), args[2], args[3], VMA(4)); + case G_Remove_Bot: + PF2_Remove_Bot(args[1]); + return 0; + case G_SetBotUserInfo: + PF2_SetBotUserInfo(args[1], VMA(2), VMA(3), args[4]); + return 0; + case G_SetBotCMD: + PF2_SetBotCMD(args[1], args[2], VMV(3), args[6], args[7], args[8], args[9], args[10]); + return 0; + case G_QVMstrftime: + VM_CheckBounds(sv_vm, args[1], args[2]); + return PF2_QVMstrftime(VMA(1), args[2], VMA(3), args[4]); + case G_CMD_ARGS: + VM_CheckBounds(sv_vm, args[1], args[2]); + strlcpy(VMA(1), Cmd_Args(), args[2]); + return 0; + case G_CMD_TOKENIZE: + Cmd_TokenizeString(VMA(1)); + return 0; + case g_strlcpy: + VM_CheckBounds(sv_vm, args[1], args[3]); + return strlcpy(VMA(1), VMA(2), args[3]); + case g_strlcat: + VM_CheckBounds(sv_vm, args[1], args[3]); + return strlcat(VMA(1), VMA(2), args[3]); + case G_MAKEVECTORS: + AngleVectors(VMA(1), pr_global_struct->v_forward, pr_global_struct->v_right, + pr_global_struct->v_up); + return 0; + case G_NEXTCLIENT: + return PF2_nextclient(NUM_FOR_GAME_EDICT(VMA(1))); + case G_PRECACHE_VWEP_MODEL: + return PF2_precache_vwep_model(VMA(1)); + case G_SETPAUSE: + PF2_setpause(args[1]); + return 0; + case G_SETUSERINFO: + PF2_SetUserInfo(args[1], VMA(2), VMA(3), args[4]); + return 0; + case G_MOVETOGOAL: + PF2_MoveToGoal(VMF(1)); + return 0; + case G_VISIBLETO: + VM_CheckBounds(sv_vm, args[4], args[3]); + memset(VMA(4), 0, args[3]); // Ensure same memory state on each run. + PF2_VisibleTo(args[1], args[2], args[3], VMA(4)); + return 0; + default: + SV_Error("Bad game system trap: %ld", (long int)args[0]); } + return 0; } + #endif /* USE_PR2 */ #endif // !CLIENTONLY diff --git a/src/pr2_edict.c b/src/pr2_edict.c index 519373852..f40fc8cdc 100644 --- a/src/pr2_edict.c +++ b/src/pr2_edict.c @@ -37,7 +37,7 @@ eval_t *PR2_GetEdictFieldValue(edict_t *ed, char *field) for (f = fields; (s = PR2_GetString(f->name)) && *s; f++) if (!strcasecmp(PR2_GetString(f->name), field)) - return (eval_t *)((char *) ed + f->ofs); + return (eval_t *)((char *)ed->v + f->ofs); return NULL; } @@ -52,7 +52,7 @@ int ED2_FindFieldOffset (char *field) for (f = fields; (s = PR2_GetString(f->name)) && *s; f++) if (!strcasecmp(PR2_GetString(f->name), field)) - return f->ofs - ((int)(uintptr_t)&(((edict_t *)0)->v)); + return f->ofs; return 0; } diff --git a/src/pr2_exec.c b/src/pr2_exec.c index 6a2a7b8df..fa0ea14cb 100644 --- a/src/pr2_exec.c +++ b/src/pr2_exec.c @@ -24,27 +24,23 @@ #ifdef USE_PR2 #include "qwsvdef.h" +#include "vm_local.h" -qbool PR2_IsValidWriteAddress(register qvm_t * qvm, intptr_t address); -qbool PR2_IsValidReadAddress(register qvm_t * qvm, intptr_t address); +gameData_t gamedata; +extern field_t *fields; -gameData_t *gamedata; - -// 0 = pr1 (qwprogs.dat etc), 1 = native (.so/.dll), 2 = q3vm (.qvm) +// 0 = pr1 (qwprogs.dat etc), 1 = native (.so/.dll), 2 = q3vm (.qvm), 3 = q3vm (.qvm) with JIT cvar_t sv_progtype = { "sv_progtype","0" }; // 0 = standard, 1 = pr2 mods set string_t fields as byte offsets to location of actual strings cvar_t sv_pr2references = {"sv_pr2references", "0"}; -#ifdef QVM_PROFILE -extern cvar_t sv_enableprofile; -#endif -//int usedll; - void ED2_PrintEdicts (void); void PR2_Profile_f (void); void ED2_PrintEdict_f (void); void ED_Count (void); +void VM_VmInfo_f( void ); + void PR2_Init(void) { int p; @@ -52,12 +48,10 @@ void PR2_Init(void) Cvar_Register(&sv_progtype); Cvar_Register(&sv_progsname); Cvar_Register(&sv_pr2references); + Cvar_Register(&vm_rtChecks); #ifdef WITH_NQPROGS Cvar_Register(&sv_forcenqprogs); #endif -#ifdef QVM_PROFILE - Cvar_Register(&sv_enableprofile); -#endif p = SV_CommandLineProgTypeArgument(); @@ -65,8 +59,8 @@ void PR2_Init(void) { usedll = Q_atoi(COM_Argv(p + 1)); - if (usedll > 2) - usedll = VM_NONE; + if (usedll > VMI_COMPILED || usedll < VMI_NONE) + usedll = VMI_NONE; Cvar_SetValue(&sv_progtype,usedll); } @@ -76,25 +70,33 @@ void PR2_Init(void) Cmd_AddCommand ("profile", PR2_Profile_f); Cmd_AddCommand ("mod", PR2_GameConsoleCommand); + Cmd_AddCommand ("vminfo", VM_VmInfo_f); memset(pr_newstrtbl, 0, sizeof(pr_newstrtbl)); } +void PR2_Profile_f(void) +{ + if(!sv_vm) + { + PR_Profile_f(); + return; + } +} + //=========================================================================== // PR2_GetString: only called to get direct addresses now //=========================================================================== char *PR2_GetString(intptr_t num) { - qvm_t *qvm; - if(!sv_vm) return PR1_GetString(num); switch (sv_vm->type) { - case VM_NONE: + case VMI_NONE: return PR1_GetString(num); - case VM_NATIVE: + case VMI_NATIVE: if (num) { return (char *)num; } @@ -102,16 +104,11 @@ char *PR2_GetString(intptr_t num) return ""; } - case VM_BYTECODE: - if (!num) { + case VMI_BYTECODE: + case VMI_COMPILED: + if (num <= 0) return ""; - } - qvm = (qvm_t*)(sv_vm->hInst); - if (! PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + num)) { - Con_DPrintf("PR2_GetString error off %8x/%8x\n", num, qvm->len_ds ); - return ""; - } - return (char *) (qvm->ds + num); + return VM_ExplicitArgPtr(sv_vm, num); } return NULL; @@ -120,7 +117,7 @@ char *PR2_GetString(intptr_t num) intptr_t PR2_EntityStringLocation(string_t offset, int max_size) { if (offset > 0 && offset < pr_edict_size * sv.max_edicts - max_size) { - return ((intptr_t)sv.edicts + offset); + return ((intptr_t)sv.game_edicts + offset); } return 0; @@ -138,46 +135,39 @@ intptr_t PR2_GlobalStringLocation(string_t offset) char *PR2_GetEntityString(string_t num) { - qvm_t *qvm; if(!sv_vm) return PR1_GetString(num); switch (sv_vm->type) { - case VM_NONE: + case VMI_NONE: return PR1_GetString(num); - case VM_NATIVE: + case VMI_NATIVE: if (num) { - char** location = (char**)PR2_EntityStringLocation(num, sizeof(char*)); - - if (location && *location) { - return *location; + if (sv_vm->pr2_references) { + char** location = (char**)PR2_EntityStringLocation(num, sizeof(char*)); + if (location && *location) { + return *location; + } + } +#ifndef idx64 + else { + return (char *) (num); } +#endif } return ""; - case VM_BYTECODE: - if (!num) + case VMI_BYTECODE: + case VMI_COMPILED: + if (num <= 0) return ""; - qvm = (qvm_t*)(sv_vm->hInst); if (sv_vm->pr2_references) { num = *(string_t*)PR2_EntityStringLocation(num, sizeof(string_t)); - - if (!PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + num)) { - Con_DPrintf("PR2_GetEntityString error off %8x/%8x\n", num, qvm->len_ds); - return ""; - } - - if (num) { - return (char *) (qvm->ds+ num); - } - } - else if (PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + num)) { - return (char *) (qvm->ds+ num); } - return ""; + return VM_ExplicitArgPtr(sv_vm, num); } return NULL; @@ -185,93 +175,23 @@ char *PR2_GetEntityString(string_t num) //=========================================================================== // PR2_SetString -// FIXME for VM +// !!!!IMPOTANT!!!! +// Server change string pointers in mod memory only in trapcall(strings passed from mod, and placed in mod memory). +// Never pass pointers outside of the mod memory to mod, this does not work in QVM in 64 bit server. //=========================================================================== void PR2_SetEntityString(edict_t* ed, string_t* target, char* s) { - qvm_t *qvm; - intptr_t off; if (!sv_vm) { PR1_SetString(target, s); return; } - - switch (sv_vm->type) - { - case VM_NONE: - PR1_SetString(target, s); - return; - - case VM_NATIVE: - { - char** location = (char**)PR2_EntityStringLocation(*target, sizeof(char*)); - - if (location) { - *location = s; - } - } - return; - - case VM_BYTECODE: - qvm = (qvm_t*)(sv_vm->hInst); - off = (byte*)s - qvm->ds; - - if (sv_vm->pr2_references) { - string_t* location = (string_t*)PR2_EntityStringLocation(*target, sizeof(string_t)); - - if (location && PR2_IsValidWriteAddress(qvm, (intptr_t)location)) { - *location = off; - } - } - else if (PR2_IsValidWriteAddress(qvm, (intptr_t)target)) { - *target = off; - } - return; - } - - *target = 0; } - void PR2_SetGlobalString(string_t* target, char* s) { - qvm_t *qvm; - intptr_t off; if (!sv_vm) { PR1_SetString(target, s); return; } - - switch (sv_vm->type) - { - case VM_NONE: - PR1_SetString(target, s); - return; - - case VM_NATIVE: - { - char** location = (char**)PR2_GlobalStringLocation(*target); - if (location) { - *location = s; - } - } - return; - - case VM_BYTECODE: - qvm = (qvm_t*)(sv_vm->hInst); - off = (byte*)s - qvm->ds; - if (sv_vm->pr2_references) { - string_t* location = (string_t*)PR2_GlobalStringLocation(*target); - if (location && PR2_IsValidWriteAddress(qvm, (intptr_t)location)) { - *location = off; - } - } - else if (PR2_IsValidWriteAddress(qvm, (intptr_t)target)) { - *target = off; - } - return; - } - - *target = 0; } /* @@ -288,7 +208,7 @@ void PR2_LoadEnts(char *data) pr2_ent_data_ptr = data; //Init parse - VM_Call(sv_vm, GAME_LOADENTS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_LOADENTS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } else { @@ -301,12 +221,12 @@ void PR2_LoadEnts(char *data) //=========================================================================== void PR2_GameStartFrame(qbool isBotFrame) { - if (isBotFrame && (!sv_vm || sv_vm->type == VM_NONE || !gamedata || gamedata->APIversion < 15)) { + if (isBotFrame && (!sv_vm || sv_vm->type == VMI_NONE || gamedata.APIversion < 15)) { return; } if (sv_vm) - VM_Call(sv_vm, GAME_START_FRAME, (int) (sv.time * 1000), isBotFrame, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 2, GAME_START_FRAME, (int) (sv.time * 1000), (int)isBotFrame, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameStartFrame(); } @@ -317,7 +237,7 @@ void PR2_GameStartFrame(qbool isBotFrame) void PR2_GameClientConnect(int spec) { if (sv_vm) - VM_Call(sv_vm, GAME_CLIENT_CONNECT, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 1, GAME_CLIENT_CONNECT, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameClientConnect(spec); } @@ -328,7 +248,7 @@ void PR2_GameClientConnect(int spec) void PR2_GamePutClientInServer(int spec) { if (sv_vm) - VM_Call(sv_vm, GAME_PUT_CLIENT_IN_SERVER, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 1, GAME_PUT_CLIENT_IN_SERVER, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GamePutClientInServer(spec); } @@ -339,7 +259,7 @@ void PR2_GamePutClientInServer(int spec) void PR2_GameClientDisconnect(int spec) { if (sv_vm) - VM_Call(sv_vm, GAME_CLIENT_DISCONNECT, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 1, GAME_CLIENT_DISCONNECT, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameClientDisconnect(spec); } @@ -350,7 +270,7 @@ void PR2_GameClientDisconnect(int spec) void PR2_GameClientPreThink(int spec) { if (sv_vm) - VM_Call(sv_vm, GAME_CLIENT_PRETHINK, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 1, GAME_CLIENT_PRETHINK, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameClientPreThink(spec); } @@ -361,7 +281,7 @@ void PR2_GameClientPreThink(int spec) void PR2_GameClientPostThink(int spec) { if (sv_vm) - VM_Call(sv_vm, GAME_CLIENT_POSTTHINK, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 1, GAME_CLIENT_POSTTHINK, spec, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameClientPostThink(spec); } @@ -372,7 +292,7 @@ void PR2_GameClientPostThink(int spec) qbool PR2_ClientCmd(void) { if (sv_vm) - return VM_Call(sv_vm, GAME_CLIENT_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return VM_Call(sv_vm, 0, GAME_CLIENT_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else return PR1_ClientCmd(); } @@ -399,7 +319,7 @@ qbool PR2_ClientSay(int isTeamSay, char *message) // if (sv_vm) - return VM_Call(sv_vm, GAME_CLIENT_SAY, isTeamSay, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return VM_Call(sv_vm, 1, GAME_CLIENT_SAY, isTeamSay, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else return PR1_ClientSay(isTeamSay, message); } @@ -410,7 +330,7 @@ qbool PR2_ClientSay(int isTeamSay, char *message) void PR2_GameSetNewParms(void) { if (sv_vm) - VM_Call(sv_vm, GAME_SETNEWPARMS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_SETNEWPARMS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameSetNewParms(); } @@ -421,7 +341,7 @@ void PR2_GameSetNewParms(void) void PR2_GameSetChangeParms(void) { if (sv_vm) - VM_Call(sv_vm, GAME_SETCHANGEPARMS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_SETCHANGEPARMS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else { PR1_GameSetChangeParms(); @@ -434,7 +354,7 @@ void PR2_GameSetChangeParms(void) void PR2_EdictTouch(func_t f) { if (sv_vm) - VM_Call(sv_vm, GAME_EDICT_TOUCH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_EDICT_TOUCH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_EdictTouch(f); } @@ -445,7 +365,7 @@ void PR2_EdictTouch(func_t f) void PR2_EdictThink(func_t f) { if (sv_vm) - VM_Call(sv_vm, GAME_EDICT_THINK, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_EDICT_THINK, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_EdictThink(f); } @@ -456,7 +376,7 @@ void PR2_EdictThink(func_t f) void PR2_EdictBlocked(func_t f) { if (sv_vm) - VM_Call(sv_vm, GAME_EDICT_BLOCKED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_EDICT_BLOCKED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_EdictBlocked(f); } @@ -464,12 +384,12 @@ void PR2_EdictBlocked(func_t f) //=========================================================================== // UserInfoChanged //=========================================================================== -qbool PR2_UserInfoChanged(void) +qbool PR2_UserInfoChanged(int after) { if (sv_vm) - return VM_Call(sv_vm, GAME_CLIENT_USERINFO_CHANGED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return VM_Call(sv_vm, 1, GAME_CLIENT_USERINFO_CHANGED, after, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else - return PR1_UserInfoChanged(); + return PR1_UserInfoChanged(after); } //=========================================================================== @@ -478,7 +398,7 @@ qbool PR2_UserInfoChanged(void) void PR2_GameShutDown(void) { if (sv_vm) - VM_Call(sv_vm, GAME_SHUTDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_SHUTDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_GameShutDown(); } @@ -490,7 +410,7 @@ void PR2_UnLoadProgs(void) { if (sv_vm) { - VM_Unload( sv_vm ); + VM_Free( sv_vm ); sv_vm = NULL; } else @@ -504,7 +424,7 @@ void PR2_UnLoadProgs(void) //=========================================================================== void PR2_LoadProgs(void) { - sv_vm = (vm_t *) VM_Load(sv_vm, (vm_type_t) (int) sv_progtype.value, sv_progsname.string, sv_syscall, sv_sys_callex); + sv_vm = VM_Create(VM_GAME, sv_progsname.string, PR2_GameSystemCalls, sv_progtype.value ); if ( sv_vm ) { @@ -545,7 +465,7 @@ void PR2_GameConsoleCommand(void) break; } } - VM_Call(sv_vm, GAME_CONSOLE_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_CONSOLE_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); pr_global_struct->self = old_self; pr_global_struct->other = old_other; } @@ -557,21 +477,128 @@ void PR2_GameConsoleCommand(void) void PR2_PausedTic(float duration) { if (sv_vm) - VM_Call(sv_vm, GAME_PAUSED_TIC, duration*1000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 1, GAME_PAUSED_TIC, (int)(duration*1000), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); else PR1_PausedTic(duration); } void PR2_ClearEdict(edict_t* e) { - if (sv_vm && sv_vm->pr2_references && (sv_vm->type == VM_NATIVE || sv_vm->type == VM_BYTECODE)) { + if (sv_vm && sv_vm->pr2_references && (sv_vm->type == VMI_NATIVE || sv_vm->type == VMI_BYTECODE || sv_vm->type == VMI_COMPILED)) { int old_self = pr_global_struct->self; pr_global_struct->self = EDICT_TO_PROG(e); - VM_Call(sv_vm, GAME_CLEAR_EDICT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + VM_Call(sv_vm, 0, GAME_CLEAR_EDICT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); pr_global_struct->self = old_self; } } +//=========================================================================== +// InitProgs +//=========================================================================== + +#define GAME_API_VERSION_MIN 16 + +void LoadGameData(intptr_t gamedata_ptr) +{ +#ifdef idx64 + gameData_vm_t* gamedata_vm; + + if (sv_vm->type == VMI_BYTECODE || sv_vm->type == VMI_COMPILED) + { + gamedata_vm = (gameData_vm_t *)PR2_GetString(gamedata_ptr); + gamedata.ents = (intptr_t)gamedata_vm->ents_p; + gamedata.global = (intptr_t)gamedata_vm->global_p; + gamedata.fields = (intptr_t)gamedata_vm->fields_p; + gamedata.APIversion = gamedata_vm->APIversion; + gamedata.sizeofent = gamedata_vm->sizeofent; + gamedata.maxentities = gamedata_vm->maxentities; + return; + } +#endif + gamedata = *(gameData_t *)PR2_GetString(gamedata_ptr); +} + +void LoadFields(void) +{ +#ifdef idx64 + if (sv_vm->type == VMI_BYTECODE || sv_vm->type == VMI_COMPILED) + { + field_vm_t *fieldvm_p; + field_t *f; + int num = 0; + fieldvm_p = (field_vm_t*)PR2_GetString((intptr_t)gamedata.fields); + while (fieldvm_p[num].name) { + num++; + } + f = fields = (field_t *)Hunk_Alloc(sizeof(field_t) * (num + 1)); + while (fieldvm_p->name){ + f->name = (stringptr_t)fieldvm_p->name; + f->ofs = fieldvm_p->ofs; + f->type = (fieldtype_t)fieldvm_p->type; + f++; + fieldvm_p++; + } + f->name = 0; + return; + } +#endif + fields = (field_t*)PR2_GetString((intptr_t)gamedata.fields); +} + +extern void PR2_FS_Restart(void); + +void PR2_InitProg(void) +{ + extern cvar_t sv_pr2references; + + intptr_t gamedata_ptr; + + Cvar_SetValue(&sv_pr2references, 0.0f); + + if (!sv_vm) { + PR1_InitProg(); + return; + } + + PR2_FS_Restart(); + + gamedata.APIversion = 0; + gamedata_ptr = (intptr_t) VM_Call(sv_vm, 2, GAME_INIT, (int)(sv.time * 1000), (int)(Sys_DoubleTime() * 100000), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + if (!gamedata_ptr) { + SV_Error("PR2_InitProg: gamedata == NULL"); + } + + LoadGameData(gamedata_ptr); + if (gamedata.APIversion < GAME_API_VERSION_MIN || gamedata.APIversion > GAME_API_VERSION) { + if (GAME_API_VERSION_MIN == GAME_API_VERSION) { + SV_Error("PR2_InitProg: Incorrect API version (%i should be %i)", gamedata.APIversion, GAME_API_VERSION); + } + else { + SV_Error("PR2_InitProg: Incorrect API version (%i should be between %i and %i)", gamedata.APIversion, GAME_API_VERSION_MIN, GAME_API_VERSION); + } + } + + sv_vm->pr2_references = gamedata.APIversion >= 15 && (int)sv_pr2references.value; +#ifdef idx64 + if (sv_vm->type == VMI_NATIVE && (!sv_vm->pr2_references || gamedata.APIversion < 15)) + SV_Error("PR2_InitProg: Native prog must support sv_pr2references for 64bit mode (mod API version (%i should be 15+))", gamedata.APIversion); +#endif + pr_edict_size = gamedata.sizeofent; + Con_DPrintf("edict size %d\n", pr_edict_size); + sv.game_edicts = (entvars_t *)(PR2_GetString((intptr_t)gamedata.ents)); + pr_global_struct = (globalvars_t*)PR2_GetString((intptr_t)gamedata.global); + pr_globals = (float *)pr_global_struct; + LoadFields(); + + sv.max_edicts = MAX_EDICTS; + if (gamedata.APIversion >= 14) { + sv.max_edicts = min(sv.max_edicts, gamedata.maxentities); + } + else { + sv.max_edicts = min(sv.max_edicts, 512); + } +} + #endif /* USE_PR2 */ #endif // !CLIENTONLY diff --git a/src/pr2_vm.c b/src/pr2_vm.c deleted file mode 100644 index 9f1b55ee7..000000000 --- a/src/pr2_vm.c +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * QW262 - * Copyright (C) 2004 [sd] angel - * - * 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 - * - * - * - */ -/* - Quake3 compatible virtual machine - map file support - print StackTrace on errors - runaway loop protection - reenterable vmMain -*/ - -#ifndef CLIENTONLY -#ifdef USE_PR2 - -#include "qwsvdef.h" - -#ifdef QVM_PROFILE -cvar_t sv_enableprofile = {"sv_enableprofile","0"}; -typedef struct -{ - int address; -#ifdef _WIN32 - __int64 instruction_count; -#else - int instruction_count; -#endif -} -profile_t; -#define MAX_PROFILE_FUNCS 0x1000 - -profile_t profile_funcs[MAX_PROFILE_FUNCS]; -int num_profile_func; - -qbool PR2_IsValidReadAddress(register qvm_t * qvm, intptr_t address) -{ - if (address >= (intptr_t)&sv && address < ((intptr_t)&sv) + sizeof(sv) - 4) { - return true; - } - if (address >= (intptr_t)&svs.clients && address < ((intptr_t)&svs.clients) + sizeof(svs.clients) - 4) { - return true; - } - if (address == (intptr_t)VersionStringFull()) { - return true; - } - - return (address >= (intptr_t)qvm->ds && address < (intptr_t)qvm->ds + qvm->len_ds); -} - -qbool PR2_IsValidWriteAddress(register qvm_t * qvm, intptr_t address) -{ - if (address >= (intptr_t)&sv && address < ((intptr_t)&sv) + sizeof(sv) - 4) { - return true; - } - if (address >= (intptr_t)&svs.clients && address < ((intptr_t)&svs.clients) + sizeof(svs.clients) - 4) { - return true; - } - - return (address >= (intptr_t)qvm->ds && address < (intptr_t)qvm->ds + qvm->len_ds); -} - -#define OLD_VM_POINTER(base,mask,x) ((void*)((char *)base+((x)&mask))) - -void* VM_POINTER(byte* base, uintptr_t mask, intptr_t offset) -{ - intptr_t address = (intptr_t) base + offset; - qvm_t* qvm = (qvm_t*) sv_vm->hInst; - - if (PR2_IsValidWriteAddress(qvm, address)) { - return (void*)address; - } - - return OLD_VM_POINTER(base, mask, offset); -} - -profile_t* ProfileEnterFunction(int address) -{ - int i; - for ( i = 0 ; i < num_profile_func ; i++ ) - { - if(profile_funcs[i].address == address) - return &profile_funcs[i]; - } - if( num_profile_func >= MAX_PROFILE_FUNCS ) - { - profile_funcs[0].address = address; - profile_funcs[0].instruction_count = 0; - - return &profile_funcs[0]; - } - profile_funcs[num_profile_func].address = address; - profile_funcs[num_profile_func].instruction_count = 0; - - return &profile_funcs[num_profile_func++]; -} -symbols_t* QVM_FindName( qvm_t * qvm, int off); -#endif - -void PR2_Profile_f(void) -{ -#ifdef QVM_PROFILE - profile_t *f, *best; -#endif -#ifdef _WIN32 - __int64 max; -#else - int max; -#endif - int num; - int i; - symbols_t *sym; - - if(!sv_vm) - { - PR_Profile_f(); - return; - } -#ifdef QVM_PROFILE - if(sv_vm->type != VM_BYTECODE) - return; - num = 0; - if(!(int)sv_enableprofile.value) - { - Con_Printf ("profiling no enabled\n"); - return; - } - do - { - max = 0; - best = NULL; - for (i=0 ; iinstruction_count > max) - { - max = f->instruction_count; - best = f; - } - } - if (best) - { - if (num < 15) - { - sym = QVM_FindName( (qvm_t*)(sv_vm->hInst), best->address ); -#ifdef _WIN32 - Con_Printf ("%18I64d %s\n", best->instruction_count, sym->name); -#else - Con_Printf ("%9d %s\n", best->instruction_count, sym->name); -#endif - - } - num++; - best->instruction_count = 0; - } - } - while (best); - num_profile_func = 0; -#endif -} - -void VM_UnloadQVM( qvm_t * qvm ) -{ - if(qvm) - Q_free( qvm ); -} - -void VM_Unload( vm_t * vm ) -{ - if ( !vm ) - return; - Con_DPrintf( "VM_Unload \"%s\"\n", vm->name ); - switch ( vm->type ) - { - case VM_NATIVE: - if ( vm->hInst ) - if ( !Sys_DLClose( (DL_t) vm->hInst ) ) - SV_Error( "VM_Unload: couldn't unload module %s\n", vm->name ); - vm->hInst = NULL; - break; - case VM_BYTECODE: - VM_UnloadQVM( (qvm_t*) vm->hInst ); - break; - case VM_NONE: - return; - - } - Q_free( vm ); -} - -qbool VM_LoadNative( vm_t * vm ) -{ - char name[MAX_OSPATH]; - char *gpath = NULL; - void ( *dllEntry ) ( void * ); - - memset(name, 0, sizeof(name)); - while ( ( gpath = FS_NextPath( gpath ) ) ) - { - snprintf(name, sizeof(name), "%s/%s." DLEXT, gpath, vm->name); - vm->hInst = Sys_DLOpen( name ); - if ( vm->hInst ) - { - Con_DPrintf( "LoadLibrary (%s)\n", name ); - break; - } - } - - if ( !vm->hInst ) - return false; - - dllEntry = (void (EXPORT_FN *)(void *)) Sys_DLProc( (DL_t) vm->hInst, "dllEntry" ); - vm->vmMain = (intptr_t (EXPORT_FN *)(int,int,int,int,int,int,int,int,int,int,int,int,int)) Sys_DLProc( (DL_t) vm->hInst, "vmMain" ); - if ( !dllEntry || !vm->vmMain ) - { - VM_Unload( vm ); - SV_Error( "VM_LoadNative: couldn't initialize module %s", name ); - } - dllEntry( (void *) vm->syscall ); - - Info_SetValueForStarKey( svs.info, "*progs", DLEXT, MAX_SERVERINFO_STRING ); - vm->type = VM_NATIVE; - return true; -} - -void VM_PrintInfo( vm_t * vm) -{ - qvm_t *qvm; - if(!vm) - { - Con_Printf( "VM_PrintInfo: NULL vm\n" ); - return; - } - - if(!vm->name[0]) - return; - - Con_DPrintf("%s: ", vm->name); - switch(vm->type) - { - case VM_NATIVE: - Con_DPrintf("native\n"); - break; - case VM_BYTECODE: - Con_DPrintf("bytecode interpreted\n"); - if((qvm=(qvm_t *)vm->hInst)) - { - Con_DPrintf(" code length: %8xh\n", qvm->len_cs*sizeof(qvm->cs[0])); - Con_DPrintf("instruction count: %8d\n", qvm->len_cs); - Con_DPrintf(" data length: %8xh\n", qvm->len_ds); - Con_DPrintf(" stack length: %8xh\n", qvm->len_ss); - } - break; - default: - Con_DPrintf("unknown\n"); - break; - } - -} - -#define MAX_LINE_LENGTH 1024 - -void LoadMapFile( qvm_t*qvm, char* fname ) -{ - char name[MAX_OSPATH]; - char lineBuffer[MAX_LINE_LENGTH]; - char symname[MAX_LINE_LENGTH]; - int i,off,seg,len,num_symbols = 0; - symbols_t *sym = NULL; - - byte *buff; - byte *p; - - Con_DPrintf("Loading symbol information\n"); - snprintf( name, sizeof( name ), "%s.map", fname ); - buff = FS_LoadTempFile( name , NULL ); - qvm->sym_info = NULL; - if ( !buff ) - return; - p=buff; - while(*p) - { - for( i = 0; i < MAX_LINE_LENGTH; i++) - { - if( p[i] == 0 || p[i] == '\n') - break; - } - if ( i == MAX_LINE_LENGTH ) - { - return; - } - - memcpy( lineBuffer, p, i ); - lineBuffer[i] = 0; - p += i; - if( *p == '\n') p++; - if( 3 != sscanf( lineBuffer,"%d %8x %s",&seg,&off,symname) ) - return; - len = strlen(symname); - if(!len)continue; - if( off < 0 ) - continue; - - if( seg == 0 && off >= qvm->len_cs) - { - Con_DPrintf("bad cs off in map file %s.map\n",fname); - qvm->sym_info = NULL; - return; - } - if( seg >= 1 && off >= qvm->len_ds ) - { - Con_DPrintf("bad ds off in map file %s.map\n",fname); - continue; - } - - if( !qvm->sym_info ) - { - qvm->sym_info = (symbols_t *) Hunk_Alloc( sizeof(symbols_t) + len + 1); - sym = qvm->sym_info; - } - else - { - sym->next = (symbols_t *) Hunk_Alloc( sizeof(symbols_t) + len + 1); - sym = sym->next; - } - sym->seg = seg; - sym->off = off; - sym->next= NULL; - num_symbols++; - strlcpy(sym->name, symname, len + 1); - } - Con_DPrintf("%i symbols loaded from %s\n",num_symbols,name); -} - -qbool VM_LoadBytecode( vm_t * vm, sys_callex_t syscall1 ) -{ - char name[MAX_OSPATH]; - byte *buff; - vmHeader_t *header; - qvm_t *qvm; - char num[32]; - int filesize; - - snprintf( name, sizeof( name ), "%s.qvm", vm->name ); - - Con_DPrintf( "VM_LoadBytecode: load %s\n", name ); - buff = FS_LoadTempFile( name , &filesize ); - - if ( !buff ) - return false; - - // add qvm crc to the serverinfo - snprintf( num, sizeof(num), "%i", CRC_Block( ( byte * ) buff, filesize ) ); - Info_SetValueForStarKey( svs.info, "*progs", num, MAX_SERVERINFO_STRING ); - - header = ( vmHeader_t * ) buff; - - header->vmMagic = LittleLong( header->vmMagic ); - header->instructionCount = LittleLong( header->instructionCount ); - header->codeOffset = LittleLong( header->codeOffset ); - header->codeLength = LittleLong( header->codeLength ); - header->dataOffset = LittleLong( header->dataOffset ); - header->dataLength = LittleLong( header->dataLength ); - header->litLength = LittleLong( header->litLength ); - header->bssLength = LittleLong( header->bssLength ); - - // check file - if ( header->vmMagic != VM_MAGIC || header->instructionCount <= 0 || header->codeLength <= 0 ) - { - return false; - } - // create vitrual machine - if(vm->hInst) - qvm = (qvm_t *)vm->hInst; - else - qvm = (qvm_t *) Q_malloc (sizeof (qvm_t)); - - qvm->len_cs = header->instructionCount + 1; //bad opcode padding. - qvm->len_ds = header->dataOffset + header->litLength + header->bssLength; - //align ds - qvm->ds_mask = 1; - while( qvm->ds_mask < qvm->len_ds) qvm->ds_mask<<=1; - qvm->len_ds = qvm->ds_mask; - qvm->ds_mask--; - - qvm->len_ss = 0x10000; // default by q3asm - if ( qvm->len_ds < qvm->len_ss ) - Sys_Error( "VM_LoadBytecode: stacksize greater than data segment" ); - - qvm->cs = ( qvm_instruction_t * ) Hunk_AllocName( qvm->len_cs * sizeof( qvm_instruction_t ), "qvmcode" ); - qvm->ds = (byte *) Hunk_AllocName( qvm->len_ds, "qvmdata" ); - qvm->ss = qvm->ds + qvm->len_ds - qvm->len_ss; - - // setup registers - qvm->PC = 0; - qvm->SP = 0; - qvm->LP = qvm->len_ds - sizeof(int); - qvm->cycles = 0; - qvm->reenter = 0; - qvm->syscall = syscall1; - - - // load instructions - { - byte *src = buff + header->codeOffset; - qvm_instruction_t *dst = qvm->cs; - opcode_t op; - int i; - - for ( i = 0; i < header->instructionCount; i++, dst++ ) - { - op = (opcode_t) *src++; - dst->opcode = op; - switch ( op ) - { - case OP_ARG: - dst->parm._int = ( int ) *src++; - break; - - case OP_ENTER: - case OP_LEAVE: - case OP_CONST: - case OP_LOCAL: - case OP_EQ: - case OP_NE: - case OP_LTI: - case OP_LEI: - case OP_GTI: - case OP_GEI: - case OP_LTU: - case OP_LEU: - case OP_GTU: - case OP_GEU: - case OP_EQF: - case OP_NEF: - case OP_LTF: - case OP_LEF: - case OP_GTF: - case OP_GEF: - case OP_BLOCK_COPY: - - dst->parm._int = LittleLong( *( int * ) src ); - src += 4; - break; - - default: - dst->parm._int = 0; - break; - } - } - dst->opcode = OP_BREAK; - dst->parm._int = 0; - } - // load data segment - { - int *src = ( int * ) ( buff + header->dataOffset ); - int *dst = ( int * ) qvm->ds; - int i; - - for ( i = 0; i < header->dataLength / 4; i++ ) - *dst++ = LittleLong( *src++ ); - - memcpy( dst, src, header->litLength ); - } - - LoadMapFile( qvm, vm->name ); - vm->type = VM_BYTECODE; - vm->hInst = qvm; - return true; -} - - -vm_t *VM_Load( vm_t * vm, vm_type_t type, char *name, sys_call_t syscall1, sys_callex_t syscallex ) -{ - if ( !name || !syscall1 || !syscallex ) - Sys_Error( "VM_Load: bad parms" ); - - if ( vm ) - VM_Unload(vm); - - vm = (vm_t *) Q_malloc (sizeof (vm_t)); - - - - Con_Printf( "VM_Load: \"%s\"\n", name ); - - // prepare vm struct - memset( vm, 0, sizeof( vm_t ) ); - strlcpy( vm->name, name, sizeof( vm->name ) ); - vm->syscall = syscall1; -#ifdef QVM_PROFILE - num_profile_func = 0; -#endif - - switch ( type ) - { - case VM_NATIVE: - if ( VM_LoadNative( vm ) ) - break; - case VM_BYTECODE: - if ( VM_LoadBytecode( vm, syscallex ) ) - break; - default: - Q_free(vm); - return NULL; - break; - } - - VM_PrintInfo(vm); - return vm; -} - - -int QVM_Exec( register qvm_t * qvm, int command, int arg0, int arg1, int arg2, int arg3, - int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ); - -intptr_t VM_Call( vm_t * vm, int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, - int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) -{ - if ( !vm ) - Sys_Error( "VM_Call with NULL vm" ); - - switch ( vm->type ) - { - case VM_NATIVE: - return vm->vmMain( command, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 ); - case VM_BYTECODE: - return QVM_Exec( (qvm_t*) vm->hInst, command, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, - arg11 ); - case VM_NONE: - Sys_Error( "VM_Call with VM_NONE type vm" ); - } - Sys_Error( "VM_Call with bad vm->type" ); - return 0; -} - -#define STACK_INT(x) (*(int*)(qvm->ds+qvm->LP+(x)*sizeof(int) )) -symbols_t* QVM_FindName( qvm_t * qvm, int off) -{ - symbols_t* sym,*found; - - found = NULL; - for(sym = qvm->sym_info; sym; sym = sym->next) - { - if( sym->seg != 0)continue; - if(!found) - found = sym; - if( sym->off <= off && sym->off >found->off ) - found = sym; - } - return found; -} -void QVM_StackTrace( qvm_t * qvm ) -{ - symbols_t *sym; - int LP, off, num; - - LP = qvm->LP; - Con_Printf( " code length: %8xh\n", qvm->len_cs * sizeof( qvm->cs[0] ) ); - Con_Printf( "instruction count: %8xh\n", qvm->len_cs ); - Con_Printf( " data length: %8xh\n", qvm->len_ds ); - Con_Printf( " stack length: %8xh\n", qvm->len_ss ); - - Con_Printf( "PC %8x LP %8x SP %8x\n", qvm->PC, qvm->LP, qvm->SP ); - - // if ( qvm->sym_info == NULL ) - // return; - - sym = QVM_FindName( qvm, qvm->PC ); - - if ( sym ) - Con_Printf( "PC-%8x %s + %d\n", sym->off, sym->name, qvm->PC - sym->off ); - - while ( LP < qvm->len_ds - (int) sizeof ( int ) ) - { - off = *( int * ) ( qvm->ds + LP ); - num = *( int * ) ( qvm->ds + LP + sizeof( int ) ); - LP += num; - if ( off < 0 || off >= qvm->len_cs ) - { - Con_Printf( "Error ret address %8x in stack %8x/%8x\n", off, LP, qvm->len_ds ); - return; - } - if ( num <= 0 ) - { - Con_Printf( "Error num in stack %8x/%8x\n", LP, qvm->len_ds ); - return; - } - - sym = QVM_FindName( qvm, off ); - if ( sym ) - Con_Printf( " %8x %s + %d\n", sym->off, sym->name, off - sym->off ); - else - Con_Printf( " %8x unknown\n", off); - - } -} - -void QVM_RunError( qvm_t * qvm, char *error, ... ) -{ - va_list argptr; - char string[1024]; - - va_start( argptr, error ); - vsnprintf( string, sizeof(string), error, argptr ); - va_end( argptr ); - - sv_error = true; - - QVM_StackTrace( qvm ); - - Con_Printf( "%s\n", string ); - - SV_Error( "QVM Program error" ); -} - -int trap_Call( qvm_t * qvm, int apinum ) -{ - int ret; - - qvm->SP++; - ret = qvm->syscall( qvm->ds, qvm->ds_mask, apinum, ( pr2val_t* ) ( qvm->ds + qvm->LP + 2*sizeof(int) ) ); - - return ret; -} - - -void PrintInstruction( qvm_t * qvm ); - -int QVM_Exec( register qvm_t * qvm, int command, int arg0, int arg1, int arg2, int arg3, - int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) -{ - qvm_parm_type_t opStack[OPSTACKSIZE + 1]; //in q3 stack var of QVM_Exec size~0x400; -#ifdef QVM_RUNAWAY_PROTECTION - int cycles[MAX_PROC_CALL],cycles_p=0; -#endif -#ifdef QVM_PROFILE - profile_t *profile_func = NULL; - symbols_t *sym; -#endif - int savePC, saveSP, saveLP, ivar = 0; - qvm_instruction_t op; - - savePC = qvm->PC; - saveSP = qvm->SP; - saveLP = qvm->LP; - - -#ifdef QVM_PROFILE - if((int)sv_enableprofile.value) - profile_func = ProfileEnterFunction(0); -#endif - if ( !qvm->reenter ) - { - //FIXME check last exit REGISTERS - qvm->LP = qvm->len_ds - sizeof(int); - } - if ( qvm->reenter++ > MAX_vmMain_Call ) - QVM_RunError( qvm, "QVM_Exec MAX_vmMain_Call reached"); - - qvm->PC = 0; - qvm->SP = 0; - qvm->LP -= 14 * sizeof(int); - - STACK_INT( 0 ) = 0; // return addres; - STACK_INT( 1 ) = 14 * sizeof(int); //11 params + command + retaddr + num args; - STACK_INT( 2 ) = command; - STACK_INT( 3 ) = arg0; - STACK_INT( 4 ) = arg1; - STACK_INT( 5 ) = arg2; - STACK_INT( 6 ) = arg3; - STACK_INT( 7 ) = arg4; - STACK_INT( 8 ) = arg5; - STACK_INT( 9 ) = arg6; - STACK_INT( 10 ) = arg7; - STACK_INT( 11 ) = arg8; - STACK_INT( 12 ) = arg9; - STACK_INT( 13 ) = arg10; - STACK_INT( 14 ) = arg11; -#ifdef QVM_RUNAWAY_PROTECTION - cycles[cycles_p] = 0; -#endif - - do - { -#ifdef SAFE_QVM - if ( qvm->PC >= qvm->len_cs || qvm->PC < 0 ) - QVM_RunError( qvm, "QVM PC out of range, %8d\n", qvm->PC ); - - if ( qvm->SP < 0 ) - QVM_RunError( qvm, "QVM opStack underflow at %8x", qvm->PC ); - - if ( qvm->SP > OPSTACKSIZE ) - QVM_RunError( qvm, "QVM opStack overflow at %8x", qvm->PC ); - - if ( qvm->LP < qvm->len_ds - qvm->len_ss ) - QVM_RunError( qvm, "QVM Stack overflow at %8x", qvm->PC ); - - if ( qvm->LP >= qvm->len_ds ) - QVM_RunError( qvm, "QVM Stack underflow at %8x", qvm->PC ); -#endif -#ifdef QVM_RUNAWAY_PROTECTION - if(cycles[cycles_p]++ > MAX_CYCLES) - QVM_RunError( qvm, "QVM runaway loop error", qvm->PC ); -#endif -#ifdef QVM_PROFILE - if((int)sv_enableprofile.value) - profile_func->instruction_count++; -#endif - op = qvm->cs[qvm->PC++]; - switch ( op.opcode ) - { - case OP_UNDEF: - QVM_RunError( qvm, "OP_UNDEF\n" ); - break; - - case OP_IGNORE: - break; - - case OP_BREAK: - QVM_RunError( qvm, "OP_BREAK\n" ); - break; - - case OP_ENTER: - qvm->LP -= op.parm._int; -#ifdef PARANOID - if ( qvm->LP < qvm->len_ds - qvm->len_ss ) - { - QVM_StackTrace( qvm ); - Sys_Error( "QVM Stack overflow on enter at %8x", qvm->PC ); - } -#endif - STACK_INT( 1 ) = op.parm._int; -#ifdef QVM_RUNAWAY_PROTECTION - if(++cycles_p >= MAX_PROC_CALL) - QVM_RunError( qvm, "MAX_PROC_CALL reached\n" ); - cycles[cycles_p] = 0; -#endif - break; - - case OP_LEAVE: - qvm->LP += op.parm._int; -#ifdef PARANOID - if ( qvm->LP >= qvm->len_ds ) - QVM_RunError( qvm, "QVM Stack underflow on leave at %8x", qvm->PC ); -#endif - qvm->PC = STACK_INT( 0 ); -#ifdef QVM_PROFILE - if((int)sv_enableprofile.value) - { - sym = QVM_FindName( qvm, qvm->PC ); - profile_func = ProfileEnterFunction(sym->off); - } -#endif - -#ifdef QVM_RUNAWAY_PROTECTION - cycles_p--; -#endif - break; - - case OP_CALL: - STACK_INT( 0 ) = qvm->PC; - ivar = opStack[qvm->SP--]._int; - if ( ivar < 0 ) - { - ivar = trap_Call( qvm, -ivar - 1 ); - opStack[qvm->SP]._int = ivar; - } - else - { - qvm->PC = ivar; -#ifdef QVM_PROFILE - if((int)sv_enableprofile.value) - profile_func = ProfileEnterFunction(ivar); -#endif - - } - break; - case OP_PUSH: - qvm->SP++; - break; - - case OP_POP: - qvm->SP--; - break; - - case OP_CONST: - qvm->SP++; - opStack[qvm->SP]._int = op.parm._int; - break; - - case OP_LOCAL: - qvm->SP++; - opStack[qvm->SP]._int = qvm->LP + op.parm._int; - break; - - case OP_JUMP: - qvm->PC = opStack[qvm->SP--]._int; - break; - //-----Compare Operators - case OP_EQ: - if ( opStack[qvm->SP - 1]._int == opStack[qvm->SP]._int ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_NE: - if ( opStack[qvm->SP - 1]._int != opStack[qvm->SP]._int ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - //singed int compare - case OP_LTI: - if ( opStack[qvm->SP - 1]._int < opStack[qvm->SP]._int ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_LEI: - if ( opStack[qvm->SP - 1]._int <= opStack[qvm->SP]._int ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_GTI: - if ( opStack[qvm->SP - 1]._int > opStack[qvm->SP]._int ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_GEI: - if ( opStack[qvm->SP - 1]._int >= opStack[qvm->SP]._int ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - //unsinged int compare - case OP_LTU: - if ( opStack[qvm->SP - 1]._uint < opStack[qvm->SP]._uint ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_LEU: - if ( opStack[qvm->SP - 1]._uint <= opStack[qvm->SP]._uint ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_GTU: - if ( opStack[qvm->SP - 1]._uint > opStack[qvm->SP]._uint ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_GEU: - if ( opStack[qvm->SP - 1]._uint >= opStack[qvm->SP]._uint ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - //float - case OP_EQF: - if ( opStack[qvm->SP - 1]._float == opStack[qvm->SP]._float ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_NEF: - if ( opStack[qvm->SP - 1]._float != opStack[qvm->SP]._float ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_LTF: - if ( opStack[qvm->SP - 1]._float < opStack[qvm->SP]._float ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_LEF: - if ( opStack[qvm->SP - 1]._float <= opStack[qvm->SP]._float ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_GTF: - if ( opStack[qvm->SP - 1]._float > opStack[qvm->SP]._float ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - - case OP_GEF: - if ( opStack[qvm->SP - 1]._float >= opStack[qvm->SP]._float ) - qvm->PC = op.parm._int; - qvm->SP -= 2; - break; - //------------------- - case OP_LOAD1: - ivar = opStack[qvm->SP]._int; - -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "data load 1 out of range %8x\n", ivar ); - opStack[qvm->SP]._int = *( char * ) ( qvm->ds + ivar ); -#else - opStack[qvm->SP]._int = *( char * ) ( qvm->ds + (ivar&qvm->ds_mask) ); -#endif - break; - - case OP_LOAD2: - ivar = opStack[qvm->SP]._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "data load 2 out of range %8x\n", ivar ); - opStack[qvm->SP]._int = *( short * ) ( qvm->ds + ivar ); -#else - opStack[qvm->SP]._int = *( short * ) ( qvm->ds + (ivar&qvm->ds_mask) ); -#endif - break; - - case OP_LOAD4: - ivar = opStack[qvm->SP]._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "data load 4 out of range %8x\n", ivar ); - opStack[qvm->SP]._int = *( int * ) ( qvm->ds + ivar ); -#else - opStack[qvm->SP]._int = *( int * ) ( qvm->ds + (ivar&qvm->ds_mask) ); -#endif - break; - case OP_STORE1: - ivar = opStack[qvm->SP - 1]._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidWriteAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "data store 1 out of range %8x\n", ivar ); - *( char * ) ( qvm->ds + ivar ) = opStack[qvm->SP]._int & 0xff; -#else - *( char * ) ( qvm->ds + (ivar&qvm->ds_mask) ) = opStack[qvm->SP]._int & 0xff; -#endif - qvm->SP -= 2; - break; - case OP_STORE2: - ivar = opStack[qvm->SP - 1]._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidWriteAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "data store 2 out of range %8x\n", ivar ); - *( short * ) ( qvm->ds + ivar ) = opStack[qvm->SP]._int & 0xffff; -#else - *( short * ) ( qvm->ds + (ivar&qvm->ds_mask) ) = opStack[qvm->SP]._int & 0xffff; -#endif - qvm->SP -= 2; - break; - - case OP_STORE4: - ivar = opStack[qvm->SP - 1]._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidWriteAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "data store 4 out of range %8x\n", ivar ); - *( int * ) ( qvm->ds + ivar ) = opStack[qvm->SP]._int; -#else - *( int * ) ( qvm->ds + (ivar&qvm->ds_mask) ) = opStack[qvm->SP]._int; -#endif - qvm->SP -= 2; - break; - - case OP_ARG: - ivar = qvm->LP + op.parm._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidWriteAddress(qvm, (intptr_t)qvm->ds + ivar)) - QVM_RunError( qvm, "arg out of range %8x\n", ivar ); - *( int * ) ( qvm->ds + ivar ) = opStack[qvm->SP--]._int; -#else - *( int * ) ( qvm->ds + (ivar&qvm->ds_mask) ) = opStack[qvm->SP--]._int; -#endif - break; - - case OP_BLOCK_COPY: - { - int off1,off2,len; - off1 = opStack[qvm->SP - 1]._int; - off2 = opStack[qvm->SP]._int; - len = op.parm._int; -#ifdef QVM_DATA_PROTECTION - if (!PR2_IsValidWriteAddress(qvm, (intptr_t)qvm->ds + off1) || !PR2_IsValidWriteAddress(qvm, (intptr_t)qvm->ds + off1 + len) || - !PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + off2) || !PR2_IsValidReadAddress(qvm, (intptr_t)qvm->ds + off2 + len)) { - QVM_RunError(qvm, "block copy out of range %8x\n", ivar); - } - memmove( qvm->ds + off1, qvm->ds + off2, len ); -#else - memmove( qvm->ds + (off1 & qvm->ds_mask), qvm->ds + (off2 & qvm->ds_mask), len ); -#endif - qvm->SP -= 2; - } - break; - //integer arifmetic - case OP_SEX8: - if( opStack[qvm->SP]._int & 0x80 ) - opStack[qvm->SP]._int |=0xFFFFFF00; - else - opStack[qvm->SP]._int &=0x000000FF; - break; - - case OP_SEX16: - if( opStack[qvm->SP]._int & 0x8000 ) - opStack[qvm->SP]._int |=0xFFFF0000; - else - opStack[qvm->SP]._int &=0x0000FFFF; - break; - - case OP_NEGI: - opStack[qvm->SP]._int = -opStack[qvm->SP]._int; - break; - - case OP_ADD: - opStack[qvm->SP - 1]._int += opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_SUB: - opStack[qvm->SP - 1]._int -= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_DIVI: - opStack[qvm->SP - 1]._int /= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_DIVU: - opStack[qvm->SP - 1]._uint /= opStack[qvm->SP]._uint; - qvm->SP--; - break; - - case OP_MODI: - opStack[qvm->SP - 1]._int %= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_MODU: - opStack[qvm->SP - 1]._uint %= opStack[qvm->SP]._uint; - qvm->SP--; - break; - - case OP_MULI: - opStack[qvm->SP - 1]._int *= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_MULU: - opStack[qvm->SP - 1]._uint *= opStack[qvm->SP]._uint; - qvm->SP--; - break; - //bits - case OP_BAND: - opStack[qvm->SP - 1]._int &= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_BOR: - opStack[qvm->SP - 1]._int |= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_BXOR: - opStack[qvm->SP - 1]._int ^= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_BCOM: - opStack[qvm->SP]._int = ~opStack[qvm->SP]._int; - break; - - case OP_LSH: - opStack[qvm->SP - 1]._int <<= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_RSHI: - opStack[qvm->SP - 1]._int >>= opStack[qvm->SP]._int; - qvm->SP--; - break; - - case OP_RSHU: - opStack[qvm->SP - 1]._uint >>= opStack[qvm->SP]._uint; - qvm->SP--; - break; - //float arithmetic - case OP_NEGF: - opStack[qvm->SP]._float = -opStack[qvm->SP]._float; - break; - - case OP_ADDF: - opStack[qvm->SP - 1]._float += opStack[qvm->SP]._float; - qvm->SP--; - break; - - case OP_SUBF: - opStack[qvm->SP - 1]._float -= opStack[qvm->SP]._float; - qvm->SP--; - break; - - case OP_MULF: - opStack[qvm->SP - 1]._float *= opStack[qvm->SP]._float; - qvm->SP--; - break; - - case OP_DIVF: - opStack[qvm->SP - 1]._float /= opStack[qvm->SP]._float; - qvm->SP--; - break; - - case OP_CVIF: - opStack[qvm->SP]._float = opStack[qvm->SP]._int; - break; - - case OP_CVFI: - opStack[qvm->SP]._int = opStack[qvm->SP]._float; - break; - default: - QVM_RunError( qvm, "invalid opcode %2.2x at off=%8x\n", op.opcode, qvm->PC - 1 ); - QVM_StackTrace( qvm ); - Sys_Error( "invalid opcode %2.2x at off=%8x\n", op.opcode, qvm->PC - 1 ); - } - - } - while ( qvm->PC > 0 ); - - ivar = opStack[qvm->SP]._int; - qvm->PC = savePC; - qvm->SP = saveSP; - qvm->LP = saveLP; - qvm->reenter--; - return ivar; -} -/* - QVM Debug stuff -*/ -char *opcode_names[] = { - "OP_UNDEF", - - "OP_IGNORE", - - "OP_BREAK", - - "OP_ENTER", - "OP_LEAVE", - "OP_CALL", - "OP_PUSH", - "OP_POP", - - "OP_CONST", - "OP_LOCAL", - - "OP_JUMP", - - //------------------- - - "OP_EQ", - "OP_NE", - - "OP_LTI", - "OP_LEI", - "OP_GTI", - "OP_GEI", - - "OP_LTU", - "OP_LEU", - "OP_GTU", - "OP_GEU", - - "OP_EQF", - "OP_NEF", - - "OP_LTF", - "OP_LEF", - "OP_GTF", - "OP_GEF", - - //------------------- - - "OP_LOAD1", - "OP_LOAD2", - "OP_LOAD4", - "OP_STORE1", - "OP_STORE2", - "OP_STORE4", // *(stack[top-1]) = stack[yop - "OP_ARG", - "OP_BLOCK_COPY", - - //------------------- - - "OP_SEX8", - "OP_SEX16", - - "OP_NEGI", - "OP_ADD", - "OP_SUB", - "OP_DIVI", - "OP_DIVU", - "OP_MODI", - "OP_MODU", - "OP_MULI", - "OP_MULU", - - "OP_BAND", - "OP_BOR", - "OP_BXOR", - "OP_BCOM", - - "OP_LSH", - "OP_RSHI", - "OP_RSHU", - - "OP_NEGF", - "OP_ADDF", - "OP_SUBF", - "OP_DIVF", - "OP_MULF", - - "OP_CVIF", - "OP_CVFI" - }; - -void PrintInstruction( qvm_t * qvm ) -{ - qvm_instruction_t op = qvm->cs[qvm->PC]; - Con_DPrintf( "PC %8x LP %8x SP %8x\n", qvm->PC, qvm->LP, qvm->SP ); - - switch ( op.opcode ) - { - case OP_ARG: - case OP_ENTER: - case OP_LEAVE: - case OP_CONST: - case OP_LOCAL: - case OP_EQ: - case OP_NE: - case OP_LTI: - case OP_LEI: - case OP_GTI: - case OP_GEI: - case OP_LTU: - case OP_LEU: - case OP_GTU: - case OP_GEU: - case OP_EQF: - case OP_NEF: - case OP_LTF: - case OP_LEF: - case OP_GTF: - case OP_GEF: - case OP_BLOCK_COPY: - Con_DPrintf( "%10.10s %8d\n", opcode_names[op.opcode], op.parm._int ); - break; - - default: - Con_DPrintf( "%10.10s\n", opcode_names[op.opcode] ); - break; - - } - -} - -#endif /* USE_PR2 */ - -#endif // !CLIENTONLY diff --git a/src/pr2_vm.h b/src/pr2_vm.h deleted file mode 100644 index 9cd030128..000000000 --- a/src/pr2_vm.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * QW262 - * Copyright (C) 2004 [sd] angel - * - * 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 __PR2_VM_H__ -#define __PR2_VM_H__ - -#define SAFE_QVM -#define QVM_RUNAWAY_PROTECTION -#define QVM_DATA_PROTECTION -#define QVM_PROFILE - -#ifdef _WIN32 -#define EXPORT_FN __cdecl -#else -#define EXPORT_FN -#endif - -#define OPSTACKSIZE 0x100 -#define MAX_PROC_CALL 100 -#define MAX_vmMain_Call 100 -#define MAX_CYCLES 3000000 - -// this gives gcc warnings unfortunatelly -//#define VM_POINTER(base,mask,x) ((x)?(void*)((char *)base+((x)&mask)):NULL) - -// #define VM_POINTER(base,mask,x) ((void*)((char *)base+((x)&mask))) -void* VM_POINTER(byte* base, uintptr_t mask, intptr_t offset); - -// meag: can leave this right now only because it is only used to return pointers to edicts -#define POINTER_TO_VM(base,mask,x) ((x)?(intptr_t)((char *)(x) - (char*)base)&mask:0) - -typedef union pr2val_s -{ - stringptr_t string; - float _float; - intptr_t _int; -} pr2val_t; - -typedef intptr_t (EXPORT_FN *sys_call_t) (intptr_t arg, ...); -typedef int (*sys_callex_t) (byte *data, unsigned int mask, int fn, pr2val_t* arg); - -typedef enum vm_type_e -{ - VM_NONE, - VM_NATIVE, - VM_BYTECODE -} vm_type_t; - - -typedef struct vm_s { - // common - vm_type_t type; - char name[MAX_QPATH]; - // shared - void *hInst; - sys_call_t syscall; - - // native - intptr_t (*vmMain) (int command, int arg0, int arg1, int arg2, int arg3, - int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, - int arg10, int arg11); - - // whether or not pr2 references should be enabled (set on GAME_INIT) - qbool pr2_references; -} vm_t ; - -typedef enum -{ - OP_UNDEF, - - OP_IGNORE, - - OP_BREAK, - - OP_ENTER, - OP_LEAVE, - OP_CALL, - OP_PUSH, - OP_POP, - - OP_CONST, - OP_LOCAL, - - OP_JUMP, - - //------------------- - - OP_EQ, - OP_NE, - - OP_LTI, - OP_LEI, - OP_GTI, - OP_GEI, - - OP_LTU, - OP_LEU, - OP_GTU, - OP_GEU, - - OP_EQF, - OP_NEF, - - OP_LTF, - OP_LEF, - OP_GTF, - OP_GEF, - - //------------------- - - OP_LOAD1, - OP_LOAD2, - OP_LOAD4, - OP_STORE1, - OP_STORE2, - OP_STORE4, // *(stack[top-1]) = stack[yop - OP_ARG, - OP_BLOCK_COPY, - - //------------------- - - OP_SEX8, - OP_SEX16, - - OP_NEGI, - OP_ADD, - OP_SUB, - OP_DIVI, - OP_DIVU, - OP_MODI, - OP_MODU, - OP_MULI, - OP_MULU, - - OP_BAND, - OP_BOR, - OP_BXOR, - OP_BCOM, - - OP_LSH, - OP_RSHI, - OP_RSHU, - - OP_NEGF, - OP_ADDF, - OP_SUBF, - OP_DIVF, - OP_MULF, - - OP_CVIF, - OP_CVFI -} opcode_t; - -typedef union { - int _int; - unsigned int _uint; - float _float; -} qvm_parm_type_t; - - -typedef struct { - opcode_t opcode; - qvm_parm_type_t parm; -} qvm_instruction_t; - -typedef struct symbols_s -{ - int off; - int seg; - struct symbols_s *next; - char name[1]; -}symbols_t; - -typedef struct { - // segments - qvm_instruction_t *cs; - unsigned char *ds; // DATASEG + LITSEG + BSSSEG - unsigned char *ss; // q3asm add stack at end of BSSSEG, defaultsize = 0x10000 - - // pointer registers - int PC; // program counter, points to cs, goes up - int SP; // operation stack pointer, initially 0, goes up index of opStack in QVM_Exec - int LP; // subroutine stack/local vars space, initially points to end of ss - - // status - int len_cs; // size of cs - int len_ds; // size of ds align up to power of 2 - int ds_mask; // bit mask of len_ds - int len_ss; // size of ss - - int cycles; // command cicles executed - int reenter; - symbols_t* sym_info; - sys_callex_t syscall; -} qvm_t; - - -/* -======================================================================== - -QVM files - -======================================================================== -*/ - -#define VM_MAGIC 0x12721444 -typedef struct -{ - int vmMagic; - - int instructionCount; - - int codeOffset; - int codeLength; - - int dataOffset; - int dataLength; - int litLength; // ( dataLength - litLength ) should be byteswapped on load - int bssLength; // zero filled memory appended to datalength -} vmHeader_t; - -typedef enum -{ - CODESEG, - DATASEG, // initialized 32 bit data, will be byte swapped - LITSEG, // strings - BSSSEG, // 0 filled - NUM_SEGMENTS -} segmentName_t; - - -extern char* opcode_names[]; -extern void VM_Unload(vm_t *vm); -vm_t* VM_Load(vm_t *vm, vm_type_t type, char *name,sys_call_t syscall,sys_callex_t syscallex); -extern intptr_t VM_Call(vm_t *vm, int /*command*/, int /*arg0*/, int , int , int , int , int , - int , int , int , int , int , int /*arg11*/); -void QVM_StackTrace( qvm_t * qvm ); -void VM_PrintInfo( vm_t * vm); - -#endif /* !__PR2_VM_H__ */ diff --git a/src/pr_cmds.c b/src/pr_cmds.c index c70e9b299..9be324511 100644 --- a/src/pr_cmds.c +++ b/src/pr_cmds.c @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static tokenizecontext_t pr1_tokencontext; #define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) -#define RETURN_STRING(s) (PR1_SetString(&((int *)pr_globals)[OFS_RETURN], s)) +#define RETURN_STRING(s) (PR1_SetString(&((int *)pr_globals)[OFS_RETURN], s)) /* =============================================================================== @@ -127,7 +127,7 @@ void PF_setorigin (void) e = G_EDICT(OFS_PARM0); org = G_VECTOR(OFS_PARM1); - VectorCopy (org, e->v.origin); + VectorCopy (org, e->v->origin); SV_AntilagReset (e); SV_LinkEdict (e, false); } @@ -150,9 +150,9 @@ void PF_setsize (void) e = G_EDICT(OFS_PARM0); min = G_VECTOR(OFS_PARM1); max = G_VECTOR(OFS_PARM2); - VectorCopy (min, e->v.mins); - VectorCopy (max, e->v.maxs); - VectorSubtract (max, min, e->v.size); + VectorCopy (min, e->v->mins); + VectorCopy (max, e->v->maxs); + VectorSubtract (max, min, e->v->size); SV_LinkEdict (e, false); } @@ -182,33 +182,33 @@ static void PF_setmodel (void) PR_RunError ("PF_setmodel: no precache: %s\n", m); ok: - e->v.model = G_INT(OFS_PARM1); - e->v.modelindex = i; + e->v->model = G_INT(OFS_PARM1); + e->v->modelindex = i; // if it is an inline model, get the size information for it if (m[0] == '*') { mod = CM_InlineModel (m); - VectorCopy (mod->mins, e->v.mins); - VectorCopy (mod->maxs, e->v.maxs); - VectorSubtract (mod->maxs, mod->mins, e->v.size); + VectorCopy (mod->mins, e->v->mins); + VectorCopy (mod->maxs, e->v->maxs); + VectorSubtract (mod->maxs, mod->mins, e->v->size); SV_LinkEdict (e, false); } else if (pr_nqprogs) { // hacks to make NQ progs happy - if (!strcmp(PR1_GetString(e->v.model), "maps/b_explob.bsp")) + if (!strcmp(PR1_GetString(e->v->model), "maps/b_explob.bsp")) { - VectorClear (e->v.mins); - VectorSet (e->v.maxs, 32, 32, 64); + VectorClear (e->v->mins); + VectorSet (e->v->maxs, 32, 32, 64); } else { // FTE does this, so we do, too; I'm not sure if it makes a difference - VectorSet (e->v.mins, -16, -16, -16); - VectorSet (e->v.maxs, 16, 16, 16); + VectorSet (e->v->mins, -16, -16, -16); + VectorSet (e->v->maxs, 16, 16, 16); } - VectorSubtract (e->v.maxs, e->v.mins, e->v.size); + VectorSubtract (e->v->maxs, e->v->mins, e->v->size); SV_LinkEdict (e, false); } } @@ -726,11 +726,11 @@ int PF_newcheckclient (int check) if (i == check) break; // didn't find anything else - if (ent->e->free) + if (ent->e.free) continue; - if (ent->v.health <= 0) + if (ent->v->health <= 0) continue; - if ((int)ent->v.flags & FL_NOTARGET) + if ((int)ent->v->flags & FL_NOTARGET) continue; // anything that is a client, or has a client as an enemy @@ -738,7 +738,7 @@ int PF_newcheckclient (int check) } // get the PVS for the entity - VectorAdd (ent->v.origin, ent->v.view_ofs, org); + VectorAdd (ent->v->origin, ent->v->view_ofs, org); checkpvs = CM_LeafPVS (CM_PointInLeaf(org)); return i; @@ -776,7 +776,7 @@ static void PF_checkclient (void) // return check if it might be visible ent = EDICT_NUM(sv.lastcheck); - if (ent->e->free || ent->v.health <= 0) + if (ent->e.free || ent->v->health <= 0) { RETURN_EDICT(sv.edicts); return; @@ -784,7 +784,7 @@ static void PF_checkclient (void) // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT(pr_global_struct->self); - VectorAdd (self->v.origin, self->v.view_ofs, vieworg); + VectorAdd (self->v->origin, self->v->view_ofs, vieworg); l = CM_Leafnum(CM_PointInLeaf(vieworg)) - 1; if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) ) { @@ -1228,15 +1228,15 @@ static void PF_findradius (void) for (i = 0; i < numtouch; i++) { ent = touchlist[i]; - if (ent->v.solid == SOLID_NOT) + if (ent->v->solid == SOLID_NOT) continue; // FIXME? for (j = 0; j < 3; j++) - eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j]) * 0.5); + eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j]) * 0.5); if (DotProduct(eorg, eorg) > rad_2) continue; - ent->v.chain = EDICT_TO_PROG(chain); + ent->v->chain = EDICT_TO_PROG(chain); chain = ent; } @@ -1315,7 +1315,7 @@ void PF_Find (void) for (e++ ; e < sv.num_edicts ; e++) { ed = EDICT_NUM(e); - if (ed->e->free) + if (ed->e.free) continue; t = E_STRING(ed,f); if (!t) @@ -1457,7 +1457,7 @@ void PF_walkmove (void) yaw = G_FLOAT(OFS_PARM0); dist = G_FLOAT(OFS_PARM1); - if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; @@ -1496,19 +1496,19 @@ void PF_droptofloor (void) ent = PROG_TO_EDICT(pr_global_struct->self); - VectorCopy (ent->v.origin, end); + VectorCopy (ent->v->origin, end); end[2] -= 256; - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, end, false, ent); if (trace.fraction == 1 || trace.allsolid) G_FLOAT(OFS_RETURN) = 0; else { - VectorCopy (trace.endpos, ent->v.origin); + VectorCopy (trace.endpos, ent->v->origin); SV_LinkEdict (ent, false); - ent->v.flags = (int)ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(trace.e.ent); G_FLOAT(OFS_RETURN) = 1; } } @@ -1624,7 +1624,7 @@ void PF_nextent (void) return; } ent = EDICT_NUM(i); - if (!ent->e->free) + if (!ent->e.free) { RETURN_EDICT(ent); return; @@ -1658,9 +1658,9 @@ void PF_changeyaw (void) float ideal, current, move, speed; ent = PROG_TO_EDICT(pr_global_struct->self); - current = anglemod( ent->v.angles[1] ); - ideal = ent->v.ideal_yaw; - speed = ent->v.yaw_speed; + current = anglemod( ent->v->angles[1] ); + ideal = ent->v->ideal_yaw; + speed = ent->v->yaw_speed; if (current == ideal) return; @@ -1686,7 +1686,7 @@ void PF_changeyaw (void) move = -speed; } - ent->v.angles[1] = anglemod (current + move); + ent->v->angles[1] = anglemod (current + move); } /* @@ -1786,7 +1786,7 @@ static void NQP_Skip (int count) nqp_buf.cursize -= count; } -static void NQP_Process(void) +static void NQP_Process (void) { int cmd; @@ -1830,16 +1830,13 @@ static void NQP_Process(void) else if (cmd == nq_svc_setview) { if (nqp_buf.cursize < 3) goto waitformore; - // nq_svc_setview - NQP_Skip(3); // TODO: make an extension for this + NQP_Skip (3); // TODO: make an extension for this } - else if (cmd == svc_updatefrags) { + else if (cmd == svc_updatefrags) nqp_expect = 4; - } else if (cmd == nq_svc_updatecolors) { - if (nqp_buf.cursize < 3) { + if (nqp_buf.cursize < 3) goto waitformore; - } MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); MSG_WriteByte (&sv.reliable_datagram, nqp_buf_data[1]); MSG_WriteString (&sv.reliable_datagram, "topcolor"); @@ -1853,14 +1850,12 @@ static void NQP_Process(void) else if (cmd == nq_svc_updatename) { int slot; byte *p; - if (nqp_buf.cursize < 3) { + if (nqp_buf.cursize < 3) goto waitformore; - } slot = nqp_buf_data[1]; p = (byte *)memchr (nqp_buf_data + 2, 0, nqp_buf.cursize - 2); - if (!p) { + if (!p) goto waitformore; - } MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); MSG_WriteByte (&sv.reliable_datagram, slot); MSG_WriteString (&sv.reliable_datagram, "name"); @@ -1875,97 +1870,84 @@ static void NQP_Process(void) NQP_Skip ((p - nqp_buf_data) + 1); } else if (cmd == svc_cdtrack) { - if (nqp_buf.cursize < 3) { + if (nqp_buf.cursize < 3) goto waitformore; - } NQP_Flush (2); NQP_Skip (1); } else if (cmd == svc_finale) { byte *p = (byte *)memchr (nqp_buf_data + 1, 0, nqp_buf.cursize - 1); - if (!p) { + if (!p) goto waitformore; - } nqp_expect = (p - nqp_buf_data) + 1; } else if (cmd == svc_intermission) { int i; NQP_Flush (1); - for (i = 0; i < 3; i++) { - MSG_WriteCoord(&sv.reliable_datagram, svs.clients[0].edict->v.origin[i]); - } - for (i = 0; i < 3; i++) { - MSG_WriteAngle(&sv.reliable_datagram, svs.clients[0].edict->v.angles[i]); - } + for (i = 0; i < 3; i++) + MSG_WriteCoord (&sv.reliable_datagram, svs.clients[0].edict->v->origin[i]); + for (i = 0; i < 3; i++) + MSG_WriteAngle (&sv.reliable_datagram, svs.clients[0].edict->v->angles[i]); } else if (cmd == nq_svc_cutscene) { byte *p = (byte *)memchr (nqp_buf_data + 1, 0, nqp_buf.cursize - 1); - if (!p) { + if (!p) goto waitformore; - } MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); MSG_WriteString (&sv.reliable_datagram, "//cutscene\n"); // ZQ extension NQP_Skip (p - nqp_buf_data + 1); } - else if (cmd == svc_temp_entity) { + else if (nqp_buf_data[0] == svc_temp_entity) { if (nqp_buf.cursize < 2) break; - switch (nqp_buf_data[1]) { - case TE_SPIKE: - case TE_SUPERSPIKE: - case TE_EXPLOSION: - case TE_TAREXPLOSION: - case TE_WIZSPIKE: - case TE_KNIGHTSPIKE: - case TE_LAVASPLASH: - case TE_TELEPORT: - if (nqp_buf.cursize < 2 + 3 * msg_coordsize) { - goto waitformore; - } - NQP_Flush(2 + 3 * msg_coordsize); - break; - case TE_GUNSHOT: - if (nqp_buf.cursize < 2 + 3 * msg_coordsize) { - goto waitformore; - } - NQP_Flush(2); - MSG_WriteByte(&sv.reliable_datagram, 1); - NQP_Flush(3 * msg_coordsize); - break; +switch (nqp_buf_data[1]) { + case TE_SPIKE: + case TE_SUPERSPIKE: + case TE_EXPLOSION: + case TE_TAREXPLOSION: + case TE_WIZSPIKE: + case TE_KNIGHTSPIKE: + case TE_LAVASPLASH: + case TE_TELEPORT: + nqp_expect = 8; + break; + case TE_GUNSHOT: + if (nqp_buf.cursize < 8) + goto waitformore; + NQP_Flush (2); + MSG_WriteByte (&sv.reliable_datagram, 1); + NQP_Flush (6); + break; - case TE_LIGHTNING1: - case TE_LIGHTNING2: - case TE_LIGHTNING3: - if (nqp_buf.cursize < 2 + 2 + 6 * msg_coordsize) { - goto waitformore; - } - NQP_Flush(4 + 6 * msg_coordsize); - break; - case NQ_TE_BEAM: - { - int entnum; - if (nqp_buf.cursize < 4 + 6 * msg_coordsize) { - goto waitformore; - } - MSG_WriteByte(&sv.reliable_datagram, svc_temp_entity); - MSG_WriteByte(&sv.reliable_datagram, TE_LIGHTNING1); - entnum = nqp_buf_data[2] + nqp_buf_data[3] * 256; - if ((unsigned int)entnum > 1023) - entnum = 0; - MSG_WriteShort(&sv.reliable_datagram, (short)(entnum - 1288)); - NQP_Skip(4); - NQP_Flush(6 * msg_coordsize); - break; - } + case TE_LIGHTNING1: + case TE_LIGHTNING2: + case TE_LIGHTNING3: + nqp_expect = 16; + break; + case NQ_TE_BEAM: + { int entnum; + if (nqp_buf.cursize < 16) + goto waitformore; + MSG_WriteByte (&sv.reliable_datagram, svc_temp_entity); + MSG_WriteByte (&sv.reliable_datagram, TE_LIGHTNING1); + entnum = nqp_buf_data[2] + nqp_buf_data[3]*256; + if ((unsigned int)entnum > 1023) + entnum = 0; + MSG_WriteShort (&sv.reliable_datagram, (short)(entnum - 1288)); + NQP_Skip (4); + nqp_expect = 12; + break; + } + + case NQ_TE_EXPLOSION2: + nqp_expect = 10; + break; + default: + Con_Printf ("WARNING: progs.dat sent an unsupported svc_temp_entity: %i\n", nqp_buf_data[1]); + goto ignore; +} - case NQ_TE_EXPLOSION2: - // TODO: Convert to TE_BLOOD (2 + 3 * coord + 2 bytes) - break; - default: - Con_Printf("WARNING: progs.dat sent an unsupported svc_temp_entity: %i\n", nqp_buf_data[1]); - goto ignore; - } } else { Con_Printf ("WARNING: progs.dat sent an unsupported svc: %i\n", cmd); @@ -2236,16 +2218,16 @@ void PF_makestatic (void) s = &sv.static_entities[sv.static_entity_count]; memset(s, 0, sizeof(sv.static_entities[0])); s->number = sv.static_entity_count + 1; - s->modelindex = SV_ModelIndex(PR_GetEntityString(ent->v.model)); + s->modelindex = SV_ModelIndex(PR_GetEntityString(ent->v->model)); if (!s->modelindex) { ED_Free (ent); return; } - s->frame = ent->v.frame; - s->colormap = ent->v.colormap; - s->skinnum = ent->v.skin; - VectorCopy(ent->v.origin, s->origin); - VectorCopy(ent->v.angles, s->angles); + s->frame = ent->v->frame; + s->colormap = ent->v->colormap; + s->skinnum = ent->v->skin; + VectorCopy(ent->v->origin, s->origin); + VectorCopy(ent->v->angles, s->angles); ++sv.static_entity_count; // throw the entity away now @@ -2854,4 +2836,4 @@ void PR_InitBuiltins (void) } } -#endif // CLIENTONLY +#endif // CLIENTONLY \ No newline at end of file diff --git a/src/pr_edict.c b/src/pr_edict.c index 3ac261d11..2115f64d5 100644 --- a/src/pr_edict.c +++ b/src/pr_edict.c @@ -92,9 +92,9 @@ Sets everything to NULL */ void ED_ClearEdict (edict_t *e) { - memset(&e->v, 0, pr_edict_size - sizeof(edict_t) + sizeof(entvars_t)); - e->e->lastruntime = 0; - e->e->free = false; + memset(e->v, 0, pr_edict_size); + e->e.lastruntime = 0; + e->e.free = false; PR_ClearEdict(e); } @@ -119,7 +119,7 @@ edict_t *ED_Alloc (void) e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy - if (e->e->free && (e->e->freetime < 2 || sv.time - e->e->freetime > 0.5)) + if (e->e.free && (e->e.freetime < 2 || sv.time - e->e.freetime > 0.5)) { ED_ClearEdict(e); return e; @@ -156,21 +156,21 @@ void ED_Free (edict_t *ed) { SV_UnlinkEdict (ed); // unlink from world bsp - ed->e->free = true; - ed->v.model = 0; - ed->v.takedamage = 0; - ed->v.modelindex = 0; - ed->v.colormap = 0; - ed->v.skin = 0; - ed->v.frame = 0; - ed->v.health = 0; - ed->v.classname = 0; - VectorClear (ed->v.origin); - VectorClear (ed->v.angles); - ed->v.nextthink = -1; - ed->v.solid = 0; - - ed->e->freetime = sv.time; + ed->e.free = true; + ed->v->model = 0; + ed->v->takedamage = 0; + ed->v->modelindex = 0; + ed->v->colormap = 0; + ed->v->skin = 0; + ed->v->frame = 0; + ed->v->health = 0; + ed->v->classname = 0; + VectorClear (ed->v->origin); + VectorClear (ed->v->angles); + ed->v->nextthink = -1; + ed->v->solid = 0; + + ed->e.freetime = sv.time; } //=========================================================================== @@ -324,7 +324,7 @@ eval_t *PR1_GetEdictFieldValue(edict_t *ed, char *field) if (!def) return NULL; - return (eval_t *)((char *)&ed->v + def->ofs*4); + return (eval_t *)((char *)ed->v + def->ofs*4); } /* @@ -500,7 +500,7 @@ void ED_Print (edict_t *ed) char *name; int type; - if (ed->e->free) + if (ed->e.free) { Con_Printf ("FREE\n"); return; @@ -513,7 +513,7 @@ void ED_Print (edict_t *ed) if (name[strlen(name)-2] == '_') continue; // skip _x, _y, _z vars - v = (int *)((char *)&ed->v + d->ofs*4); + v = (int *)((char *)ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; @@ -554,7 +554,7 @@ void ED_Write (FILE *f, edict_t *ed) fprintf (f, "{\n"); - if (ed->e->free) + if (ed->e.free) { fprintf (f, "}\n"); return; @@ -567,7 +567,7 @@ void ED_Write (FILE *f, edict_t *ed) if (name[strlen(name)-2] == '_') continue; // skip _x, _y, _z vars - v = (int *)((char *)&ed->v + d->ofs*4); + v = (int *)((char *)ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; @@ -653,14 +653,14 @@ void ED_Count (void) for (i=0 ; ie->free) + if (ent->e.free) continue; active++; - if (ent->v.solid) + if (ent->v->solid) solid++; - if (ent->v.model) + if (ent->v->model) models++; - if (ent->v.movetype == MOVETYPE_STEP) + if (ent->v->movetype == MOVETYPE_STEP) step++; } @@ -936,12 +936,12 @@ const char *ED_ParseEdict (const char *data, edict_t *ent) snprintf (com_token, MAX_COM_TOKEN, "0 %s 0", temp); } - if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) + if (!ED_ParseEpair ((void *)ent->v, key, com_token)) SV_Error ("ED_ParseEdict: parse error"); } if (!init) - ent->e->free = true; + ent->e.free = true; return data; } @@ -991,16 +991,16 @@ void ED_LoadFromFile (const char *data) // remove things from different skill levels or deathmatch if ((int)deathmatch.value) { - if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) + if (((int)ent->v->spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { ED_Free (ent); inhibit++; continue; } } - else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) - || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) - || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) + else if ((current_skill == 0 && ((int)ent->v->spawnflags & SPAWNFLAG_NOT_EASY)) + || (current_skill == 1 && ((int)ent->v->spawnflags & SPAWNFLAG_NOT_MEDIUM)) + || (current_skill >= 2 && ((int)ent->v->spawnflags & SPAWNFLAG_NOT_HARD)) ) { ED_Free (ent); inhibit++; @@ -1010,7 +1010,7 @@ void ED_LoadFromFile (const char *data) // // immediately call spawn function // - if (!ent->v.classname) + if (!ent->v->classname) { Con_Printf ("No classname for:\n"); ED_Print (ent); @@ -1019,7 +1019,7 @@ void ED_LoadFromFile (const char *data) } // look for the spawn function - func = ED_FindFunction ( PR1_GetString(ent->v.classname) ); + func = ED_FindFunction ( PR1_GetString(ent->v->classname) ); if (!func) { @@ -1187,7 +1187,7 @@ void PR1_LoadProgs (void) pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); pr_globals = (float *)pr_global_struct; - pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); + pr_edict_size = progs->entityfields * 4; // byte swap the lumps for (i = 0; i < progs->numstatements; i++) @@ -1232,7 +1232,7 @@ void PR1_LoadProgs (void) void PR1_InitProg(void) { - sv.edicts = (edict_t*) Hunk_AllocName (MAX_EDICTS * pr_edict_size, "edicts"); + sv.game_edicts = (entvars_t*) Hunk_AllocName (MAX_EDICTS * pr_edict_size, "edicts"); sv.max_edicts = MAX_EDICTS; } @@ -1260,15 +1260,14 @@ edict_t *EDICT_NUM(int n) { if (n < 0 || n >= sv.max_edicts) SV_Error ("EDICT_NUM: bad number %i", n); - return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size); + return &sv.edicts[n]; } int NUM_FOR_EDICT(edict_t *e) { int b; - b = (byte *)e - (byte *)sv.edicts; - b /= pr_edict_size; + b = e->e.entnum; if (b < 0 || b >= sv.num_edicts) SV_Error ("NUM_FOR_EDICT: bad pointer"); diff --git a/src/pr_exec.c b/src/pr_exec.c index 7c07fe392..1a302349d 100644 --- a/src/pr_exec.c +++ b/src/pr_exec.c @@ -546,11 +546,11 @@ void PR_ExecuteProgram (func_t fnum) case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FNC: // pointers - ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr = (eval_t *)((byte *)sv.game_edicts + b->_int); ptr->_int = a->_int; break; case OP_STOREP_V: - ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr = (eval_t *)((byte *)sv.game_edicts + b->_int); ptr->vector[0] = a->vector[0]; ptr->vector[1] = a->vector[1]; ptr->vector[2] = a->vector[2]; @@ -563,7 +563,7 @@ void PR_ExecuteProgram (func_t fnum) #endif if (ed == (edict_t *)sv.edicts && sv.state == ss_active) PR_RunError ("assignment to world entity"); - c->_int = (byte *)((int *)&ed->v + PR_FIELDOFS(b->_int)) - (byte *)sv.edicts; + c->_int = (byte *)((int *)ed->v + PR_FIELDOFS(b->_int)) - (byte *)sv.game_edicts; break; case OP_LOAD_F: @@ -578,7 +578,7 @@ void PR_ExecuteProgram (func_t fnum) //need for checking 'cmd mmode player N', if N >= 0x10000000 =(signed)=> negative if (b->_int >= 0) { - a = (eval_t *)((int *)&ed->v + PR_FIELDOFS(b->_int)); + a = (eval_t *)((int *)ed->v + PR_FIELDOFS(b->_int)); c->_int = a->_int; } else @@ -590,7 +590,7 @@ void PR_ExecuteProgram (func_t fnum) #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range #endif - a = (eval_t *)((int *)&ed->v + PR_FIELDOFS(b->_int)); + a = (eval_t *)((int *)ed->v + PR_FIELDOFS(b->_int)); c->vector[0] = a->vector[0]; c->vector[1] = a->vector[1]; c->vector[2] = a->vector[2]; @@ -653,12 +653,12 @@ void PR_ExecuteProgram (func_t fnum) case OP_STATE: ed = PROG_TO_EDICT(pr_global_struct->self); - ed->v.nextthink = pr_global_struct->time + 0.1; - if (a->_float != ed->v.frame) + ed->v->nextthink = pr_global_struct->time + 0.1; + if (a->_float != ed->v->frame) { - ed->v.frame = a->_float; + ed->v->frame = a->_float; } - ed->v.think = b->function; + ed->v->think = b->function; break; default: diff --git a/src/progs.h b/src/progs.h index 8751ced78..00055292b 100644 --- a/src/progs.h +++ b/src/progs.h @@ -65,12 +65,8 @@ typedef struct sv_edict_s typedef struct edict_s { - sv_edict_t *e; // server side part of the edict_t, - // basically we can get rid of this pointer at all, since we can access it via sv.sv_edicts[num] - // but this way it more friendly, I think. - - entvars_t v; // C exported fields from progs - // other fields from progs come immediately after + sv_edict_t e; // server side part of the edict_t + entvars_t *v; // C exported fields from progs } edict_t; //============================================================================ @@ -142,25 +138,25 @@ void ED_LoadFromFile (const char *data); edict_t *EDICT_NUM(int n); int NUM_FOR_EDICT(edict_t *e); -#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) +#define NEXT_EDICT(e) ((edict_t *)((byte *)(e) + sizeof(edict_t))) -#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) -#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) +#define EDICT_TO_PROG(e) ((byte *)(e)->v - (byte *)sv.game_edicts) +#define PROG_TO_EDICT(e) (&sv.edicts[(e)/pr_edict_size]) //============================================================================ #define G_FLOAT(o) (pr_globals[o]) #define G_INT(o) (*(int *)&pr_globals[o]) -#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) +#define G_EDICT(o) (&sv.edicts[(*(int *)&pr_globals[o])/pr_edict_size]) #define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) #define G_VECTOR(o) (&pr_globals[o]) #define G_STRING(o) (PR1_GetString(*(string_t *)&pr_globals[o])) #define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) -#define E_FLOAT(e,o) (((float*)&e->v)[o]) -#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) -#define E_VECTOR(e,o) (&((float*)&e->v)[o]) -#define E_STRING(e,o) (PR1_GetString(*(string_t *)&((float*)&e->v)[PR_FIELDOFS(o)])) +#define E_FLOAT(e,o) (((float*)e->v)[o]) +#define E_INT(e,o) (*(int *)&((float*)e->v)[o]) +#define E_VECTOR(e,o) (&((float*)e->v)[o]) +#define E_STRING(e,o) (PR1_GetString(*(string_t *)&((float*)e->v)[PR_FIELDOFS(o)])) typedef void (*builtin_t) (void); extern builtin_t *pr_builtins; @@ -188,8 +184,8 @@ extern int fofs_visibility; extern int fofs_hide_players; extern int fofs_teleported; -#define EdictFieldFloat(ed, fieldoffset) ((eval_t *)((byte *)&(ed)->v + (fieldoffset)))->_float -#define EdictFieldVector(ed, fieldoffset) ((eval_t *)((byte *)&(ed)->v + (fieldoffset)))->vector +#define EdictFieldFloat(ed, fieldoffset) ((eval_t *)((byte *)(ed)->v + (fieldoffset)))->_float +#define EdictFieldVector(ed, fieldoffset) ((eval_t *)((byte *)(ed)->v + (fieldoffset)))->vector void PR_RunError (char *error, ...); @@ -233,7 +229,7 @@ qbool PR1_ClientCmd(void); #define PR1_GameSetNewParms() PR_ExecuteProgram(PR_GLOBAL(SetNewParms)) #define PR1_GameStartFrame() PR_ExecuteProgram (PR_GLOBAL(StartFrame)) #define PR1_ClientKill() PR_ExecuteProgram (PR_GLOBAL(ClientKill)) -#define PR1_UserInfoChanged() (0) // PR1 does not really have it, +#define PR1_UserInfoChanged(after) (0) // PR1 does not really have it, // we have mod_UserInfo_Changed but it is slightly different. #define PR1_LoadEnts ED_LoadFromFile #define PR1_EdictThink PR_ExecuteProgram diff --git a/src/q_platform.h b/src/q_platform.h new file mode 100644 index 000000000..e0b3aac4a --- /dev/null +++ b/src/q_platform.h @@ -0,0 +1,466 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code 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. + +Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef __Q_PLATFORM_H +#define __Q_PLATFORM_H + +// this is for determining if we have an asm version of a C function +#define idx64 0 + +#ifdef Q3_VM + +#define idx386 0 +#define idppc 0 +#define idppc_altivec 0 +#define idsparc 0 + +#else + +#if (defined _M_IX86 || defined __i386__) && !defined(C_ONLY) +#define idx386 1 +#else +#define idx386 0 +#endif + +#if (defined(powerc) || defined(powerpc) || defined(ppc) || \ + defined(__ppc) || defined(__ppc__)) && !defined(C_ONLY) +#define idppc 1 +#if defined(__VEC__) +#define idppc_altivec 1 +#ifdef MACOS_X // Apple's GCC does this differently than the FSF. +#define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ + (vector unsigned char) (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) +#else +#define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ + (vector unsigned char) {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p} +#endif +#else +#define idppc_altivec 0 +#endif +#else +#define idppc 0 +#define idppc_altivec 0 +#endif + +#if defined(__sparc__) && !defined(C_ONLY) +#define idsparc 1 +#else +#define idsparc 0 +#endif + +#endif + +#ifndef __ASM_I386__ // don't include the C bits if included from qasm.h + +// for windows fastcall option +#define QDECL + +//================================================================= WIN64/32 === + +#if defined(_WIN64) || defined(__WIN64__) + +#undef idx64 +#define idx64 1 + +#undef QDECL +#define QDECL __cdecl + +#if defined( _MSC_VER ) +#define OS_STRING "win_msvc64" +#elif defined __MINGW64__ +#define OS_STRING "win_mingw64" +#else +#define OS_STRING "win64" +#endif + +#define ID_INLINE static __inline +#define PATH_SEPARATOR "\\" + +#if defined( __WIN64__ ) +#define ARCH_STRING "x86_64" +#elif defined _M_ALPHA +#define ARCH_STRING "AXP" +#endif + +#define Q3_LITTLE_ENDIAN + +#define DLEXT "dll" + +#elif defined(_WIN32) || defined(__WIN32__) + +#undef QDECL +#define QDECL __cdecl + +#if defined( _MSC_VER ) +#define OS_STRING "win_msvc" +#elif defined __MINGW32__ +#define OS_STRING "win_mingw" +#endif + +#define ID_INLINE static __inline +#define PATH_SEPARATOR "\\" + +#if defined( _M_IX86 ) || defined( __i386__ ) +#define ARCH_STRING "x86" +#elif defined _M_ALPHA +#define ARCH_STRING "AXP" +#endif + +#define Q3_LITTLE_ENDIAN + +#define DLEXT "dll" + +#endif + +//============================================================== MAC OS X === + +#if defined(MACOS_X) || defined(__APPLE_CC__) + +// make sure this is defined, just for sanity's sake... +#ifndef MACOS_X +#define MACOS_X +#endif + +#define OS_STRING "macosx" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#ifdef __ppc__ +#define ARCH_STRING "ppc" +#define Q3_BIG_ENDIAN +#elif defined __i386__ +#define ARCH_STRING "i386" +#define Q3_LITTLE_ENDIAN +#elif defined __x86_64__ +#undef idx64 +#define idx64 1 +#define ARCH_STRING "x86_64" +#define Q3_LITTLE_ENDIAN +#endif + +#define DLEXT "dylib" + +#endif + +//================================================================= LINUX === + +#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(ANDROID) || defined(__ANDROID__) + +#include + +#if defined(ANDROID) || defined(__ANDROID__) +#define OS_STRING "android" +#elif defined(__linux__) +#define OS_STRING "linux" +#else +#define OS_STRING "kFreeBSD" +#endif + +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#if defined __i386__ +#define ARCH_STRING "i386" +#elif defined __x86_64__ +#undef idx64 +#define idx64 1 +#define ARCH_STRING "x86_64" +#elif defined __powerpc64__ +#define ARCH_STRING "ppc64" +#elif defined __powerpc__ +#define ARCH_STRING "ppc" +#elif defined __s390__ +#define ARCH_STRING "s390" +#elif defined __s390x__ +#define ARCH_STRING "s390x" +#elif defined __ia64__ +#define ARCH_STRING "ia64" +#elif defined __alpha__ +#define ARCH_STRING "alpha" +#elif defined __sparc__ +#define ARCH_STRING "sparc" +#elif defined __arm__ +#define ARCH_STRING "arm" +#elif defined __cris__ +#define ARCH_STRING "cris" +#elif defined __hppa__ +#define ARCH_STRING "hppa" +#elif defined __mips__ +#define ARCH_STRING "mips" +#elif defined __sh__ +#define ARCH_STRING "sh" +#endif + +#if __FLOAT_WORD_ORDER == __BIG_ENDIAN +#define Q3_BIG_ENDIAN +#else +#define Q3_LITTLE_ENDIAN +#endif + +#define DLEXT "so" + +#endif + +//=================================================================== BSD === + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + +#include +#include + +#ifndef __BSD__ + #define __BSD__ +#endif + +#if defined(__FreeBSD__) +#define OS_STRING "freebsd" +#elif defined(__OpenBSD__) +#define OS_STRING "openbsd" +#elif defined(__NetBSD__) +#define OS_STRING "netbsd" +#endif + +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#ifdef __i386__ +#define ARCH_STRING "i386" +#elif defined __amd64__ +#undef idx64 +#define idx64 1 +#define ARCH_STRING "amd64" +#elif defined __axp__ +#define ARCH_STRING "alpha" +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define Q3_BIG_ENDIAN +#else +#define Q3_LITTLE_ENDIAN +#endif + +#define DLEXT "so" + +#endif + +//================================================================= SUNOS === + +#ifdef __sun + +#include +#include + +#define OS_STRING "solaris" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#ifdef __i386__ +#define ARCH_STRING "i386" +#elif defined __sparc +#define ARCH_STRING "sparc" +#endif + +#if defined( _BIG_ENDIAN ) +#define Q3_BIG_ENDIAN +#elif defined( _LITTLE_ENDIAN ) +#define Q3_LITTLE_ENDIAN +#endif + +#define DLEXT "so" + +#endif + +//================================================================== IRIX === + +#ifdef __sgi + +#define OS_STRING "irix" +#define ID_INLINE static __inline +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "mips" + +#define Q3_BIG_ENDIAN // SGI's MIPS are always big endian + +#define DLEXT "so" + +#endif + +//=============================================================== MORPHOS === + +#ifdef __MORPHOS__ + +#define OS_STRING "morphos" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "ppc" + +#define Q3_BIG_ENDIAN + +#define DLEXT "so" + +#endif + +#ifdef __CYGWIN__ +#define OS_STRING "cygwin" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "x86" + +#define Q3_LITTLE_ENDIAN + +#define DLEXT "dll" + +#endif + +#ifdef __DJGPP__ +#define OS_STRING "msdos" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "dos" + +#define Q3_LITTLE_ENDIAN + +#define DLEXT "dll" +#endif + + +#ifdef FTE_TARGET_WEB +#define OS_STRING "emscripten" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "web" + +#define Q3_LITTLE_ENDIAN + +#define DLEXT "so" +#endif + +#ifdef NACL +#define OS_STRING "nacl" +#define ID_INLINE static inline +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "web" + +#define Q3_LITTLE_ENDIAN + +#define DLEXT "so" +#endif + +//================================================================== Q3VM === + +#ifdef Q3_VM + +#define OS_STRING "q3vm" +#define ID_INLINE static +#define PATH_SEPARATOR "/" + +#define ARCH_STRING "bytecode" + +#define DLEXT "qvm" + +#endif + +//=========================================================================== + +//catch missing defines in above blocks +#if !defined( OS_STRING ) +#define ARCH_STRING "unknown" +//#error "Operating system not supported" +#endif + +#if !defined( ARCH_STRING ) +#define ARCH_STRING "unk" +//#error "Architecture not supported" +#endif + +#ifndef ID_INLINE +#define ID_INLINE static +//#error "ID_INLINE not defined" +#endif + +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR "/" +//#error "PATH_SEPARATOR not defined" +#endif + +#ifndef DLEXT +#define DLEXT "so" +//#error "DLEXT not defined" +#endif + +/* +//endianness +short ShortSwap (short l); +int LongSwap (int l); +float FloatSwap (const float *f); + +#if defined( Q3_BIG_ENDIAN ) && defined( Q3_LITTLE_ENDIAN ) +#error "Endianness defined as both big and little" +#elif defined( Q3_BIG_ENDIAN ) + +#define LittleShort(x) ShortSwap(x) +#define LittleLong(x) LongSwap(x) +#define LittleFloat(x) FloatSwap(&x) +#define BigShort +#define BigLong +#define BigFloat + +#elif defined( Q3_LITTLE_ENDIAN ) + +#define LittleShort +#define LittleLong +#define LittleFloat +#define BigShort(x) ShortSwap(x) +#define BigLong(x) LongSwap(x) +#define BigFloat(x) FloatSwap(&x) + +#elif defined( Q3_VM ) + +#define LittleShort +#define LittleLong +#define LittleFloat +#define BigShort +#define BigLong +#define BigFloat + +#else +#error "Endianness not defined" +#endif +*/ + + +//platform string +#ifdef NDEBUG +#define PLATFORM_STRING OS_STRING "-" ARCH_STRING +#else +#define PLATFORM_STRING OS_STRING "-" ARCH_STRING "-debug" +#endif + +#endif + +#endif diff --git a/src/qwsvdef.h b/src/qwsvdef.h index b8e782218..56ca74409 100644 --- a/src/qwsvdef.h +++ b/src/qwsvdef.h @@ -31,6 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "net.h" #include "crc.h" #include "sha1.h" +#include "sha3.h" #include "pmove.h" #include "version.h" #include "sv_log.h" diff --git a/src/server.h b/src/server.h index 505f13fb8..70c8cd8fc 100644 --- a/src/server.h +++ b/src/server.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "progs.h" #ifdef USE_PR2 -#include "pr2_vm.h" +#include "vm.h" #include "pr2.h" #include "g_public.h" #endif @@ -95,10 +95,10 @@ typedef struct int num_edicts; // increases towards MAX_EDICTS int num_baseline_edicts;// number of entities that have baselines - edict_t *edicts; // can NOT be array indexed, because - // edict_t is variable sized, but can - // be used to reference the world ent - sv_edict_t sv_edicts[MAX_EDICTS]; // part of the edict_t + + edict_t edicts[MAX_EDICTS]; + entvars_t *game_edicts; // can NOT be array indexed, because entvars_t is variable sized + int max_edicts; // might not MAX_EDICTS if mod allocates memory byte *pvs, *phs; // fully expanded and decompressed @@ -356,6 +356,7 @@ typedef struct client_s int rip_vip; double delay; double disable_updates_stop; // Vladis + qbool maxping_met; // set if user meets maxping requirements packet_t *packets, *last_packet; #ifdef MVD_PEXT1_HIGHLAGTELEPORT @@ -1007,6 +1008,7 @@ int Dem_CountTeamPlayers (char *t); char *quote (char *str); void CleanName_Init (void); void SV_LastScores_f (void); +void SV_LastStats_f (void); void SV_DemoList_f (void); void SV_DemoListRegex_f (void); void SV_MVDRemove_f (void); diff --git a/src/sv_ccmds.c b/src/sv_ccmds.c index 11a27f500..e85b7b5c7 100644 --- a/src/sv_ccmds.c +++ b/src/sv_ccmds.c @@ -280,8 +280,8 @@ void SV_God_f (void) if (!SV_SetPlayer ()) return; - sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; - if (!((int)sv_player->v.flags & FL_GODMODE) ) + sv_player->v->flags = (int)sv_player->v->flags ^ FL_GODMODE; + if (!((int)sv_player->v->flags & FL_GODMODE) ) SV_ClientPrintf (sv_client, PRINT_HIGH, "godmode OFF\n"); else SV_ClientPrintf (sv_client, PRINT_HIGH, "godmode ON\n"); @@ -299,14 +299,14 @@ void SV_Noclip_f (void) if (!SV_SetPlayer ()) return; - if (sv_player->v.movetype != MOVETYPE_NOCLIP) + if (sv_player->v->movetype != MOVETYPE_NOCLIP) { - sv_player->v.movetype = MOVETYPE_NOCLIP; + sv_player->v->movetype = MOVETYPE_NOCLIP; SV_ClientPrintf (sv_client, PRINT_HIGH, "noclip ON\n"); } else { - sv_player->v.movetype = MOVETYPE_WALK; + sv_player->v->movetype = MOVETYPE_WALK; SV_ClientPrintf (sv_client, PRINT_HIGH, "noclip OFF\n"); } } @@ -347,23 +347,23 @@ void SV_Give_f (void) case '7': case '8': case '9': - sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2'); + sv_player->v->items = (int)sv_player->v->items | IT_SHOTGUN<< (t[0] - '2'); break; case 's': - sv_player->v.ammo_shells = v; + sv_player->v->ammo_shells = v; break; case 'n': - sv_player->v.ammo_nails = v; + sv_player->v->ammo_nails = v; break; case 'r': - sv_player->v.ammo_rockets = v; + sv_player->v->ammo_rockets = v; break; case 'h': - sv_player->v.health = v; + sv_player->v->health = v; break; case 'c': - sv_player->v.ammo_cells = v; + sv_player->v->ammo_cells = v; break; } } @@ -379,17 +379,17 @@ void SV_Fly_f (void) if (!SV_SetPlayer ()) return; - if (sv_player->v.solid != SOLID_SLIDEBOX) + if (sv_player->v->solid != SOLID_SLIDEBOX) return; // dead don't fly - if (sv_player->v.movetype != MOVETYPE_FLY) + if (sv_player->v->movetype != MOVETYPE_FLY) { - sv_player->v.movetype = MOVETYPE_FLY; + sv_player->v->movetype = MOVETYPE_FLY; SV_ClientPrintf (sv_client, PRINT_HIGH, "flymode ON\n"); } else { - sv_player->v.movetype = MOVETYPE_WALK; + sv_player->v->movetype = MOVETYPE_WALK; SV_ClientPrintf (sv_client, PRINT_HIGH, "flymode OFF\n"); } } @@ -485,6 +485,9 @@ void SV_Map (qbool now) // check to make sure the level exists snprintf (expanded, MAX_QPATH, "maps/%s.bsp", level); + // Flush FS cache on each map change. + FS_FlushFSHash(); + if (!FS_FLocateFile(expanded, FSLFRT_IFFOUND, NULL)) { Con_Printf ("Can't find %s\n", expanded); @@ -1211,7 +1214,7 @@ void SV_Status_f (void) continue; s = NET_BaseAdrToString(cl->netchan.remote_address); Con_Printf ("%-16s %4i %5i %6i %-22s ", cl->name, (int)SV_CalcPing(cl), - (int)cl->edict->v.frags, cl->userid, (int)sv_use_dns.value ? SV_Resolve(s) : s); + (int)cl->edict->v->frags, cl->userid, (int)sv_use_dns.value ? SV_Resolve(s) : s); if (cl->realip.ip[0]) Con_Printf ("%-15s", NET_BaseAdrToString (cl->realip)); Con_Printf (cl->spectator ? (char *) "(s)" : (char *) ""); @@ -1247,7 +1250,7 @@ void SV_Status_f (void) s = NET_BaseAdrToString(cl->netchan.remote_address); Con_Printf ("%-18s %4i %5i %6s %s\n%-36s\n", cl->name, (int)SV_CalcPing(cl), - (int)cl->edict->v.frags, Q_yelltext((unsigned char*)va("%d", cl->userid)), + (int)cl->edict->v->frags, Q_yelltext((unsigned char*)va("%d", cl->userid)), cl->spectator ? " (s)" : "", (int)sv_use_dns.value ? SV_Resolve(s) : s); if (cl->realip.ip[0]) diff --git a/src/sv_demo.c b/src/sv_demo.c index 5fe426a57..47b52a6c3 100644 --- a/src/sv_demo.c +++ b/src/sv_demo.c @@ -1251,7 +1251,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t* dest) MSG_WriteFloat (&buf, sv.time); // send full levelname - MSG_WriteString (&buf, PR_GetEntityString(sv.edicts->v.message)); + MSG_WriteString (&buf, PR_GetEntityString(sv.edicts->v->message)); // send the movevars MSG_WriteFloat(&buf, movevars.gravity); @@ -1375,7 +1375,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t* dest) for (i = 0; i < sv.num_baseline_edicts; ++i) { edict_t* svent = EDICT_NUM(i); - entity_state_t* s = &svent->e->baseline; + entity_state_t* s = &svent->e.baseline; if (buf.cursize >= MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); @@ -1496,23 +1496,23 @@ void SV_MVD_SendInitialGamestate(mvddest_t* dest) flags = (DF_ORIGIN << 0) | (DF_ORIGIN << 1) | (DF_ORIGIN << 2) | (DF_ANGLES << 0) | (DF_ANGLES << 1) | (DF_ANGLES << 2) | DF_EFFECTS | DF_SKINNUM - | (ent->v.health <= 0 ? DF_DEAD : 0) - | (ent->v.mins[2] != -24 ? DF_GIB : 0) + | (ent->v->health <= 0 ? DF_DEAD : 0) + | (ent->v->mins[2] != -24 ? DF_GIB : 0) | DF_WEAPONFRAME | DF_MODEL; - VectorCopy(ent->v.origin, origin); - VectorCopy(ent->v.angles, angles); + VectorCopy(ent->v->origin, origin); + VectorCopy(ent->v->angles, angles); angles[0] *= -3; #ifdef USE_PR2 if( player->isBot ) - VectorCopy(ent->v.v_angle, angles); + VectorCopy(ent->v->v_angle, angles); #endif angles[2] = 0; // no roll angle - if (ent->v.health <= 0) + if (ent->v->health <= 0) { // don't show the corpse looking around... angles[0] = 0; - angles[1] = ent->v.angles[1]; + angles[1] = ent->v->angles[1]; angles[2] = 0; } @@ -1520,7 +1520,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t* dest) MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, flags); - MSG_WriteByte (&buf, ent->v.frame); + MSG_WriteByte (&buf, ent->v->frame); for (j = 0 ; j < 3 ; j++) if (flags & (DF_ORIGIN << j)) @@ -1531,16 +1531,16 @@ void SV_MVD_SendInitialGamestate(mvddest_t* dest) MSG_WriteAngle16 (&buf, angles[j]); if (flags & DF_MODEL) - MSG_WriteByte (&buf, ent->v.modelindex); + MSG_WriteByte (&buf, ent->v->modelindex); if (flags & DF_SKINNUM) - MSG_WriteByte (&buf, ent->v.skin); + MSG_WriteByte (&buf, ent->v->skin); if (flags & DF_EFFECTS) - MSG_WriteByte (&buf, ent->v.effects); + MSG_WriteByte (&buf, ent->v->effects); if (flags & DF_WEAPONFRAME) - MSG_WriteByte (&buf, ent->v.weaponframe); + MSG_WriteByte (&buf, ent->v->weaponframe); if (buf.cursize > MAX_MSGLEN/2) { @@ -1573,21 +1573,21 @@ void SV_MVD_SendInitialGamestate(mvddest_t* dest) memset(stats, 0, sizeof(stats)); - stats[STAT_HEALTH] = ent->v.health; - stats[STAT_WEAPON] = SV_ModelIndex(PR_GetEntityString(ent->v.weaponmodel)); - stats[STAT_AMMO] = ent->v.currentammo; - stats[STAT_ARMOR] = ent->v.armorvalue; - stats[STAT_SHELLS] = ent->v.ammo_shells; - stats[STAT_NAILS] = ent->v.ammo_nails; - stats[STAT_ROCKETS] = ent->v.ammo_rockets; - stats[STAT_CELLS] = ent->v.ammo_cells; - stats[STAT_ACTIVEWEAPON] = ent->v.weapon; + stats[STAT_HEALTH] = ent->v->health; + stats[STAT_WEAPON] = SV_ModelIndex(PR_GetEntityString(ent->v->weaponmodel)); + stats[STAT_AMMO] = ent->v->currentammo; + stats[STAT_ARMOR] = ent->v->armorvalue; + stats[STAT_SHELLS] = ent->v->ammo_shells; + stats[STAT_NAILS] = ent->v->ammo_nails; + stats[STAT_ROCKETS] = ent->v->ammo_rockets; + stats[STAT_CELLS] = ent->v->ammo_cells; + stats[STAT_ACTIVEWEAPON] = ent->v->weapon; - if (ent->v.health > 0) // viewheight for PF_DEAD & PF_GIB is hardwired - stats[STAT_VIEWHEIGHT] = ent->v.view_ofs[2]; + if (ent->v->health > 0) // viewheight for PF_DEAD & PF_GIB is hardwired + stats[STAT_VIEWHEIGHT] = ent->v->view_ofs[2]; // stuff the sigil bits into the high bits of items for sbar - stats[STAT_ITEMS] = (int) ent->v.items | ((int) PR_GLOBAL(serverflags) << 28); + stats[STAT_ITEMS] = (int) ent->v->items | ((int) PR_GLOBAL(serverflags) << 28); for (j = 0; j < MAX_CL_STATS; j++) { diff --git a/src/sv_demo_misc.c b/src/sv_demo_misc.c index 878e6ead8..644f4c89a 100644 --- a/src/sv_demo_misc.c +++ b/src/sv_demo_misc.c @@ -1050,6 +1050,84 @@ void SV_LastScores_f (void) } } +#define STATS_LIMIT_DEFAULT 10 +#define STATS_LIMIT_MAX 50 +void SV_LastStats_f (void) +{ + int limit = STATS_LIMIT_DEFAULT; + int i; + char buf[512]; + FILE *f = NULL; + char path[MAX_OSPATH]; + dir_t dir; + extern redirect_t sv_redirected; + + if (sv_redirected != RD_PACKET) + { + return; + } + + if (Cmd_Argc() > 2) + { + Con_Printf("usage: laststats []\n = '0' for last %i stats\n = 'n' for last n stats (max %i)\n = '' (empty) for last %i stats\n", STATS_LIMIT_MAX, STATS_LIMIT_MAX, STATS_LIMIT_DEFAULT); + return; + } + else if (Cmd_Argc() == 2) + { + limit = Q_atoi(Cmd_Argv(1)); + + if (limit <= 0 || limit > STATS_LIMIT_MAX) + { + limit = STATS_LIMIT_MAX; + } + } + + dir = Sys_listdir(va("%s/%s", fs_gamedir, sv_demoDir.string), sv_demoRegexp.string, + SORT_BY_DATE); + + if (!dir.numfiles) + { + Con_Printf("laststats 0:\n"); + Con_Printf("[]\n"); + return; + } + + if (limit > dir.numfiles) + { + limit = dir.numfiles; + } + + Con_Printf("laststats %i\n", limit); + Con_Printf("[\n"); + + for (i = dir.numfiles - limit; i < dir.numfiles; i++) + { + snprintf(path, MAX_OSPATH, "%s/%s/%s", fs_gamedir, sv_demoDir.string, + SV_MVDName2Txt(dir.files[i].name)); + + if ((f = fopen(path, "rt")) != NULL) + { + if (FS_FileLength(f) == 0) { + continue; + } + + if (i != dir.numfiles - limit) + { + Con_Printf(",\n"); + } + + while (fread(buf, 1, sizeof(buf)-1, f)) + { + Con_Printf("%s", (unsigned char*)buf); + memset(buf, 0, sizeof(buf)); + } + fclose(f); + } + } + + Con_Printf("\n]\n"); +} + // easyrecord helpers int Dem_CountPlayers (void) diff --git a/src/sv_demo_qtv.c b/src/sv_demo_qtv.c index 11c5bbd8f..6e8b63d9b 100644 --- a/src/sv_demo_qtv.c +++ b/src/sv_demo_qtv.c @@ -383,6 +383,7 @@ void SV_MVD_RunPendingConnections (void) QTVAM_PLAIN, QTVAM_CCITT, QTVAM_MD4, + QTVAM_SHA3_512, } authmethod_t; authmethod_t authmethod = QTVAM_NONE; start = p->inbuffer; @@ -456,6 +457,8 @@ void SV_MVD_RunPendingConnections (void) thisauth = QTVAM_CCITT; else if (!strcmp(com_token, "MD4")) thisauth = QTVAM_MD4; + else if (!strcmp(com_token, "SHA3_512")) + thisauth = QTVAM_SHA3_512; else { thisauth = QTVAM_NONE; @@ -530,6 +533,21 @@ void SV_MVD_RunPendingConnections (void) } break; + case QTVAM_SHA3_512: + { + sha3_context c; + const uint8_t *byte_hash; + char hash[SHA3_512_DIGEST_HEX_STR_SIZE] = {0}; + + sha3_Init512(&c); + sha3_Update(&c, p->challenge, strlen(p->challenge)); + sha3_Update(&c, qtv_password.string, strlen(qtv_password.string)); + byte_hash = sha3_Finalize(&c); + sha3_512_ByteToHex(hash, byte_hash); + p->hasauthed = !strcmp(password, hash); + } + break; + default: e = ("QTVSV 1\n" "PERROR: FTEQWSV bug detected.\n\n"); @@ -583,6 +601,17 @@ void SV_MVD_RunPendingConnections (void) send(p->socket, e, strlen(e), 0); continue; + case QTVAM_SHA3_512: + e = ("QTVSV 1\n" + "AUTH: SHA3_512\n" + "CHALLENGE: "); + + send(p->socket, e, strlen(e), 0); + send(p->socket, p->challenge, strlen(p->challenge), 0); + e = "\n\n"; + send(p->socket, e, strlen(e), 0); + continue; + default: e = ("QTVSV 1\n" "PERROR: FTEQWSV bug detected.\n\n"); diff --git a/src/sv_ents.c b/src/sv_ents.c index 319c604be..baafc1779 100644 --- a/src/sv_ents.c +++ b/src/sv_ents.c @@ -44,7 +44,7 @@ static qbool SV_AddNailUpdate (edict_t *ent) if ((int)sv_nailhack.value) return false; - if (ent->v.modelindex != sv_nailmodel && ent->v.modelindex != sv_supernailmodel) + if (ent->v->modelindex != sv_nailmodel && ent->v->modelindex != sv_supernailmodel) return false; if (msg_coordsize != 2) @@ -80,20 +80,20 @@ static void SV_EmitNailUpdate (sizebuf_t *msg, qbool recorder) ent = nails[n]; if (recorder) { - if (!ent->v.colormap) + if (!ent->v->colormap) { if (!((++nailcount)&255)) nailcount++; - ent->v.colormap = nailcount&255; + ent->v->colormap = nailcount&255; } - MSG_WriteByte (msg, (byte)ent->v.colormap); + MSG_WriteByte (msg, (byte)ent->v->colormap); } - x = ((int)(ent->v.origin[0] + 4096 + 1) >> 1) & 4095; - y = ((int)(ent->v.origin[1] + 4096 + 1) >> 1) & 4095; - z = ((int)(ent->v.origin[2] + 4096 + 1) >> 1) & 4095; - p = Q_rint(ent->v.angles[0]*(16.0/360.0)) & 15; - yaw = Q_rint(ent->v.angles[1]*(256.0/360.0)) & 255; + x = ((int)(ent->v->origin[0] + 4096 + 1) >> 1) & 4095; + y = ((int)(ent->v->origin[1] + 4096 + 1) >> 1) & 4095; + z = ((int)(ent->v->origin[2] + 4096 + 1) >> 1) & 4095; + p = Q_rint(ent->v->angles[0]*(16.0/360.0)) & 15; + yaw = Q_rint(ent->v->angles[1]*(256.0/360.0)) & 255; bits[0] = x; bits[1] = (x>>8) | (y<<4); @@ -355,7 +355,7 @@ static void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, size } ent = EDICT_NUM(newnum); //Con_Printf ("baseline %i\n", newnum); - SV_WriteDelta (client, &ent->e->baseline, &to->entities[newindex], msg, true); + SV_WriteDelta (client, &ent->e.baseline, &to->entities[newindex], msg, true); newindex++; continue; } @@ -393,13 +393,13 @@ static void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, size static int TranslateEffects (edict_t *ent) { - int fx = (int)ent->v.effects; + int fx = (int)ent->v->effects; if (pr_nqprogs) fx &= ~EF_MUZZLEFLASH; if (pr_nqprogs && (fx & EF_DIMLIGHT)) { - if ((int)ent->v.items & IT_QUAD) + if ((int)ent->v->items & IT_QUAD) fx |= EF_BLUE; - if ((int)ent->v.items & IT_INVULNERABILITY) + if ((int)ent->v->items & IT_INVULNERABILITY) fx |= EF_RED; } return fx; @@ -432,26 +432,26 @@ static void SV_MVD_WritePlayersToClient ( void ) dcl->parsecount = demo.parsecount; - VectorCopy(ent->v.origin, dcl->origin); - VectorCopy(ent->v.angles, dcl->angles); + VectorCopy(ent->v->origin, dcl->origin); + VectorCopy(ent->v->angles, dcl->angles); dcl->angles[0] *= -3; #ifdef USE_PR2 if( cl->isBot ) - VectorCopy(ent->v.v_angle, dcl->angles); + VectorCopy(ent->v->v_angle, dcl->angles); #endif dcl->angles[2] = 0; // no roll angle - if (ent->v.health <= 0) + if (ent->v->health <= 0) { // don't show the corpse looking around... dcl->angles[0] = 0; - dcl->angles[1] = ent->v.angles[1]; + dcl->angles[1] = ent->v->angles[1]; dcl->angles[2] = 0; } - dcl->weaponframe = ent->v.weaponframe; - dcl->frame = ent->v.frame; - dcl->skinnum = ent->v.skin; - dcl->model = ent->v.modelindex; + dcl->weaponframe = ent->v->weaponframe; + dcl->frame = ent->v->frame; + dcl->skinnum = ent->v->skin; + dcl->model = ent->v->modelindex; dcl->effects = TranslateEffects(ent); dcl->flags = 0; @@ -460,9 +460,9 @@ static void SV_MVD_WritePlayersToClient ( void ) dcl->sec = sv.time - cl->localtime; - if (ent->v.health <= 0) + if (ent->v->health <= 0) dcl->flags |= DF_DEAD; - if (ent->v.mins[2] != -24) + if (ent->v->mins[2] != -24) dcl->flags |= DF_GIB; continue; @@ -486,14 +486,14 @@ qbool SV_PlayerVisibleToClient (client_t* client, int j, byte* pvs, edict_t* sel if (cl->spectator) return false; - if (pvs && ent->e->num_leafs >= 0) { + if (pvs && ent->e.num_leafs >= 0) { // ignore if not touching a PV leaf - for (i = 0; i < ent->e->num_leafs; i++) { - if (pvs[ent->e->leafnums[i] >> 3] & (1 << (ent->e->leafnums[i] & 7))) { + for (i = 0; i < ent->e.num_leafs; i++) { + if (pvs[ent->e.leafnums[i] >> 3] & (1 << (ent->e.leafnums[i] & 7))) { break; } } - if (i == ent->e->num_leafs) { + if (i == ent->e.num_leafs) { return false; // not visible } } @@ -515,14 +515,14 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by usercmd_t cmd; int hideent = 0; int trackent = 0; - qbool hide_players = fofs_hide_players && ((eval_t *)((byte *)&(client->edict)->v + fofs_hide_players))->_int; + qbool hide_players = fofs_hide_players && ((eval_t *)((byte *)(client->edict)->v + fofs_hide_players))->_int; if (fofs_hideentity) - hideent = ((eval_t *)((byte *)&(client->edict)->v + fofs_hideentity))->_int / pr_edict_size; + hideent = ((eval_t *)((byte *)(client->edict)->v + fofs_hideentity))->_int / pr_edict_size; if (fofs_trackent) { - trackent = ((eval_t *)((byte *)&(client->edict)->v + fofs_trackent))->_int; + trackent = ((eval_t *)((byte *)(client->edict)->v + fofs_trackent))->_int; if (trackent < 1 || trackent > MAX_CLIENTS || svs.clients[trackent - 1].state != cs_spawned) trackent = 0; } @@ -538,7 +538,7 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by if (fofs_visibility) { // Presume not visible - ((eval_t *)((byte *)&(cl->edict)->v + fofs_visibility))->_int &= ~(1 << (client - svs.clients)); + ((eval_t *)((byte *)(cl->edict)->v + fofs_visibility))->_int &= ~(1 << (client - svs.clients)); } if (cl->state != cs_spawned) @@ -568,7 +568,7 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by if (fofs_visibility) { // Update flags so mods can tell what was visible - ((eval_t *)((byte *)&(ent)->v + fofs_visibility))->_int |= (1 << (client - svs.clients)); + ((eval_t *)((byte *)(ent)->v + fofs_visibility))->_int |= (1 << (client - svs.clients)); } if (j == hideent - 1) @@ -587,18 +587,18 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by pflags = PF_MSEC | PF_COMMAND; - if (ent->v.modelindex != sv_playermodel) + if (ent->v->modelindex != sv_playermodel) pflags |= PF_MODEL; for (i=0 ; i<3 ; i++) - if (ent->v.velocity[i]) + if (ent->v->velocity[i]) pflags |= PF_VELOCITY1<v.effects) + if (ent->v->effects) pflags |= PF_EFFECTS; - if (ent->v.skin || ent->v.modelindex >= 256) + if (ent->v->skin || ent->v->modelindex >= 256) pflags |= PF_SKINNUM; - if (ent->v.health <= 0) + if (ent->v->health <= 0) pflags |= PF_DEAD; - if (ent->v.mins[2] != -24) + if (ent->v->mins[2] != -24) pflags |= PF_GIB; if (cl->spectator) @@ -608,7 +608,7 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by else if (ent == self_ent) { // don't send a lot of data on personal entity pflags &= ~(PF_MSEC|PF_COMMAND); - if (ent->v.weaponframe) + if (ent->v->weaponframe) pflags |= PF_WEAPONFRAME; } @@ -647,17 +647,17 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by pflags |= pm_code << PF_PMC_SHIFT; // Z_EXT_PF_ONGROUND protocol extension - if ((int)ent->v.flags & FL_ONGROUND) + if ((int)ent->v->flags & FL_ONGROUND) pflags |= PF_ONGROUND; // Z_EXT_PF_SOLID protocol extension - if (ent->v.solid == SOLID_BBOX || ent->v.solid == SOLID_SLIDEBOX) + if (ent->v->solid == SOLID_BBOX || ent->v->solid == SOLID_SLIDEBOX) pflags |= PF_SOLID; if (pm_type == PM_LOCK && ent == self_ent) pflags |= PF_COMMAND; // send forced view angles - if (client->spec_track && client->spec_track - 1 == j && ent->v.weaponframe) + if (client->spec_track && client->spec_track - 1 == j && ent->v->weaponframe) pflags |= PF_WEAPONFRAME; MSG_WriteByte (msg, svc_playerinfo); @@ -665,17 +665,17 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by MSG_WriteShort (msg, pflags); if (client->mvdprotocolextensions1 & MVD_PEXT1_FLOATCOORDS) { - MSG_WriteLongCoord(msg, ent->v.origin[0]); - MSG_WriteLongCoord(msg, ent->v.origin[1]); - MSG_WriteLongCoord(msg, ent->v.origin[2]); + MSG_WriteLongCoord(msg, ent->v->origin[0]); + MSG_WriteLongCoord(msg, ent->v->origin[1]); + MSG_WriteLongCoord(msg, ent->v->origin[2]); } else { - MSG_WriteCoord(msg, ent->v.origin[0]); - MSG_WriteCoord(msg, ent->v.origin[1]); - MSG_WriteCoord(msg, ent->v.origin[2]); + MSG_WriteCoord(msg, ent->v->origin[0]); + MSG_WriteCoord(msg, ent->v->origin[1]); + MSG_WriteCoord(msg, ent->v->origin[2]); } - MSG_WriteByte (msg, ent->v.frame); + MSG_WriteByte (msg, ent->v->frame); if (pflags & PF_MSEC) { @@ -689,10 +689,10 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by { cmd = cl->lastcmd; - if (ent->v.health <= 0) + if (ent->v->health <= 0) { // don't show the corpse looking around... cmd.angles[0] = 0; - cmd.angles[1] = ent->v.angles[1]; + cmd.angles[1] = ent->v->angles[1]; cmd.angles[0] = 0; } @@ -702,7 +702,7 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by if (ent == self_ent) { // this is PM_LOCK, we only want to send view angles - VectorCopy(ent->v.v_angle, cmd.angles); + VectorCopy(ent->v->v_angle, cmd.angles); cmd.forwardmove = 0; cmd.sidemove = 0; cmd.upmove = 0; @@ -717,19 +717,19 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by for (i=0 ; i<3 ; i++) if (pflags & (PF_VELOCITY1<v.velocity[i]); + MSG_WriteShort (msg, ent->v->velocity[i]); if (pflags & PF_MODEL) - MSG_WriteByte (msg, ent->v.modelindex); + MSG_WriteByte (msg, ent->v->modelindex); if (pflags & PF_SKINNUM) - MSG_WriteByte (msg, (int)ent->v.skin | (((pflags & PF_MODEL)&&(ent->v.modelindex >= 256))<<7)); + MSG_WriteByte (msg, (int)ent->v->skin | (((pflags & PF_MODEL)&&(ent->v->modelindex >= 256))<<7)); if (pflags & PF_EFFECTS) MSG_WriteByte (msg, TranslateEffects(ent)); if (pflags & PF_WEAPONFRAME) - MSG_WriteByte (msg, ent->v.weaponframe); + MSG_WriteByte (msg, ent->v->weaponframe); } } @@ -750,19 +750,19 @@ qbool SV_EntityVisibleToClient (client_t* client, int e, byte* pvs) } // ignore ents without visible models - if (!ent->v.modelindex || !*PR_GetEntityString(ent->v.model)) + if (!ent->v->modelindex || !*PR_GetEntityString(ent->v->model)) return false; - if ( pvs && ent->e->num_leafs >= 0 ) + if ( pvs && ent->e.num_leafs >= 0 ) { int i; // ignore if not touching a PV leaf - for (i=0 ; i < ent->e->num_leafs ; i++) - if (pvs[ent->e->leafnums[i] >> 3] & (1 << (ent->e->leafnums[i]&7) )) + for (i=0 ; i < ent->e.num_leafs ; i++) + if (pvs[ent->e.leafnums[i] >> 3] & (1 << (ent->e.leafnums[i]&7) )) break; - if (i == ent->e->num_leafs) + if (i == ent->e.num_leafs) return false; // not visible } @@ -821,13 +821,13 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) int trackent = 0; if (fofs_hideentity) - hideent = ((eval_t *)((byte *)&(client->edict)->v + fofs_hideentity))->_int / pr_edict_size; + hideent = ((eval_t *)((byte *)(client->edict)->v + fofs_hideentity))->_int / pr_edict_size; else hideent = 0; if (fofs_trackent) { - trackent = ((eval_t *)((byte *)&(client->edict)->v + fofs_trackent))->_int; + trackent = ((eval_t *)((byte *)(client->edict)->v + fofs_trackent))->_int; if (trackent < 1 || trackent > MAX_CLIENTS || svs.clients[trackent - 1].state != cs_spawned) trackent = 0; } @@ -835,11 +835,11 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) // we should use org of tracked player in case or trackent. if (trackent) { - VectorAdd (svs.clients[trackent - 1].edict->v.origin, svs.clients[trackent - 1].edict->v.view_ofs, org); + VectorAdd (svs.clients[trackent - 1].edict->v->origin, svs.clients[trackent - 1].edict->v->view_ofs, org); } else { - VectorAdd (client->edict->v.origin, client->edict->v.view_ofs, org); + VectorAdd (client->edict->v->origin, client->edict->v->view_ofs, org); } pvs = CM_FatPVS (org); // search some PVS @@ -850,7 +850,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) #define ISUNDERWATER(x) ((x) == CONTENTS_WATER || (x) == CONTENTS_SLIME || (x) == CONTENTS_LAVA) // server flash should not work underwater - int content = CM_HullPointContents(&sv.worldmodel->hulls[0], 0, client->edict->v.origin); + int content = CM_HullPointContents(&sv.worldmodel->hulls[0], 0, client->edict->v->origin); disable_updates = !ISUNDERWATER(content); #undef ISUNDERWATER @@ -884,14 +884,14 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) { if (!SV_EntityVisibleToClient(client, e, pvs)) { if (fofs_visibility) { - ((eval_t *)((byte *)&(ent)->v + fofs_visibility))->_int &= ~client_flag; + ((eval_t *)((byte *)(ent)->v + fofs_visibility))->_int &= ~client_flag; } continue; } if (fofs_visibility) { // Don't include other filters in logic for setting this field - ((eval_t *)((byte *)&(ent)->v + fofs_visibility))->_int |= (1 << (client - svs.clients)); + ((eval_t *)((byte *)(ent)->v + fofs_visibility))->_int |= (1 << (client - svs.clients)); } if (e == hideent) { @@ -902,8 +902,8 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) continue; // added to the special update list if (clent) { - VectorAdd(ent->v.absmin, ent->v.absmax, org); - VectorMA(clent->v.origin, -0.5, org, org); + VectorAdd(ent->v->absmin, ent->v->absmax, org); + VectorMA(clent->v->origin, -0.5, org, org); distance = DotProduct(org, org); //Length // add to the packetentities @@ -948,12 +948,12 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) state->number = e; state->flags = 0; - VectorCopy (ent->v.origin, state->origin); - VectorCopy (ent->v.angles, state->angles); - state->modelindex = ent->v.modelindex; - state->frame = ent->v.frame; - state->colormap = ent->v.colormap; - state->skinnum = ent->v.skin; + VectorCopy (ent->v->origin, state->origin); + VectorCopy (ent->v->angles, state->angles); + state->modelindex = ent->v->modelindex; + state->frame = ent->v->frame; + state->colormap = ent->v->colormap; + state->skinnum = ent->v->skin; state->effects = TranslateEffects(ent); } } // server flash @@ -972,18 +972,18 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) for (e=1, ent=EDICT_NUM(e) ; e < sv.num_edicts ; e++, ent = NEXT_EDICT(ent)) { // ignore ents without visible models - if (!ent->v.modelindex || !*PR_GetEntityString(ent->v.model)) + if (!ent->v->modelindex || !*PR_GetEntityString(ent->v->model)) continue; // ignore if not touching a PV leaf (meag: this does nothing... complete or remove?) - if (pvs && ent->e->num_leafs >= 0) { - for (i = 0; i < ent->e->num_leafs; i++) - if (pvs[ent->e->leafnums[i] >> 3] & (1 << (ent->e->leafnums[i] & 7))) + if (pvs && ent->e.num_leafs >= 0) { + for (i = 0; i < ent->e.num_leafs; i++) + if (pvs[ent->e.leafnums[i] >> 3] & (1 << (ent->e.leafnums[i] & 7))) break; } - if ((int)ent->v.effects & EF_MUZZLEFLASH) { - ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; + if ((int)ent->v->effects & EF_MUZZLEFLASH) { + ent->v->effects = (int)ent->v->effects & ~EF_MUZZLEFLASH; MSG_WriteByte (msg, svc_muzzleflash); MSG_WriteShort (msg, e); } @@ -1007,17 +1007,17 @@ void SV_SetVisibleEntitiesForBot (client_t* client) if (!fofs_visibility) return; - VectorAdd (client->edict->v.origin, client->edict->v.view_ofs, org); + VectorAdd (client->edict->v->origin, client->edict->v->view_ofs, org); pvs = CM_FatPVS (org); // search some PVS // players first for (j = 0; j < MAX_CLIENTS; j++) { if (SV_PlayerVisibleToClient(client, j, pvs, client->edict, svs.clients[j].edict)) { - ((eval_t *)((byte *)&(svs.clients[j].edict)->v + fofs_visibility))->_int |= client_flag; + ((eval_t *)((byte *)(svs.clients[j].edict)->v + fofs_visibility))->_int |= client_flag; } else { - ((eval_t *)((byte *)&(svs.clients[j].edict)->v + fofs_visibility))->_int &= ~client_flag; + ((eval_t *)((byte *)(svs.clients[j].edict)->v + fofs_visibility))->_int &= ~client_flag; } } @@ -1027,10 +1027,10 @@ void SV_SetVisibleEntitiesForBot (client_t* client) edict_t* ent = EDICT_NUM (e); if (SV_EntityVisibleToClient(client, e, pvs)) { - ((eval_t *)((byte *)&(ent)->v + fofs_visibility))->_int |= client_flag; + ((eval_t *)((byte *)(ent)->v + fofs_visibility))->_int |= client_flag; } else { - ((eval_t *)((byte *)&(ent)->v + fofs_visibility))->_int &= ~client_flag; + ((eval_t *)((byte *)(ent)->v + fofs_visibility))->_int &= ~client_flag; } } } diff --git a/src/sv_init.c b/src/sv_init.c index 96594953a..806b11c67 100644 --- a/src/sv_init.c +++ b/src/sv_init.c @@ -105,30 +105,30 @@ static void SV_CreateBaseline (void) for (entnum = 0; entnum < sv.num_edicts ; entnum++) { svent = EDICT_NUM(entnum); - if (svent->e->free) + if (svent->e.free) continue; // create baselines for all player slots, // and any other edict that has a visible model - if (entnum > MAX_CLIENTS && !svent->v.modelindex) + if (entnum > MAX_CLIENTS && !svent->v->modelindex) continue; // // create entity baseline // - svent->e->baseline.number = entnum; - VectorCopy (svent->v.origin, svent->e->baseline.origin); - VectorCopy (svent->v.angles, svent->e->baseline.angles); - svent->e->baseline.frame = svent->v.frame; - svent->e->baseline.skinnum = svent->v.skin; + svent->e.baseline.number = entnum; + VectorCopy (svent->v->origin, svent->e.baseline.origin); + VectorCopy (svent->v->angles, svent->e.baseline.angles); + svent->e.baseline.frame = svent->v->frame; + svent->e.baseline.skinnum = svent->v->skin; if (entnum > 0 && entnum <= MAX_CLIENTS) { - svent->e->baseline.colormap = entnum; - svent->e->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); + svent->e.baseline.colormap = entnum; + svent->e.baseline.modelindex = SV_ModelIndex("progs/player.mdl"); } else { - svent->e->baseline.colormap = 0; - svent->e->baseline.modelindex = svent->v.modelindex; + svent->e.baseline.colormap = 0; + svent->e.baseline.modelindex = svent->v->modelindex; } } sv.num_baseline_edicts = sv.num_edicts; @@ -241,7 +241,7 @@ void SV_SpawnServer(char *mapname, qbool devmap, char* entityfile, qbool loading if( sv_vm && svs.clients[i].isBot ) { svs.clients[i].old_frags = 0; - svs.clients[i].edict->v.frags = 0.0; + svs.clients[i].edict->v->frags = 0.0; svs.clients[i].name[0] = 0; svs.clients[i].state = cs_free; Info_RemoveAll(&svs.clients[i]._userinfo_ctx_); @@ -350,11 +350,10 @@ void SV_SpawnServer(char *mapname, qbool devmap, char* entityfile, qbool loading for (i = 0; i < sv.max_edicts; i++) { - ent = EDICT_NUM(i); - ent->e = &sv.sv_edicts[i]; // assigning ->e field in each edict_t - ent->e->entnum = i; - ent->e->area.ed = ent; // yeah, pretty funny, but this help to find which edict_t own this area (link_t) - PR_ClearEdict(ent); + sv.edicts[i].v = (entvars_t *)((byte *)sv.game_edicts + i * pr_edict_size); + sv.edicts[i].e.entnum = i; + sv.edicts[i].e.area.ed = &sv.edicts[i]; // yeah, pretty funny, but this help to find which edict_t own this area (link_t) + PR_ClearEdict(&sv.edicts[i]); } fofs_items2 = ED_FindFieldOffset ("items2"); // ZQ_ITEMS2 extension @@ -441,7 +440,7 @@ void SV_SpawnServer(char *mapname, qbool devmap, char* entityfile, qbool loading { ent = EDICT_NUM(i+1); // restore client name. - PR_SetEntityString(ent, ent->v.netname, svs.clients[i].name); + PR_SetEntityString(ent, ent->v->netname, svs.clients[i].name); // reserve edict. svs.clients[i].edict = ent; //ZOID - make sure we update frags right @@ -523,17 +522,17 @@ void SV_SpawnServer(char *mapname, qbool devmap, char* entityfile, qbool loading #endif ent = EDICT_NUM(0); - ent->e->free = false; - PR_SetEntityString(ent, ent->v.model, sv.modelname); - ent->v.modelindex = 1; // world model - ent->v.solid = SOLID_BSP; - ent->v.movetype = MOVETYPE_PUSH; + ent->e.free = false; + PR_SetEntityString(ent, ent->v->model, sv.modelname); + ent->v->modelindex = 1; // world model + ent->v->solid = SOLID_BSP; + ent->v->movetype = MOVETYPE_PUSH; // information about the server - PR_SetEntityString(ent, ent->v.netname, VersionStringFull()); - PR_SetEntityString(ent, ent->v.targetname, SERVER_NAME); - ent->v.impulse = VERSION_NUM; - ent->v.items = pr_numbuiltins - 1; + PR_SetEntityString(ent, ent->v->netname, VersionStringFull()); + PR_SetEntityString(ent, ent->v->targetname, SERVER_NAME); + ent->v->impulse = VERSION_NUM; + ent->v->items = pr_numbuiltins - 1; PR_SetGlobalString(PR_GLOBAL(mapname), sv.mapname); // serverflags are for cross level information (sigils) diff --git a/src/sv_main.c b/src/sv_main.c index 73771cb50..97935bafe 100644 --- a/src/sv_main.c +++ b/src/sv_main.c @@ -418,7 +418,7 @@ void SV_DropClient(client_t* drop) // <-- MD drop->old_frags = 0; - drop->edict->v.frags = 0.0; + drop->edict->v->frags = 0.0; drop->name[0] = 0; Info_RemoveAll(&drop->_userinfo_ctx_); @@ -679,6 +679,22 @@ static void SVC_LastScores (void) SV_EndRedirect (); } +/* +=================== +SVC_LastStats +=================== +*/ +void SV_LastStats_f (void); +static void SVC_LastStats (void) +{ + if(!(int)sv_allowlastscores.value) + return; + + SV_BeginRedirect (RD_PACKET); + SV_LastStats_f (); + SV_EndRedirect (); +} + /* =================== SVC_DemoList @@ -1385,10 +1401,10 @@ static void SVC_DirectConnect (void) edictnum = (newcl-svs.clients)+1; ent = EDICT_NUM(edictnum); - ent->e->free = false; + ent->e.free = false; newcl->edict = ent; // restore client name. - PR_SetEntityString(ent, ent->v.netname, newcl->name); + PR_SetEntityString(ent, ent->v->netname, newcl->name); s = ( vip ? va("%d", vip) : "" ); @@ -1893,6 +1909,8 @@ static void SV_ConnectionlessPacket (void) SVC_GetChallenge (); else if (!strcmp(c,"lastscores")) SVC_LastScores (); + else if (!strcmp(c,"laststats")) + SVC_LastStats (); else if (!strcmp(c,"dlist")) SVC_DemoList (); else if (!strcmp(c,"dlistr")) @@ -3120,6 +3138,7 @@ int SV_BoundRate (qbool dl, int rate) if (rate < 500) rate = 500; + if (rate > 100000 * MAX_DUPLICATE_PACKETS) rate = 100000 * MAX_DUPLICATE_PACKETS; @@ -3324,9 +3343,9 @@ void SV_InitLocal (void) extern cvar_t pm_airstep; extern cvar_t pm_pground; extern cvar_t pm_rampjump; - //extern cvar_t pm_slidefix; + extern cvar_t pm_slidefix; extern cvar_t pm_ktjump; - //extern cvar_t pm_bunnyspeedcap; + extern cvar_t pm_bunnyspeedcap; // qws = QuakeWorld Server information static cvar_t qws_name = { "qws_name", SERVER_NAME, CVAR_ROM }; @@ -3431,9 +3450,9 @@ void SV_InitLocal (void) Cvar_Register (&sv_antilag_no_pred); Cvar_Register (&sv_antilag_projectiles); - //Cvar_Register (&pm_bunnyspeedcap); + Cvar_Register (&pm_bunnyspeedcap); Cvar_Register (&pm_ktjump); - //Cvar_Register (&pm_slidefix); + Cvar_Register (&pm_slidefix); Cvar_Register (&pm_pground); Cvar_Register (&pm_airstep); Cvar_Register (&pm_rampjump); @@ -3885,6 +3904,9 @@ void Host_Init (int argc, char **argv, int default_memsize) Con_Printf ("%4.1f megabyte heap\n", (float)hunk_size / (1024 * 1024)); Con_Printf ("QuakeWorld Initialized\n"); +#ifndef WWW_INTEGRATION + Con_Printf ("www authentication disabled (no curl support)\n"); +#endif Cbuf_InsertText ("exec server.cfg\n"); diff --git a/src/sv_move.c b/src/sv_move.c index 76630af20..06911e04a 100644 --- a/src/sv_move.c +++ b/src/sv_move.c @@ -41,8 +41,8 @@ qbool SV_CheckBottom (edict_t *ent) int x, y; float mid, bottom; - VectorAdd (ent->v.origin, ent->v.mins, mins); - VectorAdd (ent->v.origin, ent->v.maxs, maxs); + VectorAdd (ent->v->origin, ent->v->mins, mins); + VectorAdd (ent->v->origin, ent->v->maxs, maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks @@ -111,33 +111,33 @@ qbool SV_movestep (edict_t *ent, vec3_t move, qbool relink) edict_t *enemy; // try the move - VectorCopy (ent->v.origin, oldorg); - VectorAdd (ent->v.origin, move, neworg); + VectorCopy (ent->v->origin, oldorg); + VectorAdd (ent->v->origin, move, neworg); // flying monsters don't step up - if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) + if ( (int)ent->v->flags & (FL_SWIM | FL_FLY) ) { // try one move with vertical motion, then one without for (i=0 ; i<2 ; i++) { - VectorAdd (ent->v.origin, move, neworg); - enemy = PROG_TO_EDICT(ent->v.enemy); + VectorAdd (ent->v->origin, move, neworg); + enemy = PROG_TO_EDICT(ent->v->enemy); if (i == 0 && enemy != sv.edicts) { - dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; + dz = ent->v->origin[2] - PROG_TO_EDICT(ent->v->enemy)->v->origin[2]; if (dz > 40) neworg[2] -= 8; if (dz < 30) neworg[2] += 8; } - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent); if (trace.fraction == 1) { - if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) + if ( ((int)ent->v->flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) return false; // swim monster left water - VectorCopy (trace.endpos, ent->v.origin); + VectorCopy (trace.endpos, ent->v->origin); if (relink) SV_LinkEdict (ent, true); return true; @@ -155,7 +155,7 @@ qbool SV_movestep (edict_t *ent, vec3_t move, qbool relink) VectorCopy (neworg, end); end[2] -= STEPSIZE*2; - trace = SV_Trace (neworg, ent->v.mins, ent->v.maxs, end, false, ent); + trace = SV_Trace (neworg, ent->v->mins, ent->v->maxs, end, false, ent); if (trace.allsolid) return false; @@ -163,19 +163,19 @@ qbool SV_movestep (edict_t *ent, vec3_t move, qbool relink) if (trace.startsolid) { neworg[2] -= STEPSIZE; - trace = SV_Trace (neworg, ent->v.mins, ent->v.maxs, end, false, ent); + trace = SV_Trace (neworg, ent->v->mins, ent->v->maxs, end, false, ent); if (trace.allsolid || trace.startsolid) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall - if ( (int)ent->v.flags & FL_PARTIALGROUND ) + if ( (int)ent->v->flags & FL_PARTIALGROUND ) { - VectorAdd (ent->v.origin, move, ent->v.origin); + VectorAdd (ent->v->origin, move, ent->v->origin); if (relink) SV_LinkEdict (ent, true); - ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND; // Con_Printf ("fall down\n"); return true; } @@ -184,27 +184,27 @@ qbool SV_movestep (edict_t *ent, vec3_t move, qbool relink) } // check point traces down for dangling corners - VectorCopy (trace.endpos, ent->v.origin); + VectorCopy (trace.endpos, ent->v->origin); if (!SV_CheckBottom (ent)) { - if ( (int)ent->v.flags & FL_PARTIALGROUND ) + if ( (int)ent->v->flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) SV_LinkEdict (ent, true); return true; } - VectorCopy (oldorg, ent->v.origin); + VectorCopy (oldorg, ent->v->origin); return false; } - if ( (int)ent->v.flags & FL_PARTIALGROUND ) + if ( (int)ent->v->flags & FL_PARTIALGROUND ) { // Con_Printf ("back on ground\n"); - ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; + ent->v->flags = (int)ent->v->flags & ~FL_PARTIALGROUND; } - ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); + ent->v->groundentity = EDICT_TO_PROG(trace.e.ent); // the move is ok if (relink) @@ -230,7 +230,7 @@ qbool SV_StepDirection (edict_t *ent, float yaw, float dist) vec3_t move, oldorigin; float delta; - ent->v.ideal_yaw = yaw; + ent->v->ideal_yaw = yaw; PF_changeyaw(); // OUCH OUCH: its relay on what ent == self ? I'm not even mention about PR2... @@ -239,13 +239,13 @@ qbool SV_StepDirection (edict_t *ent, float yaw, float dist) move[1] = sin(yaw)*dist; move[2] = 0; - VectorCopy (ent->v.origin, oldorigin); + VectorCopy (ent->v->origin, oldorigin); if (SV_movestep (ent, move, false)) { - delta = ent->v.angles[YAW] - ent->v.ideal_yaw; + delta = ent->v->angles[YAW] - ent->v->ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step - VectorCopy (oldorigin, ent->v.origin); + VectorCopy (oldorigin, ent->v->origin); } SV_LinkEdict (ent, true); return true; @@ -265,7 +265,7 @@ void SV_FixCheckBottom (edict_t *ent) { // Con_Printf ("SV_FixCheckBottom\n"); - ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; + ent->v->flags = (int)ent->v->flags | FL_PARTIALGROUND; } @@ -283,11 +283,11 @@ void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) float d[3]; float tdir, olddir, turnaround; - olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); + olddir = anglemod( (int)(actor->v->ideal_yaw/45)*45 ); turnaround = anglemod(olddir - 180); - deltax = enemy->v.origin[0] - actor->v.origin[0]; - deltay = enemy->v.origin[1] - actor->v.origin[1]; + deltax = enemy->v->origin[0] - actor->v->origin[0]; + deltay = enemy->v->origin[1] - actor->v->origin[1]; if (deltax>10) d[1]= 0; else if (deltax<-10) @@ -348,7 +348,7 @@ void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; - actor->v.ideal_yaw = olddir; // can't move + actor->v->ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all @@ -370,9 +370,9 @@ qbool SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) for (i=0 ; i<3 ; i++) { - if (goal->v.absmin[i] > ent->v.absmax[i] + dist) + if (goal->v->absmin[i] > ent->v->absmax[i] + dist) return false; - if (goal->v.absmax[i] < ent->v.absmin[i] - dist) + if (goal->v->absmax[i] < ent->v->absmin[i] - dist) return false; } return true; @@ -393,21 +393,21 @@ void SV_MoveToGoal (void) float dist; ent = PROG_TO_EDICT(pr_global_struct->self); - goal = PROG_TO_EDICT(ent->v.goalentity); + goal = PROG_TO_EDICT(ent->v->goalentity); dist = G_FLOAT(OFS_PARM0); - if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately - if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) + if ( PROG_TO_EDICT(ent->v->enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) return; // bump around... - if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v->ideal_yaw, dist)) { SV_NewChaseDir (ent, goal, dist); } diff --git a/src/sv_nchan.c b/src/sv_nchan.c index 177296515..aa50261bd 100644 --- a/src/sv_nchan.c +++ b/src/sv_nchan.c @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void ClientReliableCheckBlock(client_t *cl, int maxsize) { if (cl->num_backbuf - || cl->netchan.message.cursize > cl->netchan.message.maxsize - maxsize - 1) + || cl->netchan.message.cursize > cl->netchan.message.maxsize - maxsize - 1) { // we would probably overflow the buffer, save it for next if (!cl->num_backbuf || cl->backbuf.cursize > cl->backbuf.maxsize - maxsize - 1) diff --git a/src/sv_phys.c b/src/sv_phys.c index c1c99a449..c65eae9cb 100644 --- a/src/sv_phys.c +++ b/src/sv_phys.c @@ -95,28 +95,28 @@ void SV_CheckVelocity (edict_t *ent) // for (i=0 ; i<3 ; i++) { - if (IS_NAN(ent->v.velocity[i])) + if (IS_NAN(ent->v->velocity[i])) { - Con_DPrintf ("Got a NaN velocity on %s\n", PR_GetEntityString(ent->v.classname)); - ent->v.velocity[i] = 0; + Con_DPrintf ("Got a NaN velocity on %s\n", PR_GetEntityString(ent->v->classname)); + ent->v->velocity[i] = 0; } - if (IS_NAN(ent->v.origin[i])) + if (IS_NAN(ent->v->origin[i])) { - Con_DPrintf ("Got a NaN origin on %s\n", PR_GetEntityString(ent->v.classname)); - ent->v.origin[i] = 0; + Con_DPrintf ("Got a NaN origin on %s\n", PR_GetEntityString(ent->v->classname)); + ent->v->origin[i] = 0; } -/* if (ent->v.velocity[i] > sv_maxvelocity.value) - ent->v.velocity[i] = sv_maxvelocity.value; - else if (ent->v.velocity[i] < -sv_maxvelocity.value) - ent->v.velocity[i] = -sv_maxvelocity.value; +/* if (ent->v->velocity[i] > sv_maxvelocity.value) + ent->v->velocity[i] = sv_maxvelocity.value; + else if (ent->v->velocity[i] < -sv_maxvelocity.value) + ent->v->velocity[i] = -sv_maxvelocity.value; */ } // SV_MAXVELOCITY fix by Maddes - wishspeed = VectorLength(ent->v.velocity); + wishspeed = VectorLength(ent->v->velocity); if (wishspeed > sv_maxvelocity.value) { - VectorScale (ent->v.velocity, sv_maxvelocity.value/wishspeed, ent->v.velocity); + VectorScale (ent->v->velocity, sv_maxvelocity.value/wishspeed, ent->v->velocity); wishspeed = sv_maxvelocity.value; } } @@ -137,7 +137,7 @@ qbool SV_RunThink (edict_t *ent) do { - thinktime = ent->v.nextthink; + thinktime = ent->v->nextthink; if (thinktime <= 0) return true; if (thinktime > sv.time + sv_frametime) @@ -147,13 +147,13 @@ qbool SV_RunThink (edict_t *ent) thinktime = sv.time; // don't let things stay in the past. // it is possible to start that way // by a trigger with a local time. - ent->v.nextthink = 0; + ent->v->nextthink = 0; pr_global_struct->time = thinktime; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); - PR_EdictThink(ent->v.think); + PR_EdictThink(ent->v->think); - if (ent->e->free) + if (ent->e.free) return false; } while (1); @@ -175,18 +175,18 @@ void SV_Impact (edict_t *e1, edict_t *e2) old_other = pr_global_struct->other; pr_global_struct->time = sv.time; - if (e1->v.touch && e1->v.solid != SOLID_NOT) + if (e1->v->touch && e1->v->solid != SOLID_NOT) { pr_global_struct->self = EDICT_TO_PROG(e1); pr_global_struct->other = EDICT_TO_PROG(e2); - PR_EdictTouch(e1->v.touch); + PR_EdictTouch(e1->v->touch); } - if (e2->v.touch && e2->v.solid != SOLID_NOT) + if (e2->v->touch && e2->v->solid != SOLID_NOT) { pr_global_struct->self = EDICT_TO_PROG(e2); pr_global_struct->other = EDICT_TO_PROG(e1); - PR_EdictTouch(e2->v.touch); + PR_EdictTouch(e2->v->touch); } pr_global_struct->self = old_self; @@ -252,8 +252,8 @@ int SV_FlyMove (edict_t *ent, float time1, trace_t *steptrace, int type) numbumps = 4; blocked = 0; - VectorCopy (ent->v.velocity, original_velocity); - VectorCopy (ent->v.velocity, primal_velocity); + VectorCopy (ent->v->velocity, original_velocity); + VectorCopy (ent->v->velocity, primal_velocity); numplanes = 0; time_left = time1; @@ -261,20 +261,20 @@ int SV_FlyMove (edict_t *ent, float time1, trace_t *steptrace, int type) for (bumpcount=0 ; bumpcountv.origin[i] + time_left * ent->v.velocity[i]; + end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i]; - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, type, ent); + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, end, type, ent); if (trace.allsolid) { // entity is trapped in another solid - VectorClear (ent->v.velocity); + VectorClear (ent->v->velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance - VectorCopy (trace.endpos, ent->v.origin); - VectorCopy (ent->v.velocity, original_velocity); + VectorCopy (trace.endpos, ent->v->origin); + VectorCopy (ent->v->velocity, original_velocity); numplanes = 0; } @@ -287,10 +287,10 @@ int SV_FlyMove (edict_t *ent, float time1, trace_t *steptrace, int type) if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor - if (trace.e.ent->v.solid == SOLID_BSP) + if (trace.e.ent->v->solid == SOLID_BSP) { - ent->v.flags = (int)ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(trace.e.ent); } } if (!trace.plane.normal[2]) @@ -304,7 +304,7 @@ int SV_FlyMove (edict_t *ent, float time1, trace_t *steptrace, int type) // run the impact function // SV_Impact (ent, trace.e.ent); - if (ent->e->free) + if (ent->e.free) break; // removed by the impact function @@ -313,7 +313,7 @@ int SV_FlyMove (edict_t *ent, float time1, trace_t *steptrace, int type) // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen - VectorClear (ent->v.velocity); + VectorClear (ent->v->velocity); return 3; } @@ -338,28 +338,28 @@ int SV_FlyMove (edict_t *ent, float time1, trace_t *steptrace, int type) if (i != numplanes) { // go along this plane - VectorCopy (new_velocity, ent->v.velocity); + VectorCopy (new_velocity, ent->v->velocity); } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); - VectorClear (ent->v.velocity); + VectorClear (ent->v->velocity); return 7; } CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, ent->v.velocity); - VectorScale (dir, d, ent->v.velocity); + d = DotProduct (dir, ent->v->velocity); + VectorScale (dir, d, ent->v->velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // - if (DotProduct (ent->v.velocity, primal_velocity) <= 0) + if (DotProduct (ent->v->velocity, primal_velocity) <= 0) { - VectorClear (ent->v.velocity); + VectorClear (ent->v->velocity); return blocked; } } @@ -375,7 +375,7 @@ SV_AddGravity */ void SV_AddGravity (edict_t *ent, float scale) { - ent->v.velocity[2] -= scale * movevars.gravity * sv_frametime; + ent->v->velocity[2] -= scale * movevars.gravity * sv_frametime; } /* @@ -398,20 +398,20 @@ trace_t SV_PushEntity (edict_t *ent, vec3_t push, unsigned int traceflags) trace_t trace; vec3_t end; - VectorAdd (ent->v.origin, push, end); + VectorAdd (ent->v->origin, push, end); - if ((int)ent->v.flags&FL_LAGGEDMOVE) + if ((int)ent->v->flags&FL_LAGGEDMOVE) traceflags |= MOVE_LAGGED; - if (ent->v.movetype == MOVETYPE_FLYMISSILE) - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE|traceflags, ent); - else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) + if (ent->v->movetype == MOVETYPE_FLYMISSILE) + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE|traceflags, ent); + else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT) // only clip against bmodels - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS|traceflags, ent); + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS|traceflags, ent); else - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL|traceflags, ent); + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL|traceflags, ent); - VectorCopy (trace.endpos, ent->v.origin); + VectorCopy (trace.endpos, ent->v->origin); SV_LinkEdict (ent, true); if (trace.e.ent) @@ -440,15 +440,15 @@ qbool SV_Push (edict_t *pusher, vec3_t move) for (i=0 ; i<3 ; i++) { - mins[i] = pusher->v.absmin[i] + move[i]; - maxs[i] = pusher->v.absmax[i] + move[i]; + mins[i] = pusher->v->absmin[i] + move[i]; + maxs[i] = pusher->v->absmax[i] + move[i]; } - VectorCopy (pusher->v.origin, pushorig); + VectorCopy (pusher->v->origin, pushorig); // move the pusher to its final position - VectorAdd (pusher->v.origin, move, pusher->v.origin); + VectorAdd (pusher->v->origin, move, pusher->v->origin); SV_LinkEdict (pusher, false); // see if any solid entities are inside the final position @@ -456,30 +456,30 @@ qbool SV_Push (edict_t *pusher, vec3_t move) check = NEXT_EDICT(sv.edicts); for (e=1 ; ee->free) + if (check->e.free) continue; - if (check->v.movetype == MOVETYPE_PUSH - || check->v.movetype == MOVETYPE_NONE - || check->v.movetype == MOVETYPE_NOCLIP) + if (check->v->movetype == MOVETYPE_PUSH + || check->v->movetype == MOVETYPE_NONE + || check->v->movetype == MOVETYPE_NOCLIP) continue; - solid_save = pusher->v.solid; - pusher->v.solid = SOLID_NOT; + solid_save = pusher->v->solid; + pusher->v->solid = SOLID_NOT; block = SV_TestEntityPosition (check); - pusher->v.solid = solid_save; + pusher->v->solid = solid_save; if (block) continue; // if the entity is standing on the pusher, it will definately be moved - if ( ! ( ((int)check->v.flags & FL_ONGROUND) - && PROG_TO_EDICT(check->v.groundentity) == pusher) ) + if ( ! ( ((int)check->v->flags & FL_ONGROUND) + && PROG_TO_EDICT(check->v->groundentity) == pusher) ) { - if ( check->v.absmin[0] >= maxs[0] - || check->v.absmin[1] >= maxs[1] - || check->v.absmin[2] >= maxs[2] - || check->v.absmax[0] <= mins[0] - || check->v.absmax[1] <= mins[1] - || check->v.absmax[2] <= mins[2] ) + if ( check->v->absmin[0] >= maxs[0] + || check->v->absmin[1] >= maxs[1] + || check->v->absmin[2] >= maxs[2] + || check->v->absmax[0] <= mins[0] + || check->v->absmax[1] <= mins[1] + || check->v->absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position @@ -488,15 +488,15 @@ qbool SV_Push (edict_t *pusher, vec3_t move) } // remove the onground flag for non-players - if (check->v.movetype != MOVETYPE_WALK) - check->v.flags = (int)check->v.flags & ~FL_ONGROUND; + if (check->v->movetype != MOVETYPE_WALK) + check->v->flags = (int)check->v->flags & ~FL_ONGROUND; - VectorCopy (check->v.origin, moved_from[num_moved]); + VectorCopy (check->v->origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity - VectorAdd (check->v.origin, move, check->v.origin); + VectorAdd (check->v->origin, move, check->v->origin); block = SV_TestEntityPosition (check); if (!block) { // pushed ok @@ -505,7 +505,7 @@ qbool SV_Push (edict_t *pusher, vec3_t move) } // if it is ok to leave in the old position, do it - VectorSubtract (check->v.origin, move, check->v.origin); + VectorSubtract (check->v->origin, move, check->v->origin); block = SV_TestEntityPosition (check); if (!block) { @@ -517,35 +517,35 @@ qbool SV_Push (edict_t *pusher, vec3_t move) } // if it is still inside the pusher, block - if (check->v.mins[0] == check->v.maxs[0]) + if (check->v->mins[0] == check->v->maxs[0]) { SV_LinkEdict (check, false); continue; } - if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) + if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER) { // corpse - check->v.mins[0] = check->v.mins[1] = 0; - VectorCopy (check->v.mins, check->v.maxs); + check->v->mins[0] = check->v->mins[1] = 0; + VectorCopy (check->v->mins, check->v->maxs); SV_LinkEdict (check, false); continue; } - VectorCopy (pushorig, pusher->v.origin); + VectorCopy (pushorig, pusher->v->origin); SV_LinkEdict (pusher, false); // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone - if (pusher->v.blocked) + if (pusher->v->blocked) { pr_global_struct->self = EDICT_TO_PROG(pusher); pr_global_struct->other = EDICT_TO_PROG(check); - PR_EdictBlocked (pusher->v.blocked); + PR_EdictBlocked (pusher->v->blocked); } // move back any entities we already moved for (i=0 ; iv.origin); + VectorCopy (moved_from[i], moved_edict[i]->v->origin); SV_LinkEdict (moved_edict[i], false); } return false; @@ -565,17 +565,17 @@ void SV_PushMove (edict_t *pusher, float movetime) int i; vec3_t move; - if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) + if (!pusher->v->velocity[0] && !pusher->v->velocity[1] && !pusher->v->velocity[2]) { - pusher->v.ltime += movetime; + pusher->v->ltime += movetime; return; } for (i=0 ; i<3 ; i++) - move[i] = pusher->v.velocity[i] * movetime; + move[i] = pusher->v->velocity[i] * movetime; if (SV_Push (pusher, move)) - pusher->v.ltime += movetime; + pusher->v->ltime += movetime; } @@ -593,12 +593,12 @@ void SV_Physics_Pusher (edict_t *ent) float l; vec3_t oldorg, move; - oldltime = ent->v.ltime; + oldltime = ent->v->ltime; - thinktime = ent->v.nextthink; - if (thinktime < ent->v.ltime + sv_frametime) + thinktime = ent->v->nextthink; + if (thinktime < ent->v->ltime + sv_frametime) { - movetime = thinktime - ent->v.ltime; + movetime = thinktime - ent->v->ltime; if (movetime < 0) movetime = 0; } @@ -607,27 +607,27 @@ void SV_Physics_Pusher (edict_t *ent) if (movetime) { - SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked + SV_PushMove (ent, movetime); // advances ent->v->ltime if not blocked } - if (thinktime > oldltime && thinktime <= ent->v.ltime) + if (thinktime > oldltime && thinktime <= ent->v->ltime) { - VectorCopy (ent->v.origin, oldorg); - ent->v.nextthink = 0; + VectorCopy (ent->v->origin, oldorg); + ent->v->nextthink = 0; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); - PR_EdictThink(ent->v.think); + PR_EdictThink(ent->v->think); - if (ent->e->free) + if (ent->e.free) return; - VectorSubtract (ent->v.origin, oldorg, move); + VectorSubtract (ent->v->origin, oldorg, move); l = VectorLength(move); if (l > 1.0/64) { // Con_Printf ("**** snap: %f\n", VectorLength (l)); - VectorCopy (oldorg, ent->v.origin); + VectorCopy (oldorg, ent->v->origin); SV_Push (ent, move); } } @@ -659,8 +659,8 @@ void SV_Physics_Noclip (edict_t *ent) if (!SV_RunThink (ent)) return; - VectorMA (ent->v.angles, sv_frametime, ent->v.avelocity, ent->v.angles); - VectorMA (ent->v.origin, sv_frametime, ent->v.velocity, ent->v.origin); + VectorMA (ent->v->angles, sv_frametime, ent->v->avelocity, ent->v->angles); + VectorMA (ent->v->origin, sv_frametime, ent->v->velocity, ent->v->origin); SV_LinkEdict (ent, false); } @@ -683,31 +683,31 @@ void SV_CheckWaterTransition (edict_t *ent) { int cont; - cont = SV_PointContents (ent->v.origin); - if (!ent->v.watertype) + cont = SV_PointContents (ent->v->origin); + if (!ent->v->watertype) { // just spawned here - ent->v.watertype = cont; - ent->v.waterlevel = 1; + ent->v->watertype = cont; + ent->v->waterlevel = 1; return; } if (cont <= CONTENTS_WATER) { - if (ent->v.watertype == CONTENTS_EMPTY) + if (ent->v->watertype == CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); } - ent->v.watertype = cont; - ent->v.waterlevel = 1; + ent->v->watertype = cont; + ent->v->waterlevel = 1; } else { - if (ent->v.watertype != CONTENTS_EMPTY) + if (ent->v->watertype != CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); } - ent->v.watertype = CONTENTS_EMPTY; - ent->v.waterlevel = cont; + ent->v->watertype = CONTENTS_EMPTY; + ent->v->waterlevel = cont; } } @@ -728,47 +728,47 @@ void SV_Physics_Toss (edict_t *ent) if (!SV_RunThink (ent)) return; - if (ent->v.velocity[2] > 0) - ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + if (ent->v->velocity[2] > 0) + ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND; // if onground, return without moving - if ( ((int)ent->v.flags & FL_ONGROUND) ) + if ( ((int)ent->v->flags & FL_ONGROUND) ) return; SV_CheckVelocity (ent); // add gravity - if (ent->v.movetype != MOVETYPE_FLY - && ent->v.movetype != MOVETYPE_FLYMISSILE) + if (ent->v->movetype != MOVETYPE_FLY + && ent->v->movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent, 1.0); // move angles - VectorMA (ent->v.angles, sv_frametime, ent->v.avelocity, ent->v.angles); + VectorMA (ent->v->angles, sv_frametime, ent->v->avelocity, ent->v->angles); // move origin - VectorScale (ent->v.velocity, sv_frametime, move); + VectorScale (ent->v->velocity, sv_frametime, move); trace = SV_PushEntity (ent, move, (sv_antilag.value == 2 && sv_antilag_projectiles.value) ? MOVE_LAGGED:0); if (trace.fraction == 1) return; - if (ent->e->free) + if (ent->e.free) return; - if (ent->v.movetype == MOVETYPE_BOUNCE) + if (ent->v->movetype == MOVETYPE_BOUNCE) backoff = 1.5; else backoff = 1; - ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); + ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, backoff); // stop if on ground if (trace.plane.normal[2] > 0.7) { - if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE ) + if (ent->v->velocity[2] < 60 || ent->v->movetype != MOVETYPE_BOUNCE ) { - ent->v.flags = (int)ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); - VectorClear (ent->v.velocity); - VectorClear (ent->v.avelocity); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(trace.e.ent); + VectorClear (ent->v->velocity); + VectorClear (ent->v->avelocity); } } @@ -801,9 +801,9 @@ void SV_Physics_Step (edict_t *ent) qbool hitsound; // frefall if not onground - if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) + if ( ! ((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) { - if (ent->v.velocity[2] < movevars.gravity*-0.1) + if (ent->v->velocity[2] < movevars.gravity*-0.1) hitsound = true; else hitsound = false; @@ -813,13 +813,13 @@ void SV_Physics_Step (edict_t *ent) // Tonik: the check for SOLID_NOT is to fix the way dead bodies and // gibs behave (should not be blocked by players & monsters); // The SOLID_TRIGGER check is disabled lest we break frikbots - if (ent->v.solid == SOLID_NOT /* || ent->v.solid == SOLID_TRIGGER*/) + if (ent->v->solid == SOLID_NOT /* || ent->v->solid == SOLID_TRIGGER*/) SV_FlyMove (ent, sv_frametime, NULL, MOVE_NOMONSTERS); else SV_FlyMove (ent, sv_frametime, NULL, MOVE_NORMAL); SV_LinkEdict (ent, true); - if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground + if ( (int)ent->v->flags & FL_ONGROUND ) // just hit ground { if (hitsound) SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); @@ -850,11 +850,11 @@ SV_RunEntity */ void SV_RunEntity (edict_t *ent) { - if (ent->e->lastruntime == sv.time) + if (ent->e.lastruntime == sv.time) return; - ent->e->lastruntime = sv.time; + ent->e.lastruntime = sv.time; - switch ((int)ent->v.movetype) + switch ((int)ent->v->movetype) { case MOVETYPE_PUSH: SV_Physics_Pusher (ent); @@ -876,7 +876,7 @@ void SV_RunEntity (edict_t *ent) SV_Physics_Toss (ent); break; default: - SV_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); + SV_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype); } } @@ -895,15 +895,15 @@ void SV_RunNQNewmis (void) ent = NEXT_EDICT(sv.edicts); for (i=1 ; ie->free) + if (ent->e.free) continue; - if (ent->e->lastruntime || ent->v.owner != pl) + if (ent->e.lastruntime || ent->v->owner != pl) continue; - if (ent->v.movetype != MOVETYPE_FLY && - ent->v.movetype != MOVETYPE_FLYMISSILE && - ent->v.movetype != MOVETYPE_BOUNCE) + if (ent->v->movetype != MOVETYPE_FLY && + ent->v->movetype != MOVETYPE_FLYMISSILE && + ent->v->movetype != MOVETYPE_BOUNCE) continue; - if (ent->v.solid != SOLID_BBOX && ent->v.solid != SOLID_TRIGGER) + if (ent->v->solid != SOLID_BBOX && ent->v->solid != SOLID_TRIGGER) continue; save_frametime = sv_frametime; @@ -985,7 +985,7 @@ void SV_Physics (void) ent = sv.edicts; for (i=0 ; ie->free) + if (ent->e.free) continue; if (PR_GLOBAL(force_retouch)) @@ -1014,7 +1014,7 @@ void SV_Physics (void) sv_player = cl->edict; if (sv_client->spectator && sv_client->spec_track > 0) - sv_player->v.goalentity = EDICT_TO_PROG(svs.clients[sv_client->spec_track-1].edict); + sv_player->v->goalentity = EDICT_TO_PROG(svs.clients[sv_client->spec_track-1].edict); } sv_player = savesvpl; @@ -1104,7 +1104,7 @@ void SV_RunBots(void) if (sv_antilag.value) { if (cl->antilag_position_next == 0 || cl->antilag_positions[(cl->antilag_position_next - 1) % MAX_ANTILAG_POSITIONS].localtime < cl->localtime) { cl->antilag_positions[cl->antilag_position_next % MAX_ANTILAG_POSITIONS].localtime = cl->localtime; - VectorCopy(cl->edict->v.origin, cl->antilag_positions[cl->antilag_position_next % MAX_ANTILAG_POSITIONS].origin); + VectorCopy(cl->edict->v->origin, cl->antilag_positions[cl->antilag_position_next % MAX_ANTILAG_POSITIONS].origin); cl->antilag_position_next++; } } diff --git a/src/sv_save.c b/src/sv_save.c index 114918985..536c52f49 100644 --- a/src/sv_save.c +++ b/src/sv_save.c @@ -114,7 +114,7 @@ void SV_SaveGame_f(void) if (svs.clients[0].state != cs_spawned) { Con_Printf ("Can't save, client #0 not spawned.\n"); return; - } else if (svs.clients[0].edict->v.health <= 0) { + } else if (svs.clients[0].edict->v->health <= 0) { Con_Printf ("Can't save game with a dead player\n"); // in fact, we can, but does it make sense? return; @@ -297,7 +297,7 @@ void SV_LoadGame_f(void) ED_ParseEdict (start, ent); // link it into the bsp tree - if (!ent->e->free) + if (!ent->e.free) SV_LinkEdict (ent, false); } entnum++; diff --git a/src/sv_send.c b/src/sv_send.c index 636427c94..ce987e76b 100644 --- a/src/sv_send.c +++ b/src/sv_send.c @@ -458,18 +458,18 @@ void SV_MulticastEx (vec3_t origin, int to, const char *cl_reliable_key) // in case of trackent we have to reflect his origin so PHS work right. if (fofs_trackent) { - trackent = ((eval_t *)((byte *)&(client->edict)->v + fofs_trackent))->_int; + trackent = ((eval_t *)((byte *)(client->edict)->v + fofs_trackent))->_int; if (trackent < 1 || trackent > MAX_CLIENTS || svs.clients[trackent - 1].state != cs_spawned) trackent = 0; } if (trackent) { - VectorAdd (svs.clients[trackent - 1].edict->v.origin, svs.clients[trackent - 1].edict->v.view_ofs, vieworg); + VectorAdd (svs.clients[trackent - 1].edict->v->origin, svs.clients[trackent - 1].edict->v->view_ofs, vieworg); } else { - VectorAdd (client->edict->v.origin, client->edict->v.view_ofs, vieworg); + VectorAdd (client->edict->v->origin, client->edict->v->view_ofs, vieworg); } if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) @@ -647,14 +647,14 @@ void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, floa channel |= SND_ATTENUATION; // use the entity origin unless it is a bmodel or a trigger - if (entity->v.solid == SOLID_BSP || (entity->v.solid == SOLID_TRIGGER && entity->v.modelindex == 0)) + if (entity->v->solid == SOLID_BSP || (entity->v->solid == SOLID_TRIGGER && entity->v->modelindex == 0)) { for (i=0 ; i<3 ; i++) - origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); + origin[i] = entity->v->origin[i]+0.5*(entity->v->mins[i]+entity->v->maxs[i]); } else { - VectorCopy (entity->v.origin, origin); + VectorCopy (entity->v->origin, origin); } MSG_WriteByte (&sv.multicast, svc_sound); @@ -731,17 +731,17 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) } // send a damage message if the player got hit this frame - if (ent->v.dmg_take || ent->v.dmg_save) + if (ent->v->dmg_take || ent->v->dmg_save) { - other = PROG_TO_EDICT(ent->v.dmg_inflictor); + other = PROG_TO_EDICT(ent->v->dmg_inflictor); MSG_WriteByte (msg, svc_damage); - MSG_WriteByte (msg, ent->v.dmg_save); - MSG_WriteByte (msg, ent->v.dmg_take); + MSG_WriteByte (msg, ent->v->dmg_save); + MSG_WriteByte (msg, ent->v->dmg_take); for (i=0 ; i<3 ; i++) - MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); + MSG_WriteCoord (msg, other->v->origin[i] + 0.5*(other->v->mins[i] + other->v->maxs[i])); - ent->v.dmg_take = 0; - ent->v.dmg_save = 0; + ent->v->dmg_take = 0; + ent->v->dmg_save = 0; } // add this to server demo @@ -754,17 +754,17 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) } // a fixangle might get lost in a dropped packet. Oh well. - if (ent->v.fixangle) + if (ent->v->fixangle) { - ent->v.fixangle = 0; + ent->v->fixangle = 0; demo.fixangle[clnum] = true; - MSG_WriteByte (msg, svc_setangle); + MSG_WriteByte(msg, svc_setangle); #ifdef MVD_PEXT1_HIGHLAGTELEPORT if (client->mvdprotocolextensions1 & MVD_PEXT1_HIGHLAGTELEPORT) { if (fofs_teleported) { - client->lastteleport_teleport = ((eval_t *)((byte *)&(client->edict)->v + fofs_teleported))->_int; + client->lastteleport_teleport = ((eval_t *)((byte *)(client->edict)->v + fofs_teleported))->_int; if (client->lastteleport_teleport) { MSG_WriteByte(msg, 1); // signal a teleport } @@ -773,9 +773,9 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) } client->lastteleport_outgoingseq = client->netchan.outgoing_sequence; client->lastteleport_incomingseq = client->netchan.incoming_sequence; - client->lastteleport_teleportyaw = (client->edict)->v.angles[YAW] - client->lastcmd.angles[YAW]; + client->lastteleport_teleportyaw = (client->edict)->v->angles[YAW] - client->lastcmd.angles[YAW]; - ((eval_t *)((byte *)&(client->edict)->v + fofs_teleported))->_int = 0; + ((eval_t *)((byte *)(client->edict)->v + fofs_teleported))->_int = 0; SV_RotateCmd(client, &client->lastcmd); } else { @@ -785,14 +785,14 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) #endif for (i=0 ; i < 3 ; i++) - MSG_WriteAngle (msg, ent->v.angles[i] ); + MSG_WriteAngle (msg, ent->v->angles[i] ); if (sv.mvdrecording) { MSG_WriteByte (&demo.datagram, svc_setangle); MSG_WriteByte (&demo.datagram, clnum); for (i=0 ; i < 3 ; i++) - MSG_WriteAngle (&demo.datagram, ent->v.angles[i] ); + MSG_WriteAngle (&demo.datagram, ent->v->angles[i] ); } } @@ -851,7 +851,7 @@ void SV_UpdateClientStats (client_t *client) // in case of trackent we have to reflect his stats like for spectator. if (fofs_trackent) { - int trackent = ((eval_t *)((byte *)&(client->edict)->v + fofs_trackent))->_int; + int trackent = ((eval_t *)((byte *)(client->edict)->v + fofs_trackent))->_int; if (trackent < 1 || trackent > MAX_CLIENTS || svs.clients[trackent - 1].state != cs_spawned) trackent = 0; @@ -859,23 +859,23 @@ void SV_UpdateClientStats (client_t *client) ent = svs.clients[trackent - 1].edict; } - stats[STAT_HEALTH] = ent->v.health; - stats[STAT_WEAPON] = SV_ModelIndex(PR_GetEntityString(ent->v.weaponmodel)); - stats[STAT_AMMO] = ent->v.currentammo; - stats[STAT_ARMOR] = ent->v.armorvalue; - stats[STAT_SHELLS] = ent->v.ammo_shells; - stats[STAT_NAILS] = ent->v.ammo_nails; - stats[STAT_ROCKETS] = ent->v.ammo_rockets; - stats[STAT_CELLS] = ent->v.ammo_cells; + stats[STAT_HEALTH] = ent->v->health; + stats[STAT_WEAPON] = SV_ModelIndex(PR_GetEntityString(ent->v->weaponmodel)); + stats[STAT_AMMO] = ent->v->currentammo; + stats[STAT_ARMOR] = ent->v->armorvalue; + stats[STAT_SHELLS] = ent->v->ammo_shells; + stats[STAT_NAILS] = ent->v->ammo_nails; + stats[STAT_ROCKETS] = ent->v->ammo_rockets; + stats[STAT_CELLS] = ent->v->ammo_cells; if (!client->spectator || client->spec_track > 0) - stats[STAT_ACTIVEWEAPON] = ent->v.weapon; + stats[STAT_ACTIVEWEAPON] = ent->v->weapon; // stuff the sigil bits into the high bits of items for sbar - stats[STAT_ITEMS] = (int) ent->v.items | ((int) PR_GLOBAL(serverflags) << 28); + stats[STAT_ITEMS] = (int) ent->v->items | ((int) PR_GLOBAL(serverflags) << 28); if (fofs_items2) // ZQ_ITEMS2 extension stats[STAT_ITEMS] |= (int)EdictFieldFloat(ent, fofs_items2) << 23; - if (ent->v.health > 0 || client->spectator) // viewheight for PF_DEAD & PF_GIB is hardwired - stats[STAT_VIEWHEIGHT] = ent->v.view_ofs[2]; + if (ent->v->health > 0 || client->spectator) // viewheight for PF_DEAD & PF_GIB is hardwired + stats[STAT_VIEWHEIGHT] = ent->v->view_ofs[2]; for (i=0 ; istats[i]) @@ -982,7 +982,7 @@ static void SV_UpdateToReliableMessages (void) ent = sv_client->edict; - if (sv_client->old_frags != (int)ent->v.frags) + if (sv_client->old_frags != (int)ent->v->frags) { for (j=0, client = svs.clients ; jv.frags); + ClientReliableWrite_Short(client, (int) ent->v->frags); } if (sv.mvdrecording) @@ -999,11 +999,11 @@ static void SV_UpdateToReliableMessages (void) { MVD_MSG_WriteByte(svc_updatefrags); MVD_MSG_WriteByte(i); - MVD_MSG_WriteShort((int)ent->v.frags); + MVD_MSG_WriteShort((int)ent->v->frags); } } - sv_client->old_frags = (int) ent->v.frags; + sv_client->old_frags = (int) ent->v->frags; } // maxspeed/entgravity changes @@ -1096,7 +1096,7 @@ void SV_SendClientMessages (void) if (fofs_visibility) { for (i = 0; i < MAX_CLIENTS; ++i) { - ((eval_t *)((byte *)&(svs.clients[i].edict)->v + fofs_visibility))->_int = 0; + ((eval_t *)((byte *)(svs.clients[i].edict)->v + fofs_visibility))->_int = 0; } } @@ -1198,22 +1198,22 @@ static void SV_BotWriteDamage(client_t* c, int i) { edict_t* ent = c->edict; - if (c->edict->v.dmg_take || c->edict->v.dmg_save) { - if (ent->v.dmg_take || ent->v.dmg_save) { + if (c->edict->v->dmg_take || c->edict->v->dmg_save) { + if (ent->v->dmg_take || ent->v->dmg_save) { int length = 3 + 3 * msg_coordsize; if (MVDWrite_Begin(dem_single, i, length)) { - edict_t* other = PROG_TO_EDICT(ent->v.dmg_inflictor); + edict_t* other = PROG_TO_EDICT(ent->v->dmg_inflictor); MVD_MSG_WriteByte(svc_damage); - MVD_MSG_WriteByte(ent->v.dmg_save); - MVD_MSG_WriteByte(ent->v.dmg_take); + MVD_MSG_WriteByte(ent->v->dmg_save); + MVD_MSG_WriteByte(ent->v->dmg_take); for (i = 0; i < 3; i++) - MVD_MSG_WriteCoord(other->v.origin[i] + 0.5 * (other->v.mins[i] + other->v.maxs[i])); + MVD_MSG_WriteCoord(other->v->origin[i] + 0.5 * (other->v->mins[i] + other->v->maxs[i])); } - ent->v.dmg_take = 0; - ent->v.dmg_save = 0; + ent->v->dmg_take = 0; + ent->v->dmg_save = 0; } } } @@ -1258,21 +1258,21 @@ void MVD_WriteStats(void) ent = c->edict; memset (stats, 0, sizeof(stats)); - stats[STAT_HEALTH] = ent->v.health; - stats[STAT_WEAPON] = SV_ModelIndex(PR_GetEntityString(ent->v.weaponmodel)); - stats[STAT_AMMO] = ent->v.currentammo; - stats[STAT_ARMOR] = ent->v.armorvalue; - stats[STAT_SHELLS] = ent->v.ammo_shells; - stats[STAT_NAILS] = ent->v.ammo_nails; - stats[STAT_ROCKETS] = ent->v.ammo_rockets; - stats[STAT_CELLS] = ent->v.ammo_cells; - stats[STAT_ACTIVEWEAPON] = ent->v.weapon; + stats[STAT_HEALTH] = ent->v->health; + stats[STAT_WEAPON] = SV_ModelIndex(PR_GetEntityString(ent->v->weaponmodel)); + stats[STAT_AMMO] = ent->v->currentammo; + stats[STAT_ARMOR] = ent->v->armorvalue; + stats[STAT_SHELLS] = ent->v->ammo_shells; + stats[STAT_NAILS] = ent->v->ammo_nails; + stats[STAT_ROCKETS] = ent->v->ammo_rockets; + stats[STAT_CELLS] = ent->v->ammo_cells; + stats[STAT_ACTIVEWEAPON] = ent->v->weapon; - if (ent->v.health > 0) // viewheight for PF_DEAD & PF_GIB is hardwired - stats[STAT_VIEWHEIGHT] = ent->v.view_ofs[2]; + if (ent->v->health > 0) // viewheight for PF_DEAD & PF_GIB is hardwired + stats[STAT_VIEWHEIGHT] = ent->v->view_ofs[2]; // stuff the sigil bits into the high bits of items for sbar - stats[STAT_ITEMS] = (int) ent->v.items | ((int) PR_GLOBAL(serverflags) << 28); + stats[STAT_ITEMS] = (int) ent->v->items | ((int) PR_GLOBAL(serverflags) << 28); for (j = 0 ; j < MAX_CL_STATS; j++) { diff --git a/src/sv_user.c b/src/sv_user.c index cdb832674..71f6d70d2 100644 --- a/src/sv_user.c +++ b/src/sv_user.c @@ -70,11 +70,8 @@ static struct { antilag_client_info_t antilag_clients[MAX_CLIENTS]; } debug_info; -#ifdef MVD_PEXT1_SERVERSIDEWEAPON static void SV_DebugServerSideWeaponScript(client_t* cl, int best_impulse); static void SV_DebugServerSideWeaponInstruction(client_t* cl); -static void SV_UserSetWeaponRank(client_t* cl, const char* new_wrank); -#endif cvar_t sv_debug_weapons = { "sv_debug_weapons", "0" }; #endif @@ -82,6 +79,7 @@ cvar_t sv_debug_weapons = { "sv_debug_weapons", "0" }; cvar_t sv_debug_usercmd = { "sv_debug_usercmd", "0" }; cvar_t sv_debug_antilag = { "sv_debug_antilag", "0" }; +static void SV_UserSetWeaponRank(client_t* cl, const char* new_wrank); static void SV_DebugClientCommand(byte playernum, const usercmd_t* cmd, int dropnum_); extern vec3_t player_mins; @@ -177,6 +175,9 @@ Check that player's ping falls below sv_maxping value */ qbool PlayerCheckPing(void) { + + if (sv_client->maxping_met) return true; + int maxping = Q_atof(sv_maxping.string); int playerping = sv_client->frames[sv_client->netchan.incoming_acknowledged & UPDATE_MASK].ping_time * 1000; @@ -185,6 +186,7 @@ qbool PlayerCheckPing(void) SV_ClientPrintf(sv_client, PRINT_HIGH, "\nYour ping is too high for this server! Maximum ping is set to %i, your ping is %i.\nForcing spectator.\n\n", maxping, playerping); return false; } + sv_client->maxping_met = true; return true; } @@ -218,7 +220,7 @@ static void Cmd_New_f (void) { MSG_WriteByte (&sv_client->netchan.message, svc_stufftext); MSG_WriteString (&sv_client->netchan.message, "cmd pext\n"); - return; + return; } // do not proceed if realip is unknown @@ -402,7 +404,7 @@ static void Cmd_New_f (void) if (sv_client->rip_vip) MSG_WriteString (&sv_client->netchan.message, ""); else - MSG_WriteString (&sv_client->netchan.message, PR_GetEntityString(sv.edicts->v.message)); + MSG_WriteString (&sv_client->netchan.message, PR_GetEntityString(sv.edicts->v->message)); // send the movevars MSG_WriteFloat(&sv_client->netchan.message, movevars.gravity); @@ -436,7 +438,7 @@ static void Cmd_New_f (void) // send music MSG_WriteByte (&sv_client->netchan.message, svc_cdtrack); - MSG_WriteByte (&sv_client->netchan.message, sv.edicts->v.sounds); + MSG_WriteByte (&sv_client->netchan.message, sv.edicts->v->sounds); // send server info string MSG_WriteByte (&sv_client->netchan.message, svc_stufftext); @@ -722,7 +724,7 @@ static void Cmd_PreSpawn_f (void) for (i = buf - sv.static_entity_count; i < sv.num_baseline_edicts; ++i) { edict_t* svent = EDICT_NUM(i); - entity_state_t* s = &svent->e->baseline; + entity_state_t* s = &svent->e.baseline; if (sv_client->netchan.message.cursize >= (sv_client->netchan.message.maxsize / 2)) { break; @@ -740,13 +742,13 @@ static void Cmd_PreSpawn_f (void) else if (s->modelindex < 256) { MSG_WriteByte(&sv_client->netchan.message, svc_spawnbaseline); MSG_WriteShort(&sv_client->netchan.message, i); - MSG_WriteByte(&sv_client->netchan.message, svent->e->baseline.modelindex); - MSG_WriteByte(&sv_client->netchan.message, svent->e->baseline.frame); - MSG_WriteByte(&sv_client->netchan.message, svent->e->baseline.colormap); - MSG_WriteByte(&sv_client->netchan.message, svent->e->baseline.skinnum); + MSG_WriteByte(&sv_client->netchan.message, svent->e.baseline.modelindex); + MSG_WriteByte(&sv_client->netchan.message, svent->e.baseline.frame); + MSG_WriteByte(&sv_client->netchan.message, svent->e.baseline.colormap); + MSG_WriteByte(&sv_client->netchan.message, svent->e.baseline.skinnum); for (j = 0; j < 3; j++) { - MSG_WriteCoord(&sv_client->netchan.message, svent->e->baseline.origin[j]); - MSG_WriteAngle(&sv_client->netchan.message, svent->e->baseline.angles[j]); + MSG_WriteCoord(&sv_client->netchan.message, svent->e.baseline.origin[j]); + MSG_WriteAngle(&sv_client->netchan.message, svent->e.baseline.angles[j]); } } ++buf; @@ -891,20 +893,20 @@ static void SV_SpawnSpectator (void) int i; edict_t *e; - VectorClear (sv_player->v.origin); - VectorClear (sv_player->v.view_ofs); - sv_player->v.view_ofs[2] = 22; - sv_player->v.fixangle = true; - sv_player->v.movetype = MOVETYPE_NOCLIP; // progs can change this to MOVETYPE_FLY, for example + VectorClear (sv_player->v->origin); + VectorClear (sv_player->v->view_ofs); + sv_player->v->view_ofs[2] = 22; + sv_player->v->fixangle = true; + sv_player->v->movetype = MOVETYPE_NOCLIP; // progs can change this to MOVETYPE_FLY, for example // search for an info_playerstart to spawn the spectator at for (i=MAX_CLIENTS-1 ; iv.classname), "info_player_start")) + if (!strcmp(PR_GetEntityString(e->v->classname), "info_player_start")) { - VectorCopy (e->v.origin, sv_player->v.origin); - VectorCopy (e->v.angles, sv_player->v.angles); + VectorCopy (e->v->origin, sv_player->v->origin); + VectorCopy (e->v->angles, sv_player->v->angles); return; } } @@ -997,7 +999,7 @@ static void Cmd_Begin_f (void) ent = EDICT_NUM( 1 + (sv_client - svs.clients) ); MSG_WriteByte (&sv_client->netchan.message, svc_setangle); for (i = 0; i < 2; i++) - MSG_WriteAngle (&sv_client->netchan.message, ent->v.v_angle[i]); + MSG_WriteAngle (&sv_client->netchan.message, ent->v->v_angle[i]); MSG_WriteAngle (&sv_client->netchan.message, 0); } @@ -1088,7 +1090,6 @@ void SV_CompleteDownoload(void) } } - /* ================== Cmd_NextDownload_f @@ -1890,7 +1891,6 @@ static void SV_Say (qbool team) } } - /* ================== Cmd_Say_f @@ -1949,7 +1949,7 @@ Cmd_Kill_f */ static void Cmd_Kill_f (void) { - if (sv_player->v.health <= 0) + if (sv_player->v->health <= 0) { SV_ClientPrintf (sv_client, PRINT_HIGH, "Can't suicide -- already dead!\n"); return; @@ -2091,7 +2091,7 @@ static void Cmd_PTrack_f (void) sv_client->spec_track = 0; ent = EDICT_NUM(sv_client - svs.clients + 1); tent = EDICT_NUM(0); - ent->v.goalentity = EDICT_TO_PROG(tent); + ent->v->goalentity = EDICT_TO_PROG(tent); return; } @@ -2102,14 +2102,14 @@ static void Cmd_PTrack_f (void) sv_client->spec_track = 0; ent = EDICT_NUM(sv_client - svs.clients + 1); tent = EDICT_NUM(0); - ent->v.goalentity = EDICT_TO_PROG(tent); + ent->v->goalentity = EDICT_TO_PROG(tent); return; } sv_client->spec_track = i + 1; // now tracking ent = EDICT_NUM(sv_client - svs.clients + 1); tent = EDICT_NUM(i + 1); - ent->v.goalentity = EDICT_TO_PROG(tent); + ent->v->goalentity = EDICT_TO_PROG(tent); } /* @@ -2316,7 +2316,7 @@ static void Cmd_SetInfo_f (void) pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); - if (PR_UserInfoChanged()) + if (PR_UserInfoChanged(0)) return; // does not allowed to be changed by mod. Info_Set (&sv_client->_userinfo_ctx_, Cmd_Argv(1), Cmd_Argv(2)); @@ -2386,6 +2386,7 @@ static void Cmd_SetInfo_f (void) //<- ProcessUserInfoChange (sv_client, Cmd_Argv (1), oldval); + PR_UserInfoChanged(1); } void ProcessUserInfoChange (client_t* sv_client, const char* key, const char* old_value) @@ -2561,14 +2562,14 @@ static void SetUpClientEdict (client_t *cl, edict_t *ent) { ED_ClearEdict(ent); // restore client name. - PR_SetEntityString(ent, ent->v.netname, cl->name); + PR_SetEntityString(ent, ent->v->netname, cl->name); // so spec will have right goalentity - if speccing someone if(cl->spectator && cl->spec_track > 0) - ent->v.goalentity = EDICT_TO_PROG(svs.clients[cl->spec_track-1].edict); + ent->v->goalentity = EDICT_TO_PROG(svs.clients[cl->spec_track-1].edict); - ent->v.colormap = NUM_FOR_EDICT(ent); + ent->v->colormap = NUM_FOR_EDICT(ent); - ent->v.team = 0; // FIXME + ent->v->team = 0; // FIXME cl->entgravity = 1.0; if (fofs_gravity) @@ -3383,18 +3384,18 @@ static void AddLinksToPmove ( areanode_t *node ) next = l->next; check = EDICT_FROM_AREA(l); - if (check->v.owner == pl) + if (check->v->owner == pl) continue; // player's own missile - if (check->v.solid == SOLID_BSP - || check->v.solid == SOLID_BBOX - || check->v.solid == SOLID_SLIDEBOX) + if (check->v->solid == SOLID_BSP + || check->v->solid == SOLID_BBOX + || check->v->solid == SOLID_SLIDEBOX) { if (check == sv_player) continue; for (i=0 ; i<3 ; i++) - if (check->v.absmin[i] > pmove_maxs[i] - || check->v.absmax[i] < pmove_mins[i]) + if (check->v->absmin[i] > pmove_maxs[i] + || check->v->absmax[i] < pmove_mins[i]) break; if (i != 3) continue; @@ -3403,20 +3404,20 @@ static void AddLinksToPmove ( areanode_t *node ) pe = &pmove.physents[pmove.numphysent]; pmove.numphysent++; - VectorCopy (check->v.origin, pe->origin); + VectorCopy (check->v->origin, pe->origin); pe->info = NUM_FOR_EDICT(check); - if (check->v.solid == SOLID_BSP) { - if ((unsigned)check->v.modelindex >= MAX_MODELS) - SV_Error ("AddLinksToPmove: check->v.modelindex >= MAX_MODELS"); - pe->model = sv.models[(int)(check->v.modelindex)]; + if (check->v->solid == SOLID_BSP) { + if ((unsigned)check->v->modelindex >= MAX_MODELS) + SV_Error ("AddLinksToPmove: check->v->modelindex >= MAX_MODELS"); + pe->model = sv.models[(int)(check->v->modelindex)]; if (!pe->model) SV_Error ("SOLID_BSP with a non-bsp model"); } else { pe->model = NULL; - VectorCopy (check->v.mins, pe->mins); - VectorCopy (check->v.maxs, pe->maxs); + VectorCopy (check->v->mins, pe->mins); + VectorCopy (check->v->maxs, pe->maxs); } } } @@ -3433,22 +3434,22 @@ static void AddLinksToPmove ( areanode_t *node ) int SV_PMTypeForClient (client_t *cl) { - if (cl->edict->v.movetype == MOVETYPE_NOCLIP) { + if (cl->edict->v->movetype == MOVETYPE_NOCLIP) { if (cl->extensions & Z_EXT_PM_TYPE_NEW) return PM_SPECTATOR; return PM_OLD_SPECTATOR; } - if (cl->edict->v.movetype == MOVETYPE_FLY) + if (cl->edict->v->movetype == MOVETYPE_FLY) return PM_FLY; - if (cl->edict->v.movetype == MOVETYPE_NONE) + if (cl->edict->v->movetype == MOVETYPE_NONE) return PM_NONE; - if (cl->edict->v.movetype == MOVETYPE_LOCK) + if (cl->edict->v->movetype == MOVETYPE_LOCK) return PM_LOCK; - if (cl->edict->v.health <= 0) + if (cl->edict->v->health <= 0) return PM_DEAD; return PM_NORMAL; @@ -3536,36 +3537,36 @@ void SV_RunCmd (usercmd_t *ucmd, qbool inside, qbool second_attempt) //bliP: 24/ } // copy humans' intentions to progs - sv_player->v.button0 = ucmd->buttons & 1; - sv_player->v.button2 = (ucmd->buttons & 2) >> 1; - sv_player->v.button1 = (ucmd->buttons & 4) >> 2; + sv_player->v->button0 = ucmd->buttons & 1; + sv_player->v->button2 = (ucmd->buttons & 2) >> 1; + sv_player->v->button1 = (ucmd->buttons & 4) >> 2; if (ucmd->impulse) - sv_player->v.impulse = ucmd->impulse; + sv_player->v->impulse = ucmd->impulse; if (fofs_movement) { EdictFieldVector(sv_player, fofs_movement)[0] = ucmd->forwardmove; EdictFieldVector(sv_player, fofs_movement)[1] = ucmd->sidemove; EdictFieldVector(sv_player, fofs_movement)[2] = ucmd->upmove; } - //bliP: cuff + // bliP: cuff if (sv_client->cuff_time > curtime) - sv_player->v.button0 = sv_player->v.impulse = 0; + sv_player->v->button0 = sv_player->v->impulse = 0; //<- // clamp view angles ucmd->angles[PITCH] = bound(sv_minpitch.value, ucmd->angles[PITCH], sv_maxpitch.value); - if (!sv_player->v.fixangle && ! second_attempt) - VectorCopy (ucmd->angles, sv_player->v.v_angle); + if (!sv_player->v->fixangle && ! second_attempt) + VectorCopy (ucmd->angles, sv_player->v->v_angle); // model angles // show 1/3 the pitch angle and all the roll angle - if (sv_player->v.health > 0) + if (sv_player->v->health > 0) { - if (!sv_player->v.fixangle) + if (!sv_player->v->fixangle) { - sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; - sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; + sv_player->v->angles[PITCH] = -sv_player->v->v_angle[PITCH]/3; + sv_player->v->angles[YAW] = sv_player->v->v_angle[YAW]; } - sv_player->v.angles[ROLL] = 0; + sv_player->v->angles[ROLL] = 0; } sv_frametime = ucmd->msec * 0.001; @@ -3578,11 +3579,11 @@ void SV_RunCmd (usercmd_t *ucmd, qbool inside, qbool second_attempt) //bliP: 24/ vec3_t oldvelocity; float old_teleport_time; - VectorCopy (sv_player->v.velocity, originalvel); - onground = (int) sv_player->v.flags & FL_ONGROUND; + VectorCopy (sv_player->v->velocity, originalvel); + onground = (int) sv_player->v->flags & FL_ONGROUND; - VectorCopy (sv_player->v.velocity, oldvelocity); - old_teleport_time = sv_player->v.teleport_time; + VectorCopy (sv_player->v->velocity, oldvelocity); + old_teleport_time = sv_player->v->teleport_time; PR_GLOBAL(frametime) = sv_frametime; pr_global_struct->time = sv.time; @@ -3591,30 +3592,30 @@ void SV_RunCmd (usercmd_t *ucmd, qbool inside, qbool second_attempt) //bliP: 24/ if (pr_nqprogs) { - sv_player->v.teleport_time = old_teleport_time; - VectorCopy (oldvelocity, sv_player->v.velocity); + sv_player->v->teleport_time = old_teleport_time; + VectorCopy (oldvelocity, sv_player->v->velocity); } - if ( onground && originalvel[2] < 0 && sv_player->v.velocity[2] == 0 && - originalvel[0] == sv_player->v.velocity[0] && - originalvel[1] == sv_player->v.velocity[1] ) + if ( onground && originalvel[2] < 0 && sv_player->v->velocity[2] == 0 && + originalvel[0] == sv_player->v->velocity[0] && + originalvel[1] == sv_player->v->velocity[1] ) { // don't let KTeams mess with physics - sv_player->v.velocity[2] = originalvel[2]; + sv_player->v->velocity[2] = originalvel[2]; } SV_RunThink (sv_player); } // copy player state to pmove - VectorSubtract (sv_player->v.mins, player_mins, offset); - VectorAdd (sv_player->v.origin, offset, pmove.origin); - VectorCopy (sv_player->v.velocity, pmove.velocity); - VectorCopy (sv_player->v.v_angle, pmove.angles); - pmove.waterjumptime = sv_player->v.teleport_time; + VectorSubtract (sv_player->v->mins, player_mins, offset); + VectorAdd (sv_player->v->origin, offset, pmove.origin); + VectorCopy (sv_player->v->velocity, pmove.velocity); + VectorCopy (sv_player->v->v_angle, pmove.angles); + pmove.waterjumptime = sv_player->v->teleport_time; pmove.cmd = *ucmd; pmove.pm_type = SV_PMTypeForClient (sv_client); - pmove.onground = ((int)sv_player->v.flags & FL_ONGROUND) != 0; + pmove.onground = ((int)sv_player->v->flags & FL_ONGROUND) != 0; pmove.jump_held = sv_client->jump_held; pmove.jump_msec = 0; @@ -3649,7 +3650,7 @@ FIXME #ifdef USE_PR2 // This is a temporary hack for Frogbots, who adjust after bumping into things // Better would be to provide a way to simulate a move command, but at least this doesn't require API change - if (blocked && !second_attempt && sv_client->isBot && sv_player->v.blocked) + if (blocked && !second_attempt && sv_client->isBot && sv_player->v->blocked) { pr_global_struct->self = EDICT_TO_PROG(sv_player); @@ -3658,14 +3659,14 @@ FIXME VectorCopy (pmove.velocity, pr_global_struct->trace_plane_normal); if (pmove.onground) { - pr_global_struct->trace_allsolid = (int) sv_player->v.flags | FL_ONGROUND; + pr_global_struct->trace_allsolid = (int) sv_player->v->flags | FL_ONGROUND; pr_global_struct->trace_ent = EDICT_TO_PROG(EDICT_NUM(pmove.physents[pmove.groundent].info)); } else { - pr_global_struct->trace_allsolid = (int) sv_player->v.flags & ~FL_ONGROUND; + pr_global_struct->trace_allsolid = (int) sv_player->v->flags & ~FL_ONGROUND; } // Give the mod a chance to replace the command - PR_EdictBlocked (sv_player->v.blocked); + PR_EdictBlocked (sv_player->v->blocked); // Run the command again SV_RunCmd (ucmd, false, true); @@ -3675,25 +3676,25 @@ FIXME // get player state back out of pmove sv_client->jump_held = pmove.jump_held; - sv_player->v.teleport_time = pmove.waterjumptime; + sv_player->v->teleport_time = pmove.waterjumptime; if (pr_nqprogs) - sv_player->v.flags = ((int)sv_player->v.flags & ~FL_WATERJUMP) | (pmove.waterjumptime ? FL_WATERJUMP : 0); - sv_player->v.waterlevel = pmove.waterlevel; - sv_player->v.watertype = pmove.watertype; + sv_player->v->flags = ((int)sv_player->v->flags & ~FL_WATERJUMP) | (pmove.waterjumptime ? FL_WATERJUMP : 0); + sv_player->v->waterlevel = pmove.waterlevel; + sv_player->v->watertype = pmove.watertype; if (pmove.onground) { - sv_player->v.flags = (int) sv_player->v.flags | FL_ONGROUND; - sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[pmove.groundent].info)); + sv_player->v->flags = (int) sv_player->v->flags | FL_ONGROUND; + sv_player->v->groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[pmove.groundent].info)); } else { - sv_player->v.flags = (int) sv_player->v.flags & ~FL_ONGROUND; + sv_player->v->flags = (int) sv_player->v->flags & ~FL_ONGROUND; } - VectorSubtract (pmove.origin, offset, sv_player->v.origin); - VectorCopy (pmove.velocity, sv_player->v.velocity); + VectorSubtract (pmove.origin, offset, sv_player->v->origin); + VectorCopy (pmove.velocity, sv_player->v->velocity); - VectorCopy (pmove.angles, sv_player->v.v_angle); + VectorCopy (pmove.angles, sv_player->v->v_angle); - if (sv_player->v.solid != SOLID_NOT) + if (sv_player->v->solid != SOLID_NOT) { // link into place and touch triggers SV_LinkEdict (sv_player, true); @@ -3704,11 +3705,11 @@ FIXME edict_t *ent; n = pmove.physents[pmove.touchindex[i]].info; ent = EDICT_NUM(n); - if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) + if (!ent->v->touch || (playertouch[n/8]&(1<<(n%8)))) continue; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv_player); - PR_EdictTouch (ent->v.touch); + PR_EdictTouch (ent->v->touch); playertouch[n/8] |= 1 << (n%8); } } @@ -3726,7 +3727,7 @@ typedef struct ssw_info_s { // Ranks best weapon for player void SV_ServerSideWeaponRank(client_t* client, int* best_weapon, int* best_impulse, int* hide_weapon, int* hide_impulse) { - entvars_t* ent = &client->edict->v; + entvars_t* ent = client->edict->v; int i; int items = (int)ent->items; int weapon = (int)ent->weapon; @@ -3841,7 +3842,6 @@ static void SV_ExecuteServerSideWeaponForgetOrder(client_t* sv_client, int best_ if (Info_Get(&sv_client->_userinfo_ctx_, "dev")[0] == '1') { SV_ClientPrintf(sv_client, PRINT_HIGH, "Best: %d, forgetorder enabled\n", best_impulse); } - } sv_client->weaponswitch_priority[0] = best_impulse; sv_client->weaponswitch_priority[1] = (hide_impulse == 1 || best_impulse == 2 ? 1 : 2); @@ -3867,7 +3867,7 @@ static void SV_ExecuteServerSideWeaponHideOnDeath(client_t* sv_client, int hide_ { char new_wrank[16] = { 0 }; - if (sv_client->edict->v.health > 0.0f || !sv_client->weaponswitch_hide_on_death) { + if (sv_client->edict->v->health > 0.0f || !sv_client->weaponswitch_hide_on_death) { return; } @@ -3884,14 +3884,14 @@ static void SV_ExecuteServerSideWeaponHideOnDeath(client_t* sv_client, int hide_ SV_NotifyUserOfBestWeapon(sv_client, hide_impulse); SV_UserSetWeaponRank(sv_client, new_wrank); - if (Info_Get(&sv_client->_userinfo_ctx_, "dev")[0] == '1' && sv_client->edict->v.weapon != hide_weapon) { + if (Info_Get(&sv_client->_userinfo_ctx_, "dev")[0] == '1' && sv_client->edict->v->weapon != hide_weapon) { SV_ClientPrintf(sv_client, PRINT_HIGH, "Hiding on death: %d\n", hide_impulse); } } static void SV_ServerSideWeaponLogic_PrePostThink(client_t* sv_client, ssw_info_t* ssw) { - entvars_t* ent = &sv_client->edict->v; + entvars_t* ent = sv_client->edict->v; qbool dev_trace = (Info_Get(&sv_client->_userinfo_ctx_, "dev")[0] == '1'); ssw->firing = (ent->button0 != 0); @@ -3906,10 +3906,7 @@ static void SV_ServerSideWeaponLogic_PrePostThink(client_t* sv_client, ssw_info_ if (mode == 2) { mode = (ssw->firing ? 0 : 1); } - - // by this point we should be down to 0 or 1 - switch_to_best_weapon = (mode == 0 && sv_client->weaponswitch_pending) || (ssw->firing && !sv_client->weaponswitch_wasfiring); - switch_to_best_weapon &= (ent->health >= 1.0f); // Don't try and switch if dead + switch_to_best_weapon = sv_client->weaponswitch_pending && (mode == 0 || ssw->firing) && (ent->health >= 1.0f); SV_ServerSideWeaponRank(sv_client, &ssw->best_weapon, &best_impulse, &ssw->hide_weapon, &hide_impulse); @@ -3919,9 +3916,7 @@ static void SV_ServerSideWeaponLogic_PrePostThink(client_t* sv_client, ssw_info_ if (switch_to_best_weapon && sv_client->weaponswitch_forgetorder) { SV_ExecuteServerSideWeaponForgetOrder(sv_client, best_impulse, hide_impulse); } - else { - SV_ExecuteServerSideWeaponHideOnDeath(sv_client, hide_impulse, ssw->hide_weapon); - } + SV_ExecuteServerSideWeaponHideOnDeath(sv_client, hide_impulse, ssw->hide_weapon); if (!ent->impulse) { if (switch_to_best_weapon) { @@ -3963,20 +3958,26 @@ static void SV_ServerSideWeaponLogic_PrePostThink(client_t* sv_client, ssw_info_ static void SV_ServerSideWeaponLogic_PostPostThink(client_t* sv_client, ssw_info_t* ssw) { - entvars_t* ent = &sv_client->edict->v; + entvars_t* ent = sv_client->edict->v; qbool dev_trace = (Info_Get(&sv_client->_userinfo_ctx_, "dev")[0] == '1'); if (ssw->impulse_set) { qbool hide_failed = (ssw->impulse_set == 1 && ent->weapon != ssw->hide_weapon); qbool pickbest_failed = (ssw->impulse_set == 2 && ent->weapon != ssw->best_weapon); + qbool failure = (hide_failed || pickbest_failed); ent->impulse = 0; if (dev_trace) { - SV_ClientPrintf(sv_client, PRINT_HIGH, "... %s failed, will try again\n", ssw->impulse_set == 1 ? "hide" : "pickbest"); + if (failure) { + SV_ClientPrintf(sv_client, PRINT_HIGH, "... %s failed, will try again\n", ssw->impulse_set == 1 ? "hide" : "pickbest"); + } + else { + SV_ClientPrintf(sv_client, PRINT_HIGH, "... %s successful, stopping\n", ssw->impulse_set == 1 ? "hide" : "pickbest"); + } } - sv_client->weaponswitch_pending &= (hide_failed || pickbest_failed); + sv_client->weaponswitch_pending &= failure; } if (ssw->hiding && ent->weapon == ssw->hide_weapon) { if (dev_trace) { @@ -4003,30 +4004,32 @@ void SV_PostRunCmd(void) { vec3_t originalvel; qbool onground; - // run post-think +#ifdef MVD_PEXT1_SERVERSIDEWEAPON + ssw_info_t ssw = { 0 }; +#endif + if (!sv_client->spectator) { #ifdef MVD_PEXT1_SERVERSIDEWEAPON - ssw_info_t ssw = { 0 }; SV_ServerSideWeaponLogic_PrePostThink(sv_client, &ssw); #endif - onground = (int) sv_player->v.flags & FL_ONGROUND; + onground = (int) sv_player->v->flags & FL_ONGROUND; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); - VectorCopy (sv_player->v.velocity, originalvel); + VectorCopy (sv_player->v->velocity, originalvel); PR_GameClientPostThink(0); - if ( onground && originalvel[2] < 0 && sv_player->v.velocity[2] == 0 - && originalvel[0] == sv_player->v.velocity[0] - && originalvel[1] == sv_player->v.velocity[1] ) { + if ( onground && originalvel[2] < 0 && sv_player->v->velocity[2] == 0 + && originalvel[0] == sv_player->v->velocity[0] + && originalvel[1] == sv_player->v->velocity[1] ) { // don't let KTeams mess with physics - sv_player->v.velocity[2] = originalvel[2]; + sv_player->v->velocity[2] = originalvel[2]; } if (pr_nqprogs) - VectorCopy (originalvel, sv_player->v.velocity); + VectorCopy (originalvel, sv_player->v->velocity); if (pr_nqprogs) SV_RunNQNewmis (); @@ -4045,7 +4048,6 @@ void SV_PostRunCmd(void) } } -#ifdef MVD_PEXT1_SERVERSIDEWEAPON /* SV_UserSetWeaponRank Sets wrank userinfo for mods to pick best weapon based on user's preferences @@ -4063,7 +4065,6 @@ static void SV_UserSetWeaponRank(client_t* cl, const char* new_wrank) } } } -#endif // SV_RotateCmd // Rotates client command so a high-ping player can better control direction as they exit teleporters on high-ping @@ -4080,7 +4081,7 @@ void SV_RotateCmd(client_t* cl, usercmd_t* cmd_) cmd_->forwardmove = result[1]; } else { - cmd_->angles[YAW] = (cl->edict)->v.angles[YAW]; + cmd_->angles[YAW] = (cl->edict)->v->angles[YAW]; } } @@ -4123,10 +4124,10 @@ static void SV_ExecuteClientMove(client_t* cl, usercmd_t oldest, usercmd_t oldcm #ifdef MVD_PEXT1_SERVERSIDEWEAPON { // This is necessary to interrupt LG/SNG where the firing takes place inside animation frames - if (sv_client->weaponswitch_enabled && sv_client->weaponswitch_pending && !sv_client->edict->v.impulse) { - sv_client->edict->v.impulse = 255; + if (sv_client->weaponswitch_enabled && sv_client->weaponswitch_pending && !sv_client->edict->v->impulse) { + sv_client->edict->v->impulse = 255; SV_RunCmd(&newcmd, false, false); - sv_client->edict->v.impulse = 0; + sv_client->edict->v->impulse = 0; } else { SV_RunCmd(&newcmd, false, false); @@ -4276,6 +4277,7 @@ static void SV_DebugServerSideWeaponInstruction(client_t* cl) } } } +#endif static void SV_DebugServerSideWeaponScript(client_t* cl, int best_impulse) { @@ -4284,7 +4286,7 @@ static void SV_DebugServerSideWeaponScript(client_t* cl, int best_impulse) char encoded[128] = { 0 }; char* w; char* o; - entvars_t* ent = &cl->edict->v; + entvars_t* ent = cl->edict->v; strlcpy(old_wrank, Info_Get(&cl->_userinfo_ctx_, "w_rank"), sizeof(old_wrank)); @@ -4299,8 +4301,7 @@ static void SV_DebugServerSideWeaponScript(client_t* cl, int best_impulse) SV_DebugWriteWeaponScript(cl - svs.clients, true, ent->items, ent->ammo_shells, ent->ammo_nails, ent->ammo_rockets, ent->ammo_cells, best_impulse, encoded); } } -#endif // #ifdef MVD_PEXT1_SERVERSIDEWEAPON -#endif // #ifdef MVD_PEXT1_DEBUG_WEAPON +#endif /* =================== @@ -4383,7 +4384,7 @@ void SV_ExecuteClientMessage (client_t *cl) int j; // don't hit dead players - if (target_cl->state != cs_spawned || target_cl->antilag_position_next == 0 || (target_cl->spectator == 0 && target_cl->edict->v.health <= 0)) { + if (target_cl->state != cs_spawned || target_cl->antilag_position_next == 0 || (target_cl->spectator == 0 && target_cl->edict->v->health <= 0)) { cl->laggedents[i].present = false; continue; } @@ -4393,7 +4394,7 @@ void SV_ExecuteClientMessage (client_t *cl) // target player's movement commands are late, extrapolate his position based on velocity if (target_time > target_cl->localtime) { - VectorMA(target_cl->edict->v.origin, min(target_time - target_cl->localtime, MAX_EXTRAPOLATE), target_cl->edict->v.velocity, cl->laggedents[i].laggedpos); + VectorMA(target_cl->edict->v->origin, min(target_time - target_cl->localtime, MAX_EXTRAPOLATE), target_cl->edict->v->velocity, cl->laggedents[i].laggedpos); continue; } @@ -4603,7 +4604,7 @@ void SV_ExecuteClientMessage (client_t *cl) if (sv_antilag.value) { if (cl->antilag_position_next == 0 || cl->antilag_positions[(cl->antilag_position_next - 1) % MAX_ANTILAG_POSITIONS].localtime < cl->localtime) { cl->antilag_positions[cl->antilag_position_next % MAX_ANTILAG_POSITIONS].localtime = cl->localtime; - VectorCopy(cl->edict->v.origin, cl->antilag_positions[cl->antilag_position_next % MAX_ANTILAG_POSITIONS].origin); + VectorCopy(cl->edict->v->origin, cl->antilag_positions[cl->antilag_position_next % MAX_ANTILAG_POSITIONS].origin); cl->antilag_position_next++; } } else { @@ -4611,7 +4612,6 @@ void SV_ExecuteClientMessage (client_t *cl) } break; - case clc_stringcmd: s = MSG_ReadString (); s[1023] = 0; @@ -4625,7 +4625,7 @@ void SV_ExecuteClientMessage (client_t *cl) // only allowed by spectators if (sv_client->spectator) { - VectorCopy(o, sv_player->v.origin); + VectorCopy(o, sv_player->v->origin); SV_LinkEdict(sv_player, false); } break; diff --git a/src/sv_world.c b/src/sv_world.c index 89e79996f..f8f7b8703 100644 --- a/src/sv_world.c +++ b/src/sv_world.c @@ -61,15 +61,15 @@ hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) // decide which clipping hull to use, based on the size - if (ent->v.solid == SOLID_BSP) + if (ent->v->solid == SOLID_BSP) { // explicit hulls in the BSP model - if (ent->v.movetype != MOVETYPE_PUSH) + if (ent->v->movetype != MOVETYPE_PUSH) SV_Error ("SOLID_BSP without MOVETYPE_PUSH"); - if ((unsigned)ent->v.modelindex >= MAX_MODELS) + if ((unsigned)ent->v->modelindex >= MAX_MODELS) SV_Error ("SV_HullForEntity: ent.modelindex >= MAX_MODELS"); - model = sv.models[(int)ent->v.modelindex]; + model = sv.models[(int)ent->v->modelindex]; if (!model) SV_Error ("SOLID_BSP with a non-bsp model"); @@ -83,16 +83,16 @@ hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) // calculate an offset value to center the origin VectorSubtract (hull->clip_mins, mins, offset); - VectorAdd (offset, ent->v.origin, offset); + VectorAdd (offset, ent->v->origin, offset); } else { // create a temp hull from bounding box sizes - VectorSubtract (ent->v.mins, maxs, hullmins); - VectorSubtract (ent->v.maxs, mins, hullmaxs); + VectorSubtract (ent->v->mins, maxs, hullmins); + VectorSubtract (ent->v->maxs, mins, hullmaxs); hull = CM_HullForBox (hullmins, hullmaxs); - VectorCopy (ent->v.origin, offset); + VectorCopy (ent->v->origin, offset); } @@ -215,10 +215,10 @@ SV_UnlinkEdict */ void SV_UnlinkEdict (edict_t *ent) { - if (!ent->e->area.prev) + if (!ent->e.area.prev) return; // not linked in anywhere - RemoveLink (&ent->e->area); - ent->e->area.prev = ent->e->area.next = NULL; + RemoveLink (&ent->e.area); + ent->e.area.prev = ent->e.area.next = NULL; } /* @@ -244,15 +244,15 @@ int SV_AreaEdicts (vec3_t mins, vec3_t maxs, edict_t **edicts, int max_edicts, i for (l = start->next ; l != start ; l = l->next) { touch = EDICT_FROM_AREA(l); - if (touch->v.solid == SOLID_NOT) + if (touch->v->solid == SOLID_NOT) continue; - if (mins[0] > touch->v.absmax[0] - || mins[1] > touch->v.absmax[1] - || mins[2] > touch->v.absmax[2] - || maxs[0] < touch->v.absmin[0] - || maxs[1] < touch->v.absmin[1] - || maxs[2] < touch->v.absmin[2]) + if (mins[0] > touch->v->absmax[0] + || mins[1] > touch->v->absmax[1] + || mins[2] > touch->v->absmax[2] + || maxs[0] < touch->v->absmin[0] + || maxs[1] < touch->v->absmin[1] + || maxs[2] < touch->v->absmin[2]) continue; if (count == max_edicts) @@ -301,7 +301,7 @@ static void SV_TouchLinks ( edict_t *ent, areanode_t *node ) edict_t *touchlist[MAX_EDICTS], *touch; int old_self, old_other; - numtouch = SV_AreaEdicts (ent->v.absmin, ent->v.absmax, touchlist, sv.max_edicts, AREA_TRIGGERS); + numtouch = SV_AreaEdicts(ent->v->absmin, ent->v->absmax, touchlist, sv.max_edicts, AREA_TRIGGERS); // touch linked edicts for (i = 0; i < numtouch; i++) @@ -309,7 +309,7 @@ static void SV_TouchLinks ( edict_t *ent, areanode_t *node ) touch = touchlist[i]; if (touch == ent) continue; - if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) + if (!touch->v->touch || touch->v->solid != SOLID_TRIGGER) continue; old_self = pr_global_struct->self; @@ -318,7 +318,7 @@ static void SV_TouchLinks ( edict_t *ent, areanode_t *node ) pr_global_struct->self = EDICT_TO_PROG(touch); pr_global_struct->other = EDICT_TO_PROG(ent); pr_global_struct->time = sv.time; - PR_EdictTouch (touch->v.touch); + PR_EdictTouch (touch->v->touch); pr_global_struct->self = old_self; pr_global_struct->other = old_other; @@ -334,11 +334,11 @@ void SV_LinkToLeafs (edict_t *ent) { int i, leafnums[MAX_ENT_LEAFS]; - ent->e->num_leafs = CM_FindTouchedLeafs (ent->v.absmin, ent->v.absmax, leafnums, + ent->e.num_leafs = CM_FindTouchedLeafs (ent->v->absmin, ent->v->absmax, leafnums, MAX_ENT_LEAFS, 0, NULL); - for (i = 0; i < ent->e->num_leafs; i++) { - // ent->e->leafnums are real leafnum minus one (for pvs checks) - ent->e->leafnums[i] = leafnums[i] - 1; + for (i = 0; i < ent->e.num_leafs; i++) { + // ent->e.leafnums are real leafnum minus one (for pvs checks) + ent->e.leafnums[i] = leafnums[i] - 1; } } @@ -353,48 +353,48 @@ void SV_LinkEdict (edict_t *ent, qbool touch_triggers) { areanode_t *node; - if (ent->e->area.prev) + if (ent->e.area.prev) SV_UnlinkEdict (ent); // unlink from old position - + if (ent == sv.edicts) return; // don't add the world - if (ent->e->free) + if (ent->e.free) return; // set the abs box - VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin); - VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax); + VectorAdd (ent->v->origin, ent->v->mins, ent->v->absmin); + VectorAdd (ent->v->origin, ent->v->maxs, ent->v->absmax); // // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded // - if ((int)ent->v.flags & FL_ITEM) + if ((int)ent->v->flags & FL_ITEM) { - ent->v.absmin[0] -= 15; - ent->v.absmin[1] -= 15; - ent->v.absmax[0] += 15; - ent->v.absmax[1] += 15; + ent->v->absmin[0] -= 15; + ent->v->absmin[1] -= 15; + ent->v->absmax[0] += 15; + ent->v->absmax[1] += 15; } else { // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch - ent->v.absmin[0] -= 1; - ent->v.absmin[1] -= 1; - ent->v.absmin[2] -= 1; - ent->v.absmax[0] += 1; - ent->v.absmax[1] += 1; - ent->v.absmax[2] += 1; + ent->v->absmin[0] -= 1; + ent->v->absmin[1] -= 1; + ent->v->absmin[2] -= 1; + ent->v->absmax[0] += 1; + ent->v->absmax[1] += 1; + ent->v->absmax[2] += 1; } - + // link to PVS leafs - if (ent->v.modelindex) + if (ent->v->modelindex) SV_LinkToLeafs (ent); else - ent->e->num_leafs = 0; + ent->e.num_leafs = 0; - if (ent->v.solid == SOLID_NOT) + if (ent->v->solid == SOLID_NOT) return; // find the first node that the ent's box crosses @@ -403,20 +403,20 @@ void SV_LinkEdict (edict_t *ent, qbool touch_triggers) { if (node->axis == -1) break; - if (ent->v.absmin[node->axis] > node->dist) + if (ent->v->absmin[node->axis] > node->dist) node = node->children[0]; - else if (ent->v.absmax[node->axis] < node->dist) + else if (ent->v->absmax[node->axis] < node->dist) node = node->children[1]; else - break; // crosses the node + break; // crosses the node } // link it in - if (ent->v.solid == SOLID_TRIGGER) - InsertLinkBefore (&ent->e->area, &node->trigger_edicts); + if (ent->v->solid == SOLID_TRIGGER) + InsertLinkBefore (&ent->e.area, &node->trigger_edicts); else - InsertLinkBefore (&ent->e->area, &node->solid_edicts); + InsertLinkBefore (&ent->e.area, &node->solid_edicts); // if touch_triggers, touch all entities at this node and decend for more if (touch_triggers) @@ -458,11 +458,11 @@ edict_t *SV_TestEntityPosition (edict_t *ent) { trace_t trace; - if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) + if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT) // only clip against bmodels - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NOMONSTERS, ent); + trace = SV_Trace (ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, MOVE_NOMONSTERS, ent); else - trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent); + trace = SV_Trace(ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, MOVE_NORMAL, ent); if (trace.startsolid) return sv.edicts; @@ -536,32 +536,32 @@ void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip ) touch = touchlist[i]; if (touch == clip->passedict) continue; - if (touch->v.solid == SOLID_TRIGGER) + if (touch->v->solid == SOLID_TRIGGER) SV_Error ("Trigger in clipping list"); - if ((clip->type & MOVE_NOMONSTERS) && touch->v.solid != SOLID_BSP) + if ((clip->type & MOVE_NOMONSTERS) && touch->v->solid != SOLID_BSP) continue; - if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) + if (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0]) continue; // points never interact if (clip->type & MOVE_LAGGED) { //can't touch lagged ents - we do an explicit test for them later in SV_AntilagClipCheck. - if (touch->e->entnum - 1 < w.maxlagents) - if (w.lagents[touch->e->entnum - 1].present) + if (touch->e.entnum - 1 < w.maxlagents) + if (w.lagents[touch->e.entnum - 1].present) continue; } if (clip->passedict) { - if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) + if (PROG_TO_EDICT(touch->v->owner) == clip->passedict) continue; // don't clip against own missiles - if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) + if (PROG_TO_EDICT(clip->passedict->v->owner) == touch) continue; // don't clip against owner } - if ((int)touch->v.flags & FL_MONSTER) + if ((int)touch->v->flags & FL_MONSTER) trace = SV_ClipMoveToEntity (touch, NULL, clip->start, clip->mins2, clip->maxs2, clip->end); else trace = SV_ClipMoveToEntity (touch, NULL, clip->start, clip->mins, clip->maxs, clip->end); @@ -618,16 +618,16 @@ void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t b void SV_AntilagReset (edict_t *ent) { - if (ent->e->entnum == 0 || ent->e->entnum > MAX_CLIENTS) + if (ent->e.entnum == 0 || ent->e.entnum > MAX_CLIENTS) return; - svs.clients[ent->e->entnum - 1].antilag_position_next = 0; + svs.clients[ent->e.entnum - 1].antilag_position_next = 0; } void SV_AntilagClipSetUp ( areanode_t *node, moveclip_t *clip ) { edict_t *passedict = clip->passedict; - int entnum = passedict->e->entnum; + int entnum = passedict->e.entnum; clip->type &= ~MOVE_LAGGED; @@ -638,9 +638,9 @@ void SV_AntilagClipSetUp ( areanode_t *node, moveclip_t *clip ) w.maxlagents = svs.clients[entnum - 1].laggedents_count; w.lagentsfrac = svs.clients[entnum - 1].laggedents_frac; } - else if (passedict->v.owner) + else if (passedict->v->owner) { - int owner = PROG_TO_EDICT(passedict->v.owner)->e->entnum; + int owner = PROG_TO_EDICT(passedict->v->owner)->e.entnum; if (owner && owner <= MAX_CLIENTS && !svs.clients[owner - 1].isBot) { @@ -668,38 +668,38 @@ void SV_AntilagClipCheck ( areanode_t *node, moveclip_t *clip ) continue; touch = EDICT_NUM(i + 1); - if (touch->v.solid == SOLID_NOT) + if (touch->v->solid == SOLID_NOT) continue; if (touch == clip->passedict) continue; - if (touch->v.solid == SOLID_TRIGGER) - SV_Error ("Trigger (%s) in clipping list", PR_GetEntityString(touch->v.classname)); + if (touch->v->solid == SOLID_TRIGGER) + SV_Error ("Trigger (%s) in clipping list", PR_GetEntityString(touch->v->classname)); - if ((clip->type & MOVE_NOMONSTERS) && touch->v.solid != SOLID_BSP) + if ((clip->type & MOVE_NOMONSTERS) && touch->v->solid != SOLID_BSP) continue; - VectorInterpolate(touch->v.origin, w.lagentsfrac, w.lagents[i].laggedpos, lp); + VectorInterpolate(touch->v->origin, w.lagentsfrac, w.lagents[i].laggedpos, lp); - if ( clip->boxmins[0] > lp[0]+touch->v.maxs[0] - || clip->boxmins[1] > lp[1]+touch->v.maxs[1] - || clip->boxmins[2] > lp[2]+touch->v.maxs[2] - || clip->boxmaxs[0] < lp[0]+touch->v.mins[0] - || clip->boxmaxs[1] < lp[1]+touch->v.mins[1] - || clip->boxmaxs[2] < lp[2]+touch->v.mins[2] ) + if ( clip->boxmins[0] > lp[0]+touch->v->maxs[0] + || clip->boxmins[1] > lp[1]+touch->v->maxs[1] + || clip->boxmins[2] > lp[2]+touch->v->maxs[2] + || clip->boxmaxs[0] < lp[0]+touch->v->mins[0] + || clip->boxmaxs[1] < lp[1]+touch->v->mins[1] + || clip->boxmaxs[2] < lp[2]+touch->v->mins[2] ) continue; - if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) + if (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0]) continue; // points never interact if (clip->passedict) { - if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) + if (PROG_TO_EDICT(touch->v->owner) == clip->passedict) continue; // don't clip against own missiles - if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) + if (PROG_TO_EDICT(clip->passedict->v->owner) == touch) continue; // don't clip against owner } - if ((int)touch->v.flags & FL_MONSTER) + if ((int)touch->v->flags & FL_MONSTER) trace = SV_ClipMoveToEntity (touch, &lp, clip->start, clip->mins2, clip->maxs2, clip->end); else trace = SV_ClipMoveToEntity (touch, &lp, clip->start, clip->mins, clip->maxs, clip->end); diff --git a/src/sv_world.h b/src/sv_world.h index 18d59093d..3aa47e6c0 100644 --- a/src/sv_world.h +++ b/src/sv_world.h @@ -51,12 +51,12 @@ void SV_ClearWorld (void); void SV_UnlinkEdict (edict_t *ent); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself -// flags ent->v.modified +// flags ent->v->modified void SV_LinkEdict (edict_t *ent, qbool touch_triggers); // Needs to be called any time an entity changes origin, mins, maxs, or solid -// flags ent->v.modified -// sets ent->v.absmin and ent->v.absmax +// flags ent->v->modified +// sets ent->v->absmin and ent->v->absmax // if touchtriggers, calls prog functions for the intersected triggers int SV_PointContents (vec3_t p); diff --git a/src/sys.h b/src/sys.h index 4963ce131..b2c4334b4 100644 --- a/src/sys.h +++ b/src/sys.h @@ -19,11 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sys.h -- non-portable functions -#ifdef _WIN32 -#define PATH_SEPARATOR "\\" -#else -#define PATH_SEPARATOR "/" -#endif +#include "q_platform.h" #ifdef _WIN32 #define Sys_MSleep(x) Sleep(x) @@ -42,10 +38,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define _MAX_DIR 1024 typedef void *DL_t; -#define DLEXT "so" #else typedef HMODULE DL_t; -#define DLEXT "dll" #endif DL_t Sys_DLOpen(const char *path); @@ -199,6 +193,7 @@ int Sys_compare_by_name (const void *a, const void *b); #define SORT_BY_DATE 1 #define SORT_BY_NAME 2 +#define ARRAY_LEN(x) (sizeof(x) / sizeof(*(x))) // library loading. diff --git a/src/vm.c b/src/vm.c new file mode 100644 index 000000000..6a4929db9 --- /dev/null +++ b/src/vm.c @@ -0,0 +1,1617 @@ +/* + * 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 + * + * + */ +// vm.c -- virtual machine +/* + + +intermix code and data +symbol table + +a dll has one imported function: VM_SystemCall +and one exported function: Perform + + +*/ +#ifdef USE_PR2 +#include "qwsvdef.h" +#include "vm_local.h" + +opcode_info_t ops[ OP_MAX ] = +{ + { 0, 0, 0, 0 }, // undef + { 0, 0, 0, 0 }, // ignore + { 0, 0, 0, 0 }, // break + + { 4, 0, 0, 0 }, // enter + { 4,-4, 0, 0 }, // leave + { 0, 0, 1, 0 }, // call + { 0, 4, 0, 0 }, // push + { 0,-4, 1, 0 }, // pop + + { 4, 4, 0, 0 }, // const + { 4, 4, 0, 0 }, // local + { 0,-4, 1, 0 }, // jump + + { 4,-8, 2, JUMP }, // eq + { 4,-8, 2, JUMP }, // ne + + { 4,-8, 2, JUMP }, // lti + { 4,-8, 2, JUMP }, // lei + { 4,-8, 2, JUMP }, // gti + { 4,-8, 2, JUMP }, // gei + + { 4,-8, 2, JUMP }, // ltu + { 4,-8, 2, JUMP }, // leu + { 4,-8, 2, JUMP }, // gtu + { 4,-8, 2, JUMP }, // geu + + { 4,-8, 2, JUMP }, // eqf + { 4,-8, 2, JUMP }, // nef + + { 4,-8, 2, JUMP }, // ltf + { 4,-8, 2, JUMP }, // lef + { 4,-8, 2, JUMP }, // gtf + { 4,-8, 2, JUMP }, // gef + + { 0, 0, 1, 0 }, // load1 + { 0, 0, 1, 0 }, // load2 + { 0, 0, 1, 0 }, // load4 + { 0,-8, 2, 0 }, // store1 + { 0,-8, 2, 0 }, // store2 + { 0,-8, 2, 0 }, // store4 + { 1,-4, 1, 0 }, // arg + { 4,-8, 2, 0 }, // bcopy + + { 0, 0, 1, 0 }, // sex8 + { 0, 0, 1, 0 }, // sex16 + + { 0, 0, 1, 0 }, // negi + { 0,-4, 3, 0 }, // add + { 0,-4, 3, 0 }, // sub + { 0,-4, 3, 0 }, // divi + { 0,-4, 3, 0 }, // divu + { 0,-4, 3, 0 }, // modi + { 0,-4, 3, 0 }, // modu + { 0,-4, 3, 0 }, // muli + { 0,-4, 3, 0 }, // mulu + + { 0,-4, 3, 0 }, // band + { 0,-4, 3, 0 }, // bor + { 0,-4, 3, 0 }, // bxor + { 0, 0, 1, 0 }, // bcom + + { 0,-4, 3, 0 }, // lsh + { 0,-4, 3, 0 }, // rshi + { 0,-4, 3, 0 }, // rshu + + { 0, 0, 1, 0 }, // negf + { 0,-4, 3, 0 }, // addf + { 0,-4, 3, 0 }, // subf + { 0,-4, 3, 0 }, // divf + { 0,-4, 3, 0 }, // mulf + + { 0, 0, 1, 0 }, // cvif + { 0, 0, 1, 0 } // cvfi +}; + +const char *opname[ 256 ] = { + "OP_UNDEF", + + "OP_IGNORE", + + "OP_BREAK", + + "OP_ENTER", + "OP_LEAVE", + "OP_CALL", + "OP_PUSH", + "OP_POP", + + "OP_CONST", + + "OP_LOCAL", + + "OP_JUMP", + + //------------------- + + "OP_EQ", + "OP_NE", + + "OP_LTI", + "OP_LEI", + "OP_GTI", + "OP_GEI", + + "OP_LTU", + "OP_LEU", + "OP_GTU", + "OP_GEU", + + "OP_EQF", + "OP_NEF", + + "OP_LTF", + "OP_LEF", + "OP_GTF", + "OP_GEF", + + //------------------- + + "OP_LOAD1", + "OP_LOAD2", + "OP_LOAD4", + "OP_STORE1", + "OP_STORE2", + "OP_STORE4", + "OP_ARG", + + "OP_BLOCK_COPY", + + //------------------- + + "OP_SEX8", + "OP_SEX16", + + "OP_NEGI", + "OP_ADD", + "OP_SUB", + "OP_DIVI", + "OP_DIVU", + "OP_MODI", + "OP_MODU", + "OP_MULI", + "OP_MULU", + + "OP_BAND", + "OP_BOR", + "OP_BXOR", + "OP_BCOM", + + "OP_LSH", + "OP_RSHI", + "OP_RSHU", + + "OP_NEGF", + "OP_ADDF", + "OP_SUBF", + "OP_DIVF", + "OP_MULF", + + "OP_CVIF", + "OP_CVFI" +}; + +cvar_t vm_rtChecks = { "vm_rtChecks", "1"}; + +int vm_debugLevel; + +// used by SV_Error to get rid of running vm's before longjmp +static int forced_unload; + +struct vm_s vmTable[ VM_COUNT ]; +void VM_VmInfo_f( void ); +void VM_VmProfile_f( void ); + + +void VM_Debug( int level ) { + vm_debugLevel = level; +} + + +/* +============== +VM_CheckBounds +============== +*/ +void VM_CheckBounds( const vm_t *vm, unsigned int address, unsigned int length ) +{ + //if ( !vm->entryPoint ) + { + if ( (address | length) > vm->dataMask || (address + length) > vm->dataMask ) + { + SV_Error( "program tried to bypass data segment bounds" ); + } + } +} + +/* +============== +VM_CheckBounds2 +============== +*/ +void VM_CheckBounds2( const vm_t *vm, unsigned int addr1, unsigned int addr2, unsigned int length ) +{ + //if ( !vm->entryPoint ) + { + if ( (addr1 | addr2 | length) > vm->dataMask || (addr1 + length) > vm->dataMask || (addr2+length) > vm->dataMask ) + { + SV_Error( "program tried to bypass data segment bounds" ); + } + } +} + +/* +============== +VM_Init +============== +*/ + +void ED2_PrintEdicts (void); +void PR2_Profile_f (void); +void ED2_PrintEdict_f (void); +void ED_Count (void); +void PR_CleanLogText_Init(void); +vm_t *currentVM = NULL; // bk001212 +vm_t *lastVM = NULL; // bk001212 +int vm_debugLevel; + + +void *VM_ArgPtr( intptr_t intValue ) { + if ( !intValue ) { + return NULL; + } + // bk001220 - currentVM is missing on reconnect + if ( currentVM==NULL ) + return NULL; + + if ( currentVM->entryPoint ) { + return (void *)(currentVM->dataBase + intValue); + } + else { + return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask)); + } +} + +void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ) { + if ( !intValue ) { + return NULL; + } + + // bk010124 - currentVM is missing on reconnect here as well? + if ( vm==NULL ) + return NULL; + + // + if ( vm->entryPoint ) { + return (void *)(vm->dataBase + intValue); + } + else { + return (void *)(vm->dataBase + (intValue & vm->dataMask)); + } +} + +intptr_t VM_Ptr2VM( void* ptr ) { + if ( !ptr ) { + return 0; + } + // bk001220 - currentVM is missing on reconnect + if ( currentVM==NULL ) + return 0; + + if ( currentVM->entryPoint ) { + return (intptr_t)ptr; + } else { + return (((byte*)ptr - currentVM->dataBase )) & currentVM->dataMask; + } +} + + +intptr_t VM_ExplicitPtr2VM( vm_t *vm, void* ptr ) { + if ( !ptr ) { + return 0; + } + // bk001220 - currentVM is missing on reconnect + if ( vm==NULL ) + return 0; + + if ( vm->entryPoint ) { + return (intptr_t)ptr; + } else { + return (((byte*)ptr - vm->dataBase )) & vm->dataMask; + } +} + +/* +=============== +VM_ValueToSymbol + +Assumes a program counter value +=============== +*/ +const char *VM_ValueToSymbol( vm_t *vm, int value ) { + vmSymbol_t *sym; + static char text[MAX_COM_TOKEN]; + + sym = vm->symbols; + if ( !sym ) { + return "NO SYMBOLS"; + } + + // find the symbol + while ( sym->next && sym->next->symValue <= value ) { + sym = sym->next; + } + + if ( value == sym->symValue ) { + return sym->symName; + } + + snprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue ); + + return text; +} + +/* +=============== +VM_ValueToFunctionSymbol + +For profiling, find the symbol behind this value +=============== +*/ +vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) { + vmSymbol_t *sym; + static vmSymbol_t nullSym; + + sym = vm->symbols; + if ( !sym ) { + return &nullSym; + } + + while ( sym->next && sym->next->symValue <= value ) { + sym = sym->next; + } + + return sym; +} + +/* +=============== +VM_SymbolToValue +=============== +*/ +int VM_SymbolToValue( vm_t *vm, const char *symbol ) { + vmSymbol_t *sym; + + for ( sym = vm->symbols ; sym ; sym = sym->next ) { + if ( !strcmp( symbol, sym->symName ) ) { + return sym->symValue; + } + } + return 0; +} + +/* +=============== +ParseHex +=============== +*/ +int ParseHex( const char *text ) { + int value; + int c; + + value = 0; + while ( ( c = *text++ ) != 0 ) { + if ( c >= '0' && c <= '9' ) { + value = value * 16 + c - '0'; + continue; + } + if ( c >= 'a' && c <= 'f' ) { + value = value * 16 + 10 + c - 'a'; + continue; + } + if ( c >= 'A' && c <= 'F' ) { + value = value * 16 + 10 + c - 'A'; + continue; + } + } + + return value; +} + +/* +=============== +VM_LoadSymbols +=============== +*/ +void VM_LoadSymbols( vm_t *vm ) { + union { + char *c; + void *v; + } mapfile; + char *text_p; + //char name[MAX_QPATH]; + char symbols[MAX_QPATH]; + vmSymbol_t **prev, *sym; + int count; + int value; + int chars; + int segment; + int numInstructions; + + // don't load symbols if not developer + //if ( !com_developer->integer ) { return; } + + //COM_StripExtension((char*)vm->name, name); + snprintf( symbols, sizeof( symbols ), "%s.map", vm->name ); + mapfile.v = FS_LoadTempFile( symbols, NULL ); + if ( !mapfile.c ) { + Con_Printf( "Couldn't load symbol file: %s\n", symbols ); + return; + } + + numInstructions = vm->instructionCount; + + // parse the symbols + text_p = mapfile.c; + prev = &vm->symbols; + count = 0; + + while ( 1 ) { + text_p = COM_Parse( text_p ); + if ( !text_p ) { + break; + } + segment = ParseHex( com_token ); + if ( segment ) { + COM_Parse( text_p ); + COM_Parse( text_p ); + continue; // only load code segment values + } + + text_p = COM_Parse( text_p ); + if ( !text_p ) { + Con_Printf( "WARNING: incomplete line at end of file\n" ); + break; + } + value = ParseHex( com_token ); + + text_p = COM_Parse( text_p ); + if ( !text_p ) { + Con_Printf( "WARNING: incomplete line at end of file\n" ); + break; + } + chars = strlen( com_token ); + sym = Hunk_Alloc( sizeof( *sym ) + chars); + *prev = sym; + prev = &sym->next; + sym->next = NULL; + + // convert value from an instruction number to a code offset + if ( vm->instructionPointers && value >= 0 && value < numInstructions ) { + value = vm->instructionPointers[value]; + } + + sym->symValue = value; + strlcpy( sym->symName, com_token, chars + 1 ); + + count++; + } + + vm->numSymbols = count; + Con_Printf( "%i symbols parsed from %s\n", count, symbols ); + +} + +static void VM_SwapLongs( void *data, int length ) +{ + int i, *ptr; + ptr = (int *) data; + length /= sizeof( int ); + for ( i = 0; i < length; i++ ) { + ptr[ i ] = LittleLong( ptr[ i ] ); + } +} + +/* +============ +VM_DllSyscall + +Dlls will call this directly + + rcg010206 The horror; the horror. + + The syscall mechanism relies on stack manipulation to get its args. + This is likely due to C's inability to pass "..." parameters to + a function in one clean chunk. On PowerPC Linux, these parameters + are not necessarily passed on the stack, so while (&arg[0] == arg) + is true, (&arg[1] == 2nd function parameter) is not necessarily + accurate, as arg's value might have been stored to the stack or + other piece of scratch memory to give it a valid address, but the + next parameter might still be sitting in a register. + + Quake's syscall system also assumes that the stack grows downward, + and that any needed types can be squeezed, safely, into a signed int. + + This hack below copies all needed values for an argument to a + array in memory, so that Quake can get the correct values. This can + also be used on systems where the stack grows upwards, as the + presumably standard and safe stdargs.h macros are used. + + As for having enough space in a signed int for your datatypes, well, + it might be better to wait for DOOM 3 before you start porting. :) + + The original code, while probably still inherently dangerous, seems + to work well enough for the platforms it already works on. Rather + than add the performance hit for those platforms, the original code + is still in use there. + + For speed, we just grab 15 arguments, and don't worry about exactly + how many the syscall actually needs; the extra is thrown away. + +============ +*/ +#if 1 // - disabled because now is different for each module +intptr_t QDECL VM_DllSyscall( intptr_t arg, ... ) { +#if !idx386 || defined __clang__ + // rcg010206 - see commentary above + intptr_t args[16]; + va_list ap; + int i; + + args[0] = arg; + + va_start( ap, arg ); + for (i = 1; i < ARRAY_LEN( args ); i++ ) + args[ i ] = va_arg( ap, intptr_t ); + va_end( ap ); + + return currentVM->systemCall( args ); +#else // original id code + return currentVM->systemCall( &arg ); +#endif +} +#endif + +/* +================= +VM_ValidateHeader +================= +*/ +static char *VM_ValidateHeader( vmHeader_t *header, int fileSize ) +{ + static char errMsg[128]; + int n; + + // truncated + if ( fileSize < ( sizeof( vmHeader_t ) - sizeof( int ) ) ) { + sprintf( errMsg, "truncated image header (%i bytes long)", fileSize ); + return errMsg; + } + + // bad magic + if ( LittleLong( header->vmMagic ) != VM_MAGIC && LittleLong( header->vmMagic ) != VM_MAGIC_VER2 ) { + sprintf( errMsg, "bad file magic %08x", LittleLong( header->vmMagic ) ); + return errMsg; + } + + // truncated + if ( fileSize < sizeof( vmHeader_t ) && LittleLong( header->vmMagic ) != VM_MAGIC_VER2 ) { + sprintf( errMsg, "truncated image header (%i bytes long)", fileSize ); + return errMsg; + } + + if ( LittleLong( header->vmMagic ) == VM_MAGIC_VER2 ) + n = sizeof( vmHeader_t ); + else + n = ( sizeof( vmHeader_t ) - sizeof( int ) ); + + // byte swap the header + VM_SwapLongs( header, n ); + + // bad code offset + if ( header->codeOffset >= fileSize ) { + sprintf( errMsg, "bad code segment offset %i", header->codeOffset ); + return errMsg; + } + + // bad code length + if ( header->codeLength <= 0 || header->codeOffset + header->codeLength > fileSize ) { + sprintf( errMsg, "bad code segment length %i", header->codeLength ); + return errMsg; + } + + // bad data offset + if ( header->dataOffset >= fileSize || header->dataOffset != header->codeOffset + header->codeLength ) { + sprintf( errMsg, "bad data segment offset %i", header->dataOffset ); + return errMsg; + } + + // bad data length + if ( header->dataOffset + header->dataLength > fileSize ) { + sprintf( errMsg, "bad data segment length %i", header->dataLength ); + return errMsg; + } + + if ( header->vmMagic == VM_MAGIC_VER2 ) + { + // bad lit/jtrg length + if ( header->dataOffset + header->dataLength + header->litLength + header->jtrgLength != fileSize ) { + sprintf( errMsg, "bad lit/jtrg segment length" ); + return errMsg; + } + } + // bad lit length + else if ( header->dataOffset + header->dataLength + header->litLength != fileSize ) + { + sprintf( errMsg, "bad lit segment length %i", header->litLength ); + return errMsg; + } + + return NULL; +} + +/* +================= +VM_LoadQVM + +Load a .qvm file + +if ( alloc ) + - Validate header, swap data + - Alloc memory for data/instructions + - Alloc memory for instructionPointers - NOT NEEDED + - Load instructions + - Clear/load data +else + - Check for header changes + - Clear/load data + +================= +*/ +static vmHeader_t *VM_LoadQVM( vm_t *vm, qbool alloc ) { + int length; + unsigned int dataLength; + unsigned int dataAlloc; + int i; + char filename[MAX_QPATH], *errorMsg; + unsigned int crc32sum; + //qbool tryjts; + vmHeader_t *header; + char num[32]; + + // load the image + snprintf( filename, sizeof( filename ), "%s.qvm", vm->name ); + Con_Printf( "Loading vm file %s...\n", filename ); + header = ( vmHeader_t*)FS_LoadTempFile( filename, &length ); + if ( !header ) { + Con_Printf( "Failed.\n" ); + VM_Free( vm ); + return NULL; + } + + crc32sum = CRC_Block( ( byte * ) header, length ); + sprintf( num, "%i", crc32sum ); + Info_SetValueForStarKey( svs.info, "*progs", num, MAX_SERVERINFO_STRING ); + + // will also swap header + errorMsg = VM_ValidateHeader( header, length ); + if ( errorMsg ) { + VM_Free( vm ); + Con_Printf( "%s\n", errorMsg ); + return NULL; + } + + vm->crc32sum = crc32sum; + //tryjts = false; + + if( header->vmMagic == VM_MAGIC_VER2 ) { + Con_Printf( "...which has vmMagic VM_MAGIC_VER2\n" ); + } else { + // tryjts = true; + } + + vm->exactDataLength = header->dataLength + header->litLength + header->bssLength; + dataLength = vm->exactDataLength + PROGRAM_STACK_EXTRA; + vm->dataLength = dataLength; + + // round up to next power of 2 so all data operations can + // be mask protected + for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) + ; + dataLength = 1 << i; + + // reserve some space for effective LOCAL+LOAD* checks + dataAlloc = dataLength + 1024; + + if ( dataLength >= (1U<<31) || dataAlloc >= (1U<<31) ) { + VM_Free( vm ); + Con_Printf( "%s: data segment is too large\n", __func__ ); + return NULL; + } + + if ( alloc ) { + // allocate zero filled space for initialized and uninitialized data + vm->dataBase = Hunk_Alloc( dataAlloc); + vm->dataMask = dataLength - 1; + vm->dataAlloc = dataAlloc; + } else { + // clear the data, but make sure we're not clearing more than allocated + if ( vm->dataAlloc != dataAlloc ) { + VM_Free( vm ); + Con_Printf( "Warning: Data region size of %s not matching after" + "VM_Restart()\n", filename ); + return NULL; + } + memset( vm->dataBase, 0, vm->dataAlloc ); + } + + // copy the intialized data + memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); + + // byte swap the longs + VM_SwapLongs( vm->dataBase, header->dataLength ); + + if( header->vmMagic == VM_MAGIC_VER2 ) { + int previousNumJumpTableTargets = vm->numJumpTableTargets; + + header->jtrgLength &= ~0x03; + + vm->numJumpTableTargets = header->jtrgLength >> 2; + Con_Printf( "Loading %d jump table targets\n", vm->numJumpTableTargets ); + + if ( alloc ) { + vm->jumpTableTargets = Hunk_Alloc( header->jtrgLength); + } else { + if ( vm->numJumpTableTargets != previousNumJumpTableTargets ) { + VM_Free( vm ); + + Con_Printf( "Warning: Jump table size of %s not matching after " + "VM_Restart()\n", filename ); + return NULL; + } + + memset( vm->jumpTableTargets, 0, header->jtrgLength ); + } + + memcpy( vm->jumpTableTargets, (byte *)header + header->dataOffset + + header->dataLength + header->litLength, header->jtrgLength ); + + // byte swap the longs + VM_SwapLongs( vm->jumpTableTargets, header->jtrgLength ); + } + + /*if ( tryjts == true && (length = Load_JTS( vm, crc32sum, NULL, vmPakIndex )) >= 0 ) { + // we are trying to load newer file? + if ( vm->jumpTableTargets && vm->numJumpTableTargets != length >> 2 ) { + Con_Printf( "Reload jts file\n" ); + vm->jumpTableTargets = NULL; + alloc = true; + } + vm->numJumpTableTargets = length >> 2; + Con_Printf( "Loading %d external jump table targets\n", vm->numJumpTableTargets ); + if ( alloc == true ) { + vm->jumpTableTargets = Hunk_Alloc( length); + } else { + memset( vm->jumpTableTargets, 0, length ); + } + Load_JTS( vm, crc32sum, vm->jumpTableTargets, vmPakIndex ); + }*/ + + return header; +} + +/* +================= +VM_LoadInstructions + +loads instructions in structured format +================= +*/ +const char *VM_LoadInstructions( const byte *code_pos, int codeLength, int instructionCount, instruction_t *buf ) +{ + static char errBuf[ 128 ]; + const byte *code_start, *code_end; + int i, n, op0, op1, opStack; + instruction_t *ci; + + code_start = code_pos; // for printing + code_end = code_pos + codeLength; + + ci = buf; + opStack = 0; + op1 = OP_UNDEF; + + // load instructions and perform some initial calculations/checks + for ( i = 0; i < instructionCount; i++, ci++, op1 = op0 ) { + op0 = *code_pos; + if ( op0 < 0 || op0 >= OP_MAX ) { + sprintf( errBuf, "bad opcode %02X at offset %d", op0, (int)(code_pos - code_start) ); + return errBuf; + } + n = ops[ op0 ].size; + if ( code_pos + 1 + n > code_end ) { + sprintf( errBuf, "code_pos > code_end" ); + return errBuf; + } + code_pos++; + ci->op = op0; + if ( n == 4 ) { + ci->value = LittleLong( *((int*)code_pos) ); + code_pos += 4; + } else if ( n == 1 ) { + ci->value = *((unsigned char*)code_pos); + code_pos += 1; + } else { + ci->value = 0; + } + + // setup jump value from previous const + if ( op0 == OP_JUMP && op1 == OP_CONST ) { + ci->value = (ci-1)->value; + } + + ci->opStack = opStack; + opStack += ops[ op0 ].stack; + } + + return NULL; +} + +/* +=============================== +VM_CheckInstructions + +performs additional consistency and security checks +=============================== +*/ +const char *VM_CheckInstructions( instruction_t *buf, + int instructionCount, + const byte *jumpTableTargets, + int numJumpTableTargets, + int dataLength ) +{ + static char errBuf[ 128 ]; + int i, n, v, op0, op1, opStack, pstack; + instruction_t *ci, *proc; + int startp, endp; + + ci = buf; + opStack = 0; + + // opstack checks + for ( i = 0; i < instructionCount; i++, ci++ ) { + opStack += ops[ ci->op ].stack; + if ( opStack < 0 ) { + sprintf( errBuf, "opStack underflow at %i", i ); + return errBuf; + } + if ( opStack >= PROC_OPSTACK_SIZE * 4 ) { + sprintf( errBuf, "opStack overflow at %i", i ); + return errBuf; + } + } + + ci = buf; + pstack = 0; + op1 = OP_UNDEF; + proc = NULL; + + startp = 0; + endp = instructionCount - 1; + + // Additional security checks + + for ( i = 0; i < instructionCount; i++, ci++, op1 = op0 ) { + op0 = ci->op; + + // function entry + if ( op0 == OP_ENTER ) { + // missing block end + if ( proc || ( pstack && op1 != OP_LEAVE ) ) { + sprintf( errBuf, "missing proc end before %i", i ); + return errBuf; + } + if ( ci->opStack != 0 ) { + v = ci->opStack; + sprintf( errBuf, "bad entry opstack %i at %i", v, i ); + return errBuf; + } + v = ci->value; + if ( v < 0 || v >= PROGRAM_STACK_SIZE || (v & 3) ) { + sprintf( errBuf, "bad entry programStack %i at %i", v, i ); + return errBuf; + } + + pstack = ci->value; + + // mark jump target + ci->jused = 1; + proc = ci; + startp = i + 1; + + // locate endproc + for ( endp = 0, n = i+1 ; n < instructionCount; n++ ) { + if ( buf[n].op == OP_PUSH && buf[n+1].op == OP_LEAVE ) { + endp = n; + break; + } + } + + if ( endp == 0 ) { + sprintf( errBuf, "missing end proc for %i", i ); + return errBuf; + } + + continue; + } + + // proc opstack will carry max.possible opstack value + if ( proc && ci->opStack > proc->opStack ) + proc->opStack = ci->opStack; + + // function return + if ( op0 == OP_LEAVE ) { + // bad return programStack + if ( pstack != ci->value ) { + v = ci->value; + sprintf( errBuf, "bad programStack %i at %i", v, i ); + return errBuf; + } + // bad opStack before return + if ( ci->opStack != 4 ) { + v = ci->opStack; + sprintf( errBuf, "bad opStack %i at %i", v, i ); + return errBuf; + } + v = ci->value; + if ( v < 0 || v >= PROGRAM_STACK_SIZE || (v & 3) ) { + sprintf( errBuf, "bad return programStack %i at %i", v, i ); + return errBuf; + } + if ( op1 == OP_PUSH ) { + if ( proc == NULL ) { + sprintf( errBuf, "unexpected proc end at %i", i ); + return errBuf; + } + proc = NULL; + startp = i + 1; // next instruction + endp = instructionCount - 1; // end of the image + } + continue; + } + + // conditional jumps + if ( ops[ ci->op ].flags & JUMP ) { + v = ci->value; + // conditional jumps should have opStack == 8 + if ( ci->opStack != 8 ) { + sprintf( errBuf, "bad jump opStack %i at %i", ci->opStack, i ); + return errBuf; + } + //if ( v >= header->instructionCount ) { + // allow only local proc jumps + if ( v < startp || v > endp ) { + sprintf( errBuf, "jump target %i at %i is out of range (%i,%i)", v, i-1, startp, endp ); + return errBuf; + } + if ( buf[v].opStack != 0 ) { + n = buf[v].opStack; + sprintf( errBuf, "jump target %i has bad opStack %i", v, n ); + return errBuf; + } + // mark jump target + buf[v].jused = 1; + continue; + } + + // unconditional jumps + if ( op0 == OP_JUMP ) { + // jumps should have opStack == 4 + if ( ci->opStack != 4 ) { + sprintf( errBuf, "bad jump opStack %i at %i", ci->opStack, i ); + return errBuf; + } + if ( op1 == OP_CONST ) { + v = buf[i-1].value; + // allow only local jumps + if ( v < startp || v > endp ) { + sprintf( errBuf, "jump target %i at %i is out of range (%i,%i)", v, i-1, startp, endp ); + return errBuf; + } + if ( buf[v].opStack != 0 ) { + n = buf[v].opStack; + sprintf( errBuf, "jump target %i has bad opStack %i", v, n ); + return errBuf; + } + if ( buf[v].op == OP_ENTER ) { + n = buf[v].op; + sprintf( errBuf, "jump target %i has bad opcode %i", v, n ); + return errBuf; + } + if ( v == (i-1) ) { + sprintf( errBuf, "self loop at %i", v ); + return errBuf; + } + // mark jump target + buf[v].jused = 1; + } else { + if ( proc ) + proc->swtch = 1; + else + ci->swtch = 1; + } + continue; + } + + if ( op0 == OP_CALL ) { + if ( ci->opStack < 4 ) { + sprintf( errBuf, "bad call opStack at %i", i ); + return errBuf; + } + if ( op1 == OP_CONST ) { + v = buf[i-1].value; + // analyse only local function calls + if ( v >= 0 ) { + if ( v >= instructionCount ) { + sprintf( errBuf, "call target %i is out of range", v ); + return errBuf; + } + if ( buf[v].op != OP_ENTER ) { + n = buf[v].op; + sprintf( errBuf, "call target %i has bad opcode %i", v, n ); + return errBuf; + } + if ( v == 0 ) { + sprintf( errBuf, "explicit vmMain call inside VM" ); + return errBuf; + } + // mark jump target + buf[v].jused = 1; + } + } + continue; + } + + if ( ci->op == OP_ARG ) { + v = ci->value & 255; + // argument can't exceed programStack frame + if ( v < 8 || v > pstack - 4 || (v & 3) ) { + sprintf( errBuf, "bad argument address %i at %i", v, i ); + return errBuf; + } + continue; + } + + if ( ci->op == OP_LOCAL ) { + v = ci->value; + if ( proc == NULL ) { + sprintf( errBuf, "missing proc frame for local %i at %i", v, i ); + return errBuf; + } + if ( (ci+1)->op == OP_LOAD1 || (ci+1)->op == OP_LOAD2 || (ci+1)->op == OP_LOAD4 || (ci+1)->op == OP_ARG ) { + // FIXME: alloc 256 bytes of programStack in VM_CallCompiled()? + if ( v < 8 || v >= proc->value + 256 ) { + sprintf( errBuf, "bad local address %i at %i", v, i ); + return errBuf; + } + } + } + + if ( ci->op == OP_LOAD4 && op1 == OP_CONST ) { + v = (ci-1)->value; + if ( v < 0 || v > dataLength - 4 ) { + sprintf( errBuf, "bad load4 address %i at %i", v, i - 1 ); + return errBuf; + } + } + + if ( ci->op == OP_LOAD2 && op1 == OP_CONST ) { + v = (ci-1)->value; + if ( v < 0 || v > dataLength - 2 ) { + sprintf( errBuf, "bad load2 address %i at %i", v, i - 1 ); + return errBuf; + } + } + + if ( ci->op == OP_LOAD1 && op1 == OP_CONST ) { + v = (ci-1)->value; + if ( v < 0 || v > dataLength - 1 ) { + sprintf( errBuf, "bad load1 address %i at %i", v, i - 1 ); + return errBuf; + } + } + + if ( ci->op == OP_BLOCK_COPY ) { + v = ci->value; + if ( v >= dataLength ) { + sprintf( errBuf, "bad count %i for block copy at %i", v, i - 1 ); + return errBuf; + } + } + +// op1 = op0; +// ci++; + } + + if ( op1 != OP_UNDEF && op1 != OP_LEAVE ) { + sprintf( errBuf, "missing return instruction at the end of the image" ); + return errBuf; + } + + // ensure that the optimization pass knows about all the jump table targets + if ( jumpTableTargets ) { + // first pass - validate + for( i = 0; i < numJumpTableTargets; i++ ) { + n = *(int *)(jumpTableTargets + ( i * sizeof( int ) ) ); + if ( n < 0 || n >= instructionCount ) { + Con_Printf( "jump target %i set on instruction %i that is out of range [0..%i]", + i, n, instructionCount - 1 ); + break; + } + if ( buf[n].opStack != 0 ) { + Con_Printf( "jump target %i set on instruction %i (%s) with bad opStack %i\n", + i, n, opname[ buf[n].op ], buf[n].opStack ); + break; + } + } + if ( i != numJumpTableTargets ) { + // we may trap this on buggy VM_MAGIC_VER2 images + // but we can safely optimize code even without JTRGSEG + // so just switch to VM_MAGIC path here + goto __noJTS; + } + // second pass - apply + for( i = 0; i < numJumpTableTargets; i++ ) { + n = *(int *)(jumpTableTargets + ( i * sizeof( int ) ) ); + buf[n].jused = 1; + } + } else { +__noJTS: + v = 0; + // instructions with opStack > 0 can't be jump labels so its safe to optimize/merge + for ( i = 0, ci = buf; i < instructionCount; i++, ci++ ) { + if ( ci->op == OP_ENTER ) { + v = ci->swtch; + continue; + } + // if there is a switch statement in function - + // mark all potential jump labels + if ( ci->swtch ) + v = ci->swtch; + if ( ci->opStack > 0 ) + ci->jused = 0; + else if ( v ) + ci->jused = 1; + } + } + + return NULL; +} +qbool VM_LoadNative( vm_t * vm) +{ + char name[MAX_OSPATH]; + char *gpath = NULL; + void ( *dllEntry ) ( void * ); + + while ( ( gpath = FS_NextPath( gpath ) ) ) + { + snprintf( name, sizeof( name ), "%s/%s." DLEXT, gpath, vm->name ); + vm->dllHandle = Sys_DLOpen( name ); + if ( vm->dllHandle ) + { + Con_Printf( "LoadLibrary (%s)\n", name ); + break; + } + } + if ( !vm->dllHandle ) + return false; + + dllEntry = Sys_DLProc( vm->dllHandle, "dllEntry" ); + vm->entryPoint = Sys_DLProc( vm->dllHandle, "vmMain" ); + if ( !dllEntry || !vm->entryPoint ) + { + Sys_DLClose( vm->dllHandle ); + SV_Error( "VM_LoadNative: couldn't initialize module %s", name ); + } + dllEntry( vm->dllSyscall ); + + Info_SetValueForStarKey( svs.info, "*qvm", DLEXT, MAX_SERVERINFO_STRING ); + Info_SetValueForStarKey( svs.info, "*progs", DLEXT, MAX_SERVERINFO_STRING ); + vm->type = VMI_NATIVE; + return true; +} + +/* +================ +VM_Create + +If image ends in .qvm it will be interpreted, otherwise +it will attempt to load as a system dll +================ +*/ +vm_t *VM_Create( vmIndex_t index, const char *name, syscall_t systemCalls, /*dllSyscall_t dllSyscalls,*/ vmInterpret_t interpret ) { + //int remaining; + vmHeader_t *header; + vm_t *vm; + + if ( !systemCalls ) { + SV_Error( "VM_Create: bad parms" ); + } + + if ( (unsigned)index >= VM_COUNT ) { + SV_Error( "VM_Create: bad vm index %i", index ); + } + + //remaining = Hunk_MemoryRemaining(); + + vm = &vmTable[ index ]; + + // see if we already have the VM + if ( vm->name ) { + if ( vm->index != index ) { + SV_Error( "VM_Create: bad allocated vm index %i", vm->index ); + return NULL; + } + return vm; + } + + vm->name = name; + vm->index = index; + vm->systemCall = systemCalls; + vm->dllSyscall = VM_DllSyscall;//dllSyscalls; + //vm->privateFlag = CVAR_PRIVATE; + + // never allow dll loading with a demo + /*if ( interpret == VMI_NATIVE ) { + if ( Cvar_VariableIntegerValue( "fs_restrict" ) ) { + interpret = VMI_COMPILED; + } + }*/ + + if ( interpret == VMI_NATIVE ) { + // try to load as a system dll + //Con_Printf( "Loading dll file %s.\n", name ); + if ( VM_LoadNative( vm ) ) { + //vm->privateFlag = 0; // allow reading private cvars + vm->dataAlloc = ~0U; + vm->dataMask = ~0U; + vm->dataBase = 0; + return vm; + } + + Con_Printf( "Failed to load dll, looking for qvm.\n" ); + interpret = VMI_COMPILED; + } + + // load the image + if( ( header = VM_LoadQVM( vm, true ) ) == NULL ) { + return NULL; + } + + // allocate space for the jump targets, which will be filled in by the compile/prep functions + vm->instructionCount = header->instructionCount; + //vm->instructionPointers = Hunk_Alloc(vm->instructionCount * sizeof(*vm->instructionPointers), h_high); + vm->instructionPointers = NULL; + + // copy or compile the instructions + vm->codeLength = header->codeLength; + + // the stack is implicitly at the end of the image + vm->programStack = vm->dataMask + 1; + vm->stackBottom = vm->programStack - PROGRAM_STACK_SIZE - PROGRAM_STACK_EXTRA; + + vm->compiled = false; + + +#ifdef NO_VM_COMPILED + if(interpret >= VMI_COMPILED) { + Con_Printf("Architecture doesn't have a bytecode compiler, using interpreter\n"); + interpret = VMI_BYTECODE; + } +#else + if ( interpret >= VMI_COMPILED ) { + if ( VM_Compile( vm, header ) ) { + vm->compiled = true; + } + } +#endif + // VM_Compile may have reset vm->compiled if compilation failed + if ( !vm->compiled ) { + if ( !VM_PrepareInterpreter2( vm, header ) ) { + //FS_FreeFile( header ); // free the original file + VM_Free( vm ); + return NULL; + } + } + vm->type = interpret; + + // free the original file + //FS_FreeFile( header ); + + // load the map file + VM_LoadSymbols( vm ); + + //Con_Printf( "%s loaded in %d bytes on the hunk\n", vm->name, remaining - Hunk_MemoryRemaining() ); + + return vm; +} + +/* +============== +VM_Free +============== +*/ +void VM_Free( vm_t *vm ) { + + if( !vm ) { + return; + } + +/* if ( vm->callLevel ) { + if ( !forced_unload ) { + SV_Error( ERR_FATAL, "VM_Free(%s) on running vm", vm->name ); + return; + } else { + Con_Printf( "forcefully unloading %s vm\n", vm->name ); + } + }*/ + + if ( vm->destroy ) + vm->destroy( vm ); + + if ( vm->dllHandle ) + Sys_DLClose( vm->dllHandle ); + +#if 0 // now automatically freed by hunk + if ( vm->codeBase.ptr ) { + Z_Free( vm->codeBase.ptr ); + } + if ( vm->dataBase ) { + Z_Free( vm->dataBase ); + } + if ( vm->instructionPointers ) { + Z_Free( vm->instructionPointers ); + } +#endif + currentVM = NULL; + lastVM = NULL; + memset( vm, 0, sizeof( *vm ) ); +} + + +void VM_Clear( void ) { + int i; + for ( i = 0; i < VM_COUNT; i++ ) { + VM_Free( &vmTable[ i ] ); + } + currentVM = NULL; + lastVM = NULL; +} + +/* +============== +VM_Call + + +Upon a system call, the stack will look like: + +sp+32 parm1 +sp+28 parm0 +sp+24 return value +sp+20 return address +sp+16 local1 +sp+14 local0 +sp+12 arg1 +sp+8 arg0 +sp+4 return stack +sp return address + +An interpreted function will immediately execute +an OP_ENTER instruction, which will subtract space for +locals from sp +============== +*/ + +intptr_t QDECL VM_Call( vm_t *vm, int nargs, int callnum, ... ) +{ + vm_t *oldVM; + intptr_t r; + int i; + + if ( !vm ) { + SV_Error( "VM_Call with NULL vm" ); + } + + + oldVM = currentVM; + currentVM = vm; + lastVM = vm; + + if ( vm_debugLevel ) { + Con_Printf( "VM_Call( %d )\n", callnum ); + } + +#ifdef DEBUG + if ( nargs >= MAX_VMMAIN_CALL_ARGS ) { + SV_Error( "VM_Call: nargs >= MAX_VMMAIN_CALL_ARGS" ); + } +#endif + + ++vm->callLevel; + // if we have a dll loaded, call it directly + if ( vm->entryPoint ) + { + //rcg010207 - see dissertation at top of VM_DllSyscall() in this file. + int args[MAX_VMMAIN_CALL_ARGS-1]; + va_list ap; + va_start( ap, callnum ); + for ( i = 0; i < nargs; i++ ) { + args[i] = va_arg( ap, int ); + } + va_end(ap); + + // add more agruments if you're changed MAX_VMMAIN_CALL_ARGS: + r = vm->entryPoint( callnum, args[0], args[1], args[2] ); + } else { +#if idx386 && !defined __clang__ // calling convention doesn't need conversion in some cases +#ifndef NO_VM_COMPILED + if ( vm->compiled ) + r = VM_CallCompiled( vm, nargs+1, (int*)&callnum ); + else +#endif + r = VM_CallInterpreted2( vm, nargs+1, (int*)&callnum ); +#else + int args[MAX_VMMAIN_CALL_ARGS]; + va_list ap; + + args[0] = callnum; + va_start( ap, callnum ); + for ( i = 0; i < nargs; i++ ) { + args[i+1] = va_arg( ap, int ); + } + va_end(ap); +#ifndef NO_VM_COMPILED + if ( vm->compiled ) + r = VM_CallCompiled( vm, nargs+1, &args[0] ); + else +#endif + r = VM_CallInterpreted2( vm, nargs+1, &args[0] ); +#endif + } + --vm->callLevel; + if ( oldVM != NULL ) // bk001220 - assert(currentVM!=NULL) for oldVM==NULL + currentVM = oldVM; + + return r; +} + + +//================================================================= + +static int QDECL VM_ProfileSort( const void *a, const void *b ) { + vmSymbol_t *sa, *sb; + + sa = *(vmSymbol_t **)a; + sb = *(vmSymbol_t **)b; + + if ( sa->profileCount < sb->profileCount ) { + return -1; + } + if ( sa->profileCount > sb->profileCount ) { + return 1; + } + return 0; +} + +/* +============== +VM_VmProfile_f + +============== +*/ +void VM_VmProfile_f( void ) { + vm_t *vm; + vmSymbol_t **sorted, *sym; + int i; + double total; + + vm = &vmTable[VM_GAME]; + + if ( !vm->name ) { + Con_Printf( " VM is not running.\n" ); + return; + } + if ( vm == NULL ) { + return; + } + + if ( !vm->numSymbols ) { + return; + } + + sorted = Q_malloc( vm->numSymbols * sizeof( *sorted ) ); + sorted[0] = vm->symbols; + total = sorted[0]->profileCount; + for ( i = 1 ; i < vm->numSymbols ; i++ ) { + sorted[i] = sorted[i-1]->next; + total += sorted[i]->profileCount; + } + + qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort ); + + for ( i = 0 ; i < vm->numSymbols ; i++ ) { + int perc; + + sym = sorted[i]; + + perc = 100 * (float) sym->profileCount / total; + Con_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName ); + sym->profileCount = 0; + } + + Con_Printf(" %9.0f total\n", total ); + + Q_free( sorted ); +} + +/* +============== +VM_VmInfo_f +============== +*/ +void VM_VmInfo_f( void ) { + vm_t *vm; + int i; + + Con_Printf( "Registered virtual machines:\n" ); + for ( i = 0 ; i < VM_COUNT ; i++ ) { + vm = &vmTable[i]; + if ( !vm->name ) { + continue; + } + Con_Printf( "%s : ", vm->name ); + if ( vm->dllHandle ) { + Con_Printf( "native\n" ); + continue; + } + if ( vm->compiled ) { + Con_Printf( "compiled on load\n" ); + } else { + Con_Printf( "interpreted\n" ); + } + Con_Printf( " code length : %7i\n", vm->codeLength ); + Con_Printf( " table length: %7i\n", vm->instructionCount*4 ); + Con_Printf( " data length : %7i\n", vm->dataMask + 1 ); + } +} + +/* +=============== +VM_LogSyscalls + +Insert calls to this while debugging the vm compiler +=============== +*/ +void VM_LogSyscalls(int *args) { +#if 1 + static int callnum; + static FILE *f; + + if (!f) { + f = fopen("syscalls.log", "w"); + if (!f) { + return; + } + } + callnum++; + fprintf(f, "%i: %p (%i) = %i %i %i %i\n", callnum, (void*)(args - (int *)currentVM->dataBase), + args[0], args[1], args[2], args[3], args[4]); +#endif +} +#endif /* USE_PR2 */ diff --git a/src/vm.h b/src/vm.h new file mode 100644 index 000000000..9fedfb547 --- /dev/null +++ b/src/vm.h @@ -0,0 +1,90 @@ +#ifndef VM_H +#define VM_H + +/* +============================================================== + +VIRTUAL MACHINE + +============================================================== +*/ + +#ifdef _WIN32 +#define QDECL __cdecl +#else +#define QDECL +#endif + +typedef struct vm_s vm_t; + +typedef enum { + VMI_NONE, + VMI_NATIVE, + VMI_BYTECODE, + VMI_COMPILED +} vmInterpret_t; + + +typedef enum { + VM_BAD = -1, + VM_GAME = 0, + VM_COUNT +} vmIndex_t; + +extern vm_t *currentVM; +typedef intptr_t (*syscall_t)( intptr_t *parms ); +typedef intptr_t (QDECL *dllSyscall_t)( intptr_t callNum, ... ); +typedef void (QDECL *dllEntry_t)( dllSyscall_t syscallptr ); + + +void VM_Init( void ); +vm_t *VM_Create( vmIndex_t index, const char* name, syscall_t systemCalls, /*dllSyscall_t dllSyscalls,*/ vmInterpret_t interpret ); + +extern vm_t *currentVM; + +// module should be bare: "cgame", not "cgame.dll" or "vm/cgame.qvm" + +void VM_Free( vm_t *vm ); +void VM_Clear(void); +void VM_Forced_Unload_Start(void); +void VM_Forced_Unload_Done(void); +vm_t *VM_Restart( vm_t *vm ); + +intptr_t QDECL VM_Call( vm_t *vm, int nargs, int callNum, ... ); + +void VM_Debug( int level ); +void VM_CheckBounds( const vm_t *vm, unsigned int address, unsigned int length ); +void VM_CheckBounds2( const vm_t *vm, unsigned int addr1, unsigned int addr2, unsigned int length ); + +#if 1 +#define VM_CHECKBOUNDS VM_CheckBounds +#define VM_CHECKBOUNDS2 VM_CheckBounds2 +#else // for performance evaluation purposes +#define VM_CHECKBOUNDS(vm,a,b) +#define VM_CHECKBOUNDS2(vm,a,b,c) +#endif + +void *VM_ArgPtr( intptr_t intValue ); +void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ); +intptr_t VM_Ptr2VM( void* ptr ) ; +intptr_t VM_ExplicitPtr2VM( vm_t *vm, void* ptr ); + +typedef union floatint_u +{ + int i; + unsigned int u; + float f; + byte b[4]; +} +floatint_t; + +#define VMA(x) VM_ArgPtr(args[x]) +static inline float _vmf(intptr_t x) +{ + floatint_t v; + v.i = (int)x; + return v.f; +} +#define VMF(x) _vmf(args[x]) + +#endif diff --git a/src/vm_interpreted.c b/src/vm_interpreted.c new file mode 100644 index 000000000..615d1dae6 --- /dev/null +++ b/src/vm_interpreted.c @@ -0,0 +1,636 @@ +/* + * 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 +*/ +#ifdef USE_PR2 +#ifdef SERVERONLY +#include "qwsvdef.h" +#else +#include "quakedef.h" +#endif +#include "vm_local.h" + + +char *VM_Indent( vm_t *vm ) { + static char *string = " "; + if ( vm->callLevel > 20 ) { + return string; + } + return string + 2 * ( 20 - vm->callLevel ); +} + + +void VM_StackTrace( vm_t *vm, int programCounter, int programStack ) { + int count; + + count = 0; + do { + Con_Printf( "%s\n", VM_ValueToSymbol( vm, programCounter ) ); + programStack = *(int *)&vm->dataBase[programStack+4]; + programCounter = *(int *)&vm->dataBase[programStack]; + } while ( programCounter != -1 && ++count < 32 ); + +} + +// macro opcode sequences +typedef enum { + MOP_LOCAL_LOAD4 = OP_MAX, + MOP_LOCAL_LOAD4_CONST, + MOP_LOCAL_LOCAL, + MOP_LOCAL_LOCAL_LOAD4, +} macro_op_t; + + +/* +================= +VM_FindMOps + +Search for known macro-op sequences +================= +*/ +static void VM_FindMOps( instruction_t *buf, int instructionCount ) +{ + int i, op0; + instruction_t *ci; + + ci = buf; + i = 0; + + while ( i < instructionCount ) + { + op0 = ci->op; + + if ( op0 == OP_LOCAL && (ci+1)->op == OP_LOAD4 && (ci+2)->op == OP_CONST ) { + ci->op = MOP_LOCAL_LOAD4_CONST; + ci += 3; i += 3; + continue; + } + + if ( op0 == OP_LOCAL && (ci+1)->op == OP_LOAD4 ) { + ci->op = MOP_LOCAL_LOAD4; + ci += 2; i += 2; + continue; + } + + if ( op0 == OP_LOCAL && (ci+1)->op == OP_LOCAL && (ci+2)->op == OP_LOAD4 ) { + ci->op = MOP_LOCAL_LOCAL_LOAD4; + ci += 3; i += 3; + continue; + } + + if ( op0 == OP_LOCAL && (ci+1)->op == OP_LOCAL ) { + ci->op = MOP_LOCAL_LOCAL; + ci += 2; i += 2; + continue; + } + + ci++; + i++; + } +} + + +/* +==================== +VM_PrepareInterpreter2 +==================== +*/ +qbool VM_PrepareInterpreter2( vm_t *vm, vmHeader_t *header ) +{ + const char *errMsg; + instruction_t *buf; + buf = ( instruction_t *) Hunk_Alloc( (vm->instructionCount + 8) * sizeof( instruction_t )); + + errMsg = VM_LoadInstructions( (byte *) header + header->codeOffset, header->codeLength, header->instructionCount, buf ); + if ( !errMsg ) { + errMsg = VM_CheckInstructions( buf, vm->instructionCount, vm->jumpTableTargets, vm->numJumpTableTargets, vm->exactDataLength ); + } + if ( errMsg ) { + Con_Printf( "VM_PrepareInterpreter2 error: %s\n", errMsg ); + return false; + } + + //VM_ReplaceInstructions( vm, buf ); + + VM_FindMOps( buf, vm->instructionCount ); + + vm->codeBase.ptr = (void*)buf; + return true; +} + + +/* +============== +VM_CallInterpreted2 + + +Upon a system call, the stack will look like: + +sp+32 parm1 +sp+28 parm0 +sp+24 return stack +sp+20 return address +sp+16 local1 +sp+14 local0 +sp+12 arg1 +sp+8 arg0 +sp+4 return stack +sp return address + +An interpreted function will immediately execute +an OP_ENTER instruction, which will subtract space for +locals from sp +============== +*/ +int VM_CallInterpreted2( vm_t *vm, int nargs, int *args ) { + int stack[MAX_OPSTACK_SIZE]; + int *opStack, *opStackTop; + unsigned int programStack; + unsigned int stackOnEntry; + byte *image; + int v1, v0; + int dataMask; + instruction_t *inst, *ci; + floatint_t r0, r1; + int opcode; + int *img; + int i; + + // interpret the code + //vm->currentlyInterpreting = true; + + // we might be called recursively, so this might not be the very top + programStack = stackOnEntry = vm->programStack; + + // set up the stack frame + image = vm->dataBase; + inst = (instruction_t *)vm->codeBase.ptr; + dataMask = vm->dataMask; + + // leave a free spot at start of stack so + // that as long as opStack is valid, opStack-1 will + // not corrupt anything + opStack = &stack[1]; + opStackTop = stack + ARRAY_LEN( stack ) - 1; + + programStack -= (MAX_VMMAIN_CALL_ARGS+2)*4; + img = (int*)&image[ programStack ]; + for ( i = 0; i < nargs; i++ ) { + img[ i + 2 ] = args[ i ]; + } + img[ 1 ] = 0; // return stack + img[ 0 ] = -1; // will terminate the loop on return + + ci = inst; + + // main interpreter loop, will exit when a LEAVE instruction + // grabs the -1 program counter + + while ( 1 ) { + + r0.i = opStack[0]; + r1.i = opStack[-1]; + +nextInstruction2: + + v0 = ci->value; + opcode = ci->op; + ci++; + + switch ( opcode ) { + + case OP_BREAK: + vm->breakCount++; + goto nextInstruction2; + + case OP_ENTER: + // get size of stack frame + programStack -= v0; + if ( programStack <= vm->stackBottom ) { + VM_StackTrace(vm, ci - (instruction_t *)vm->codeBase.ptr, programStack); + SV_Error( "VM programStack overflow" ); + } + if ( opStack + ((ci-1)->opStack/4) >= opStackTop ) { + VM_StackTrace(vm, ci - (instruction_t *)vm->codeBase.ptr, programStack); + SV_Error( "VM opStack overflow" ); + } + break; + + case OP_LEAVE: + // remove our stack frame + programStack += v0; + + // grab the saved program counter + v1 = *(int *)&image[ programStack ]; + // check for leaving the VM + if ( v1 == -1 ) { + goto done; + } else if ( (unsigned)v1 >= vm->instructionCount ) { + VM_StackTrace(vm, ci - (instruction_t *)vm->codeBase.ptr, programStack); + SV_Error( "VM program counter out of range in OP_LEAVE" ); + } + ci = inst + v1; + break; + + case OP_CALL: + // save current program counter + *(int *)&image[ programStack ] = ci - inst; + + // jump to the location on the stack + if ( r0.i < 0 ) { + // system call + // save the stack to allow recursive VM entry + //vm->programStack = programStack - 4; + vm->programStack = programStack - 8; + *(int *)&image[ programStack + 4 ] = ~r0.i; + { +#if idx64 //__WORDSIZE == 64 + // the vm has ints on the stack, we expect + // longs so we have to convert it + intptr_t argarr[16]; + int argn; + for ( argn = 0; argn < ARRAY_LEN( argarr ); ++argn ) { + argarr[ argn ] = *(int*)&image[ programStack + 4 + 4*argn ]; + } + v0 = vm->systemCall( &argarr[0] ); +#else + //VM_LogSyscalls( (int *)&image[ programStack + 4 ] ); + v0 = vm->systemCall( (intptr_t *)&image[ programStack + 4 ] ); +#endif + } + + // save return value + //opStack++; + ci = inst + *(int *)&image[ programStack ]; + *opStack = v0; + } else if ( r0.u < vm->instructionCount ) { + // vm call + ci = inst + r0.i; + opStack--; + } else { + VM_StackTrace(vm, ci - (instruction_t *)vm->codeBase.ptr, programStack); + SV_Error( "VM program counter out of range in OP_CALL" ); + } + break; + + // push and pop are only needed for discarded or bad function return values + case OP_PUSH: + opStack++; + break; + + case OP_POP: + opStack--; + break; + + case OP_CONST: + opStack++; + r1.i = r0.i; + r0.i = *opStack = v0; + goto nextInstruction2; + + case OP_LOCAL: + opStack++; + r1.i = r0.i; + r0.i = *opStack = v0 + programStack; + goto nextInstruction2; + + case OP_JUMP: + if ( r0.u >= vm->instructionCount ) { + VM_StackTrace(vm, ci - (instruction_t *)vm->codeBase.ptr, programStack); + SV_Error( "VM program counter out of range in OP_JUMP" ); + } + ci = inst + r0.i; + opStack--; + break; + + /* + =================================================================== + BRANCHES + =================================================================== + */ + + case OP_EQ: + opStack -= 2; + if ( r1.i == r0.i ) + ci = inst + v0; + break; + + case OP_NE: + opStack -= 2; + if ( r1.i != r0.i ) + ci = inst + v0; + break; + + case OP_LTI: + opStack -= 2; + if ( r1.i < r0.i ) + ci = inst + v0; + break; + + case OP_LEI: + opStack -= 2; + if ( r1.i <= r0.i ) + ci = inst + v0; + break; + + case OP_GTI: + opStack -= 2; + if ( r1.i > r0.i ) + ci = inst + v0; + break; + + case OP_GEI: + opStack -= 2; + if ( r1.i >= r0.i ) + ci = inst + v0; + break; + + case OP_LTU: + opStack -= 2; + if ( r1.u < r0.u ) + ci = inst + v0; + break; + + case OP_LEU: + opStack -= 2; + if ( r1.u <= r0.u ) + ci = inst + v0; + break; + + case OP_GTU: + opStack -= 2; + if ( r1.u > r0.u ) + ci = inst + v0; + break; + + case OP_GEU: + opStack -= 2; + if ( r1.u >= r0.u ) + ci = inst + v0; + break; + + case OP_EQF: + opStack -= 2; + if ( r1.f == r0.f ) + ci = inst + v0; + break; + + case OP_NEF: + opStack -= 2; + if ( r1.f != r0.f ) + ci = inst + v0; + break; + + case OP_LTF: + opStack -= 2; + if ( r1.f < r0.f ) + ci = inst + v0; + break; + + case OP_LEF: + opStack -= 2; + if ( r1.f <= r0.f ) + ci = inst + v0; + break; + + case OP_GTF: + opStack -= 2; + if ( r1.f > r0.f ) + ci = inst + v0; + break; + + case OP_GEF: + opStack -= 2; + if ( r1.f >= r0.f ) + ci = inst + v0; + break; + + //=================================================================== + + case OP_LOAD1: + r0.i = *opStack = image[ r0.i & dataMask ]; + goto nextInstruction2; + + case OP_LOAD2: + r0.i = *opStack = *(unsigned short *)&image[ r0.i & dataMask ]; + goto nextInstruction2; + + case OP_LOAD4: + r0.i = *opStack = *(int *)&image[ r0.i & dataMask ]; + goto nextInstruction2; + + case OP_STORE1: + image[ r1.i & dataMask ] = r0.i; + opStack -= 2; + break; + + case OP_STORE2: + *(short *)&image[ r1.i & dataMask ] = r0.i; + opStack -= 2; + break; + + case OP_STORE4: + *(int *)&image[ r1.i & dataMask ] = r0.i; + opStack -= 2; + break; + + case OP_ARG: + // single byte offset from programStack + *(int *)&image[ ( v0 + programStack ) /*& ( dataMask & ~3 ) */ ] = r0.i; + opStack--; + break; + + case OP_BLOCK_COPY: + { + int *src, *dest; + int count, srci, desti; + + count = v0; + // MrE: copy range check + srci = r0.i & dataMask; + desti = r1.i & dataMask; + count = ((srci + count) & dataMask) - srci; + count = ((desti + count) & dataMask) - desti; + + src = (int *)&image[ srci ]; + dest = (int *)&image[ desti ]; + + memcpy( dest, src, count ); + opStack -= 2; + } + break; + + case OP_SEX8: + *opStack = (signed char)*opStack; + break; + + case OP_SEX16: + *opStack = (short)*opStack; + break; + + case OP_NEGI: + *opStack = -r0.i; + break; + + case OP_ADD: + opStack[-1] = r1.i + r0.i; + opStack--; + break; + + case OP_SUB: + opStack[-1] = r1.i - r0.i; + opStack--; + break; + + case OP_DIVI: + opStack[-1] = r1.i / r0.i; + opStack--; + break; + + case OP_DIVU: + opStack[-1] = r1.u / r0.u; + opStack--; + break; + + case OP_MODI: + opStack[-1] = r1.i % r0.i; + opStack--; + break; + + case OP_MODU: + opStack[-1] = r1.u % r0.u; + opStack--; + break; + + case OP_MULI: + opStack[-1] = r1.i * r0.i; + opStack--; + break; + + case OP_MULU: + opStack[-1] = r1.u * r0.u; + opStack--; + break; + + case OP_BAND: + opStack[-1] = r1.u & r0.u; + opStack--; + break; + + case OP_BOR: + opStack[-1] = r1.u | r0.u; + opStack--; + break; + + case OP_BXOR: + opStack[-1] = r1.u ^ r0.u; + opStack--; + break; + + case OP_BCOM: + *opStack = ~ r0.u; + break; + + case OP_LSH: + opStack[-1] = r1.i << r0.i; + opStack--; + break; + + case OP_RSHI: + opStack[-1] = r1.i >> r0.i; + opStack--; + break; + + case OP_RSHU: + opStack[-1] = r1.u >> r0.i; + opStack--; + break; + + case OP_NEGF: + *(float *)opStack = - r0.f; + break; + + case OP_ADDF: + *(float *)(opStack-1) = r1.f + r0.f; + opStack--; + break; + + case OP_SUBF: + *(float *)(opStack-1) = r1.f - r0.f; + opStack--; + break; + + case OP_DIVF: + *(float *)(opStack-1) = r1.f / r0.f; + opStack--; + break; + + case OP_MULF: + *(float *)(opStack-1) = r1.f * r0.f; + opStack--; + break; + + case OP_CVIF: + *(float *)opStack = (float) r0.i; + break; + + case OP_CVFI: + *opStack = (int) r0.f; + break; + + case MOP_LOCAL_LOAD4: + ci++; + opStack++; + r1.i = r0.i; + r0.i = *opStack = *(int *)&image[ v0 + programStack ]; + goto nextInstruction2; + + case MOP_LOCAL_LOAD4_CONST: + r1.i = opStack[1] = *(int *)&image[ v0 + programStack ]; + r0.i = opStack[2] = (ci+1)->value; + opStack += 2; + ci += 2; + goto nextInstruction2; + + case MOP_LOCAL_LOCAL: + r1.i = opStack[1] = v0 + programStack; + r0.i = opStack[2] = ci->value + programStack; + opStack += 2; + ci++; + goto nextInstruction2; + + case MOP_LOCAL_LOCAL_LOAD4: + r1.i = opStack[1] = v0 + programStack; + r0.i /*= opStack[2]*/ = ci->value + programStack; + r0.i = opStack[2] = *(int *)&image[ r0.i /*& dataMask*/ ]; + opStack += 2; + ci += 2; + goto nextInstruction2; + } + } + +done: + //vm->currentlyInterpreting = false; + + if ( opStack != &stack[2] ) { + VM_StackTrace(vm, ci - (instruction_t *)vm->codeBase.ptr, programStack); + SV_Error( "Interpreter error: opStack = %ld", (long int) (opStack - stack) ); + } + + vm->programStack = stackOnEntry; + + // return the result + return *opStack; +} +#endif /* USE_PR2 */ diff --git a/src/vm_local.h b/src/vm_local.h new file mode 100644 index 000000000..315ea63d6 --- /dev/null +++ b/src/vm_local.h @@ -0,0 +1,293 @@ +/* + * 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 VM_LOCAL_H +#define VM_LOCAL_H + +#include "vm.h" +#define MAX_OPSTACK_SIZE 512 +#define PROC_OPSTACK_SIZE 30 +#define STACK_MASK (MAX_OPSTACK_SIZE-1) +#define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack ) + +// we don't need more than 4 arguments (counting callnum) for vmMain, at least in Quake3 +#define MAX_VMMAIN_CALL_ARGS 4 + +// don't change +// Hardcoded in q3asm an reserved at end of bss +#define PROGRAM_STACK_SIZE 0x10000 + +// for some buggy mods +#define PROGRAM_STACK_EXTRA (32*1024) + +#define PAD(base, alignment) (((base)+(alignment)-1) & ~((alignment)-1)) +#define PADLEN(base, alignment) (PAD((base), (alignment)) - (base)) + +typedef enum { + OP_UNDEF, + + OP_IGNORE, + + OP_BREAK, + + OP_ENTER, + OP_LEAVE, + OP_CALL, + OP_PUSH, + OP_POP, + + OP_CONST, + OP_LOCAL, + + OP_JUMP, + + //------------------- + + OP_EQ, + OP_NE, + + OP_LTI, + OP_LEI, + OP_GTI, + OP_GEI, + + OP_LTU, + OP_LEU, + OP_GTU, + OP_GEU, + + OP_EQF, + OP_NEF, + + OP_LTF, + OP_LEF, + OP_GTF, + OP_GEF, + + //------------------- + + OP_LOAD1, + OP_LOAD2, + OP_LOAD4, + OP_STORE1, + OP_STORE2, + OP_STORE4, // *(stack[top-1]) = stack[top] + OP_ARG, + + OP_BLOCK_COPY, + + //------------------- + + OP_SEX8, + OP_SEX16, + + OP_NEGI, + OP_ADD, + OP_SUB, + OP_DIVI, + OP_DIVU, + OP_MODI, + OP_MODU, + OP_MULI, + OP_MULU, + + OP_BAND, + OP_BOR, + OP_BXOR, + OP_BCOM, + + OP_LSH, + OP_RSHI, + OP_RSHU, + + OP_NEGF, + OP_ADDF, + OP_SUBF, + OP_DIVF, + OP_MULF, + + OP_CVIF, + OP_CVFI, + + OP_MAX +} opcode_t; + +typedef struct { + int value; // 32 + byte op; // 8 + byte opStack; // 8 + unsigned jused:1; + unsigned swtch:1; +} instruction_t; + +typedef struct vmSymbol_s { + struct vmSymbol_s *next; + int symValue; + int profileCount; + char symName[1]; // variable sized +} vmSymbol_t; + + +//typedef void(*vmfunc_t)(void); + +typedef union vmFunc_u { + byte *ptr; + void (*func)(void); +} vmFunc_t; + +#define VM_MAGIC 0x12721444 +#define VM_MAGIC_VER2 0x12721445 +typedef struct +{ + int vmMagic; + + int instructionCount; + + int codeOffset; + int codeLength; + + int dataOffset; + int dataLength; + int litLength; // ( dataLength - litLength ) should be byteswapped on load + int bssLength; // zero filled memory appended to datalength + //!!! below here is VM_MAGIC_VER2 !!! + int jtrgLength; // number of jump table targets +} vmHeader_t; + + +typedef struct vm_s vm_t; + +struct vm_s { + + unsigned int programStack; // the vm may be recursively entered + syscall_t systemCall; + byte *dataBase; + int *opStack; // pointer to local function stack + + int instructionCount; + intptr_t *instructionPointers; + + //------------------------------------ + + const char *name; + vmIndex_t index; + + // for dynamic linked modules + void *dllHandle; + dllSyscall_t entryPoint; + dllSyscall_t dllSyscall; + void (*destroy)(vm_t* self); + + // for interpreted modules + //qbool currentlyInterpreting; + + qbool compiled; + + vmFunc_t codeBase; + unsigned int codeSize; // code + jump targets, needed for proper munmap() + unsigned int codeLength; // just for information + + unsigned int dataMask; + unsigned int dataLength; // data segment length + unsigned int exactDataLength; // from qvm header + unsigned int dataAlloc; // actually allocated + + unsigned int stackBottom; // if programStack < stackBottom, error + int *opStackTop; + + int numSymbols; + vmSymbol_t *symbols; + + int callLevel; // counts recursive VM_Call + int breakFunction; // increment breakCount on function entry to this + int breakCount; + + byte *jumpTableTargets; + int numJumpTableTargets; + + uint32_t crc32sum; + + qbool forceDataMask; + vmInterpret_t type; + qbool pr2_references; + //int privateFlag; +}; + +extern int vm_debugLevel; + +extern cvar_t vm_rtChecks; +qbool VM_Compile( vm_t *vm, vmHeader_t *header ); +int VM_CallCompiled( vm_t *vm, int nargs, int *args ); + +qbool VM_PrepareInterpreter2( vm_t *vm, vmHeader_t *header ); +int VM_CallInterpreted2( vm_t *vm, int nargs, int *args ); + +vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ); +int VM_SymbolToValue( vm_t *vm, const char *symbol ); +const char *VM_ValueToSymbol( vm_t *vm, int value ); +void VM_LogSyscalls( int *args ); + +const char *VM_LoadInstructions( const byte *code_pos, int codeLength, int instructionCount, instruction_t *buf ); +const char *VM_CheckInstructions( instruction_t *buf, int instructionCount, + const byte *jumpTableTargets, + int numJumpTableTargets, + int dataLength ); + +//void VM_ReplaceInstructions( vm_t *vm, instruction_t *buf ); + +#define JUMP (1<<0) + +typedef struct opcode_info_s +{ + int size; + int stack; + int nargs; + int flags; +} opcode_info_t ; + +extern opcode_info_t ops[ OP_MAX ]; + +void VM_Init( void ); +vm_t *VM_Create( vmIndex_t index, const char* name, syscall_t systemCalls, /*dllSyscall_t dllSyscalls,*/ vmInterpret_t interpret ); + +// module should be bare: "cgame", not "cgame.dll" or "vm/cgame.qvm" + +void VM_Free( vm_t *vm ); +void VM_Clear(void); +void VM_Forced_Unload_Start(void); +void VM_Forced_Unload_Done(void); +vm_t *VM_Restart( vm_t *vm ); + +intptr_t QDECL VM_Call( vm_t *vm, int nargs, int callNum, ... ); + +void VM_Debug( int level ); +void VM_CheckBounds( const vm_t *vm, unsigned int address, unsigned int length ); +void VM_CheckBounds2( const vm_t *vm, unsigned int addr1, unsigned int addr2, unsigned int length ); + +#if 1 +#define VM_CHECKBOUNDS VM_CheckBounds +#define VM_CHECKBOUNDS2 VM_CheckBounds2 +#else // for performance evaluation purposes +#define VM_CHECKBOUNDS(vm,a,b) +#define VM_CHECKBOUNDS2(vm,a,b,c) +#endif + + + +#endif // VM_LOCAL_H + diff --git a/src/vm_x86.c b/src/vm_x86.c new file mode 100644 index 000000000..bdd384a97 --- /dev/null +++ b/src/vm_x86.c @@ -0,0 +1,2968 @@ +/* + * 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 + * + * + */ + +#ifdef USE_PR2 +#ifdef SERVERONLY +#include "qwsvdef.h" +#else +#include "quakedef.h" +#include "pr_comp.h" +#include "g_public.h" +#endif +#include "vm_local.h" + +#if ( idx386) || (idx64) +#ifdef _WIN32 +#include +#endif + +#ifdef __FreeBSD__ +#include +#endif + +#ifndef _WIN32 +#include // for PROT_ stuff +#endif + +/* need this on NX enabled systems (i386 with PAE kernel or + * noexec32=on x86_64) */ +#if defined(__linux__) || defined(__FreeBSD__) +#define VM_X86_MMAP +#endif + +//#define VM_LOG_SYSCALLS +#define JUMP_OPTIMIZE 0 + +#if JUMP_OPTIMIZE +#define NUM_PASSES 7 +#else +#define NUM_PASSES 3 +#endif + +/* +** -------------------------------------------------------------------------------- +** +** PROCESSOR STUFF +** +** -------------------------------------------------------------------------------- +*/ + +#define CPU_FCOM 0x01 +#define CPU_MMX 0x02 +#define CPU_SSE 0x04 +#define CPU_SSE2 0x08 +#define CPU_SSE3 0x10 + +int CPU_Flags = 0; +#if defined _MSC_VER + +static void CPUID( int func, unsigned int *regs ) +{ +#if _MSC_VER >= 1400 + __cpuid( regs, func ); +#else + __asm { + mov edi,regs + mov eax,[edi] + cpuid + mov [edi], eax + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], edx + } +#endif +} + +#else + +static void CPUID( int func, unsigned int *regs ) +{ + __asm__ __volatile__( "cpuid" : + "=a"(regs[0]), + "=b"(regs[1]), + "=c"(regs[2]), + "=d"(regs[3]) : + "a"(func) ); +} +#endif + +int Sys_GetProcessorId( char *vendor ) +{ + unsigned int regs[4]; + + // setup initial features +#if idx64 + CPU_Flags |= CPU_SSE | CPU_SSE2 | CPU_FCOM; +#else + CPU_Flags = 0; +#endif + + // get CPU feature bits + CPUID( 1, regs ); + + // bit 15 of EDX denotes CMOV/FCMOV/FCOMI existence + if ( regs[3] & ( 1 << 15 ) ) + CPU_Flags |= CPU_FCOM; + + // bit 23 of EDX denotes MMX existence + if ( regs[3] & ( 1 << 23 ) ) + CPU_Flags |= CPU_MMX; + + // bit 25 of EDX denotes SSE existence + if ( regs[3] & ( 1 << 25 ) ) + CPU_Flags |= CPU_SSE; + + // bit 26 of EDX denotes SSE2 existence + if ( regs[3] & ( 1 << 26 ) ) + CPU_Flags |= CPU_SSE2; + + // bit 0 of ECX denotes SSE3 existence + if ( regs[2] & ( 1 << 0 ) ) + CPU_Flags |= CPU_SSE3; + + if ( vendor ) { +#if idx64 + strcpy( vendor, "64-bit " ); + vendor += strlen( vendor ); +#else + vendor[0] = '\0'; +#endif + // get CPU vendor string + CPUID( 0, regs ); + memcpy( vendor+0, (char*) ®s[1], 4 ); + memcpy( vendor+4, (char*) ®s[3], 4 ); + memcpy( vendor+8, (char*) ®s[2], 4 ); + vendor[12] = '\0'; vendor += 12; + if ( CPU_Flags ) { + // print features +#if !idx64 // do not print default 64-bit features in 32-bit mode + strcat( vendor, " w/" ); + if ( CPU_Flags & CPU_FCOM ) + strcat( vendor, " CMOV" ); + if ( CPU_Flags & CPU_MMX ) + strcat( vendor, " MMX" ); + if ( CPU_Flags & CPU_SSE ) + strcat( vendor, " SSE" ); + if ( CPU_Flags & CPU_SSE2 ) + strcat( vendor, " SSE2" ); +#endif + //if ( CPU_Flags & CPU_SSE3 ) + // strcat( vendor, " SSE3" ); + } + } + return 1; +} + +static void *VM_Alloc_Compiled( vm_t *vm, int codeLength, int tableLength ); +static void VM_Destroy_Compiled( vm_t *vm ); + +/* + ------------- + eax scratch + ebx* dataBase + ecx scratch (required for shifts) + edx scratch (required for divisions) + esi* program stack + edi* opstack + ebp* current proc stack ( dataBase + program stack ) + ------------- + rax scratch + rbx* dataBase + rcx scratch (required for shifts) + rdx scratch (required for divisions) + rsi* programStack + rdi* opstack + rbp* current proc stack ( dataBase + program stack ) + r8 instructionPointers + r9 dataMask + r12* systemCall + r13* stackBottom + r14* opStackTop + xmm0 scratch + xmm1 scratch + xmm2 scratch + xmm3 scratch + xmm4 scratch + xmm5 scratch + + Windows ABI: you are required to preserve the XMM6-XMM15 registers + System V ABI: you don't have to preserve any of the XMM registers + + Example how data segment will look like during vmMain execution: + | .... | + |------| vm->programStack -=36 (8+12+16) // set by vmMain + | ???? | +0 - unused, reserved for interpreter + | ???? | +4 - unused, reserved for interpreter + |------- + | arg0 | +8 \ + | arg4 | +12 | - passed arguments, accessible from subroutines + | arg8 | +16 / + |------| + | loc0 | +20 \ + | loc4 | +24 \ - locals, accessible only from local scope + | loc8 | +28 / + | lc12 | +32 / + |------| vm->programStack -= 24 ( 8 + MAX_VMMAIN_CALL_ARGS*4 ) // set by VM_CallCompiled() + | ???? | +0 - unused, reserved for interpreter + | ???? | +4 - unused, reserved for interpreter + | arg0 | +8 \ + | arg1 | +12 \ - passed arguments, accessible from vmMain + | arg2 | +16 / + | arg3 | +20 / + |------| vm->programStack = vm->dataMask + 1 // set by VM_Create() + + jump/call opStack rules: + + 1) opStack must be 8 before conditional jump + 2) opStack must be 4 before unconditional jump + 3) opStack must be >=4 before OP_CALL + 4) opStack must remain the same after OP_CALL + 5) you may not jump in/call locations with opStack != 0 + +*/ + +#define ISS8(V) ( (V) >= -128 && (V) <= 127 ) +#define ISU8(V) ( (V) >= 0 && (V) <= 127 ) + +#define REWIND(N) { compiledOfs -= (N); instructionOffsets[ ip-1 ] = compiledOfs; }; + +typedef enum +{ + REG_EAX = 0, + REG_ECX +} reg_t; + + +typedef enum +{ + LAST_COMMAND_NONE = 0, + LAST_COMMAND_MOV_EDI_EAX, + LAST_COMMAND_MOV_EDI_CONST, + LAST_COMMAND_MOV_EAX_EDI, + LAST_COMMAND_MOV_EAX_EDI_CALL, + LAST_COMMAND_SUB_DI_4, + LAST_COMMAND_SUB_DI_8, + LAST_COMMAND_SUB_DI_12, + LAST_COMMAND_STORE_FLOAT_EDI, + LAST_COMMAND_STORE_FLOAT_EDI_X87, + LAST_COMMAND_STORE_FLOAT_EDI_SSE +} ELastCommand; + +typedef enum +{ + FUNC_ENTR = 0, + FUNC_CALL, + FUNC_SYSC, + FUNC_FTOL, + FUNC_BCPY, + FUNC_NCPY, + FUNC_PSOF, + FUNC_OSOF, + FUNC_BADJ, + FUNC_ERRJ, + FUNC_DATA, + FUNC_LAST +} funcx86_t; + +// macro opcode sequences +typedef enum { + MOP_UNDEF = OP_MAX, + MOP_IGNORE4, + MOP_ADD4, + MOP_SUB4, + MOP_BAND4, + MOP_BOR4, + MOP_NCPY, +} macro_op_t; + +static byte *code; +static int compiledOfs; +static int *instructionOffsets; +static intptr_t *instructionPointers; + +static instruction_t *inst = NULL; +static instruction_t *ci; +static instruction_t *ni; + +static int fp_cw[2] = { 0x0000, 0x0F7F }; // [0] - current value, [1] - round towards zero + +static int ip, pass; +static int lastConst; +static opcode_t pop1; + +static ELastCommand LastCommand; + +int funcOffset[FUNC_LAST]; + +#ifdef DEBUG_VM +static int errParam = 0; +#endif + +static void ErrJump( void ) +{ + SV_Error( "program tried to execute code outside VM" ); +} + + +static void BadJump( void ) +{ + SV_Error( "program tried to execute code at bad location inside VM" ); +} + + +static void BadStack( void ) +{ + SV_Error( "program tried to overflow program stack" ); +} + + +static void BadOpStack( void ) +{ + SV_Error( "program tried to overflow opcode stack" ); +} + + +static void BadData( void ) +{ +#ifdef DEBUG_VM + SV_Error( "program tried to read/write out of data segment at %i", errParam ); +#else + SV_Error( "program tried to read/write out of data segment" ); +#endif +} + + +static void (*const errJumpPtr)(void) = ErrJump; +static void (*const badJumpPtr)(void) = BadJump; +static void (*const badStackPtr)(void) = BadStack; +static void (*const badOpStackPtr)(void) = BadOpStack; +static void (*const badDataPtr)(void) = BadData; + +static void VM_FreeBuffers( void ) +{ + // should be freed in reversed allocation order + Q_free( instructionOffsets ); + Q_free( inst ); +} + +static const inline qbool HasFCOM( void ) +{ +#if idx386 + return ( CPU_Flags & CPU_FCOM ); +#else + return true; // assume idx64 +#endif +} + + +static const inline qbool HasSSEFP( void ) +{ +#if idx386 + return ( CPU_Flags & CPU_SSE ) && ( CPU_Flags & CPU_SSE2 ); +#else + return true; // assume idx64 +#endif +} + +static void Emit1( int v ) +{ + if ( code ) + { + code[ compiledOfs ] = v; + } + compiledOfs++; + + LastCommand = LAST_COMMAND_NONE; +} + +static void Emit4( int v ) +{ + Emit1( v & 255 ); + Emit1( ( v >> 8 ) & 255 ); + Emit1( ( v >> 16 ) & 255 ); + Emit1( ( v >> 24 ) & 255 ); +} + + +#if idx64 +static void Emit8( int64_t v ) +{ + Emit1( ( v >> 0 ) & 255 ); + Emit1( ( v >> 8 ) & 255 ); + Emit1( ( v >> 16 ) & 255 ); + Emit1( ( v >> 24 ) & 255 ); + Emit1( ( v >> 32 ) & 255 ); + Emit1( ( v >> 40 ) & 255 ); + Emit1( ( v >> 48 ) & 255 ); + Emit1( ( v >> 56 ) & 255 ); +} +#endif + + +static void EmitPtr( const void *ptr ) +{ +#if idx64 + Emit8( (intptr_t)ptr ); +#else + Emit4( (intptr_t)ptr ); +#endif +} + + +static int Hex( int c ) +{ + if ( c >= '0' && c <= '9' ) { + return c - '0'; + } + if ( c >= 'A' && c <= 'F' ) { + return 10 + c - 'A'; + } + if ( c >= 'a' && c <= 'f' ) { + return 10 + c - 'a'; + } + + VM_FreeBuffers(); + SV_Error( "Hex: bad char '%c'", c ); + + return 0; +} + + +static void EmitString( const char *string ) +{ + int c1, c2; + int v; + + while ( 1 ) { + c1 = string[0]; + c2 = string[1]; + + v = ( Hex( c1 ) << 4 ) | Hex( c2 ); + Emit1( v ); + + if ( !string[2] ) { + break; + } + string += 3; + } +} + + +static void EmitRexString( const char *string ) +{ +#if idx64 + Emit1( 0x48 ); +#endif + EmitString( string ); +} + + +static void EmitAlign( int align ) +{ + int i, n; + + n = compiledOfs & ( align - 1 ); + + for ( i = 0; i < n ; i++ ) + EmitString( "90" ); // nop +} + + +static void EmitCommand( ELastCommand command ) +{ + switch( command ) + { + case LAST_COMMAND_MOV_EDI_EAX: + EmitString( "89 07" ); // mov dword ptr [edi], eax + break; + + case LAST_COMMAND_MOV_EAX_EDI: + EmitString( "8B 07" ); // mov eax, dword ptr [edi] + break; + + case LAST_COMMAND_SUB_DI_4: + EmitRexString( "83 EF 04" ); // sub edi, 4 + break; + + case LAST_COMMAND_SUB_DI_8: + EmitRexString( "83 EF 08" ); // sub edi, 8 + break; + + case LAST_COMMAND_SUB_DI_12: + EmitRexString( "83 EF 0C" ); // sub edi, 12 + break; + + case LAST_COMMAND_STORE_FLOAT_EDI_SSE: + EmitString( "F3 0F 11 07" ); // movss dword ptr [edi], xmm0 + break; + + case LAST_COMMAND_STORE_FLOAT_EDI_X87: + EmitString( "D9 1F" ); // fstp dword ptr [edi] + break; + + case LAST_COMMAND_STORE_FLOAT_EDI: // meta command + if ( HasSSEFP() ) { + EmitString( "F3 0F 11 07" );// movss dword ptr [edi], xmm0 + command = LAST_COMMAND_STORE_FLOAT_EDI_SSE; + } else { + EmitString( "D9 1F" ); // fstp dword ptr [edi] + command = LAST_COMMAND_STORE_FLOAT_EDI_X87; + } + break; + + default: + break; + } + LastCommand = command; +} + +static void EmitAddEDI4( vm_t *vm ) +{ + if ( LastCommand == LAST_COMMAND_NONE ) + { + EmitRexString( "83 C7 04" ); // add edi,4 + return; + } + + if ( LastCommand == LAST_COMMAND_SUB_DI_4 ) // sub edi, 4 + { +#if idx64 + REWIND( 4 ); +#else + REWIND( 3 ); +#endif + LastCommand = LAST_COMMAND_NONE; + return; + } + + if ( LastCommand == LAST_COMMAND_SUB_DI_8 ) // sub edi, 8 + { +#if idx64 + REWIND( 4 ); +#else + REWIND( 3 ); +#endif + EmitCommand( LAST_COMMAND_SUB_DI_4 ); + return; + } + + if ( LastCommand == LAST_COMMAND_SUB_DI_12 ) // sub edi, 12 + { +#if idx64 + REWIND( 4 ); +#else + REWIND( 3 ); +#endif + EmitCommand( LAST_COMMAND_SUB_DI_8 ); + return; + } + + EmitRexString( "83 C7 04" ); // add edi,4 +} + + +static void EmitMovEAXEDI( vm_t *vm ) +{ + opcode_t pop = pop1; + pop1 = OP_UNDEF; + + if ( LastCommand == LAST_COMMAND_NONE ) + { + EmitString( "8B 07" ); // mov eax, dword ptr [edi] + return; + } + + if ( LastCommand == LAST_COMMAND_MOV_EAX_EDI ) + return; + + if ( LastCommand == LAST_COMMAND_MOV_EAX_EDI_CALL ) + return; + + if ( LastCommand == LAST_COMMAND_MOV_EDI_EAX ) // mov dword ptr [edi], eax + { + REWIND( 2 ); + LastCommand = LAST_COMMAND_NONE; + return; + } + + if ( pop == OP_DIVI || pop == OP_DIVU || pop == OP_MULI || pop == OP_MULU || + pop == OP_STORE4 || pop == OP_STORE2 || pop == OP_STORE1 ) + { + return; + } + + if ( LastCommand == LAST_COMMAND_MOV_EDI_CONST ) // mov dword ptr [edi], 0x12345678 + { + REWIND( 6 ); + if ( lastConst == 0 ) { + EmitString( "31 C0" ); // xor eax, eax + } else { + EmitString( "B8" ); // mov eax, 0x12345678 + Emit4( lastConst ); + } + return; + } + + EmitString( "8B 07" ); // mov eax, dword ptr [edi] +} + + +void EmitMovECXEDI( vm_t *vm ) +{ + opcode_t pop = pop1; + pop1 = OP_UNDEF; + + if ( LastCommand == LAST_COMMAND_NONE ) + { + EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] + return; + } + + if ( LastCommand == LAST_COMMAND_MOV_EAX_EDI_CALL ) + { + EmitString( "89 C1" ); // mov ecx, eax + return; + } + + if ( LastCommand == LAST_COMMAND_MOV_EAX_EDI ) // mov eax, dword ptr [edi] + { + REWIND( 2 ); + EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] + return; + } + + if ( LastCommand == LAST_COMMAND_MOV_EDI_EAX ) // mov dword ptr [edi], eax + { + REWIND( 2 ); + EmitString( "89 C1" ); // mov ecx, eax + return; + } + + if (pop == OP_DIVI || pop == OP_DIVU || pop == OP_MULI || pop == OP_MULU || + pop == OP_STORE4 || pop == OP_STORE2 || pop == OP_STORE1 ) + { + EmitString( "89 C1" ); // mov ecx, eax + return; + } + + if ( LastCommand == LAST_COMMAND_MOV_EDI_CONST ) // mov dword ptr [edi], 0x12345678 + { + REWIND( 6 ); + EmitString( "B9" ); // mov ecx, 0x12345678 + Emit4( lastConst ); + return; + } + + EmitString( "8B 0F" ); // mov ecx, dword ptr [edi] +} + + +static void EmitCheckReg( vm_t *vm, int reg, int size ) +{ + int n; + + if ( !( (int)vm_rtChecks.value & 8 ) || vm->forceDataMask ) { + if ( vm->forceDataMask ) { + if ( reg == REG_EAX ) + EmitString( "25" ); // and eax, 0x12345678 + else + EmitString( "81 E1" ); // and ecx, 0x12345678 + Emit4( vm->dataMask ); + } + return; + } + +#ifdef DEBUG_VM + EmitString( "50" ); // push eax + EmitRexString( "B8" ); // mov eax, &errParam + EmitPtr( &errParam ); + EmitString( "C7 00" ); // mov [rax], ip-1 + Emit4( ip-1 ); + EmitString( "58" ); // pop eax +#endif + +#if idx64 + if ( reg == REG_EAX ) + EmitString( "44 39 C8" );// cmp eax, r9d // vm->dataMask + else + EmitString( "44 39 C9" );// cmp ecx, r9d // vm->dataMask +#else + if ( reg == REG_EAX ) + EmitString( "3D" ); // cmp eax, 0x12345678 + else + EmitString( "81 F9" ); // cmp ecx, 0x12345678 + + Emit4( vm->dataMask - (size - 1) ); +#endif + + // error reporting + EmitString( "0F 87" ); // ja +errorFunction + n = funcOffset[FUNC_DATA] - compiledOfs; + Emit4( n - 6 ); +} + + +static int EmitLoadFloatEDI_SSE( vm_t *vm ) +{ + // movss dword ptr [edi], xmm0 + if ( LastCommand == LAST_COMMAND_STORE_FLOAT_EDI_SSE ) + { + if ( !vm ) + return 1; + REWIND( 4 ); + LastCommand = LAST_COMMAND_NONE; + return 1; + } + EmitString( "F3 0F 10 07" ); // movss xmm0, dword ptr [edi] + return 0; +} + + +static int EmitLoadFloatEDI_X87( vm_t *vm ) +{ + // fstp dword ptr [edi] + if ( LastCommand == LAST_COMMAND_STORE_FLOAT_EDI_X87 ) + { + if ( !vm ) + return 1; + REWIND( 2 ); + LastCommand = LAST_COMMAND_NONE; + return 1; + } + + EmitString( "D9 07" ); // fld dword ptr [edi] + return 0; +} + + +static int EmitLoadFloatEDI( vm_t *vm ) +{ + if ( HasSSEFP() ) + return EmitLoadFloatEDI_SSE( vm ); + else + return EmitLoadFloatEDI_X87( vm ); +} + +#if JUMP_OPTIMIZE +const char *NearJumpStr( int op ) +{ + switch ( op ) + { + case OP_EQF: + case OP_EQ: return "74"; // je + + case OP_NEF: + case OP_NE: return "75"; // jne + + case OP_LTI: return "7C"; // jl + case OP_LEI: return "7E"; // jle + case OP_GTI: return "7F"; // jg + case OP_GEI: return "7D"; // jge + + case OP_LTF: + case OP_LTU: return "72"; // jb + + case OP_LEF: + case OP_LEU: return "76"; // jbe + + case OP_GTF: + case OP_GTU: return "77"; // ja + + case OP_GEF: + case OP_GEU: return "73"; // jae + + case OP_JUMP: return "EB"; // jmp + + //default: + // SV_Error( "Bad opcode %i", op ); + }; + return NULL; +} +#endif + + +const char *FarJumpStr( int op, int *n ) +{ + switch ( op ) + { + case OP_EQF: + case OP_EQ: *n = 2; return "0F 84"; // je + + case OP_NEF: + case OP_NE: *n = 2; return "0F 85"; // jne + + case OP_LTI: *n = 2; return "0F 8C"; // jl + case OP_LEI: *n = 2; return "0F 8E"; // jle + case OP_GTI: *n = 2; return "0F 8F"; // jg + case OP_GEI: *n = 2; return "0F 8D"; // jge + + case OP_LTF: + case OP_LTU: *n = 2; return "0F 82"; // jb + + case OP_LEF: + case OP_LEU: *n = 2; return "0F 86"; // jbe + + case OP_GTF: + case OP_GTU: *n = 2; return "0F 87"; // ja + + case OP_GEF: + case OP_GEU: *n = 2; return "0F 83"; // jae + + case OP_JUMP: *n = 1; return "E9"; // jmp + }; + return NULL; +} + + +void EmitJump( vm_t *vm, instruction_t *i, int op, int addr ) +{ + const char *str; + int v, jump_size = 0; + + v = instructionOffsets[ addr ] - compiledOfs; + +#if JUMP_OPTIMIZE + if ( i->njump ) { + // can happen + if ( v < -126 || v > 129 ) { + str = FarJumpStr( op, &jump_size ); + EmitString( str ); + Emit4( v - 4 - jump_size ); + i->njump = 0; + return; + } + EmitString( NearJumpStr( op ) ); + Emit1( v - 2 ); + return; + } + + if ( pass >= 2 && pass < NUM_PASSES-2 ) { + if ( v >= -126 && v <= 129 ) { + EmitString( NearJumpStr( op ) ); + Emit1( v - 2 ); + i->njump = 1; + return; + } + } +#endif + + str = FarJumpStr( op, &jump_size ); + if ( jump_size == 0 ) { + SV_Error( "VM_CompileX86 error: %s\n", "bad jump size" ); + } else { + EmitString( str ); + Emit4( v - 4 - jump_size ); + } +} + + +void EmitFloatJump( vm_t *vm, instruction_t *i, int op, int addr ) +{ + switch ( op ) { + case OP_EQF: + EmitString( "80 E4 40" ); // and ah,0x40 + EmitJump( vm, i, OP_NE, addr ); + break; + + case OP_NEF: + EmitString( "80 E4 40" ); // and ah,0x40 + EmitJump( vm, i, OP_EQ, addr ); + break; + + case OP_LTF: + EmitString( "80 E4 01" ); // and ah,0x01 + EmitJump( vm, i, OP_NE, addr ); + break; + + case OP_LEF: + EmitString( "80 E4 41" ); // and ah,0x41 + EmitJump( vm, i, OP_NE, addr ); + break; + + case OP_GTF: + EmitString( "80 E4 41" ); // and ah,0x41 + EmitJump( vm, i, OP_EQ, addr ); + break; + + case OP_GEF: + EmitString( "80 E4 01" ); // and ah,0x01 + EmitJump( vm, i, OP_EQ, addr ); + break; + }; + +} + + +static void EmitCallAddr( vm_t *vm, int addr ) +{ + int v; + v = instructionOffsets[ addr ] - compiledOfs; + EmitString( "E8" ); + Emit4( v - 5 ); +} + + +static void EmitCallOffset( funcx86_t Func ) +{ + int v; + v = funcOffset[ Func ] - compiledOfs; + EmitString( "E8" ); // call +funcOffset[ Func ] + Emit4( v - 5 ); +} + + +#ifdef _WIN32 +#define SHADOW_BASE 40 +#else // linux/*BSD ABI +#define SHADOW_BASE 8 +#endif + +#define PUSH_STACK 32 +#define PARAM_STACK 128 + +static void EmitCallFunc( vm_t *vm ) +{ + static int sysCallOffset = 0; + int n; + + EmitString( "85 C0" ); // test eax, eax + EmitString( "7C" ); // jl +offset (SystemCall) + Emit1( sysCallOffset ); // will be valid after first pass +sysCallOffset = compiledOfs; + + // jump target range check + if ( (int)vm_rtChecks.value & 4 ) { + EmitString( "3D" ); // cmp eax, vm->instructionCount + Emit4( vm->instructionCount ); + EmitString( "0F 83" ); // jae +funcOffset[FUNC_ERRJ] + n = funcOffset[FUNC_ERRJ] - compiledOfs; + Emit4( n - 6 ); + } + + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + + // save proc base and programStack + EmitString( "55" ); // push ebp + EmitString( "56" ); // push esi + + // calling another vm function +#if idx64 + EmitString( "41 FF 14 C0" ); // call dword ptr [r8+rax*8] +#else + EmitString( "8D 0C 85" ); // lea ecx, [vm->instructionPointers+eax*4] + EmitPtr( instructionPointers ); + EmitString( "FF 11" ); // call dword ptr [ecx] +#endif + + // restore proc base and programStack so there is + // no need to validate programStack anymore + EmitString( "5E" ); // pop esi + EmitString( "5D" ); // pop ebp + + EmitString( "C3" ); // ret + +sysCallOffset = compiledOfs - sysCallOffset; + + // systemCall: + // convert negative num to system call number + // and store right before the first arg + EmitString( "F7 D0" ); // not eax + + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + + // we may jump here from ConstOptimize() also +funcOffset[FUNC_SYSC] = compiledOfs; + +#if idx64 + // allocate stack for shadow(win32)+parameters + EmitString( "48 81 EC" ); // sub rsp, 200 + Emit4( SHADOW_BASE + PUSH_STACK + PARAM_STACK ); + + // save scratch registers + EmitString( "48 8D 54 24" ); // lea rdx, [rsp+SHADOW_BASE] + Emit1( SHADOW_BASE ); + EmitString( "48 89 32" ); // mov [rdx+00], rsi + EmitString( "48 89 7A 08" ); // mov [rdx+08], rdi + EmitString( "4C 89 42 10" ); // mov [rdx+16], r8 + EmitString( "4C 89 4A 18" ); // mov [rdx+24], r9 + + // ecx = &int64_params[0] + EmitString( "48 8D 4C 24" ); // lea rcx, [rsp+SHADOW_BASE+PUSH_STACK] + Emit1( SHADOW_BASE + PUSH_STACK ); + + // save syscallNum + EmitString( "48 89 01" ); // mov [rcx], rax + + // vm->programStack = programStack - 4; + EmitString( "48 BA" ); // mov rdx, &vm->programStack + EmitPtr( &vm->programStack ); + //EmitString( "8D 46 FC" ); // lea eax, [esi-4] + EmitString( "8D 46 F8" ); // lea eax, [esi-8] + EmitString( "89 02" ); // mov [rdx], eax + //EmitString( "89 32" ); // mov dword ptr [rdx], esi + + // params = (vm->dataBase + programStack + 8); + EmitString( "48 8D 74 33 08" ); // lea rsi, [rbx+rsi+8] + + // rcx = &int64_params[1] + EmitString( "48 83 C1 08" ); // add rcx, 8 + + // dest_params[1-15] = params[1-15]; + EmitString( "31 D2" ); // xor edx, edx + // loop + EmitString( "48 63 04 96" ); // movsxd rax, dword [rsi+rdx*4] + EmitString( "48 89 04 D1" ); // mov qword ptr[rcx+rdx*8], rax + EmitString( "48 83 C2 01" ); // add rdx, 1 + EmitString( "48 83 FA" ); // cmp rdx, 15 + Emit1( (PARAM_STACK/8) - 1 ); + EmitString( "7C EE" ); // jl -18 + +#ifdef _WIN32 + // rcx = &int64_params[0] + EmitString( "48 83 E9 08" ); // sub rcx, 8 +#else // linux/*BSD ABI + // rdi = &int64_params[0] + EmitString( "48 8D 79 F8" ); // lea rdi, [rcx-8] +#endif + + // currentVm->systemCall( param ); + EmitString( "41 FF 14 24" ); // call qword [r12] + + // restore registers + EmitString( "48 8D 54 24" ); // lea rdx, [rsp+SHADOW_BASE] + Emit1( SHADOW_BASE ); + EmitString( "48 8B 32" ); // mov rsi, [rdx+00] + EmitString( "48 8B 7A 08" ); // mov rdi, [rdx+08] + EmitString( "4C 8B 42 10" ); // mov r8, [rdx+16] + EmitString( "4C 8B 4A 18" ); // mov r9, [rdx+24] + + // we added the return value: *(opstack+1) = eax + EmitAddEDI4( vm ); // add edi, 4 + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + + // return stack + EmitString( "48 81 C4" ); // add rsp, 200 + Emit4( SHADOW_BASE + PUSH_STACK + PARAM_STACK ); + + EmitRexString( "8D 2C 33" ); // lea rbp, [rbx+rsi] + + EmitString( "C3" ); // ret + +#else // i386 + + // params = (int *)((byte *)currentVM->dataBase + programStack + 4); + EmitString( "8D 4D 04" ); // lea ecx, [ebp+4] + + // function prologue + EmitString( "55" ); // push ebp + EmitRexString( "89 E5" ); // mov ebp, esp + EmitRexString( "83 EC 04" ); // sub esp, 4 + // align stack before call + EmitRexString( "83 E4 F0" ); // and esp, -16 + + // ABI note: esi/edi must not change during call! + + // currentVM->programStack = programStack - 4; + EmitString( "8D 56 FC" ); // lea edx, [esi-4] + EmitString( "89 15" ); // mov [&vm->programStack], edx + EmitPtr( &vm->programStack ); + + // params[0] = syscallNum + EmitString( "89 01" ); // mov [ecx], eax + + // cdecl - set params + EmitString( "89 0C 24" ); // mov [esp], ecx + + // currentVm->systemCall( param ); + EmitString( "FF 15" ); // call dword ptr [¤tVM->systemCall] + EmitPtr( &vm->systemCall ); + + // we added the return value: *(opstack+1) = eax +#if 0 + EmitAddEDI4( vm ); // add edi, 4 + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov [edi], eax +#else // break dependency from edi value? + EmitString( "89 47 04" ); // mov [edi+4], eax + EmitAddEDI4( vm ); // add edi, 4 +#endif + + // function epilogue + EmitRexString( "89 EC" ); // mov esp, ebp + EmitString( "5D" ); // pop ebp + EmitString( "C3" ); // ret +#endif +} + + +static void EmitFTOLFunc( vm_t *vm ) +{ + EmitRexString( "B8" ); // mov eax, &fp_cw[0] + EmitPtr( &fp_cw[0] ); + EmitString( "9B D9 38" ); // fnstcw word ptr [eax] + EmitString( "D9 68 04" ); // fldcw word ptr [eax+4] + EmitString( "DB 1F" ); // fistp dword ptr [edi] + EmitString( "D9 28" ); // fldcw word ptr [eax] + EmitString( "C3" ); // ret +} + + +static void EmitBCPYFunc( vm_t *vm ) +{ + // FIXME: range check + EmitString( "56" ); // push esi + EmitString( "57" ); // push edi + EmitString( "8B 37" ); // mov esi,[edi] + EmitString( "8B 7F FC" ); // mov edi,[edi-4] + EmitString( "B8" ); // mov eax, datamask + Emit4( vm->dataMask ); + EmitString( "21 C6" ); // and esi, eax + EmitString( "21 C7" ); // and edi, eax +#if idx64 + EmitString( "48 01 DE" ); // add rsi, rbx + EmitString( "48 01 DF" ); // add rdi, rbx +#else + EmitString( "03 F3" ); // add esi, ebx + EmitString( "03 FB" ); // add edi, ebx +#endif + EmitString( "F3 A5" ); // rep movsd + EmitString( "5F" ); // pop edi + EmitString( "5E" ); // pop esi + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + EmitString( "C3" ); // ret +} + + +static void EmitPSOFFunc( vm_t *vm ) +{ + EmitRexString( "B8" ); // mov eax, badStackPtr + EmitPtr( &badStackPtr ); + EmitString( "FF 10" ); // call [eax] + EmitString( "C3" ); // ret +} + + +static void EmitOSOFFunc( vm_t *vm ) +{ + EmitRexString( "B8" ); // mov eax, badOptackPtr + EmitPtr( &badOpStackPtr ); + EmitString( "FF 10" ); // call [eax] + EmitString( "C3" ); // ret +} + + +static void EmitBADJFunc( vm_t *vm ) +{ + EmitRexString( "B8" ); // mov eax, badJumpPtr + EmitPtr( &badJumpPtr ); + EmitString( "FF 10" ); // call [eax] + EmitString( "C3" ); // ret +} + + +static void EmitERRJFunc( vm_t *vm ) +{ + EmitRexString( "B8" ); // mov eax, errJumpPtr + EmitPtr( &errJumpPtr ); + EmitString( "FF 10" ); // call [eax] + EmitString( "C3" ); // ret +} + + +static void EmitDATAFunc( vm_t *vm ) +{ + EmitRexString( "B8" ); // mov eax, badDataPtr + EmitPtr( &badDataPtr ); + EmitString( "FF 10" ); // call [eax] + EmitString( "C3" ); // ret +} + + +static void EmitNCPYFunc( vm_t *vm ) +{ + static int Lend, Lcopy, Lpadz, Lpop0, Lpop1; // jump labels + int n; + + //EmitString( "8B 4D 10" ); // mov ecx, dword ptr [ebp+16] // counter + EmitString( "89 C1" ); // mov ecx, eax // get cached value from previous OP_ARG instruction + EmitString( "85 C9" ); // test ecx, ecx + EmitString( "74" ); // je +Lend + Emit1( Lend ); Lend = compiledOfs; + EmitString( "57" ); // push edi + EmitString( "8B 55 0C" ); // mov edx, dword ptr [ebp+12] // source + EmitString( "8B 7D 08" ); // mov edi, dword ptr [ebp+08] // destination + EmitRexString( "01 DA" ); // add edx, ebx // + vm->dataBase + +#if 0 + if ( vm->forceDataMask ) + { +#ifdef idx64 + EmitString( "44 21 CF" ); // and edi, r9d +#else + EmitString( "81 E7" ); // and edi, vm->dataMask + Emit4( vm->dataMask ); +#endif + EmitRexString( "01 DF" ); // add edi, ebx // + vm->dataBase + } + else +#endif + if ( (int)vm_rtChecks.value & 8 ) // security checks + { + EmitString( "89 F8" ); // mov eax, edi + EmitString( "09 C8" ); // or eax, ecx + EmitString( "3D" ); // cmp eax, vm->dataMask + Emit4( vm->dataMask ); + EmitString( "0F 87" ); // ja +errorFunction + n = funcOffset[FUNC_DATA] - compiledOfs; + Emit4( n - 6 ); + EmitString( "8D 04 0F" ); // lea eax, dword ptr [edi + ecx] + EmitRexString( "01 DF" ); // add edi, ebx // + vm->dataBase + EmitString( "3D" ); // cmp eax, vm->dataMask + Emit4( vm->dataMask ); + EmitString( "0F 87" ); // ja +errorFunction + n = funcOffset[FUNC_DATA] - compiledOfs; + Emit4( n - 6 ); + } + else + { + EmitRexString( "01 DF" ); // add edi, ebx // + vm->dataBase + } + +Lcopy = compiledOfs - Lcopy; + EmitString( "8A 02" ); // mov al, dword ptr [edx] + EmitString( "88 07" ); // mov dword ptr [edi], al + EmitRexString( "83 C2 01" );// add edx, 1 + EmitRexString( "83 C7 01" );// add edi, 1 + EmitRexString( "84 C0" ); // test al, al + EmitString( "74" ); // je +Lpadz + Emit1( Lpadz ); Lpadz = compiledOfs; + EmitString( "83 E9 01" ); // sub ecx, 1 + EmitString( "75" ); // jne +Lcopy + Emit1( Lcopy ); Lcopy = compiledOfs; + EmitString( "5F" ); // pop edi + EmitString( "C3" ); // ret +Lpadz = compiledOfs - Lpadz; + EmitString( "85 C9" ); // test ecx, ecx + EmitString( "74" ); // je +Lpop0 + Emit1( Lpop0 ); Lpop0 = compiledOfs; +#if 0 + // zero only one char + EmitString( "31 C0" ); // xor eax, eax + EmitString( "88 07" ); // mov dword ptr [edi], al +#else + // zero all remaining chars + EmitString( "83 E9 01" ); // sub ecx, 1 + EmitString( "74" ); // je +Lpop1 + Emit1( Lpop1 ); Lpop1 = compiledOfs; + EmitString( "89 CA" ); // mov edx, ecx + EmitString( "C1 E9 02" ); // shr ecx, 2 + EmitString( "31 C0" ); // xor eax, eax + EmitString( "83 E2 03" ); // and edx, 3 + EmitString( "F3 AB" ); // rep stosd + EmitString( "89 D1" ); // mov ecx, edx + //EmitString( "83 E1 03" ); // and ecx, 3 + EmitString( "F3 AA" ); // rep stosb +Lpop1 = compiledOfs - Lpop1; +#endif +Lpop0 = compiledOfs - Lpop0; + EmitString( "5F" ); // pop edi +Lend = compiledOfs - Lend; + EmitString( "C3" ); // ret +} + + +/* +================= +EmitFCalcEDI +================= +*/ +static void EmitFCalcEDI( int op ) +{ + switch ( op ) + { + case OP_ADDF: EmitString( "D8 07" ); break; // fadd dword ptr [edi] + case OP_SUBF: EmitString( "D8 27" ); break; // fsub dword ptr [edi] + case OP_MULF: EmitString( "D8 0F" ); break; // fmul dword ptr [edi] + case OP_DIVF: EmitString( "D8 37" ); break; // fdiv dword ptr [edi] + default: SV_Error( "bad float op" ); break; + }; +} + + +/* +================= +EmitFCalcPop +================= +*/ +static void EmitFCalcPop( int op ) +{ + switch ( op ) + { + case OP_ADDF: EmitString( "DE C1" ); break; // faddp + case OP_SUBF: EmitString( "DE E9" ); break; // fsubp + case OP_MULF: EmitString( "DE C9" ); break; // fmulp + case OP_DIVF: EmitString( "DE F9" ); break; // fdivp + default: SV_Error( "bad opcode %02x", op ); break; + }; +} + + +/* +================= +CommuteFloatOp +================= +*/ +static int CommuteFloatOp( int op ) +{ + switch ( op ) { + case OP_LEF: return OP_GEF; + case OP_LTF: return OP_GTF; + case OP_GEF: return OP_LEF; + case OP_GTF: return OP_LTF; + default: return op; + } +} + + +/* +================= +ConstOptimize +================= +*/ +static qbool ConstOptimize( vm_t *vm ) +{ + int v; + int op1; + qbool sign_extend; + + op1 = ni->op; + + switch ( op1 ) { + + case OP_LOAD4: + EmitAddEDI4( vm ); + if ( ISS8( ci->value ) ) { + EmitString( "8B 43" ); // mov eax, dword ptr [ebx+0x7F] + Emit1( ci->value ); + } else { + EmitString( "8B 83" ); // mov eax, dword ptr [ebx+0x12345678] + Emit4( ci->value ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + ip += 1; + return true; + + case OP_LOAD2: + EmitAddEDI4( vm ); + sign_extend = ( (ci+2)->op == OP_SEX16 ); + if ( ISS8( ci->value ) ) { + if ( sign_extend ) { + EmitString( "0F BF 43" ); // movsx eax, word ptr [ebx+0x7F] + ip += 1; + } else { + EmitString( "0F B7 43" ); // movzx eax, word ptr [ebx+0x7F] + } + Emit1( ci->value ); + } else { + if ( sign_extend ) { + EmitString( "0F BF 83" ); // movsx eax, word ptr [ebx+0x12345678] + ip += 1; + } else { + EmitString( "0F B7 83" ); // movzx eax, word ptr [ebx+0x12345678] + } + Emit4( ci->value ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + ip += 1; + return true; + + case OP_LOAD1: + EmitAddEDI4( vm ); + sign_extend = ( (ci+2)->op == OP_SEX8 ); + if ( ISS8( ci->value ) ) { + if ( sign_extend ) { + EmitString( "0F BE 43" ); // movsx eax, byte ptr [ebx+0x7F] + ip += 1; + } else { + EmitString( "0F B6 43" ); // movzx eax, byte ptr [ebx+0x7F] + } + Emit1( ci->value ); + } else { + if ( sign_extend ) { + EmitString( "0F BE 83" ); // movsx eax, word ptr [ebx+0x12345678] + ip += 1; + } else { + EmitString( "0F B6 83" ); // movzx eax, word ptr [ebx+0x12345678] + } + Emit4( ci->value ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + ip += 1; + return true; + + case OP_STORE4: + EmitMovEAXEDI( vm ); + if ( !ci->value ) { + EmitString( "31 C9" ); // xor ecx, ecx + } else { + EmitString( "B9" ); // mov ecx, 0x12345678 + Emit4( ci->value ); + } + EmitCheckReg( vm, REG_EAX, 4 ); + EmitString( "89 0C 03" ); // mov dword ptr [ebx + eax], ecx + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + ip += 1; + return true; + + case OP_STORE2: + EmitMovEAXEDI( vm ); + if ( !ci->value ) { + EmitString( "31 C9" ); // xor ecx, ecx + } else { + EmitString( "B9" ); // mov ecx, 0x12345678 + Emit4( ci->value ); + } + EmitCheckReg( vm, REG_EAX, 2 ); + EmitString( "66 89 0C 03" ); // mov word ptr [ebx + eax], cx + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + ip += 1; + return true; + + case OP_STORE1: + EmitMovEAXEDI( vm ); + if ( !ci->value ) { + EmitString( "31 C9" ); // xor ecx, ecx + } else { + EmitString( "B9" ); // mov ecx, 0x12345678 + Emit4( ci->value ); + } + EmitCheckReg( vm, REG_EAX, 1 ); + EmitString( "88 0C 03" ); // mov byte ptr [ebx + eax], cl + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + ip += 1; + return true; + + case OP_ADD: + v = ci->value; + EmitMovEAXEDI( vm ); + if ( ISS8( v ) ) { + EmitString( "83 C0" ); // add eax, 0x7F + Emit1( v ); + } else { + EmitString( "05" ); // add eax, 0x12345678 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; // OP_ADD + return true; + + case OP_SUB: + v = ci->value; + EmitMovEAXEDI( vm ); + if ( ISS8( v ) ) { + EmitString( "83 E8" ); // sub eax, 0x7F + Emit1( v ); + } else { + EmitString( "2D" ); // sub eax, 0x12345678 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_MULI: + v = ci->value; + EmitMovEAXEDI( vm ); + if ( ISS8( v ) ) { + EmitString( "6B C0" ); // imul eax, 0x7F + Emit1( v ); + } else { + EmitString( "69 C0" ); // imul eax, 0x12345678 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_MULF: + case OP_DIVF: + case OP_ADDF: + case OP_SUBF: + v = ci->value; + EmitLoadFloatEDI( vm ); + if ( HasSSEFP() ) { + EmitString( "C7 45 00" ); // mov dword ptr [ebp], v + Emit4( v ); + EmitString( "F3 0F 10 4D 00" ); // movss xmm1, dword ptr [ebp] + switch( op1 ) { + case OP_ADDF: EmitString( "0F 58 C1" ); break; // addps xmm0, xmm1 + case OP_SUBF: EmitString( "0F 5C C1" ); break; // subps xmm0, xmm1 + case OP_MULF: EmitString( "0F 59 C1" ); break; // mulps xmm0, xmm1 + case OP_DIVF: EmitString( "0F 5E C1" ); break; // divps xmm0, xmm1 + } + } else { + EmitString( "C7 45 00" ); // mov dword ptr [ebp], 0x12345678 + Emit4( v ); + EmitString( "D9 45 00" ); // fld dword ptr [ebp] + EmitFCalcPop( op1 ); // fmulp/fdivp/faddp/fsubp + } + EmitCommand( LAST_COMMAND_STORE_FLOAT_EDI ); + ip +=1; + return true; + + case OP_LSH: + v = ci->value; + if ( v < 0 || v > 31 ) + break; + EmitMovEAXEDI( vm ); + EmitString( "C1 E0" ); // shl eax, 0x12 + Emit1( v ); + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; // OP_LSH + return true; + + case OP_RSHI: + v = ci->value; + if ( v < 0 || v > 31 ) + break; + EmitMovEAXEDI( vm ); + EmitString( "C1 F8" ); // sar eax, 0x12 + Emit1( v ); + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_RSHU: + v = ci->value; + if ( v < 0 || v > 31 ) + break; + EmitMovEAXEDI( vm ); + EmitString( "C1 E8" ); // shr eax, 0x12 + Emit1( v ); + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_BAND: + v = ci->value; + EmitMovEAXEDI( vm ); + if ( ISU8( v ) ) { + EmitString( "83 E0" ); // and eax, 0x7F + Emit1( v ); + } else { + EmitString( "25" ); // and eax, 0x12345678 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_BOR: + v = ci->value; + EmitMovEAXEDI( vm ); + if ( ISU8( v ) ) { + EmitString( "83 C8" ); // or eax, 0x7F + Emit1( v ); + } else { + EmitString( "0D" ); // or eax, 0x12345678 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_BXOR: + v = ci->value; + EmitMovEAXEDI( vm ); + if ( ISU8( v ) ) { + EmitString( "83 F0" ); // xor eax, 0x7F + Emit1( v ); + } else { + EmitString( "35" ); // xor eax, 0x12345678 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); + ip += 1; + return true; + + case OP_JUMP: + EmitJump( vm, ni, ni->op, ci->value ); + ip += 1; // OP_JUMP + return true; + + case OP_CALL: +#ifdef VM_LOG_SYSCALLS + // [dataBase + programStack + 0] = ip; + EmitString( "C7 45 00" ); // mov dword ptr [ebp], 0x12345678 + Emit4( ip ); +#endif + v = ci->value; + // try to inline some syscalls + if ( HasSSEFP() && v == ~g_sqrt ) { + // inline SSE implementation of sin/cos is too problematic... + EmitString( "F3 0F 10 45 08" ); // movss xmm0, dword ptr [ebp + 8] + EmitAddEDI4( vm ); + EmitString( "F3 0F 51 C0" ); // sqrtss xmm0, xmm0 + EmitCommand( LAST_COMMAND_STORE_FLOAT_EDI ); + ip += 1; + return true; + } else if ( v == ~g_sin || v == ~g_cos || v == ~g_sqrt ) { + EmitString( "D9 45 08" ); // fld dword ptr [ebp + 8] + switch ( v ) { + case ~g_sqrt: EmitString( "D9 FA" ); break; // fsqrt + case ~g_sin: EmitString( "D9 FE" ); break; // fsin + case ~g_cos: EmitString( "D9 FF" ); break; // fcos + } + EmitAddEDI4( vm ); // add edi, 4 + EmitCommand( LAST_COMMAND_STORE_FLOAT_EDI_X87 );// fstp dword ptr[edi] + ip += 1; + return true; + } + + if ( v < 0 ) // syscall + { + EmitString( "B8" ); // mov eax, 0x12345678 + Emit4( ~v ); + EmitCallOffset( FUNC_SYSC ); + LastCommand = LAST_COMMAND_MOV_EAX_EDI_CALL; + ip += 1; // OP_CALL + return true; + } + EmitString( "55" ); // push ebp + EmitString( "56" ); // push rsi + EmitString( "53" ); // push rbx + EmitCallAddr( vm, v ); // call +addr + EmitString( "5B" ); // pop rbx + EmitString( "5E" ); // pop rsi + EmitString( "5D" ); // pop ebp + ip += 1; // OP_CALL + return true; + + case OP_EQF: + case OP_NEF: + case OP_LTF: + case OP_LEF: + case OP_GTF: + case OP_GEF: + if ( !HasFCOM() ) + return false; + EmitLoadFloatEDI( vm ); + EmitCommand( LAST_COMMAND_SUB_DI_4 ); + v = ci->value; + if ( HasSSEFP() ) { + if ( v == 0 ) { + EmitString( "0F 57 C9" ); // xorps xmm1, xmm1 + } else { + EmitString( "C7 45 00" ); // mov dword ptr [ebp], v + Emit4( v ); + EmitString( "F3 0F 10 4D 00" ); // movss xmm1, dword ptr [ebp] + } + EmitString( "0F 2F C1" ); // comiss xmm0, xmm1 + EmitJump( vm, ni, ni->op, ni->value ); + } else { + if ( v == 0 ) { + EmitString( "D9 EE" ); // fldz + } else { + EmitString( "C7 45 00" ); // mov [ebp], 0x12345678 + Emit4( v ); + EmitString( "D9 45 00" ); // fld dword ptr [ebp] + } + EmitString( "DF E9" ); // fucomip + EmitString( "DD D8" ); // fstp st(0) + EmitJump( vm, ni, CommuteFloatOp( ni->op ), ni->value ); + } + ip +=1; + return true; + + case OP_EQ: + case OP_NE: + case OP_GEI: + case OP_GTI: + case OP_GTU: + case OP_GEU: + case OP_LTU: + case OP_LEU: + case OP_LEI: + case OP_LTI: + EmitMovEAXEDI( vm ); + EmitCommand( LAST_COMMAND_SUB_DI_4 ); + v = ci->value; + if ( v == 0 && ( op1 == OP_EQ || op1 == OP_NE ) ) { + EmitString( "85 C0" ); // test eax, eax + } else { + if ( ISS8( v ) ) { + EmitString( "83 F8" ); // cmp eax, 0x7F + Emit1( v ); + } else { + EmitString( "3D" ); // cmp eax, 0xFFFFFFFF + Emit4( v ); + } + } + EmitJump( vm, ni, ni->op, ni->value ); + ip += 1; + return true; + + default: + break; + } + + return false; +} + +/* +================= +VM_FindMOps + +Search for known macro-op sequences +================= +*/ +static void VM_FindMOps( instruction_t *buf, int instructionCount ) +{ + int n, v, op0; + instruction_t *i; + + i = buf; + n = 0; + + while ( n < instructionCount ) + { + op0 = i->op; + if ( op0 == OP_LOCAL ) { + // OP_LOCAL + OP_LOCAL + OP_LOAD4 + OP_CONST + OP_XXX + OP_STORE4 + if ( (i+1)->op == OP_LOCAL && i->value == (i+1)->value && (i+2)->op == OP_LOAD4 && (i+3)->op == OP_CONST && (i+4)->op != OP_UNDEF && (i+5)->op == OP_STORE4 ) { + v = (i+4)->op; + if ( v == OP_ADD ) { + i->op = MOP_ADD4; + i += 6; n += 6; + continue; + } + if ( v == OP_SUB ) { + i->op = MOP_SUB4; + i += 6; n += 6; + continue; + } + if ( v == OP_BAND ) { + i->op = MOP_BAND4; + i += 6; n += 6; + continue; + } + if ( v == OP_BOR ) { + i->op = MOP_BOR4; + i += 6; n += 6; + continue; + } + } + + // skip useless sequences + if ( (i+1)->op == OP_LOCAL && (i+0)->value == (i+1)->value && (i+2)->op == OP_LOAD4 && (i+3)->op == OP_STORE4 ) { + i->op = MOP_IGNORE4; + i += 4; n += 4; + continue; + } + } else if ( op0 == OP_CONST && (i+1)->op == OP_CALL && (i+2)->op == OP_POP && i >= buf+6 && (i-1)->op == OP_ARG && !i->jused ) { + // some void function( arg1, arg2, arg3 ) + if ( i->value == ~g_strlcpy ) { + i->op = MOP_NCPY; + i += 3; n += 3; + continue; + } + } + + i++; + n++; + } +} + + +/* +================= +EmitMOPs +================= +*/ +static qbool EmitMOPs( vm_t *vm, int op ) +{ + int v, n; + switch ( op ) + { + //[local] += CONST + case MOP_ADD4: + n = inst[ip+2].value; + v = ci->value; // local variable address + if ( ISS8( n ) ) { + if ( ISS8( v ) ) { + EmitString( "83 45" ); // add dword ptr [ebp + 0x7F], 0x12 + Emit1( v ); + Emit1( n ); + } else { + EmitString( "83 85" ); // add dword ptr [ebp + 0x12345678], 0x12 + Emit4( v ); + Emit1( n ); + } + } else { + if ( ISS8( v ) ) { + EmitString( "81 45" ); // add dword ptr [ebp + 0x7F], 0x12345678 + Emit1( v ); + Emit4( n ); + } else { + EmitString( "81 85" ); // add dword ptr [ebp + 0x12345678], 0x12345678 + Emit4( v ); + Emit4( n ); + } + } + ip += 5; + return true; + + //[local] -= CONST + case MOP_SUB4: + n = inst[ip+2].value; + v = ci->value; // local variable address + if ( ISS8( n ) ) { + if ( ISS8( v ) ) { + EmitString( "83 6D" ); // sub dword ptr [ebp + 0x7F], 0x12 + Emit1( v ); + Emit1( n ); + } else { + EmitString( "83 AD" ); // sub dword ptr [ebp + 0x12345678], 0x12 + Emit4( v ); + Emit1( n ); + } + } else { + if ( ISS8( v ) ) { + EmitString( "81 6D" ); // sub dword ptr [ebp + 0x7F], 0x12345678 + Emit1( v ); + Emit4( n ); + } else { + EmitString( "81 AD" ); // sub dword ptr[esi+0x12345678], 0x12345678 + Emit4( v ); + Emit4( n ); + } + } + ip += 5; + return true; + + //[local] &= CONST + case MOP_BAND4: + n = inst[ip+2].value; + v = ci->value; // local variable address + if ( ISS8( n ) ) { + if ( ISS8( v ) ) { + EmitString( "83 65" ); // and dword ptr [ebp + 0x7F], 0x12 + Emit1( v ); + Emit1( n ); + } else { + EmitString( "83 A5" ); // and dword ptr [ebp + 0x12345678], 0x12 + Emit4( v ); + Emit1( n ); + } + } else { + if ( ISS8( v ) ) { + EmitString( "81 65" ); // and dword ptr [ebp + 0x7F], 0x12345678 + Emit1( v ); + Emit4( n ); + } else { + EmitString( "81 A5" ); // and dword ptr [ebp + 0x12345678], 0x12345678 + Emit4( v ); + Emit4( n ); + } + } + ip += 5; + return true; + + //[local] |= CONST + case MOP_BOR4: + n = inst[ip+2].value; + v = ci->value; // local variable address + if ( ISS8( n ) ) { + if ( ISS8( v ) ) { + EmitString( "83 4D" ); // or dword ptr [ebp + 0x7F], 0x12 + Emit1( v ); + Emit1( n ); + } else { + EmitString( "83 8D" ); // or dword ptr [ebp + 0x12345678], 0x12 + Emit4( v ); + Emit1( n ); + } + } else { + if ( ISS8( v ) ) { + EmitString( "81 4D" ); // or dword ptr [ebp + 0x7F], 0x12345678 + Emit1( v ); + Emit4( n ); + } else { + EmitString( "81 8D" ); // or dword ptr [ebp + 0x12345678], 0x12345678 + Emit4( v ); + Emit4( n ); + } + } + ip += 5; + return true; + + // [local] = [local] + case MOP_IGNORE4: + ip += 3; + return true; + + // const + call + pop + case MOP_NCPY: + EmitCallOffset( FUNC_NCPY ); + ip += 2; + return true; + + }; + return false; +} + +/* +================= +VM_Compile +================= +*/ +qbool VM_Compile( vm_t *vm, vmHeader_t *header ) { + const char *errMsg; + int instructionCount; + int proc_base; + int proc_len; + int i, n, v; + qbool wantres; + + Sys_GetProcessorId(NULL); + inst = (instruction_t*)Q_malloc( (header->instructionCount + 8 ) * sizeof( instruction_t ) ); + instructionOffsets = (int*)Q_malloc( header->instructionCount * sizeof( int ) ); + + errMsg = VM_LoadInstructions( (byte *) header + header->codeOffset, header->codeLength, header->instructionCount, inst ); + if ( !errMsg ) { + errMsg = VM_CheckInstructions( inst, vm->instructionCount, vm->jumpTableTargets, vm->numJumpTableTargets, vm->exactDataLength ); + } + if ( errMsg ) { + VM_FreeBuffers(); + Con_Printf( "VM_CompileX86 error: %s\n", errMsg ); + return false; + } + + //VM_ReplaceInstructions( vm, inst ); + + VM_FindMOps( inst, vm->instructionCount ); + + code = NULL; // we will allocate memory later, after last defined pass + instructionPointers = NULL; + + memset( funcOffset, 0, sizeof( funcOffset ) ); + + instructionCount = header->instructionCount; + + for( pass = 0; pass < NUM_PASSES; pass++ ) + { +__compile: + pop1 = OP_UNDEF; + lastConst = 0; + + // translate all instructions + ip = 0; + compiledOfs = 0; + LastCommand = LAST_COMMAND_NONE; + + proc_base = -1; + proc_len = 0; + +#if idx64 + EmitString( "53" ); // push rbx + EmitString( "56" ); // push rsi + EmitString( "57" ); // push rdi + EmitString( "55" ); // push rbp + EmitString( "41 54" ); // push r12 + EmitString( "41 55" ); // push r13 + EmitString( "41 56" ); // push r14 + EmitString( "41 57" ); // push r15 + + EmitRexString( "BB" ); // mov rbx, vm->dataBase + EmitPtr( vm->dataBase ); + + EmitString( "49 B8" ); // mov r8, vm->instructionPointers + EmitPtr( instructionPointers ); + + EmitString( "49 C7 C1" ); // mov r9, vm->dataMask + Emit4( vm->dataMask ); + + EmitString( "49 BC" ); // mov r12, vm->systemCall + EmitPtr( &vm->systemCall ); + + EmitString( "49 C7 C5" ); // mov r13, vm->stackBottom + Emit4( vm->stackBottom ); + + EmitRexString( "B8" ); // mov rax, &vm->programStack + EmitPtr( &vm->programStack ); + EmitString( "8B 30" ); // mov esi, [rax] + + EmitRexString( "B8" ); // mov rax, &vm->opStack + EmitPtr( &vm->opStack ); + EmitRexString( "8B 38" ); // mov rdi, [rax] + + EmitRexString( "B8" ); // mov rax, &vm->opStackTop + EmitPtr( &vm->opStackTop ); + EmitString( "4C 8B 30" ); // mov r14, [rax] + +#else + EmitString( "60" ); // pushad + + EmitRexString( "BB" ); // mov ebx, vm->dataBase + EmitPtr( vm->dataBase ); + + EmitString( "8B 35" ); // mov esi, [vm->programStack] + EmitPtr( &vm->programStack ); + + EmitString( "8B 3D" ); // mov edi, [vm->opStack] + EmitPtr( &vm->opStack ); +#endif + + EmitCallOffset( FUNC_ENTR ); + +#if idx64 + +#ifdef DEBUG_VM + EmitRexString( "B8" ); // mov rax, &vm->programStack + EmitPtr( &vm->programStack ); + EmitString( "89 30" ); // mov [rax], esi +#endif + + EmitRexString( "B8" ); // mov rax, &vm->opStack + EmitPtr( &vm->opStack ); + EmitRexString( "89 38" ); // mov [rax], rdi + + EmitString( "41 5F" ); // pop r15 + EmitString( "41 5E" ); // pop r14 + EmitString( "41 5D" ); // pop r13 + EmitString( "41 5C" ); // pop r12 + EmitString( "5D" ); // pop rbp + EmitString( "5F" ); // pop rdi + EmitString( "5E" ); // pop rsi + EmitString( "5B" ); // pop rbx +#else + +#ifdef DEBUG_VM + EmitString( "89 35" ); // [vm->programStack], esi + EmitPtr( &vm->programStack ); +#endif + + EmitString( "89 3D" ); // [vm->opStack], edi + EmitPtr( &vm->opStack ); + + EmitString( "61" ); // popad +#endif + + EmitString( "C3" ); // ret + + EmitAlign( 4 ); + + // main function entry offset + funcOffset[FUNC_ENTR] = compiledOfs; + + while ( ip < instructionCount ) + { + instructionOffsets[ ip ] = compiledOfs; + + ci = &inst[ ip ]; + ni = &inst[ ip + 1 ]; + ip++; + + if ( ci->jused ) { + LastCommand = LAST_COMMAND_NONE; + pop1 = OP_UNDEF; + } + + switch ( ci->op ) { + + case OP_UNDEF: + case OP_IGNORE: + break; + + case OP_BREAK: + EmitString( "CC" ); // int 3 + break; + + case OP_ENTER: + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "83 EE" ); // sub esi, 0x12 + Emit1( v ); + } else { + EmitString( "81 EE" ); // sub esi, 0x12345678 + Emit4( v ); + } + + // locate endproc + for ( n = -1, i = ip + 1; i < instructionCount; i++ ) { + if ( inst[ i ].op == OP_PUSH && inst[ i + 1 ].op == OP_LEAVE ) { + n = i; + break; + } + } + + // should never happen because equal check in VM_LoadInstructions() but anyway + if ( n == -1 ) { + VM_FreeBuffers(); + Con_Printf( "VM_CompileX86 error: %s\n", "missing proc end" ); + return false; + } + + proc_base = ip + 1; + proc_len = n - proc_base + 1 ; + + // programStack overflow check + if ( (int)vm_rtChecks.value & 1 ) { +#if idx64 + EmitString( "4C 39 EE" ); // cmp rsi, r13 +#else + EmitString( "81 FE" ); // cmp esi, vm->stackBottom + Emit4( vm->stackBottom ); +#endif + EmitString( "0F 82" ); // jb +funcOffset[FUNC_PSOF] + n = funcOffset[FUNC_PSOF] - compiledOfs; + Emit4( n - 6 ); + } + + // opStack overflow check + if ( (int)vm_rtChecks.value & 2 ) { + if ( ISU8( ci->opStack ) ) { + EmitRexString( "8D 47" ); // lea eax, [edi+0x7F] + Emit1( ci->opStack ); + } else { + EmitRexString( "8D 87" ); // lea eax, [edi+0x12345678] + Emit4( ci->opStack ); + } +#if idx64 + EmitString( "4C 39 F0" ); // cmp rax, r14 +#else + EmitString( "3B 05" ); // cmp eax, [&vm->opStackTop] + EmitPtr( &vm->opStackTop ); +#endif + EmitString( "0F 87" ); // ja +funcOffset[FUNC_OSOF] + n = funcOffset[FUNC_OSOF] - compiledOfs; + Emit4( n - 6 ); + } + + EmitRexString( "8D 2C 33" ); // lea ebp, [ebx+esi] + break; + + case OP_CONST: + + // we can safely perform optimizations only in case if + // we are 100% sure that next instruction is not a jump label + if ( !ni->jused && ConstOptimize( vm ) ) + break; + + EmitAddEDI4( vm ); // add edi, 4 + EmitString( "C7 07" ); // mov dword ptr [edi], 0x12345678 + lastConst = ci->value; + Emit4( lastConst ); + LastCommand = LAST_COMMAND_MOV_EDI_CONST; + break; + + case OP_LOCAL: + // optimization: merge OP_LOCAL + OP_LOAD4 + if ( ni->op == OP_LOAD4 ) { + EmitAddEDI4( vm ); // add edi, 4 + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "8B 45" ); // mov eax, dword ptr [ebp + 0x7F] + Emit1( v ); + } else { + EmitString( "8B 85" ); // mov eax, dword ptr [ebp + 0x12345678] + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + ip++; + break; + } + + // optimization: merge OP_LOCAL + OP_LOAD2 + if ( ni->op == OP_LOAD2 ) { + EmitAddEDI4( vm ); // add edi, 4 + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "0F B7 45" ); // movzx eax, word ptr [ebp + 0x7F] + Emit1( v ); + } else { + EmitString( "0F B7 85" ); // movzx eax, word ptr [ebp + 0x12345678] + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + ip++; + break; + } + + // optimization: merge OP_LOCAL + OP_LOAD1 + if ( ni->op == OP_LOAD1 ) { + EmitAddEDI4( vm ); // add edi, 4 + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "0F B6 45" ); // movzx eax, byte ptr [ebp + 0x7F] + Emit1( v ); + } else { + EmitString( "0F B6 85" ); // movzx eax, byte ptr [ebp + 0x12345678] + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + ip++; + break; + } + + // TODO: i = j + k; + // TODO: i = j - k; + + EmitAddEDI4( vm ); // add edi, 4 + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "8D 46" ); // lea eax, [esi + 0x7F] + Emit1( v ); + } else { + EmitString( "8D 86" ); // lea eax, [esi + 0x12345678] + Emit4( v ); + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + + case OP_ARG: + if ( LastCommand == LAST_COMMAND_STORE_FLOAT_EDI_SSE ) { + REWIND(4); + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "F3 0F 11 45" ); // movss dword ptr [ebp + 0x7F], xmm0 + Emit1( v ); + } else { + EmitString( "F3 0F 11 85" ); // movss dword ptr [ebp + 0x12345678], xmm0 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + } + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + if ( (ci+1)->op == MOP_NCPY && !(ci+1)->jused ) { + // we will read counter from eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + } + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "89 45" ); // mov dword ptr [ebp + 0x7F], eax + Emit1( v ); + } else { + EmitString( "89 85" ); // mov dword ptr [ebp + 0x12345678], eax + Emit4( v ); + } + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_CALL: +#ifdef VM_LOG_SYSCALLS + // [dataBase + programStack + 0] = ip-1; + EmitString( "C7 45 00" ); // mov dword ptr [ebp], 0x12345678 + Emit4( ip-1 ); +#endif + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitCallOffset( FUNC_CALL ); // call +FUNC_CALL + break; + + case OP_PUSH: + EmitAddEDI4( vm ); // add edi, 4 + break; + + case OP_POP: + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_LEAVE: +#ifdef DEBUG_VM + v = ci->value; + if ( ISU8( v ) ) { + EmitString( "83 C6" ); // add esi, 0x12 + Emit1( v ); + } else { + EmitString( "81 C6" ); // add esi, 0x12345678 + Emit4( v ); + } +#endif + EmitString( "C3" ); // ret + break; + + case OP_LOAD4: + if ( LastCommand == LAST_COMMAND_MOV_EDI_EAX ) { + REWIND( 2 ); + EmitCheckReg( vm, REG_EAX, 4 ); // range check eax + EmitString( "8B 04 03" ); // mov eax, dword ptr [ebx + eax] + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + } + EmitMovECXEDI( vm ); // mov ecx, dword ptr [edi] + EmitCheckReg( vm, REG_ECX, 4 ); // range check ecx + EmitString( "8B 04 0B" ); // mov eax, dword ptr [ebx + ecx] + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + break; + + case OP_LOAD2: + if ( LastCommand == LAST_COMMAND_MOV_EDI_EAX ) { + REWIND( 2 ); + EmitCheckReg( vm, REG_EAX, 2 ); // range check eax + if ( ni->op == OP_SEX16 ) { + EmitString( "0F BF 04 03" ); // movsx eax, word ptr [ebx + eax] + ip++; + } else { + EmitString( "0F B7 04 03" ); // movzx eax, word ptr [ebx + eax] + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + } + EmitMovECXEDI( vm ); // mov ecx, dword ptr [edi] + EmitCheckReg( vm, REG_ECX, 2 ); // range check ecx + if ( ni->op == OP_SEX16 ) { + EmitString( "0F BF 04 0B" ); // movsx eax, word ptr [ebx + ecx] + ip++; + } else { + EmitString( "0F B7 04 0B" ); // movzx eax, word ptr [ebx + ecx] + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + break; + + case OP_LOAD1: + if ( LastCommand == LAST_COMMAND_MOV_EDI_EAX ) { + REWIND( 2 ); + EmitCheckReg( vm, REG_EAX, 1 ); // range check eax + if ( ni->op == OP_SEX8 ) { + EmitString( "0F BE 04 03" ); // movsx eax, byte ptr [ebx + eax] + ip++; + } else { + EmitString( "0F B6 04 03" ); // movzx eax, byte ptr [ebx + eax] + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + } + EmitMovECXEDI( vm ); // mov ecx, dword ptr [edi] + EmitCheckReg( vm, REG_ECX, 1 ); // range check ecx + if ( ni->op == OP_SEX8 ) { + EmitString( "0F BE 04 0B" ); // movsx eax, byte ptr [ebx + ecx] + ip++; + } else { + EmitString( "0F B6 04 0B" ); // movzx eax, byte ptr [ebx + ecx] + } + EmitCommand( LAST_COMMAND_MOV_EDI_EAX ); // mov dword ptr [edi], eax + break; + + case OP_STORE4: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "8B 4F FC" ); // mov ecx, dword ptr [edi-4] + EmitCheckReg( vm, REG_ECX, 4 ); // range check + EmitString( "89 04 0B" ); // mov dword ptr [ebx + ecx], eax + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + break; + + case OP_STORE2: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "8B 4F FC" ); // mov ecx, dword ptr [edi-4] + EmitCheckReg( vm, REG_ECX, 2 ); // range check + EmitString( "66 89 04 0B" ); // mov word ptr [ebx + ecx], ax + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + break; + + case OP_STORE1: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "8B 4F FC" ); // mov ecx, dword ptr [edi-4] + EmitCheckReg( vm, REG_ECX, 1 ); // range check + EmitString( "88 04 0B" ); // mov byte ptr [ebx + ecx], al + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + break; + + case OP_EQ: + case OP_NE: + case OP_LTI: + case OP_LEI: + case OP_GTI: + case OP_GEI: + case OP_LTU: + case OP_LEU: + case OP_GTU: + case OP_GEU: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + EmitString( "39 47 04" ); // cmp dword ptr [edi+4], eax + EmitJump( vm, ci, ci->op, ci->value ); + break; + + case OP_EQF: + case OP_NEF: + case OP_LTF: + case OP_LEF: + case OP_GTF: + case OP_GEF: + if ( HasFCOM() ) { + EmitLoadFloatEDI( vm ); + if ( HasSSEFP() ) { + EmitString( "F3 0F 10 4F FC" ); // movss xmm1, dword ptr [edi-4] + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + EmitString( "0F 2F C8" ); // comiss xmm1, xmm0 + } else { + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + //EmitString( "D9 47 08" ); // fld dword ptr [edi+8] + EmitString( "D9 47 04" ); // fld dword ptr [edi+4] + EmitString( "DF E9" ); // fucomip + EmitString( "DD D8" ); // fstp st(0) + } + EmitJump( vm, ci, ci->op, ci->value ); + } else { + EmitCommand( LAST_COMMAND_SUB_DI_8 ); // sub edi, 8 + EmitString( "D9 47 04" ); // fld dword ptr [edi+4] + EmitString( "D8 5F 08" ); // fcomp dword ptr [edi+8] + EmitString( "DF E0" ); // fnstsw ax + EmitFloatJump( vm, ci, ci->op, ci->value ); + } + pop1 = OP_UNDEF; + break; + + case OP_NEGI: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "F7 D8" ); // neg eax + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + + case OP_ADD: + wantres = ( ops[ ni->op ].stack <= 0 ); + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + if ( wantres ) { + EmitString( "03 47 FC" ); // add eax, dword ptr [edi-4] + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + } else { + EmitString( "01 47 FC" ); // add dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + break; + + case OP_SUB: + wantres = ( ops[ ni->op ].stack <= 0 ); + if ( wantres ) { + EmitMovECXEDI( vm ); // mov ecx,dword ptr [edi] + EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + EmitString( "29 C8" ); // sub eax, ecx + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + } else { + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "29 47 FC" ); // sub dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + break; + + case OP_DIVI: + wantres = ( ops[ ni->op ].stack <= 0 ); + EmitMovECXEDI( vm ); // mov ecx,dword ptr [edi] + EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] + if ( wantres ) { + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + EmitString( "99" ); // cdq + EmitString( "F7 F9" ); // idiv ecx + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + } else{ + EmitString( "99" ); // cdq + EmitString( "F7 F9" ); // idiv ecx + EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + break; + + case OP_DIVU: + EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] + EmitString( "33 D2" ); // xor edx, edx + EmitString( "F7 37" ); // div dword ptr [edi] + EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_MODI: + EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] + EmitString( "99" ); // cdq + EmitString( "F7 3F" ); // idiv dword ptr [edi] + EmitString( "89 57 FC" ); // mov dword ptr [edi-4],edx + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_MODU: + EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] + EmitString( "33 D2" ); // xor edx, edx + EmitString( "F7 37" ); // div dword ptr [edi] + EmitString( "89 57 FC" ); // mov dword ptr [edi-4],edx + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_MULI: + wantres = ( ops[ ni->op ].stack <= 0 ); + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "F7 6F FC" ); // imul eax, dword ptr [edi-4] + if ( wantres ) { + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + } else { + EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + break; + + case OP_MULU: + EmitString( "8B 47 FC" ); // mov eax,dword ptr [edi-4] + EmitString( "F7 27" ); // mul dword ptr [edi] + EmitString( "89 47 FC" ); // mov dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_BAND: + wantres = ( ops[ ni->op ].stack <= 0 ); + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + if ( wantres ) { + EmitString( "23 47 FC" ); // and eax, dword ptr [edi-4] + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + } else { + EmitString( "21 47 FC" ); // and dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + break; + + case OP_BOR: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "09 47 FC" ); // or dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_BXOR: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "31 47 FC" ); // xor dword ptr [edi-4],eax + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_BCOM: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitString( "F7 D0" ); // not eax + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + + case OP_LSH: + EmitMovECXEDI( vm ); // mov ecx, dword ptr [edi] + EmitString( "D3 67 FC" ); // shl dword ptr [edi-4], cl + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_RSHI: + EmitMovECXEDI( vm ); // mov ecx, dword ptr [edi] + EmitString( "D3 7F FC" ); // sar dword ptr [edi-4], cl + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_RSHU: + EmitMovECXEDI( vm ); // mov ecx, dword ptr [edi] + EmitString( "D3 6F FC" ); // shr dword ptr [edi-4], cl + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + break; + + case OP_NEGF: + //if ( !ci->fpu ) + EmitLoadFloatEDI( vm ); // fld dword ptr [edi] | movss xmm0, dword ptr [edi] + if ( HasSSEFP() ) { + EmitString( "0F 57 C9" ); // xorps xmm1, xmm1 + EmitString( "0F 5C C8" ); // subps xmm1, xmm0 + EmitString( "0F 28 C1" ); // movaps xmm0, xmm1 + } else { + EmitString( "D9 E0" ); // fchs + } + //if ( !ci->fpu || ci->store ) + EmitCommand( LAST_COMMAND_STORE_FLOAT_EDI ); // fstp dword ptr [edi] + break; + + case OP_ADDF: + case OP_SUBF: + case OP_DIVF: + case OP_MULF: + wantres = ( ops[ ni->op ].stack <= 0 ); + if ( HasSSEFP() ) { + if ( LastCommand == LAST_COMMAND_STORE_FLOAT_EDI_SSE ) { + REWIND(4); + EmitString( "0F 28 C8" ); // movaps xmm1, xmm0 + } else { + EmitString( "F3 0F 10 0F" ); // movss xmm1, dword ptr [edi] + } + EmitString( "F3 0F 10 47 FC" ); // movss xmm0, dword ptr [edi-4] + if ( wantres ) { + if ( ni->op == OP_STORE4 ) { + EmitString( "8B 47 F8" ); // mov eax, dword ptr [edi-8] + EmitCheckReg( vm, REG_EAX, 4 ); + } else if ( ni->op == OP_ARG ) { + // + } else { + EmitCommand( LAST_COMMAND_SUB_DI_4 ); + } + } + switch( ci->op ) { + case OP_ADDF: EmitString( "0F 58 C1" ); break; // addps xmm0, xmm1 + case OP_SUBF: EmitString( "0F 5C C1" ); break; // subps xmm0, xmm1 + case OP_MULF: EmitString( "0F 59 C1" ); break; // mulps xmm0, xmm1 + case OP_DIVF: EmitString( "0F 5E C1" ); break; // divps xmm0, xmm1 + } + if ( wantres ) { + if ( ni->op == OP_STORE4 ) { + EmitString( "F3 0F 11 04 03" ); // movss dword ptr [ebx + eax], xmm0 + EmitCommand( LAST_COMMAND_SUB_DI_12 ); + pop1 = OP_UNDEF; + ip++; // OP_STORE4 + } else if ( ni->op == OP_ARG ) { + v = ni->value; + if ( ISU8( v ) ) { + EmitString( "F3 0F 11 45" ); // movss dword ptr [ebp + 0x7F], xmm0 + Emit1( v ); + } else { + EmitString( "F3 0F 11 85" ); // movss dword ptr [ebp + 0x12345678], xmm0 + Emit4( v ); + } + EmitCommand( LAST_COMMAND_SUB_DI_8 ); + pop1 = OP_UNDEF; + ip++; // OP_ARG + } else { + EmitCommand( LAST_COMMAND_STORE_FLOAT_EDI ); + } + } else { + EmitString( "F3 0F 11 47 FC" ); // movss dword ptr [edi-4], xmm0 + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + } else { + EmitString( "D9 47 FC" ); // fld dword ptr [edi-4] + EmitFCalcEDI( ci->op ); // fadd|fsub|fmul|fdiv dword ptr [edi] + EmitString( "D9 5F FC" ); // fstp dword ptr [edi-4] + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + } + break; + + case OP_CVIF: + if ( HasSSEFP() ) { + if ( LastCommand == LAST_COMMAND_MOV_EDI_EAX ) { + REWIND(2); + EmitString( "F3 0F 2A C0" ); // cvtsi2ss xmm0, eax + } else { + EmitString( "F3 0F 2A 07" ); // cvtsi2ss xmm0, dword ptr [edi] + } + } else { + EmitString( "DB 07" ); // fild dword ptr [edi] + } + //if ( !ci->fpu ) + EmitCommand( LAST_COMMAND_STORE_FLOAT_EDI ); + break; + + case OP_CVFI: + if ( HasSSEFP() ) { + // assume that rounding mode in MXCSR is correctly set in 64-bit environment + EmitLoadFloatEDI_SSE( vm ); // movss xmm0, dword ptr [edi] + EmitString( "F3 0F 2C C0" ); // cvttss2si eax, xmm0 + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + } else { + EmitLoadFloatEDI_X87( vm ); // fld dword ptr [edi] + // call the library conversion function + EmitCallOffset( FUNC_FTOL ); // call +FUNC_FTOL + } + break; + + case OP_SEX8: + EmitString( "0F BE 07" ); // movsx eax, byte ptr [edi] + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + + case OP_SEX16: + EmitString( "0F BF 07" ); // movsx eax, word ptr [edi] + EmitCommand( LAST_COMMAND_MOV_EDI_EAX );// mov dword ptr [edi], eax + break; + + case OP_BLOCK_COPY: + EmitString( "B9" ); // mov ecx, 0x12345678 + Emit4( ci->value >> 2 ); + EmitCallOffset( FUNC_BCPY ); + break; + + case OP_JUMP: + EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi] + EmitCommand( LAST_COMMAND_SUB_DI_4 ); // sub edi, 4 + + // jump target range check + if ( (int)vm_rtChecks.value & 4 ) { + if ( proc_base != -1 ) { + // allow jump within local function scope only + EmitString( "89 C2" ); // mov edx, eax + if ( ISU8( proc_base ) ) { + EmitString( "83 EA" ); // sub edx, 0x7F + Emit1( proc_base ); + } else { + EmitString( "81 EA" ); // sub edx, 0x12345678 + Emit4( proc_base ); + } + if ( ISU8( proc_len ) ) { + EmitString( "83 FA" ); // cmp edx, 0x7F + Emit1( proc_len ); + } else { + EmitString( "81 FA" ); // cmp edx, 0x12345678 + Emit4( proc_len ); + } + } else { + EmitString( "3D" ); // cmp eax, 0x12345678 + Emit4( vm->instructionCount ); + } + EmitString( "0F 83" ); // jae +funcOffset[FUNC_BADJ] + n = funcOffset[FUNC_BADJ] - compiledOfs; + Emit4( n - 6 ); + } +#if idx64 + EmitString( "41 FF 24 C0" ); // jmp dword ptr [r8 + rax*8] +#else + EmitString( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4] + EmitPtr( instructionPointers ); +#endif + break; + + case MOP_IGNORE4: + case MOP_ADD4: + case MOP_SUB4: + case MOP_BAND4: + case MOP_BOR4: + case MOP_NCPY: + if ( !EmitMOPs( vm, ci->op ) ) + SV_Error( "VM_CompileX86: bad opcode %02X", ci->op ); + break; + + default: + SV_Error( "VM_CompileX86: bad opcode %02X", ci->op ); + VM_FreeBuffers(); + return false; + } + + pop1 = (opcode_t)ci->op; + } // while( ip < header->instructionCount ) + + // **************** + // system functions + // **************** + EmitAlign( 8 ); + funcOffset[FUNC_CALL] = compiledOfs; + EmitCallFunc( vm ); + + EmitAlign( 8 ); + funcOffset[FUNC_FTOL] = compiledOfs; + EmitFTOLFunc( vm ); + + EmitAlign( 8 ); + funcOffset[FUNC_BCPY] = compiledOfs; + EmitBCPYFunc( vm ); + + EmitAlign( 8 ); + funcOffset[FUNC_NCPY] = compiledOfs; + EmitNCPYFunc( vm ); + + // *************** + // error functions + // *************** + + // bad jump + EmitAlign( 8 ); + funcOffset[FUNC_BADJ] = compiledOfs; + EmitBADJFunc( vm ); + + // error jump + EmitAlign( 8 ); + funcOffset[FUNC_ERRJ] = compiledOfs; + EmitERRJFunc( vm ); + + // programStack overflow + EmitAlign( 8 ); + funcOffset[FUNC_PSOF] = compiledOfs; + EmitPSOFFunc( vm ); + + // opStack overflow + EmitAlign( 8 ); + funcOffset[FUNC_OSOF] = compiledOfs; + EmitOSOFFunc( vm ); + + // read/write access violation + EmitAlign( 8 ); + funcOffset[FUNC_DATA] = compiledOfs; + EmitDATAFunc( vm ); + + EmitAlign( sizeof( intptr_t ) ); // for instructionPointers + + } // for( pass = 0; pass < n; pass++ ) + + n = header->instructionCount * sizeof( intptr_t ); + + if ( code == NULL ) { + code = (byte*)VM_Alloc_Compiled( vm, PAD(compiledOfs,8), n ); + if ( code == NULL ) { + return false; + } + instructionPointers = (intptr_t*)(byte*)(code + PAD(compiledOfs,8)); + //vm->instructionPointers = instructionPointers; // for debug purposes? + pass = NUM_PASSES-1; // repeat last pass + goto __compile; + } + + // offset all the instruction pointers for the new location + for ( i = 0 ; i < header->instructionCount ; i++ ) { + if ( !inst[i].jused ) { + instructionPointers[ i ] = (intptr_t)badJumpPtr; + continue; + } + instructionPointers[ i ] = (intptr_t)vm->codeBase.ptr + instructionOffsets[ i ]; + } + + VM_FreeBuffers(); + +#ifdef VM_X86_MMAP + if ( mprotect( vm->codeBase.ptr, vm->codeSize, PROT_READ|PROT_EXEC ) ) { + VM_Destroy_Compiled( vm ); + Con_Printf( "VM_CompileX86: mprotect failed\n" ); + return false; + } +#elif _WIN32 + { + DWORD oldProtect = 0; + + // remove write permissions. + if ( !VirtualProtect( vm->codeBase.ptr, vm->codeSize, PAGE_EXECUTE_READ, &oldProtect ) ) { + VM_Destroy_Compiled( vm ); + Con_Printf( "VM_CompileX86: VirtualProtect failed\n" ); + return false; + } + } +#endif + + vm->destroy = VM_Destroy_Compiled; + + Con_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs ); + + return true; +} +/* +================= +VM_Alloc_Compiled +================= +*/ +static void *VM_Alloc_Compiled( vm_t *vm, int codeLength, int tableLength ) +{ + void *ptr; + int length; + + length = codeLength + tableLength; +#ifdef VM_X86_MMAP + ptr = mmap( NULL, length, PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0 ); + if ( ptr == MAP_FAILED ) { + SV_Error( "VM_CompileX86: mmap failed" ); + return NULL; + } +#elif _WIN32 + // allocate memory with EXECUTE permissions under windows. + ptr = VirtualAlloc( NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + if ( !ptr ) { + SV_Error( "VM_CompileX86: VirtualAlloc failed" ); + return NULL; + } +#else + ptr = malloc( length ); + if ( !ptr ) { + SV_Error( "VM_CompileX86: malloc failed" ); + return NULL; + } +#endif + vm->codeBase.ptr = (byte*)ptr; + vm->codeLength = codeLength; + vm->codeSize = length; + + return vm->codeBase.ptr; +} + + +/* +============== +VM_Destroy_Compiled +============== +*/ +static void VM_Destroy_Compiled( vm_t* vm ) +{ +#ifdef VM_X86_MMAP + munmap( vm->codeBase.ptr, vm->codeSize ); +#elif _WIN32 + VirtualFree( vm->codeBase.ptr, 0, MEM_RELEASE ); +#else + free( vm->codeBase.ptr ); +#endif + vm->codeBase.ptr = NULL; +} + +/* +============== +VM_CallCompiled + +This function is called directly by the generated code +============== +*/ +int VM_CallCompiled( vm_t *vm, int nargs, int *args ) +{ + int opStack[MAX_OPSTACK_SIZE]; + unsigned int stackOnEntry; + int *image; + int *oldOpTop; + int i; + + // we might be called recursively, so this might not be the very top + stackOnEntry = vm->programStack; + oldOpTop = vm->opStackTop; + + vm->programStack -= (MAX_VMMAIN_CALL_ARGS+2)*4; + + // set up the stack frame + image = (int*)( vm->dataBase + vm->programStack ); + for ( i = 0; i < nargs; i++ ) { + image[ i + 2 ] = args[ i ]; + } + + image[1] = 0; // return stack + image[0] = -1; // will terminate loop on return + + opStack[1] = 0; + + vm->opStack = opStack; + vm->opStackTop = opStack + ARRAY_LEN( opStack ) - 1; + + vm->codeBase.func(); // go into generated code + + if ( vm->opStack != &opStack[1] ) { + SV_Error( "opStack corrupted in compiled code" ); + } + +#ifdef DEBUG_VM + if ( vm->programStack != stackOnEntry - CALL_PSTACK ) { + SV_Error( "programStack corrupted in compiled code" ); + } +#endif + + vm->programStack = stackOnEntry; + vm->opStackTop = oldOpTop; + + return vm->opStack[0]; +} +#else +int VM_CallCompiled( vm_t *vm, int nargs, int *args ) { return 0;} +qbool VM_Compile( vm_t *vm, vmHeader_t *header ) { return false; } +#endif +#endif /* USE_PR2 */ + diff --git a/src/zone.c b/src/zone.c index 00fcbb229..1cf2c7f54 100644 --- a/src/zone.c +++ b/src/zone.c @@ -224,22 +224,13 @@ void Hunk_FreeToLowMark(int mark) hunk_low_used = mark; } -int Hunk_HighMark(void) +static int Hunk_HighMark(void) { - if (hunk_tempactive) { - hunk_tempactive = false; - Hunk_FreeToHighMark(hunk_tempmark); - } - return hunk_high_used; } -void Hunk_FreeToHighMark(int mark) +static 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); } @@ -252,7 +243,7 @@ void Hunk_FreeToHighMark(int mark) Hunk_HighAllocName =================== */ -void *Hunk_HighAllocName(int size, char *name) +static void *Hunk_HighAllocName(int size, char *name) { hunk_t *h; @@ -260,11 +251,6 @@ void *Hunk_HighAllocName(int size, char *name) Sys_Error("Hunk_HighAllocName: bad size: %i", size); } - if (hunk_tempactive) { - Hunk_FreeToHighMark(hunk_tempmark); - hunk_tempactive = false; - } - #ifdef PARANOID Hunk_Check(); #endif @@ -294,23 +280,33 @@ void *Hunk_HighAllocName(int size, char *name) /* ================= -Hunk_TempAlloc +Hunk_TempFlush -Return space from the top of the hunk +Free the temporary memory zone to baseline. ================= */ -void *Hunk_TempAlloc(int size) +void Hunk_TempFlush(void) { - void *buf; - - size = (size + 15) & ~15; - if (hunk_tempactive) { Hunk_FreeToHighMark(hunk_tempmark); hunk_tempactive = false; } hunk_tempmark = Hunk_HighMark(); +} + +/* +================= +Hunk_TempAlloc + +Return space from the top of the hunk +================= +*/ +void *Hunk_TempAlloc(int size) +{ + void *buf; + + Hunk_TempFlush(); buf = Hunk_HighAllocName(size, "temp"); @@ -319,6 +315,21 @@ void *Hunk_TempAlloc(int size) return buf; } +/* +================= +Hunk_TempAllocMore + +Return space after any previous temporary allocation without clearing first. +================= +*/ +void *Hunk_TempAllocMore(int size) +{ + if (!hunk_tempactive) + return Hunk_TempAlloc(size); + + return Hunk_HighAllocName(size, "temp+"); +} + #ifdef SERVERONLY /* =============================================================================== diff --git a/src/zone.h b/src/zone.h index 62f865672..056d9ca51 100644 --- a/src/zone.h +++ b/src/zone.h @@ -85,15 +85,12 @@ void Memory_Init (void *buf, int size); void *Hunk_Alloc (int size); // returns 0 filled memory void *Hunk_AllocName (int size, const char *name); -void *Hunk_HighAllocName (int size, char *name); - int Hunk_LowMark (void); void Hunk_FreeToLowMark (int mark); -int Hunk_HighMark (void); -void Hunk_FreeToHighMark (int mark); - +void Hunk_TempFlush(void); void *Hunk_TempAlloc (int size); +void *Hunk_TempAllocMore(int size); void Hunk_Check (void);