From fbcf8a89f57da1902021cfb8a43dd109e9ccfdb7 Mon Sep 17 00:00:00 2001 From: Ivan Bolsunov Date: Sun, 22 Oct 2023 05:21:18 +0400 Subject: [PATCH 1/3] SERVER: add support for API16 so we could run latest KTX Seems like there is some issues, minor testing was done under MacOS, had to compile without optimizations since it crashed with optimizations, unfortunatelly it doesn't crash without optimizations so was not able to debug it. Also need to revisit cmodel.c since during "merge" it lost recent sanity checks validations. --- Makefile | 6 +- ezQuake.vcxproj | 10 +- ezQuake.vcxproj.filters | 22 +- src/cmodel.c | 732 +++++----- src/cmodel.h | 2 + src/g_public.h | 53 +- src/pmove.c | 12 +- src/pr2.h | 8 +- src/pr2_cmds.c | 1704 +++++++++------------- src/pr2_edict.c | 4 +- src/pr2_exec.c | 327 +++-- src/pr2_vm.c | 1281 ----------------- src/pr2_vm.h | 258 ---- src/pr_cmds.c | 224 ++- src/pr_edict.c | 81 +- src/pr_exec.c | 18 +- src/progs.h | 30 +- src/q_platform.h | 466 ++++++ src/qwsvdef.h | 1 + src/server.h | 12 +- src/sv_ccmds.c | 37 +- src/sv_demo.c | 52 +- src/sv_demo_misc.c | 78 + src/sv_demo_qtv.c | 29 + src/sv_ents.c | 176 +-- src/sv_init.c | 53 +- src/sv_main.c | 36 +- src/sv_move.c | 74 +- src/sv_nchan.c | 2 +- src/sv_phys.c | 278 ++-- src/sv_save.c | 4 +- src/sv_send.c | 118 +- src/sv_user.c | 270 ++-- src/sv_world.c | 164 +-- src/sv_world.h | 6 +- src/sys.h | 9 +- src/vm.c | 1617 +++++++++++++++++++++ src/vm.h | 90 ++ src/vm_interpreted.c | 636 +++++++++ src/vm_local.h | 293 ++++ src/vm_x86.c | 2968 +++++++++++++++++++++++++++++++++++++++ src/zone.c | 59 +- src/zone.h | 7 +- 43 files changed, 8348 insertions(+), 3959 deletions(-) delete mode 100644 src/pr2_vm.c delete mode 100644 src/pr2_vm.h create mode 100644 src/q_platform.h create mode 100644 src/vm.c create mode 100644 src/vm.h create mode 100644 src/vm_interpreted.c create mode 100644 src/vm_local.h create mode 100644 src/vm_x86.c 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..ff11e355d 100644 --- a/ezQuake.vcxproj +++ b/ezQuake.vcxproj @@ -2133,9 +2133,6 @@ msversion.bat CppCode - - CppCode - CppCode @@ -2145,6 +2142,9 @@ msversion.bat CppCode + + + @@ -2950,6 +2950,7 @@ msversion.bat + @@ -3015,8 +3016,9 @@ msversion.bat + + - diff --git a/ezQuake.vcxproj.filters b/ezQuake.vcxproj.filters index 0f115246a..3e6ae9bc3 100644 --- a/ezQuake.vcxproj.filters +++ b/ezQuake.vcxproj.filters @@ -407,9 +407,6 @@ Source Files\Documentation - - Source Files\Server - Source Files\Server @@ -428,6 +425,15 @@ Source Files\Server + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + Source Files\UserInterface @@ -1231,6 +1237,9 @@ Source Files + + Source Files + Source Files @@ -1381,10 +1390,13 @@ Source Files\Server - + Source Files\Server - + + Source Files\Server + + Source Files\Server diff --git a/src/cmodel.c b/src/cmodel.c index edd667a7f..62c70db62 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,153 +1259,220 @@ 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]) { - if (strcmp(name, map_name)) - Con_Printf("CM_LoadMap: '%s' != '%s'\n", 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; +} + +static qbool CM_CalcChecksum(vfsfile_t *f, dheader_t *header, unsigned *checksum, unsigned *checksum2) +{ + byte *buf; + int i, read; - // Align the lumps - for (i = 0; i < HEADER_LUMPS; ++i) { - pad_lumps |= (header->lumps[i].fileofs % 4) != 0; + // 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 (header->lumps[i].fileofs < 0 || header->lumps[i].filelen < 0) { - Host_Error("CM_LoadMap: %s has invalid lump definitions", name); + 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]) { + if (strcmp(name, map_name)) + Con_Printf("CM_LoadMap: '%s' != '%s'\n", 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]; } @@ -1632,6 +1537,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; @@ -1654,22 +1594,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; } @@ -1680,17 +1616,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 dec94ea73..aecdf8761 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, i; extern char *shortinfotbl[]; @@ -2465,8 +2002,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++ ) { @@ -2478,8 +2014,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)); @@ -2509,22 +2044,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); @@ -2549,10 +2084,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; @@ -2562,7 +2094,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) @@ -2576,7 +2108,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; @@ -2587,13 +2119,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 ); @@ -2613,16 +2143,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; @@ -2662,10 +2192,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 ) { @@ -2678,20 +2208,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; } } @@ -2704,22 +2235,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); @@ -2729,46 +2253,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[]; @@ -2787,7 +2296,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; } @@ -2806,7 +2315,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; } @@ -2816,6 +2325,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++ ) { @@ -2837,206 +2347,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_AddTextEx(&cbuf_server, 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 89d0e7194..a3dc6dfa1 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 dc572a956..cb03b715e 100644 --- a/src/zone.c +++ b/src/zone.c @@ -221,22 +221,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); } @@ -249,7 +240,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; @@ -257,11 +248,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 @@ -291,23 +277,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"); @@ -316,6 +312,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); From da3ab34039e81cd0214c8e6c4a93cf14e1db4946 Mon Sep 17 00:00:00 2001 From: Ivan Bolsunov Date: Mon, 23 Oct 2023 02:19:36 +0400 Subject: [PATCH 2/3] PCRE2: fix invalid return code usage --- src/fonts.c | 2 +- src/sv_demo_misc.c | 25 +++++++++++-------------- src/sv_mod_frags.c | 2 +- src/sv_sys_win.c | 23 +++++++---------------- src/sys_posix.c | 24 +++++++++--------------- src/sys_win.c | 24 ++++++++---------------- 6 files changed, 37 insertions(+), 63 deletions(-) diff --git a/src/fonts.c b/src/fonts.c index b056a89cc..1cbc67c06 100644 --- a/src/fonts.c +++ b/src/fonts.c @@ -474,7 +474,7 @@ void Draw_ListFonts_f(void) } if (regexp) { match_data = pcre2_match_data_create_from_pattern(regexp, NULL); - if (pcre2_match(regexp, (PCRE2_SPTR)dir.files[i].name, strlen(dir.files[i].name), 0, 0, match_data, NULL) <= 0) { + if (pcre2_match(regexp, (PCRE2_SPTR)dir.files[i].name, strlen(dir.files[i].name), 0, 0, match_data, NULL) < 0) { pcre2_match_data_free(match_data); continue; } diff --git a/src/sv_demo_misc.c b/src/sv_demo_misc.c index 644f4c89a..32aee8b01 100644 --- a/src/sv_demo_misc.c +++ b/src/sv_demo_misc.c @@ -366,32 +366,29 @@ void SV_DemoList (qbool use_regex) { PCRE2_UCHAR error_str[256]; pcre2_get_error_message(error, error_str, sizeof(error_str)); - Con_Printf("Sys_listdir: pcre2_compile(%s) error: %s at offset %d\n", + Con_Printf("SV_DemoList: pcre2_compile(%s) error: %s at offset %d\n", Cmd_Argv(j), error_str, error_offset); pcre2_code_free(preg); break; } match_data = pcre2_match_data_create_from_pattern(preg, NULL); - switch (error = pcre2_match(preg, (PCRE2_SPTR)list->name, - strlen(list->name), 0, 0, match_data, NULL)) + error = pcre2_match(preg, (PCRE2_SPTR)list->name, strlen(list->name), 0, 0, match_data, NULL); + pcre2_match_data_free(match_data); + pcre2_code_free(preg); + if (error < 0) { - case 0: - pcre2_match_data_free(match_data); - pcre2_code_free(preg); - continue; - case PCRE2_ERROR_NOMATCH: + if (error != PCRE2_ERROR_NOMATCH) { + Con_Printf("SV_DemoList: pcre2_match(%s, %s) error code: %d\n", + Cmd_Argv(j), list->name, error); + } break; - default: - Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", - Cmd_Argv(j), list->name, error); } - pcre2_match_data_free(match_data); - pcre2_code_free(preg); - break; } else + { if (strstr(list->name, Cmd_Argv(j)) == NULL) break; + } } if (Cmd_Argc() == j) diff --git a/src/sv_mod_frags.c b/src/sv_mod_frags.c index cb07c3dd8..8ee98f7e0 100644 --- a/src/sv_mod_frags.c +++ b/src/sv_mod_frags.c @@ -131,7 +131,7 @@ const char **qwmsg_pcre_check(const char *str, const char *qwm_str, int str_len) match_data = pcre2_match_data_create_from_pattern(reg, NULL); stringcount = pcre2_match(reg, (PCRE2_SPTR)str, str_len, 0, 0, match_data, NULL); - if (stringcount <= 0) { + if (stringcount < 0) { pcre2_match_data_free (match_data); pcre2_code_free(reg); return NULL; diff --git a/src/sv_sys_win.c b/src/sv_sys_win.c index 838f5aa54..d7d24a3e8 100644 --- a/src/sv_sys_win.c +++ b/src/sv_sys_win.c @@ -206,23 +206,14 @@ dir_t Sys_listdir (const char *path, const char *ext, int sort_type) if (!all) { match_data = pcre2_match_data_create_from_pattern(preg, NULL); - switch (error = pcre2_match(preg, (PCRE2_SPTR)fd.cFileName, - strlen(fd.cFileName), 0, 0, match_data, NULL)) - { - case 0: - pcre2_match_data_free(match_data); - break; - case PCRE2_ERROR_NOMATCH: - pcre2_match_data_free(match_data); - continue; - default: - Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", - ext, fd.cFileName, error); - if (!all) { - pcre2_match_data_free(match_data); - pcre2_code_free(preg); + error = pcre2_match(preg, (PCRE2_SPTR)fd.cFileName, + strlen(fd.cFileName), 0, 0, match_data, NULL); + if (error < 0) { + if (error != PCRE2_ERROR_NOMATCH) { + Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", + ext, fd.cFileName, error); } - return dir; + continue; } } diff --git a/src/sys_posix.c b/src/sys_posix.c index 219ca1f27..2590ad658 100644 --- a/src/sys_posix.c +++ b/src/sys_posix.c @@ -171,7 +171,7 @@ Sys_listdir dir_t Sys_listdir (const char *path, const char *ext, int sort_type) { - static file_t list[MAX_DIRFILES]; + static file_t list[MAX_DIRFILES]; // FIXME: thread un-safe. dir_t dir; char pathname[MAX_OSPATH]; DIR *d; @@ -214,21 +214,15 @@ dir_t Sys_listdir (const char *path, const char *ext, int sort_type) if (!all) { match_data = pcre2_match_data_create_from_pattern(preg, NULL); - switch (error = pcre2_match(preg, (PCRE2_SPTR)oneentry->d_name, - strlen(oneentry->d_name), 0, 0, match_data, NULL)) - { - case 0: - pcre2_match_data_free(match_data); - break; - case PCRE2_ERROR_NOMATCH: - pcre2_match_data_free(match_data); + error = pcre2_match(preg, (PCRE2_SPTR)oneentry->d_name, + strlen(oneentry->d_name), 0, 0, match_data, NULL); + pcre2_match_data_free(match_data); + if (error < 0) { + if (error != PCRE2_ERROR_NOMATCH) { + Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", + ext, oneentry->d_name, error); + } continue; - default: - Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", - ext, oneentry->d_name, error); - pcre2_match_data_free(match_data); - pcre2_code_free(preg); - return dir; } } snprintf(pathname, sizeof(pathname), "%s/%s", path, oneentry->d_name); diff --git a/src/sys_win.c b/src/sys_win.c index c86bd73be..03281135e 100644 --- a/src/sys_win.c +++ b/src/sys_win.c @@ -376,23 +376,15 @@ dir_t Sys_listdir (const char *path, const char *ext, int sort_type) if (!all) { match_data = pcre2_match_data_create_from_pattern(preg, NULL); - switch (error = pcre2_match(preg, (PCRE2_SPTR)fd.cFileName, - strlen(fd.cFileName), 0, 0, match_data, NULL)) - { - case 0: - pcre2_match_data_free(match_data); - break; - case PCRE2_ERROR_NOMATCH: - pcre2_match_data_free(match_data); - continue; - default: - Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", - ext, fd.cFileName, error); - pcre2_match_data_free(match_data); - if (!all) { - pcre2_code_free(preg); + error = pcre2_match(preg, (PCRE2_SPTR)fd.cFileName, + strlen(fd.cFileName), 0, 0, match_data, NULL); + pcre2_match_data_free(match_data); + if (error < 0) { + if (error != PCRE2_ERROR_NOMATCH) { + Con_Printf("Sys_listdir: pcre2_match(%s, %s) error code: %d\n", + ext, fd.cFileName, error); } - return dir; + continue; } } From 7eab6c6e47f636bf6bc6e96c8532f516e321b9bc Mon Sep 17 00:00:00 2001 From: Ivan Bolsunov Date: Sun, 17 Dec 2023 15:19:09 +0400 Subject: [PATCH 3/3] RENDERER: fix out of range memory read in safe_strstr() --- src/gl_program.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gl_program.c b/src/gl_program.c index c0e6bec45..007d62b6a 100644 --- a/src/gl_program.c +++ b/src/gl_program.c @@ -582,8 +582,12 @@ static const char* safe_strstr(const char* source, size_t max_length, const char size_t search_length = strlen(search_string); const char* position; - position = (const char*)memchr(source, search_string[0], max_length); - while (position) { + while (true) { + position = (const char*)memchr(source, search_string[0], max_length); + if (!position) { + break; + } + // Move along if (max_length < (position - source)) { break; @@ -598,8 +602,8 @@ static const char* safe_strstr(const char* source, size_t max_length, const char } // Try again - source = position; - position = (const char*)memchr(source + 1, search_string[0], max_length); + source = position + 1; + max_length -= 1; } return NULL;