diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4947258619..e3a0475392 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -112,6 +112,11 @@ endif()
# ${Architecture} must match ARCH_STRING in q_platform.h,
# and is used in DLL names (jagamex86.dll, jagamex86.dylib, jagamei386.so).
if(WIN32)
+
+ if(MSVC)
+ enable_language(ASM_MASM)
+ endif()
+
set(X86 ON)
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
set(Architecture "x86_64")
@@ -126,6 +131,7 @@ else()
set(Architecture "arm64")
add_definitions(-DPNG_ARM_NEON_OPT=0)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
+ set(ARM32 ON)
set(Architecture "arm")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
set(X86 ON)
@@ -226,6 +232,8 @@ if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:SSE2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /safeseh:no")
+
# We don't try to control symbol visibility under MSVC.
set(OPENJK_VISIBILITY_FLAGS "")
elseif (("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") OR ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang"))
diff --git a/codemp/CMakeLists.txt b/codemp/CMakeLists.txt
index 1e424ffd51..1f13628714 100644
--- a/codemp/CMakeLists.txt
+++ b/codemp/CMakeLists.txt
@@ -259,10 +259,29 @@ if(BuildMPEngine OR BuildMPDed)
"${MPDir}/qcommon/tags.h"
"${MPDir}/qcommon/timing.h"
"${MPDir}/qcommon/vm.cpp"
+ "${MPDir}/qcommon/vm_local.h"
+ "${MPDir}/qcommon/vm_interpreted.cpp"
"${MPDir}/qcommon/z_memman_pc.cpp"
${SharedCommonFiles}
)
+
+ if(X86)
+ set(MPEngineAndDedCommonFiles ${MPEngineAndDedCommonFiles}
+ "${MPDir}/qcommon/vm_x86.cpp"
+ )
+ elseif(ARM32)
+ set(MPEngineAndDedCommonFiles ${MPEngineAndDedCommonFiles}
+ "${MPDir}/qcommon/vm_arm.cpp"
+ )
+ endif()
+
+ if(MSVC)
+ set(MPEngineAndDedCommonFiles ${MPEngineAndDedCommonFiles}
+ "${MPDir}/win32/q3asm.asm"
+ )
+ endif()
+
if(WIN32)
set(MPEngineAndDedCommonFiles ${MPEngineAndDedCommonFiles})
endif(WIN32)
diff --git a/codemp/client/cl_cgameapi.cpp b/codemp/client/cl_cgameapi.cpp
index 15267443a6..49ad244ebc 100644
--- a/codemp/client/cl_cgameapi.cpp
+++ b/codemp/client/cl_cgameapi.cpp
@@ -29,6 +29,9 @@ along with this program; if not, see .
#include "snd_ambient.h"
#include "FXExport.h"
#include "FxUtil.h"
+#include "qcommon/vm_local.h"
+
+#include
extern IHeapAllocator *G2VertSpaceClient;
extern botlib_export_t *botlib_export;
@@ -37,6 +40,62 @@ extern botlib_export_t *botlib_export;
static cgameExport_t *cge; // cgame export table
static vm_t *cgvm; // cgame vm, valid for legacy and new api
+typedef std::unordered_map g2HandleToG2_m;
+
+static g2HandleToG2_m g2Mapping;
+static g2handle_t g2NextHandle = (g2handle_t)1; // Start at 1, because 0 has special meaning
+
+static inline CGhoul2Info_v *CL_G2Map_GetG2FromHandle( g2handleptr_t g2h )
+{ // Returns the pointer to the g2 object if the handle is valid
+ // Native libraries should not use the pointer, but in theory they could use
+ // it. Thus we don't perform any mapping for native libraries to avoid
+ // issues with custom modules.
+ if ( cgvm->dllHandle ) return (CGhoul2Info_v*)g2h;
+
+ g2handle_t g2handle = (g2handle_t)g2h;
+ g2HandleToG2_m::iterator ghlIt = g2Mapping.find(g2handle);
+
+ if (ghlIt == g2Mapping.end()) return NULL;
+ return g2Mapping[g2handle];
+}
+
+static inline CGhoul2Info_v **CL_G2Map_GetG2PtrFromHandle( g2handleptr_t *g2h )
+{ // Returns a pointer to the g2 object pointer in the map so g2 functions can update the pointer
+ // Native libraries should not use the pointer, but in theory they could use
+ // it. Thus we don't perform any mapping for native libraries to avoid
+ // issues with custom modules.
+ if ( cgvm->dllHandle ) return (CGhoul2Info_v **)g2h;
+
+ g2handle_t g2handle = *((g2handle_t*)g2h);
+ if ( !g2handle )
+ { // Special case: the g2 handle is not valid, yet. Return a pointer to a static temporary pointer. Insertion is handled by calling CL_G2Map_Update after calling the G2API
+ static CGhoul2Info_v *g2Tmp;
+ g2Tmp = NULL;
+ return &g2Tmp;
+ }
+ return &g2Mapping[g2handle];
+}
+
+static void CL_G2Map_Update( g2handleptr_t *g2h, CGhoul2Info_v *g2Ptr )
+{ // Inserts and/or erases to/from the map and updates the handle pointer
+ if ( cgvm->dllHandle ) return;
+
+ g2handle_t *g2handle = (g2handle_t*)g2h;
+ if ( !*g2handle && g2Ptr )
+ { // Got a 0 handle, but a non-0 pointer: add to map and set handle
+ // Unlikely to happen, but should we ever cycle through the whole integer range start searching for gaps
+ while ( CL_G2Map_GetG2FromHandle(g2NextHandle) || !g2NextHandle ) g2NextHandle++;
+
+ g2Mapping[g2NextHandle] = g2Ptr;
+ *g2handle = g2NextHandle++;
+ }
+ else if ( *g2h && !g2Ptr )
+ { // Got a non-0 handle, but 0 pointer: remove from map and set handle to 0
+ g2Mapping.erase( *g2handle );
+ *g2handle = 0;
+ }
+}
+
//
// cgame vmMain calls
//
@@ -213,9 +272,26 @@ qboolean CGVM_NoUseableForce( void ) {
return cge->NoUseableForce();
}
+static size_t vec3_size = sizeof(vec3_t);
void CGVM_GetOrigin( int entID, vec3_t out ) {
if ( cgvm->isLegacy ) {
- VM_Call( cgvm, CG_GET_ORIGIN, entID, reinterpret_cast< intptr_t >( out ) );
+ float *out_ptr;
+ float *out_ptr_vm;
+
+ if ( !cgvm->dllHandle ) {
+ out_ptr = (float*)VM_ExtraMemory_ClaimData(cgvm, (float*)out, vec3_size);
+ out_ptr_vm = (float*)VM_PtrToOffset( cgvm, (void*)out_ptr );
+ } else {
+ out_ptr_vm = out_ptr = out;
+ }
+ VM_Call( cgvm, CG_GET_ORIGIN, entID, reinterpret_cast< intptr_t >( out_ptr_vm ) );
+ if ( !cgvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( out_ptr ) {
+ VectorCopy( out_ptr, out );
+ VM_ExtraMemory_Release( cgvm, vec3_size );
+ }
+ }
return;
}
VMSwap v( cgvm );
@@ -225,7 +301,23 @@ void CGVM_GetOrigin( int entID, vec3_t out ) {
void CGVM_GetAngles( int entID, vec3_t out ) {
if ( cgvm->isLegacy ) {
- VM_Call( cgvm, CG_GET_ANGLES, entID, reinterpret_cast< intptr_t >( out ) );
+ float *out_ptr;
+ float *out_ptr_vm;
+
+ if ( !cgvm->dllHandle ) {
+ out_ptr = (float*)VM_ExtraMemory_ClaimData(cgvm, (float*)out, vec3_size);
+ out_ptr_vm = (float*)VM_PtrToOffset( cgvm, (void*)out_ptr );
+ } else {
+ out_ptr_vm = out_ptr = out;
+ }
+ VM_Call( cgvm, CG_GET_ANGLES, entID, reinterpret_cast< intptr_t >( out_ptr_vm ) );
+ if ( !cgvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( out_ptr ) {
+ VectorCopy( out_ptr, out );
+ VM_ExtraMemory_Release( cgvm, vec3_size );
+ }
+ }
return;
}
VMSwap v( cgvm );
@@ -253,7 +345,20 @@ trajectory_t *CGVM_GetAngleTrajectory( int entID ) {
void CGVM_ROFF_NotetrackCallback( int entID, const char *notetrack ) {
if ( cgvm->isLegacy ) {
- VM_Call( cgvm, CG_ROFF_NOTETRACK_CALLBACK, entID, reinterpret_cast< intptr_t >( notetrack ) );
+ int notetrack_length = 0;
+ const char *notetrack_ptr;
+
+ if ( !cgvm->dllHandle ) {
+ notetrack_length = strlen( notetrack ) + 1; // +1 for the terminating 0-byte, which ClaimString automatically allocates, too
+ notetrack_ptr = (const char*)VM_PtrToOffset( cgvm, VM_ExtraMemory_ClaimString(cgvm, notetrack) );
+ } else {
+ notetrack_ptr = notetrack;
+ }
+ VM_Call( cgvm, CG_ROFF_NOTETRACK_CALLBACK, entID, reinterpret_cast< intptr_t >( notetrack_ptr ) );
+ if ( !cgvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( notetrack_ptr && notetrack_length ) VM_ExtraMemory_Release( cgvm, notetrack_length );
+ }
return;
}
VMSwap v( cgvm );
@@ -324,7 +429,42 @@ static void CL_CM_LoadMap( const char *mapname, qboolean subBSP ) {
}
static void CL_GetGlconfig( glconfig_t *glconfig ) {
- *glconfig = cls.glconfig;
+ if ( cgvm->dllHandle ) {
+ // For native libraries we can just copy the struct over cause they should be identical
+ *glconfig = cls.glconfig;
+ } else {
+ // As the QVM is 32 bit internally the size of our glconfig_t is bigger on 64 bit, because of the 4 string
+ // pointers. Instead of just assigning the glconfig we have to manually copy it over.
+ glconfig_qvm_t *glconfig_qvm = (glconfig_qvm_t*)glconfig;
+
+ // We now reserve additional memory when loading the QVM. The QVM shouldn't try to access it on its own, but it
+ // is valid inside the QVM and if we set a pointer to it the QVM should be able to access it. Thus we now claim
+ // some of this extra memory in QVM-scope to write our glconfig strings in there and tell the QVM where to find
+ // them.
+ // FIXME: It would probably be a good idea to keep a reference to this extra memory ourselves in case a module
+ // decides to call this functions multiple times. Currently we would claim additional memory each time.
+ glconfig_qvm->renderer_string = VM_PtrToOffset( cgvm, VM_ExtraMemory_ClaimString( cgvm, cls.glconfig.renderer_string ) );
+ glconfig_qvm->vendor_string = VM_PtrToOffset( cgvm, VM_ExtraMemory_ClaimString( cgvm, cls.glconfig.vendor_string ) );
+ glconfig_qvm->version_string = VM_PtrToOffset( cgvm, VM_ExtraMemory_ClaimString( cgvm, cls.glconfig.version_string ) );
+ glconfig_qvm->extensions_string = VM_PtrToOffset( cgvm, VM_ExtraMemory_ClaimString( cgvm, cls.glconfig.extensions_string ) );
+
+ // Assign the rest of the values
+ glconfig_qvm->maxTextureSize = cls.glconfig.maxTextureSize;
+ glconfig_qvm->maxActiveTextures = cls.glconfig.maxActiveTextures;
+ glconfig_qvm->maxTextureFilterAnisotropy = cls.glconfig.maxTextureFilterAnisotropy;
+ glconfig_qvm->colorBits = cls.glconfig.colorBits;
+ glconfig_qvm->depthBits = cls.glconfig.depthBits;
+ glconfig_qvm->stencilBits = cls.glconfig.stencilBits;
+ glconfig_qvm->deviceSupportsGamma = cls.glconfig.deviceSupportsGamma;
+ glconfig_qvm->textureCompression = cls.glconfig.textureCompression;
+ glconfig_qvm->textureEnvAddAvailable = cls.glconfig.textureEnvAddAvailable;
+ glconfig_qvm->clampToEdgeAvailable = cls.glconfig.clampToEdgeAvailable;
+ glconfig_qvm->vidWidth = cls.glconfig.vidWidth;
+ glconfig_qvm->vidHeight = cls.glconfig.vidHeight;
+ glconfig_qvm->displayFrequency = cls.glconfig.displayFrequency;
+ glconfig_qvm->isFullscreen = cls.glconfig.isFullscreen;
+ glconfig_qvm->stereoEnabled = cls.glconfig.stereoEnabled;
+ }
}
static void CL_GetGameState( gameState_t *gs ) {
@@ -356,6 +496,28 @@ static void CL_S_Shutup( qboolean shutup ) {
s_shutUp = shutup;
}
+static void CL_R_AddRefEntityToScene( const refEntity_t *refEnt ) {
+ if ( cgvm->dllHandle ) {
+ // We can directly pass the refEnt for native libraries
+ re->AddRefEntityToScene( refEnt );
+ return;
+ }
+
+ // The renderer should look up the handle on its own, but as we have to stay compatible with the modular renderer
+ // system we can't change the renderer for this and have to meet its expectations instead...
+
+ // Get a copy of the refEntity data. For a QVM mod the ghoul2 pointer at the end might contain some additional data,
+ // because the QVM stores it as 32 bit and if the engine is 64 bit it is going to read slightly out of bounds of the
+ // struct in the QVM, but CL_G2Map_GetG2FromHandle should take care of that by casting.
+ refEntity_t refEntCopy = *refEnt;
+
+ // Get the ghoul2 instance from the handle
+ refEntCopy.ghoul2 = CL_G2Map_GetG2FromHandle( (g2handleptr_t)refEnt->ghoul2 );
+
+ // Pass our copy
+ re->AddRefEntityToScene( &refEntCopy );
+}
+
static int CL_GetCurrentCmdNumber( void ) {
return cl.cmdNumber;
}
@@ -443,7 +605,7 @@ static void CL_RMG_Init( int /* terrainID */, const char * /* terrainInfo */ ) {
static qboolean CGFX_PlayBoltedEffectID( int id, vec3_t org, void *ghoul2, const int boltNum, const int entNum, const int modelNum, int iLooptime, qboolean isRelative ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
int boltInfo=0;
if ( re->G2API_AttachEnt( &boltInfo, g2, modelNum, boltNum, entNum, modelNum ) )
{
@@ -479,81 +641,86 @@ static void CL_G2API_ListModelBones( void *ghlInfo, int frame ) {
static void CL_G2API_SetGhoul2ModelIndexes( void *ghoul2, qhandle_t *modelList, qhandle_t *skinList ) {
if ( !ghoul2 ) return;
- re->G2API_SetGhoul2ModelIndexes( *((CGhoul2Info_v *)ghoul2), modelList, skinList );
+ re->G2API_SetGhoul2ModelIndexes( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelList, skinList );
}
static qboolean CL_G2API_HaveWeGhoul2Models( void *ghoul2) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_HaveWeGhoul2Models( *((CGhoul2Info_v *)ghoul2) );
+ return re->G2API_HaveWeGhoul2Models( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)) );
}
static qboolean CL_G2API_GetBoltMatrix( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static qboolean CL_G2API_GetBoltMatrix_NoReconstruct( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
if ( !ghoul2 ) return qfalse;
re->G2API_BoltMatrixReconstruction( qfalse );
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static qboolean CL_G2API_GetBoltMatrix_NoRecNoRot( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
if ( !ghoul2 ) return qfalse;
// Intentionally not setting bolt matrix reconstruction state per original code comments
re->G2API_BoltMatrixSPMethod( qtrue );
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static int CL_G2API_InitGhoul2Model( void **ghoul2Ptr, const char *fileName, int modelIndex, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- return re->G2API_InitGhoul2Model( (CGhoul2Info_v **)ghoul2Ptr, fileName, modelIndex, customSkin, customShader, modelFlags, lodBias );
+ CGhoul2Info_v **g2Ptr = CL_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghoul2Ptr );
+ int ret = re->G2API_InitGhoul2Model( g2Ptr, fileName, modelIndex, customSkin, customShader, modelFlags, lodBias );
+ CL_G2Map_Update( (g2handleptr_t*)ghoul2Ptr, *g2Ptr );
+ return ret;
}
static qboolean CL_G2API_SetSkin( void *ghoul2, int modelIndex, qhandle_t customSkin, qhandle_t renderSkin ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_SetSkin( g2, modelIndex, customSkin, renderSkin );
}
static void CL_G2API_CollisionDetect( CollisionRecord_t *collRecMap, void* ghoul2, const vec3_t angles, const vec3_t position, int frameNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, int traceFlags, int useLod, float fRadius ) {
if ( !ghoul2 ) return;
- re->G2API_CollisionDetect( collRecMap, *((CGhoul2Info_v *)ghoul2), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceClient, traceFlags, useLod, fRadius );
+ re->G2API_CollisionDetect( collRecMap, *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceClient, traceFlags, useLod, fRadius );
}
static void CL_G2API_CollisionDetectCache( CollisionRecord_t *collRecMap, void* ghoul2, const vec3_t angles, const vec3_t position, int frameNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, int traceFlags, int useLod, float fRadius ) {
if ( !ghoul2 ) return;
- re->G2API_CollisionDetectCache( collRecMap, *((CGhoul2Info_v *)ghoul2), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceClient, traceFlags, useLod, fRadius );
+ re->G2API_CollisionDetectCache( collRecMap, *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceClient, traceFlags, useLod, fRadius );
}
static void CL_G2API_CleanGhoul2Models( void **ghoul2Ptr ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- re->G2API_CleanGhoul2Models( (CGhoul2Info_v **)ghoul2Ptr );
+ CGhoul2Info_v **g2Ptr = CL_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghoul2Ptr );
+ re->G2API_CleanGhoul2Models( g2Ptr );
+ CL_G2Map_Update( (g2handleptr_t*)ghoul2Ptr, *g2Ptr );
}
static qboolean CL_G2API_SetBoneAngles( void *ghoul2, int modelIndex, const char *boneName, const vec3_t angles, const int flags, const int up, const int right, const int forward, qhandle_t *modelList, int blendTime , int currentTime ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetBoneAngles( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName, angles, flags, (const Eorientations)up, (const Eorientations)right, (const Eorientations)forward, modelList, blendTime , currentTime );
+ return re->G2API_SetBoneAngles( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName, angles, flags, (const Eorientations)up, (const Eorientations)right, (const Eorientations)forward, modelList, blendTime , currentTime );
}
static qboolean CL_G2API_SetBoneAnim( void *ghoul2, const int modelIndex, const char *boneName, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetBoneAnim( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime );
+ return re->G2API_SetBoneAnim( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime );
}
static qboolean CL_G2API_GetBoneAnim( void *ghoul2, const char *boneName, const int currentTime, float *currentFrame, int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList, const int modelIndex ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_GetBoneAnim( g2, modelIndex, boneName, currentTime, currentFrame, startFrame, endFrame, flags, animSpeed, modelList );
}
static qboolean CL_G2API_GetBoneFrame( void *ghoul2, const char *boneName, const int currentTime, float *currentFrame, int *modelList, const int modelIndex ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
int iDontCare1 = 0, iDontCare2 = 0, iDontCare3 = 0;
float fDontCare1 = 0;
@@ -567,7 +734,7 @@ static void CL_G2API_GetGLAName( void *ghoul2, int modelIndex, char *fillBuf ) {
return;
}
- char *tmp = re->G2API_GetGLAName( *((CGhoul2Info_v *)ghoul2), modelIndex );
+ char *tmp = re->G2API_GetGLAName( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex );
if ( tmp )
strcpy( fillBuf, tmp );
else
@@ -577,12 +744,12 @@ static void CL_G2API_GetGLAName( void *ghoul2, int modelIndex, char *fillBuf ) {
static int CL_G2API_CopyGhoul2Instance( void *g2From, void *g2To, int modelIndex ) {
if ( !g2From || !g2To ) return 0;
- return re->G2API_CopyGhoul2Instance( *((CGhoul2Info_v *)g2From), *((CGhoul2Info_v *)g2To), modelIndex );
+ return re->G2API_CopyGhoul2Instance( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), *(CL_G2Map_GetG2FromHandle((g2handleptr_t)g2To)), modelIndex );
}
static void CL_G2API_CopySpecificGhoul2Model( void *g2From, int modelFrom, void *g2To, int modelTo ) {
if ( !g2From || !g2To) return;
- re->G2API_CopySpecificG2Model( *((CGhoul2Info_v *)g2From), modelFrom, *((CGhoul2Info_v *)g2To), modelTo );
+ re->G2API_CopySpecificG2Model( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), modelFrom, *(CL_G2Map_GetG2FromHandle((g2handleptr_t)g2To)), modelTo );
}
static void CL_G2API_DuplicateGhoul2Instance( void *g2From, void **g2To ) {
@@ -590,31 +757,39 @@ static void CL_G2API_DuplicateGhoul2Instance( void *g2From, void **g2To ) {
g_G2AllocServer = 0;
#endif
if ( !g2From || !g2To ) return;
- re->G2API_DuplicateGhoul2Instance( *((CGhoul2Info_v *)g2From), (CGhoul2Info_v **)g2To );
+ CGhoul2Info_v **g2ToPtr = CL_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)g2To );
+ re->G2API_DuplicateGhoul2Instance( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), g2ToPtr );
+ CL_G2Map_Update( (g2handleptr_t*)g2To, *g2ToPtr );
}
static qboolean CL_G2API_HasGhoul2ModelOnIndex( void *ghlInfo, int modelIndex ) {
- return re->G2API_HasGhoul2ModelOnIndex( (CGhoul2Info_v **)ghlInfo, modelIndex );
+ CGhoul2Info_v **g2Ptr = CL_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_HasGhoul2ModelOnIndex( g2Ptr, modelIndex );
+ CL_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static qboolean CL_G2API_RemoveGhoul2Model( void *ghlInfo, int modelIndex ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- return re->G2API_RemoveGhoul2Model( (CGhoul2Info_v **)ghlInfo, modelIndex );
+ CGhoul2Info_v **g2Ptr = CL_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_RemoveGhoul2Model( g2Ptr, modelIndex );
+ CL_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static qboolean CL_G2API_SkinlessModel( void *ghlInfo, int modelIndex ) {
if ( !ghlInfo ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghlInfo);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfo));
return re->G2API_SkinlessModel( g2, modelIndex );
}
static int CL_G2API_GetNumGoreMarks( void *ghlInfo, int modelIndex ) {
#ifdef _G2_GORE
if ( !ghlInfo ) return 0;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghlInfo);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfo));
return re->G2API_GetNumGoreMarks( g2, modelIndex );
#else
return 0;
@@ -624,62 +799,62 @@ static int CL_G2API_GetNumGoreMarks( void *ghlInfo, int modelIndex ) {
static void CL_G2API_AddSkinGore( void *ghlInfo, SSkinGoreData *gore ) {
#ifdef _G2_GORE
if ( !ghlInfo ) return;
- re->G2API_AddSkinGore( *((CGhoul2Info_v *)ghlInfo), *(SSkinGoreData *)gore );
+ re->G2API_AddSkinGore( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfo)), *(SSkinGoreData *)gore );
#endif
}
static void CL_G2API_ClearSkinGore( void *ghlInfo ) {
#ifdef _G2_GORE
if ( !ghlInfo ) return;
- re->G2API_ClearSkinGore( *((CGhoul2Info_v *)ghlInfo) );
+ re->G2API_ClearSkinGore( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfo)) );
#endif
}
static int CL_G2API_Ghoul2Size( void *ghlInfo ) {
if ( !ghlInfo ) return 0;
- return re->G2API_Ghoul2Size( *((CGhoul2Info_v *)ghlInfo) );
+ return re->G2API_Ghoul2Size( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfo)) );
}
static int CL_G2API_AddBolt( void *ghoul2, int modelIndex, const char *boneName ) {
if ( !ghoul2 ) return -1;
- return re->G2API_AddBolt( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName );
+ return re->G2API_AddBolt( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName );
}
static qboolean CL_G2API_AttachEnt( int *boltInfo, void *ghlInfoTo, int toBoltIndex, int entNum, int toModelNum ) {
if ( !ghlInfoTo ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghlInfoTo);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfoTo));
return re->G2API_AttachEnt( boltInfo, g2, 0, toBoltIndex, entNum, toModelNum );
}
static void CL_G2API_SetBoltInfo( void *ghoul2, int modelIndex, int boltInfo ) {
if ( !ghoul2 ) return;
- re->G2API_SetBoltInfo( *((CGhoul2Info_v *)ghoul2), modelIndex, boltInfo );
+ re->G2API_SetBoltInfo( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltInfo );
}
static qboolean CL_G2API_SetRootSurface( void *ghoul2, const int modelIndex, const char *surfaceName ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetRootSurface( *((CGhoul2Info_v *)ghoul2), modelIndex, surfaceName );
+ return re->G2API_SetRootSurface( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, surfaceName );
}
static qboolean CL_G2API_SetSurfaceOnOff( void *ghoul2, const char *surfaceName, const int flags ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetSurfaceOnOff( *((CGhoul2Info_v *)ghoul2), surfaceName, flags );
+ return re->G2API_SetSurfaceOnOff( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), surfaceName, flags );
}
static qboolean CL_G2API_SetNewOrigin( void *ghoul2, const int boltIndex ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetNewOrigin( *((CGhoul2Info_v *)ghoul2), boltIndex );
+ return re->G2API_SetNewOrigin( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boltIndex );
}
static qboolean CL_G2API_DoesBoneExist( void *ghoul2, int modelIndex, const char *boneName ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_DoesBoneExist( g2, modelIndex, boneName );
}
static int CL_G2API_GetSurfaceRenderStatus( void *ghoul2, const int modelIndex, const char *surfaceName ) {
if ( !ghoul2 ) return -1;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_GetSurfaceRenderStatus( g2, modelIndex, surfaceName );
}
@@ -693,7 +868,7 @@ static void CL_G2API_SetTime( int time, int clock ) {
static void CL_G2API_AbsurdSmoothing( void *ghoul2, qboolean status ) {
if ( !ghoul2 ) return;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
re->G2API_AbsurdSmoothing( g2, status );
}
@@ -702,7 +877,7 @@ static void CL_G2API_SetRagDoll( void *ghoul2, sharedRagDollParams_t *params ) {
CRagDollParams rdParams;
if ( !params ) {
- re->G2API_ResetRagDoll( *((CGhoul2Info_v *)ghoul2) );
+ re->G2API_ResetRagDoll( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)) );
return;
}
@@ -725,7 +900,7 @@ static void CL_G2API_SetRagDoll( void *ghoul2, sharedRagDollParams_t *params ) {
rdParams.RagPhase = (CRagDollParams::ERagPhase)params->RagPhase;
rdParams.effectorsToTurnOff = (CRagDollParams::ERagEffector)params->effectorsToTurnOff;
- re->G2API_SetRagDoll( *((CGhoul2Info_v *)ghoul2), &rdParams );
+ re->G2API_SetRagDoll( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), &rdParams );
}
static void CL_G2API_AnimateG2Models( void *ghoul2, int time, sharedRagDollUpdateParams_t *params ) {
@@ -741,58 +916,58 @@ static void CL_G2API_AnimateG2Models( void *ghoul2, int time, sharedRagDollUpdat
rduParams.me = params->me;
rduParams.settleFrame = params->settleFrame;
- re->G2API_AnimateG2ModelsRag( *((CGhoul2Info_v *)ghoul2), time, &rduParams );
+ re->G2API_AnimateG2ModelsRag( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, &rduParams );
}
static qboolean CL_G2API_RagPCJConstraint( void *ghoul2, const char *boneName, vec3_t min, vec3_t max ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_RagPCJConstraint( *((CGhoul2Info_v *)ghoul2), boneName, min, max );
+ return re->G2API_RagPCJConstraint( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, min, max );
}
static qboolean CL_G2API_RagPCJGradientSpeed( void *ghoul2, const char *boneName, const float speed ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_RagPCJGradientSpeed( *((CGhoul2Info_v *)ghoul2), boneName, speed );
+ return re->G2API_RagPCJGradientSpeed( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, speed );
}
static qboolean CL_G2API_RagEffectorGoal( void *ghoul2, const char *boneName, vec3_t pos ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_RagEffectorGoal( *((CGhoul2Info_v *)ghoul2), boneName, pos );
+ return re->G2API_RagEffectorGoal( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, pos );
}
static qboolean CL_G2API_GetRagBonePos( void *ghoul2, const char *boneName, vec3_t pos, vec3_t entAngles, vec3_t entPos, vec3_t entScale ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_GetRagBonePos( *((CGhoul2Info_v *)ghoul2), boneName, pos, entAngles, entPos, entScale );
+ return re->G2API_GetRagBonePos( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, pos, entAngles, entPos, entScale );
}
static qboolean CL_G2API_RagEffectorKick( void *ghoul2, const char *boneName, vec3_t velocity ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_RagEffectorKick( *((CGhoul2Info_v *)ghoul2), boneName, velocity );
+ return re->G2API_RagEffectorKick( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, velocity );
}
static qboolean CL_G2API_RagForceSolve( void *ghoul2, qboolean force ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_RagForceSolve( *((CGhoul2Info_v *)ghoul2), force );
+ return re->G2API_RagForceSolve( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), force );
}
static qboolean CL_G2API_SetBoneIKState( void *ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetBoneIKState( *((CGhoul2Info_v *)ghoul2), time, boneName, ikState, params );
+ return re->G2API_SetBoneIKState( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, boneName, ikState, params );
}
static qboolean CL_G2API_IKMove( void *ghoul2, int time, sharedIKMoveParams_t *params ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_IKMove( *((CGhoul2Info_v *)ghoul2), time, params );
+ return re->G2API_IKMove( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, params );
}
static qboolean CL_G2API_RemoveBone( void *ghoul2, const char *boneName, int modelIndex ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_RemoveBone( g2, modelIndex, boneName );
}
static void CL_G2API_AttachInstanceToEntNum( void *ghoul2, int entityNum, qboolean server ) {
if ( !ghoul2 ) return;
- re->G2API_AttachInstanceToEntNum( *((CGhoul2Info_v *)ghoul2), entityNum, server );
+ re->G2API_AttachInstanceToEntNum( *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), entityNum, server );
}
static void CL_G2API_ClearAttachedInstance( int entityNum ) {
@@ -805,13 +980,13 @@ static void CL_G2API_CleanEntAttachments( void ) {
static qboolean CL_G2API_OverrideServer( void *serverInstance ) {
if ( !serverInstance ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)serverInstance);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)serverInstance));
return re->G2API_OverrideServerWithClientData( g2, 0 );
}
static void CL_G2API_GetSurfaceName( void *ghoul2, int surfNumber, int modelIndex, char *fillBuf ) {
if ( !ghoul2 ) return;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
char *tmp = re->G2API_GetSurfaceName( g2, modelIndex, surfNumber );
strcpy( fillBuf, tmp );
}
@@ -1142,7 +1317,7 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) {
return 0;
case CG_R_ADDREFENTITYTOSCENE:
- re->AddRefEntityToScene( (const refEntity_t *)VMA(1) );
+ CL_R_AddRefEntityToScene( (const refEntity_t *)VMA(1) );
return 0;
case CG_R_ADDPOLYTOSCENE:
@@ -1482,64 +1657,64 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) {
return 0;
case CG_G2_HAVEWEGHOULMODELS:
- return CL_G2API_HaveWeGhoul2Models( VMA(1) );
+ return CL_G2API_HaveWeGhoul2Models( (void*)args[1] );
case CG_G2_SETMODELS:
- CL_G2API_SetGhoul2ModelIndexes( VMA(1), (qhandle_t *)VMA(2), (qhandle_t *)VMA(3) );
+ CL_G2API_SetGhoul2ModelIndexes( (void*)args[1], (qhandle_t *)VMA(2), (qhandle_t *)VMA(3) );
return 0;
case CG_G2_GETBOLT:
- return CL_G2API_GetBoltMatrix(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return CL_G2API_GetBoltMatrix((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case CG_G2_GETBOLT_NOREC:
- return CL_G2API_GetBoltMatrix_NoReconstruct(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return CL_G2API_GetBoltMatrix_NoReconstruct((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case CG_G2_GETBOLT_NOREC_NOROT:
- return CL_G2API_GetBoltMatrix_NoRecNoRot(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return CL_G2API_GetBoltMatrix_NoRecNoRot((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case CG_G2_INITGHOUL2MODEL:
return CL_G2API_InitGhoul2Model((void **)VMA(1), (const char *)VMA(2), args[3], (qhandle_t) args[4], (qhandle_t) args[5], args[6], args[7]);
case CG_G2_SETSKIN:
- return CL_G2API_SetSkin( VMA(1), args[2], args[3], args[4] );
+ return CL_G2API_SetSkin( (void*)args[1], args[2], args[3], args[4] );
case CG_G2_COLLISIONDETECT:
- CL_G2API_CollisionDetect ( (CollisionRecord_t*)VMA(1), VMA(2), (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
+ CL_G2API_CollisionDetect ( (CollisionRecord_t*)VMA(1), (void*)args[2], (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
return 0;
case CG_G2_COLLISIONDETECTCACHE:
- CL_G2API_CollisionDetectCache ( (CollisionRecord_t*)VMA(1), VMA(2), (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
+ CL_G2API_CollisionDetectCache ( (CollisionRecord_t*)VMA(1), (void*)args[2], (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
return 0;
case CG_G2_ANGLEOVERRIDE:
- return CL_G2API_SetBoneAngles( VMA(1), args[2], (const char *)VMA(3), (float *)VMA(4), args[5], args[6], args[7], args[8], (qhandle_t *)VMA(9), args[10], args[11] );
+ return CL_G2API_SetBoneAngles( (void*)args[1], args[2], (const char *)VMA(3), (float *)VMA(4), args[5], args[6], args[7], args[8], (qhandle_t *)VMA(9), args[10], args[11] );
case CG_G2_CLEANMODELS:
CL_G2API_CleanGhoul2Models( (void **)VMA(1) );
return 0;
case CG_G2_PLAYANIM:
- return CL_G2API_SetBoneAnim( VMA(1), args[2], (const char *)VMA(3), args[4], args[5], args[6], VMF(7), args[8], VMF(9), args[10] );
+ return CL_G2API_SetBoneAnim( (void*)args[1], args[2], (const char *)VMA(3), args[4], args[5], args[6], VMF(7), args[8], VMF(9), args[10] );
case CG_G2_GETBONEANIM:
- return CL_G2API_GetBoneAnim( VMA(1), (const char *)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), (int *)VMA(6), (int *)VMA(7), (float *)VMA(8), (int *)VMA(9), args[10] );
+ return CL_G2API_GetBoneAnim( (void*)args[1], (const char *)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), (int *)VMA(6), (int *)VMA(7), (float *)VMA(8), (int *)VMA(9), args[10] );
case CG_G2_GETBONEFRAME:
- return CL_G2API_GetBoneFrame( VMA(1), (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), args[6] );
+ return CL_G2API_GetBoneFrame( (void*)args[1], (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), args[6] );
case CG_G2_GETGLANAME:
- CL_G2API_GetGLAName( VMA(1), args[2], (char *)VMA(3) );
+ CL_G2API_GetGLAName( (void*)args[1], args[2], (char *)VMA(3) );
return 0;
case CG_G2_COPYGHOUL2INSTANCE:
- return CL_G2API_CopyGhoul2Instance( VMA(1), VMA(2), args[3] );
+ return CL_G2API_CopyGhoul2Instance( (void*)args[1], (void*)args[2], args[3] );
case CG_G2_COPYSPECIFICGHOUL2MODEL:
- CL_G2API_CopySpecificGhoul2Model( VMA(1), args[2], VMA(3), args[4] );
+ CL_G2API_CopySpecificGhoul2Model( (void*)args[1], args[2], (void*)args[3], args[4] );
return 0;
case CG_G2_DUPLICATEGHOUL2INSTANCE:
- CL_G2API_DuplicateGhoul2Instance( VMA(1), (void **)VMA(2) );
+ CL_G2API_DuplicateGhoul2Instance( (void*)args[1], (void **)VMA(2) );
return 0;
case CG_G2_HASGHOUL2MODELONINDEX:
@@ -1549,46 +1724,46 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) {
return CL_G2API_RemoveGhoul2Model( VMA(1), args[2]);
case CG_G2_SKINLESSMODEL:
- return CL_G2API_SkinlessModel( VMA(1), args[2] );
+ return CL_G2API_SkinlessModel( (void*)args[1], args[2] );
case CG_G2_GETNUMGOREMARKS:
- return CL_G2API_GetNumGoreMarks( VMA(1), args[2] );
+ return CL_G2API_GetNumGoreMarks( (void*)args[1], args[2] );
case CG_G2_ADDSKINGORE:
- CL_G2API_AddSkinGore( VMA(1), (SSkinGoreData *)VMA(2));
+ CL_G2API_AddSkinGore( (void*)args[1], (SSkinGoreData *)VMA(2));
return 0;
case CG_G2_CLEARSKINGORE:
- CL_G2API_ClearSkinGore ( VMA(1) );
+ CL_G2API_ClearSkinGore ( (void*)args[1] );
return 0;
case CG_G2_SIZE:
- return CL_G2API_Ghoul2Size ( VMA(1) );
+ return CL_G2API_Ghoul2Size ( (void*)args[1] );
case CG_G2_ADDBOLT:
- return CL_G2API_AddBolt( VMA(1), args[2], (const char *)VMA(3));
+ return CL_G2API_AddBolt( (void*)args[1], args[2], (const char *)VMA(3));
case CG_G2_ATTACHENT:
- return CL_G2API_AttachEnt( (int*)VMA(1), VMA(2), args[3], args[4], args[5] );
+ return CL_G2API_AttachEnt( (int*)VMA(1), (void*)args[2], args[3], args[4], args[5] );
case CG_G2_SETBOLTON:
- CL_G2API_SetBoltInfo( VMA(1), args[2], args[3] );
+ CL_G2API_SetBoltInfo( (void*)args[1], args[2], args[3] );
return 0;
case CG_G2_SETROOTSURFACE:
- return CL_G2API_SetRootSurface( VMA(1), args[2], (const char *)VMA(3));
+ return CL_G2API_SetRootSurface( (void*)args[1], args[2], (const char *)VMA(3));
case CG_G2_SETSURFACEONOFF:
- return CL_G2API_SetSurfaceOnOff( VMA(1), (const char *)VMA(2), args[3]);
+ return CL_G2API_SetSurfaceOnOff( (void*)args[1], (const char *)VMA(2), args[3]);
case CG_G2_SETNEWORIGIN:
- return CL_G2API_SetNewOrigin( VMA(1), args[2]);
+ return CL_G2API_SetNewOrigin( (void*)args[1], args[2]);
case CG_G2_DOESBONEEXIST:
- return CL_G2API_DoesBoneExist( VMA(1), args[2], (const char *)VMA(3));
+ return CL_G2API_DoesBoneExist( (void*)args[1], args[2], (const char *)VMA(3));
case CG_G2_GETSURFACERENDERSTATUS:
- return CL_G2API_GetSurfaceRenderStatus( VMA(1), args[2], (const char *)VMA(3));
+ return CL_G2API_GetSurfaceRenderStatus( (void*)args[1], args[2], (const char *)VMA(3));
case CG_G2_GETTIME:
return CL_G2API_GetTime();
@@ -1598,48 +1773,48 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) {
return 0;
case CG_G2_ABSURDSMOOTHING:
- CL_G2API_AbsurdSmoothing( VMA(1), (qboolean)args[2]);
+ CL_G2API_AbsurdSmoothing( (void*)args[1], (qboolean)args[2]);
return 0;
case CG_G2_SETRAGDOLL:
- CL_G2API_SetRagDoll( VMA(1), (sharedRagDollParams_t *)VMA(2) );
+ CL_G2API_SetRagDoll( (void*)args[1], (sharedRagDollParams_t *)VMA(2) );
return 0;
case CG_G2_ANIMATEG2MODELS:
- CL_G2API_AnimateG2Models( VMA(1), args[2], (sharedRagDollUpdateParams_t *)VMA(3) );
+ CL_G2API_AnimateG2Models( (void*)args[1], args[2], (sharedRagDollUpdateParams_t *)VMA(3) );
return 0;
//additional ragdoll options -rww
case CG_G2_RAGPCJCONSTRAINT:
- return CL_G2API_RagPCJConstraint( VMA(1), (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4));
+ return CL_G2API_RagPCJConstraint( (void*)args[1], (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4));
case CG_G2_RAGPCJGRADIENTSPEED:
- return CL_G2API_RagPCJGradientSpeed( VMA(1), (const char *)VMA(2), VMF(3));
+ return CL_G2API_RagPCJGradientSpeed( (void*)args[1], (const char *)VMA(2), VMF(3));
case CG_G2_RAGEFFECTORGOAL:
- return CL_G2API_RagEffectorGoal( VMA(1), (const char *)VMA(2), (float *)VMA(3));
+ return CL_G2API_RagEffectorGoal( (void*)args[1], (const char *)VMA(2), (float *)VMA(3));
case CG_G2_GETRAGBONEPOS:
- return CL_G2API_GetRagBonePos( VMA(1), (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4), (float *)VMA(5), (float *)VMA(6));
+ return CL_G2API_GetRagBonePos( (void*)args[1], (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4), (float *)VMA(5), (float *)VMA(6));
case CG_G2_RAGEFFECTORKICK:
- return CL_G2API_RagEffectorKick( VMA(1), (const char *)VMA(2), (float *)VMA(3));
+ return CL_G2API_RagEffectorKick( (void*)args[1], (const char *)VMA(2), (float *)VMA(3));
case CG_G2_RAGFORCESOLVE:
- return CL_G2API_RagForceSolve( VMA(1), (qboolean)args[2]);
+ return CL_G2API_RagForceSolve( (void*)args[1], (qboolean)args[2]);
case CG_G2_SETBONEIKSTATE:
- return CL_G2API_SetBoneIKState( VMA(1), args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5));
+ return CL_G2API_SetBoneIKState( (void*)args[1], args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5));
case CG_G2_IKMOVE:
- return CL_G2API_IKMove( VMA(1), args[2], (sharedIKMoveParams_t *)VMA(3));
+ return CL_G2API_IKMove( (void*)args[1], args[2], (sharedIKMoveParams_t *)VMA(3));
case CG_G2_REMOVEBONE:
- return CL_G2API_RemoveBone( VMA(1), (const char *)VMA(2), args[3] );
+ return CL_G2API_RemoveBone( (void*)args[1], (const char *)VMA(2), args[3] );
case CG_G2_ATTACHINSTANCETOENTNUM:
- CL_G2API_AttachInstanceToEntNum( VMA(1), args[2], (qboolean)args[3]);
+ CL_G2API_AttachInstanceToEntNum( (void*)args[1], args[2], (qboolean)args[3]);
return 0;
case CG_G2_CLEARATTACHEDINSTANCE:
@@ -1651,10 +1826,10 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) {
return 0;
case CG_G2_OVERRIDESERVER:
- return CL_G2API_OverrideServer( VMA(1) );
+ return CL_G2API_OverrideServer( (void*)args[1] );
case CG_G2_GETSURFACENAME:
- CL_G2API_GetSurfaceName( VMA(1), args[2], args[3], (char *)VMA(4) );
+ CL_G2API_GetSurfaceName( (void*)args[1], args[2], args[3], (char *)VMA(4) );
return 0;
case CG_SP_GETSTRINGTEXTSTRING:
@@ -1765,7 +1940,7 @@ void CL_BindCGame( void ) {
cgi.R_AddDecalToScene = re->AddDecalToScene;
cgi.R_AddLightToScene = re->AddLightToScene;
cgi.R_AddPolysToScene = re->AddPolyToScene;
- cgi.R_AddRefEntityToScene = re->AddRefEntityToScene;
+ cgi.R_AddRefEntityToScene = CL_R_AddRefEntityToScene;
cgi.R_AnyLanguage_ReadCharFromString = re->AnyLanguage_ReadCharFromString;
cgi.R_AutomapElevationAdjustment = re->AutomapElevationAdjustment;
cgi.R_ClearDecals = re->ClearDecals;
diff --git a/codemp/client/cl_uiapi.cpp b/codemp/client/cl_uiapi.cpp
index 4ed911798b..92b9bd1561 100644
--- a/codemp/client/cl_uiapi.cpp
+++ b/codemp/client/cl_uiapi.cpp
@@ -28,6 +28,9 @@ along with this program; if not, see .
#include "snd_ambient.h"
#include "FXExport.h"
#include "FxUtil.h"
+#include "qcommon/vm_local.h"
+
+#include
extern IHeapAllocator *G2VertSpaceClient;
extern botlib_export_t *botlib_export;
@@ -36,6 +39,62 @@ extern botlib_export_t *botlib_export;
static uiExport_t *uie; // ui export table
static vm_t *uivm; // ui vm, valid for legacy and new api
+typedef std::unordered_map g2HandleToG2_m;
+
+static g2HandleToG2_m g2Mapping;
+static g2handle_t g2NextHandle = (g2handle_t)1; // Start at 1, because 0 has special meaning
+
+static inline CGhoul2Info_v *CL_UI_G2Map_GetG2FromHandle( g2handleptr_t g2h )
+{ // Returns the pointer to the g2 object if the handle is valid
+ // Native libraries should not use the pointer, but in theory they could use
+ // it. Thus we don't perform any mapping for native libraries to avoid
+ // issues with custom modules.
+ if ( uivm->dllHandle ) return (CGhoul2Info_v*)g2h;
+
+ g2handle_t g2handle = (g2handle_t)g2h;
+ g2HandleToG2_m::iterator ghlIt = g2Mapping.find(g2handle);
+
+ if (ghlIt == g2Mapping.end()) return NULL;
+ return g2Mapping[g2handle];
+}
+
+static inline CGhoul2Info_v **CL_UI_G2Map_GetG2PtrFromHandle( g2handleptr_t *g2h )
+{ // Returns a pointer to the g2 object pointer in the map so g2 functions can update the pointer
+ // Native libraries should not use the pointer, but in theory they could use
+ // it. Thus we don't perform any mapping for native libraries to avoid
+ // issues with custom modules.
+ if ( uivm->dllHandle ) return (CGhoul2Info_v **)g2h;
+
+ g2handle_t g2handle = *((g2handle_t*)g2h);
+ if ( !g2handle )
+ { // Special case: the g2 handle is not valid, yet. Return a pointer to a static temporary pointer. Insertion is handled by calling CL_UI_G2Map_Update after calling the G2API
+ static CGhoul2Info_v *g2Tmp;
+ g2Tmp = NULL;
+ return &g2Tmp;
+ }
+ return &g2Mapping[g2handle];
+}
+
+static void CL_UI_G2Map_Update( g2handleptr_t *g2h, CGhoul2Info_v *g2Ptr )
+{ // Inserts and/or erases to/from the map and updates the handle pointer
+ if ( uivm->dllHandle ) return;
+
+ g2handle_t *g2handle = (g2handle_t*)g2h;
+ if ( !*g2handle && g2Ptr )
+ { // Got a 0 handle, but a non-0 pointer: add to map and set handle
+ // Unlikely to happen, but should we ever cycle through the whole integer range start searching for gaps
+ while ( CL_UI_G2Map_GetG2FromHandle(g2NextHandle) || !g2NextHandle ) g2NextHandle++;
+
+ g2Mapping[g2NextHandle] = g2Ptr;
+ *g2handle = g2NextHandle++;
+ }
+ else if ( *g2h && !g2Ptr )
+ { // Got a non-0 handle, but 0 pointer: remove from map and set handle to 0
+ g2Mapping.erase( *g2handle );
+ *g2handle = 0;
+ }
+}
+
//
// ui vmMain calls
//
@@ -154,7 +213,42 @@ static void CL_GetClientState( uiClientState_t *state ) {
}
static void CL_GetGlconfig( glconfig_t *config ) {
- *config = cls.glconfig;
+ if ( uivm->dllHandle ) {
+ // For native libraries we can just copy the struct over cause they should be identical
+ *config = cls.glconfig;
+ } else {
+ // As the QVM is 32 bit internally the size of our glconfig_t is bigger on 64 bit, because of the 4 string
+ // pointers. Instead of just assigning the glconfig we have to manually copy it over.
+ glconfig_qvm_t *glconfig_qvm = (glconfig_qvm_t*)config;
+
+ // We now reserve additional memory when loading the QVM. The QVM shouldn't try to access it on its own, but it
+ // is valid inside the QVM and if we set a pointer to it the QVM should be able to access it. Thus we now claim
+ // some of this extra memory in QVM-scope to write our glconfig strings in there and tell the QVM where to find
+ // them.
+ // FIXME: It would probably be a good idea to keep a reference to this extra memory ourselves in case a module
+ // decides to call this functions multiple times. Currently we would claim additional memory each time.
+ glconfig_qvm->renderer_string = VM_PtrToOffset( uivm, VM_ExtraMemory_ClaimString( uivm, cls.glconfig.renderer_string ) );
+ glconfig_qvm->vendor_string = VM_PtrToOffset( uivm, VM_ExtraMemory_ClaimString( uivm, cls.glconfig.vendor_string ) );
+ glconfig_qvm->version_string = VM_PtrToOffset( uivm, VM_ExtraMemory_ClaimString( uivm, cls.glconfig.version_string ) );
+ glconfig_qvm->extensions_string = VM_PtrToOffset( uivm, VM_ExtraMemory_ClaimString( uivm, cls.glconfig.extensions_string ) );
+
+ // Assign the rest of the values
+ glconfig_qvm->maxTextureSize = cls.glconfig.maxTextureSize;
+ glconfig_qvm->maxActiveTextures = cls.glconfig.maxActiveTextures;
+ glconfig_qvm->maxTextureFilterAnisotropy = cls.glconfig.maxTextureFilterAnisotropy;
+ glconfig_qvm->colorBits = cls.glconfig.colorBits;
+ glconfig_qvm->depthBits = cls.glconfig.depthBits;
+ glconfig_qvm->stencilBits = cls.glconfig.stencilBits;
+ glconfig_qvm->deviceSupportsGamma = cls.glconfig.deviceSupportsGamma;
+ glconfig_qvm->textureCompression = cls.glconfig.textureCompression;
+ glconfig_qvm->textureEnvAddAvailable = cls.glconfig.textureEnvAddAvailable;
+ glconfig_qvm->clampToEdgeAvailable = cls.glconfig.clampToEdgeAvailable;
+ glconfig_qvm->vidWidth = cls.glconfig.vidWidth;
+ glconfig_qvm->vidHeight = cls.glconfig.vidHeight;
+ glconfig_qvm->displayFrequency = cls.glconfig.displayFrequency;
+ glconfig_qvm->isFullscreen = cls.glconfig.isFullscreen;
+ glconfig_qvm->stereoEnabled = cls.glconfig.stereoEnabled;
+ }
}
static void GetClipboardData( char *buf, int buflen ) {
@@ -236,6 +330,28 @@ static void CL_R_ShaderNameFromIndex( char *name, int index ) {
name[0] = '\0';
}
+void CL_R_AddRefEntityToScene( const refEntity_t *refEnt ) {
+ if ( uivm->dllHandle ) {
+ // We can directly pass the refEnt for native libraries
+ re->AddRefEntityToScene( refEnt );
+ return;
+ }
+
+ // The renderer should look up the handle on its own, but as we have to stay compatible with the modular renderer
+ // system we can't change the renderer for this and have to meet its expectations instead...
+
+ // Get a copy of the refEntity data. For a QVM mod the ghoul2 pointer at the end might contain some additional data,
+ // because the QVM stores it as 32 bit and if the engine is 64 bit it is going to read slightly out of bounds of the
+ // struct in the QVM, but CL_UI_G2Map_GetG2FromHandle should take care of that by casting.
+ refEntity_t refEntCopy = *refEnt;
+
+ // Get the ghoul2 instance from the handle
+ refEntCopy.ghoul2 = CL_UI_G2Map_GetG2FromHandle( (g2handleptr_t)refEnt->ghoul2 );
+
+ // Pass our copy
+ re->AddRefEntityToScene( &refEntCopy );
+}
+
static void CL_G2API_ListModelSurfaces( void *ghlInfo ) {
if ( !ghlInfo ) {
return;
@@ -257,7 +373,7 @@ static void CL_G2API_SetGhoul2ModelIndexes( void *ghoul2, qhandle_t *modelList,
return;
}
- re->G2API_SetGhoul2ModelIndexes( *((CGhoul2Info_v *)ghoul2), modelList, skinList );
+ re->G2API_SetGhoul2ModelIndexes( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelList, skinList );
}
static qboolean CL_G2API_HaveWeGhoul2Models( void *ghoul2) {
@@ -265,7 +381,7 @@ static qboolean CL_G2API_HaveWeGhoul2Models( void *ghoul2) {
return qfalse;
}
- return re->G2API_HaveWeGhoul2Models( *((CGhoul2Info_v *)ghoul2) );
+ return re->G2API_HaveWeGhoul2Models( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)) );
}
static qboolean CL_G2API_GetBoltMatrix( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
@@ -273,7 +389,7 @@ static qboolean CL_G2API_GetBoltMatrix( void *ghoul2, const int modelIndex, cons
return qfalse;
}
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static qboolean CL_G2API_GetBoltMatrix_NoReconstruct( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
@@ -282,7 +398,7 @@ static qboolean CL_G2API_GetBoltMatrix_NoReconstruct( void *ghoul2, const int mo
}
re->G2API_BoltMatrixReconstruction( qfalse );
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static qboolean CL_G2API_GetBoltMatrix_NoRecNoRot( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
@@ -292,7 +408,7 @@ static qboolean CL_G2API_GetBoltMatrix_NoRecNoRot( void *ghoul2, const int model
re->G2API_BoltMatrixReconstruction( qfalse );
re->G2API_BoltMatrixSPMethod( qtrue );
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static int CL_G2API_InitGhoul2Model( void **ghoul2Ptr, const char *fileName, int modelIndex, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias ) {
@@ -303,7 +419,10 @@ static int CL_G2API_InitGhoul2Model( void **ghoul2Ptr, const char *fileName, int
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- return re->G2API_InitGhoul2Model( (CGhoul2Info_v **)ghoul2Ptr, fileName, modelIndex, customSkin, customShader, modelFlags, lodBias );
+ CGhoul2Info_v **g2Ptr = CL_UI_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghoul2Ptr );
+ int ret = re->G2API_InitGhoul2Model( g2Ptr, fileName, modelIndex, customSkin, customShader, modelFlags, lodBias );
+ CL_UI_G2Map_Update( (g2handleptr_t*)ghoul2Ptr, *g2Ptr );
+ return ret;
}
static qboolean CL_G2API_SetSkin( void *ghoul2, int modelIndex, qhandle_t customSkin, qhandle_t renderSkin ) {
@@ -311,13 +430,13 @@ static qboolean CL_G2API_SetSkin( void *ghoul2, int modelIndex, qhandle_t custom
return qfalse;
}
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_SetSkin( g2, modelIndex, customSkin, renderSkin );
}
static void CL_G2API_CollisionDetect(
CollisionRecord_t *collRecMap,
- void* ghoul2,
+ void *ghoul2,
const vec3_t angles,
const vec3_t position,
int frameNumber,
@@ -335,7 +454,7 @@ static void CL_G2API_CollisionDetect(
re->G2API_CollisionDetect(
collRecMap,
- *((CGhoul2Info_v *)ghoul2),
+ *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)),
angles,
position,
frameNumber,
@@ -351,7 +470,7 @@ static void CL_G2API_CollisionDetect(
static void CL_G2API_CollisionDetectCache(
CollisionRecord_t *collRecMap,
- void* ghoul2,
+ void *ghoul2,
const vec3_t angles,
const vec3_t position,
int frameNumber,
@@ -369,7 +488,7 @@ static void CL_G2API_CollisionDetectCache(
re->G2API_CollisionDetectCache(
collRecMap,
- *((CGhoul2Info_v *)ghoul2),
+ *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)),
angles,
position,
frameNumber,
@@ -391,7 +510,10 @@ static void CL_G2API_CleanGhoul2Models( void **ghoul2Ptr ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- re->G2API_CleanGhoul2Models( (CGhoul2Info_v **)ghoul2Ptr );
+
+ CGhoul2Info_v **g2Ptr = CL_UI_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghoul2Ptr );
+ re->G2API_CleanGhoul2Models( g2Ptr );
+ CL_UI_G2Map_Update( (g2handleptr_t*)ghoul2Ptr, *g2Ptr );
}
static qboolean CL_G2API_SetBoneAngles(
@@ -412,7 +534,7 @@ static qboolean CL_G2API_SetBoneAngles(
}
return re->G2API_SetBoneAngles(
- *((CGhoul2Info_v *)ghoul2),
+ *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)),
modelIndex,
boneName,
angles,
@@ -442,7 +564,7 @@ static qboolean CL_G2API_SetBoneAnim(
}
return re->G2API_SetBoneAnim(
- *((CGhoul2Info_v *)ghoul2),
+ *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)),
modelIndex,
boneName,
startFrame,
@@ -470,7 +592,7 @@ static qboolean CL_G2API_GetBoneAnim(
return qfalse;
}
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_GetBoneAnim(
g2,
modelIndex,
@@ -496,7 +618,7 @@ static qboolean CL_G2API_GetBoneFrame(
return qfalse;
}
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
int iDontCare1 = 0, iDontCare2 = 0, iDontCare3 = 0;
float fDontCare1 = 0;
@@ -520,7 +642,7 @@ static void CL_G2API_GetGLAName( void *ghoul2, int modelIndex, char *fillBuf ) {
return;
}
- char *tmp = re->G2API_GetGLAName( *((CGhoul2Info_v *)ghoul2), modelIndex );
+ char *tmp = re->G2API_GetGLAName( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex );
if ( tmp )
strcpy( fillBuf, tmp );
else
@@ -532,7 +654,7 @@ static int CL_G2API_CopyGhoul2Instance( void *g2From, void *g2To, int modelIndex
return 0;
}
- return re->G2API_CopyGhoul2Instance( *((CGhoul2Info_v *)g2From), *((CGhoul2Info_v *)g2To), modelIndex );
+ return re->G2API_CopyGhoul2Instance( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)g2To)), modelIndex );
}
static void CL_G2API_CopySpecificGhoul2Model( void *g2From, int modelFrom, void *g2To, int modelTo ) {
@@ -540,7 +662,7 @@ static void CL_G2API_CopySpecificGhoul2Model( void *g2From, int modelFrom, void
return;
}
- re->G2API_CopySpecificG2Model( *((CGhoul2Info_v *)g2From), modelFrom, *((CGhoul2Info_v *)g2To), modelTo );
+ re->G2API_CopySpecificG2Model( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), modelFrom, *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)g2To)), modelTo );
}
static void CL_G2API_DuplicateGhoul2Instance( void *g2From, void **g2To ) {
@@ -551,7 +673,10 @@ static void CL_G2API_DuplicateGhoul2Instance( void *g2From, void **g2To ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- re->G2API_DuplicateGhoul2Instance( *((CGhoul2Info_v *)g2From), (CGhoul2Info_v **)g2To );
+
+ CGhoul2Info_v **g2ToPtr = CL_UI_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)g2To );
+ re->G2API_DuplicateGhoul2Instance( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), g2ToPtr );
+ CL_UI_G2Map_Update( (g2handleptr_t*)g2To, *g2ToPtr );
}
static qboolean CL_G2API_HasGhoul2ModelOnIndex( void *ghlInfo, int modelIndex ) {
@@ -559,7 +684,10 @@ static qboolean CL_G2API_HasGhoul2ModelOnIndex( void *ghlInfo, int modelIndex )
return qfalse;
}
- return re->G2API_HasGhoul2ModelOnIndex( (CGhoul2Info_v **)ghlInfo, modelIndex );
+ CGhoul2Info_v **g2Ptr = CL_UI_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_HasGhoul2ModelOnIndex( g2Ptr, modelIndex );
+ CL_UI_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static qboolean CL_G2API_RemoveGhoul2Model( void *ghlInfo, int modelIndex ) {
@@ -570,7 +698,11 @@ static qboolean CL_G2API_RemoveGhoul2Model( void *ghlInfo, int modelIndex ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- return re->G2API_RemoveGhoul2Model( (CGhoul2Info_v **)ghlInfo, modelIndex );
+
+ CGhoul2Info_v **g2Ptr = CL_UI_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_RemoveGhoul2Model( g2Ptr, modelIndex );
+ CL_UI_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static int CL_G2API_AddBolt( void *ghoul2, int modelIndex, const char *boneName ) {
@@ -578,7 +710,7 @@ static int CL_G2API_AddBolt( void *ghoul2, int modelIndex, const char *boneName
return -1;
}
- return re->G2API_AddBolt( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName );
+ return re->G2API_AddBolt( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName );
}
static void CL_G2API_SetBoltInfo( void *ghoul2, int modelIndex, int boltInfo ) {
@@ -586,7 +718,7 @@ static void CL_G2API_SetBoltInfo( void *ghoul2, int modelIndex, int boltInfo ) {
return;
}
- re->G2API_SetBoltInfo( *((CGhoul2Info_v *)ghoul2), modelIndex, boltInfo );
+ re->G2API_SetBoltInfo( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltInfo );
}
static qboolean CL_G2API_SetRootSurface( void *ghoul2, const int modelIndex, const char *surfaceName ) {
@@ -594,7 +726,7 @@ static qboolean CL_G2API_SetRootSurface( void *ghoul2, const int modelIndex, con
return qfalse;
}
- return re->G2API_SetRootSurface( *((CGhoul2Info_v *)ghoul2), modelIndex, surfaceName );
+ return re->G2API_SetRootSurface( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, surfaceName );
}
static qboolean CL_G2API_SetSurfaceOnOff( void *ghoul2, const char *surfaceName, const int flags ) {
@@ -602,7 +734,7 @@ static qboolean CL_G2API_SetSurfaceOnOff( void *ghoul2, const char *surfaceName,
return qfalse;
}
- return re->G2API_SetSurfaceOnOff( *((CGhoul2Info_v *)ghoul2), surfaceName, flags );
+ return re->G2API_SetSurfaceOnOff( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), surfaceName, flags );
}
static qboolean CL_G2API_SetNewOrigin( void *ghoul2, const int boltIndex ) {
@@ -610,7 +742,7 @@ static qboolean CL_G2API_SetNewOrigin( void *ghoul2, const int boltIndex ) {
return qfalse;
}
- return re->G2API_SetNewOrigin( *((CGhoul2Info_v *)ghoul2), boltIndex );
+ return re->G2API_SetNewOrigin( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boltIndex );
}
static int CL_G2API_GetTime( void ) {
@@ -629,7 +761,7 @@ static void CL_G2API_SetRagDoll( void *ghoul2, sharedRagDollParams_t *params ) {
CRagDollParams rdParams;
if ( !params ) {
- re->G2API_ResetRagDoll( *((CGhoul2Info_v *)ghoul2) );
+ re->G2API_ResetRagDoll( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)) );
return;
}
@@ -652,7 +784,7 @@ static void CL_G2API_SetRagDoll( void *ghoul2, sharedRagDollParams_t *params ) {
rdParams.RagPhase = (CRagDollParams::ERagPhase)params->RagPhase;
rdParams.effectorsToTurnOff = (CRagDollParams::ERagEffector)params->effectorsToTurnOff;
- re->G2API_SetRagDoll( *((CGhoul2Info_v *)ghoul2), &rdParams );
+ re->G2API_SetRagDoll( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), &rdParams );
}
static void CL_G2API_AnimateG2Models( void *ghoul2, int time, sharedRagDollUpdateParams_t *params ) {
@@ -673,7 +805,7 @@ static void CL_G2API_AnimateG2Models( void *ghoul2, int time, sharedRagDollUpdat
rduParams.me = params->me;
rduParams.settleFrame = params->settleFrame;
- re->G2API_AnimateG2ModelsRag( *((CGhoul2Info_v *)ghoul2), time, &rduParams );
+ re->G2API_AnimateG2ModelsRag( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, &rduParams );
}
static qboolean CL_G2API_SetBoneIKState( void *ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params ) {
@@ -681,7 +813,7 @@ static qboolean CL_G2API_SetBoneIKState( void *ghoul2, int time, const char *bon
return qfalse;
}
- return re->G2API_SetBoneIKState( *((CGhoul2Info_v *)ghoul2), time, boneName, ikState, params );
+ return re->G2API_SetBoneIKState( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, boneName, ikState, params );
}
static qboolean CL_G2API_IKMove( void *ghoul2, int time, sharedIKMoveParams_t *params ) {
@@ -689,7 +821,7 @@ static qboolean CL_G2API_IKMove( void *ghoul2, int time, sharedIKMoveParams_t *p
return qfalse;
}
- return re->G2API_IKMove( *((CGhoul2Info_v *)ghoul2), time, params );
+ return re->G2API_IKMove( *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, params );
}
static void CL_G2API_GetSurfaceName( void *ghoul2, int surfNumber, int modelIndex, char *fillBuf ) {
@@ -697,7 +829,7 @@ static void CL_G2API_GetSurfaceName( void *ghoul2, int surfNumber, int modelInde
return;
}
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
char *tmp = re->G2API_GetSurfaceName( g2, modelIndex, surfNumber );
strcpy( fillBuf, tmp );
}
@@ -707,8 +839,8 @@ static qboolean CL_G2API_AttachG2Model( void *ghoul2From, int modelIndexFrom, vo
return qfalse;
}
- CGhoul2Info_v *g2From = ((CGhoul2Info_v *)ghoul2From);
- CGhoul2Info_v *g2To = ((CGhoul2Info_v *)ghoul2To);
+ CGhoul2Info_v *g2From = (CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2From));
+ CGhoul2Info_v *g2To = (CL_UI_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2To));
return re->G2API_AttachG2Model(*g2From, modelIndexFrom, *g2To, toBoltIndex, toModel);
}
@@ -888,7 +1020,7 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
return 0;
case UI_R_ADDREFENTITYTOSCENE:
- re->AddRefEntityToScene( (const refEntity_t *)VMA(1) );
+ CL_R_AddRefEntityToScene( (refEntity_t*)VMA(1) );
return 0;
case UI_R_ADDPOLYTOSCENE:
@@ -1144,20 +1276,20 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
return 0;
case UI_G2_HAVEWEGHOULMODELS:
- return CL_G2API_HaveWeGhoul2Models( VMA(1) );
+ return CL_G2API_HaveWeGhoul2Models( (void*)args[1] );
case UI_G2_SETMODELS:
- CL_G2API_SetGhoul2ModelIndexes( VMA(1),(qhandle_t *)VMA(2),(qhandle_t *)VMA(3));
+ CL_G2API_SetGhoul2ModelIndexes( (void*)args[1],(qhandle_t *)VMA(2),(qhandle_t *)VMA(3));
return 0;
case UI_G2_GETBOLT:
- return CL_G2API_GetBoltMatrix(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return CL_G2API_GetBoltMatrix((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case UI_G2_GETBOLT_NOREC:
- return CL_G2API_GetBoltMatrix_NoReconstruct(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return CL_G2API_GetBoltMatrix_NoReconstruct((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case UI_G2_GETBOLT_NOREC_NOROT:
- return CL_G2API_GetBoltMatrix_NoRecNoRot(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return CL_G2API_GetBoltMatrix_NoRecNoRot((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case UI_G2_INITGHOUL2MODEL:
#ifdef _FULL_G2_LEAK_CHECKING
@@ -1170,7 +1302,7 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
return 0; //not supported for ui
case UI_G2_ANGLEOVERRIDE:
- return CL_G2API_SetBoneAngles(VMA(1), args[2], (const char *)VMA(3), (float *)VMA(4), args[5], (const Eorientations) args[6], (const Eorientations) args[7], (const Eorientations) args[8], (qhandle_t *)VMA(9), args[10], args[11] );
+ return CL_G2API_SetBoneAngles((void*)args[1], args[2], (const char *)VMA(3), (float *)VMA(4), args[5], (const Eorientations) args[6], (const Eorientations) args[7], (const Eorientations) args[8], (qhandle_t *)VMA(9), args[10], args[11] );
case UI_G2_CLEANMODELS:
#ifdef _FULL_G2_LEAK_CHECKING
@@ -1180,30 +1312,30 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
return 0;
case UI_G2_PLAYANIM:
- return CL_G2API_SetBoneAnim(VMA(1), args[2], (const char *)VMA(3), args[4], args[5], args[6], VMF(7), args[8], VMF(9), args[10]);
+ return CL_G2API_SetBoneAnim((void*)args[1], args[2], (const char *)VMA(3), args[4], args[5], args[6], VMF(7), args[8], VMF(9), args[10]);
case UI_G2_GETBONEANIM:
- return CL_G2API_GetBoneAnim(VMA(1), (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), (int *)VMA(6), (int *)VMA(7), (float *)VMA(8), (int *)VMA(9), args[10]);
+ return CL_G2API_GetBoneAnim((void*)args[1], (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), (int *)VMA(6), (int *)VMA(7), (float *)VMA(8), (int *)VMA(9), args[10]);
case UI_G2_GETBONEFRAME:
- return CL_G2API_GetBoneFrame(VMA(1), (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), args[6]);
+ return CL_G2API_GetBoneFrame((void*)args[1], (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), args[6]);
case UI_G2_GETGLANAME:
- CL_G2API_GetGLAName( VMA(1), args[2], (char *)VMA(3) );
+ CL_G2API_GetGLAName( (void*)args[1], args[2], (char *)VMA(3) );
return 0;
case UI_G2_COPYGHOUL2INSTANCE:
- return (int)CL_G2API_CopyGhoul2Instance(VMA(1), VMA(2), args[3]);
+ return (int)CL_G2API_CopyGhoul2Instance((void*)args[1], (void*)args[2], args[3]);
case UI_G2_COPYSPECIFICGHOUL2MODEL:
- CL_G2API_CopySpecificGhoul2Model(VMA(1), args[2], VMA(3), args[4]);
+ CL_G2API_CopySpecificGhoul2Model((void*)args[1], args[2], (void*)args[3], args[4]);
return 0;
case UI_G2_DUPLICATEGHOUL2INSTANCE:
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 0;
#endif
- CL_G2API_DuplicateGhoul2Instance(VMA(1), (void **)VMA(2));
+ CL_G2API_DuplicateGhoul2Instance((void*)args[1], (void **)VMA(2));
return 0;
case UI_G2_HASGHOUL2MODELONINDEX:
@@ -1216,20 +1348,20 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
return (int)CL_G2API_RemoveGhoul2Model(VMA(1), args[2]);
case UI_G2_ADDBOLT:
- return CL_G2API_AddBolt(VMA(1), args[2], (const char *)VMA(3));
+ return CL_G2API_AddBolt((void*)args[1], args[2], (const char *)VMA(3));
case UI_G2_SETBOLTON:
- CL_G2API_SetBoltInfo(VMA(1), args[2], args[3]);
+ CL_G2API_SetBoltInfo((void*)args[1], args[2], args[3]);
return 0;
case UI_G2_SETROOTSURFACE:
- return CL_G2API_SetRootSurface(VMA(1), args[2], (const char *)VMA(3));
+ return CL_G2API_SetRootSurface((void*)args[1], args[2], (const char *)VMA(3));
case UI_G2_SETSURFACEONOFF:
- return CL_G2API_SetSurfaceOnOff(VMA(1), (const char *)VMA(2), args[3]);
+ return CL_G2API_SetSurfaceOnOff((void*)args[1], (const char *)VMA(2), args[3]);
case UI_G2_SETNEWORIGIN:
- return CL_G2API_SetNewOrigin(VMA(1), args[2]);
+ return CL_G2API_SetNewOrigin((void*)args[1], args[2]);
case UI_G2_GETTIME:
return CL_G2API_GetTime();
@@ -1247,20 +1379,20 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
break;
case UI_G2_SETBONEIKSTATE:
- return CL_G2API_SetBoneIKState(VMA(1), args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5));
+ return CL_G2API_SetBoneIKState((void*)args[1], args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5));
case UI_G2_IKMOVE:
- return CL_G2API_IKMove(VMA(1), args[2], (sharedIKMoveParams_t *)VMA(3));
+ return CL_G2API_IKMove((void*)args[1], args[2], (sharedIKMoveParams_t *)VMA(3));
case UI_G2_GETSURFACENAME:
- CL_G2API_GetSurfaceName( (void *)args[1], args[2], args[3], (char *)VMA( 4 ) );
+ CL_G2API_GetSurfaceName( (void*)args[1], args[2], args[3], (char *)VMA( 4 ) );
return 0;
case UI_G2_SETSKIN:
- return CL_G2API_SetSkin(VMA(1), args[2], args[3], args[4]);
+ return CL_G2API_SetSkin((void*)args[1], args[2], args[3], args[4]);
case UI_G2_ATTACHG2MODEL:
- return CL_G2API_AttachG2Model(VMA(1), args[2], VMA(3), args[4], args[5]);
+ return CL_G2API_AttachG2Model((void*)args[1], args[2], (void*)args[3], args[4], args[5]);
default:
Com_Error( ERR_DROP, "Bad UI system trap: %ld", (long int) args[0] );
@@ -1370,7 +1502,7 @@ void CL_BindUI( void ) {
uii.R_AddLightToScene = re->AddLightToScene;
uii.R_AddPolysToScene = re->AddPolyToScene;
- uii.R_AddRefEntityToScene = re->AddRefEntityToScene;
+ uii.R_AddRefEntityToScene = CL_R_AddRefEntityToScene;
uii.R_ClearScene = re->ClearScene;
uii.R_DrawStretchPic = re->DrawStretchPic;
uii.R_Font_DrawString = re->Font_DrawString;
diff --git a/codemp/game/g_public.h b/codemp/game/g_public.h
index d90e350978..6caaf26215 100644
--- a/codemp/game/g_public.h
+++ b/codemp/game/g_public.h
@@ -254,6 +254,86 @@ typedef struct sharedEntity_s {
int next_roff_time; //rww - npc's need to know when they're getting roff'd
} sharedEntity_t;
+typedef struct sharedEntity_qvm_s {
+ entityState_t s; // communicated by server to clients
+ uint32_t playerState; //needs to be in the gentity for bg entity access
+ //if you want to actually see the contents I guess
+ //you will have to be sure to VMA it first.
+ uint32_t m_pVehicle; //vehicle data
+ uint32_t ghoul2; //g2 instance
+ int localAnimIndex; //index locally (game/cgame) to anim data for this skel
+ vec3_t modelScale; //needed for g2 collision
+
+ //from here up must also be unified with bgEntity/centity
+
+ entityShared_t r; // shared by both the server system and game
+
+ //Script/ICARUS-related fields
+ int taskID[NUM_TIDS];
+ uint32_t parms;
+ uint32_t behaviorSet[NUM_BSETS];
+ uint32_t script_targetname;
+ int delayScriptTime;
+ uint32_t fullName;
+
+ //rww - targetname and classname are now shared as well. ICARUS needs access to them.
+ uint32_t targetname;
+ uint32_t classname; // set in QuakeEd
+
+ //rww - and yet more things to share. This is because the nav code is in the exe because it's all C++.
+ int waypoint; //Set once per frame, if you've moved, and if someone asks
+ int lastWaypoint; //To make sure you don't double-back
+ int lastValidWaypoint; //ALWAYS valid -used for tracking someone you lost
+ int noWaypointTime; //Debouncer - so don't keep checking every waypoint in existance every frame that you can't find one
+ int combatPoint;
+ int failedWaypoints[MAX_FAILED_NODES];
+ int failedWaypointCheckTime;
+
+ int next_roff_time; //rww - npc's need to know when they're getting roff'd
+} sharedEntity_qvm_t;
+
+typedef struct sharedEntityMapper_s {
+ entityState_t *s; // communicated by server to clients
+ playerState_t **playerState; //needs to be in the gentity for bg entity access
+ //if you want to actually see the contents I guess
+ //you will have to be sure to VMA it first.
+#if (!defined(MACOS_X) && !defined(__GCC__) && !defined(__GNUC__))
+ Vehicle_t **m_pVehicle; //vehicle data
+#else
+ struct Vehicle_s **m_pVehicle; //vehicle data
+#endif
+ void **ghoul2; //g2 instance
+ int *localAnimIndex; //index locally (game/cgame) to anim data for this skel
+ vec3_t *modelScale; //needed for g2 collision
+
+ //from here up must also be unified with bgEntity/centity
+
+ entityShared_t *r; // shared by both the server system and game
+
+ //Script/ICARUS-related fields
+ int (*taskID)[NUM_TIDS];
+ parms_t **parms;
+ char **behaviorSet[NUM_BSETS];
+ char **script_targetname;
+ int *delayScriptTime;
+ char **fullName;
+
+ //rww - targetname and classname are now shared as well. ICARUS needs access to them.
+ char **targetname;
+ char **classname; // set in QuakeEd
+
+ //rww - and yet more things to share. This is because the nav code is in the exe because it's all C++.
+ int *waypoint; //Set once per frame, if you've moved, and if someone asks
+ int *lastWaypoint; //To make sure you don't double-back
+ int *lastValidWaypoint; //ALWAYS valid -used for tracking someone you lost
+ int *noWaypointTime; //Debouncer - so don't keep checking every waypoint in existance every frame that you can't find one
+ int *combatPoint;
+ int (*failedWaypoints)[MAX_FAILED_NODES];
+ int *failedWaypointCheckTime;
+
+ int *next_roff_time; //rww - npc's need to know when they're getting roff'd
+} sharedEntityMapper_t;
+
#if !defined(_GAME) && defined(__cplusplus)
class CSequencer;
class CTaskManager;
diff --git a/codemp/icarus/GameInterface.cpp b/codemp/icarus/GameInterface.cpp
index d32b2bb964..0376cc5742 100644
--- a/codemp/icarus/GameInterface.cpp
+++ b/codemp/icarus/GameInterface.cpp
@@ -30,6 +30,7 @@ along with this program; if not, see .
#include "qcommon/RoffSystem.h"
#include "Q3_Interface.h"
#include "server/sv_gameapi.h"
+#include "qcommon/vm_local.h"
ICARUS_Instance *iICARUS;
bufferlist_t ICARUS_BufferList;
@@ -84,13 +85,13 @@ ICARUS_RunScript
Runs the script by the given name
=============
*/
-int ICARUS_RunScript( sharedEntity_t *ent, const char *name )
+int ICARUS_RunScript( sharedEntityMapper_t *ent, const char *name )
{
char *buf;
int len;
//Make sure the caller is valid
- if ( gSequencers[ent->s.number] == NULL )
+ if ( gSequencers[ent->s->number] == NULL )
{
//Com_Printf( "%s : entity is not a valid script user\n", ent->classname );
return false;
@@ -139,12 +140,12 @@ int ICARUS_RunScript( sharedEntity_t *ent, const char *name )
}
//Attempt to run the script
- if S_FAILED(gSequencers[ent->s.number]->Run( buf, len ))
+ if S_FAILED(gSequencers[ent->s->number]->Run( buf, len ))
return false;
- if ( ( ICARUS_entFilter == -1 ) || ( ICARUS_entFilter == ent->s.number ) )
+ if ( ( ICARUS_entFilter == -1 ) || ( ICARUS_entFilter == ent->s->number ) )
{
- Q3_DebugPrint( WL_VERBOSE, "%d Script %s executed by %s %s\n", svs.time, (char *) name, ent->classname, ent->targetname );
+ Q3_DebugPrint( WL_VERBOSE, "%d Script %s executed by %s %s\n", svs.time, (char *) name, SV_EntityMapperReadString(ent->classname), SV_EntityMapperReadString(ent->targetname) );
}
return true;
@@ -184,19 +185,19 @@ Frees up ICARUS resources from all entities
void ICARUS_Shutdown( void )
{
bufferlist_t::iterator ei;
- sharedEntity_t *ent = SV_GentityNum(0);
+ sharedEntityMapper_t *ent = SV_GentityMapperNum(0);
//Release all ICARUS resources from the entities
for ( int i = 0; i < /*globals.num_entities*/MAX_GENTITIES; i++ )
{
- ent = SV_GentityNum(i);
+ ent = SV_GentityMapperNum(i);
if (gSequencers[i])
{
- if (ent->s.number >= MAX_GENTITIES ||
- ent->s.number < 0)
+ if (ent->s->number >= MAX_GENTITIES ||
+ ent->s->number < 0)
{
- ent->s.number = i;
+ ent->s->number = i;
assert(0);
}
ICARUS_FreeEnt( ent );
@@ -235,27 +236,29 @@ FIXME: shouldn't ICARUS handle this internally?
==============
*/
-void ICARUS_FreeEnt( sharedEntity_t *ent )
+void ICARUS_FreeEnt( sharedEntityMapper_t *ent )
{
+ const char *script_targetname;
assert( iICARUS );
- if (ent->s.number >= MAX_GENTITIES ||
- ent->s.number < 0)
+ if (ent->s->number >= MAX_GENTITIES ||
+ ent->s->number < 0)
{
assert(0);
return;
}
//Make sure the ent is valid
- if ( gSequencers[ent->s.number] == NULL )
+ if ( gSequencers[ent->s->number] == NULL )
return;
//Remove them from the ICARUSE_EntList list so that when their g_entity index is reused, ICARUS doesn't try to affect the new (incorrect) ent.
- if VALIDSTRING( ent->script_targetname )
+ script_targetname = SV_EntityMapperReadString( ent->script_targetname );
+ if VALIDSTRING( script_targetname )
{
char temp[1024];
- strncpy( (char *) temp, ent->script_targetname, 1023 );
+ strncpy( (char *) temp, script_targetname, 1023 );
temp[ 1023 ] = 0;
entlist_t::iterator it = ICARUS_EntList.find( Q_strupr(temp) );
@@ -267,11 +270,11 @@ void ICARUS_FreeEnt( sharedEntity_t *ent )
}
//Delete the sequencer and the task manager
- iICARUS->DeleteSequencer( gSequencers[ent->s.number] );
+ iICARUS->DeleteSequencer( gSequencers[ent->s->number] );
//Clean up the pointers
- gSequencers[ent->s.number] = NULL;
- gTaskManagers[ent->s.number] = NULL;
+ gSequencers[ent->s->number] = NULL;
+ gTaskManagers[ent->s->number] = NULL;
}
@@ -283,18 +286,19 @@ Determines whether or not an entity needs ICARUS information
==============
*/
-bool ICARUS_ValidEnt( sharedEntity_t *ent )
+bool ICARUS_ValidEnt( sharedEntityMapper_t *ent )
{
+ const char *script_targetname = SV_EntityMapperReadString( ent->script_targetname );
int i;
//Targeted by a script
- if VALIDSTRING( ent->script_targetname )
+ if VALIDSTRING( script_targetname )
return true;
//Potentially able to call a script
for ( i = 0; i < NUM_BSETS; i++ )
{
- if VALIDSTRING( ent->behaviorSet[i] )
+ if VALIDSTRING( SV_EntityMapperReadString(ent->behaviorSet[i]) )
{
//Com_Printf( "WARNING: Entity %d (%s) has behaviorSet but no script_targetname -- using targetname\n", ent->s.number, ent->targetname );
@@ -302,11 +306,17 @@ bool ICARUS_ValidEnt( sharedEntity_t *ent )
//rww - You CANNOT do things like this now. We're switching memory around to be able to read this memory from vm land,
//and while this allows us to read it on our "fake" entity here, we can't modify pointers like this. We can however do
//something completely hackish such as the following.
- assert(ent->s.number >= 0 && ent->s.number < MAX_GENTITIES);
- sharedEntity_t *trueEntity = SV_GentityNum(ent->s.number);
+ assert(ent->s->number >= 0 && ent->s->number < MAX_GENTITIES);
+ sharedEntity_t *trueEntity = SV_GentityNum(ent->s->number);
+
//This works because we're modifying the actual shared game vm data and turning one pointer into another.
//While these pointers both look like garbage to us in here, they are not.
- trueEntity->script_targetname = trueEntity->targetname;
+ if ( VM_IsCurrentQVM() )
+ {
+ sharedEntity_qvm_t *trueEntityQVM = (sharedEntity_qvm_t*)trueEntity;
+ trueEntityQVM->script_targetname = trueEntityQVM->targetname;
+ }
+ else trueEntity->script_targetname = trueEntity->targetname;
return true;
}
}
@@ -322,17 +332,18 @@ Associate the entity's id and name so that it can be referenced later
==============
*/
-void ICARUS_AssociateEnt( sharedEntity_t *ent )
+void ICARUS_AssociateEnt( sharedEntityMapper_t *ent )
{
+ const char *script_targetname = SV_EntityMapperReadString( ent->script_targetname );
char temp[1024];
- if ( VALIDSTRING( ent->script_targetname ) == false )
+ if ( VALIDSTRING( script_targetname ) == false )
return;
- strncpy( (char *) temp, ent->script_targetname, 1023 );
+ strncpy( (char *) temp, script_targetname, 1023 );
temp[ 1023 ] = 0;
- ICARUS_EntList[ Q_strupr( (char *) temp ) ] = ent->s.number;
+ ICARUS_EntList[ Q_strupr( (char *) temp ) ] = ent->s->number;
}
/*
@@ -628,19 +639,20 @@ Precache all scripts being used by the entity
==============
*/
-void ICARUS_PrecacheEnt( sharedEntity_t *ent )
+void ICARUS_PrecacheEnt( sharedEntityMapper_t *ent )
{
+ const char *behaviorStr;
char newname[MAX_FILENAME_LENGTH];
int i;
for ( i = 0; i < NUM_BSETS; i++ )
{
- if ( ent->behaviorSet[i] == NULL )
+ if ( !(behaviorStr = SV_EntityMapperReadString(ent->behaviorSet[i])) )
continue;
- if ( GetIDForString( BSTable, ent->behaviorSet[i] ) == -1 )
+ if ( GetIDForString( BSTable, behaviorStr ) == -1 )
{//not a behavior set
- Com_sprintf( newname, sizeof(newname), "%s/%s", Q3_SCRIPT_DIR, ent->behaviorSet[i] );
+ Com_sprintf( newname, sizeof(newname), "%s/%s", Q3_SCRIPT_DIR, behaviorStr );
//Precache this, and all internally referenced scripts
ICARUS_InterrogateScript( newname );
@@ -657,25 +669,25 @@ Allocates a sequencer and task manager only if an entity is a potential script u
*/
void Q3_TaskIDClear( int *taskID );
-void ICARUS_InitEnt( sharedEntity_t *ent )
+void ICARUS_InitEnt( sharedEntityMapper_t *ent )
{
//Make sure this is a fresh ent
assert( iICARUS );
- assert( gTaskManagers[ent->s.number] == NULL );
- assert( gSequencers[ent->s.number] == NULL );
+ assert( gTaskManagers[ent->s->number] == NULL );
+ assert( gSequencers[ent->s->number] == NULL );
- if ( gSequencers[ent->s.number] != NULL )
+ if ( gSequencers[ent->s->number] != NULL )
return;
- if ( gTaskManagers[ent->s.number] != NULL )
+ if ( gTaskManagers[ent->s->number] != NULL )
return;
//Create the sequencer and setup the task manager
- gSequencers[ent->s.number] = iICARUS->GetSequencer( ent->s.number );
- gTaskManagers[ent->s.number] = gSequencers[ent->s.number]->GetTaskManager();
+ gSequencers[ent->s->number] = iICARUS->GetSequencer( ent->s->number );
+ gTaskManagers[ent->s->number] = gSequencers[ent->s->number]->GetTaskManager();
//Initialize all taskIDs to -1
- memset( &ent->taskID, -1, sizeof( ent->taskID ) );
+ memset( ent->taskID, -1, sizeof( *(ent->taskID) ) );
//Add this entity to a map of valid associated ents for quick retrieval later
ICARUS_AssociateEnt( ent );
@@ -692,13 +704,13 @@ ICARUS_LinkEntity
int ICARUS_LinkEntity( int entID, CSequencer *sequencer, CTaskManager *taskManager )
{
- sharedEntity_t *ent = SV_GentityNum(entID);
+ sharedEntityMapper_t *ent = SV_GentityMapperNum(entID);
if ( ent == NULL )
return false;
- gSequencers[ent->s.number] = sequencer;
- gTaskManagers[ent->s.number] = taskManager;
+ gSequencers[ent->s->number] = sequencer;
+ gTaskManagers[ent->s->number] = taskManager;
ICARUS_AssociateEnt( ent );
@@ -722,9 +734,9 @@ void Svcmd_ICARUS_f( void )
//g_ICARUSDebug->integer = WL_DEBUG;
if ( VALIDSTRING( Cmd_Argv( 2 ) ) )
{
- sharedEntity_t *ent = G_Find( NULL, FOFS( script_targetname ), gi.argv(2) );
+ sharedEntityMapper_t *ent = G_Find( NULL, FOFS( script_targetname ), gi.argv(2) );
- if ( ent == NULL )
+ if ( *ent == NULL )
{
Com_Printf( "Entity \"%s\" not found!\n", gi.argv(2) );
return;
@@ -733,7 +745,7 @@ void Svcmd_ICARUS_f( void )
//Start logging
Com_Printf("Logging ICARUS info for entity %s\n", gi.argv(2) );
- ICARUS_entFilter = ( ent->s.number == ICARUS_entFilter ) ? -1 : ent->s.number;
+ ICARUS_entFilter = ( ent->s->number == ICARUS_entFilter ) ? -1 : ent->s->number;
return;
}
diff --git a/codemp/icarus/GameInterface.h b/codemp/icarus/GameInterface.h
index 666fd6c035..68c45ddf78 100644
--- a/codemp/icarus/GameInterface.h
+++ b/codemp/icarus/GameInterface.h
@@ -38,7 +38,7 @@ typedef std::map < std::string, pscript_t* > bufferlist_t;
extern interface_export_t interface_export;
extern void Interface_Init( interface_export_t *pe );
-extern int ICARUS_RunScript( sharedEntity_t *ent, const char *name );
+extern int ICARUS_RunScript( sharedEntityMapper_t *ent, const char *name );
extern bool ICARUS_RegisterScript( const char *name, qboolean bCalledDuringInterrogate = qfalse);
extern ICARUS_Instance *iICARUS;
extern bufferlist_t ICARUS_BufferList;
@@ -48,10 +48,10 @@ extern entlist_t ICARUS_EntList;
// g_ICARUS.cpp
//
void ICARUS_Init( void );
-bool ICARUS_ValidEnt( sharedEntity_t *ent );
-void ICARUS_InitEnt( sharedEntity_t *ent );
-void ICARUS_FreeEnt( sharedEntity_t *ent );
-void ICARUS_AssociateEnt( sharedEntity_t *ent );
+bool ICARUS_ValidEnt( sharedEntityMapper_t *ent );
+void ICARUS_InitEnt( sharedEntityMapper_t *ent );
+void ICARUS_FreeEnt( sharedEntityMapper_t *ent );
+void ICARUS_AssociateEnt( sharedEntityMapper_t *ent );
void ICARUS_Shutdown( void );
void Svcmd_ICARUS_f( void );
diff --git a/codemp/icarus/Q3_Interface.cpp b/codemp/icarus/Q3_Interface.cpp
index 059d65dc46..d9d52682a9 100644
--- a/codemp/icarus/Q3_Interface.cpp
+++ b/codemp/icarus/Q3_Interface.cpp
@@ -121,12 +121,12 @@ void Q3_TaskIDClear( int *taskID )
/*
-------------------------
-qboolean Q3_TaskIDPending( sharedEntity_t *ent, taskID_t taskType )
+qboolean Q3_TaskIDPending( sharedEntityMapper_t *ent, taskID_t taskType )
-------------------------
*/
-qboolean Q3_TaskIDPending( sharedEntity_t *ent, taskID_t taskType )
+qboolean Q3_TaskIDPending( sharedEntityMapper_t *ent, taskID_t taskType )
{
- if ( !gSequencers[ent->s.number] || !gTaskManagers[ent->s.number] )
+ if ( !gSequencers[ent->s->number] || !gTaskManagers[ent->s->number] )
{
return qfalse;
}
@@ -136,7 +136,7 @@ qboolean Q3_TaskIDPending( sharedEntity_t *ent, taskID_t taskType )
return qfalse;
}
- if ( ent->taskID[taskType] >= 0 )//-1 is none
+ if ( (*(ent->taskID))[taskType] >= 0 )//-1 is none
{
return qtrue;
}
@@ -146,43 +146,43 @@ qboolean Q3_TaskIDPending( sharedEntity_t *ent, taskID_t taskType )
/*
-------------------------
-void Q3_TaskIDComplete( sharedEntity_t *ent, taskID_t taskType )
+void Q3_TaskIDComplete( sharedEntityMapper_t *ent, taskID_t taskType )
-------------------------
*/
-void Q3_TaskIDComplete( sharedEntity_t *ent, taskID_t taskType )
+void Q3_TaskIDComplete( sharedEntityMapper_t *ent, taskID_t taskType )
{
if ( taskType < TID_CHAN_VOICE || taskType >= NUM_TIDS )
{
return;
}
- if ( gTaskManagers[ent->s.number] && Q3_TaskIDPending( ent, taskType ) )
+ if ( gTaskManagers[ent->s->number] && Q3_TaskIDPending( ent, taskType ) )
{//Complete it
- gTaskManagers[ent->s.number]->Completed( ent->taskID[taskType] );
+ gTaskManagers[ent->s->number]->Completed( (*(ent->taskID))[taskType] );
//See if any other tasks have the name number and clear them so we don't complete more than once
- int clearTask = ent->taskID[taskType];
+ int clearTask = (*(ent->taskID))[taskType];
for ( int tid = 0; tid < NUM_TIDS; tid++ )
{
- if ( ent->taskID[tid] == clearTask )
+ if ( (*(ent->taskID))[tid] == clearTask )
{
- Q3_TaskIDClear( &ent->taskID[tid] );
+ Q3_TaskIDClear( &((*(ent->taskID))[tid]) );
}
}
//clear it - should be cleared in for loop above
- //Q3_TaskIDClear( &ent->taskID[taskType] );
+ //Q3_TaskIDClear( &((*(ent->taskID)))[taskType] );
}
//otherwise, wasn't waiting for a task to complete anyway
}
/*
-------------------------
-void Q3_SetTaskID( sharedEntity_t *ent, taskID_t taskType, int taskID )
+void Q3_SetTaskID( sharedEntityMapper_t *ent, taskID_t taskType, int taskID )
-------------------------
*/
-void Q3_TaskIDSet( sharedEntity_t *ent, taskID_t taskType, int taskID )
+void Q3_TaskIDSet( sharedEntityMapper_t *ent, taskID_t taskType, int taskID )
{
if ( taskType < TID_CHAN_VOICE || taskType >= NUM_TIDS )
{
@@ -192,7 +192,7 @@ void Q3_TaskIDSet( sharedEntity_t *ent, taskID_t taskType, int taskID )
//Might be stomping an old task, so complete and clear previous task if there was one
Q3_TaskIDComplete( ent, taskType );
- ent->taskID[taskType] = taskID;
+ (*(ent->taskID))[taskType] = taskID;
}
@@ -236,9 +236,9 @@ Q3_GetEntityByName
Returns the sequencer of the entity by the given name
=============
*/
-static sharedEntity_t *Q3_GetEntityByName( const char *name )
+static sharedEntityMapper_t *Q3_GetEntityByName( const char *name )
{
- sharedEntity_t *ent;
+ sharedEntityMapper_t *ent;
entlist_t::iterator ei;
char temp[1024];
@@ -253,13 +253,13 @@ static sharedEntity_t *Q3_GetEntityByName( const char *name )
if ( ei == ICARUS_EntList.end() )
return NULL;
- ent = SV_GentityNum((*ei).second);
+ ent = SV_GentityMapperNum((*ei).second);
return ent;
// this now returns the ent instead of the sequencer -- dmv 06/27/01
// if (ent == NULL)
// return NULL;
-// return gSequencers[ent->s.number];
+// return gSequencers[ent->s->number];
}
/*
@@ -694,7 +694,7 @@ void Q3_DebugPrint( int level, const char *format, ... )
if ( ( entNum < 0 ) || ( entNum >= MAX_GENTITIES ) )
entNum = 0;
- Com_Printf ( S_COLOR_BLUE"DEBUG: %s(%d): %s\n", SV_GentityNum(entNum)->script_targetname, entNum, buffer );
+ Com_Printf ( S_COLOR_BLUE"DEBUG: %s(%d): %s\n", SV_EntityMapperReadString(SV_GentityMapperNum(entNum)->script_targetname), entNum, buffer );
break;
}
default:
diff --git a/codemp/icarus/Q3_Interface.h b/codemp/icarus/Q3_Interface.h
index a8df673de2..8534744a1a 100644
--- a/codemp/icarus/Q3_Interface.h
+++ b/codemp/icarus/Q3_Interface.h
@@ -296,8 +296,8 @@ extern char cinematicSkipScript[1024];
//General
extern void Q3_TaskIDClear( int *taskID );
-extern qboolean Q3_TaskIDPending( sharedEntity_t *ent, taskID_t taskType );
-extern void Q3_TaskIDComplete( sharedEntity_t *ent, taskID_t taskType );
+extern qboolean Q3_TaskIDPending( sharedEntityMapper_t *ent, taskID_t taskType );
+extern void Q3_TaskIDComplete( sharedEntityMapper_t *ent, taskID_t taskType );
extern void Q3_DPrintf( const char *, ... );
extern void Q3_CameraRoll( float angle, float duration );
diff --git a/codemp/icarus/Sequencer.cpp b/codemp/icarus/Sequencer.cpp
index 6a21194b82..eedc1f66cd 100644
--- a/codemp/icarus/Sequencer.cpp
+++ b/codemp/icarus/Sequencer.cpp
@@ -618,7 +618,7 @@ int CSequencer::ParseAffect( CBlock *block, bstream_t *bstream )
CSequencer *stream_sequencer = NULL;
char *entname = NULL;
int ret;
- sharedEntity_t *ent = 0;
+ sharedEntityMapper_t *ent = 0;
entname = (char*) block->GetMemberData( 0 );
ent = m_ie->I_GetEntityByName( entname );
@@ -700,7 +700,7 @@ int CSequencer::ParseAffect( CBlock *block, bstream_t *bstream )
if( ent )
{
- stream_sequencer = gSequencers[ent->s.number];//ent->sequencer;
+ stream_sequencer = gSequencers[ent->s->number];//ent->sequencer;
}
if (stream_sequencer == NULL)
@@ -1735,7 +1735,7 @@ Checks for affect command pre-processing
void CSequencer::CheckAffect( CBlock **command )
{
CBlock *block = *command;
- sharedEntity_t *ent = NULL;
+ sharedEntityMapper_t *ent = NULL;
char *entname = NULL;
int memberNum = 0;
@@ -1821,7 +1821,7 @@ void CSequencer::CheckAffect( CBlock **command )
if( ent )
{
- sequencer = gSequencers[ent->s.number];//ent->sequencer;
+ sequencer = gSequencers[ent->s->number];//ent->sequencer;
}
if(memberNum == 0)
{ //there was no get, increment manually before next step
@@ -1856,7 +1856,7 @@ void CSequencer::CheckAffect( CBlock **command )
if( ent )
{ // ents need to update upon being affected
//ent->taskManager->Update();
- gTaskManagers[ent->s.number]->Update();
+ gTaskManagers[ent->s->number]->Update();
}
return;
@@ -1888,7 +1888,7 @@ void CSequencer::CheckAffect( CBlock **command )
if( ent )
{ // ents need to update upon being affected
//ent->taskManager->Update();
- gTaskManagers[ent->s.number]->Update();
+ gTaskManagers[ent->s->number]->Update();
}
}
diff --git a/codemp/icarus/TaskManager.cpp b/codemp/icarus/TaskManager.cpp
index ec0b1bea44..f5c5829f67 100644
--- a/codemp/icarus/TaskManager.cpp
+++ b/codemp/icarus/TaskManager.cpp
@@ -336,9 +336,9 @@ Update
int CTaskManager::Update( void )
{
- sharedEntity_t *owner = SV_GentityNum(m_ownerID);
+ sharedEntityMapper_t *owner = SV_GentityMapperNum(m_ownerID);
- if ( (owner->r.svFlags&SVF_ICARUS_FREEZE) )
+ if ( (owner->r->svFlags&SVF_ICARUS_FREEZE) )
{
return TASK_FAILED;
}
diff --git a/codemp/icarus/interface.h b/codemp/icarus/interface.h
index 4bfbba5fde..05b270267c 100644
--- a/codemp/icarus/interface.h
+++ b/codemp/icarus/interface.h
@@ -35,7 +35,7 @@ typedef struct interface_export_s
int (*I_LoadFile)( const char *name, void **buf );
void (*I_CenterPrint)( const char *format, ... );
void (*I_DPrintf)( int, const char *, ... );
- sharedEntity_t *(*I_GetEntityByName)( const char *name ); //Polls the engine for the sequencer of the entity matching the name passed
+ sharedEntityMapper_t *(*I_GetEntityByName)( const char *name ); //Polls the engine for the sequencer of the entity matching the name passed
unsigned int (*I_GetTime)( void ); //Gets the current time
unsigned int (*I_GetTimeScale)(void );
int (*I_PlaySound)( int taskID, int entID, const char *name, const char *channel );
diff --git a/codemp/qcommon/RoffSystem.cpp b/codemp/qcommon/RoffSystem.cpp
index 753eb75c84..a77b9f3a38 100644
--- a/codemp/qcommon/RoffSystem.cpp
+++ b/codemp/qcommon/RoffSystem.cpp
@@ -624,17 +624,17 @@ qboolean CROFFSystem::List( int id )
//---------------------------------------------------------------------------
qboolean CROFFSystem::Play( int entID, int id, qboolean doTranslation, qboolean isClient )
{
- sharedEntity_t *ent = NULL;
+ sharedEntityMapper_t *ent = NULL;
if ( !isClient )
{
- ent = SV_GentityNum( entID );
+ ent = SV_GentityMapperNum( entID );
if ( ent == NULL )
{ // shame on you..
return qfalse;
}
- ent->r.mIsRoffing = qtrue;
+ ent->r->mIsRoffing = qtrue;
/*rjr if(ent->GetPhysics() == PHYSICS_TYPE_NONE)
{
@@ -655,7 +655,7 @@ qboolean CROFFSystem::Play( int entID, int id, qboolean doTranslation, qboolean
roffing_ent->mIsClient = isClient;
if ( !isClient )
- VectorCopy(ent->s.apos.trBase, roffing_ent->mStartAngles);
+ VectorCopy(ent->s->apos.trBase, roffing_ent->mStartAngles);
mROFFEntList.push_back( roffing_ent );
@@ -859,7 +859,7 @@ void CROFFSystem::UpdateEntities(qboolean isClient)
qboolean CROFFSystem::ApplyROFF( SROFFEntity *roff_ent, CROFFSystem::CROFF *roff )
{
vec3_t f, r, u, result;
- sharedEntity_t *ent = NULL;
+ sharedEntityMapper_t *ent = NULL;
trajectory_t *originTrajectory = NULL, *angleTrajectory = NULL;
float *origin = NULL, *angle = NULL;
@@ -884,17 +884,17 @@ qboolean CROFFSystem::ApplyROFF( SROFFEntity *roff_ent, CROFFSystem::CROFF *roff
#endif
{
// Find the entity to apply the roff to
- ent = SV_GentityNum( roff_ent->mEntID );
+ ent = SV_GentityMapperNum( roff_ent->mEntID );
if ( ent == 0 )
{ // bad stuff
return qfalse;
}
- originTrajectory = &ent->s.pos;
- angleTrajectory = &ent->s.apos;
- origin = ent->r.currentOrigin;
- angle = ent->r.currentAngles;
+ originTrajectory = &ent->s->pos;
+ angleTrajectory = &ent->s->apos;
+ origin = ent->r->currentOrigin;
+ angle = ent->r->currentAngles;
}
@@ -904,7 +904,7 @@ qboolean CROFFSystem::ApplyROFF( SROFFEntity *roff_ent, CROFFSystem::CROFF *roff
SetLerp( angleTrajectory, TR_STATIONARY, angle, NULL, sv.time, roff->mLerp );
if (!roff_ent->mIsClient)
{
- ent->r.mIsRoffing = qfalse;
+ ent->r->mIsRoffing = qfalse;
}
return qfalse;
}
@@ -944,7 +944,7 @@ qboolean CROFFSystem::ApplyROFF( SROFFEntity *roff_ent, CROFFSystem::CROFF *roff
//rww - npcs need to know when they're getting roff'd
if ( !roff_ent->mIsClient )
- ent->next_roff_time = roff_ent->mNextROFFTime;
+ *(ent->next_roff_time) = roff_ent->mNextROFFTime;
return qtrue;
@@ -1012,7 +1012,7 @@ void CROFFSystem::ProcessNote(SROFFEntity *roff_ent, char *note)
//---------------------------------------------------------------------------
qboolean CROFFSystem::ClearLerp( SROFFEntity *roff_ent )
{
- sharedEntity_t *ent = NULL;
+ sharedEntityMapper_t *ent = NULL;
trajectory_t *originTrajectory = NULL, *angleTrajectory = NULL;
float *origin = NULL, *angle = NULL;
@@ -1031,17 +1031,17 @@ qboolean CROFFSystem::ClearLerp( SROFFEntity *roff_ent )
#endif
{
// Find the entity to apply the roff to
- ent = SV_GentityNum( roff_ent->mEntID );
+ ent = SV_GentityMapperNum( roff_ent->mEntID );
if ( ent == 0 )
{ // bad stuff
return qfalse;
}
- originTrajectory = &ent->s.pos;
- angleTrajectory = &ent->s.apos;
- origin = ent->r.currentOrigin;
- angle = ent->r.currentAngles;
+ originTrajectory = &ent->s->pos;
+ angleTrajectory = &ent->s->apos;
+ origin = ent->r->currentOrigin;
+ angle = ent->r->currentAngles;
}
SetLerp( originTrajectory, TR_STATIONARY, origin, NULL, sv.time, ROFF_SAMPLE_RATE );
diff --git a/codemp/qcommon/common.cpp b/codemp/qcommon/common.cpp
index d4609a1fdd..f98c4583e9 100644
--- a/codemp/qcommon/common.cpp
+++ b/codemp/qcommon/common.cpp
@@ -34,6 +34,9 @@ along with this program; if not, see .
#define WIN32_LEAN_AND_MEAN
#include
#endif
+#include
+
+static jmp_buf abortframe;
FILE *debuglogfile;
fileHandle_t logfile;
@@ -43,6 +46,7 @@ fileHandle_t com_journalDataFile; // config files are written here
cvar_t *com_speeds;
cvar_t *com_developer;
cvar_t *com_dedicated;
+cvar_t *com_dedicatedForceErrorsToFatal;
cvar_t *com_timescale;
cvar_t *com_fixedtime;
cvar_t *com_journal;
@@ -294,7 +298,7 @@ void NORETURN QDECL Com_Error( int code, const char *fmt, ... ) {
// ERR_DROPs on dedicated drop to an interactive console
// which doesn't make sense for dedicated as it's generally
// run unattended
- if ( com_dedicated && com_dedicated->integer ) {
+ if ( com_dedicated && com_dedicated->integer && com_dedicatedForceErrorsToFatal && com_dedicatedForceErrorsToFatal->integer ) {
code = ERR_FATAL;
}
@@ -319,7 +323,7 @@ void NORETURN QDECL Com_Error( int code, const char *fmt, ... ) {
}
if ( code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT || code == ERR_DROP || code == ERR_NEED_CD ) {
- throw code;
+ longjmp(abortframe, code+1); // +1 to avoid 0 value
} else {
CL_Shutdown ();
SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage));
@@ -1156,10 +1160,17 @@ Com_Init
void Com_Init( char *commandLine ) {
char *s;
int qport;
+ int errCode;
Com_Printf( "%s %s %s\n", JK_VERSION, PLATFORM_STRING, SOURCE_DATE );
- try
+ if ( (errCode = setjmp(abortframe)) )
+ {
+ errCode--;
+ Com_CatchError( errCode );
+ Sys_Error( "Error during initialization: %s", Com_ErrorString(errCode)) ;
+ }
+ else
{
// initialize the weak pseudo-random number generator for use later.
Com_InitRand();
@@ -1225,6 +1236,7 @@ void Com_Init( char *commandLine ) {
#ifdef DEDICATED
com_dedicated = Cvar_Get ("dedicated", "2", CVAR_INIT);
Cvar_CheckRange( com_dedicated, 1, 2, qtrue );
+ com_dedicatedForceErrorsToFatal = Cvar_Get( "com_dedicatedForceErrorsToFatal", "1", CVAR_ARCHIVE );
#else
//OJKFIXME: Temporarily disabled dedicated server when not using the dedicated server binary.
// Issue is the server not having a renderer when not using ^^^^^
@@ -1328,11 +1340,6 @@ void Com_Init( char *commandLine ) {
com_fullyInitialized = qtrue;
Com_Printf ("--- Common Initialization Complete ---\n");
}
- catch ( int code )
- {
- Com_CatchError (code);
- Sys_Error ("Error during initialization: %s", Com_ErrorString (code));
- }
}
//==================================================================
@@ -1495,8 +1502,15 @@ Com_Frame
=================
*/
void Com_Frame( void ) {
-
- try
+ int errCode;
+ if ( (errCode = setjmp(abortframe)) )
+ {
+ errCode--;
+ Com_CatchError( errCode );
+ Com_Printf ("%s\n", Com_ErrorString(errCode) );
+ return;
+ }
+ else
{
#ifdef G2_PERFORMANCE_ANALYSIS
G2PerformanceTimer_PreciseFrame.Start();
@@ -1673,11 +1687,6 @@ void Com_Frame( void ) {
com_frameNumber++;
}
- catch (int code) {
- Com_CatchError (code);
- Com_Printf ("%s\n", Com_ErrorString (code));
- return;
- }
#ifdef G2_PERFORMANCE_ANALYSIS
G2Time_PreciseFrame += G2PerformanceTimer_PreciseFrame.End();
diff --git a/codemp/qcommon/files.cpp b/codemp/qcommon/files.cpp
index 9529217d75..0d12e336e4 100644
--- a/codemp/qcommon/files.cpp
+++ b/codemp/qcommon/files.cpp
@@ -314,18 +314,77 @@ FILE* missingFiles = NULL;
# endif
#endif
-const char *get_filename(const char *path) {
- const char *slash = strrchr(path, PATH_SEP);
- if (!slash || slash == path) return "";
- return slash + 1;
-}
-
const char *get_filename_ext(const char *filename) {
const char *dot = strrchr(filename, '.');
if (!dot || dot == filename) return "";
return dot + 1;
}
+// We don't want VMs to be able to access the following file extensions
+static char *invalidExtensions[] = {
+ "dll",
+ "exe",
+ "bat",
+ "cmd",
+ "dylib",
+ "so",
+ "qvm",
+ "pk3",
+};
+static int invalidExtensionsAmount = sizeof(invalidExtensions) / sizeof(invalidExtensions[0]);
+
+qboolean FS_IsInvalidExtension( const char *ext ) {
+ int i;
+
+ // Compare against the list of forbidden extensions
+ for ( i = 0; i < invalidExtensionsAmount; i++ ) {
+ if ( !Q_stricmp(ext, invalidExtensions[i]) )
+ return qtrue;
+ }
+
+ // Additional check we don't currently need, but if support for another platform is added and the list above is not
+ // updated we catch it with this check.
+ if ( !Q_stricmp(va(".%s", ext), DLL_EXT) )
+ return qtrue;
+
+ return qfalse;
+}
+
+// Invalid characters. Originally intended to be OS-specific, but considering that VMs run on different systems it's
+// probably not a bad idea to share the same behavior on all systems.
+qboolean FS_ContainsInvalidCharacters( const char *filename ) {
+ static char *invalidCharacters = "<>:\"|?*";
+ char *ptr = invalidCharacters;
+
+ while ( *ptr ) {
+ if ( strchr(filename, *ptr) )
+ return qtrue;
+ ptr++;
+ }
+
+ return qfalse;
+}
+
+qboolean FS_IsInvalidWriteOSPath(const char *ospath) {
+ const char *resolved;
+ const char *realPath;
+
+ // Resolve paths
+ resolved = Sys_ResolvePath( ospath );
+ if ( !strlen(resolved) ) return qtrue;
+ realPath = Sys_RealPath( resolved );
+ if ( !strlen(realPath) ) return qtrue;
+
+ // Check all three, the original input, the resolved and the real path, in case there is a symlink
+ // NOTE: Checking the ospath shouldn't be required, because we resolved the path, but let's check it anyway.
+ if ( FS_IsInvalidExtension(get_filename_ext(ospath)) || FS_IsInvalidExtension(get_filename_ext(resolved)) || FS_IsInvalidExtension(get_filename_ext(realPath)) ) {
+ Com_Printf( "FS_IsInvalidWriteOSPath: blocked writing binary file \"%s\" (%s) [%s].\n", ospath, resolved, realPath );
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
/*
==============
FS_Initialized
@@ -586,11 +645,10 @@ ERR_FATAL if trying to maniuplate a file with the platform library, or pk3 exten
*/
static void FS_CheckFilenameIsMutable( const char *filename, const char *function )
{
- // Check if the filename ends with the library, or pk3 extension
- if( COM_CompareExtension( filename, DLL_EXT )
- || COM_CompareExtension( filename, ".pk3" ) )
+ // Call FS_InvalidWriteOSPath from JK2MV, because it handles symlinks and some Windows specific tricks to bypass these checks.
+ if ( FS_IsInvalidWriteOSPath(filename) )
{
- Com_Error( ERR_FATAL, "%s: Not allowed to manipulate '%s' due "
+ Com_Error( ERR_DROP, "%s: Not allowed to manipulate '%s' due "
"to %s extension", function, filename, COM_GetExtension( filename ) );
}
}
@@ -607,6 +665,11 @@ void FS_CopyFile( char *fromOSPath, char *toOSPath ) {
int len;
byte *buf;
+ if ( FS_ContainsInvalidCharacters(toOSPath) ) {
+ Com_Printf( "FS_CopyFile: invalid filename (%s)\n", toOSPath );
+ return;
+ }
+
FS_CheckFilenameIsMutable( fromOSPath, __func__ );
Com_Printf( "copy %s to %s\n", fromOSPath, toOSPath );
@@ -4149,6 +4212,13 @@ int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ) {
sync = qfalse;
+ // Only check the unresolved path for invalid characters, the os probably knows what it's doing
+ if ( FS_ContainsInvalidCharacters(qpath) ) {
+ Com_Printf( "FS_FOpenFileByMode: invalid filename (%s)\n", qpath );
+ *f = 0;
+ return -1;
+ }
+
switch( mode ) {
case FS_READ:
r = FS_FOpenFileRead( qpath, f, qtrue );
diff --git a/codemp/qcommon/msg.cpp b/codemp/qcommon/msg.cpp
index 79d511e00e..ffe71b70a9 100644
--- a/codemp/qcommon/msg.cpp
+++ b/codemp/qcommon/msg.cpp
@@ -1207,7 +1207,7 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
print = 1;
if (sv.state)
{
- Com_Printf( "%3i: #%-3i (%s) ", msg->readcount, number, SV_GentityNum(number)->classname );
+ Com_Printf( "%3i: #%-3i (%s) ", msg->readcount, number, SV_EntityMapperReadString(SV_GentityMapperNum(number)->classname) );
}
else
{
diff --git a/codemp/qcommon/q_shared.h b/codemp/qcommon/q_shared.h
index 526ede7d25..86c862f7d5 100644
--- a/codemp/qcommon/q_shared.h
+++ b/codemp/qcommon/q_shared.h
@@ -147,7 +147,8 @@ typedef union fileBuffer_u {
byte *b;
} fileBuffer_t;
-typedef int32_t qhandle_t, thandle_t, fxHandle_t, sfxHandle_t, fileHandle_t, clipHandle_t;
+typedef int32_t qhandle_t, thandle_t, fxHandle_t, sfxHandle_t, fileHandle_t, clipHandle_t, g2handle_t;
+typedef intptr_t g2handleptr_t;
#define NULL_HANDLE ((qhandle_t)0)
#define NULL_SOUND ((sfxHandle_t)0)
diff --git a/codemp/qcommon/qcommon.h b/codemp/qcommon/qcommon.h
index 92d6724f35..953a70c686 100644
--- a/codemp/qcommon/qcommon.h
+++ b/codemp/qcommon/qcommon.h
@@ -270,7 +270,18 @@ typedef enum vmSlots_e {
MAX_VM
} vmSlots_t;
-typedef struct vm_s {
+typedef struct vmSymbol_s {
+ struct vmSymbol_s *next;
+ struct vmSymbol_s *caller;
+ int symValue;
+ int symInstr;
+ long profileCount;
+ int callCount;
+ char symName[1]; // variable sized
+} vmSymbol_t;
+
+typedef struct vm_s vm_t;
+struct vm_s {
vmSlots_t slot; // VM_GAME, VM_CGAME, VM_UI
char name[MAX_QPATH];
void *dllHandle;
@@ -284,7 +295,41 @@ typedef struct vm_s {
VMMainProc* main; // module vmMain
intptr_t (QDECL *syscall)( intptr_t *parms ); // engine syscall handler
} legacy;
-} vm_t;
+
+ // QVM stuff
+ int programStack; // the vm may be recursively entered
+ void (*destroy)(vm_t* self);
+ qboolean currentlyInterpreting;
+
+ qboolean compiled;
+ byte *codeBase;
+ int entryOfs;
+ int callProcOfs;
+ int callProcOfsSyscall;
+ int codeLength;
+
+ intptr_t *instructionPointers;
+ int instructionCount;
+
+ byte *dataBase;
+ int dataMask;
+
+ int stackBottom; // if programStack < stackBottom, error
+
+ int numSymbols;
+ vmSymbol_t *symbols;
+ vmSymbol_t **symbolTable;
+
+ int callLevel; // counts recursive VM_Call
+ int breakFunction; // increment breakCount on function entry to this
+ int breakCount;
+
+ byte *jumpTableTargets;
+ int numJumpTableTargets;
+
+ int extraMemOffset;
+ int extraMemUse;
+};
extern vm_t *currentVM;
@@ -613,7 +658,7 @@ fileHandle_t FS_SV_FOpenFileWrite( const char *filename );
fileHandle_t FS_SV_FOpenFileAppend( const char *filename );
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp );
void FS_SV_Rename( const char *from, const char *to, qboolean safe );
-long FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE );
+long FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE);
// if uniqueFILE is true, then a new FILE will be fopened even if the file
// is found in an already open pak file. If uniqueFILE is false, you must call
// FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed
@@ -774,6 +819,7 @@ void Com_StartupVariable( const char *match );
extern cvar_t *com_developer;
extern cvar_t *com_dedicated;
+extern cvar_t *com_dedicatedForceErrorsToFatal;
extern cvar_t *com_speeds;
extern cvar_t *com_timescale;
extern cvar_t *com_sv_running;
diff --git a/codemp/qcommon/vm.cpp b/codemp/qcommon/vm.cpp
index 11aa3ee558..a6f10523e5 100644
--- a/codemp/qcommon/vm.cpp
+++ b/codemp/qcommon/vm.cpp
@@ -26,6 +26,7 @@ along with this program; if not, see .
#include
#include "qcommon/qcommon.h"
+#include "vm_local.h"
vm_t *currentVM = NULL;
@@ -41,6 +42,8 @@ const char *vmStrs[MAX_VM] = {
"UIVM",
};
+cvar_t *vmModeCvar[MAX_VM];
+
// VM slots are automatically allocated by VM_Create, and freed by VM_Free
// The VM table should never be directly accessed from other files.
// Example usage:
@@ -52,14 +55,17 @@ const char *vmStrs[MAX_VM] = {
static vm_t *vmTable[MAX_VM];
-#ifdef _DEBUG
cvar_t *vm_legacy;
-#endif
void VM_Init( void ) {
-#ifdef _DEBUG
vm_legacy = Cvar_Get( "vm_legacy", "0", 0 );
-#endif
+
+ vmModeCvar[VM_CGAME] = Cvar_Get( "vm_cgame", "2", CVAR_ARCHIVE );
+ vmModeCvar[VM_GAME] = Cvar_Get( "vm_game", "2", CVAR_ARCHIVE );
+ vmModeCvar[VM_UI] = Cvar_Get( "vm_ui", "2", CVAR_ARCHIVE );
+
+ Cmd_AddCommand ("vmprofile", VM_VmProfile_f );
+ Cmd_AddCommand ("vminfo", VM_VmInfo_f );
memset( vmTable, 0, sizeof(vmTable) );
}
@@ -111,8 +117,123 @@ vm_t *VM_Restart( vm_t *vm ) {
return VM_Create( saved.slot );
}
+
+/*
+=================
+VM_LoadQVM
+
+Load a .qvm file
+=================
+*/
+vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc, qboolean freeVM )
+{
+ int dataLength;
+ int i;
+ char filename[MAX_QPATH];
+ union {
+ vmHeader_t *h;
+ void *v;
+ } header;
+
+ // load the image
+ Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
+ Com_Printf( "Loading vm file %s...\n", filename );
+
+ FS_ReadFile(filename, &header.v);
+
+ if ( !header.h ) {
+ Com_Printf( "Failed.\n" );
+ if ( freeVM ) VM_Free( vm );
+
+ Com_Printf(S_COLOR_YELLOW "Warning: Couldn't open VM file %s\n", filename);
+
+ return NULL;
+ }
+
+ // show where the qvm was loaded from
+ //FS_Which(filename, vm->searchPath);
+
+ if( LittleLong( header.h->vmMagic ) == VM_MAGIC ) {
+ // byte swap the header
+ // sizeof( vmHeader_t ) - sizeof( int ) is the 1.32b vm header size
+ for ( size_t i = 0 ; i < ( sizeof( vmHeader_t ) - sizeof( int ) ) / 4 ; i++ ) {
+ ((int *)header.h)[i] = LittleLong( ((int *)header.h)[i] );
+ }
+
+ // validate
+ if ( header.h->bssLength < 0
+ || header.h->dataLength < 0
+ || header.h->litLength < 0
+ || header.h->codeLength <= 0 )
+ {
+ if ( freeVM ) VM_Free(vm);
+ FS_FreeFile(header.v);
+
+ Com_Printf(S_COLOR_YELLOW "Warning: %s has bad header\n", filename);
+ return NULL;
+ }
+ } else {
+ if ( freeVM ) VM_Free( vm );
+ FS_FreeFile(header.v);
+
+ Com_Printf(S_COLOR_YELLOW "Warning: %s does not have a recognisable "
+ "magic number in its header\n", filename);
+ return NULL;
+ }
+
+ // round up to next power of 2 so all data operations can
+ // be mask protected
+ dataLength = header.h->dataLength + header.h->litLength +
+ header.h->bssLength;
+
+ // Allocate more memory to have room for additional data to be added within the QVM's scope
+ vm->extraMemOffset = dataLength - PROGRAM_STACK_SIZE;
+ vm->extraMemUse = 0;
+ dataLength += QVM_EXTRA_MEMORY_AMOUNT;
+
+ for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
+ }
+ dataLength = 1 << i;
+
+ if(alloc)
+ {
+ // allocate zero filled space for initialized and uninitialized data
+ vm->dataBase = (byte *)Hunk_Alloc(dataLength, h_high);
+ vm->dataMask = dataLength - 1;
+ }
+ else
+ {
+ // clear the data, but make sure we're not clearing more than allocated
+ if(vm->dataMask + 1 != dataLength)
+ {
+ if ( freeVM ) VM_Free(vm);
+ FS_FreeFile(header.v);
+
+ Com_Printf(S_COLOR_YELLOW "Warning: Data region size of %s not matching after "
+ "VM_Restart()\n", filename);
+ return NULL;
+ }
+
+ Com_Memset(vm->dataBase, 0, dataLength);
+ }
+
+ // copy the intialized data
+ Com_Memcpy( vm->dataBase, (byte *)header.h + header.h->dataOffset,
+ header.h->dataLength + header.h->litLength );
+
+ // byte swap the longs
+ for ( i = 0 ; i < header.h->dataLength ; i += 4 ) {
+ *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
+ }
+
+ return header.h;
+}
+
vm_t *VM_CreateLegacy( vmSlots_t vmSlot, intptr_t( *systemCalls )(intptr_t *) ) {
+ vmHeader_t *header;
vm_t *vm = NULL;
+ int remaining = Hunk_MemoryRemaining();
+ int interpret = vmModeCvar[vmSlot]->integer;
if ( !systemCalls ) {
Com_Error( ERR_FATAL, "VM_CreateLegacy: bad parms" );
@@ -133,6 +254,49 @@ vm_t *VM_CreateLegacy( vmSlots_t vmSlot, intptr_t( *systemCalls )(intptr_t *) )
Q_strncpyz( vm->name, vmNames[vmSlot], sizeof(vm->name) );
vm->legacy.syscall = systemCalls;
+ // QVM
+ if ( interpret != VMI_NATIVE && (header = VM_LoadQVM(vm, qtrue, qfalse)) ) {
+ // allocate space for the jump targets, which will be filled in by the compile/prep functions
+ vm->instructionCount = header->instructionCount;
+ vm->instructionPointers = (intptr_t *)Hunk_Alloc(vm->instructionCount * sizeof(*vm->instructionPointers), h_high);
+
+ // copy or compile the instructions
+ vm->codeLength = header->codeLength;
+
+ vm->compiled = qfalse;
+
+ #ifdef NO_VM_COMPILED
+ if(interpret >= VMI_COMPILED) {
+ Com_Printf("Architecture doesn't have a bytecode compiler, using interpreter\n");
+ interpret = VMI_BYTECODE;
+ }
+ #else
+ if(interpret != VMI_BYTECODE)
+ {
+ vm->compiled = qtrue;
+ VM_Compile( vm, header );
+ }
+ #endif
+ // VM_Compile may have reset vm->compiled if compilation failed
+ if (!vm->compiled)
+ {
+ VM_PrepareInterpreter( vm, header );
+ }
+
+ // free the original file
+ FS_FreeFile( header );
+
+ // load the map file
+ VM_LoadSymbols( vm );
+
+ // the stack is implicitly at the end of the image
+ vm->programStack = vm->dataMask + 1;
+ vm->stackBottom = vm->programStack - PROGRAM_STACK_SIZE;
+
+ Com_Printf("%s loaded in %d bytes on the hunk\n", vmNames[vmSlot], remaining - Hunk_MemoryRemaining());
+ return vm;
+ }
+
// find the legacy syscall api
FS_FindPureDLL( vm->name );
vm->dllHandle = Sys_LoadLegacyGameDll( vm->name, &vm->legacy.main, VM_DllSyscall );
@@ -154,15 +318,18 @@ vm_t *VM_CreateLegacy( vmSlots_t vmSlot, intptr_t( *systemCalls )(intptr_t *) )
vm_t *VM_Create( vmSlots_t vmSlot ) {
vm_t *vm = NULL;
-#ifdef _DEBUG
if ( (vm_legacy->integer & (1<integer != VMI_NATIVE ) {
+ // If the module shouldn't be a native library return and have the caller try the legacy system, because QVMs are part of that
+ return NULL;
+ }
+
// find a free vm
vmTable[vmSlot] = (vm_t *)Z_Malloc( sizeof(*vm), TAG_VM, qtrue );
vm = vmTable[vmSlot];
@@ -261,18 +428,26 @@ void *VM_ArgPtr( intptr_t intValue ) {
if ( !currentVM )
return NULL;
- return (void *)intValue;
+ if ( currentVM->dllHandle ) {
+ return (void *)intValue;
+ } else {
+ return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask));
+ }
}
void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ) {
if ( !intValue )
return NULL;
- // currentVM is missing on reconnect here as well?
- if ( !currentVM )
+ // vm is missing on reconnect here as well?
+ if ( !vm )
return NULL;
- return (void *)intValue;
+ if ( vm->dllHandle ) {
+ return (void *)intValue;
+ } else {
+ return (void *)(vm->dataBase + (intValue & vm->dataMask));
+ }
}
float _vmf( intptr_t x ) {
@@ -289,6 +464,731 @@ intptr_t QDECL VM_Call( vm_t *vm, int callnum, intptr_t arg0, intptr_t arg1, int
VMSwap v( vm );
- return vm->legacy.main( callnum, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11 );
+ if ( vm->dllHandle ) {
+ return vm->legacy.main( callnum, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11 );
+ } else {
+ intptr_t r;
+ if ( vm_debugLevel ) {
+ Com_Printf( "VM_Call( %d )\n", callnum );
+ }
+
+ ++vm->callLevel;
+
+#if ( id386 || idsparc ) && !defined __clang__ // calling convention doesn't need conversion in some cases
+#ifndef NO_VM_COMPILED
+ if ( vm->compiled )
+ r = VM_CallCompiled( vm, (int*)&callnum );
+ else
+#endif
+ r = VM_CallInterpreted( vm, (int*)&callnum );
+#else
+ struct {
+ int callnum;
+ int args[MAX_VMMAIN_ARGS-1];
+ } a;
+
+ a.callnum = callnum;
+ a.args[0] = arg0;
+ a.args[1] = arg1;
+ a.args[2] = arg2;
+ a.args[3] = arg3;
+ a.args[4] = arg4;
+ a.args[5] = arg5;
+ a.args[6] = arg6;
+ a.args[7] = arg7;
+ a.args[8] = arg8;
+ a.args[9] = arg9;
+ a.args[10] = arg10;
+ a.args[11] = arg11;
+#ifndef NO_VM_COMPILED
+ if ( vm->compiled )
+ r = VM_CallCompiled( vm, &a.callnum );
+ else
+#endif
+ r = VM_CallInterpreted( vm, &a.callnum );
+#endif
+ --vm->callLevel;
+
+ return r;
+ }
+}
+
+// QVM functions
+int vm_debugLevel;
+void VM_Debug( int level ) {
+ vm_debugLevel = level;
+}
+
+qboolean vm_profileInclusive;
+static vmSymbol_t nullSymbol;
+
+/*
+===============
+VM_ValueToInstr
+
+Calculates QVM instruction number for program counter value
+===============
+*/
+static int VM_ValueToInstr( vm_t *vm, int value ) {
+ intptr_t instrOffs = vm->instructionPointers[0] - vm->entryOfs;
+
+ for (int i = 0; i < vm->instructionCount; i++) {
+ if (vm->instructionPointers[i] - instrOffs > value) {
+ return i - 1;
+ }
+ }
+
+ return vm->instructionCount;
+}
+
+/*
+===============
+VM_ValueToSymbol
+
+Assumes a program counter value
+===============
+*/
+const char *VM_ValueToSymbol( vm_t *vm, int value ) {
+ static char text[MAX_TOKEN_CHARS];
+ vmSymbol_t *sym;
+
+ sym = VM_ValueToFunctionSymbol( vm, value );
+
+ if ( sym == &nullSymbol ) {
+ Com_sprintf( text, sizeof( text ), "%s(vmMain+%d) [%#x]",
+ vm->name, VM_ValueToInstr( vm, value ), value );
+ return text;
+ }
+
+ // predefined helper routines
+ if (value < vm->entryOfs) {
+ Com_sprintf( text, sizeof( text ), "%s(%s+%#x) [%#x]",
+ vm->name, sym->symName, value - sym->symValue, value );
+ return text;
+ }
+
+ Com_sprintf( text, sizeof( text ), "%s(%s+%d) [%#x]", vm->name, sym->symName,
+ VM_ValueToInstr( vm, value ) - sym->symInstr, value );
+
+ return text;
+}
+
+/*
+===============
+VM_ValueToFunctionSymbol
+
+For profiling, find the symbol behind this value
+===============
+*/
+vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) {
+ if ( (unsigned)vm->codeLength <= (unsigned)value ) {
+ return &nullSymbol;
+ }
+
+ if ( vm->symbolTable ) {
+ return vm->symbolTable[value];
+ }
+
+ if ( vm->symbols ) {
+ vmSymbol_t *sym;
+
+ for ( sym = vm->symbols; sym->next; sym = sym->next ) {
+ if ( sym->next->symValue > value && value >= sym->symValue ) {
+ return sym;
+ }
+ }
+ if ( sym->symValue <= value ) {
+ return sym;
+ }
+ }
+
+ return &nullSymbol;
+}
+
+
+/*
+===============
+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;
+}
+
+
+/*
+=====================
+VM_SymbolForCompiledPointer
+=====================
+*/
+const char *VM_SymbolForCompiledPointer( void *code ) {
+ for ( int i = 0; i < MAX_VM; i++ ) {
+ vm_t *vm = vmTable[i];
+
+ if ( vm && vm->compiled ) {
+ if ( vm->codeBase <= code && code < vm->codeBase + vm->codeLength ) {
+ return VM_ValueToSymbol( vm, (byte *)code - vm->codeBase );
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+===============
+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;
+}
+
+static void VM_GeneratePerfMap(vm_t *vm) {
+#ifdef __linux__
+ // generate perf .map file for profiling compiled QVM
+ char mapFile[MAX_OSPATH];
+ FILE *f;
+ int length;
+
+ if ( !vm->symbols ) {
+ return;
+ }
+
+ Com_sprintf( mapFile, sizeof(mapFile), "/tmp/perf-%d.map", Sys_PID() );
+
+ if ( (f = fopen( mapFile, "a" )) == NULL ) {
+ Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open %s\n", mapFile );
+ return;
+ }
+
+ // perf .map format: "hex_address hex_length name\n"
+ for ( vmSymbol_t *sym = vm->symbols; sym; sym = sym->next ) {
+
+ if ( sym->next ) {
+ length = sym->next->symValue - sym->symValue;
+ } else {
+ length = vm->codeLength - sym->symValue;
+ }
+
+ fprintf( f, "%lx %x %s\n", (unsigned long)(vm->codeBase + sym->symValue), length, sym->symName );
+ }
+
+ fclose( f );
+#endif // __linux__
+}
+
+/*
+===============
+VM_LoadSymbols
+===============
+*/
+void VM_LoadSymbols( vm_t *vm ) {
+ union {
+ char *c;
+ void *v;
+ } mapfile;
+ char name[MAX_QPATH];
+ char symbols[MAX_QPATH];
+
+ if ( vm->dllHandle ) {
+ return;
+ }
+
+ //
+ // add symbols for vm_x86 predefined procedures
+ //
+
+ vmSymbol_t **prev, *sym;
+
+ prev = &vm->symbols;
+
+ if ( vm->callProcOfs ) {
+ const char *symName;
+
+ symName = "CallDoSyscall";
+ sym = *prev = (vmSymbol_t *)Hunk_Alloc( sizeof( *sym ) + strlen( symName ), h_high );
+ Q_strncpyz( sym->symName, symName, strlen( symName ) + 1 );
+ sym->symValue = 0;
+
+ symName = "CallProcedure";
+ sym = sym->next = (vmSymbol_t *)Hunk_Alloc( sizeof( *sym ) + strlen( symName ), h_high );
+ Q_strncpyz( sym->symName, symName, strlen( symName ) + 1 );
+ sym->symValue = vm->callProcOfs;
+
+ // "CallProcedureSyscall" used by ioq3 optimizer, tail of "CallProcedure"
+ symName = "CallProcedure";
+ sym = sym->next = (vmSymbol_t *)Hunk_Alloc( sizeof( *sym ) + strlen( symName ), h_high );
+ Q_strncpyz( sym->symName, symName, strlen( symName ) + 1 );
+ sym->symValue = vm->callProcOfsSyscall;
+
+ vm->numSymbols = 3;
+ prev = &sym->next;
+ sym->next = NULL;
+ }
+
+ //
+ // parse symbols
+ //
+ COM_StripExtension(vm->name, name, sizeof(name));
+ Com_sprintf( symbols, sizeof(symbols), "vm/%s.map", name );
+ FS_ReadFile( symbols, &mapfile.v );
+
+ if ( mapfile.c ) {
+ const char *text_p;
+ const char *token;
+ int chars;
+ int segment;
+ int value;
+ int count = 0;
+
+ text_p = mapfile.c;
+
+ while ( 1 ) {
+ token = COM_Parse( &text_p );
+ if ( !token[0] ) {
+ break;
+ }
+ segment = ParseHex( token );
+ if ( segment ) {
+ COM_Parse( &text_p );
+ COM_Parse( &text_p );
+ continue; // only load code segment values
+ }
+
+ token = COM_Parse( &text_p );
+ if ( !token[0] ) {
+ Com_Printf( "WARNING: incomplete line at end of file\n" );
+ break;
+ }
+ value = ParseHex( token );
+ if ( value < 0 || vm->instructionCount <= value ) {
+ COM_Parse( &text_p );
+ continue; // don't load syscalls
+ }
+
+ token = COM_Parse( &text_p );
+ if ( !token[0] ) {
+ Com_Printf( "WARNING: incomplete line at end of file\n" );
+ break;
+ }
+
+
+ chars = (int)strlen( token );
+ sym = (vmSymbol_t *)Hunk_Alloc( sizeof( *sym ) + chars, h_high );
+ *prev = sym;
+ prev = &sym->next;
+ sym->next = NULL;
+ sym->symInstr = value;
+ sym->symValue = vm->instructionPointers[value] - vm->instructionPointers[0] + vm->entryOfs;
+ Q_strncpyz( sym->symName, token, chars + 1 );
+
+ count++;
+ }
+
+ vm->numSymbols += count;
+ Com_Printf( "%i symbols parsed from %s\n", count, symbols );
+ FS_FreeFile( mapfile.v );
+ } else {
+ const char *symName = "vmMain";
+ sym = *prev = (vmSymbol_t *)Hunk_Alloc( sizeof( *sym ) + strlen( symName ), h_high );
+ prev = &sym->next;
+ Q_strncpyz( sym->symName, symName, strlen( symName ) + 1 );
+ sym->symValue = vm->entryOfs;
+ vm->numSymbols += 1;
+
+ Com_Printf( "Couldn't load symbol file: %s\n", symbols );
+ }
+
+
+ if ( vm->compiled && com_developer->integer )
+ {
+ VM_GeneratePerfMap( vm );
+ }
+
+#ifdef DEBUG_VM
+ //
+ // code->symbol lookup table for profiling and debugging interpreted QVM
+ //
+
+ if ( !vm->compiled )
+ {
+ vmSymbol_t *sym;
+
+ vm->symbolTable = (vmSymbol_t **)Hunk_Alloc( vm->codeLength * sizeof(*vm->symbolTable), h_high );
+
+ for ( sym = vm->symbols; sym; sym = sym->next ) {
+ vm->symbolTable[sym->symValue] = sym;
+ }
+
+ sym = NULL;
+ for ( int i = 0; i < vm->codeLength; i++ ) {
+ if ( vm->symbolTable[i] ) {
+ sym = vm->symbolTable[i];
+ }
+ vm->symbolTable[i] = sym;
+ }
+ }
+#endif // DEBUG_VM
+}
+
+
+//=================================================================
+
+static int QDECL VM_ProfileSort( const void *a, const void *b ) {
+ const vmSymbol_t *sa, *sb;
+
+ sa = *(const vmSymbol_t * const *)a;
+ sb = *(const vmSymbol_t * const *)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 = NULL;
+ vmSymbol_t **sorted, *sym;
+ int i;
+ long total;
+ int totalCalls;
+ int time;
+ static int profileTime;
+ qboolean printHelp = qfalse;
+ qboolean resetCounts = qfalse;
+ qboolean printAll = qfalse;
+
+ if ( Cmd_Argc() >= 2 ) {
+ const char *arg = Cmd_Argv(1);
+
+ if ( !Q_stricmp(arg, "exclusive") ) {
+ vm_profileInclusive = qfalse;
+ resetCounts = qtrue;
+ Com_Printf("Collecting exclusive function instruction counts...\n");
+ } else if ( !Q_stricmp(arg, "inclusive") ) {
+ vm_profileInclusive = qtrue;
+ resetCounts = qtrue;
+ Com_Printf("Collecting inclusive function instruction counts...\n");
+ } else if ( !Q_stricmp(arg, "print") ) {
+ if (Cmd_Argc() >= 3) {
+ for ( i = 0; i < MAX_VM; i++ ) {
+ if ( !Q_stricmp(Cmd_Argv(2), vmTable[i]->name) ) {
+ vm = vmTable[i];
+ break;
+ }
+ }
+ } else {
+ // pick first VM with symbols
+ for ( i = 0; i < MAX_VM; i++ ) {
+ if ( !vmTable[i]->compiled && vmTable[i]->numSymbols ) {
+ vm = vmTable[i];
+ break;
+ }
+ }
+ }
+ } else {
+ printHelp = qtrue;
+ }
+ } else {
+ printHelp = qtrue;
+ }
+
+ if ( resetCounts ) {
+ profileTime = Sys_Milliseconds();
+
+ for ( i = 0; i < MAX_VM; i++ ) {
+ for ( sym = vmTable[i]->symbols ; sym ; sym = sym->next ) {
+ sym->profileCount = 0;
+ sym->callCount = 0;
+ sym->caller = NULL;
+ }
+ }
+ return;
+ }
+
+ if ( printHelp ) {
+ Com_Printf("Usage: vmprofile exclusive start collecting exclusive counts\n");
+ Com_Printf(" vmprofile inclusive start collecting inclusive counts\n");
+ Com_Printf(" vmprofile print [vm] print collected data\n");
+ return;
+ }
+
+ if ( vm == NULL || vm->compiled ) {
+ Com_Printf("Only interpreted VM can be profiled\n");
+ return;
+ }
+ if ( vm->numSymbols <= 0 ) {
+ Com_Printf("No symbols\n");
+ return;
+ }
+
+ sorted = (vmSymbol_t **)Z_Malloc( vm->numSymbols * sizeof( *sorted ), TAG_VM, qtrue);
+ sorted[0] = vm->symbols;
+ total = sorted[0]->profileCount;
+ totalCalls = sorted[0]->callCount;
+ for ( i = 1 ; i < vm->numSymbols ; i++ ) {
+ sorted[i] = sorted[i-1]->next;
+ total += sorted[i]->profileCount;
+ totalCalls += sorted[i]->callCount;
+ }
+
+ // assume everything is called from vmMain
+ if ( vm_profileInclusive )
+ total = VM_ValueToFunctionSymbol( vm, 0 )->profileCount;
+
+ if ( total > 0 ) {
+ qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort );
+
+ Com_Printf( "%4s %12s %9s Function Name\n",
+ vm_profileInclusive ? "Incl" : "Excl",
+ "Instructions", "Calls" );
+
+ // todo: collect associations for generating callgraphs
+ fileHandle_t callgrind = FS_FOpenFileWrite( va("callgrind.out.%s", vm->name) );
+ // callgrind header
+ FS_Printf( callgrind,
+ "events: VM_Instructions\n"
+ "fl=vm/%s.qvm\n\n", vm->name );
+
+ for ( i = 0 ; i < vm->numSymbols ; i++ ) {
+ int perc;
+
+ sym = sorted[i];
+
+ if (printAll || sym->profileCount != 0 || sym->callCount != 0) {
+ perc = 100 * sym->profileCount / total;
+ Com_Printf( "%3i%% %12li %9i %s\n", perc, sym->profileCount, sym->callCount, sym->symName );
+ }
+
+ FS_Printf(callgrind,
+ "fn=%s\n"
+ "0 %li\n\n",
+ sym->symName, sym->profileCount);
+ }
+
+ FS_FCloseFile( callgrind );
+ }
+
+ time = Sys_Milliseconds() - profileTime;
+
+ Com_Printf(" %12li %9i total\n", total, totalCalls );
+ Com_Printf(" %12li %9i total per second\n", 1000 * total / time, 1000 * totalCalls / time );
+
+ Z_Free( sorted );
+}
+
+/*
+==============
+VM_VmInfo_f
+
+==============
+*/
+void VM_VmInfo_f( void ) {
+ vm_t *vm;
+ int i;
+
+ Com_Printf( "Registered virtual machines:\n" );
+ for ( i = 0 ; i < MAX_VM ; i++ ) {
+ vm = vmTable[i];
+ if ( !vm || !vm->name[0] ) {
+ continue;
+ }
+ Com_Printf( "%s : ", vm->name );
+ if ( vm->dllHandle ) {
+ if ( vm->isLegacy ) {
+ Com_Printf( "native [legacy]\n" );
+ } else {
+ Com_Printf( "native\n" );
+ }
+ } else {
+ if (vm->compiled) {
+ Com_Printf("compiled on load\n");
+ } else {
+ Com_Printf("interpreted\n");
+ }
+ Com_Printf(" code length : %7i\n", vm->codeLength);
+ Com_Printf(" table length: %7i\n", vm->instructionCount * 4);
+ Com_Printf(" data length : %7i\n", vm->dataMask + 1);
+ if (vm->numSymbols) {
+ Com_Printf(" symbols : %i\n", vm->numSymbols);
+ }
+ }
+ }
+}
+
+/*
+===============
+VM_LogSyscalls
+
+Insert calls to this while debugging the vm compiler
+===============
+*/
+void VM_LogSyscalls( int *args ) {
+ static int callnum;
+ static FILE *f;
+
+ if ( !f ) {
+ f = fopen("syscalls.log", "w" );
+ }
+ 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] );
+}
+
+/*
+=================
+VM_BlockCopy
+Executes a block copy operation within currentVM data space
+=================
+*/
+
+void VM_BlockCopy(unsigned int dest, unsigned int src, size_t n)
+{
+ unsigned int dataMask = currentVM->dataMask;
+
+ if ((dest & dataMask) != dest
+ || (src & dataMask) != src
+ || ((dest + n) & dataMask) != dest + n
+ || ((src + n) & dataMask) != src + n)
+ {
+ Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!");
+ }
+
+ Com_Memcpy(currentVM->dataBase + dest, currentVM->dataBase + src, n);
+}
+
+/*
+================
+ VM_ExtraMemory
+----------------
+For QVMs we allocate additional memory when loading the QVM to write data inside
+there that the module expects to be given as pointers. Ideally the module
+would pass a pointer to its own memory for the engine to fill, but JKA does this
+different for some subsystems, because JKA shipped with dlls rather than QVMs.
+To stay compatible with vanilla code expectations in the QVM we need to copy our
+data into QVM memory for the QVM to have access to it.
+================
+*/
+void VM_ExtraMemory_Clear( vm_t *vm )
+{
+ // Not for dlls
+ if ( vm->dllHandle ) return;
+ vm->extraMemUse = 0;
+}
+
+void *VM_ExtraMemory_Claim( vm_t *vm, int amount )
+{
+ int retBlock;
+
+ // Not for dlls
+ if ( vm->dllHandle ) return NULL;
+
+ // Pad the amount
+ amount = PAD(amount, sizeof(void*) );
+
+ // Make sure it fits
+ if ( vm->extraMemOffset + vm->extraMemUse + amount >= vm->dataMask - PROGRAM_STACK_SIZE ) {
+ //Com_Error( ERR_FATAL, "VM_ExtraMemory_Claim: failed to claim %i bytes\n", amount );
+ Com_Printf( "VM_ExtraMemory_Claim: failed to claim %i bytes\n", amount );
+ return NULL;
+ }
+
+ // Remember where we are now and increate the use counter
+ retBlock = vm->extraMemOffset + vm->extraMemUse;
+ vm->extraMemUse += amount;
+
+ // Return address that we can write to
+ return (void *)(vm->dataBase + (retBlock & vm->dataMask));
+}
+
+void VM_ExtraMemory_Release( vm_t *vm, int amount )
+{ // NOTE: Must be in reverse order of claim
+ // Not for dlls
+ if ( vm->dllHandle ) return;
+
+ // Pad the amount
+ amount = PAD(amount, sizeof(void*) );
+
+ // Make sure it fits
+ if ( vm->extraMemUse - amount < 0 ) {
+ Com_Error( ERR_FATAL, "VM_ExtraMemory_Release: failed to release %i bytes, because a lower amount had been claimed\n", amount );
+ }
+
+ // Subtract the amount
+ vm->extraMemUse -= amount;
+}
+
+void *VM_ExtraMemory_ClaimData( vm_t *vm, const void *data, uint32_t size )
+{
+ if ( !data ) return NULL;
+ void *dst = VM_ExtraMemory_Claim( vm, size );
+ if ( dst ) memcpy( dst, data, size );
+ return dst;
+}
+
+char *VM_ExtraMemory_ClaimString( vm_t *vm, const char *inputString )
+{
+ if ( !inputString ) return NULL;
+ int length = strlen( inputString ) + 1;
+ return (char*)VM_ExtraMemory_ClaimData( vm, inputString, length );
+}
+
+size_t VM_PtrToOffset( vm_t *vm, void *ptr )
+{
+ // Dlls are not masked
+ if ( vm->dllHandle ) return (size_t)ptr;
+
+ // Return 0 if it's a NULL pointer. This allows calls like VM_PtrToOffset( vm, VM_ExtraMemory_ClaimData(...) )
+ if ( !ptr ) return 0;
+
+ // Make sure it's within the module memory
+ if ( ptr < vm->dataBase || ptr > vm->dataBase + vm->dataMask ) {
+ Com_Error( ERR_FATAL, "VM_PtrToOffset: tried to get offset for non-vm pointer\n" );
+ }
+
+ return (byte*)ptr - vm->dataBase;
+}
+
+qboolean VM_IsCurrentQVM( void )
+{
+ if ( !currentVM ) return qfalse;
+ return (qboolean)(currentVM->dllHandle == NULL);
}
diff --git a/codemp/qcommon/vm_arm.cpp b/codemp/qcommon/vm_arm.cpp
new file mode 100644
index 0000000000..42a4253b75
--- /dev/null
+++ b/codemp/qcommon/vm_arm.cpp
@@ -0,0 +1,1051 @@
+#include
+#include "vm_local.h"
+
+// ==============================================
+// r3 = instructionPointers
+// r4 = instructionCount
+// r5 = dataBase
+// r6 = programStack pointer
+// r7 = opStack
+// r8 = dataMask
+// r9 = codeBase
+// r10 = opStackIndex
+//
+// vm->code:
+// 0: VM_CompiledSyscall
+// 4: VM_ARM_VOp
+// 8: VM_CompiledErrorJump
+// 12: code
+// ==============================================
+
+#define MAX_CONSTANTS_POOL_SIZE 32
+
+typedef enum {
+ R0 = 0 << 12,
+ R1 = 1 << 12,
+ R2 = 2 << 12,
+ R3 = 3 << 12,
+ R4 = 4 << 12,
+ R5 = 5 << 12,
+ R6 = 6 << 12,
+ R7 = 7 << 12,
+ R8 = 8 << 12,
+ R9 = 9 << 12,
+ R10 = 10 << 12
+} r_t;
+
+typedef enum {
+ S0, S1
+} s_t;
+
+typedef struct {
+ qboolean movwt;
+ qboolean idiva;
+ qboolean mls;
+ qboolean vfp;
+} cpuFeatures_t;
+
+cpuFeatures_t cpu;
+byte *nativeCode;
+size_t nativeCodeSize, nativeCodeLength;
+
+uint32_t localConstantPool[MAX_CONSTANTS_POOL_SIZE]; size_t localConstantPoolLength;
+byte **localConstantPoolPointers; size_t localConstantCurrentPool;
+byte *firstLocalConstantPtr;
+
+#define Emit32(X) EmitInteger(0x##X)
+void EmitInteger(uint32_t num) {
+ if (nativeCodeLength + sizeof(uint32_t) >= nativeCodeSize) {
+ Com_Error(ERR_DROP, "native code too big for buffer");
+ }
+
+ *(uint32_t *)(&nativeCode[nativeCodeLength]) = num;
+ nativeCodeLength += sizeof(uint32_t);
+}
+
+int CalculateConstant12(uint32_t *num) {
+ uint32_t rot;
+ uint32_t immediate = *num;
+
+ for (rot = 0; rot < 32; rot += 2) {
+ if (!(immediate & ~0xFFU)) {
+ *num = 0;
+ *num = immediate;
+ *num |= (rot / 2) << 8;
+ return 0;
+ }
+
+ immediate = (immediate << 2) | (immediate >> 30);
+ }
+
+ return 1;
+}
+
+void EmitLocalConstantPool(qboolean endOfProcedure) {
+ if (localConstantPoolLength) {
+ if (!endOfProcedure) {
+ byte *sourceAddr = &nativeCode[nativeCodeLength];
+ byte *destAddr = sourceAddr + sizeof(uint32_t) + localConstantPoolLength * sizeof(uint32_t);
+
+ uint32_t jmpdiff;
+ if (destAddr >= sourceAddr + 8) {
+ jmpdiff = (destAddr - (sourceAddr + 8)) / 4;
+ } else {
+ jmpdiff = 0x00FFFFFFU - (sourceAddr + 4 - destAddr) / 4;
+ }
+
+ if (jmpdiff & 0xFF000000) {
+ Com_Error(ERR_DROP, "jump target out of range");
+ }
+
+ EmitInteger(0xEA000000|jmpdiff);
+ }
+
+ localConstantPoolPointers[localConstantCurrentPool++] = &nativeCode[nativeCodeLength];
+ for (size_t i = 0; i < localConstantPoolLength; i++) {
+ EmitInteger(localConstantPool[i]);
+ }
+
+ localConstantPoolLength = 0;
+ }
+}
+
+void EmitTOS2Reg(r_t r, qboolean pop) {
+ EmitInteger(0xE797010A | (uint32_t)r); // ldr rX, [r7, r10, LSL #2]
+
+ if (pop) {
+ Emit32(E24AA001); // sub r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ }
+}
+
+void EmitTOS2SReg(s_t s, qboolean pop) {
+ Emit32(E087010A); // add r0, r7, r10, LSL #2
+ switch (s) {
+ case S0: Emit32(ED900A00); break; // flds s0, [r0]
+ case S1: Emit32(EDD00A00); break; // flds s1, [r0]
+ }
+
+ if (pop) {
+ Emit32(E24AA001); // sub r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ }
+}
+
+void EmitReg2TOS(r_t r, qboolean push) {
+ if (push) {
+ Emit32(E28AA001); // add r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ }
+
+ EmitInteger(0xE787010A | (uint32_t)r); // str rX, [r7, r10, LSL #2]
+}
+
+void EmitSReg2TOS(s_t s, qboolean push) {
+ if (push) {
+ Emit32(E28AA001); // add r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ }
+
+ Emit32(E087010A); // add r0, r7, r10, LSL #2
+ switch (s) {
+ case S0: Emit32(ED800A00); break; // fsts s0, [r0]
+ case S1: Emit32(EDC00A00); break; // fsts s1, [r0]
+ }
+}
+
+void EmitRegisterLoadConst(r_t r, uint32_t constant) {
+ uint32_t immediate = constant;
+ if ( !CalculateConstant12(&immediate) ) {
+ uint32_t opcode = 0xE3A00000; // mov rX, #immediate
+ opcode |= (uint32_t)r;
+ opcode |= immediate;
+
+ EmitInteger(opcode);
+ return;
+ }
+
+ immediate = ~immediate;
+ if ( !CalculateConstant12(&immediate) ) {
+ uint32_t opcode = 0xE3E00000; // movn rX, #immediate
+ opcode |= (uint32_t)r;
+ opcode |= immediate;
+
+ EmitInteger(opcode);
+ return;
+ }
+
+ // at this point the constant needs to be loaded either by movw and movt (which is >= ARMv7)
+ // or by loading it from a memory address near to the current pc
+ if (cpu.movwt) {
+ uint32_t w = (constant & 0x00000FFF) | ((constant & 0x0000F000) << 4);
+ EmitInteger(0xE3000000 | (uint32_t)r | w); // movw rX, #16bit immediate
+
+ uint32_t t = (constant >> 16);
+ t = (t & 0x00000FFF) | ((t & 0x0000F000) << 4);
+ EmitInteger(0xE3400000 | (uint32_t)r | t); // movt rX, #16bit immediate
+ } else {
+ if (localConstantPoolLength >= MAX_CONSTANTS_POOL_SIZE) {
+ EmitLocalConstantPool(qfalse);
+ }
+
+ if (!firstLocalConstantPtr) firstLocalConstantPtr = &nativeCode[nativeCodeLength];
+ if (localConstantPoolPointers[localConstantCurrentPool]) {
+ byte *constantPtr = localConstantPoolPointers[localConstantCurrentPool] + localConstantPoolLength * sizeof(uint32_t);
+ intptr_t ptrdiff = -8 + constantPtr - &nativeCode[nativeCodeLength];
+
+ uint32_t opcode = 0;
+ if (ptrdiff <= 0) {
+ opcode |= 0xE51F0000; // ldr rX, [pc, #-X]
+ ptrdiff = -ptrdiff;
+ } else {
+ opcode |= 0xE59F0000; // ldr rX, [pc, #+X]
+ }
+
+ if (ptrdiff > 4095) {
+ // too far away. should never happen.
+ Com_Error(ERR_DROP, "constant relative address out of range.");
+ }
+
+ opcode |= (uint32_t)r;
+ opcode |= (uint32_t)ptrdiff;
+ EmitInteger(opcode);
+ } else {
+ // address is still unknown (first pass)
+ Emit32(00000000);
+ }
+
+ localConstantPool[localConstantPoolLength++] = constant;
+ }
+}
+
+void EmitConstantJumpInstruction(vm_t *vm, opcode_t opCode, uint32_t destinationOpCodeNum) {
+ size_t sourceAddr = nativeCodeLength;
+
+ if (destinationOpCodeNum >= (uint32_t)vm->instructionCount) {
+ Com_Error(ERR_DROP, "jump violation detected.");
+ }
+
+ size_t destAddr = vm->instructionPointers[destinationOpCodeNum];
+ if (!destAddr) {
+ // address is still unknown (first pass)
+ Emit32(00000000);
+ return;
+ }
+
+ uint32_t jmpdiff;
+ if (destAddr >= sourceAddr + 8) {
+ jmpdiff = (destAddr - (sourceAddr + 8)) / 4;
+ } else {
+ jmpdiff = 0x00FFFFFFU - (sourceAddr + 4 - destAddr) / 4;
+ }
+
+ if (jmpdiff & 0xFF000000) {
+ Com_Error(ERR_DROP, "jump target out of range.");
+ }
+
+ switch (opCode) {
+ case OP_EQ: case OP_EQF: EmitInteger(0x0A000000|jmpdiff); break; // (BEQ) Check equality (integer or float) (compares NIS vs TOS, jump to $PARM if true).
+ case OP_NE: case OP_NEF: EmitInteger(0x1A000000|jmpdiff); break; // (BNE) Check inequality (integer or float) (NIS vs TOS, jump to $PARM if true).
+ case OP_LTI: case OP_LTF: EmitInteger(0xBA000000|jmpdiff); break; // (BLT) Check less-than (signed integer or float) (NIS vs TOS, jump to $PARM if true).
+ case OP_LEI: case OP_LEF: EmitInteger(0xDA000000|jmpdiff); break; // (BLE) Check less-than or equal-to (signed integer) (NIS vs TOS, jump to $PARM if true).
+ case OP_GTI: EmitInteger(0xCA000000|jmpdiff); break; // (BGT) Check greater-than (signed integer) (NIS vs TOS), jump to $PARM if true.
+ case OP_GEI: EmitInteger(0xAA000000|jmpdiff); break; // (BGE) Check greater-than or equal-to (signed integer) (NIS vs TOS), jump to $PARM if true.
+ case OP_LTU: EmitInteger(0x3A000000|jmpdiff); break; // (BCC) Check less-than (unsigned integer) (NIS vs TOS), jump to $PARM if true.
+ case OP_LEU: EmitInteger(0x9A000000|jmpdiff); break; // (BLS) Check less-than or equal-to (unsigned integer) (NIS vs TOS), jump to $PARM if true.
+ case OP_GTU: case OP_GTF: EmitInteger(0x8A000000|jmpdiff); break; // (BHI) Check greater-than (unsigned integer or float) (NIS vs TOS), jump to $PARM if true.
+ case OP_GEU: case OP_GEF: EmitInteger(0x2A000000|jmpdiff); break; // (BCS) Check greater-than or equal-to (unsigned integer or float) (NIS vs TOS), jump to $PARM if true.
+ default: Com_Error(ERR_DROP, "OpCode implementation missing for %u", opCode);
+ }
+}
+
+void __attribute__((optimize("O0"))) VM_ARM_VOp() {
+ int32_t nis, tos, op;
+ uint32_t parm;
+
+ __asm__ volatile(
+ "str r0, %[nis]\n"
+ "str r1, %[tos]\n"
+ "str r2, %[op]\n"
+ "str r3, %[parm]\n"
+ : [nis] "=m" (nis),
+ [tos] "=m" (tos),
+ [op] "=m" (op),
+ [parm] "=m" (parm)
+ :: "r0", "r1", "r2", "r3"
+ );
+
+ switch (op) {
+ case OP_BLOCK_COPY:
+ VM_BlockCopy(nis, tos, parm);
+ return;
+ case OP_DIVI:
+ tos = nis / tos;
+ break;
+ case OP_DIVU:
+ tos = (uint32_t)nis / (uint32_t)tos;
+ break;
+ case OP_MODI:
+ tos = nis % tos;
+ break;
+ case OP_MODU:
+ tos = (uint32_t)nis % (uint32_t)tos;
+ break;
+
+ case OP_EQF:
+ tos = *(float *)&nis == *(float *)&tos;
+ break;
+ case OP_NEF:
+ tos = *(float *)&nis != *(float *)&tos;
+ break;
+ case OP_LTF:
+ tos = *(float *)&nis < *(float *)&tos;
+ break;
+ case OP_LEF:
+ tos = *(float *)&nis <= *(float *)&tos;
+ break;
+ case OP_GTF:
+ tos = *(float *)&nis > *(float *)&tos;
+ break;
+ case OP_GEF:
+ tos = *(float *)&nis >= *(float *)&tos;
+ break;
+
+ case OP_NEGF:
+ *(float *)&tos = -*(float *)&tos;
+ break;
+ case OP_ADDF:
+ *(float *)&tos = *(float *)&nis + *(float *)&tos;
+ break;
+ case OP_SUBF:
+ *(float *)&tos = *(float *)&nis - *(float *)&tos;
+ break;
+ case OP_DIVF:
+ *(float *)&tos = *(float *)&nis / *(float *)&tos;
+ break;
+ case OP_MULF:
+ *(float *)&tos = *(float *)&nis * *(float *)&tos;
+ break;
+ case OP_CVIF:
+ *(float *)&tos = tos;
+ break;
+ case OP_CVFI:
+ tos = *(float *)&tos;
+ break;
+ }
+
+ __asm__ volatile(
+ "ldr r0, %[tos]\n"
+ :: [tos] "m" (tos)
+ : "r0"
+ );
+}
+
+void __attribute__((optimize("O0"))) VM_CompiledSyscall() {
+ int *opStack, *sp;
+ int nic, opStackIndex;
+ vm_t *savedvm;
+
+ __asm__ volatile(
+ "str r0, %[nic]\n"
+ "str r6, %[programStackPtr]\n"
+ "str r7, %[opStack]\n"
+ "str r10, %[opStackIndex]\n"
+ : [nic] "=m" (nic),
+ [programStackPtr] "=m" (sp),
+ [opStack] "=m" (opStack),
+ [opStackIndex] "=m" (opStackIndex)
+ :: "r0", "r6", "r7", "r10"
+ );
+
+ currentVM->programStack = ((byte *)sp - currentVM->dataBase);
+
+ intptr_t args[MAX_VMSYSCALL_ARGS];
+ args[0] = ~nic;
+ int index;
+ for(index = 1; index < MAX_VMSYSCALL_ARGS; index++)
+ args[index] = sp[1 + index];
+
+ savedvm = currentVM;
+ opStack += opStackIndex;
+ *opStack = currentVM->legacy.syscall(args);
+ currentVM = savedvm;
+}
+
+void VM_CompiledErrorJump() {
+ Com_Error(ERR_DROP, "program tried to execute code outside VM");
+}
+
+void VM_Compile(vm_t *vm, vmHeader_t *header) {
+ size_t i, pass;
+ int ic;
+
+#ifdef __linux__
+ FILE *cpuinfo = fopen("/proc/cpuinfo", "rb");
+
+ char *arg = 0; size_t size = 0;
+ int extnum = 0; char extstr[128];
+ extstr[0] = '\0';
+ while (getline(&arg, &size, cpuinfo) != -1) {
+ if (!strncmp(arg, "model name", 10)) {
+ if (strstr(arg, "ARMv7") && !cpu.movwt && !cpu.mls) {
+ cpu.movwt = cpu.mls = qtrue;
+ Q_strcat(extstr, sizeof(extstr), " movw/movt mls"); extnum++;
+ }
+ }
+
+ if (!strncmp(arg, "Features", 8)) {
+ if (strstr(arg, " idiva") && !cpu.idiva) {
+ cpu.idiva = qtrue;
+ Q_strcat(extstr, sizeof(extstr), " idiva"); extnum++;
+ }
+
+ if (strstr(arg, " vfp") && !cpu.vfp) {
+ cpu.vfp = qtrue;
+ Q_strcat(extstr, sizeof(extstr), " vfp"); extnum++;
+ }
+ }
+ }
+
+ if (extnum) {
+ Com_Printf("using instruction set extensions:%s\n", extstr);
+ }
+
+ free(arg); fclose(cpuinfo);
+#else
+ // TODO
+#endif
+
+ nativeCodeSize = (size_t)vm->codeLength * 10;
+ nativeCode = (byte *)Z_Malloc(nativeCodeSize, TAG_VM, qfalse);
+ localConstantPoolPointers = (byte **)Z_Malloc(vm->instructionCount * sizeof(byte *), TAG_VM, qtrue);
+
+ for (pass = 0; pass < 2; pass++) {
+ nativeCodeLength = 0;
+ localConstantCurrentPool = 0;
+ localConstantPoolLength = 0;
+
+ // Function Pointers
+ EmitInteger((uint32_t)VM_CompiledSyscall);
+ EmitInteger((uint32_t)VM_ARM_VOp);
+ EmitInteger((uint32_t)VM_CompiledErrorJump);
+
+ // Init registers code
+ EmitRegisterLoadConst(R3, (uint32_t)vm->instructionPointers);
+ EmitRegisterLoadConst(R4, vm->instructionCount);
+ EmitRegisterLoadConst(R5, (uint32_t)vm->dataBase);
+ EmitRegisterLoadConst(R8, (uint32_t)vm->dataMask);
+
+ for (i = 0, ic = 0; ic < vm->instructionCount; i++, ic++) {
+ byte *instructionptr = (byte *)header + header->codeOffset + i;
+ opcode_t currOp = (opcode_t)*instructionptr;
+ byte *arg = instructionptr + 1;
+
+ if (localConstantPoolLength) {
+ ptrdiff_t constoffsetlen = ((ptrdiff_t)-8 + &nativeCode[nativeCodeLength] + sizeof(uint32_t) + localConstantPoolLength * sizeof(uint32_t)) - firstLocalConstantPtr;
+ if (constoffsetlen >= (ptrdiff_t)(4095 - 15 * sizeof(uint32_t))) {
+ EmitLocalConstantPool(qfalse);
+ }
+ }
+
+ vm->instructionPointers[ic] = nativeCodeLength;
+
+ switch (currOp) {
+ /* ---------------------------------------------------------------------------- */
+ /* ---------------------------------- BASICS ---------------------------------- */
+ /* ---------------------------------------------------------------------------- */
+ case OP_IGNORE: {
+ Emit32(E1A00000); // nop
+ break;
+ } case OP_ENTER: {
+ // Begin procedure body, adjust stack $PARM octets for frame (always at least 8 (i.e. 2 words)).
+ // Frame contains all local storage/variables and arguments space for any calls within this procedure.
+ Emit32(E52DE004); // push {lr}
+ EmitRegisterLoadConst(R0, *(uint32_t *)arg);
+ Emit32(E0466000); // sub r6, r6, r0
+ i += sizeof(uint32_t);
+ break;
+ } case OP_LEAVE: {
+ // End procedure body, $PARM is same as that of the matching ENTER.
+ EmitRegisterLoadConst(R0, *(uint32_t *)arg);
+ Emit32(E0866000); // add r6, r6, r0
+ Emit32(E49DF004); // pop {pc}
+
+ EmitLocalConstantPool(qtrue);
+ i += sizeof(uint32_t);
+ break;
+ } case OP_LOCAL: {
+ // Get address of local storage (local variable or argument) (TOS <- (frame + $PARM)).
+ EmitRegisterLoadConst(R1, *(uint32_t *)arg);
+ Emit32(E0860001); // add r0, r6, r1
+ Emit32(E0400005); // sub r0, r0, r5
+
+ EmitReg2TOS(R0, qtrue);
+ i += sizeof(uint32_t);
+ break;
+ } case OP_CONST: {
+ // Push literal value onto stack (TOS <- $PARM).
+ EmitRegisterLoadConst(R0, *(uint32_t *)arg);
+ EmitReg2TOS(R0, qtrue);
+ i += sizeof(uint32_t);
+ break;
+ } case OP_LOAD1: {
+ // Load 1-octet value from address in TOS (TOS <- [TOS]).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0000008); // and r0, r0, r8
+
+ Emit32(E7D50000); // ldrb r0, [r5, r0]
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_LOAD2: {
+ // Load 2-octet value from address in TOS (TOS <- [TOS]).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0000008); // and r0, r0, r8
+ Emit32(E3C00001); // bic r0, r0, #1
+
+ Emit32(E19500B0); // ldrh r0, [r5, r0]
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_LOAD4: {
+ // Load 4-octet value from address in TOS (TOS <- [TOS]).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0000008); // and r0, r0, r8
+ Emit32(E3C00003); // bic r0, r0, #3
+
+ Emit32(E7950000); // ldr r0, [r5, r0]
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_STORE1: {
+ // TOS is 1-octet value to store, destination address in next-in-stack ([NIS] <- TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E0000008); // and r0, r0, r8
+
+ Emit32(E7C51000); // strb r1, [r5, r0]
+ break;
+ } case OP_STORE2: {
+ // TOS is 2-octet value to store, destination address in next-in-stack ([NIS] <- TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E0000008); // and r0, r0, r8
+ Emit32(E3C00001); // bic r0, r0, #1
+
+ Emit32(E18510B0); // strh r1, [r5, r0]
+ break;
+ } case OP_STORE4: {
+ // TOS is 4-octet value to store, destination address in next-in-stack ([NIS] <- TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E0000008); // and r0, r0, r8
+ Emit32(E3C00003); // bic r0, r0, #3
+
+ Emit32(E7851000); // str r1, [r5, r0]
+ break;
+ } case OP_PUSH: {
+ // Push nonsense (void) value to opstack (TOS <- 0).
+ Emit32(E28AA001); // add r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ break;
+ } case OP_POP: {
+ // Pop a value from stack (remove TOS, decrease stack by 1).
+ Emit32(E24AA001); // sub r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ break;
+ } case OP_ARG: {
+ // TOS is 4-octet value to store into arguments-marshalling space of the indicated octet offset (ARGS[offset] <- TOS).
+ Emit32(E0460005); // sub r0, r6, r5
+ EmitRegisterLoadConst(R1, *(uint8_t *)arg);
+ Emit32(E0800001); // add r0, r0, r1
+ Emit32(E0000008); // and r0, r0, r8
+ Emit32(E3C00003); // bic r0, r0, #3
+
+ EmitTOS2Reg(R1, qtrue);
+ Emit32(E7851000); // str r1, [r5, r0]
+ i += sizeof(uint8_t);
+ break;
+ } case OP_BLOCK_COPY: {
+ // Copy $PARM bytes from [TOS] to [NIS]
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, currOp);
+ EmitRegisterLoadConst(R3, *(uint32_t *)arg);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ i += sizeof(uint32_t);
+ break;
+ /* ---------------------------------------------------------------------------- */
+ /* --------------------------------- BRANCHES --------------------------------- */
+ /* ---------------------------------------------------------------------------- */
+ } case OP_CALL: {
+ // Make call to procedure (code address <- TOS).
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E3500000); // cmp r0, #0
+ Emit32(BA000006); // blt dosyscall
+ Emit32(E1500004); // cmp r0, r4
+ Emit32(2A000002); // bcs errjmp
+
+ // in-vm-call:
+ Emit32(E7931100); // ldr r1, [r3, r0, lsl #2]
+ Emit32(E12FFF31); // blx r1
+ Emit32(EA000007); // b outjmp
+
+ // errjmp:
+ Emit32(E5991008); // ldr r1, [r9, #8] (VM_CompiledErrorJump)
+ Emit32(E12FFF31); // blx r1
+
+ // dosyscall:
+ Emit32(E28AA001); // add r10, #1
+ Emit32(E20AA0FF); // and r10, #255
+ Emit32(E92D07F8); // push {r3-r10}
+ Emit32(E5991000); // ldr r1, [r9] (VM_CompiledSyscall)
+ Emit32(E12FFF31); // blx r1
+ Emit32(E8BD07F8); // pop {r3-r10}
+
+ // outjmp:
+ break;
+ } case OP_JUMP: {
+ // Branch (code address <- TOS)
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E1500004); // cmp r0, r4
+ Emit32(3A000001); // bcc dojmp
+
+ // errjmp:
+ Emit32(E5991008); // ldr r1, [r9, #8] (VM_CompiledErrorJump)
+ Emit32(E12FFF31); // blx r1
+
+ // dojmp:
+ Emit32(E7931100); // ldr r1, [r3, r0, lsl #2]
+ Emit32(E12FFF11); // bx r1
+ 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: {
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E1500001); // cmp r0, r1
+ EmitConstantJumpInstruction(vm, currOp, *(uint32_t *)arg);
+ i += sizeof(uint32_t);
+ break;
+ } case OP_EQF:
+ case OP_NEF:
+ case OP_LTF:
+ case OP_LEF:
+ case OP_GTF:
+ case OP_GEF: {
+ if (cpu.vfp) {
+ EmitTOS2SReg(S1, qtrue);
+ EmitTOS2SReg(S0, qtrue);
+ Emit32(EEB40AE0); // fcmpes s0, s1
+ Emit32(EEF1FA10); // fmstat
+ EmitConstantJumpInstruction(vm, currOp, *(uint32_t *)arg);
+ } else {
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qtrue);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, currOp);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+
+ Emit32(E3500001); // cmp r0, #1
+ EmitConstantJumpInstruction(vm, OP_EQ, *(uint32_t *)arg);
+ }
+ i += sizeof(uint32_t);
+ break;
+ /* ---------------------------------------------------------------------------- */
+ /* ---------------------------- INTEGER OPERATIONS ---------------------------- */
+ /* ---------------------------------------------------------------------------- */
+ } case OP_SEX8: {
+ // Sign-extend 8-bit (TOS <- TOS).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E6AF0070); // sxtb r0, r0
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_SEX16: {
+ // Sign-extend 16-bit (TOS <- TOS).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E6BF0070); // sxth r0, r0
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_NEGI: {
+ // Negate signed integer (TOS <- -TOS).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E2600000); // neg r0, r0
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_ADD: {
+ // Add integer-wise (TOS <- NIS + TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0800001); // add r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_SUB: {
+ // Subtract integer-wise (TOS <- NIS - TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0400001); // sub r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_DIVI: {
+ // Divide (signed integer) (TOS <- NIS / TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+
+ if (cpu.idiva) {
+ Emit32(E710F110); // sdiv r0, r0, r1
+ } else {
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_DIVI);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ }
+
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_DIVU: {
+ // Divide (unsigned integer) (TOS <- NIS / TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+
+ if (cpu.idiva) {
+ Emit32(E730F110); // udiv r0, r0, r1
+ } else {
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_DIVU);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ }
+
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_MODI: {
+ // Modulo (signed integer) (TOS <- NIS mod TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+
+ if (cpu.idiva && cpu.mls) {
+ Emit32(E712F110); // sdiv r2, r0, r1
+ Emit32(E0600291); // mls r0, r1, r2, r0
+ } else {
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_MODI);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ }
+
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_MODU: {
+ // Modulo (unsigned integer) (TOS <- NIS mod TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+
+ if (cpu.idiva && cpu.mls) {
+ Emit32(E732F110); // udiv r2, r0, r1
+ Emit32(E0600291); // mls r0, r1, r2, r0
+ } else {
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_MODU);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ }
+
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_MULI:
+ case OP_MULU: {
+ // Multiply (signed/unsigned integer) (TOS <- NIS * TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0000190); // mul r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_BAND: {
+ // Bitwise AND (TOS <- NIS & TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0000001); // and r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_BOR: {
+ // Bitwise OR (TOS <- NIS | TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E1800001); // orr r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_BXOR: {
+ // Bitwise XOR (TOS <- NIS ^ TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E0200001); // eor r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_BCOM: {
+ // Bitwise complement (TOS <- ~TOS).
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E1E00000); // mvn r0, r0
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_LSH: {
+ // Bitwise left-shift (TOS <- NIS << TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E1A00110); // lsl r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_RSHI: {
+ // Algebraic (signed) right-shift (TOS <- NIS >> TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E1A00150); // asr r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ } case OP_RSHU: {
+ // Bitwise (unsigned) right-shift (TOS <- NIS >> TOS).
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E1A00130); // lsr r0, r0, r1
+ EmitReg2TOS(R0, qfalse);
+ break;
+ /* ---------------------------------------------------------------------------- */
+ /* ----------------------------- FLOAT OPERATIONS ----------------------------- */
+ /* ---------------------------------------------------------------------------- */
+ } case OP_NEGF: {
+ // Negate float value (TOS <- -TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EEB10A40); // fnegs s0, s0
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_NEGF);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } case OP_ADDF: {
+ // Add integer-wise (TOS <- NIS + TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S1, qtrue);
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EE300A20); // fadds s0, s0, s1
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_ADDF);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } case OP_SUBF: {
+ // Subtract floats (TOS <- NIS - TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S1, qtrue);
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EE300A60); // fsubs s0, s0, s1
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_SUBF);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } case OP_DIVF: {
+ // Divide floats (TOS <- NIS / TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S1, qtrue);
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EE800A20); // fdivs s0, s0, s1
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_DIVF);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } case OP_MULF: {
+ // Multiply floats (TOS <- NIS x TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S1, qtrue);
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EE200A20); // fmuls s0, s0, s1
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qtrue);
+ EmitTOS2Reg(R0, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_MULF);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } case OP_CVIF: {
+ // Convert signed integer to float (TOS <- TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EEB80AC0); // fsitos s0, s0
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_CVIF);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } case OP_CVFI: {
+ // Convert float to signed integer (TOS <- TOS).
+ if (cpu.vfp) {
+ EmitTOS2SReg(S0, qfalse);
+ Emit32(EEBD0AC0); // ftosizs s0, s0
+ EmitSReg2TOS(S0, qfalse);
+ } else {
+ EmitTOS2Reg(R1, qfalse);
+ Emit32(E92D07F8); // push {r3-r10}
+ EmitRegisterLoadConst(R2, OP_CVFI);
+ Emit32(E5995004); // ldr r5, [r9, #4] (VM_ARM_VOp)
+ Emit32(E12FFF35); // blx r5
+ Emit32(E8BD07F8); // pop {r3-r10}
+ EmitReg2TOS(R0, qfalse);
+ }
+ break;
+ } default:
+ Com_Error(ERR_FATAL, "unknown instruction %#02x!", currOp);
+ }
+ }
+ }
+
+ // copy to an exact sized buffer with the appropriate permission bits
+ vm->codeLength = nativeCodeLength;
+
+ vm->codeBase = (byte *)mmap(NULL, nativeCodeLength, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+ if(vm->codeBase == MAP_FAILED) {
+ Com_Error(ERR_FATAL, "VM_CompileARM: can't mmap memory");
+ }
+
+ Com_Memcpy( vm->codeBase, nativeCode, nativeCodeLength );
+
+ if(mprotect(vm->codeBase, nativeCodeLength, PROT_READ|PROT_EXEC)) {
+ Com_Error(ERR_FATAL, "VM_CompileARM: mprotect failed");
+ }
+
+ Z_Free(nativeCode);
+ Z_Free(localConstantPoolPointers);
+
+ Com_Printf( "VM file %s compiled to %zu bytes of code\n", vm->name, nativeCodeLength );
+
+ // offset all the instruction pointers for the new location
+ for ( int i = 0 ; i < header->instructionCount ; i++ ) {
+ vm->instructionPointers[i] += (intptr_t)vm->codeBase;
+ }
+}
+
+int __attribute__((optimize("O0"))) VM_CallCompiled(vm_t *vm, int *args) {
+ int stack[OPSTACK_SIZE / sizeof(int)];
+ unsigned int stackIndex;
+ int programStack, stackOnEntry;
+ int *programStackPtr;
+ byte *image;
+ int *stackPtr;
+ int arg;
+
+ currentVM = vm;
+
+ // interpret the code
+ vm->currentlyInterpreting = qtrue;
+
+ // 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;
+
+ programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
+
+ for ( arg = 0; arg < MAX_VMMAIN_ARGS; arg++ )
+ *(int *)&image[ programStack + 8 + arg * 4 ] = args[ arg ];
+
+ *(int *)&image[ programStack + 4 ] = 0; // return stack
+ *(int *)&image[ programStack ] = -1; // will terminate the loop on return
+
+ programStackPtr = (int *)&image[ programStack ];
+
+ // set up opStack
+ *(unsigned int *)stack = 0xDEADC0DEu;
+ stackPtr = stack;
+ stackIndex = 0;
+
+ // off we go into generated code...
+ __asm__ volatile (
+ "push {r0-r10,lr}\n"
+
+ "ldr r6, %[programStack]\n"
+ "ldr r7, %[stack]\n"
+ "ldr r9, %[codeAddress]\n"
+ "ldr r10, %[stackIndex]\n"
+
+ "mov r0, r9\n"
+ "add r0, #12\n"
+ "blx r0\n"
+
+ "str r6, %[programStackOut]\n"
+ "str r10, %[stackIndexOut]\n"
+
+ "pop {r0-r10,lr}"
+ : [programStackOut] "=m" (programStackPtr),
+ [stackIndexOut] "=m" (stackIndex)
+ : [codeAddress] "m" (vm->codeBase),
+ [programStack] "m" (programStackPtr),
+ [stack] "m" (stackPtr),
+ [stackIndex] "m" (stackIndex)
+ : "r0", "r6", "r7", "r9", "r10"
+ );
+
+ if (*(unsigned int *)stack != 0xDEADC0DEu || stackIndex != 1) {
+ Com_Error(ERR_DROP, "opStack corrupted in VM_CallCompiled");
+ }
+
+ if (programStackPtr != (int *)&image[programStack]) {
+ Com_Error(ERR_DROP, "programStack corrupted in VM_CallCompiled");
+ }
+
+ vm->programStack = stackOnEntry;
+
+ return stack[stackIndex];
+}
diff --git a/codemp/qcommon/vm_interpreted.cpp b/codemp/qcommon/vm_interpreted.cpp
new file mode 100644
index 0000000000..228edbbd4b
--- /dev/null
+++ b/codemp/qcommon/vm_interpreted.cpp
@@ -0,0 +1,930 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+#include "vm_local.h"
+#include
+
+//#define DEBUG_VM
+#ifdef DEBUG_VM
+static const char * const opnames[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"
+};
+#endif
+
+#if idppc
+
+//FIXME: these, um... look the same to me
+#if defined(__GNUC__)
+static QINLINE unsigned int loadWord(void *addr) {
+ unsigned int word;
+
+ asm("lwbrx %0,0,%1" : "=r" (word) : "r" (addr));
+ return word;
+}
+#else
+static QINLINE unsigned int __lwbrx(register void *addr,
+ register int offset) {
+ register unsigned int word;
+
+ asm("lwbrx %0,%2,%1" : "=r" (word) : "r" (addr), "b" (offset));
+ return word;
+}
+#define loadWord(addr) __lwbrx(addr,0)
+#endif
+
+#else
+ static QINLINE int loadWord(void *addr) {
+ int word;
+ memcpy(&word, addr, 4);
+ return LittleLong(word);
+ }
+#endif
+
+char *VM_Indent( vm_t *vm ) {
+ static char *string = " ";
+ if ( vm->callLevel > 20 ) {
+ return string;
+ }
+ return string + 2 * ( 20 - vm->callLevel );
+}
+
+/*
+====================
+VM_StackTrace
+
+Call with (programCounter, programStack) inside of VM_CallInterpreted
+and with (*(int *)&image[programStack], stomped) inside of a systemCall.
+====================
+*/
+void VM_StackTrace( vm_t *vm, int programCounter, int programStack ) {
+ int count;
+
+ count = 0;
+ do {
+ Com_Printf( "%s\n", VM_ValueToSymbol( vm, programCounter ) );
+ programStack = *(int *)&vm->dataBase[programStack+4];
+ programCounter = *(int *)&vm->dataBase[programStack];
+ } while ( programCounter != -1 && ++count < 32 );
+
+}
+
+
+/*
+====================
+VM_PrepareInterpreter
+====================
+*/
+void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) {
+ int op;
+ int byte_pc;
+ int int_pc;
+ byte *code;
+ int instruction;
+ int *codeBase;
+
+ vm->codeBase = (byte *)Hunk_Alloc( vm->codeLength*4, h_high ); // we're now int aligned
+// memcpy( vm->codeBase, (byte *)header + header->codeOffset, vm->codeLength );
+
+ // we don't need to translate the instructions, but we still need
+ // to find each instructions starting point for jumps
+ int_pc = byte_pc = 0;
+ instruction = 0;
+ code = (byte *)header + header->codeOffset;
+ codeBase = (int *)vm->codeBase;
+
+ // Copy and expand instructions to words while building instruction table
+ while ( instruction < header->instructionCount ) {
+ vm->instructionPointers[ instruction ] = int_pc;
+ instruction++;
+
+ op = (int)code[ byte_pc ];
+ codeBase[int_pc] = op;
+ if(byte_pc > header->codeLength)
+ Com_Error(ERR_DROP, "VM_PrepareInterpreter: pc > header->codeLength");
+
+ byte_pc++;
+ int_pc++;
+
+ // these are the only opcodes that aren't a single byte
+ switch ( op ) {
+ case OP_ENTER:
+ case OP_CONST:
+ case OP_LOCAL:
+ case OP_LEAVE:
+ 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:
+ codeBase[int_pc] = loadWord(&code[byte_pc]);
+ byte_pc += 4;
+ int_pc++;
+ break;
+ case OP_ARG:
+ codeBase[int_pc] = (int)code[byte_pc];
+ byte_pc++;
+ int_pc++;
+ break;
+ default:
+ break;
+ }
+
+ }
+ int_pc = 0;
+ instruction = 0;
+
+ // Now that the code has been expanded to int-sized opcodes, we'll translate instruction index
+ //into an index into codeBase[], which contains opcodes and operands.
+ while ( instruction < header->instructionCount ) {
+ op = codeBase[ int_pc ];
+ instruction++;
+ int_pc++;
+
+ switch ( op ) {
+ // These ops need to translate addresses in jumps from instruction index to int index
+ 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:
+ if(codeBase[int_pc] < 0 || codeBase[int_pc] > vm->instructionCount)
+ Com_Error(ERR_DROP, "VM_PrepareInterpreter: Jump to invalid instruction number");
+
+ // codeBase[pc] is the instruction index. Convert that into an offset into
+ //the int-aligned codeBase[] by the lookup table.
+ codeBase[int_pc] = vm->instructionPointers[codeBase[int_pc]];
+ int_pc++;
+ break;
+
+ // These opcodes have an operand that isn't an instruction index
+ case OP_ENTER:
+ case OP_CONST:
+ case OP_LOCAL:
+ case OP_LEAVE:
+ case OP_BLOCK_COPY:
+ case OP_ARG:
+ int_pc++;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+}
+
+/*
+==============
+VM_Call
+
+
+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
+==============
+*/
+
+#define DEBUGSTR va("%s%i", VM_Indent(vm), opStackOfs)
+
+int VM_CallInterpreted( vm_t *vm, int *args ) {
+ byte stack[OPSTACK_SIZE + 15];
+ int *opStack;
+ uint8_t opStackOfs;
+ int programCounter;
+ int programStack;
+ int stackOnEntry;
+ byte *image;
+ int *codeImage;
+ int v1;
+ int dataMask;
+ int arg;
+#ifdef DEBUG_VM
+ vmSymbol_t *profileSymbol;
+#endif
+
+ // interpret the code
+ vm->currentlyInterpreting = qtrue;
+
+ // we might be called recursively, so this might not be the very top
+ programStack = stackOnEntry = vm->programStack;
+
+#ifdef DEBUG_VM
+ profileSymbol = VM_ValueToFunctionSymbol( vm, 0 );
+ // uncomment this for debugging breakpoints
+ vm->breakFunction = 0;
+#endif
+ // set up the stack frame
+
+ image = vm->dataBase;
+ codeImage = (int *)vm->codeBase;
+ dataMask = vm->dataMask;
+
+ programCounter = 0;
+
+ programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
+
+ for ( arg = 0; arg < MAX_VMMAIN_ARGS; arg++ )
+ *(int *)&image[ programStack + 8 + arg * 4 ] = args[ arg ];
+
+ *(int *)&image[ programStack + 4 ] = 0; // return stack
+ *(int *)&image[ programStack ] = -1; // will terminate the loop on return
+
+ VM_Debug(0);
+
+ // leave a free spot at start of stack so
+ // that as long as opStack is valid, opStack-1 will
+ // not corrupt anything
+ opStack = (int *)PADP(stack, 16);
+ *(unsigned *)opStack = 0xDEADBEEFu;
+ opStackOfs = 0;
+
+// vm_debugLevel=2;
+ // main interpreter loop, will exit when a LEAVE instruction
+ // grabs the -1 program counter
+
+#define r2 codeImage[programCounter]
+
+ while ( 1 ) {
+ int opcode, r0, r1;
+// unsigned int r2;
+
+nextInstruction:
+ r0 = opStack[opStackOfs];
+ r1 = opStack[(uint8_t) (opStackOfs - 1)];
+nextInstruction2:
+ opcode = codeImage[ programCounter++ ];
+
+#ifdef DEBUG_VM
+ if ( (unsigned)programCounter >= (unsigned)vm->codeLength ) {
+ Com_Error( ERR_DROP, "VM pc out of range" );
+ return 0;
+ }
+
+ if ( programStack <= vm->stackBottom ) {
+ Com_Error( ERR_DROP, "VM stack overflow" );
+ return 0;
+ }
+
+ if ( programStack & 3 ) {
+ Com_Error( ERR_DROP, "VM program stack misaligned" );
+ return 0;
+ }
+
+ if ( vm_debugLevel > 1 ) {
+ Com_Printf( "%s %s\n", DEBUGSTR, opnames[opcode] );
+ }
+
+ for (vmSymbol_t *sym = profileSymbol; sym != NULL; sym = sym->caller)
+ sym->profileCount++;
+#endif
+ switch ( opcode ) {
+#ifdef DEBUG_VM
+ default:
+ Com_Error( ERR_DROP, "Bad VM instruction" ); // this should be scanned on load!
+ return 0;
+#endif
+ case OP_BREAK:
+ vm->breakCount++;
+ goto nextInstruction2;
+ case OP_CONST:
+ opStackOfs++;
+ r1 = r0;
+ r0 = opStack[opStackOfs] = r2;
+
+ programCounter += 1;
+ goto nextInstruction2;
+ case OP_LOCAL:
+ opStackOfs++;
+ r1 = r0;
+ r0 = opStack[opStackOfs] = r2+programStack;
+
+ programCounter += 1;
+ goto nextInstruction2;
+
+ case OP_LOAD4:
+#ifdef DEBUG_VM
+ if(opStack[opStackOfs] & 3)
+ {
+ Com_Error( ERR_DROP, "OP_LOAD4 misaligned" );
+ return 0;
+ }
+#endif
+ r0 = opStack[opStackOfs] = *(int *) &image[r0 & dataMask & ~3 ];
+ goto nextInstruction2;
+ case OP_LOAD2:
+ r0 = opStack[opStackOfs] = *(unsigned short *)&image[ r0&dataMask&~1 ];
+ goto nextInstruction2;
+ case OP_LOAD1:
+ r0 = opStack[opStackOfs] = image[ r0&dataMask ];
+ goto nextInstruction2;
+
+ case OP_STORE4:
+ *(int *)&image[ r1&(dataMask & ~3) ] = r0;
+ opStackOfs -= 2;
+ goto nextInstruction;
+ case OP_STORE2:
+ *(short *)&image[ r1&(dataMask & ~1) ] = r0;
+ opStackOfs -= 2;
+ goto nextInstruction;
+ case OP_STORE1:
+ image[ r1&dataMask ] = r0;
+ opStackOfs -= 2;
+ goto nextInstruction;
+
+ case OP_ARG:
+ // single byte offset from programStack
+ *(int *)&image[ (codeImage[programCounter] + programStack)&dataMask&~3 ] = r0;
+ opStackOfs--;
+ programCounter += 1;
+ goto nextInstruction;
+
+ case OP_BLOCK_COPY:
+ VM_BlockCopy(r1, r0, r2);
+ programCounter += 1;
+ opStackOfs -= 2;
+ goto nextInstruction;
+
+ case OP_CALL:
+ // save current program counter
+ *(int *)&image[ programStack ] = programCounter;
+
+ // jump to the location on the stack
+ programCounter = r0;
+ opStackOfs--;
+ if ( programCounter < 0 ) {
+ // system call
+ int r;
+// int temp;
+#ifdef DEBUG_VM
+ int stomped;
+
+ if ( vm_debugLevel ) {
+ Com_Printf( "%s---> systemcall(%i)\n", DEBUGSTR, -1 - programCounter );
+ }
+#endif
+ // save the stack to allow recursive VM entry
+// temp = vm->callLevel;
+ vm->programStack = programStack - 4;
+#ifdef DEBUG_VM
+ stomped = *(int *)&image[ programStack + 4 ];
+#endif
+ *(int *)&image[ programStack + 4 ] = -1 - programCounter;
+
+//VM_LogSyscalls( (int *)&image[ programStack + 4 ] );
+ {
+ // the vm has ints on the stack, we expect
+ // pointers so we might have to convert it
+ if (sizeof(intptr_t) != sizeof(int)) {
+ intptr_t argarr[ MAX_VMSYSCALL_ARGS ];
+ int *imagePtr = (int *)&image[ programStack ];
+ for (size_t i = 0; i < ARRAY_LEN(argarr); ++i) {
+ argarr[i] = *(++imagePtr);
+ }
+ r = vm->legacy.syscall( argarr );
+ } else {
+ intptr_t* argptr = (intptr_t *)&image[ programStack + 4 ];
+ r = vm->legacy.syscall( argptr );
+ }
+ }
+
+#ifdef DEBUG_VM
+ // this is just our stack frame pointer, only needed
+ // for debugging
+ *(int *)&image[ programStack + 4 ] = stomped;
+#endif
+
+ // save return value
+ opStackOfs++;
+ opStack[opStackOfs] = r;
+ programCounter = *(int *)&image[ programStack ];
+// vm->callLevel = temp;
+#ifdef DEBUG_VM
+ if ( vm_debugLevel ) {
+ Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) );
+ }
+#endif
+ } else if ( programCounter >= vm->instructionCount ) {
+ Com_Error( ERR_DROP, "VM program counter out of range in OP_CALL" );
+ return 0;
+ } else {
+ programCounter = vm->instructionPointers[ programCounter ];
+ }
+ goto nextInstruction;
+
+ // push and pop are only needed for discarded or bad function return values
+ case OP_PUSH:
+ opStackOfs++;
+ goto nextInstruction;
+ case OP_POP:
+ opStackOfs--;
+ goto nextInstruction;
+
+ case OP_ENTER:
+#ifdef DEBUG_VM
+ if ( vm_profileInclusive ) {
+ vmSymbol_t *newSym = VM_ValueToFunctionSymbol( vm, programCounter );
+ qboolean link = qtrue;
+
+ newSym->callCount++;
+
+ // deal with recursion
+ for ( vmSymbol_t *sym = profileSymbol; sym; sym = sym->caller )
+ if (sym == newSym)
+ link = qfalse;
+
+ if (link) {
+ newSym->caller = profileSymbol;
+ profileSymbol = newSym;
+ }
+ } else {
+ profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter );
+ profileSymbol->callCount++;
+ }
+#endif
+ // get size of stack frame
+ v1 = r2;
+
+ programCounter += 1;
+ programStack -= v1;
+#ifdef DEBUG_VM
+ // save old stack frame for debugging traces
+ *(int *)&image[programStack+4] = programStack + v1;
+ if ( vm_debugLevel ) {
+ Com_Printf( "%s---> %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter - 5 ) );
+ if ( vm->breakFunction && programCounter - 5 == vm->breakFunction ) {
+ // this is to allow setting breakpoints here in the debugger
+ vm->breakCount++;
+// vm_debugLevel = 2;
+// VM_StackTrace( vm, programCounter, programStack );
+ }
+// vm->callLevel++;
+ }
+#endif
+ goto nextInstruction;
+ case OP_LEAVE:
+ // remove our stack frame
+ v1 = r2;
+
+ programStack += v1;
+
+ // grab the saved program counter
+ programCounter = *(int *)&image[ programStack ];
+#ifdef DEBUG_VM
+ profileSymbol->caller = NULL;
+ profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter );
+ if ( vm_debugLevel ) {
+// vm->callLevel--;
+ Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) );
+ }
+#endif
+ // check for leaving the VM
+ if ( programCounter == -1 ) {
+ goto done;
+ } else if ( (unsigned)programCounter >= (unsigned)vm->codeLength ) {
+ Com_Error( ERR_DROP, "VM program counter out of range in OP_LEAVE" );
+ return 0;
+ }
+ goto nextInstruction;
+
+ /*
+ ===================================================================
+ BRANCHES
+ ===================================================================
+ */
+
+ case OP_JUMP:
+ if ( (unsigned)r0 >= (unsigned)vm->instructionCount )
+ {
+ Com_Error( ERR_DROP, "VM program counter out of range in OP_JUMP" );
+ return 0;
+ }
+
+ programCounter = vm->instructionPointers[ r0 ];
+
+ opStackOfs--;
+ goto nextInstruction;
+
+ case OP_EQ:
+ opStackOfs -= 2;
+ if ( r1 == r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_NE:
+ opStackOfs -= 2;
+ if ( r1 != r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_LTI:
+ opStackOfs -= 2;
+ if ( r1 < r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_LEI:
+ opStackOfs -= 2;
+ if ( r1 <= r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_GTI:
+ opStackOfs -= 2;
+ if ( r1 > r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_GEI:
+ opStackOfs -= 2;
+ if ( r1 >= r0 ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_LTU:
+ opStackOfs -= 2;
+ if ( ((unsigned)r1) < ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_LEU:
+ opStackOfs -= 2;
+ if ( ((unsigned)r1) <= ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_GTU:
+ opStackOfs -= 2;
+ if ( ((unsigned)r1) > ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_GEU:
+ opStackOfs -= 2;
+ if ( ((unsigned)r1) >= ((unsigned)r0) ) {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_EQF:
+ opStackOfs -= 2;
+
+ if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] == ((float *) opStack)[(uint8_t) (opStackOfs + 2)])
+ {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_NEF:
+ opStackOfs -= 2;
+
+ if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] != ((float *) opStack)[(uint8_t) (opStackOfs + 2)])
+ {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_LTF:
+ opStackOfs -= 2;
+
+ if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] < ((float *) opStack)[(uint8_t) (opStackOfs + 2)])
+ {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_LEF:
+ opStackOfs -= 2;
+
+ if(((float *) opStack)[(uint8_t) ((uint8_t) (opStackOfs + 1))] <= ((float *) opStack)[(uint8_t) ((uint8_t) (opStackOfs + 2))])
+ {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_GTF:
+ opStackOfs -= 2;
+
+ if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] > ((float *) opStack)[(uint8_t) (opStackOfs + 2)])
+ {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+ case OP_GEF:
+ opStackOfs -= 2;
+
+ if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] >= ((float *) opStack)[(uint8_t) (opStackOfs + 2)])
+ {
+ programCounter = r2; //vm->instructionPointers[r2];
+ goto nextInstruction;
+ } else {
+ programCounter += 1;
+ goto nextInstruction;
+ }
+
+
+ //===================================================================
+
+ case OP_NEGI:
+ opStack[opStackOfs] = -r0;
+ goto nextInstruction;
+ case OP_ADD:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 + r0;
+ goto nextInstruction;
+ case OP_SUB:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 - r0;
+ goto nextInstruction;
+ case OP_DIVI:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 / r0;
+ goto nextInstruction;
+ case OP_DIVU:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) / ((unsigned) r0);
+ goto nextInstruction;
+ case OP_MODI:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 % r0;
+ goto nextInstruction;
+ case OP_MODU:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) % ((unsigned) r0);
+ goto nextInstruction;
+ case OP_MULI:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 * r0;
+ goto nextInstruction;
+ case OP_MULU:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) * ((unsigned) r0);
+ goto nextInstruction;
+
+ case OP_BAND:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) & ((unsigned) r0);
+ goto nextInstruction;
+ case OP_BOR:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) | ((unsigned) r0);
+ goto nextInstruction;
+ case OP_BXOR:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) ^ ((unsigned) r0);
+ goto nextInstruction;
+ case OP_BCOM:
+ opStack[opStackOfs] = ~((unsigned) r0);
+ goto nextInstruction;
+
+ case OP_LSH:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 << r0;
+ goto nextInstruction;
+ case OP_RSHI:
+ opStackOfs--;
+ opStack[opStackOfs] = r1 >> r0;
+ goto nextInstruction;
+ case OP_RSHU:
+ opStackOfs--;
+ opStack[opStackOfs] = ((unsigned) r1) >> r0;
+ goto nextInstruction;
+
+ case OP_NEGF:
+ ((float *) opStack)[opStackOfs] = -((float *) opStack)[opStackOfs];
+ goto nextInstruction;
+ case OP_ADDF:
+ opStackOfs--;
+ ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] + ((float *) opStack)[(uint8_t) (opStackOfs + 1)];
+ goto nextInstruction;
+ case OP_SUBF:
+ opStackOfs--;
+ ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] - ((float *) opStack)[(uint8_t) (opStackOfs + 1)];
+ goto nextInstruction;
+ case OP_DIVF:
+ opStackOfs--;
+ ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] / ((float *) opStack)[(uint8_t) (opStackOfs + 1)];
+ goto nextInstruction;
+ case OP_MULF:
+ opStackOfs--;
+ ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] * ((float *) opStack)[(uint8_t) (opStackOfs + 1)];
+ goto nextInstruction;
+
+ case OP_CVIF:
+ ((float *) opStack)[opStackOfs] = (float) opStack[opStackOfs];
+ goto nextInstruction;
+ case OP_CVFI:
+ opStack[opStackOfs] = (int) ((float *) opStack)[opStackOfs];
+ goto nextInstruction;
+ case OP_SEX8:
+ opStack[opStackOfs] = (signed char) opStack[opStackOfs];
+ goto nextInstruction;
+ case OP_SEX16:
+ opStack[opStackOfs] = (short) opStack[opStackOfs];
+ goto nextInstruction;
+ }
+ }
+
+done:
+ vm->currentlyInterpreting = qfalse;
+
+ if (opStackOfs != 1 || *(unsigned *)opStack != 0xDEADBEEFu)
+ Com_Error(ERR_DROP, "Interpreter error: opStack[0] = %X, opStackOfs = %d", opStack[0], opStackOfs);
+
+ vm->programStack = stackOnEntry;
+
+ // return the result
+ return opStack[opStackOfs];
+}
diff --git a/codemp/qcommon/vm_local.h b/codemp/qcommon/vm_local.h
new file mode 100644
index 0000000000..1b1f224b15
--- /dev/null
+++ b/codemp/qcommon/vm_local.h
@@ -0,0 +1,194 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+
+#include "qcommon.h"
+#include "qfiles.h"
+#include
+
+// Max number of arguments to pass from engine to vm's vmMain function.
+// command number + 12 arguments
+#define MAX_VMMAIN_ARGS 13
+
+// Max number of arguments to pass from a vm to engine's syscall handler function for the vm.
+// syscall number + 15 arguments
+#define MAX_VMSYSCALL_ARGS 16
+
+// don't change, this is hardcoded into x86 VMs, opStack protection relies
+// on this
+#define OPSTACK_SIZE 1024
+#define OPSTACK_MASK (OPSTACK_SIZE-1)
+
+// don't change
+// Hardcoded in q3asm a reserved at end of bss
+#define PROGRAM_STACK_SIZE 0x10000
+#define PROGRAM_STACK_MASK (PROGRAM_STACK_SIZE-1)
+
+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
+} opcode_t;
+
+
+
+typedef int vmptr_t;
+
+#define VM_OFFSET_PROGRAM_STACK 0
+#define VM_OFFSET_SYSTEM_CALL 4
+
+
+#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 {
+ VMI_NATIVE,
+ VMI_BYTECODE,
+ VMI_COMPILED
+} vmInterpret_t;
+
+
+extern vm_t *currentVM;
+extern int vm_debugLevel;
+extern qboolean vm_profileInclusive;
+
+void VM_Compile( vm_t *vm, vmHeader_t *header );
+int VM_CallCompiled( vm_t *vm, int *args );
+
+void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header );
+int VM_CallInterpreted( vm_t *vm, 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 );
+const char *VM_SymbolForCompiledPointer( void *code );
+void VM_LogSyscalls( int *args );
+
+void VM_BlockCopy(unsigned int dest, unsigned int src, size_t n);
+
+void VM_Debug( int level );
+
+void VM_LoadSymbols( vm_t *vm );
+void VM_VmInfo_f( void );
+void VM_VmProfile_f( void );
+
+#define QVM_EXTRA_MEMORY_AMOUNT 8192
+
+void VM_ExtraMemory_Clear( vm_t *vm );
+void *VM_ExtraMemory_Claim( vm_t *vm, int amount );
+void VM_ExtraMemory_Release( vm_t *vm, int amount );
+void *VM_ExtraMemory_ClaimData( vm_t *vm, const void *data, uint32_t size );
+char *VM_ExtraMemory_ClaimString( vm_t *vm, const char *inputString );
+size_t VM_PtrToOffset( vm_t *vm, void *ptr );
+qboolean VM_IsCurrentQVM( void );
diff --git a/codemp/qcommon/vm_x86.cpp b/codemp/qcommon/vm_x86.cpp
new file mode 100644
index 0000000000..104e26b455
--- /dev/null
+++ b/codemp/qcommon/vm_x86.cpp
@@ -0,0 +1,1839 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+// vm_x86.c -- load time compiler and execution environment for x86
+
+#include "vm_local.h"
+#include
+
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include
+#else
+ #ifdef __FreeBSD__
+ #include
+ #endif
+
+ #include // for PROT_ stuff
+
+ /* need this on NX enabled systems (i386 with PAE kernel or
+ * noexec32=on x86_64) */
+ #define VM_X86_MMAP
+
+ // workaround for systems that use the old MAP_ANON macro
+ #ifndef MAP_ANONYMOUS
+ #define MAP_ANONYMOUS MAP_ANON
+ #endif
+#endif
+
+static void VM_Destroy_Compiled(vm_t* self);
+
+/*
+
+ eax scratch
+ ebx/bl opStack offset
+ ecx scratch (required for shifts)
+ edx scratch (required for divisions)
+ esi program stack
+ edi opStack base
+x86_64:
+ r8 vm->instructionPointers
+ r9 vm->dataBase
+
+*/
+
+#define VMFREE_BUFFERS() do {Z_Free(buf); Z_Free(jused);} while(0)
+static byte *buf = NULL;
+static byte *jused = NULL;
+static int jusedSize = 0;
+static int compiledOfs = 0;
+static byte *code = NULL;
+static int pc = 0;
+
+static int instruction, pass;
+static int lastConst = 0;
+static int oc0, oc1, pop0, pop1;
+static int jlabel;
+
+typedef enum
+{
+ LAST_COMMAND_NONE = 0,
+ LAST_COMMAND_MOV_STACK_EAX,
+ LAST_COMMAND_SUB_BL_1,
+ LAST_COMMAND_SUB_BL_2,
+} ELastCommand;
+
+typedef enum
+{
+ VM_JMP_VIOLATION = 0,
+ VM_BLOCK_COPY = 1
+} ESysCallType;
+
+static ELastCommand LastCommand;
+
+static int iss8(int32_t v)
+{
+ return (SCHAR_MIN <= v && v <= SCHAR_MAX);
+}
+
+#if 0
+static int isu8(uint32_t v)
+{
+ return (v <= UCHAR_MAX);
+}
+#endif
+
+static int NextConstant4(void)
+{
+ return (code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24));
+}
+
+static int Constant4( void ) {
+ int v;
+
+ v = NextConstant4();
+ pc += 4;
+ return v;
+}
+
+static int Constant1( void ) {
+ int v;
+
+ v = code[pc];
+ pc += 1;
+ return v;
+}
+
+static void Emit1( int v )
+{
+ buf[ compiledOfs ] = v;
+ compiledOfs++;
+
+ LastCommand = LAST_COMMAND_NONE;
+}
+
+static void Emit2(int v)
+{
+ Emit1(v & 255);
+ Emit1((v >> 8) & 255);
+}
+
+
+static void Emit4(int v)
+{
+ Emit1(v & 0xFF);
+ Emit1((v >> 8) & 0xFF);
+ Emit1((v >> 16) & 0xFF);
+ Emit1((v >> 24) & 0xFF);
+}
+
+static void EmitPtr(void *ptr)
+{
+ intptr_t v = (intptr_t) ptr;
+
+ Emit4(v);
+#if defined(idx64)
+ Emit1((v >> 32) & 0xFF);
+ Emit1((v >> 40) & 0xFF);
+ Emit1((v >> 48) & 0xFF);
+ Emit1((v >> 56) & 0xFF);
+#endif
+}
+
+static int Hex( int c ) {
+ if ( c >= 'a' && c <= 'f' ) {
+ return 10 + c - 'a';
+ }
+ if ( c >= 'A' && c <= 'F' ) {
+ return 10 + c - 'A';
+ }
+ if ( c >= '0' && c <= '9' ) {
+ return c - '0';
+ }
+
+ VMFREE_BUFFERS();
+ Com_Error( ERR_DROP, "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(byte rex, const char *string)
+{
+#if defined(idx64)
+ if(rex)
+ Emit1(rex);
+#endif
+
+ EmitString(string);
+}
+
+
+#define MASK_REG(modrm, mask) \
+ do { \
+ EmitString("81"); \
+ EmitString((modrm)); \
+ Emit4((mask)); \
+ } while (0)
+
+// add bl, bytes
+#define STACK_PUSH(bytes) \
+ do { \
+ EmitString("80 C3"); \
+ Emit1(bytes); \
+ } while (0)
+
+// sub bl, bytes
+#define STACK_POP(bytes) \
+ do { \
+ EmitString("80 EB"); \
+ Emit1(bytes); \
+ } while (0)
+
+static void EmitCommand(ELastCommand command)
+{
+ switch(command)
+ {
+ case LAST_COMMAND_MOV_STACK_EAX:
+ EmitString("89 04 9F"); // mov dword ptr [edi + ebx * 4], eax
+ break;
+
+ case LAST_COMMAND_SUB_BL_1:
+ STACK_POP(1); // sub bl, 1
+ break;
+
+ case LAST_COMMAND_SUB_BL_2:
+ STACK_POP(2); // sub bl, 2
+ break;
+ default:
+ break;
+ }
+ LastCommand = command;
+}
+
+static void EmitPushStack(vm_t *vm)
+{
+ if (!jlabel)
+ {
+ if(LastCommand == LAST_COMMAND_SUB_BL_1)
+ { // sub bl, 1
+ compiledOfs -= 3;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ return;
+ }
+ if(LastCommand == LAST_COMMAND_SUB_BL_2)
+ { // sub bl, 2
+ compiledOfs -= 3;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ STACK_POP(1); // sub bl, 1
+ return;
+ }
+ }
+
+ STACK_PUSH(1); // add bl, 1
+}
+
+static void EmitMovEAXStack(vm_t *vm, int andit)
+{
+ if(!jlabel)
+ {
+ if(LastCommand == LAST_COMMAND_MOV_STACK_EAX)
+ { // mov [edi + ebx * 4], eax
+ compiledOfs -= 3;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ }
+ else if(pop1 == OP_CONST && buf[compiledOfs-7] == 0xC7 && buf[compiledOfs-6] == 0x04 && buf[compiledOfs - 5] == 0x9F)
+ { // mov [edi + ebx * 4], 0x12345678
+ compiledOfs -= 7;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ EmitString("B8"); // mov eax, 0x12345678
+
+ if(andit)
+ Emit4(lastConst & andit);
+ else
+ Emit4(lastConst);
+
+ return;
+ }
+ else if(pop1 != OP_DIVI && pop1 != OP_DIVU && pop1 != OP_MULI && pop1 != OP_MULU &&
+ pop1 != OP_STORE4 && pop1 != OP_STORE2 && pop1 != OP_STORE1)
+ {
+ EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
+ }
+ }
+ else
+ EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
+
+ if(andit)
+ {
+ EmitString("25"); // and eax, 0x12345678
+ Emit4(andit);
+ }
+}
+
+void EmitMovECXStack(vm_t *vm)
+{
+ if(!jlabel)
+ {
+ if(LastCommand == LAST_COMMAND_MOV_STACK_EAX) // mov [edi + ebx * 4], eax
+ {
+ compiledOfs -= 3;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ EmitString("89 C1"); // mov ecx, eax
+ return;
+ }
+ if(pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU ||
+ pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1)
+ {
+ EmitString("89 C1"); // mov ecx, eax
+ return;
+ }
+ }
+
+ EmitString("8B 0C 9F"); // mov ecx, dword ptr [edi + ebx * 4]
+}
+
+
+void EmitMovEDXStack(vm_t *vm, int andit)
+{
+ if(!jlabel)
+ {
+ if(LastCommand == LAST_COMMAND_MOV_STACK_EAX)
+ { // mov dword ptr [edi + ebx * 4], eax
+ compiledOfs -= 3;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+
+ EmitString("8B D0"); // mov edx, eax
+ }
+ else if(pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU ||
+ pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1)
+ {
+ EmitString("8B D0"); // mov edx, eax
+ }
+ else if(pop1 == OP_CONST && buf[compiledOfs-7] == 0xC7 && buf[compiledOfs-6] == 0x07 && buf[compiledOfs - 5] == 0x9F)
+ { // mov dword ptr [edi + ebx * 4], 0x12345678
+ compiledOfs -= 7;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ EmitString("BA"); // mov edx, 0x12345678
+
+ if(andit)
+ Emit4(lastConst & andit);
+ else
+ Emit4(lastConst);
+
+ return;
+ }
+ else
+ EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
+
+ }
+ else
+ EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
+
+ if(andit)
+ MASK_REG("E2", andit); // and edx, 0x12345678
+}
+
+#define JUSED(x) \
+ do { \
+ if (x < 0 || x >= vm->instructionCount) { \
+ VMFREE_BUFFERS(); \
+ Com_Error( ERR_DROP, \
+ "VM_CompileX86: jump target out of range at offset %d", pc ); \
+ } \
+ jused[x] = 1; \
+ } while(0)
+
+#define SET_JMPOFS(x) do { buf[(x)] = compiledOfs - ((x) + 1); } while(0)
+
+
+/*
+=================
+ErrJump
+Error handler for jump/call to invalid instruction number
+=================
+*/
+
+static void ErrJump(void)
+{
+ Com_Error(ERR_DROP, "program tried to execute code outside VM");
+}
+
+/*
+=================
+DoSyscall
+
+Assembler helper routines will write its arguments directly to global variables so as to
+work around different calling conventions
+=================
+*/
+
+int vm_syscallNum;
+int vm_programStack;
+int *vm_opStackBase;
+uint8_t vm_opStackOfs;
+intptr_t vm_arg;
+
+void DoSyscall(void)
+{
+ vm_t *savedVM;
+
+ // save currentVM so as to allow for recursive VM entry
+ savedVM = currentVM;
+ // modify VM stack pointer for recursive VM entry
+ currentVM->programStack = vm_programStack - 4;
+
+ if(vm_syscallNum < 0)
+ {
+ int *data, *ret;
+#if defined(idx64)
+ intptr_t args[MAX_VMSYSCALL_ARGS];
+#endif
+
+ data = (int *) (savedVM->dataBase + vm_programStack + 4);
+ ret = &vm_opStackBase[vm_opStackOfs + 1 ];
+
+#if defined(idx64)
+ args[0] = ~vm_syscallNum;
+ for(size_t index = 1; index < ARRAY_LEN(args); index++)
+ args[index] = data[index];
+
+ *ret = savedVM->legacy.syscall(args);
+#else
+ data[0] = ~vm_syscallNum;
+ *ret = savedVM->legacy.syscall((intptr_t *) data);
+#endif
+ }
+ else
+ {
+ switch(vm_syscallNum)
+ {
+ case VM_JMP_VIOLATION:
+ ErrJump();
+ break;
+ case VM_BLOCK_COPY:
+ if(vm_opStackOfs < 1)
+ Com_Error(ERR_DROP, "VM_BLOCK_COPY failed due to corrupted opStack");
+
+ VM_BlockCopy(vm_opStackBase[(vm_opStackOfs - 1)], vm_opStackBase[vm_opStackOfs], vm_arg);
+ break;
+ default:
+ Com_Error(ERR_DROP, "Unknown VM operation %d", vm_syscallNum);
+ break;
+ }
+ }
+
+ currentVM = savedVM;
+}
+
+/*
+=================
+EmitCallRel
+Relative call to vm->codeBase + callOfs
+=================
+*/
+
+void EmitCallRel(vm_t *vm, int callOfs)
+{
+ EmitString("E8"); // call 0x12345678
+ Emit4(callOfs - compiledOfs - 4);
+}
+
+/*
+=================
+EmitCallDoSyscall
+Call to DoSyscall()
+=================
+*/
+
+int EmitCallDoSyscall(vm_t *vm)
+{
+ // save frame pointer
+ EmitString("55"); // push ebp
+ EmitRexString(0x48, "89 E5"); // mov ebp, esp
+
+ // use edx register to store DoSyscall address
+ EmitRexString(0x48, "BA"); // mov edx, DoSyscall
+ EmitPtr((void*)DoSyscall);
+
+ // Push important registers to stack as we can't really make
+ // any assumptions about calling conventions.
+ EmitString("51"); // push ecx
+ EmitString("53"); // push ebx
+ EmitString("56"); // push esi
+ EmitString("57"); // push edi
+#if defined(idx64)
+ EmitRexString(0x41, "50"); // push r8
+ EmitRexString(0x41, "51"); // push r9
+ EmitRexString(0x41, "54"); // push r12
+ EmitRexString(0x41, "55"); // push r13
+ EmitRexString(0x41, "56"); // push r14
+ EmitRexString(0x41, "57"); // push r15
+#endif
+
+ // write arguments to global vars
+ // syscall number
+ EmitString("A3"); // mov [0x12345678], eax
+ EmitPtr(&vm_syscallNum);
+ // vm_programStack value
+ EmitString("89 F0"); // mov eax, esi
+ EmitString("A3"); // mov [0x12345678], eax
+ EmitPtr(&vm_programStack);
+ // vm_opStackOfs
+ EmitString("88 D8"); // mov al, bl
+ EmitString("A2"); // mov [0x12345678], al
+ EmitPtr(&vm_opStackOfs);
+ // vm_opStackBase
+ EmitRexString(0x48, "89 F8"); // mov eax, edi
+ EmitRexString(0x48, "A3"); // mov [0x12345678], eax
+ EmitPtr(&vm_opStackBase);
+ // vm_arg
+ EmitString("89 C8"); // mov eax, ecx
+ EmitString("A3"); // mov [0x12345678], eax
+ EmitPtr(&vm_arg);
+
+ // align the stack pointer to a 16-byte-boundary
+#if defined(_MSC_VER) && defined(idx64)
+ EmitRexString(0x48, "89 E6"); // mov esi, esp
+ EmitRexString(0x48, "83 E4 F0"); // and esp, 0xFFFFFFF0
+#else
+ EmitRexString(0x48, "89 E0"); // mov eax, esp
+ EmitString("51"); // push ecx (decrease esp in a portable way)
+ EmitRexString(0x48, "83 E4 F0"); // and esp, 0xFFFFFFF0
+ EmitString("59"); // pop ecx (increase esp in a portable way)
+ EmitString("50"); // push eax
+#endif
+
+ // call the syscall wrapper function DoSyscall()
+
+ EmitString("FF D2"); // call edx
+
+ // reset the stack pointer to its previous value
+#if defined(_MSC_VER) && defined(idx64)
+ EmitRexString(0x48, "89 F4"); // mov esp, esi
+#else
+ EmitString("5C"); // pop esp
+#endif
+#if defined(idx64)
+ EmitRexString(0x41, "5F"); // pop r15
+ EmitRexString(0x41, "5E"); // pop r14
+ EmitRexString(0x41, "5D"); // pop r13
+ EmitRexString(0x41, "5C"); // pop r12
+ EmitRexString(0x41, "59"); // pop r9
+ EmitRexString(0x41, "58"); // pop r8
+#endif
+ EmitString("5F"); // pop edi
+ EmitString("5E"); // pop esi
+ EmitString("5B"); // pop ebx
+ EmitString("59"); // pop ecx
+
+ // restore frame pointer
+ EmitString("5D"); // pop ebp
+ EmitString("C3"); // ret
+
+ return compiledOfs;
+}
+
+/*
+=================
+EmitCallErrJump
+Emit the code that triggers execution of the jump violation handler
+=================
+*/
+
+static void EmitCallErrJump(vm_t *vm, int sysCallOfs)
+{
+ EmitString("B8"); // mov eax, 0x12345678
+ Emit4(VM_JMP_VIOLATION);
+
+ EmitCallRel(vm, sysCallOfs);
+}
+
+/*
+=================
+EmitCallProcedure
+VM OP_CALL procedure for call destinations obtained at runtime
+=================
+*/
+
+int EmitCallProcedure(vm_t *vm, int sysCallOfs)
+{
+ int jmpSystemCall, jmpBadAddr;
+ int retval;
+
+ // Save frame pointer
+ EmitString("55"); // push ebp
+ EmitRexString(0x48, "89 E5"); // mov ebp, esp
+
+ EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
+ STACK_POP(1); // sub bl, 1
+ EmitString("85 C0"); // test eax, eax
+
+ // Jump to syscall code, 1 byte offset should suffice
+ EmitString("7C"); // jl systemCall
+ jmpSystemCall = compiledOfs++;
+
+ /************ Call inside VM ************/
+
+ EmitString("81 F8"); // cmp eax, vm->instructionCount
+ Emit4(vm->instructionCount);
+
+ // Error jump if invalid jump target
+ EmitString("73"); // jae badAddr
+ jmpBadAddr = compiledOfs++;
+
+#if defined(idx64)
+ EmitRexString(0x49, "FF 14 C0"); // call qword ptr [r8 + eax * 8]
+#else
+ EmitString("FF 14 85"); // call dword ptr [vm->instructionPointers + eax * 4]
+ Emit4((intptr_t) vm->instructionPointers);
+#endif
+ EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("5D"); // pop ebp
+ EmitString("C3"); // ret
+
+ // badAddr:
+ SET_JMPOFS(jmpBadAddr);
+ EmitCallErrJump(vm, sysCallOfs);
+
+ /************ System Call ************/
+
+ // systemCall:
+ SET_JMPOFS(jmpSystemCall);
+ retval = compiledOfs;
+
+ EmitCallRel(vm, sysCallOfs);
+
+ // have opStack reg point at return value
+ STACK_PUSH(1); // add bl, 1
+ EmitString("5D"); // pop ebp
+ EmitString("C3"); // ret
+
+ return retval;
+}
+
+/*
+=================
+EmitJumpIns
+Jump to constant instruction number
+=================
+*/
+
+void EmitJumpIns(vm_t *vm, const char *jmpop, int cdest)
+{
+ JUSED(cdest);
+
+ EmitString(jmpop); // j??? 0x12345678
+
+ // we only know all the jump addresses in the third pass
+ if(pass == 2)
+ Emit4(vm->instructionPointers[cdest] - compiledOfs - 4);
+ else
+ compiledOfs += 4;
+}
+
+/*
+=================
+EmitCallIns
+Call to constant instruction number
+=================
+*/
+
+void EmitCallIns(vm_t *vm, int cdest)
+{
+ JUSED(cdest);
+
+ EmitString("E8"); // call 0x12345678
+
+ // we only know all the jump addresses in the third pass
+ if(pass == 2)
+ Emit4(vm->instructionPointers[cdest] - compiledOfs - 4);
+ else
+ compiledOfs += 4;
+}
+
+/*
+=================
+EmitCallConst
+Call to constant instruction number or syscall
+=================
+*/
+
+void EmitCallConst(vm_t *vm, int cdest, int callProcOfsSyscall)
+{
+ if(cdest < 0)
+ {
+ EmitString("B8"); // mov eax, cdest
+ Emit4(cdest);
+
+ EmitCallRel(vm, callProcOfsSyscall);
+ }
+ else
+ EmitCallIns(vm, cdest);
+}
+
+/*
+=================
+EmitBranchConditions
+Emits x86 branch condition as given in op
+=================
+*/
+void EmitBranchConditions(vm_t *vm, int op)
+{
+ switch(op)
+ {
+ case OP_EQ:
+ EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
+ break;
+ case OP_NE:
+ EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
+ break;
+ case OP_LTI:
+ EmitJumpIns(vm, "0F 8C", Constant4()); // jl 0x12345678
+ break;
+ case OP_LEI:
+ EmitJumpIns(vm, "0F 8E", Constant4()); // jle 0x12345678
+ break;
+ case OP_GTI:
+ EmitJumpIns(vm, "0F 8F", Constant4()); // jg 0x12345678
+ break;
+ case OP_GEI:
+ EmitJumpIns(vm, "0F 8D", Constant4()); // jge 0x12345678
+ break;
+ case OP_LTU:
+ EmitJumpIns(vm, "0F 82", Constant4()); // jb 0x12345678
+ break;
+ case OP_LEU:
+ EmitJumpIns(vm, "0F 86", Constant4()); // jbe 0x12345678
+ break;
+ case OP_GTU:
+ EmitJumpIns(vm, "0F 87", Constant4()); // ja 0x12345678
+ break;
+ case OP_GEU:
+ EmitJumpIns(vm, "0F 83", Constant4()); // jae 0x12345678
+ break;
+ }
+}
+
+
+/*
+=================
+ConstOptimize
+Constant values for immediately following instructions may be translated to immediate values
+instead of opStack operations, which will save expensive operations on memory
+=================
+*/
+
+qboolean ConstOptimize(vm_t *vm, int callProcOfsSyscall)
+{
+ int v;
+ int op1;
+
+ // we can safely perform optimizations only in case if
+ // we are 100% sure that next instruction is not a jump label
+ if (vm->jumpTableTargets && !jused[instruction])
+ op1 = code[pc+4];
+ else
+ return qfalse;
+
+ switch ( op1 ) {
+
+ case OP_LOAD4:
+ EmitPushStack(vm);
+#if defined(idx64)
+ EmitRexString(0x41, "8B 81"); // mov eax, dword ptr [r9 + 0x12345678]
+ Emit4(Constant4() & vm->dataMask);
+#else
+ EmitString("B8"); // mov eax, 0x12345678
+ EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
+ EmitString("8B 00"); // mov eax, dword ptr [eax]
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+
+ pc++; // OP_LOAD4
+ instruction += 1;
+ return qtrue;
+
+ case OP_LOAD2:
+ EmitPushStack(vm);
+#if defined(idx64)
+ EmitRexString(0x41, "0F B7 81"); // movzx eax, word ptr [r9 + 0x12345678]
+ Emit4(Constant4() & vm->dataMask);
+#else
+ EmitString("B8"); // mov eax, 0x12345678
+ EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
+ EmitString("0F B7 00"); // movzx eax, word ptr [eax]
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+
+ pc++; // OP_LOAD2
+ instruction += 1;
+ return qtrue;
+
+ case OP_LOAD1:
+ EmitPushStack(vm);
+#if defined(idx64)
+ EmitRexString(0x41, "0F B6 81"); // movzx eax, byte ptr [r9 + 0x12345678]
+ Emit4(Constant4() & vm->dataMask);
+#else
+ EmitString("B8"); // mov eax, 0x12345678
+ EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
+ EmitString("0F B6 00"); // movzx eax, byte ptr [eax]
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+
+ pc++; // OP_LOAD1
+ instruction += 1;
+ return qtrue;
+
+ case OP_STORE4:
+ EmitMovEAXStack(vm, (vm->dataMask & ~3));
+#if defined(idx64)
+ EmitRexString(0x41, "C7 04 01"); // mov dword ptr [r9 + eax], 0x12345678
+ Emit4(Constant4());
+#else
+ EmitString("C7 80"); // mov dword ptr [eax + 0x12345678], 0x12345678
+ Emit4((intptr_t) vm->dataBase);
+ Emit4(Constant4());
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ pc++; // OP_STORE4
+ instruction += 1;
+ return qtrue;
+
+ case OP_STORE2:
+ EmitMovEAXStack(vm, (vm->dataMask & ~1));
+#if defined(idx64)
+ Emit1(0x66); // mov word ptr [r9 + eax], 0x1234
+ EmitRexString(0x41, "C7 04 01");
+ Emit2(Constant4());
+#else
+ EmitString("66 C7 80"); // mov word ptr [eax + 0x12345678], 0x1234
+ Emit4((intptr_t) vm->dataBase);
+ Emit2(Constant4());
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+
+ pc++; // OP_STORE2
+ instruction += 1;
+ return qtrue;
+
+ case OP_STORE1:
+ EmitMovEAXStack(vm, vm->dataMask);
+#if defined(idx64)
+ EmitRexString(0x41, "C6 04 01"); // mov byte [r9 + eax], 0x12
+ Emit1(Constant4());
+#else
+ EmitString("C6 80"); // mov byte ptr [eax + 0x12345678], 0x12
+ Emit4((intptr_t) vm->dataBase);
+ Emit1(Constant4());
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+
+ pc++; // OP_STORE1
+ instruction += 1;
+ return qtrue;
+
+ case OP_ADD:
+ v = Constant4();
+
+ EmitMovEAXStack(vm, 0);
+ if(iss8(v))
+ {
+ EmitString("83 C0"); // add eax, 0x7F
+ Emit1(v);
+ }
+ else
+ {
+ EmitString("05"); // add eax, 0x12345678
+ Emit4(v);
+ }
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc++; // OP_ADD
+ instruction += 1;
+ return qtrue;
+
+ case OP_SUB:
+ v = Constant4();
+
+ EmitMovEAXStack(vm, 0);
+ if(iss8(v))
+ {
+ EmitString("83 E8"); // sub eax, 0x7F
+ Emit1(v);
+ }
+ else
+ {
+ EmitString("2D"); // sub eax, 0x12345678
+ Emit4(v);
+ }
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc++; // OP_SUB
+ instruction += 1;
+ return qtrue;
+
+ case OP_MULI:
+ v = Constant4();
+
+ EmitMovEAXStack(vm, 0);
+ if(iss8(v))
+ {
+ EmitString("6B C0"); // imul eax, 0x7F
+ Emit1(v);
+ }
+ else
+ {
+ EmitString("69 C0"); // imul eax, 0x12345678
+ Emit4(v);
+ }
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+ pc++; // OP_MULI
+ instruction += 1;
+
+ return qtrue;
+
+ case OP_LSH:
+ v = NextConstant4();
+ if(v < 0 || v > 31)
+ break;
+
+ EmitMovEAXStack(vm, 0);
+ EmitString("C1 E0"); // shl eax, 0x12
+ Emit1(v);
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc += 5; // CONST + OP_LSH
+ instruction += 1;
+ return qtrue;
+
+ case OP_RSHI:
+ v = NextConstant4();
+ if(v < 0 || v > 31)
+ break;
+
+ EmitMovEAXStack(vm, 0);
+ EmitString("C1 F8"); // sar eax, 0x12
+ Emit1(v);
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc += 5; // CONST + OP_RSHI
+ instruction += 1;
+ return qtrue;
+
+ case OP_RSHU:
+ v = NextConstant4();
+ if(v < 0 || v > 31)
+ break;
+
+ EmitMovEAXStack(vm, 0);
+ EmitString("C1 E8"); // shr eax, 0x12
+ Emit1(v);
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc += 5; // CONST + OP_RSHU
+ instruction += 1;
+ return qtrue;
+
+ case OP_BAND:
+ v = Constant4();
+
+ EmitMovEAXStack(vm, 0);
+ if(iss8(v))
+ {
+ EmitString("83 E0"); // and eax, 0x7F
+ Emit1(v);
+ }
+ else
+ {
+ EmitString("25"); // and eax, 0x12345678
+ Emit4(v);
+ }
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc += 1; // OP_BAND
+ instruction += 1;
+ return qtrue;
+
+ case OP_BOR:
+ v = Constant4();
+
+ EmitMovEAXStack(vm, 0);
+ if(iss8(v))
+ {
+ EmitString("83 C8"); // or eax, 0x7F
+ Emit1(v);
+ }
+ else
+ {
+ EmitString("0D"); // or eax, 0x12345678
+ Emit4(v);
+ }
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc += 1; // OP_BOR
+ instruction += 1;
+ return qtrue;
+
+ case OP_BXOR:
+ v = Constant4();
+
+ EmitMovEAXStack(vm, 0);
+ if(iss8(v))
+ {
+ EmitString("83 F0"); // xor eax, 0x7F
+ Emit1(v);
+ }
+ else
+ {
+ EmitString("35"); // xor eax, 0x12345678
+ Emit4(v);
+ }
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+
+ pc += 1; // OP_BXOR
+ instruction += 1;
+ return qtrue;
+
+ 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:
+ EmitMovEAXStack(vm, 0);
+ EmitCommand(LAST_COMMAND_SUB_BL_1);
+ EmitString("3D"); // cmp eax, 0x12345678
+ Emit4(Constant4());
+
+ pc++; // OP_*
+ EmitBranchConditions(vm, op1);
+ instruction++;
+
+ return qtrue;
+
+ case OP_EQF:
+ case OP_NEF:
+ if(NextConstant4())
+ break;
+ pc += 5; // CONST + OP_EQF|OP_NEF
+
+ EmitMovEAXStack(vm, 0);
+ EmitCommand(LAST_COMMAND_SUB_BL_1);
+ // floating point hack :)
+ EmitString("25"); // and eax, 0x7FFFFFFF
+ Emit4(0x7FFFFFFF);
+ if(op1 == OP_EQF)
+ EmitJumpIns(vm, "0F 84", Constant4()); // jz 0x12345678
+ else
+ EmitJumpIns(vm, "0F 85", Constant4()); // jnz 0x12345678
+
+ instruction += 1;
+ return qtrue;
+
+
+ case OP_JUMP:
+ EmitJumpIns(vm, "E9", Constant4()); // jmp 0x12345678
+
+ pc += 1; // OP_JUMP
+ instruction += 1;
+ return qtrue;
+
+ case OP_CALL:
+ v = Constant4();
+ EmitCallConst(vm, v, callProcOfsSyscall);
+
+ pc += 1; // OP_CALL
+ instruction += 1;
+ return qtrue;
+
+ default:
+ break;
+ }
+
+ return qfalse;
+}
+
+/*
+=================
+VM_Compile
+=================
+*/
+void VM_Compile(vm_t *vm, vmHeader_t *header)
+{
+ int op;
+ int maxLength;
+ int v;
+ int i;
+ int callProcOfsSyscall, callProcOfs, callDoSyscallOfs;
+
+ jusedSize = header->instructionCount + 2;
+
+ // allocate a very large temp buffer, we will shrink it later
+ maxLength = header->codeLength * 8 + 64;
+ buf = (byte *)Z_Malloc(maxLength, TAG_VM, qtrue);
+ jused = (byte *)Z_Malloc(jusedSize, TAG_VM, qtrue);
+ code = (byte *)Z_Malloc(header->codeLength + 32, TAG_VM, qtrue);
+
+ Com_Memset(jused, 0, jusedSize);
+ Com_Memset(buf, 0, maxLength);
+
+ // copy code in larger buffer and put some zeros at the end
+ // so we can safely look ahead for a few instructions in it
+ // without a chance to get false-positive because of some garbage bytes
+ Com_Memset(code, 0, header->codeLength+32);
+ Com_Memcpy(code, (byte *)header + header->codeOffset, header->codeLength );
+
+ // ensure that the optimisation pass knows about all the jump
+ // table targets
+ pc = -1; // a bogus value to be printed in out-of-bounds error messages
+ for( i = 0; i < vm->numJumpTableTargets; i++ ) {
+ JUSED( *(int *)(vm->jumpTableTargets + ( i * sizeof( int ) ) ) );
+ }
+
+ // Start buffer with x86-VM specific procedures
+ compiledOfs = 0;
+
+ callDoSyscallOfs = compiledOfs;
+ callProcOfs = EmitCallDoSyscall(vm);
+ callProcOfsSyscall = EmitCallProcedure(vm, callDoSyscallOfs);
+ vm->entryOfs = compiledOfs;
+ vm->callProcOfs = callProcOfs;
+ vm->callProcOfsSyscall = callProcOfsSyscall;
+
+ for(pass=0; pass < 3; pass++) {
+ oc0 = -23423;
+ oc1 = -234354;
+ pop0 = -43435;
+ pop1 = -545455;
+
+ // translate all instructions
+ pc = 0;
+ instruction = 0;
+ //code = (byte *)header + header->codeOffset;
+ compiledOfs = vm->entryOfs;
+
+ LastCommand = LAST_COMMAND_NONE;
+
+ while(instruction < header->instructionCount)
+ {
+ if(compiledOfs > maxLength - 16)
+ {
+ VMFREE_BUFFERS();
+ Com_Error(ERR_DROP, "VM_CompileX86: maxLength exceeded");
+ }
+
+ vm->instructionPointers[ instruction ] = compiledOfs;
+
+ if ( !vm->jumpTableTargets )
+ jlabel = 1;
+ else
+ jlabel = jused[ instruction ];
+
+ instruction++;
+
+ if(pc > header->codeLength)
+ {
+ VMFREE_BUFFERS();
+ Com_Error(ERR_DROP, "VM_CompileX86: pc > header->codeLength");
+ }
+
+ op = code[ pc ];
+ pc++;
+ switch ( op ) {
+ case 0:
+ break;
+ case OP_BREAK:
+ EmitString("CC"); // int 3
+ break;
+ case OP_ENTER:
+ // save frame pointer
+ EmitString("55"); // push ebp
+ EmitRexString(0x48, "89 E5"); // mov ebp, esp
+ EmitString("81 EE"); // sub esi, 0x12345678
+ Emit4(Constant4());
+ break;
+ case OP_CONST:
+ if(ConstOptimize(vm, callProcOfsSyscall))
+ break;
+
+ EmitPushStack(vm);
+ EmitString("C7 04 9F"); // mov dword ptr [edi + ebx * 4], 0x12345678
+ lastConst = Constant4();
+
+ Emit4(lastConst);
+ if(code[pc] == OP_JUMP)
+ JUSED(lastConst);
+
+ break;
+ case OP_LOCAL:
+ EmitPushStack(vm);
+ EmitString("8D 86"); // lea eax, [0x12345678 + esi]
+ oc0 = oc1;
+ oc1 = Constant4();
+ Emit4(oc1);
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ case OP_ARG:
+ EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("8B D6"); // mov edx, esi
+ EmitString("81 C2"); // add edx, 0x12345678
+ Emit4((Constant1() & 0xFF));
+ MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
+#if defined(idx64)
+ EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
+#else
+ EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_CALL:
+ EmitCallRel(vm, callProcOfs);
+ break;
+ case OP_PUSH:
+ EmitPushStack(vm);
+ break;
+ case OP_POP:
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_LEAVE:
+ v = Constant4();
+ EmitString("81 C6"); // add esi, 0x12345678
+ Emit4(v);
+ // Restore frame pointer
+ EmitString("5D"); // pop ebp
+ EmitString("C3"); // ret
+ break;
+ case OP_LOAD4:
+ if (code[pc] == OP_CONST && code[pc+5] == OP_ADD && code[pc+6] == OP_STORE4)
+ {
+ if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
+ {
+ compiledOfs -= 12;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ }
+
+ pc++; // OP_CONST
+ v = Constant4();
+
+ EmitMovEDXStack(vm, vm->dataMask);
+ if(v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
+ {
+#if defined(idx64)
+ EmitRexString(0x41, "FF 04 11"); // inc dword ptr [r9 + edx]
+#else
+ EmitString("FF 82"); // inc dword ptr [edx + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ }
+ else
+ {
+#if defined(idx64)
+ EmitRexString(0x41, "8B 04 11"); // mov eax, dword ptr [r9 + edx]
+#else
+ EmitString("8B 82"); // mov eax, dword ptr [edx + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitString("05"); // add eax, v
+ Emit4(v);
+
+ if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
+ {
+#if defined(idx64)
+ EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
+#else
+ EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ }
+ else
+ {
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
+ MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
+#if defined(idx64)
+ EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
+#else
+ EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ }
+ }
+
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ pc++; // OP_ADD
+ pc++; // OP_STORE
+ instruction += 3;
+ break;
+ }
+
+ if(code[pc] == OP_CONST && code[pc+5] == OP_SUB && code[pc+6] == OP_STORE4)
+ {
+ if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
+ {
+ compiledOfs -= 12;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ }
+
+ pc++; // OP_CONST
+ v = Constant4();
+
+ EmitMovEDXStack(vm, vm->dataMask);
+ if(v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
+ {
+#if defined(idx64)
+ EmitRexString(0x41, "FF 0C 11"); // dec dword ptr [r9 + edx]
+#else
+ EmitString("FF 8A"); // dec dword ptr [edx + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ }
+ else
+ {
+#if defined(idx64)
+ EmitRexString(0x41, "8B 04 11"); // mov eax, dword ptr [r9 + edx]
+#else
+ EmitString("8B 82"); // mov eax, dword ptr [edx + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitString("2D"); // sub eax, v
+ Emit4(v);
+
+ if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
+ {
+#if defined(idx64)
+ EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
+#else
+ EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ }
+ else
+ {
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
+ MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
+#if defined(idx64)
+ EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
+#else
+ EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ }
+ }
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ pc++; // OP_SUB
+ pc++; // OP_STORE
+ instruction += 3;
+ break;
+ }
+
+ if(buf[compiledOfs - 3] == 0x89 && buf[compiledOfs - 2] == 0x04 && buf[compiledOfs - 1] == 0x9F)
+ {
+ compiledOfs -= 3;
+ vm->instructionPointers[instruction - 1] = compiledOfs;
+ MASK_REG("E0", vm->dataMask); // and eax, 0x12345678
+#if defined(idx64)
+ EmitRexString(0x41, "8B 04 01"); // mov eax, dword ptr [r9 + eax]
+#else
+ EmitString("8B 80"); // mov eax, dword ptr [eax + 0x1234567]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ }
+
+ EmitMovEAXStack(vm, vm->dataMask);
+#if defined(idx64)
+ EmitRexString(0x41, "8B 04 01"); // mov eax, dword ptr [r9 + eax]
+#else
+ EmitString("8B 80"); // mov eax, dword ptr [eax + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ case OP_LOAD2:
+ EmitMovEAXStack(vm, vm->dataMask);
+#if defined(idx64)
+ EmitRexString(0x41, "0F B7 04 01"); // movzx eax, word ptr [r9 + eax]
+#else
+ EmitString("0F B7 80"); // movzx eax, word ptr [eax + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ case OP_LOAD1:
+ EmitMovEAXStack(vm, vm->dataMask);
+#if defined(idx64)
+ EmitRexString(0x41, "0F B6 04 01"); // movzx eax, byte ptr [r9 + eax]
+#else
+ EmitString("0F B6 80"); // movzx eax, byte ptr [eax + 0x12345678]
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ case OP_STORE4:
+ EmitMovEAXStack(vm, 0);
+ EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
+ MASK_REG("E2", vm->dataMask & ~3); // and edx, 0x12345678
+#if defined(idx64)
+ EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
+#else
+ EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ break;
+ case OP_STORE2:
+ EmitMovEAXStack(vm, 0);
+ EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
+ MASK_REG("E2", vm->dataMask & ~1); // and edx, 0x12345678
+#if defined(idx64)
+ Emit1(0x66); // mov word ptr [r9 + edx], eax
+ EmitRexString(0x41, "89 04 11");
+#else
+ EmitString("66 89 82"); // mov word ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ break;
+ case OP_STORE1:
+ EmitMovEAXStack(vm, 0);
+ EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
+ MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
+#if defined(idx64)
+ EmitRexString(0x41, "88 04 11"); // mov byte ptr [r9 + edx], eax
+#else
+ EmitString("88 82"); // mov byte ptr [edx + 0x12345678], eax
+ Emit4((intptr_t) vm->dataBase);
+#endif
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ 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:
+ EmitMovEAXStack(vm, 0);
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("39 44 9F 04"); // cmp eax, dword ptr 4[edi + ebx * 4]
+
+ EmitBranchConditions(vm, op);
+ break;
+ case OP_EQF:
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("F3 0F 10 44 9F 04"); // movss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("0F 2E 44 9F 08"); // ucomiss xmm0, dword ptr 8[edi + ebx * 4]
+ EmitString("7A 06"); // jp +0x6 (jump over next opcode)
+ EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
+ break;
+ case OP_NEF:
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("F3 0F 10 44 9F 04"); // movss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("0F 2E 44 9F 08"); // ucomiss xmm0, dword ptr 8[edi + ebx * 4]
+ {
+ int dest = Constant4();
+ EmitJumpIns(vm, "0F 8A", dest); // jp 0x12345678
+ EmitJumpIns(vm, "0F 85", dest); // jne 0x12345678
+ }
+ break;
+ case OP_LTF:
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("F3 0F 10 44 9F 08"); // movss xmm0, dword ptr 8[edi + ebx * 4]
+ EmitString("0F 2E 44 9F 04"); // ucomiss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitJumpIns(vm, "0F 87", Constant4()); // ja 0x12345678
+ break;
+ case OP_LEF:
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("F3 0F 10 44 9F 08"); // movss xmm0, dword ptr 8[edi + ebx * 4]
+ EmitString("0F 2E 44 9F 04"); // ucomiss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitJumpIns(vm, "0F 83", Constant4()); // jae 0x12345678
+ break;
+ case OP_GTF:
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("F3 0F 10 44 9F 04"); // movss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("0F 2E 44 9F 08"); // ucomiss xmm0, dword ptr 8[edi + ebx * 4]
+ EmitJumpIns(vm, "0F 87", Constant4()); // ja 0x12345678
+ break;
+ case OP_GEF:
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ EmitString("F3 0F 10 44 9F 04"); // movss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("0F 2E 44 9F 08"); // ucomiss xmm0, dword ptr 8[edi + ebx * 4]
+ EmitJumpIns(vm, "0F 83", Constant4()); // jae 0x12345678
+ break;
+ case OP_NEGI:
+ EmitMovEAXStack(vm, 0);
+ EmitString("F7 D8"); // neg eax
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
+ break;
+ case OP_ADD:
+ EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("01 44 9F FC"); // add dword ptr -4[edi + ebx * 4], eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_SUB:
+ EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("29 44 9F FC"); // sub dword ptr -4[edi + ebx * 4], eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_DIVI:
+ EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
+ EmitString("99"); // cdq
+ EmitString("F7 3C 9F"); // idiv dword ptr [edi + ebx * 4]
+ EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_DIVU:
+ EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
+ EmitString("33 D2"); // xor edx, edx
+ EmitString("F7 34 9F"); // div dword ptr [edi + ebx * 4]
+ EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_MODI:
+ EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
+ EmitString("99" ); // cdq
+ EmitString("F7 3C 9F"); // idiv dword ptr [edi + ebx * 4]
+ EmitString("89 54 9F FC"); // mov dword ptr -4[edi + ebx * 4],edx
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_MODU:
+ EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
+ EmitString("33 D2"); // xor edx, edx
+ EmitString("F7 34 9F"); // div dword ptr [edi + ebx * 4]
+ EmitString("89 54 9F FC"); // mov dword ptr -4[edi + ebx * 4],edx
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_MULI:
+ EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
+ EmitString("F7 2C 9F"); // imul dword ptr [edi + ebx * 4]
+ EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_MULU:
+ EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
+ EmitString("F7 24 9F"); // mul dword ptr [edi + ebx * 4]
+ EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_BAND:
+ EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("21 44 9F FC"); // and dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_BOR:
+ EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("09 44 9F FC"); // or dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_BXOR:
+ EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
+ EmitString("31 44 9F FC"); // xor dword ptr -4[edi + ebx * 4],eax
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_BCOM:
+ EmitString("F7 14 9F"); // not dword ptr [edi + ebx * 4]
+ break;
+ case OP_LSH:
+ EmitMovECXStack(vm);
+ EmitString("D3 64 9F FC"); // shl dword ptr -4[edi + ebx * 4], cl
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_RSHI:
+ EmitMovECXStack(vm);
+ EmitString("D3 7C 9F FC"); // sar dword ptr -4[edi + ebx * 4], cl
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_RSHU:
+ EmitMovECXStack(vm);
+ EmitString("D3 6C 9F FC"); // shr dword ptr -4[edi + ebx * 4], cl
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ break;
+ case OP_NEGF:
+ EmitString("0F 57 C0"); // xorps xmm0, xmm0
+ EmitString("F3 0F 5C 04 9F"); // subss xmm0, dword ptr [edi + ebx * 4]
+ EmitString("F3 0F 11 04 9F"); // storess dword ptr [edi + ebx * 4], xmm0
+ break;
+ case OP_ADDF:
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("F3 0F 10 04 9F"); // movss xmm0, dword ptr [edi + ebx * 4]
+ EmitString("F3 0F 58 44 9F 04"); // addss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("F3 0F 11 04 9F"); // movss dword ptr [edi + ebx * 4], xmm0
+ break;
+ case OP_SUBF:
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("F3 0F 10 04 9F"); // movss xmm0, dword ptr [edi + ebx * 4]
+ EmitString("F3 0F 5C 44 9F 04"); // subss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("F3 0F 11 04 9F"); // movss dword ptr [edi + ebx * 4], xmm0
+ break;
+ case OP_DIVF:
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("F3 0F 10 04 9F"); // movss xmm0, dword ptr [edi + ebx * 4]
+ EmitString("F3 0F 5E 44 9F 04"); // divss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("F3 0F 11 04 9F"); // movss dword ptr [edi + ebx * 4], xmm0
+ break;
+ case OP_MULF:
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("F3 0F 10 04 9F"); // movss xmm0, dword ptr [edi + ebx * 4]
+ EmitString("F3 0F 59 44 9F 04"); // mulss xmm0, dword ptr 4[edi + ebx * 4]
+ EmitString("F3 0F 11 04 9F"); // movss dword ptr [edi + ebx * 4], xmm0
+ break;
+ case OP_CVIF:
+ EmitString("F3 0F 2A 04 9F"); // cvtsi2ss xmm0, dword ptr [edi + ebx * 4]
+ EmitString("F3 0F 11 04 9F"); // movss dword ptr [edi + ebx * 4], xmm0
+ break;
+ case OP_CVFI:
+ EmitString("F3 0F 2C 04 9F"); // cvttss2si eax, dword ptr [edi + ebx *4]
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ case OP_SEX8:
+ EmitString("0F BE 04 9F"); // movsx eax, byte ptr [edi + ebx * 4]
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+ case OP_SEX16:
+ EmitString("0F BF 04 9F"); // movsx eax, word ptr [edi + ebx * 4]
+ EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
+ break;
+
+ case OP_BLOCK_COPY:
+ EmitString("B8"); // mov eax, 0x12345678
+ Emit4(VM_BLOCK_COPY);
+ EmitString("B9"); // mov ecx, 0x12345678
+ Emit4(Constant4());
+
+ EmitCallRel(vm, callDoSyscallOfs);
+
+ EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
+ break;
+
+ case OP_JUMP:
+ EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
+ EmitString("8B 44 9F 04"); // mov eax, dword ptr 4[edi + ebx * 4]
+ EmitString("81 F8"); // cmp eax, vm->instructionCount
+ Emit4(vm->instructionCount);
+#if defined(idx64)
+ EmitString("73 04"); // jae +4
+ EmitRexString(0x49, "FF 24 C0"); // jmp qword ptr [r8 + eax * 8]
+#else
+ EmitString("73 07"); // jae +7
+ EmitString("FF 24 85"); // jmp dword ptr [instructionPointers + eax * 4]
+ Emit4((intptr_t) vm->instructionPointers);
+#endif
+ EmitCallErrJump(vm, callDoSyscallOfs);
+ break;
+ default:
+ VMFREE_BUFFERS();
+ Com_Error(ERR_DROP, "VM_CompileX86: bad opcode %i at offset %i", op, pc);
+ }
+ pop0 = pop1;
+ pop1 = op;
+ }
+ }
+
+ // copy to an exact sized buffer with the appropriate permission bits
+ vm->codeLength = compiledOfs;
+#ifdef VM_X86_MMAP
+ vm->codeBase = (byte *)mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+ if(vm->codeBase == MAP_FAILED)
+ Com_Error(ERR_FATAL, "VM_CompileX86: can't mmap memory");
+#elif _WIN32
+ // allocate memory with EXECUTE permissions under windows.
+ vm->codeBase = (byte*)VirtualAlloc(NULL, compiledOfs, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if(!vm->codeBase)
+ Com_Error(ERR_FATAL, "VM_CompileX86: VirtualAlloc failed");
+#else
+ vm->codeBase = malloc(compiledOfs);
+ if(!vm->codeBase)
+ Com_Error(ERR_FATAL, "VM_CompileX86: malloc failed");
+#endif
+
+ Com_Memcpy( vm->codeBase, buf, compiledOfs );
+
+#ifdef VM_X86_MMAP
+ if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC))
+ Com_Error(ERR_FATAL, "VM_CompileX86: mprotect failed");
+#elif _WIN32
+ {
+ DWORD oldProtect = 0;
+
+ // remove write permissions.
+ if(!VirtualProtect(vm->codeBase, compiledOfs, PAGE_EXECUTE_READ, &oldProtect))
+ Com_Error(ERR_FATAL, "VM_CompileX86: VirtualProtect failed");
+ }
+#endif
+
+ Z_Free( code );
+ Z_Free( buf );
+ Z_Free( jused );
+ Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs );
+
+ vm->destroy = VM_Destroy_Compiled;
+
+ // offset all the instruction pointers for the new location
+ for ( i = 0 ; i < header->instructionCount ; i++ ) {
+ vm->instructionPointers[i] += (intptr_t) vm->codeBase;
+ }
+}
+
+void VM_Destroy_Compiled(vm_t* self)
+{
+#ifdef VM_X86_MMAP
+ munmap(self->codeBase, self->codeLength);
+#elif _WIN32
+ VirtualFree(self->codeBase, 0, MEM_RELEASE);
+#else
+ free(self->codeBase);
+#endif
+}
+
+/*
+==============
+VM_CallCompiled
+
+This function is called directly by the generated code
+==============
+*/
+
+#if defined(_MSC_VER) && defined(idx64)
+extern "C" uint8_t qvmcall64(int *programStack, int *opStack, intptr_t *instructionPointers, byte *dataBase);
+#endif
+
+int VM_CallCompiled(vm_t *vm, int *args)
+{
+ byte stack[OPSTACK_SIZE + 15];
+ void *entryPoint;
+ int programStack, stackOnEntry;
+ byte *image;
+ int *opStack;
+ int opStackOfs;
+ int arg;
+
+ currentVM = vm;
+
+ // interpret the code
+ vm->currentlyInterpreting = qtrue;
+
+ // 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;
+
+ programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
+
+ for ( arg = 0; arg < MAX_VMMAIN_ARGS; arg++ )
+ *(int *)&image[ programStack + 8 + arg * 4 ] = args[ arg ];
+
+ *(int *)&image[ programStack + 4 ] = 0; // return stack
+ *(int *)&image[ programStack ] = -1; // will terminate the loop on return
+
+ // off we go into generated code...
+ entryPoint = vm->codeBase + vm->entryOfs;
+ opStack = (int *)PADP(stack, 16);
+ *(unsigned *)opStack = 0xDEADBEEFu;
+ opStackOfs = 0;
+
+#ifdef _MSC_VER
+ #if defined(idx64)
+ opStackOfs = qvmcall64(&programStack, opStack, vm->instructionPointers, vm->dataBase);
+ #else
+ __asm
+ {
+ pushad
+
+ mov esi, dword ptr programStack
+ mov edi, dword ptr opStack
+ mov ebx, dword ptr opStackOfs
+
+ call entryPoint
+
+ mov dword ptr opStackOfs, ebx
+ mov dword ptr opStack, edi
+ mov dword ptr programStack, esi
+
+ popad
+ }
+ #endif
+#elif defined(idx64)
+ __asm__ volatile(
+ "movq %5, %%rax\n"
+ "movq %3, %%r8\n"
+ "movq %4, %%r9\n"
+ "push %%r15\n"
+ "push %%r14\n"
+ "push %%r13\n"
+ "push %%r12\n"
+ "callq *%%rax\n"
+ "pop %%r12\n"
+ "pop %%r13\n"
+ "pop %%r14\n"
+ "pop %%r15\n"
+ : "+S" (programStack), "+D" (opStack), "+b" (opStackOfs)
+ : "g" (vm->instructionPointers), "g" (vm->dataBase), "g" (entryPoint)
+ : "cc", "memory", "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11"
+ );
+#else
+ __asm__ volatile(
+ "calll *%3\n"
+ : "+S" (programStack), "+D" (opStack), "+b" (opStackOfs)
+ : "g" (entryPoint)
+ : "cc", "memory", "%eax", "%ecx", "%edx"
+ );
+#endif
+
+ if(opStackOfs != 1 || *(unsigned *)opStack != 0xDEADBEEFu)
+ {
+ Com_Error(ERR_DROP, "opStack corrupted in compiled code");
+ }
+ if(programStack != stackOnEntry - (8 + 4 * MAX_VMMAIN_ARGS))
+ Com_Error(ERR_DROP, "programStack corrupted in compiled code");
+
+ vm->programStack = stackOnEntry;
+
+ return opStack[opStackOfs];
+}
diff --git a/codemp/rd-common/tr_types.h b/codemp/rd-common/tr_types.h
index 93c56777d3..5c72722f29 100644
--- a/codemp/rd-common/tr_types.h
+++ b/codemp/rd-common/tr_types.h
@@ -370,3 +370,32 @@ typedef struct glconfig_s {
qboolean isFullscreen;
qboolean stereoEnabled;
} glconfig_t;
+
+// Copy of glconfig_t to use with qvms
+typedef struct {
+ uint32_t renderer_string;
+ uint32_t vendor_string;
+ uint32_t version_string;
+ uint32_t extensions_string;
+
+ int maxTextureSize; // queried from GL
+ int maxActiveTextures; // multitexture ability
+ float maxTextureFilterAnisotropy;
+
+ int colorBits, depthBits, stencilBits;
+
+ qboolean deviceSupportsGamma;
+ textureCompression_t textureCompression;
+ qboolean textureEnvAddAvailable;
+ qboolean clampToEdgeAvailable;
+
+ int vidWidth, vidHeight;
+
+ int displayFrequency;
+
+ // synonymous with "does rendering consume the entire screen?", therefore
+ // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that
+ // used CDS.
+ qboolean isFullscreen;
+ qboolean stereoEnabled;
+} glconfig_qvm_t;
diff --git a/codemp/server/NPCNav/navigator.cpp b/codemp/server/NPCNav/navigator.cpp
index 3331687cd3..055e15a63e 100644
--- a/codemp/server/NPCNav/navigator.cpp
+++ b/codemp/server/NPCNav/navigator.cpp
@@ -939,7 +939,7 @@ void CNavigator::ShowNodes( void )
//if( NAVDEBUG_showRadius )
if (0)
{
- dist = DistanceSquared( SV_GentityNum(0)->r.currentOrigin, position );
+ dist = DistanceSquared( SV_GentityMapperNum(0)->r->currentOrigin, position );
radius = (*ni)->GetRadius();
// if player within node radius or 256, draw radius (sometimes the radius is really small, so we check for 256 to catch everything)
if( (dist <= radius*radius) || dist <= 65536 )
@@ -949,11 +949,11 @@ void CNavigator::ShowNodes( void )
}
else
{
- dist = DistanceSquared( SV_GentityNum(0)->r.currentOrigin, position );
+ dist = DistanceSquared( SV_GentityMapperNum(0)->r->currentOrigin, position );
}
if ( dist < 1048576 )
{
- if ( SV_inPVS( SV_GentityNum(0)->r.currentOrigin, position ) )
+ if ( SV_inPVS( SV_GentityMapperNum(0)->r->currentOrigin, position ) )
{
(*ni)->Draw(showRadius);
}
@@ -981,12 +981,12 @@ void CNavigator::ShowEdges( void )
STL_ITERATE( ni, m_nodes )
{
(*ni)->GetPosition( start );
- if ( DistanceSquared( SV_GentityNum(0)->r.currentOrigin, start ) >= 1048576 )
+ if ( DistanceSquared( SV_GentityMapperNum(0)->r->currentOrigin, start ) >= 1048576 )
{
continue;
}
- if (!SV_inPVS(SV_GentityNum(0)->r.currentOrigin, start))
+ if (!SV_inPVS(SV_GentityMapperNum(0)->r->currentOrigin, start))
{
continue;
}
@@ -1012,12 +1012,12 @@ void CNavigator::ShowEdges( void )
//Set this as drawn
drawMap[id][(*ni)->GetID()] = true;
- if ( DistanceSquared( SV_GentityNum(0)->r.currentOrigin, end ) >= 1048576 )
+ if ( DistanceSquared( SV_GentityMapperNum(0)->r->currentOrigin, end ) >= 1048576 )
{
continue;
}
- if ( !SV_inPVS( SV_GentityNum(0)->r.currentOrigin, end ) )
+ if ( !SV_inPVS( SV_GentityMapperNum(0)->r->currentOrigin, end ) )
continue;
/*
@@ -1156,7 +1156,7 @@ TestNodePath
-------------------------
*/
-int CNavigator::TestNodePath( sharedEntity_t *ent, int okToHitEntNum, vec3_t position, qboolean includeEnts )
+int CNavigator::TestNodePath( sharedEntityMapper_t *ent, int okToHitEntNum, vec3_t position, qboolean includeEnts )
{ //rwwFIXMEFIXME: Share clipmask?
int clipmask = MASK_SOLID;//ent->clipmask;
@@ -1165,7 +1165,7 @@ int CNavigator::TestNodePath( sharedEntity_t *ent, int okToHitEntNum, vec3_t pos
clipmask &= ~CONTENTS_BODY;
}
//Check the path
- if ( GVM_NAV_ClearPathToPoint( ent->s.number, ent->r.mins, ent->r.maxs, position, clipmask, okToHitEntNum ) == false )
+ if ( GVM_NAV_ClearPathToPoint( ent->s->number, ent->r->mins, ent->r->maxs, position, clipmask, okToHitEntNum ) == false )
return false;
return true;
@@ -1177,9 +1177,9 @@ TestNodeLOS
-------------------------
*/
-int CNavigator::TestNodeLOS( sharedEntity_t *ent, vec3_t position )
+int CNavigator::TestNodeLOS( sharedEntityMapper_t *ent, vec3_t position )
{
- return GVM_NPC_ClearLOS2( ent->s.number, position );
+ return GVM_NPC_ClearLOS2( ent->s->number, position );
}
/*
@@ -1188,7 +1188,7 @@ TestBestFirst
-------------------------
*/
-int CNavigator::TestBestFirst( sharedEntity_t *ent, int lastID, int flags )
+int CNavigator::TestBestFirst( sharedEntityMapper_t *ent, int lastID, int flags )
{
//Must be a valid one to begin with
if ( lastID == NODE_NONE )
@@ -1208,7 +1208,7 @@ int CNavigator::TestBestFirst( sharedEntity_t *ent, int lastID, int flags )
//Setup our last node as our root, and search for a closer one according to its edges
int bestNode = ( TestNodePath( ent, ENTITYNUM_NONE, nodePos, qtrue ) ) ? lastID : NODE_NONE;
- float bestDist = ( bestNode == NODE_NONE ) ? Q3_INFINITE : DistanceSquared( ent->r.currentOrigin, nodePos );
+ float bestDist = ( bestNode == NODE_NONE ) ? Q3_INFINITE : DistanceSquared( ent->r->currentOrigin, nodePos );
//Test all these edges first
for ( int i = 0; i < numEdges; i++ )
@@ -1223,21 +1223,21 @@ int CNavigator::TestBestFirst( sharedEntity_t *ent, int lastID, int flags )
testNode->GetPosition( nodePos );
- dist = DistanceSquared( ent->r.currentOrigin, nodePos );
+ dist = DistanceSquared( ent->r->currentOrigin, nodePos );
//Test against current best
if ( dist < bestDist )
{
//See if this node is valid
- if ( CheckedNode(testNode->GetID(),ent->s.number) == CHECKED_PASSED || TestNodePath( ent, ENTITYNUM_NONE, nodePos, qtrue ) )
+ if ( CheckedNode(testNode->GetID(),ent->s->number) == CHECKED_PASSED || TestNodePath( ent, ENTITYNUM_NONE, nodePos, qtrue ) )
{
bestDist = dist;
bestNode = testNode->GetID();
- SetCheckedNode(testNode->GetID(),ent->s.number,CHECKED_PASSED);
+ SetCheckedNode(testNode->GetID(),ent->s->number,CHECKED_PASSED);
}
else
{
- SetCheckedNode(testNode->GetID(),ent->s.number,CHECKED_FAILED);
+ SetCheckedNode(testNode->GetID(),ent->s->number,CHECKED_FAILED);
}
}
}
@@ -1326,7 +1326,7 @@ int CNavigator::CollectNearestNodes( vec3_t origin, int radius, int maxCollect,
return collected;
}
-int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goal, int flags )
+int CNavigator::GetBestPathBetweenEnts( sharedEntityMapper_t *ent, sharedEntityMapper_t *goal, int flags )
{
//Must have nodes
if ( m_nodes.size() == 0 )
@@ -1340,8 +1340,8 @@ int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goa
nodeChain_l::iterator nci2;
//Collect all nodes within a certain radius
- CollectNearestNodes( ent->r.currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain );
- CollectNearestNodes( goal->r.currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain2 );
+ CollectNearestNodes( ent->r->currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain );
+ CollectNearestNodes( goal->r->currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain2 );
vec3_t position;
vec3_t position2;
@@ -1353,8 +1353,8 @@ int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goa
int nodeFlags = 0;
// bool recalc = false;
- ent->waypoint = NODE_NONE;
- goal->waypoint = NODE_NONE;
+ *ent->waypoint = NODE_NONE;
+ *goal->waypoint = NODE_NONE;
//Look through all nodes
STL_ITERATE( nci, nodeChain )
@@ -1364,34 +1364,34 @@ int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goa
node->GetPosition( position );
- if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_FAILED )
+ if ( CheckedNode(nodeNum,ent->s->number) == CHECKED_FAILED )
{//already checked this node against ent and it failed
continue;
}
- if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_PASSED )
+ if ( CheckedNode(nodeNum,ent->s->number) == CHECKED_PASSED )
{//already checked this node against ent and it passed
}
else
{//haven't checked this node against ent yet
if ( NodeFailed( ent, nodeNum ) )
{
- SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED );
+ SetCheckedNode( nodeNum, ent->s->number, CHECKED_FAILED );
continue;
}
//okay, since we only have to do this once, let's check to see if this node is even usable (could help us short-circuit a whole loop of the dest nodes)
radius = node->GetRadius();
//If we're not within the known clear radius of this node OR out of Z height range...
- if ( (int)(*nci).distance >= (radius*radius) || ( fabs( position[2] - ent->r.currentOrigin[2] ) >= MAX_Z_DELTA ) )
+ if ( (int)(*nci).distance >= (radius*radius) || ( fabs( position[2] - ent->r->currentOrigin[2] ) >= MAX_Z_DELTA ) )
{
//We're not *within* this node, so check clear path, etc.
//FIXME: any way to call G_FindClosestPointOnLineSegment and see if I can at least get to the waypoint's path
if ( flags & NF_CLEAR_PATH )//|| flags & NF_CLEAR_LOS )
{//need a clear path or LOS
- if ( !SV_inPVS( ent->r.currentOrigin, position ) )
+ if ( !SV_inPVS( ent->r->currentOrigin, position ) )
{//not even potentially clear
- SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED );
+ SetCheckedNode( nodeNum, ent->s->number, CHECKED_FAILED );
continue;
}
}
@@ -1399,14 +1399,14 @@ int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goa
//Do we need a clear path?
if ( flags & NF_CLEAR_PATH )
{
- if ( TestNodePath( ent, goal->s.number, position, qtrue ) == false )
+ if ( TestNodePath( ent, goal->s->number, position, qtrue ) == false )
{
- SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED );
+ SetCheckedNode( nodeNum, ent->s->number, CHECKED_FAILED );
continue;
}
}
}//otherwise, inside the node so it must be clear (?)
- SetCheckedNode( nodeNum, ent->s.number, CHECKED_PASSED );
+ SetCheckedNode( nodeNum, ent->s->number, CHECKED_PASSED );
}
if ( d_altRoutes->integer )
@@ -1437,7 +1437,7 @@ int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goa
node2->GetPosition( position2 );
//Okay, first get the entire path cost, including distance to first node from ents' positions
- cost = floor(Distance( ent->r.currentOrigin, position ) + Distance( goal->r.currentOrigin, position2 ));
+ cost = floor(Distance( ent->r->currentOrigin, position ) + Distance( goal->r->currentOrigin, position2 ));
if ( d_altRoutes->integer )
{
@@ -1455,60 +1455,60 @@ int CNavigator::GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goa
}
//okay, this is the shortest path we've found yet, check clear path, etc.
- if ( CheckedNode( nodeNum2, goal->s.number ) == CHECKED_FAILED )
+ if ( CheckedNode( nodeNum2, goal->s->number ) == CHECKED_FAILED )
{//already checked this node against goal and it failed
continue;
}
- if ( CheckedNode( nodeNum2, goal->s.number ) == CHECKED_PASSED )
+ if ( CheckedNode( nodeNum2, goal->s->number ) == CHECKED_PASSED )
{//already checked this node against goal and it passed
}
else
{//haven't checked this node against goal yet
if ( NodeFailed( goal, nodeNum2 ) )
{
- SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED );
+ SetCheckedNode( nodeNum2, goal->s->number, CHECKED_FAILED );
continue;
}
radius = node2->GetRadius();
//If we're not within the known clear radius of this node OR out of Z height range...
- if ( (int)(*nci2).distance >= (radius*radius) || ( fabs( position2[2] - goal->r.currentOrigin[2] ) >= MAX_Z_DELTA ) )
+ if ( (int)(*nci2).distance >= (radius*radius) || ( fabs( position2[2] - goal->r->currentOrigin[2] ) >= MAX_Z_DELTA ) )
{
//We're not *within* this node, so check clear path, etc.
if ( flags & NF_CLEAR_PATH )//|| flags & NF_CLEAR_LOS )
{//need a clear path or LOS
- if ( !SV_inPVS( goal->r.currentOrigin, position2 ) )
+ if ( !SV_inPVS( goal->r->currentOrigin, position2 ) )
{//not even potentially clear
- SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED );
+ SetCheckedNode( nodeNum2, goal->s->number, CHECKED_FAILED );
continue;
}
}
//Do we need a clear path?
if ( flags & NF_CLEAR_PATH )
{
- if ( TestNodePath( goal, ent->s.number, position2, qfalse ) == false )//qtrue?
+ if ( TestNodePath( goal, ent->s->number, position2, qfalse ) == false )//qtrue?
{
- SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED );
+ SetCheckedNode( nodeNum2, goal->s->number, CHECKED_FAILED );
continue;
}
}
}//otherwise, inside the node so it must be clear (?)
- SetCheckedNode( nodeNum2, goal->s.number, CHECKED_PASSED );
+ SetCheckedNode( nodeNum2, goal->s->number, CHECKED_PASSED );
}
bestCost = cost;
bestNode = nextNode;
- ent->waypoint = (*nci).nodeID;
- goal->waypoint = (*nci2).nodeID;
+ *ent->waypoint = (*nci).nodeID;
+ *goal->waypoint = (*nci2).nodeID;
}
}
if ( !d_altRoutes->integer )
{//bestNode would not have been set by GetBestNodeAltRoute above, so get it here
- if ( ent->waypoint != NODE_NONE && goal->waypoint != NODE_NONE )
+ if ( *ent->waypoint != NODE_NONE && *goal->waypoint != NODE_NONE )
{//have 2 valid waypoints which means a valid path
- bestNode = GetBestNodeAltRoute( ent->waypoint, goal->waypoint, &bestCost, NODE_NONE );
+ bestNode = GetBestNodeAltRoute( *ent->waypoint, *goal->waypoint, &bestCost, NODE_NONE );
}
}
return bestNode;
@@ -1520,7 +1520,7 @@ GetNearestWaypoint
-------------------------
*/
-int CNavigator::GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int targetID )
+int CNavigator::GetNearestNode( sharedEntityMapper_t *ent, int lastID, int flags, int targetID )
{
int bestNode = NODE_NONE;
//Must have nodes
@@ -1546,7 +1546,7 @@ int CNavigator::GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int
nodeChain_l::iterator nci;
//Collect all nodes within a certain radius
- CollectNearestNodes( ent->r.currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain );
+ CollectNearestNodes( ent->r->currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain );
vec3_t position;
int radius;
@@ -1570,7 +1570,7 @@ int CNavigator::GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int
if ( (int)(*nci).distance < (radius*radius) )
{
//Do a z-difference sanity check
- if ( fabs( position[2] - ent->r.currentOrigin[2] ) < MAX_Z_DELTA )
+ if ( fabs( position[2] - ent->r->currentOrigin[2] ) < MAX_Z_DELTA )
{
//Found one
return (*nci).nodeID;
@@ -1578,11 +1578,11 @@ int CNavigator::GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int
}
//We're not *within* this node, so...
- if ( CheckedNode((*nci).nodeID,ent->s.number) == CHECKED_FAILED )
+ if ( CheckedNode((*nci).nodeID,ent->s->number) == CHECKED_FAILED )
{
continue;
}
- else if ( CheckedNode((*nci).nodeID,ent->s.number) == CHECKED_FAILED )
+ else if ( CheckedNode((*nci).nodeID,ent->s->number) == CHECKED_FAILED )
{
continue;
}
@@ -1593,7 +1593,7 @@ int CNavigator::GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int
{
if ( TestNodePath( ent, ENTITYNUM_NONE, position, qfalse ) == false )//qtrue?
{
- SetCheckedNode((*nci).nodeID,ent->s.number,CHECKED_FAILED);
+ SetCheckedNode((*nci).nodeID,ent->s->number,CHECKED_FAILED);
continue;
}
}
@@ -1604,12 +1604,12 @@ int CNavigator::GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int
{
if ( TestNodeLOS( ent, position ) == false )
{
- nodeChecked[(*nci).nodeID][ent->s.number] = CHECKED_FAILED;
+ nodeChecked[(*nci).nodeID][ent->s->number] = CHECKED_FAILED;
continue;
}
}
*/
- SetCheckedNode((*nci).nodeID,ent->s.number,CHECKED_PASSED);
+ SetCheckedNode((*nci).nodeID,ent->s->number,CHECKED_PASSED);
}
if ( targetID != WAYPOINT_NONE )
@@ -1730,7 +1730,7 @@ void CNavigator::SetCheckedNode(int wayPoint,int ent,byte value)
#define CHECK_FAILED_EDGE_INTERVAL 1000
#define CHECK_FAILED_EDGE_INTITIAL 5000//10000
-void CNavigator::CheckFailedNodes( sharedEntity_t *ent )
+void CNavigator::CheckFailedNodes( sharedEntityMapper_t *ent )
{
vec3_t nodePos;
int j;
@@ -1739,56 +1739,56 @@ void CNavigator::CheckFailedNodes( sharedEntity_t *ent )
if ( m_nodes.size() == 0 )
return;
- if ( ent->failedWaypointCheckTime && ent->failedWaypointCheckTime < svs.time )
+ if ( *ent->failedWaypointCheckTime && *ent->failedWaypointCheckTime < svs.time )
{
int failed = 0;
//do this only once every 1 second
for ( j = 0; j < MAX_FAILED_NODES; j++ )
{
- if ( ent->failedWaypoints[j] != 0 )
+ if ( (*ent->failedWaypoints)[j] != 0 )
{
failed++;
//-1 because 0 is a valid node but also the default, so we add one when we add one
- m_nodes[ent->failedWaypoints[j]-1]->GetPosition( nodePos );
- if ( !GVM_NAV_ClearPathToPoint( ent->s.number, ent->r.mins, ent->r.maxs, nodePos, (CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP), ENTITYNUM_NONE ) )
+ m_nodes[(*ent->failedWaypoints)[j]-1]->GetPosition( nodePos );
+ if ( !GVM_NAV_ClearPathToPoint( ent->s->number, ent->r->mins, ent->r->maxs, nodePos, (CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP), ENTITYNUM_NONE ) )
{//no path clear of architecture, so clear this since we can't check against entities
- ent->failedWaypoints[j] = 0;
+ (*ent->failedWaypoints)[j] = 0;
failed--;
}
//have clear architectural path, now check against ents only
- else if ( GVM_NAV_ClearPathToPoint( ent->s.number, ent->r.mins, ent->r.maxs, nodePos, CONTENTS_BODY, ENTITYNUM_NONE ) )
+ else if ( GVM_NAV_ClearPathToPoint( ent->s->number, ent->r->mins, ent->r->maxs, nodePos, CONTENTS_BODY, ENTITYNUM_NONE ) )
{//clear of ents, too, so all clear, clear this one out
- ent->failedWaypoints[j] = 0;
+ (*ent->failedWaypoints)[j] = 0;
failed--;
}
}
}
if ( !failed )
{
- ent->failedWaypointCheckTime = 0;
+ *ent->failedWaypointCheckTime = 0;
}
else
{
- ent->failedWaypointCheckTime = svs.time + CHECK_FAILED_EDGE_INTERVAL + Q_irand( 0, 1000 );
+ *ent->failedWaypointCheckTime = svs.time + CHECK_FAILED_EDGE_INTERVAL + Q_irand( 0, 1000 );
}
}
}
-void CNavigator::AddFailedNode( sharedEntity_t *ent, int nodeID )
+void CNavigator::AddFailedNode( sharedEntityMapper_t *ent, int nodeID )
{
int j;
for ( j = 0; j < MAX_FAILED_NODES; j++ )
{
- if ( ent->failedWaypoints[j] == 0 )
+ if ( (*ent->failedWaypoints)[j] == 0 )
{
- ent->failedWaypoints[j] = nodeID+1;//+1 because 0 is the default value and that's a valid node, so we take the +1 out when we check the node above
- if ( !ent->failedWaypointCheckTime )
+ (*ent->failedWaypoints)[j] = nodeID+1;//+1 because 0 is the default value and that's a valid node, so we take the +1 out when we check the node above
+ if ( !*ent->failedWaypointCheckTime )
{
- ent->failedWaypointCheckTime = svs.time + CHECK_FAILED_EDGE_INTITIAL;
+ *ent->failedWaypointCheckTime = svs.time + CHECK_FAILED_EDGE_INTITIAL;
}
return;
}
- if ( ent->failedWaypoints[j] == nodeID+1 )
+ if ( (*ent->failedWaypoints)[j] == nodeID+1 )
{//already have this one marked as failed
return;
}
@@ -1797,21 +1797,22 @@ void CNavigator::AddFailedNode( sharedEntity_t *ent, int nodeID )
{//ran out of failed nodes, get rid of first one, shift rest up
for ( j = 0; j < MAX_FAILED_NODES-1; j++ )
{
- ent->failedWaypoints[j] = ent->failedWaypoints[j+1];
+ (*ent->failedWaypoints)[j] = (*ent->failedWaypoints)[j+1];
}
}
- ent->failedWaypoints[MAX_FAILED_NODES-1] = nodeID+1;
- if ( !ent->failedWaypointCheckTime )
+ (*ent->failedWaypoints)[MAX_FAILED_NODES-1] = nodeID+1;
+ if ( !*ent->failedWaypointCheckTime )
{
- ent->failedWaypointCheckTime = svs.time + CHECK_FAILED_EDGE_INTITIAL;
+ *ent->failedWaypointCheckTime = svs.time + CHECK_FAILED_EDGE_INTITIAL;
}
}
-qboolean CNavigator::NodeFailed( sharedEntity_t *ent, int nodeID )
+qboolean CNavigator::NodeFailed( sharedEntityMapper_t *ent, int nodeID )
{
for ( int j = 0; j < MAX_FAILED_NODES; j++ )
{
- if ( (ent->failedWaypoints[j]-1) == nodeID )
+ //if ( ((*(ent->failedWaypoints))[j])-1) == nodeID )
+ if ( ((((int*)(*(ent->failedWaypoints)))[j])-1) == nodeID ) // QVM-FIXME: Is this correct?
{
return qtrue;
}
@@ -2077,11 +2078,11 @@ qboolean CNavigator::CheckFailedEdge( failedEdge_t *failedEdge )
{
vec3_t start, end, mins, maxs;
int ignore, clipmask;
- sharedEntity_t *ent = SV_GentityNum(failedEdge->entID); //(failedEdge->entIDentID]:NULL;
+ sharedEntityMapper_t *ent = SV_GentityMapperNum(failedEdge->entID); //(failedEdge->entIDentID]:NULL;
int hitEntNum;
- if ( !ent || /*!ent->inuse || !ent->client || ent->health <= 0*/ (ent->s.eType != ET_PLAYER && ent->s.eType != ET_NPC) ||
- (ent->s.eFlags & EF_DEAD))
+ if ( !ent || /*!ent->inuse || !ent->client || ent->health <= 0*/ (ent->s->eType != ET_PLAYER && ent->s->eType != ET_NPC) ||
+ (ent->s->eFlags & EF_DEAD))
{
VectorSet( mins, -15, -15, DEFAULT_MINS_2+STEPSIZE );
VectorSet( maxs, 15, 15, DEFAULT_MAXS_2 );
@@ -2090,9 +2091,9 @@ qboolean CNavigator::CheckFailedEdge( failedEdge_t *failedEdge )
}
else
{
- VectorCopy( ent->r.mins, mins );
+ VectorCopy( ent->r->mins, mins );
mins[2] += STEPSIZE;
- VectorCopy( ent->r.maxs, maxs );
+ VectorCopy( ent->r->maxs, maxs );
ignore = failedEdge->entID;
clipmask = MASK_SOLID;//ent->clipmask; //rwwFIXMEFIXME: share clipmask?
}
diff --git a/codemp/server/NPCNav/navigator.h b/codemp/server/NPCNav/navigator.h
index d026322e12..4070602522 100644
--- a/codemp/server/NPCNav/navigator.h
+++ b/codemp/server/NPCNav/navigator.h
@@ -183,7 +183,7 @@ class CNavigator
void ShowEdges( void );
void ShowPath( int start, int end );
- int GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int targetID );
+ int GetNearestNode( sharedEntityMapper_t *ent, int lastID, int flags, int targetID );
int GetBestNode( int startID, int endID, int rejectID = NODE_NONE );
@@ -201,9 +201,9 @@ class CNavigator
int GetProjectedNode( vec3_t origin, int nodeID );
//MCG Added BEGIN
- void CheckFailedNodes( sharedEntity_t *ent );
- void AddFailedNode( sharedEntity_t *ent, int nodeID );
- qboolean NodeFailed( sharedEntity_t *ent, int nodeID );
+ void CheckFailedNodes( sharedEntityMapper_t *ent );
+ void AddFailedNode( sharedEntityMapper_t *ent, int nodeID );
+ qboolean NodeFailed( sharedEntityMapper_t *ent, int nodeID );
qboolean NodesAreNeighbors( int startID, int endID );
void ClearFailedEdge( failedEdge_t *failedEdge );
void ClearAllFailedEdges( void );
@@ -214,7 +214,7 @@ class CNavigator
qboolean RouteBlocked( int startID, int testEdgeID, int endID, int rejectRank );
int GetBestNodeAltRoute( int startID, int endID, int *pathCost, int rejectID = NODE_NONE );
int GetBestNodeAltRoute( int startID, int endID, int rejectID = NODE_NONE );
- int GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goal, int flags );
+ int GetBestPathBetweenEnts( sharedEntityMapper_t *ent, sharedEntityMapper_t *goal, int flags );
int GetNodeRadius( int nodeID );
void CheckBlockedEdges( void );
void ClearCheckedNodes( void );
@@ -228,9 +228,9 @@ class CNavigator
protected:
- int TestNodePath( sharedEntity_t *ent, int okToHitEntNum, vec3_t position, qboolean includeEnts );
- int TestNodeLOS( sharedEntity_t *ent, vec3_t position );
- int TestBestFirst( sharedEntity_t *ent, int lastID, int flags );
+ int TestNodePath( sharedEntityMapper_t *ent, int okToHitEntNum, vec3_t position, qboolean includeEnts );
+ int TestNodeLOS( sharedEntityMapper_t *ent, vec3_t position );
+ int TestBestFirst( sharedEntityMapper_t *ent, int lastID, int flags );
#if __NEWCOLLECT
int CollectNearestNodes( vec3_t origin, int radius, int maxCollect, nodeChain_l &nodeChain );
diff --git a/codemp/server/server.h b/codemp/server/server.h
index 6a049f92c7..eacbd1c1ba 100644
--- a/codemp/server/server.h
+++ b/codemp/server/server.h
@@ -68,6 +68,8 @@ typedef struct server_s {
char *entityParsePoint; // used during game VM init
+ sharedEntityMapper_t gentitiesMapper[MAX_GENTITIES];
+
// the game virtual machine will update these on init and changes
sharedEntity_t *gentities;
int gentitySize;
@@ -152,6 +154,7 @@ typedef struct client_s {
int lastClientCommand; // reliable client message sequence
char lastClientCommandString[MAX_STRING_CHARS];
sharedEntity_t *gentity; // SV_GentityNum(clientnum)
+ sharedEntityMapper_t *gentityMapper;
char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked
// downloading
@@ -376,15 +379,38 @@ void SV_SendClientSnapshot( client_t *client );
//
// sv_game.c
//
-int SV_NumForGentity( sharedEntity_t *ent );
+int SV_NumForGentity( const sharedEntity_t *ent );
+int SV_NumForGentityMapper( const sharedEntityMapper_t *ent );
sharedEntity_t *SV_GentityNum( int num );
+sharedEntityMapper_t *SV_GentityMapperNum( int num );
playerState_t *SV_GameClientNum( int num );
svEntity_t *SV_SvEntityForGentity( sharedEntity_t *gEnt );
+svEntity_t *SV_SvEntityForGentityMapper( sharedEntityMapper_t *gEnt );
sharedEntity_t *SV_GEntityForSvEntity( svEntity_t *svEnt );
+sharedEntityMapper_t *SV_GEntityMapperForSvEntity( svEntity_t *svEnt );
+sharedEntityMapper_t *SV_GEntityMapperForGentity( const sharedEntity_t *gEnt );
void SV_InitGameProgs ( void );
void SV_ShutdownGameProgs ( void );
qboolean SV_inPVS (const vec3_t p1, const vec3_t p2);
+CGhoul2Info_v *SV_G2Map_GetG2FromHandle( g2handleptr_t g2h );
+CGhoul2Info_v **SV_G2Map_GetG2PtrFromHandle( g2handleptr_t g2h );
+void SV_G2Map_Update( g2handleptr_t *g2h, CGhoul2Info_v *g2Ptr );
+
+#define ENTITYMAP_READER_PROTO( type, funcName ) type funcName( type *inPtr );
+
+ENTITYMAP_READER_PROTO( char*, SV_EntityMapperReadString );
+ENTITYMAP_READER_PROTO( void*, SV_EntityMapperReadData );
+ENTITYMAP_READER_PROTO( playerState_t*, SV_EntityMapperReadPlayerState );
+#if (!defined(MACOS_X) && !defined(__GCC__) && !defined(__GNUC__))
+ ENTITYMAP_READER_PROTO( Vehicle_t*, SV_EntityMapperReadVehicle );
+#else
+ ENTITYMAP_READER_PROTO( struct Vehicle_s*, SV_EntityMapperReadVehicle );
+#endif
+ENTITYMAP_READER_PROTO( parms_t*, SV_EntityMapperReadParms );
+
+void *SV_EntityMapperReadGhoul2( void **inPtr );
+
//
// sv_bot.c
//
@@ -410,11 +436,11 @@ void BotImport_DebugPolygonDelete(int id);
void SV_ClearWorld (void);
// called after the world model has been loaded, before linking any entities
-void SV_UnlinkEntity( sharedEntity_t *ent );
+void SV_UnlinkEntity( sharedEntityMapper_t *ent );
// call before removing an entity, and before trying to move one,
// so it doesn't clip against itself
-void SV_LinkEntity( sharedEntity_t *ent );
+void SV_LinkEntity( sharedEntityMapper_t *ent );
// Needs to be called any time an entity changes origin, mins, maxs,
// or solid. Automatically unlinks if needed.
// sets ent->v.absmin and ent->v.absmax
@@ -422,7 +448,7 @@ void SV_LinkEntity( sharedEntity_t *ent );
// is not solid
-clipHandle_t SV_ClipHandleForEntity( const sharedEntity_t *ent );
+clipHandle_t SV_ClipHandleForEntity( const sharedEntityMapper_t *ent );
void SV_SectorList_f( void );
diff --git a/codemp/server/sv_bot.cpp b/codemp/server/sv_bot.cpp
index 3dfa473a63..3518c20e5b 100644
--- a/codemp/server/sv_bot.cpp
+++ b/codemp/server/sv_bot.cpp
@@ -202,7 +202,8 @@ int SV_BotAllocateClient(void) {
}
cl->gentity = SV_GentityNum( i );
- cl->gentity->s.number = i;
+ cl->gentityMapper = SV_GentityMapperNum( i );
+ cl->gentityMapper->s->number = i;
cl->state = CS_ACTIVE;
cl->lastPacketTime = svs.time;
cl->netchan.remoteAddress.type = NA_BOT;
@@ -226,8 +227,8 @@ void SV_BotFreeClient( int clientNum ) {
cl = &svs.clients[clientNum];
cl->state = CS_FREE;
cl->name[0] = 0;
- if ( cl->gentity ) {
- cl->gentity->r.svFlags &= ~SVF_BOT;
+ if ( cl->gentityMapper ) {
+ cl->gentityMapper->r->svFlags &= ~SVF_BOT;
}
if ( cl->demo.demorecording ) {
diff --git a/codemp/server/sv_client.cpp b/codemp/server/sv_client.cpp
index b19ff6f935..ff53ab933e 100644
--- a/codemp/server/sv_client.cpp
+++ b/codemp/server/sv_client.cpp
@@ -133,7 +133,6 @@ void SV_DirectConnect( const netadr_t *from ) {
int i;
client_t *cl, *newcl;
client_t temp;
- sharedEntity_t *ent;
int clientNum;
int version;
int qport;
@@ -303,8 +302,8 @@ void SV_DirectConnect( const netadr_t *from ) {
// this is the only place a client_t is ever initialized
*newcl = temp;
clientNum = newcl - svs.clients;
- ent = SV_GentityNum( clientNum );
- newcl->gentity = ent;
+ newcl->gentity = SV_GentityNum( clientNum );
+ newcl->gentityMapper = SV_GentityMapperNum( clientNum );
// save the challenge
newcl->challenge = challenge;
@@ -547,7 +546,6 @@ SV_ClientEnterWorld
*/
void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) {
int clientNum;
- sharedEntity_t *ent;
Com_DPrintf( "Going from CS_PRIMED to CS_ACTIVE for %s\n", client->name );
client->state = CS_ACTIVE;
@@ -562,9 +560,9 @@ void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) {
// set up the entity for the client
clientNum = client - svs.clients;
- ent = SV_GentityNum( clientNum );
- ent->s.number = clientNum;
- client->gentity = ent;
+ client->gentity = SV_GentityNum( clientNum );
+ client->gentityMapper = SV_GentityMapperNum( clientNum );
+ client->gentityMapper->s->number = clientNum;
client->lastUserInfoChange = 0; //reset the delay
client->lastUserInfoCount = 0; //reset the count
diff --git a/codemp/server/sv_game.cpp b/codemp/server/sv_game.cpp
index 807394ed2e..22e08a046f 100644
--- a/codemp/server/sv_game.cpp
+++ b/codemp/server/sv_game.cpp
@@ -35,7 +35,7 @@ along with this program; if not, see .
// these functions must be used instead of pointer arithmetic, because
// the game allocates gentities with private information after the server shared part
-int SV_NumForGentity( sharedEntity_t *ent ) {
+int SV_NumForGentity( const sharedEntity_t *ent ) {
int num;
num = ( (byte *)ent - (byte *)sv.gentities ) / sv.gentitySize;
@@ -43,6 +43,10 @@ int SV_NumForGentity( sharedEntity_t *ent ) {
return num;
}
+int SV_NumForGentityMapper( const sharedEntityMapper_t *ent ) {
+ return ent - sv.gentitiesMapper;
+}
+
sharedEntity_t *SV_GentityNum( int num ) {
sharedEntity_t *ent;
@@ -51,6 +55,11 @@ sharedEntity_t *SV_GentityNum( int num ) {
return ent;
}
+sharedEntityMapper_t *SV_GentityMapperNum( int num ) {
+ if ( num < 0 || num >= (int)ARRAY_LEN(sv.gentitiesMapper) ) return NULL;
+ return &sv.gentitiesMapper[num];
+}
+
playerState_t *SV_GameClientNum( int num ) {
playerState_t *ps;
@@ -66,6 +75,13 @@ svEntity_t *SV_SvEntityForGentity( sharedEntity_t *gEnt ) {
return &sv.svEntities[ gEnt->s.number ];
}
+svEntity_t *SV_SvEntityForGentityMapper( sharedEntityMapper_t *gEnt ) {
+ if ( !gEnt || gEnt->s->number < 0 || gEnt->s->number >= MAX_GENTITIES ) {
+ Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" );
+ }
+ return &sv.svEntities[ gEnt->s->number ];
+}
+
sharedEntity_t *SV_GEntityForSvEntity( svEntity_t *svEnt ) {
int num;
@@ -73,6 +89,17 @@ sharedEntity_t *SV_GEntityForSvEntity( svEntity_t *svEnt ) {
return SV_GentityNum( num );
}
+sharedEntityMapper_t *SV_GEntityMapperForSvEntity( svEntity_t *svEnt ) {
+ int num;
+
+ num = svEnt - sv.svEntities;
+ return SV_GentityMapperNum( num );
+}
+
+sharedEntityMapper_t *SV_GEntityMapperForGentity( const sharedEntity_t *gEnt ) {
+ return SV_GentityMapperNum( SV_NumForGentity(gEnt) );
+}
+
/*
=================
SV_inPVS
diff --git a/codemp/server/sv_gameapi.cpp b/codemp/server/sv_gameapi.cpp
index d216669abf..4be41259fd 100644
--- a/codemp/server/sv_gameapi.cpp
+++ b/codemp/server/sv_gameapi.cpp
@@ -30,6 +30,9 @@ along with this program; if not, see .
#include "icarus/GameInterface.h"
#include "qcommon/timing.h"
#include "NPCNav/navigator.h"
+#include "qcommon/vm_local.h"
+
+#include
botlib_export_t *botlib_export;
@@ -37,6 +40,62 @@ botlib_export_t *botlib_export;
static gameExport_t *ge; // game export table
static vm_t *gvm; // game vm, valid for legacy and new api
+typedef std::unordered_map g2HandleToG2_m;
+
+static g2HandleToG2_m g2Mapping;
+static g2handle_t g2NextHandle = (g2handle_t)1; // Start at 1, because 0 has special meaning
+
+CGhoul2Info_v *SV_G2Map_GetG2FromHandle( g2handleptr_t g2h )
+{ // Returns the pointer to the g2 object if the handle is valid
+ // Native libraries should not use the pointer, but in theory they could use
+ // it. Thus we don't perform any mapping for native libraries to avoid
+ // issues with custom modules.
+ if ( gvm->dllHandle ) return (CGhoul2Info_v*)g2h;
+
+ g2handle_t g2handle = (g2handle_t)g2h;
+ g2HandleToG2_m::iterator ghlIt = g2Mapping.find(g2handle);
+
+ if (ghlIt == g2Mapping.end()) return NULL;
+ return g2Mapping[g2handle];
+}
+
+ CGhoul2Info_v **SV_G2Map_GetG2PtrFromHandle( g2handleptr_t *g2h )
+{ // Returns a pointer to the g2 object pointer in the map so g2 functions can update the pointer
+ // Native libraries should not use the pointer, but in theory they could use
+ // it. Thus we don't perform any mapping for native libraries to avoid
+ // issues with custom modules.
+ if ( gvm->dllHandle ) return (CGhoul2Info_v **)g2h;
+
+ g2handle_t g2handle = *((g2handle_t*)g2h);
+ if ( !g2handle )
+ { // Special case: the g2 handle is not valid, yet. Return a pointer to a static temporary pointer. Insertion is handled by calling SV_G2Map_Update after calling the G2API
+ static CGhoul2Info_v *g2Tmp;
+ g2Tmp = NULL;
+ return &g2Tmp;
+ }
+ return &g2Mapping[g2handle];
+}
+
+void SV_G2Map_Update( g2handleptr_t *g2h, CGhoul2Info_v *g2Ptr )
+{ // Inserts and/or erases to/from the map and updates the handle pointer
+ if ( gvm->dllHandle ) return;
+
+ g2handle_t *g2handle = (g2handle_t*)g2h;
+ if ( !*g2handle && g2Ptr )
+ { // Got a 0 handle, but a non-0 pointer: add to map and set handle
+ // Unlikely to happen, but should we ever cycle through the whole integer range start searching for gaps
+ while ( SV_G2Map_GetG2FromHandle(g2NextHandle) || !g2NextHandle ) g2NextHandle++;
+
+ g2Mapping[g2NextHandle] = g2Ptr;
+ *g2handle = g2NextHandle++;
+ }
+ else if ( *g2h && !g2Ptr )
+ { // Got a non-0 handle, but 0 pointer: remove from map and set handle to 0
+ g2Mapping.erase( *g2handle );
+ *g2handle = 0;
+ }
+}
+
//
// game vmMain calls
//
@@ -63,7 +122,7 @@ void GVM_ShutdownGame( int restart ) {
char *GVM_ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
if ( gvm->isLegacy )
- return (char *)VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, firstTime, isBot );
+ return (char*)VM_ExplicitArgPtr( gvm, VM_Call(gvm, GAME_CLIENT_CONNECT, clientNum, firstTime, isBot) );
VMSwap v( gvm );
return ge->ClientConnect( clientNum, firstTime, isBot );
@@ -109,7 +168,18 @@ void GVM_ClientCommand( int clientNum ) {
void GVM_ClientThink( int clientNum, usercmd_t *ucmd ) {
if ( gvm->isLegacy ) {
- VM_Call( gvm, GAME_CLIENT_THINK, clientNum, reinterpret_cast< intptr_t >( ucmd ) );
+ usercmd_t *ucmd_ptr;
+
+ if ( !gvm->dllHandle ) {
+ ucmd_ptr = (usercmd_t*)(intptr_t)(VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (usercmd_t*)ucmd, sizeof(usercmd_t)) ) );
+ } else {
+ ucmd_ptr = ucmd;
+ }
+ VM_Call( gvm, GAME_CLIENT_THINK, clientNum, reinterpret_cast< intptr_t >( ucmd_ptr ) );
+ if ( !gvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( ucmd_ptr ) VM_ExtraMemory_Release( gvm, sizeof(usercmd_t) );
+ }
return;
}
VMSwap v( gvm );
@@ -145,7 +215,20 @@ int GVM_BotAIStartFrame( int time ) {
void GVM_ROFF_NotetrackCallback( int entID, const char *notetrack ) {
if ( gvm->isLegacy ) {
- VM_Call( gvm, GAME_ROFF_NOTETRACK_CALLBACK, entID, reinterpret_cast< intptr_t >( notetrack ) );
+ int notetrack_length = 0;
+ const char *notetrack_ptr;
+
+ if ( !gvm->dllHandle ) {
+ notetrack_length = strlen( notetrack ) + 1; // +1 for the terminating 0-byte, which ClaimString automatically allocates, too
+ notetrack_ptr = (const char*)(intptr_t)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimString(gvm, notetrack) );
+ } else {
+ notetrack_ptr = notetrack;
+ }
+ VM_Call( gvm, GAME_ROFF_NOTETRACK_CALLBACK, entID, reinterpret_cast< intptr_t >( notetrack_ptr ) );
+ if ( !gvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( notetrack_ptr && notetrack_length ) VM_ExtraMemory_Release( gvm, notetrack_length );
+ }
return;
}
VMSwap v( gvm );
@@ -319,25 +402,95 @@ int GVM_ICARUS_GetSetIDForString( void ) {
return ge->ICARUS_GetSetIDForString();
}
+static size_t vec3_size = sizeof(vec3_t);
qboolean GVM_NAV_ClearPathToPoint( int entID, vec3_t pmins, vec3_t pmaxs, vec3_t point, int clipmask, int okToHitEnt ) {
- if ( gvm->isLegacy )
- return (qboolean)VM_Call( gvm, GAME_NAV_CLEARPATHTOPOINT, entID, reinterpret_cast< intptr_t >( pmins ), reinterpret_cast< intptr_t >( pmaxs ), reinterpret_cast< intptr_t >( point ), clipmask, okToHitEnt );
+ if ( gvm->isLegacy ) {
+
+ qboolean ret;
+
+ float *pmins_ptr;
+ float *pmaxs_ptr;
+ float *point_ptr;
+
+ if ( !gvm->dllHandle ) {
+ pmins_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)pmins, vec3_size) );
+ pmaxs_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)pmaxs, vec3_size) );
+ point_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)point, vec3_size) );
+ } else {
+ pmins_ptr = pmins;
+ pmaxs_ptr = pmaxs;
+ point_ptr = point;
+ }
+ ret = (qboolean)VM_Call( gvm, GAME_NAV_CLEARPATHTOPOINT, entID, reinterpret_cast< intptr_t >( pmins_ptr ), reinterpret_cast< intptr_t >( pmaxs_ptr ), reinterpret_cast< intptr_t >( point_ptr ), clipmask, okToHitEnt );
+
+ if ( !gvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( point_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ if ( pmaxs_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ if ( pmins_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ }
+
+ return ret;
+ }
VMSwap v( gvm );
return ge->NAV_ClearPathToPoint( entID, pmins, pmaxs, point, clipmask, okToHitEnt );
}
qboolean GVM_NPC_ClearLOS2( int entID, const vec3_t end ) {
- if ( gvm->isLegacy )
- return (qboolean)VM_Call( gvm, GAME_NAV_CLEARLOS, entID, reinterpret_cast< intptr_t >( end ) );
+ if ( gvm->isLegacy ) {
+ qboolean ret;
+ const float *end_ptr;
+ if ( !gvm->dllHandle ) {
+ end_ptr = (const float*)VM_ExtraMemory_ClaimData( gvm, (float*)end, vec3_size );
+ } else {
+ end_ptr = (const float*)end;
+ }
+ ret = (qboolean)VM_Call( gvm, GAME_NAV_CLEARLOS, entID, reinterpret_cast< intptr_t >( end_ptr ) );
+
+ if ( !gvm->dllHandle ) {
+ if ( end_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ }
+
+ return ret;
+ }
VMSwap v( gvm );
return ge->NPC_ClearLOS2( entID, end );
}
int GVM_NAVNEW_ClearPathBetweenPoints( vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int ignore, int clipmask ) {
- if ( gvm->isLegacy )
- return VM_Call( gvm, GAME_NAV_CLEARPATHBETWEENPOINTS, reinterpret_cast< intptr_t >( start ), reinterpret_cast< intptr_t >( end ), reinterpret_cast< intptr_t >( mins ), reinterpret_cast< intptr_t >( maxs ), ignore, clipmask );
+ if ( gvm->isLegacy ) {
+ int ret;
+
+ float *start_ptr;
+ float *end_ptr;
+ float *mins_ptr;
+ float *maxs_ptr;
+
+ if ( !gvm->dllHandle ) {
+ start_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)start, vec3_size) );
+ end_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)end, vec3_size) );
+ mins_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)mins, vec3_size) );
+ maxs_ptr = (float*)VM_PtrToOffset( gvm, VM_ExtraMemory_ClaimData(gvm, (float*)maxs, vec3_size) );
+ } else {
+ start_ptr = start;
+ end_ptr = end;
+ mins_ptr = mins;
+ maxs_ptr = maxs;
+ }
+ ret = VM_Call( gvm, GAME_NAV_CLEARPATHBETWEENPOINTS, reinterpret_cast< intptr_t >( start_ptr ), reinterpret_cast< intptr_t >( end_ptr ), reinterpret_cast< intptr_t >( mins_ptr ), reinterpret_cast< intptr_t >( maxs_ptr ), ignore, clipmask );
+
+ if ( !gvm->dllHandle ) {
+ // Memory may only be released in reverse order.
+ if ( maxs_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ if ( mins_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ if ( end_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ if ( start_ptr ) VM_ExtraMemory_Release( gvm, vec3_size );
+ }
+
+ return ret;
+ }
VMSwap v( gvm );
return ge->NAVNEW_ClearPathBetweenPoints( start, end, mins, maxs, ignore, clipmask );
@@ -415,9 +568,9 @@ int CM_ModelContents( clipHandle_t model, int subBSPIndex );
int CM_LoadSubBSP( const char *name, qboolean clientload );
int CM_FindSubBSP( int modelIndex );
char *CM_SubBSPEntityString( int index );
-qboolean Q3_TaskIDPending( sharedEntity_t *ent, taskID_t taskType );
-void Q3_TaskIDSet( sharedEntity_t *ent, taskID_t taskType, int taskID );
-void Q3_TaskIDComplete( sharedEntity_t *ent, taskID_t taskType );
+qboolean Q3_TaskIDPending( sharedEntityMapper_t *ent, taskID_t taskType );
+void Q3_TaskIDSet( sharedEntityMapper_t *ent, taskID_t taskType, int taskID );
+void Q3_TaskIDComplete( sharedEntityMapper_t *ent, taskID_t taskType );
void Q3_SetVar( int taskID, int entID, const char *type_name, const char *data );
int Q3_VariableDeclared( const char *name );
int Q3_GetFloatVariable( const char *name, float *value );
@@ -426,13 +579,124 @@ int Q3_GetVectorVariable( const char *name, vec3_t value );
void SV_BotWaypointReception( int wpnum, wpobject_t **wps );
void SV_BotCalculatePaths( int rmg );
+static void SV_UpdateSharedEntitiesMapping( void ) {
+ int i, j;
+ int entCount = Com_Clampi( 0, ARRAY_LEN(sv.gentitiesMapper), sv.num_entities );
+ sharedEntityMapper_t *entM;
+
+ if ( gvm->dllHandle ) {
+ sharedEntity_t *ent;
+ for ( i = 0; i < entCount; i++ ) {
+ // Get the shared entity and the mapper
+ ent = (sharedEntity_t *)((byte *)sv.gentities + sv.gentitySize*(i));
+ entM = &sv.gentitiesMapper[i];
+
+ // Assign all values
+ entM->s = &ent->s;
+ entM->playerState = &ent->playerState;
+ entM->m_pVehicle = &ent->m_pVehicle;
+ entM->ghoul2 = &ent->ghoul2;
+ entM->localAnimIndex = &ent->localAnimIndex;
+ entM->modelScale = &ent->modelScale;
+ entM->r = &ent->r;
+ entM->taskID = &ent->taskID;
+ entM->parms = &ent->parms;
+ for ( j = 0; j < NUM_BSETS; j++ ) {
+ entM->behaviorSet[j] = &(ent->behaviorSet[j]);
+ }
+ entM->script_targetname = &ent->script_targetname;
+ entM->delayScriptTime = &ent->delayScriptTime;
+ entM->fullName = &ent->fullName;
+ entM->targetname = &ent->targetname;
+ entM->classname = &ent->classname;
+ entM->waypoint = &ent->waypoint;
+ entM->lastWaypoint = &ent->lastWaypoint;
+ entM->lastValidWaypoint = &ent->lastValidWaypoint;
+ entM->noWaypointTime = &ent->noWaypointTime;
+ entM->combatPoint = &ent->combatPoint;
+ entM->failedWaypoints = &ent->failedWaypoints;
+ entM->failedWaypointCheckTime = &ent->failedWaypointCheckTime;
+ entM->next_roff_time = &ent->next_roff_time;
+ }
+ } else {
+ sharedEntity_qvm_t *ent;
+ for ( i = 0; i < entCount; i++ ) {
+ // Get the shared entity and the mapper
+ ent = (sharedEntity_qvm_t *)((byte *)sv.gentities + sv.gentitySize*(i));
+ entM = &sv.gentitiesMapper[i];
+
+ // Assign all values
+ entM->s = &ent->s;
+ entM->playerState = (playerState_t**)&ent->playerState;
+#if (!defined(MACOS_X) && !defined(__GCC__) && !defined(__GNUC__))
+ entM->m_pVehicle = (Vehicle_t**)&ent->m_pVehicle;
+#else
+ entM->m_pVehicle = (struct Vehicle_s**)&ent->m_pVehicle;
+#endif
+ entM->ghoul2 = (void**)&ent->ghoul2;
+ entM->localAnimIndex = &ent->localAnimIndex;
+ entM->modelScale = &ent->modelScale;
+ entM->r = &ent->r;
+ entM->taskID = &ent->taskID;
+ entM->parms = (parms_t**)&ent->parms;
+ for ( j = 0; j < NUM_BSETS; j++ ) {
+ entM->behaviorSet[j] = (char**)&(ent->behaviorSet[j]);
+ }
+ entM->script_targetname = (char**)&ent->script_targetname;
+ entM->delayScriptTime = &ent->delayScriptTime;
+ entM->fullName = (char**)&ent->fullName;
+ entM->targetname = (char**)&ent->targetname;
+ entM->classname = (char**)&ent->classname;
+ entM->waypoint = &ent->waypoint;
+ entM->lastWaypoint = &ent->lastWaypoint;
+ entM->lastValidWaypoint = &ent->lastValidWaypoint;
+ entM->noWaypointTime = &ent->noWaypointTime;
+ entM->combatPoint = &ent->combatPoint;
+ entM->failedWaypoints = &ent->failedWaypoints;
+ entM->failedWaypointCheckTime = &ent->failedWaypointCheckTime;
+ entM->next_roff_time = &ent->next_roff_time;
+ }
+ }
+}
+
+#define ENTITYMAP_READER( type, funcName ) \
+ type funcName( type *inPtr ) { \
+ if ( gvm->dllHandle ) { \
+ return *inPtr; \
+ } else { \
+ return (type)VM_ArgPtr((intptr_t)(*(uint32_t*)inPtr)); \
+ } \
+ }
+
+ENTITYMAP_READER( char*, SV_EntityMapperReadString );
+ENTITYMAP_READER( void*, SV_EntityMapperReadData );
+ENTITYMAP_READER( playerState_t*, SV_EntityMapperReadPlayerState );
+#if (!defined(MACOS_X) && !defined(__GCC__) && !defined(__GNUC__))
+ ENTITYMAP_READER( Vehicle_t*, SV_EntityMapperReadVehicle );
+#else
+ ENTITYMAP_READER( struct Vehicle_s*, SV_EntityMapperReadVehicle );
+#endif
+ENTITYMAP_READER( parms_t*, SV_EntityMapperReadParms );
+
+void *SV_EntityMapperReadGhoul2( void **inPtr ) {
+ if ( gvm->dllHandle ) {
+ return *inPtr;
+ } else {
+ // For QVMs the address is actually a handle we have to interpret as uint32_t
+ return (void*)(intptr_t)(*(uint32_t*)inPtr);
+ }
+}
+
static void SV_LocateGameData( sharedEntity_t *gEnts, int numGEntities, int sizeofGEntity_t, playerState_t *clients, int sizeofGameClient ) {
+
sv.gentities = gEnts;
sv.gentitySize = sizeofGEntity_t;
sv.num_entities = numGEntities;
sv.gameClients = clients;
sv.gameClientSize = sizeofGameClient;
+
+ SV_UpdateSharedEntitiesMapping();
}
static void SV_GameDropClient( int clientNum, const char *reason ) {
@@ -458,11 +722,13 @@ static qboolean SV_EntityContact( const vec3_t mins, const vec3_t maxs, const sh
clipHandle_t ch;
trace_t trace;
+ sharedEntityMapper_t *gEntM = SV_GEntityMapperForGentity( gEnt );
+
// check for exact collision
- origin = gEnt->r.currentOrigin;
- angles = gEnt->r.currentAngles;
+ origin = gEntM->r->currentOrigin;
+ angles = gEntM->r->currentAngles;
- ch = SV_ClipHandleForEntity( gEnt );
+ ch = SV_ClipHandleForEntity( gEntM );
CM_TransformedBoxTrace ( &trace, vec3_origin, vec3_origin, mins, maxs,
ch, -1, origin, angles, capsule );
@@ -473,6 +739,8 @@ static void SV_SetBrushModel( sharedEntity_t *ent, const char *name ) {
clipHandle_t h;
vec3_t mins, maxs;
+ sharedEntityMapper_t *gEntM = SV_GEntityMapperForGentity( ent );
+
if (!name)
{
Com_Error( ERR_DROP, "SV_SetBrushModel: NULL" );
@@ -480,36 +748,36 @@ static void SV_SetBrushModel( sharedEntity_t *ent, const char *name ) {
if (name[0] == '*')
{
- ent->s.modelindex = atoi( name + 1 );
+ gEntM->s->modelindex = atoi( name + 1 );
if (sv.mLocalSubBSPIndex != -1)
{
- ent->s.modelindex += sv.mLocalSubBSPModelOffset;
+ gEntM->s->modelindex += sv.mLocalSubBSPModelOffset;
}
- h = CM_InlineModel( ent->s.modelindex );
+ h = CM_InlineModel( gEntM->s->modelindex );
CM_ModelBounds(h, mins, maxs);
- VectorCopy (mins, ent->r.mins);
- VectorCopy (maxs, ent->r.maxs);
- ent->r.bmodel = qtrue;
- ent->r.contents = CM_ModelContents( h, -1 );
+ VectorCopy (mins, gEntM->r->mins);
+ VectorCopy (maxs, gEntM->r->maxs);
+ gEntM->r->bmodel = qtrue;
+ gEntM->r->contents = CM_ModelContents( h, -1 );
}
else if (name[0] == '#')
{
- ent->s.modelindex = CM_LoadSubBSP(va("maps/%s.bsp", name + 1), qfalse);
- CM_ModelBounds( ent->s.modelindex, mins, maxs );
+ gEntM->s->modelindex = CM_LoadSubBSP(va("maps/%s.bsp", name + 1), qfalse);
+ CM_ModelBounds( gEntM->s->modelindex, mins, maxs );
- VectorCopy (mins, ent->r.mins);
- VectorCopy (maxs, ent->r.maxs);
- ent->r.bmodel = qtrue;
+ VectorCopy (mins, gEntM->r->mins);
+ VectorCopy (maxs, gEntM->r->maxs);
+ gEntM->r->bmodel = qtrue;
//rwwNOTE: We don't ever want to set contents -1, it includes CONTENTS_LIGHTSABER.
//Lots of stuff will explode if there's a brush with CONTENTS_LIGHTSABER that isn't attached to a client owner.
//ent->contents = -1; // we don't know exactly what is in the brushes
- h = CM_InlineModel( ent->s.modelindex );
- ent->r.contents = CM_ModelContents( h, CM_FindSubBSP(ent->s.modelindex) );
+ h = CM_InlineModel( gEntM->s->modelindex );
+ gEntM->r->contents = CM_ModelContents( h, CM_FindSubBSP(gEntM->s->modelindex) );
}
else
{
@@ -563,38 +831,6 @@ static void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) {
*cmd = svs.clients[clientNum].lastUsercmd;
}
-static sharedEntity_t gLocalModifier;
-static sharedEntity_t *ConvertedEntity( sharedEntity_t *ent ) { //Return an entity with the memory shifted around to allow reading/modifying VM memory
- int i = 0;
-
- assert(ent);
-
- gLocalModifier.s = ent->s;
- gLocalModifier.r = ent->r;
- while (i < NUM_TIDS)
- {
- gLocalModifier.taskID[i] = ent->taskID[i];
- i++;
- }
- i = 0;
- gLocalModifier.parms = (parms_t *)VM_ArgPtr((intptr_t)ent->parms);
- while (i < NUM_BSETS)
- {
- gLocalModifier.behaviorSet[i] = (char *)VM_ArgPtr((intptr_t)ent->behaviorSet[i]);
- i++;
- }
- i = 0;
- gLocalModifier.script_targetname = (char *)VM_ArgPtr((intptr_t)ent->script_targetname);
- gLocalModifier.delayScriptTime = ent->delayScriptTime;
- gLocalModifier.fullName = (char *)VM_ArgPtr((intptr_t)ent->fullName);
- gLocalModifier.targetname = (char *)VM_ArgPtr((intptr_t)ent->targetname);
- gLocalModifier.classname = (char *)VM_ArgPtr((intptr_t)ent->classname);
-
- gLocalModifier.ghoul2 = ent->ghoul2;
-
- return &gLocalModifier;
-}
-
static const char *SV_SetActiveSubBSP( int index ) {
if ( index >= 0 ) {
sv.mLocalSubBSPIndex = CM_FindSubBSP( index );
@@ -683,7 +919,7 @@ static qboolean SV_ICARUS_RegisterScript( const char *name, qboolean bCalledDuri
}
static qboolean SV_ICARUS_ValidEnt( sharedEntity_t *ent ) {
- return (qboolean)ICARUS_ValidEnt( ent );
+ return (qboolean)ICARUS_ValidEnt( SV_GEntityMapperForGentity(ent) );
}
static qboolean ICARUS_IsInitialized( int entID ) {
@@ -708,15 +944,15 @@ static qboolean ICARUS_IsRunning( int entID ) {
}
static qboolean ICARUS_TaskIDPending( sharedEntity_t *ent, int taskID ) {
- return Q3_TaskIDPending( ent, (taskID_t)taskID );
+ return Q3_TaskIDPending( SV_GEntityMapperForGentity(ent), (taskID_t)taskID );
}
static void SV_ICARUS_TaskIDSet( sharedEntity_t *ent, int taskType, int taskID ) {
- Q3_TaskIDSet( ent, (taskID_t)taskType, taskID );
+ Q3_TaskIDSet( SV_GEntityMapperForGentity(ent), (taskID_t)taskType, taskID );
}
static void SV_ICARUS_TaskIDComplete( sharedEntity_t *ent, int taskType ) {
- Q3_TaskIDComplete( ent, (taskID_t)taskType );
+ Q3_TaskIDComplete( SV_GEntityMapperForGentity(ent), (taskID_t)taskType );
}
static int SV_ICARUS_GetStringVariable( const char *name, const char *value ) {
@@ -768,7 +1004,7 @@ static void SV_Nav_ShowPath( int start, int end ) {
}
static int SV_Nav_GetNearestNode( sharedEntity_t *ent, int lastID, int flags, int targetID ) {
- return navigator.GetNearestNode( ent, lastID, flags, targetID );
+ return navigator.GetNearestNode( SV_GEntityMapperForGentity(ent), lastID, flags, targetID );
}
static int SV_Nav_GetBestNode( int startID, int endID, int rejectID ) {
@@ -808,15 +1044,15 @@ static int SV_Nav_GetProjectedNode( vec3_t origin, int nodeID ) {
}
static void SV_Nav_CheckFailedNodes( sharedEntity_t *ent ) {
- navigator.CheckFailedNodes( ent );
+ navigator.CheckFailedNodes( SV_GEntityMapperForGentity(ent) );
}
static void SV_Nav_AddFailedNode( sharedEntity_t *ent, int nodeID ) {
- navigator.AddFailedNode( ent, nodeID );
+ navigator.AddFailedNode( SV_GEntityMapperForGentity(ent), nodeID );
}
static qboolean SV_Nav_NodeFailed( sharedEntity_t *ent, int nodeID ) {
- return navigator.NodeFailed( ent, nodeID );
+ return navigator.NodeFailed( SV_GEntityMapperForGentity(ent), nodeID );
}
static qboolean SV_Nav_NodesAreNeighbors( int startID, int endID ) {
@@ -860,7 +1096,7 @@ static int SV_Nav_GetBestNodeAltRoute2( int startID, int endID, int rejectID ) {
}
static int SV_Nav_GetBestPathBetweenEnts( sharedEntity_t *ent, sharedEntity_t *goal, int flags ) {
- return navigator.GetBestPathBetweenEnts( ent, goal, flags );
+ return navigator.GetBestPathBetweenEnts( SV_GEntityMapperForGentity(ent), SV_GEntityMapperForGentity(goal), flags );
}
static int SV_Nav_GetNodeRadius( int nodeID ) {
@@ -1467,75 +1703,81 @@ static void SV_G2API_ListModelBones( void *ghlInfo, int frame ) {
static void SV_G2API_SetGhoul2ModelIndexes( void *ghoul2, qhandle_t *modelList, qhandle_t *skinList ) {
if ( !ghoul2 ) return;
- re->G2API_SetGhoul2ModelIndexes( *((CGhoul2Info_v *)ghoul2), modelList, skinList );
+ re->G2API_SetGhoul2ModelIndexes( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelList, skinList );
}
static qboolean SV_G2API_HaveWeGhoul2Models( void *ghoul2) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_HaveWeGhoul2Models( *((CGhoul2Info_v *)ghoul2) );
+ return re->G2API_HaveWeGhoul2Models( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)) );
}
static qboolean SV_G2API_GetBoltMatrix( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static qboolean SV_G2API_GetBoltMatrix_NoReconstruct( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
if ( !ghoul2 ) return qfalse;
re->G2API_BoltMatrixReconstruction( qfalse );
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static qboolean SV_G2API_GetBoltMatrix_NoRecNoRot( void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale ) {
if ( !ghoul2 ) return qfalse;
re->G2API_BoltMatrixReconstruction( qfalse );
re->G2API_BoltMatrixSPMethod( qtrue );
- return re->G2API_GetBoltMatrix( *((CGhoul2Info_v *)ghoul2), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
+ return re->G2API_GetBoltMatrix( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale );
}
static int SV_G2API_InitGhoul2Model( void **ghoul2Ptr, const char *fileName, int modelIndex, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 1;
#endif
- return re->G2API_InitGhoul2Model( (CGhoul2Info_v **)ghoul2Ptr, fileName, modelIndex, customSkin, customShader, modelFlags, lodBias );
+ CGhoul2Info_v **g2Ptr = SV_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghoul2Ptr );
+ int ret = re->G2API_InitGhoul2Model( g2Ptr, fileName, modelIndex, customSkin, customShader, modelFlags, lodBias );
+ SV_G2Map_Update( (g2handleptr_t*)ghoul2Ptr, *g2Ptr );
+ return ret;
}
static qboolean SV_G2API_SetSkin( void *ghoul2, int modelIndex, qhandle_t customSkin, qhandle_t renderSkin ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_SetSkin( g2, modelIndex, customSkin, renderSkin );
}
static void SV_G2API_CollisionDetect( CollisionRecord_t *collRecMap, void* ghoul2, const vec3_t angles, const vec3_t position, int frameNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, int traceFlags, int useLod, float fRadius ) {
if ( !ghoul2 ) return;
- re->G2API_CollisionDetect( collRecMap, *((CGhoul2Info_v *)ghoul2), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceServer, traceFlags, useLod, fRadius );
+ re->G2API_CollisionDetect( collRecMap, *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceServer, traceFlags, useLod, fRadius );
}
static void SV_G2API_CollisionDetectCache( CollisionRecord_t *collRecMap, void* ghoul2, const vec3_t angles, const vec3_t position, int frameNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, int traceFlags, int useLod, float fRadius ) {
if ( !ghoul2 ) return;
- re->G2API_CollisionDetectCache( collRecMap, *((CGhoul2Info_v *)ghoul2), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceServer, traceFlags, useLod, fRadius );
+ re->G2API_CollisionDetectCache( collRecMap, *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), angles, position, frameNumber, entNum, rayStart, rayEnd, scale, G2VertSpaceServer, traceFlags, useLod, fRadius );
}
static void SV_G2API_CleanGhoul2Models( void **ghoul2Ptr ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 1;
#endif
- re->G2API_CleanGhoul2Models( (CGhoul2Info_v **)ghoul2Ptr );
+
+ CGhoul2Info_v **g2Ptr = SV_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghoul2Ptr );
+ re->G2API_CleanGhoul2Models( g2Ptr );
+ SV_G2Map_Update( (g2handleptr_t*)ghoul2Ptr, *g2Ptr );
}
static qboolean SV_G2API_SetBoneAngles( void *ghoul2, int modelIndex, const char *boneName, const vec3_t angles, const int flags, const int up, const int right, const int forward, qhandle_t *modelList, int blendTime , int currentTime ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetBoneAngles( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName, angles, flags, (const Eorientations)up, (const Eorientations)right, (const Eorientations)forward, modelList, blendTime , currentTime );
+ return re->G2API_SetBoneAngles( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName, angles, flags, (const Eorientations)up, (const Eorientations)right, (const Eorientations)forward, modelList, blendTime , currentTime );
}
static qboolean SV_G2API_SetBoneAnim( void *ghoul2, const int modelIndex, const char *boneName, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetBoneAnim( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime );
+ return re->G2API_SetBoneAnim( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime );
}
static qboolean SV_G2API_GetBoneAnim( void *ghoul2, const char *boneName, const int currentTime, float *currentFrame, int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList, const int modelIndex ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_GetBoneAnim( g2, modelIndex, boneName, currentTime, currentFrame, startFrame, endFrame, flags, animSpeed, modelList );
}
@@ -1546,7 +1788,7 @@ static void SV_G2API_GetGLAName( void *ghoul2, int modelIndex, char *fillBuf ) {
return;
}
- char *tmp = re->G2API_GetGLAName( *((CGhoul2Info_v *)ghoul2), modelIndex );
+ char *tmp = re->G2API_GetGLAName( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex );
if ( tmp )
strcpy( fillBuf, tmp );
else
@@ -1555,12 +1797,12 @@ static void SV_G2API_GetGLAName( void *ghoul2, int modelIndex, char *fillBuf ) {
static int SV_G2API_CopyGhoul2Instance( void *g2From, void *g2To, int modelIndex ) {
if ( !g2From || !g2To ) return 0;
- return re->G2API_CopyGhoul2Instance( *((CGhoul2Info_v *)g2From), *((CGhoul2Info_v *)g2To), modelIndex );
+ return re->G2API_CopyGhoul2Instance( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), *(SV_G2Map_GetG2FromHandle((g2handleptr_t)g2To)), modelIndex );
}
static void SV_G2API_CopySpecificGhoul2Model( void *g2From, int modelFrom, void *g2To, int modelTo ) {
if ( !g2From || !g2To ) return;
- re->G2API_CopySpecificG2Model( *((CGhoul2Info_v *)g2From), modelFrom, *((CGhoul2Info_v *)g2To), modelTo );
+ re->G2API_CopySpecificG2Model( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), modelFrom, *(SV_G2Map_GetG2FromHandle((g2handleptr_t)g2To)), modelTo );
}
static void SV_G2API_DuplicateGhoul2Instance( void *g2From, void **g2To ) {
@@ -1568,72 +1810,84 @@ static void SV_G2API_DuplicateGhoul2Instance( void *g2From, void **g2To ) {
g_G2AllocServer = 1;
#endif
if ( !g2From || !g2To ) return;
- re->G2API_DuplicateGhoul2Instance( *((CGhoul2Info_v *)g2From), (CGhoul2Info_v **)g2To );
+
+ CGhoul2Info_v **g2ToPtr = SV_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)g2To );
+ re->G2API_DuplicateGhoul2Instance( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)g2From)), g2ToPtr );
+ SV_G2Map_Update( (g2handleptr_t*)g2To, *g2ToPtr );
}
static qboolean SV_G2API_HasGhoul2ModelOnIndex( void *ghlInfo, int modelIndex ) {
- return re->G2API_HasGhoul2ModelOnIndex( (CGhoul2Info_v **)ghlInfo, modelIndex );
+ CGhoul2Info_v **g2Ptr = SV_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_HasGhoul2ModelOnIndex( g2Ptr, modelIndex );
+ SV_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static qboolean SV_G2API_RemoveGhoul2Model( void *ghlInfo, int modelIndex ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 1;
#endif
- return re->G2API_RemoveGhoul2Model( (CGhoul2Info_v **)ghlInfo, modelIndex );
+ CGhoul2Info_v **g2Ptr = SV_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_RemoveGhoul2Model( g2Ptr, modelIndex );
+ SV_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static qboolean SV_G2API_RemoveGhoul2Models( void *ghlInfo ) {
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 1;
#endif
- return re->G2API_RemoveGhoul2Models( (CGhoul2Info_v **)ghlInfo );
+ CGhoul2Info_v **g2Ptr = SV_G2Map_GetG2PtrFromHandle( (g2handleptr_t*)ghlInfo );
+ qboolean ret = re->G2API_RemoveGhoul2Models( g2Ptr );
+ SV_G2Map_Update( (g2handleptr_t*)ghlInfo, *g2Ptr );
+ return ret;
}
static int SV_G2API_Ghoul2Size( void *ghlInfo ) {
if ( !ghlInfo ) return 0;
- return re->G2API_Ghoul2Size( *((CGhoul2Info_v *)ghlInfo) );
+ return re->G2API_Ghoul2Size( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghlInfo)) );
}
static int SV_G2API_AddBolt( void *ghoul2, int modelIndex, const char *boneName ) {
if ( !ghoul2 ) return -1;
- return re->G2API_AddBolt( *((CGhoul2Info_v *)ghoul2), modelIndex, boneName );
+ return re->G2API_AddBolt( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boneName );
}
static void SV_G2API_SetBoltInfo( void *ghoul2, int modelIndex, int boltInfo ) {
if ( !ghoul2 ) return;
- re->G2API_SetBoltInfo( *((CGhoul2Info_v *)ghoul2), modelIndex, boltInfo );
+ re->G2API_SetBoltInfo( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, boltInfo );
}
static qboolean SV_G2API_SetRootSurface( void *ghoul2, const int modelIndex, const char *surfaceName ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetRootSurface( *((CGhoul2Info_v *)ghoul2), modelIndex, surfaceName );
+ return re->G2API_SetRootSurface( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), modelIndex, surfaceName );
}
static qboolean SV_G2API_SetSurfaceOnOff( void *ghoul2, const char *surfaceName, const int flags ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetSurfaceOnOff( *((CGhoul2Info_v *)ghoul2), surfaceName, flags );
+ return re->G2API_SetSurfaceOnOff( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), surfaceName, flags );
}
static qboolean SV_G2API_SetNewOrigin( void *ghoul2, const int boltIndex ) {
if ( !ghoul2 ) return qfalse;
- return re->G2API_SetNewOrigin( *((CGhoul2Info_v *)ghoul2), boltIndex );
+ return re->G2API_SetNewOrigin( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boltIndex );
}
static qboolean SV_G2API_DoesBoneExist( void *ghoul2, int modelIndex, const char *boneName ) {
if ( !ghoul2 ) return qfalse;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_DoesBoneExist( g2, modelIndex, boneName );
}
static int SV_G2API_GetSurfaceRenderStatus( void *ghoul2, const int modelIndex, const char *surfaceName ) {
if ( !ghoul2 ) return -1;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_GetSurfaceRenderStatus( g2, modelIndex, surfaceName );
}
static void SV_G2API_AbsurdSmoothing( void *ghoul2, qboolean status ) {
if ( !ghoul2 ) return;
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
re->G2API_AbsurdSmoothing( g2, status );
}
@@ -1643,7 +1897,7 @@ static void SV_G2API_SetRagDoll( void *ghoul2, sharedRagDollParams_t *params ) {
CRagDollParams rdParams;
if ( !params ) {
- re->G2API_ResetRagDoll( *((CGhoul2Info_v *)ghoul2) );
+ re->G2API_ResetRagDoll( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)) );
return;
}
@@ -1666,7 +1920,7 @@ static void SV_G2API_SetRagDoll( void *ghoul2, sharedRagDollParams_t *params ) {
rdParams.RagPhase = (CRagDollParams::ERagPhase)params->RagPhase;
rdParams.effectorsToTurnOff = (CRagDollParams::ERagEffector)params->effectorsToTurnOff;
- re->G2API_SetRagDoll( *((CGhoul2Info_v *)ghoul2), &rdParams );
+ re->G2API_SetRagDoll( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), &rdParams );
}
static void SV_G2API_AnimateG2Models( void *ghoul2, int time, sharedRagDollUpdateParams_t *params ) {
@@ -1683,48 +1937,48 @@ static void SV_G2API_AnimateG2Models( void *ghoul2, int time, sharedRagDollUpdat
rduParams.me = params->me;
rduParams.settleFrame = params->settleFrame;
- re->G2API_AnimateG2ModelsRag( *((CGhoul2Info_v *)ghoul2), time, &rduParams );
+ re->G2API_AnimateG2ModelsRag( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, &rduParams );
}
static qboolean SV_G2API_RagPCJConstraint( void *ghoul2, const char *boneName, vec3_t min, vec3_t max ) {
- return re->G2API_RagPCJConstraint( *((CGhoul2Info_v *)ghoul2), boneName, min, max );
+ return re->G2API_RagPCJConstraint( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, min, max );
}
static qboolean SV_G2API_RagPCJGradientSpeed( void *ghoul2, const char *boneName, const float speed ) {
- return re->G2API_RagPCJGradientSpeed( *((CGhoul2Info_v *)ghoul2), boneName, speed );
+ return re->G2API_RagPCJGradientSpeed( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, speed );
}
static qboolean SV_G2API_RagEffectorGoal( void *ghoul2, const char *boneName, vec3_t pos ) {
- return re->G2API_RagEffectorGoal( *((CGhoul2Info_v *)ghoul2), boneName, pos );
+ return re->G2API_RagEffectorGoal( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, pos );
}
static qboolean SV_G2API_GetRagBonePos( void *ghoul2, const char *boneName, vec3_t pos, vec3_t entAngles, vec3_t entPos, vec3_t entScale ) {
- return re->G2API_GetRagBonePos( *((CGhoul2Info_v *)ghoul2), boneName, pos, entAngles, entPos, entScale );
+ return re->G2API_GetRagBonePos( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, pos, entAngles, entPos, entScale );
}
static qboolean SV_G2API_RagEffectorKick( void *ghoul2, const char *boneName, vec3_t velocity ) {
- return re->G2API_RagEffectorKick( *((CGhoul2Info_v *)ghoul2), boneName, velocity );
+ return re->G2API_RagEffectorKick( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), boneName, velocity );
}
static qboolean SV_G2API_RagForceSolve( void *ghoul2, qboolean force ) {
- return re->G2API_RagForceSolve( *((CGhoul2Info_v *)ghoul2), force );
+ return re->G2API_RagForceSolve( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), force );
}
static qboolean SV_G2API_SetBoneIKState( void *ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params ) {
- return re->G2API_SetBoneIKState( *((CGhoul2Info_v *)ghoul2), time, boneName, ikState, params );
+ return re->G2API_SetBoneIKState( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, boneName, ikState, params );
}
static qboolean SV_G2API_IKMove( void *ghoul2, int time, sharedIKMoveParams_t *params ) {
- return re->G2API_IKMove( *((CGhoul2Info_v *)ghoul2), time, params );
+ return re->G2API_IKMove( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), time, params );
}
static qboolean SV_G2API_RemoveBone( void *ghoul2, const char *boneName, int modelIndex ) {
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
return re->G2API_RemoveBone( g2, modelIndex, boneName );
}
static void SV_G2API_AttachInstanceToEntNum( void *ghoul2, int entityNum, qboolean server ) {
- re->G2API_AttachInstanceToEntNum( *((CGhoul2Info_v *)ghoul2), entityNum, server );
+ re->G2API_AttachInstanceToEntNum( *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2)), entityNum, server );
}
static void SV_G2API_ClearAttachedInstance( int entityNum ) {
@@ -1736,12 +1990,12 @@ static void SV_G2API_CleanEntAttachments( void ) {
}
static qboolean SV_G2API_OverrideServer( void *serverInstance ) {
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)serverInstance);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)serverInstance));
return re->G2API_OverrideServerWithClientData( g2, 0 );
}
static void SV_G2API_GetSurfaceName( void *ghoul2, int surfNumber, int modelIndex, char *fillBuf ) {
- CGhoul2Info_v &g2 = *((CGhoul2Info_v *)ghoul2);
+ CGhoul2Info_v &g2 = *(SV_G2Map_GetG2FromHandle((g2handleptr_t)ghoul2));
char *tmp = re->G2API_GetSurfaceName( g2, modelIndex, surfNumber );
strcpy( fillBuf, tmp );
}
@@ -1750,6 +2004,27 @@ static void GVM_Cvar_Set( const char *var_name, const char *value ) {
Cvar_VM_Set( var_name, value, VM_GAME );
}
+static void GVM_LinkEntity( sharedEntity_t *gEnt ) {
+ SV_LinkEntity( SV_GEntityMapperForGentity(gEnt) );
+}
+
+static void GVM_UnlinkEntity( sharedEntity_t *gEnt ) {
+ SV_UnlinkEntity( SV_GEntityMapperForGentity(gEnt) );
+}
+
+int SV_ICARUS_RunScript( sharedEntity_t *ent, const char *name ) {
+ return ICARUS_RunScript( SV_GEntityMapperForGentity(ent), name );
+}
+void SV_ICARUS_InitEnt( sharedEntity_t *ent ) {
+ ICARUS_InitEnt( SV_GEntityMapperForGentity(ent) );
+}
+void SV_ICARUS_FreeEnt( sharedEntity_t *ent ) {
+ ICARUS_FreeEnt( SV_GEntityMapperForGentity(ent) );
+}
+void SV_ICARUS_AssociateEnt( sharedEntity_t *ent ) {
+ ICARUS_AssociateEnt( SV_GEntityMapperForGentity(ent) );
+}
+
// legacy syscall
intptr_t SV_GameSystemCalls( intptr_t *args ) {
@@ -1891,11 +2166,11 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
return 0;
case G_LINKENTITY:
- SV_LinkEntity( (sharedEntity_t *)VMA(1) );
+ SV_LinkEntity( SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)) );
return 0;
case G_UNLINKENTITY:
- SV_UnlinkEntity( (sharedEntity_t *)VMA(1) );
+ SV_UnlinkEntity( SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)) );
return 0;
case G_ENTITIES_IN_BOX:
@@ -2034,7 +2309,7 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
//rww - icarus traps
case G_ICARUS_RUNSCRIPT:
- return ICARUS_RunScript(ConvertedEntity((sharedEntity_t *)VMA(1)), (const char *)VMA(2));
+ return ICARUS_RunScript(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), (const char *)VMA(2));
case G_ICARUS_REGISTERSCRIPT:
return ICARUS_RegisterScript((const char *)VMA(1), (qboolean)args[2]);
@@ -2044,7 +2319,7 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
return 0;
case G_ICARUS_VALIDENT:
- return ICARUS_ValidEnt(ConvertedEntity((sharedEntity_t *)VMA(1)));
+ return ICARUS_ValidEnt(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)));
case G_ICARUS_ISINITIALIZED:
return ICARUS_IsInitialized( args[1] );
@@ -2056,18 +2331,18 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
return ICARUS_IsRunning( args[1] );
case G_ICARUS_TASKIDPENDING:
- return Q3_TaskIDPending((sharedEntity_t *)VMA(1), (taskID_t)args[2]);
+ return Q3_TaskIDPending(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), (taskID_t)args[2]);
case G_ICARUS_INITENT:
- ICARUS_InitEnt(ConvertedEntity((sharedEntity_t *)VMA(1)));
+ ICARUS_InitEnt(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)));
return 0;
case G_ICARUS_FREEENT:
- ICARUS_FreeEnt(ConvertedEntity((sharedEntity_t *)VMA(1)));
+ ICARUS_FreeEnt(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)));
return 0;
case G_ICARUS_ASSOCIATEENT:
- ICARUS_AssociateEnt(ConvertedEntity((sharedEntity_t *)VMA(1)));
+ ICARUS_AssociateEnt(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)));
return 0;
case G_ICARUS_SHUTDOWN:
@@ -2078,12 +2353,12 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
//rww - note that we are passing in the true entity here.
//This is because we allow modification of certain non-pointer values,
//which is valid.
- Q3_TaskIDSet((sharedEntity_t *)VMA(1), (taskID_t)args[2], args[3]);
+ Q3_TaskIDSet(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), (taskID_t)args[2], args[3]);
return 0;
case G_ICARUS_TASKIDCOMPLETE:
//same as above.
- Q3_TaskIDComplete((sharedEntity_t *)VMA(1), (taskID_t)args[2]);
+ Q3_TaskIDComplete(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), (taskID_t)args[2]);
return 0;
case G_ICARUS_SETVAR:
@@ -2135,7 +2410,7 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
navigator.ShowPath(args[1], args[2]);
return 0;
case G_NAV_GETNEARESTNODE:
- return navigator.GetNearestNode((sharedEntity_t *)VMA(1), args[2], args[3], args[4]);
+ return navigator.GetNearestNode(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), args[2], args[3], args[4]);
case G_NAV_GETBESTNODE:
return navigator.GetBestNode(args[1], args[2], args[3]);
case G_NAV_GETNODEPOSITION:
@@ -2155,13 +2430,13 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
case G_NAV_GETPROJECTEDNODE:
return navigator.GetProjectedNode((float *)VMA(1), args[2]);
case G_NAV_CHECKFAILEDNODES:
- navigator.CheckFailedNodes((sharedEntity_t *)VMA(1));
+ navigator.CheckFailedNodes(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)));
return 0;
case G_NAV_ADDFAILEDNODE:
- navigator.AddFailedNode((sharedEntity_t *)VMA(1), args[2]);
+ navigator.AddFailedNode(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), args[2]);
return 0;
case G_NAV_NODEFAILED:
- return navigator.NodeFailed((sharedEntity_t *)VMA(1), args[2]);
+ return navigator.NodeFailed(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), args[2]);
case G_NAV_NODESARENEIGHBORS:
return navigator.NodesAreNeighbors(args[1], args[2]);
case G_NAV_CLEARFAILEDEDGE:
@@ -2187,7 +2462,7 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
case G_NAV_GETBESTNODEALT2:
return navigator.GetBestNodeAltRoute(args[1], args[2], args[3]);
case G_NAV_GETBESTPATHBETWEENENTS:
- return navigator.GetBestPathBetweenEnts((sharedEntity_t *)VMA(1), (sharedEntity_t *)VMA(2), args[3]);
+ return navigator.GetBestPathBetweenEnts(SV_GEntityMapperForGentity((sharedEntity_t *)VMA(1)), SV_GEntityMapperForGentity((sharedEntity_t *)VMA(2)), args[3]);
case G_NAV_GETNODERADIUS:
return navigator.GetNodeRadius(args[1]);
case G_NAV_CHECKBLOCKEDEDGES:
@@ -2418,6 +2693,8 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
botlib_export->ai.BotRemoveConsoleMessage( args[1], args[2] );
return 0;
case BOTLIB_AI_NEXT_CONSOLE_MESSAGE:
+ // QVM-FIXME: bot_consolemessage_s contains pointers and needs translation for 64 bit engine version, but
+ // vanilla modules don't event call it.
return botlib_export->ai.BotNextConsoleMessage( args[1], (struct bot_consolemessage_s *)VMA(2) );
case BOTLIB_AI_NUM_CONSOLE_MESSAGE:
return botlib_export->ai.BotNumConsoleMessages( args[1] );
@@ -2597,20 +2874,20 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
return 0;
case G_G2_HAVEWEGHOULMODELS:
- return SV_G2API_HaveWeGhoul2Models( VMA(1) );
+ return SV_G2API_HaveWeGhoul2Models( (void*)args[1] );
case G_G2_SETMODELS:
- SV_G2API_SetGhoul2ModelIndexes( VMA(1),(qhandle_t *)VMA(2),(qhandle_t *)VMA(3));
+ SV_G2API_SetGhoul2ModelIndexes( (void*)args[1],(qhandle_t *)VMA(2),(qhandle_t *)VMA(3));
return 0;
case G_G2_GETBOLT:
- return SV_G2API_GetBoltMatrix(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return SV_G2API_GetBoltMatrix((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case G_G2_GETBOLT_NOREC:
- return SV_G2API_GetBoltMatrix_NoReconstruct(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return SV_G2API_GetBoltMatrix_NoReconstruct((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case G_G2_GETBOLT_NOREC_NOROT:
- return SV_G2API_GetBoltMatrix_NoRecNoRot(VMA(1), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
+ return SV_G2API_GetBoltMatrix_NoRecNoRot((void*)args[1], args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9));
case G_G2_INITGHOUL2MODEL:
#ifdef _FULL_G2_LEAK_CHECKING
@@ -2620,47 +2897,47 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
(qhandle_t) args[5], args[6], args[7]);
case G_G2_SETSKIN:
- return SV_G2API_SetSkin(VMA(1), args[2], args[3], args[4]);
+ return SV_G2API_SetSkin((void*)args[1], args[2], args[3], args[4]);
case G_G2_SIZE:
- return SV_G2API_Ghoul2Size ( VMA(1) );
+ return SV_G2API_Ghoul2Size ( (void*)args[1] );
case G_G2_ADDBOLT:
- return SV_G2API_AddBolt(VMA(1), args[2], (const char *)VMA(3));
+ return SV_G2API_AddBolt((void*)args[1], args[2], (const char *)VMA(3));
case G_G2_SETBOLTINFO:
- SV_G2API_SetBoltInfo(VMA(1), args[2], args[3]);
+ SV_G2API_SetBoltInfo((void*)args[1], args[2], args[3]);
return 0;
case G_G2_ANGLEOVERRIDE:
- return SV_G2API_SetBoneAngles(VMA(1), args[2], (const char *)VMA(3), (float *)VMA(4), args[5],
+ return SV_G2API_SetBoneAngles((void*)args[1], args[2], (const char *)VMA(3), (float *)VMA(4), args[5],
(const Eorientations) args[6], (const Eorientations) args[7], (const Eorientations) args[8],
(qhandle_t *)VMA(9), args[10], args[11] );
case G_G2_PLAYANIM:
- return SV_G2API_SetBoneAnim(VMA(1), args[2], (const char *)VMA(3), args[4], args[5],
+ return SV_G2API_SetBoneAnim((void*)args[1], args[2], (const char *)VMA(3), args[4], args[5],
args[6], VMF(7), args[8], VMF(9), args[10]);
case G_G2_GETBONEANIM:
- return SV_G2API_GetBoneAnim(VMA(1), (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5),
+ return SV_G2API_GetBoneAnim((void*)args[1], (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5),
(int *)VMA(6), (int *)VMA(7), (float *)VMA(8), (int *)VMA(9), args[10]);
case G_G2_GETGLANAME:
- SV_G2API_GetGLAName( VMA(1), args[2], (char *)VMA(3) );
+ SV_G2API_GetGLAName( (void*)args[1], args[2], (char *)VMA(3) );
return 0;
case G_G2_COPYGHOUL2INSTANCE:
- return (int)SV_G2API_CopyGhoul2Instance(VMA(1), VMA(2), args[3]);
+ return (int)SV_G2API_CopyGhoul2Instance((void*)args[1], (void*)args[2], args[3]);
case G_G2_COPYSPECIFICGHOUL2MODEL:
- SV_G2API_CopySpecificGhoul2Model(VMA(1), args[2], VMA(3), args[4]);
+ SV_G2API_CopySpecificGhoul2Model((void*)args[1], args[2], (void*)args[3], args[4]);
return 0;
case G_G2_DUPLICATEGHOUL2INSTANCE:
#ifdef _FULL_G2_LEAK_CHECKING
g_G2AllocServer = 1;
#endif
- SV_G2API_DuplicateGhoul2Instance(VMA(1), (void **)VMA(2));
+ SV_G2API_DuplicateGhoul2Instance((void*)args[1], (void **)VMA(2));
return 0;
case G_G2_HASGHOUL2MODELONINDEX:
@@ -2689,64 +2966,64 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
return 0;
case G_G2_COLLISIONDETECT:
- SV_G2API_CollisionDetect ( (CollisionRecord_t*)VMA(1), VMA(2), (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
+ SV_G2API_CollisionDetect ( (CollisionRecord_t*)VMA(1), (void*)args[2], (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
return 0;
case G_G2_COLLISIONDETECTCACHE:
- SV_G2API_CollisionDetectCache ( (CollisionRecord_t*)VMA(1), VMA(2), (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
+ SV_G2API_CollisionDetectCache ( (CollisionRecord_t*)VMA(1), (void*)args[2], (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), args[10], args[11], VMF(12) );
return 0;
case G_G2_SETROOTSURFACE:
- return SV_G2API_SetRootSurface(VMA(1), args[2], (const char *)VMA(3));
+ return SV_G2API_SetRootSurface((void*)args[1], args[2], (const char *)VMA(3));
case G_G2_SETSURFACEONOFF:
- return SV_G2API_SetSurfaceOnOff(VMA(1), (const char *)VMA(2), /*(const int)VMA(3)*/args[3]);
+ return SV_G2API_SetSurfaceOnOff((void*)args[1], (const char *)VMA(2), /*(const int)VMA(3)*/args[3]);
case G_G2_SETNEWORIGIN:
- return SV_G2API_SetNewOrigin(VMA(1), /*(const int)VMA(2)*/args[2]);
+ return SV_G2API_SetNewOrigin((void*)args[1], /*(const int)VMA(2)*/args[2]);
case G_G2_DOESBONEEXIST:
- return SV_G2API_DoesBoneExist(VMA(1), args[2], (const char *)VMA(3));
+ return SV_G2API_DoesBoneExist((void*)args[1], args[2], (const char *)VMA(3));
case G_G2_GETSURFACERENDERSTATUS:
- return SV_G2API_GetSurfaceRenderStatus(VMA(1), args[2], (const char *)VMA(3));
+ return SV_G2API_GetSurfaceRenderStatus((void*)args[1], args[2], (const char *)VMA(3));
case G_G2_ABSURDSMOOTHING:
- SV_G2API_AbsurdSmoothing(VMA(1), (qboolean)args[2]);
+ SV_G2API_AbsurdSmoothing((void*)args[1], (qboolean)args[2]);
return 0;
case G_G2_SETRAGDOLL:
- SV_G2API_SetRagDoll( VMA(1), (sharedRagDollParams_t *)VMA(2) );
+ SV_G2API_SetRagDoll( (void*)args[1], (sharedRagDollParams_t *)VMA(2) );
return 0;
case G_G2_ANIMATEG2MODELS:
- SV_G2API_AnimateG2Models( VMA(1), args[2], (sharedRagDollUpdateParams_t *)VMA(3) );
+ SV_G2API_AnimateG2Models( (void*)args[1], args[2], (sharedRagDollUpdateParams_t *)VMA(3) );
return 0;
//additional ragdoll options -rww
case G_G2_RAGPCJCONSTRAINT:
- return SV_G2API_RagPCJConstraint(VMA(1), (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4));
+ return SV_G2API_RagPCJConstraint((void*)args[1], (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4));
case G_G2_RAGPCJGRADIENTSPEED:
- return SV_G2API_RagPCJGradientSpeed(VMA(1), (const char *)VMA(2), VMF(3));
+ return SV_G2API_RagPCJGradientSpeed((void*)args[1], (const char *)VMA(2), VMF(3));
case G_G2_RAGEFFECTORGOAL:
- return SV_G2API_RagEffectorGoal(VMA(1), (const char *)VMA(2), (float *)VMA(3));
+ return SV_G2API_RagEffectorGoal((void*)args[1], (const char *)VMA(2), (float *)VMA(3));
case G_G2_GETRAGBONEPOS:
- return SV_G2API_GetRagBonePos(VMA(1), (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4), (float *)VMA(5), (float *)VMA(6));
+ return SV_G2API_GetRagBonePos((void*)args[1], (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4), (float *)VMA(5), (float *)VMA(6));
case G_G2_RAGEFFECTORKICK:
- return SV_G2API_RagEffectorKick(VMA(1), (const char *)VMA(2), (float *)VMA(3));
+ return SV_G2API_RagEffectorKick((void*)args[1], (const char *)VMA(2), (float *)VMA(3));
case G_G2_RAGFORCESOLVE:
- return SV_G2API_RagForceSolve(VMA(1), (qboolean)args[2]);
+ return SV_G2API_RagForceSolve((void*)args[1], (qboolean)args[2]);
case G_G2_SETBONEIKSTATE:
- return SV_G2API_SetBoneIKState(VMA(1), args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5));
+ return SV_G2API_SetBoneIKState((void*)args[1], args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5));
case G_G2_IKMOVE:
- return SV_G2API_IKMove(VMA(1), args[2], (sharedIKMoveParams_t *)VMA(3));
+ return SV_G2API_IKMove((void*)args[1], args[2], (sharedIKMoveParams_t *)VMA(3));
case G_G2_REMOVEBONE:
- return SV_G2API_RemoveBone(VMA(1), (const char *)VMA(2), args[3]);
+ return SV_G2API_RemoveBone((void*)args[1], (const char *)VMA(2), args[3]);
case G_G2_ATTACHINSTANCETOENTNUM:
- SV_G2API_AttachInstanceToEntNum(VMA(1), args[2], (qboolean)args[3]);
+ SV_G2API_AttachInstanceToEntNum((void*)args[1], args[2], (qboolean)args[3]);
return 0;
case G_G2_CLEARATTACHEDINSTANCE:
SV_G2API_ClearAttachedInstance(args[1]);
@@ -2755,10 +3032,10 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) {
SV_G2API_CleanEntAttachments();
return 0;
case G_G2_OVERRIDESERVER:
- return SV_G2API_OverrideServer(VMA(1));
+ return SV_G2API_OverrideServer((void*)args[1]);
case G_G2_GETSURFACENAME:
- SV_G2API_GetSurfaceName(VMA(1), args[2], args[3], (char *)VMA(4));
+ SV_G2API_GetSurfaceName((void*)args[1], args[2], args[3], (char *)VMA(4));
return 0;
case G_SET_ACTIVE_SUBBSP:
@@ -2793,8 +3070,10 @@ void SV_InitGame( qboolean restart ) {
// clear level pointers
sv.entityParsePoint = CM_EntityString();
- for ( i=0, cl=svs.clients; iinteger; i++, cl++ )
+ for ( i=0, cl=svs.clients; iinteger; i++, cl++ ) {
cl->gentity = NULL;
+ cl->gentityMapper = NULL;
+ }
GVM_InitGame( sv.time, Com_Milliseconds(), restart );
}
@@ -2846,7 +3125,7 @@ void SV_BindGame( void ) {
gi.GetUserinfo = SV_GetUserinfo;
gi.InPVS = SV_inPVS;
gi.InPVSIgnorePortals = SV_inPVSIgnorePortals;
- gi.LinkEntity = SV_LinkEntity;
+ gi.LinkEntity = GVM_LinkEntity;
gi.LocateGameData = SV_LocateGameData;
gi.PointContents = SV_PointContents;
gi.SendConsoleCommand = Cbuf_ExecuteText;
@@ -2857,13 +3136,13 @@ void SV_BindGame( void ) {
gi.SetUserinfo = SV_SetUserinfo;
gi.SiegePersSet = SV_SiegePersSet;
gi.SiegePersGet = SV_SiegePersGet;
- gi.UnlinkEntity = SV_UnlinkEntity;
+ gi.UnlinkEntity = GVM_UnlinkEntity;
gi.ROFF_Clean = SV_ROFF_Clean;
gi.ROFF_UpdateEntities = SV_ROFF_UpdateEntities;
gi.ROFF_Cache = SV_ROFF_Cache;
gi.ROFF_Play = SV_ROFF_Play;
gi.ROFF_Purge_Ent = SV_ROFF_Purge_Ent;
- gi.ICARUS_RunScript = ICARUS_RunScript;
+ gi.ICARUS_RunScript = SV_ICARUS_RunScript;
gi.ICARUS_RegisterScript = SV_ICARUS_RegisterScript;
gi.ICARUS_Init = ICARUS_Init;
gi.ICARUS_ValidEnt = SV_ICARUS_ValidEnt;
@@ -2871,9 +3150,9 @@ void SV_BindGame( void ) {
gi.ICARUS_MaintainTaskManager = ICARUS_MaintainTaskManager;
gi.ICARUS_IsRunning = ICARUS_IsRunning;
gi.ICARUS_TaskIDPending = ICARUS_TaskIDPending;
- gi.ICARUS_InitEnt = ICARUS_InitEnt;
- gi.ICARUS_FreeEnt = ICARUS_FreeEnt;
- gi.ICARUS_AssociateEnt = ICARUS_AssociateEnt;
+ gi.ICARUS_InitEnt = SV_ICARUS_InitEnt;
+ gi.ICARUS_FreeEnt = SV_ICARUS_FreeEnt;
+ gi.ICARUS_AssociateEnt = SV_ICARUS_AssociateEnt;
gi.ICARUS_Shutdown = ICARUS_Shutdown;
gi.ICARUS_TaskIDSet = SV_ICARUS_TaskIDSet;
gi.ICARUS_TaskIDComplete = SV_ICARUS_TaskIDComplete;
diff --git a/codemp/server/sv_init.cpp b/codemp/server/sv_init.cpp
index cc1728f2da..6c0c8a8786 100644
--- a/codemp/server/sv_init.cpp
+++ b/codemp/server/sv_init.cpp
@@ -94,8 +94,8 @@ void SV_UpdateConfigstrings(client_t *client)
continue;
// do not always send server info to all clients
- if ( index == CS_SERVERINFO && client->gentity &&
- (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
+ if ( index == CS_SERVERINFO && client->gentityMapper &&
+ (client->gentityMapper->r->svFlags & SVF_NOSERVERINFO) ) {
continue;
}
SV_SendConfigstring(client, index);
@@ -142,7 +142,7 @@ void SV_SetConfigstring (int index, const char *val) {
continue;
}
// do not always send server info to all clients
- if ( index == CS_SERVERINFO && client->gentity && (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
+ if ( index == CS_SERVERINFO && client->gentityMapper && (client->gentityMapper->r->svFlags & SVF_NOSERVERINFO) ) {
continue;
}
@@ -217,20 +217,20 @@ baseline will be transmitted
================
*/
void SV_CreateBaseline( void ) {
- sharedEntity_t *svent;
+ sharedEntityMapper_t *svent;
int entnum;
for ( entnum = 1; entnum < sv.num_entities ; entnum++ ) {
- svent = SV_GentityNum(entnum);
- if (!svent->r.linked) {
+ svent = SV_GentityMapperNum(entnum);
+ if (!svent->r->linked) {
continue;
}
- svent->s.number = entnum;
+ svent->s->number = entnum;
//
// take current state as baseline
//
- sv.svEntities[entnum].baseline = svent->s;
+ sv.svEntities[entnum].baseline = *(svent->s);
}
}
@@ -668,13 +668,12 @@ Ghoul2 Insert End
}
else {
client_t *client;
- sharedEntity_t *ent;
client = &svs.clients[i];
client->state = CS_ACTIVE;
- ent = SV_GentityNum( i );
- ent->s.number = i;
- client->gentity = ent;
+ client->gentityMapper = SV_GentityMapperNum( i );
+ client->gentityMapper->s->number = i;
+ client->gentity = SV_GentityNum( i );
client->deltaMessage = -1;
client->nextSnapshotTime = svs.time; // generate a snapshot immediately
diff --git a/codemp/server/sv_main.cpp b/codemp/server/sv_main.cpp
index 3b29b4ca1d..242d1f55b6 100644
--- a/codemp/server/sv_main.cpp
+++ b/codemp/server/sv_main.cpp
@@ -861,11 +861,11 @@ void SV_CalcPings( void ) {
cl->ping = 999;
continue;
}
- if ( !cl->gentity ) {
+ if ( !cl->gentityMapper ) {
cl->ping = 999;
continue;
}
- if ( cl->gentity->r.svFlags & SVF_BOT ) {
+ if ( cl->gentityMapper->r->svFlags & SVF_BOT ) {
cl->ping = 0;
continue;
}
diff --git a/codemp/server/sv_snapshot.cpp b/codemp/server/sv_snapshot.cpp
index 5f844a9bf0..d52ab4d78d 100644
--- a/codemp/server/sv_snapshot.cpp
+++ b/codemp/server/sv_snapshot.cpp
@@ -339,7 +339,7 @@ static int QDECL SV_QsortEntityNumbers( const void *a, const void *b ) {
SV_AddEntToSnapshot
===============
*/
-static void SV_AddEntToSnapshot( svEntity_t *svEnt, sharedEntity_t *gEnt, snapshotEntityNumbers_t *eNums ) {
+static void SV_AddEntToSnapshot( svEntity_t *svEnt, sharedEntityMapper_t *gEnt, snapshotEntityNumbers_t *eNums ) {
// if we have already added this entity to this snapshot, don't add again
if ( svEnt->snapshotCounter == sv.snapshotCounter ) {
return;
@@ -351,7 +351,7 @@ static void SV_AddEntToSnapshot( svEntity_t *svEnt, sharedEntity_t *gEnt, snapsh
return;
}
- eNums->snapshotEntities[ eNums->numSnapshotEntities ] = gEnt->s.number;
+ eNums->snapshotEntities[ eNums->numSnapshotEntities ] = gEnt->s->number;
eNums->numSnapshotEntities++;
}
@@ -364,7 +364,7 @@ float g_svCullDist = -1.0f;
static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame,
snapshotEntityNumbers_t *eNums, qboolean portal ) {
int e, i;
- sharedEntity_t *ent;
+ sharedEntityMapper_t *ent;
svEntity_t *svEnt;
int l;
int clientarea, clientcluster;
@@ -391,42 +391,42 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra
clientpvs = CM_ClusterPVS (clientcluster);
for ( e = 0 ; e < sv.num_entities ; e++ ) {
- ent = SV_GentityNum(e);
+ ent = SV_GentityMapperNum(e);
// never send entities that aren't linked in
- if ( !ent->r.linked ) {
+ if ( !ent->r->linked ) {
continue;
}
- if (ent->s.eFlags & EF_PERMANENT)
+ if (ent->s->eFlags & EF_PERMANENT)
{ // he's permanent, so don't send him down!
continue;
}
- if (ent->s.number != e) {
+ if (ent->s->number != e) {
Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n");
- ent->s.number = e;
+ ent->s->number = e;
}
// entities can be flagged to explicitly not be sent to the client
- if ( ent->r.svFlags & SVF_NOCLIENT ) {
+ if ( ent->r->svFlags & SVF_NOCLIENT ) {
continue;
}
// entities can be flagged to be sent to only one client
- if ( ent->r.svFlags & SVF_SINGLECLIENT ) {
- if ( ent->r.singleClient != frame->ps.clientNum ) {
+ if ( ent->r->svFlags & SVF_SINGLECLIENT ) {
+ if ( ent->r->singleClient != frame->ps.clientNum ) {
continue;
}
}
// entities can be flagged to be sent to everyone but one client
- if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) {
- if ( ent->r.singleClient == frame->ps.clientNum ) {
+ if ( ent->r->svFlags & SVF_NOTSINGLECLIENT ) {
+ if ( ent->r->singleClient == frame->ps.clientNum ) {
continue;
}
}
- svEnt = SV_SvEntityForGentity( ent );
+ svEnt = SV_SvEntityForGentityMapper( ent );
// don't double add an entity through portals
if ( svEnt->snapshotCounter == sv.snapshotCounter ) {
@@ -434,20 +434,20 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra
}
// entities can request not to be sent to certain clients (NOTE: always send to ourselves)
- if ( e != frame->ps.clientNum && (ent->r.svFlags & SVF_BROADCASTCLIENTS)
- && !(ent->r.broadcastClients[frame->ps.clientNum/32] & (1 << (frame->ps.clientNum % 32))) )
+ if ( e != frame->ps.clientNum && (ent->r->svFlags & SVF_BROADCASTCLIENTS)
+ && !(ent->r->broadcastClients[frame->ps.clientNum/32] & (1 << (frame->ps.clientNum % 32))) )
{
continue;
}
// broadcast entities are always sent, and so is the main player so we don't see noclip weirdness
- if ( (ent->r.svFlags & SVF_BROADCAST) || e == frame->ps.clientNum
- || (ent->r.broadcastClients[frame->ps.clientNum/32] & (1 << (frame->ps.clientNum % 32))) )
+ if ( (ent->r->svFlags & SVF_BROADCAST) || e == frame->ps.clientNum
+ || (ent->r->broadcastClients[frame->ps.clientNum/32] & (1 << (frame->ps.clientNum % 32))) )
{
SV_AddEntToSnapshot( svEnt, ent, eNums );
continue;
}
- if (ent->s.isPortalEnt)
+ if (ent->s->isPortalEnt)
{ //rww - portal entities are always sent as well
SV_AddEntToSnapshot( svEnt, ent, eNums );
continue;
@@ -496,13 +496,13 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra
if (g_svCullDist != -1.0f)
{ //do a distance cull check
- VectorAdd(ent->r.absmax, ent->r.absmin, difference);
+ VectorAdd(ent->r->absmax, ent->r->absmin, difference);
VectorScale(difference, 0.5f, difference);
VectorSubtract(origin, difference, difference);
length = VectorLength(difference);
// calculate the diameter
- VectorSubtract(ent->r.absmax, ent->r.absmin, difference);
+ VectorSubtract(ent->r->absmax, ent->r->absmin, difference);
radius = VectorLength(difference);
if (length-radius >= g_svCullDist)
{ //then don't add it
@@ -514,15 +514,15 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra
SV_AddEntToSnapshot( svEnt, ent, eNums );
// if its a portal entity, add everything visible from its camera position
- if ( ent->r.svFlags & SVF_PORTAL ) {
- if ( ent->s.generic1 ) {
+ if ( ent->r->svFlags & SVF_PORTAL ) {
+ if ( ent->s->generic1 ) {
vec3_t dir;
- VectorSubtract(ent->s.origin, origin, dir);
- if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) {
+ VectorSubtract(ent->s->origin, origin, dir);
+ if ( VectorLengthSquared(dir) > (float) ent->s->generic1 * ent->s->generic1 ) {
continue;
}
}
- SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue );
+ SV_AddEntitiesVisibleFromPoint( ent->s->origin2, frame, eNums, qtrue );
}
}
}
@@ -545,10 +545,10 @@ static void SV_BuildClientSnapshot( client_t *client ) {
clientSnapshot_t *frame;
snapshotEntityNumbers_t entityNumbers;
int i;
- sharedEntity_t *ent;
+ sharedEntityMapper_t *ent;
entityState_t *state;
svEntity_t *svEnt;
- sharedEntity_t *clent;
+ sharedEntityMapper_t *clent;
playerState_t *ps;
// bump the counter used to prevent double adding
@@ -563,7 +563,7 @@ static void SV_BuildClientSnapshot( client_t *client ) {
frame->num_entities = 0;
- clent = client->gentity;
+ clent = client->gentityMapper;
if ( !clent || client->state == CS_ZOMBIE ) {
return;
}
@@ -578,12 +578,11 @@ static void SV_BuildClientSnapshot( client_t *client ) {
if (ps->m_iVehicleNum)
{ //get the vehicle's playerstate too then
- sharedEntity_t *veh = SV_GentityNum(ps->m_iVehicleNum);
+ sharedEntityMapper_t *veh = SV_GentityMapperNum(ps->m_iVehicleNum);
+ playerState_t *vps;
- if (veh && veh->playerState)
+ if (veh && (vps = SV_EntityMapperReadPlayerState(veh->playerState)))
{ //Now VMA it and we've got ourselves a playerState
- playerState_t *vps = ((playerState_t *)VM_ArgPtr((intptr_t)veh->playerState));
-
frame->vps = *vps;
#ifdef _ONEBIT_COMBO
frame->pDeltaOneBitVeh = &vps->deltaOneBits;
@@ -628,9 +627,9 @@ static void SV_BuildClientSnapshot( client_t *client ) {
frame->num_entities = 0;
frame->first_entity = svs.nextSnapshotEntities;
for ( i = 0 ; i < entityNumbers.numSnapshotEntities ; i++ ) {
- ent = SV_GentityNum(entityNumbers.snapshotEntities[i]);
+ ent = SV_GentityMapperNum(entityNumbers.snapshotEntities[i]);
state = &svs.snapshotEntities[svs.nextSnapshotEntities % svs.numSnapshotEntities];
- *state = ent->s;
+ *state = *ent->s;
svs.nextSnapshotEntities++;
// this should never hit, map should always be restarted first in SV_Frame
if ( svs.nextSnapshotEntities >= 0x7FFFFFFE ) {
diff --git a/codemp/server/sv_world.cpp b/codemp/server/sv_world.cpp
index 309f721178..2db0ec2724 100644
--- a/codemp/server/sv_world.cpp
+++ b/codemp/server/sv_world.cpp
@@ -36,18 +36,18 @@ given entity. If the entity is a bsp model, the headnode will
be returned, otherwise a custom box tree will be constructed.
================
*/
-clipHandle_t SV_ClipHandleForEntity( const sharedEntity_t *ent ) {
- if ( ent->r.bmodel ) {
+clipHandle_t SV_ClipHandleForEntity( const sharedEntityMapper_t *ent ) {
+ if ( ent->r->bmodel ) {
// explicit hulls in the BSP model
- return CM_InlineModel( ent->s.modelindex );
+ return CM_InlineModel( ent->s->modelindex );
}
- if ( ent->r.svFlags & SVF_CAPSULE ) {
+ if ( ent->r->svFlags & SVF_CAPSULE ) {
// create a temp capsule from bounding box sizes
- return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qtrue );
+ return CM_TempBoxModel( ent->r->mins, ent->r->maxs, qtrue );
}
// create a temp tree from bounding box sizes
- return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qfalse );
+ return CM_TempBoxModel( ent->r->mins, ent->r->maxs, qfalse );
}
@@ -168,14 +168,14 @@ SV_UnlinkEntity
===============
*/
-void SV_UnlinkEntity( sharedEntity_t *gEnt ) {
+void SV_UnlinkEntity( sharedEntityMapper_t *gEnt ) {
svEntity_t *ent;
svEntity_t *scan;
worldSector_t *ws;
- ent = SV_SvEntityForGentity( gEnt );
+ ent = SV_SvEntityForGentityMapper( gEnt );
- gEnt->r.linked = qfalse;
+ gEnt->r->linked = qfalse;
ws = ent->worldSector;
if ( !ws ) {
@@ -206,7 +206,7 @@ SV_LinkEntity
===============
*/
#define MAX_TOTAL_ENT_LEAFS 128
-void SV_LinkEntity( sharedEntity_t *gEnt ) {
+void SV_LinkEntity( sharedEntityMapper_t *gEnt ) {
worldSector_t *node;
int leafs[MAX_TOTAL_ENT_LEAFS];
int cluster;
@@ -217,77 +217,77 @@ void SV_LinkEntity( sharedEntity_t *gEnt ) {
float *origin, *angles;
svEntity_t *ent;
- ent = SV_SvEntityForGentity( gEnt );
+ ent = SV_SvEntityForGentityMapper( gEnt );
if ( ent->worldSector ) {
SV_UnlinkEntity( gEnt ); // unlink from old position
}
// encode the size into the entityState_t for client prediction
- if ( gEnt->r.bmodel ) {
- gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value
- } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) {
+ if ( gEnt->r->bmodel ) {
+ gEnt->s->solid = SOLID_BMODEL; // a solid_box will never create this value
+ } else if ( gEnt->r->contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) {
// assume that x/y are equal and symetric
- i = gEnt->r.maxs[0];
+ i = gEnt->r->maxs[0];
if (i<1)
i = 1;
if (i>255)
i = 255;
// z is not symetric
- j = (-gEnt->r.mins[2]);
+ j = (-gEnt->r->mins[2]);
if (j<1)
j = 1;
if (j>255)
j = 255;
// and z maxs can be negative...
- k = (gEnt->r.maxs[2]+32);
+ k = (gEnt->r->maxs[2]+32);
if (k<1)
k = 1;
if (k>255)
k = 255;
- gEnt->s.solid = (k<<16) | (j<<8) | i;
+ gEnt->s->solid = (k<<16) | (j<<8) | i;
- if (gEnt->s.solid == SOLID_BMODEL)
+ if (gEnt->s->solid == SOLID_BMODEL)
{ //yikes, this would make everything explode violently.
- gEnt->s.solid = (k<<16) | (j<<8) | (i-1);
+ gEnt->s->solid = (k<<16) | (j<<8) | (i-1);
}
}
else
{
- gEnt->s.solid = 0;
+ gEnt->s->solid = 0;
}
// get the position
- origin = gEnt->r.currentOrigin;
- angles = gEnt->r.currentAngles;
+ origin = gEnt->r->currentOrigin;
+ angles = gEnt->r->currentAngles;
// set the abs box
- if ( gEnt->r.bmodel && (angles[0] || angles[1] || angles[2]) ) {
+ if ( gEnt->r->bmodel && (angles[0] || angles[1] || angles[2]) ) {
// expand for rotation
float max;
- max = RadiusFromBounds( gEnt->r.mins, gEnt->r.maxs );
+ max = RadiusFromBounds( gEnt->r->mins, gEnt->r->maxs );
for (i=0 ; i<3 ; i++) {
- gEnt->r.absmin[i] = origin[i] - max;
- gEnt->r.absmax[i] = origin[i] + max;
+ gEnt->r->absmin[i] = origin[i] - max;
+ gEnt->r->absmax[i] = origin[i] + max;
}
} else {
// normal
- VectorAdd (origin, gEnt->r.mins, gEnt->r.absmin);
- VectorAdd (origin, gEnt->r.maxs, gEnt->r.absmax);
+ VectorAdd (origin, gEnt->r->mins, gEnt->r->absmin);
+ VectorAdd (origin, gEnt->r->maxs, gEnt->r->absmax);
}
// because movement is clipped an epsilon away from an actual edge,
// we must fully check even when bounding boxes don't quite touch
- gEnt->r.absmin[0] -= 1;
- gEnt->r.absmin[1] -= 1;
- gEnt->r.absmin[2] -= 1;
- gEnt->r.absmax[0] += 1;
- gEnt->r.absmax[1] += 1;
- gEnt->r.absmax[2] += 1;
+ gEnt->r->absmin[0] -= 1;
+ gEnt->r->absmin[1] -= 1;
+ gEnt->r->absmin[2] -= 1;
+ gEnt->r->absmax[0] += 1;
+ gEnt->r->absmax[1] += 1;
+ gEnt->r->absmax[2] += 1;
// link to PVS leafs
ent->numClusters = 0;
@@ -296,7 +296,7 @@ void SV_LinkEntity( sharedEntity_t *gEnt ) {
ent->areanum2 = -1;
//get all leafs, including solids
- num_leafs = CM_BoxLeafnums( gEnt->r.absmin, gEnt->r.absmax,
+ num_leafs = CM_BoxLeafnums( gEnt->r->absmin, gEnt->r->absmax,
leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf );
// if none of the leafs were inside the map, the
@@ -314,8 +314,8 @@ void SV_LinkEntity( sharedEntity_t *gEnt ) {
if (ent->areanum != -1 && ent->areanum != area) {
if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) {
Com_DPrintf ("Object %i touching 3 areas at %f %f %f\n",
- gEnt->s.number,
- gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]);
+ gEnt->s->number,
+ gEnt->r->absmin[0], gEnt->r->absmin[1], gEnt->r->absmin[2]);
}
ent->areanum2 = area;
} else {
@@ -341,7 +341,7 @@ void SV_LinkEntity( sharedEntity_t *gEnt ) {
ent->lastCluster = CM_LeafCluster( lastLeaf );
}
- gEnt->r.linkcount++;
+ gEnt->r->linkcount++;
// find the first world sector node that the ent's box crosses
node = sv_worldSectors;
@@ -349,9 +349,9 @@ void SV_LinkEntity( sharedEntity_t *gEnt ) {
{
if (node->axis == -1)
break;
- if ( gEnt->r.absmin[node->axis] > node->dist)
+ if ( gEnt->r->absmin[node->axis] > node->dist)
node = node->children[0];
- else if ( gEnt->r.absmax[node->axis] < node->dist)
+ else if ( gEnt->r->absmax[node->axis] < node->dist)
node = node->children[1];
else
break; // crosses the node
@@ -362,7 +362,7 @@ void SV_LinkEntity( sharedEntity_t *gEnt ) {
ent->nextEntityInWorldSector = node->entities;
node->entities = ent;
- gEnt->r.linked = qtrue;
+ gEnt->r->linked = qtrue;
}
/*
@@ -391,19 +391,19 @@ SV_AreaEntities_r
*/
void SV_AreaEntities_r( worldSector_t *node, areaParms_t *ap ) {
svEntity_t *check, *next;
- sharedEntity_t *gcheck;
+ sharedEntityMapper_t *gcheck;
for ( check = node->entities ; check ; check = next ) {
next = check->nextEntityInWorldSector;
- gcheck = SV_GEntityForSvEntity( check );
+ gcheck = SV_GEntityMapperForSvEntity( check );
- if ( gcheck->r.absmin[0] > ap->maxs[0]
- || gcheck->r.absmin[1] > ap->maxs[1]
- || gcheck->r.absmin[2] > ap->maxs[2]
- || gcheck->r.absmax[0] < ap->mins[0]
- || gcheck->r.absmax[1] < ap->mins[1]
- || gcheck->r.absmax[2] < ap->mins[2]) {
+ if ( gcheck->r->absmin[0] > ap->maxs[0]
+ || gcheck->r->absmin[1] > ap->maxs[1]
+ || gcheck->r->absmin[2] > ap->maxs[2]
+ || gcheck->r->absmax[0] < ap->mins[0]
+ || gcheck->r->absmax[1] < ap->mins[1]
+ || gcheck->r->absmax[2] < ap->mins[2]) {
continue;
}
@@ -484,17 +484,17 @@ SV_ClipToEntity
====================
*/
void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int entityNum, int contentmask, int capsule ) {
- sharedEntity_t *touch;
+ sharedEntityMapper_t *touch;
clipHandle_t clipHandle;
float *origin, *angles;
- touch = SV_GentityNum( entityNum );
+ touch = SV_GentityMapperNum( entityNum );
Com_Memset(trace, 0, sizeof(trace_t));
// if it doesn't have any brushes of a type we
// are looking for, ignore it
- if ( ! ( contentmask & touch->r.contents ) ) {
+ if ( ! ( contentmask & touch->r->contents ) ) {
trace->fraction = 1.0;
return;
}
@@ -502,10 +502,10 @@ void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, con
// might intersect, so do an exact clip
clipHandle = SV_ClipHandleForEntity (touch);
- origin = touch->r.currentOrigin;
- angles = touch->r.currentAngles;
+ origin = touch->r->currentOrigin;
+ angles = touch->r->currentAngles;
- if ( !touch->r.bmodel ) {
+ if ( !touch->r->bmodel ) {
angles = vec3_origin; // boxes don't rotate
}
@@ -514,7 +514,7 @@ void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, con
origin, angles, capsule);
if ( trace->fraction < 1 ) {
- trace->entityNum = touch->s.number;
+ trace->entityNum = touch->s->number;
}
}
@@ -538,42 +538,39 @@ static float VectorDistance(vec3_t p1, vec3_t p2)
static void SV_ClipMoveToEntities( moveclip_t *clip ) {
static int touchlist[MAX_GENTITIES];
int i, num;
- sharedEntity_t *touch;
- int passOwnerNum;
+ sharedEntityMapper_t *touch;
+ int passOwnerNum = -1; // Default to -1
trace_t trace, oldTrace= {0};
clipHandle_t clipHandle;
float *origin, *angles;
int thisOwnerShared = 1;
+ sharedEntityMapper_t *passEnt = SV_GentityMapperNum( clip->passEntityNum );
num = SV_AreaEntities( clip->boxmins, clip->boxmaxs, touchlist, MAX_GENTITIES);
- if ( clip->passEntityNum != ENTITYNUM_NONE ) {
- passOwnerNum = ( SV_GentityNum( clip->passEntityNum ) )->r.ownerNum;
- if ( passOwnerNum == ENTITYNUM_NONE ) {
- passOwnerNum = -1;
+ if ( passEnt && passEnt->r ) {
+ if ( clip->passEntityNum != ENTITYNUM_NONE && passEnt->r->ownerNum != ENTITYNUM_NONE ) {
+ passOwnerNum = passEnt->r->ownerNum;
}
- } else {
- passOwnerNum = -1;
- }
- if ( SV_GentityNum(clip->passEntityNum)->r.svFlags & SVF_OWNERNOTSHARED )
- {
- thisOwnerShared = 0;
+ if ( passEnt->r->svFlags & SVF_OWNERNOTSHARED ) {
+ thisOwnerShared = 0;
+ }
}
for ( i=0 ; itrace.allsolid ) {
return;
}
- touch = SV_GentityNum( touchlist[i] );
+ touch = SV_GentityMapperNum( touchlist[i] );
// see if we should ignore this entity
if ( clip->passEntityNum != ENTITYNUM_NONE ) {
if ( touchlist[i] == clip->passEntityNum ) {
continue; // don't clip against the pass entity
}
- if ( touch->r.ownerNum == clip->passEntityNum) {
- if (touch->r.svFlags & SVF_OWNERNOTSHARED)
+ if ( touch->r->ownerNum == clip->passEntityNum) {
+ if (touch->r->svFlags & SVF_OWNERNOTSHARED)
{
if ( clip->contentmask != (MASK_SHOT | CONTENTS_LIGHTSABER) &&
clip->contentmask != (MASK_SHOT))
@@ -586,15 +583,15 @@ static void SV_ClipMoveToEntities( moveclip_t *clip ) {
continue; // don't clip against own missiles
}
}
- if ( touch->r.ownerNum == passOwnerNum &&
- !(touch->r.svFlags & SVF_OWNERNOTSHARED) &&
+ if ( touch->r->ownerNum == passOwnerNum &&
+ !(touch->r->svFlags & SVF_OWNERNOTSHARED) &&
thisOwnerShared ) {
continue; // don't clip against other missiles from our owner
}
- if (touch->s.eType == ET_MISSILE &&
- !(touch->r.svFlags & SVF_OWNERNOTSHARED) &&
- touch->r.ownerNum == passOwnerNum)
+ if (touch->s->eType == ET_MISSILE &&
+ !(touch->r->svFlags & SVF_OWNERNOTSHARED) &&
+ touch->r->ownerNum == passOwnerNum)
{ //blah, hack
continue;
}
@@ -602,11 +599,11 @@ static void SV_ClipMoveToEntities( moveclip_t *clip ) {
// if it doesn't have any brushes of a type we
// are looking for, ignore it
- if ( ! ( clip->contentmask & touch->r.contents ) ) {
+ if ( ! ( clip->contentmask & touch->r->contents ) ) {
continue;
}
- if ((clip->contentmask == (MASK_SHOT|CONTENTS_LIGHTSABER) || clip->contentmask == MASK_SHOT) && (touch->r.contents > 0 && (touch->r.contents & CONTENTS_NOSHOT)))
+ if ((clip->contentmask == (MASK_SHOT|CONTENTS_LIGHTSABER) || clip->contentmask == MASK_SHOT) && (touch->r->contents > 0 && (touch->r->contents & CONTENTS_NOSHOT)))
{
continue;
}
@@ -614,11 +611,11 @@ static void SV_ClipMoveToEntities( moveclip_t *clip ) {
// might intersect, so do an exact clip
clipHandle = SV_ClipHandleForEntity (touch);
- origin = touch->r.currentOrigin;
- angles = touch->r.currentAngles;
+ origin = touch->r->currentOrigin;
+ angles = touch->r->currentAngles;
- if ( !touch->r.bmodel ) {
+ if ( !touch->r->bmodel ) {
angles = vec3_origin; // boxes don't rotate
}
@@ -634,13 +631,13 @@ static void SV_ClipMoveToEntities( moveclip_t *clip ) {
if ( trace.allsolid ) {
clip->trace.allsolid = qtrue;
- trace.entityNum = touch->s.number;
+ trace.entityNum = touch->s->number;
} else if ( trace.startsolid ) {
clip->trace.startsolid = qtrue;
- trace.entityNum = touch->s.number;
+ trace.entityNum = touch->s->number;
//rww - added this because we want to get the number of an ent even if our trace starts inside it.
- clip->trace.entityNum = touch->s.number;
+ clip->trace.entityNum = touch->s->number;
}
if ( trace.fraction < clip->trace.fraction ) {
@@ -649,7 +646,7 @@ static void SV_ClipMoveToEntities( moveclip_t *clip ) {
// make sure we keep a startsolid from a previous trace
oldStart = clip->trace.startsolid;
- trace.entityNum = touch->s.number;
+ trace.entityNum = touch->s->number;
clip->trace = trace;
clip->trace.startsolid = (qboolean)((unsigned)clip->trace.startsolid | (unsigned)oldStart);
}
@@ -658,10 +655,10 @@ Ghoul2 Insert Start
*/
#if 0
// decide if we should do the ghoul2 collision detection right here
- if ((trace.entityNum == touch->s.number) && (clip->traceFlags))
+ if ((trace.entityNum == touch->s->number) && (clip->traceFlags))
{
// do we actually have a ghoul2 model here?
- if (touch->s.ghoul2)
+ if (touch->s->ghoul2)
{
int oldTraceRecSize = 0;
int newTraceRecSize = 0;
@@ -680,8 +677,8 @@ Ghoul2 Insert Start
}
}
- G2API_CollisionDetect(&clip->trace.G2CollisionMap[0], *((CGhoul2Info_v *)touch->s.ghoul2),
- touch->s.angles, touch->s.origin, sv.time, touch->s.number, clip->start, clip->end, touch->s.modelScale, G2VertSpaceServer, clip->traceFlags, clip->useLod);
+ G2API_CollisionDetect(&clip->trace.G2CollisionMap[0], *((CGhoul2Info_v *)touch->s->ghoul2),
+ touch->s->angles, touch->s->origin, sv.time, touch->s->number, clip->start, clip->end, touch->s->modelScale, G2VertSpaceServer, clip->traceFlags, clip->useLod);
// set our new trace record size
@@ -703,7 +700,7 @@ Ghoul2 Insert Start
#else
//rww - since this is multiplayer and we don't have the luxury of violating networking rules in horrible ways,
//this must be done somewhat differently.
- if ((clip->traceFlags & G2TRFLAG_DOGHOULTRACE) && trace.entityNum == touch->s.number && touch->ghoul2 && ((clip->traceFlags & G2TRFLAG_HITCORPSES) || !(touch->s.eFlags & EF_DEAD)))
+ if ((clip->traceFlags & G2TRFLAG_DOGHOULTRACE) && trace.entityNum == touch->s->number && SV_EntityMapperReadGhoul2(touch->ghoul2) && ((clip->traceFlags & G2TRFLAG_HITCORPSES) || !(touch->s->eFlags & EF_DEAD)))
{ //standard behavior will be to ignore g2 col on dead ents, but if traceFlags is set to allow, then we'll try g2 col on EF_DEAD people too.
static G2Trace_t G2Trace;
vec3_t angles;
@@ -732,13 +729,13 @@ Ghoul2 Insert Start
tN++;
}
- if (touch->s.number < MAX_CLIENTS)
+ if (touch->s->number < MAX_CLIENTS)
{
- VectorCopy(touch->s.apos.trBase, angles);
+ VectorCopy(touch->s->apos.trBase, angles);
}
else
{
- VectorCopy(touch->r.currentAngles, angles);
+ VectorCopy(touch->r->currentAngles, angles);
}
angles[ROLL] = angles[PITCH] = 0;
@@ -747,27 +744,27 @@ Ghoul2 Insert Start
#ifndef FINAL_BUILD
if (sv_showghoultraces->integer)
{
- Com_Printf( "Ghoul2 trace lod=%1d length=%6.0f to %s\n",clip->useLod,VectorDistance(clip->start, clip->end), re->G2API_GetModelName (*(CGhoul2Info_v *)touch->ghoul2, 0));
+ Com_Printf( "Ghoul2 trace lod=%1d length=%6.0f to %s\n",clip->useLod,VectorDistance(clip->start, clip->end), re->G2API_GetModelName(*(SV_G2Map_GetG2FromHandle((g2handleptr_t)SV_EntityMapperReadGhoul2(touch->ghoul2))), 0));
}
#endif
if (com_optvehtrace &&
com_optvehtrace->integer &&
- touch->s.eType == ET_NPC &&
- touch->s.NPC_class == CLASS_VEHICLE &&
- touch->m_pVehicle)
+ touch->s->eType == ET_NPC &&
+ touch->s->NPC_class == CLASS_VEHICLE &&
+ SV_EntityMapperReadVehicle(touch->m_pVehicle))
{ //for vehicles cache the transform data.
- re->G2API_CollisionDetectCache(G2Trace, *((CGhoul2Info_v *)touch->ghoul2), angles, touch->r.currentOrigin, sv.time, touch->s.number, clip->start, clip->end, touch->modelScale, G2VertSpaceServer, 0, clip->useLod, fRadius);
+ re->G2API_CollisionDetectCache(G2Trace, *(SV_G2Map_GetG2FromHandle((g2handleptr_t)SV_EntityMapperReadGhoul2(touch->ghoul2))), angles, touch->r->currentOrigin, sv.time, touch->s->number, clip->start, clip->end, *touch->modelScale, G2VertSpaceServer, 0, clip->useLod, fRadius);
}
else
{
- re->G2API_CollisionDetect(G2Trace, *((CGhoul2Info_v *)touch->ghoul2), angles, touch->r.currentOrigin, sv.time, touch->s.number, clip->start, clip->end, touch->modelScale, G2VertSpaceServer, 0, clip->useLod, fRadius);
+ re->G2API_CollisionDetect(G2Trace, *(SV_G2Map_GetG2FromHandle((g2handleptr_t)SV_EntityMapperReadGhoul2(touch->ghoul2))), angles, touch->r->currentOrigin, sv.time, touch->s->number, clip->start, clip->end, *touch->modelScale, G2VertSpaceServer, 0, clip->useLod, fRadius);
}
tN = 0;
while (tN < MAX_G2_COLLISIONS)
{
- if (G2Trace[tN].mEntityNum == touch->s.number)
+ if (G2Trace[tN].mEntityNum == touch->s->number)
{ //ok, valid
bestTr = tN;
break;
@@ -885,7 +882,7 @@ SV_PointContents
*/
int SV_PointContents( const vec3_t p, int passEntityNum ) {
int touch[MAX_GENTITIES];
- sharedEntity_t *hit;
+ sharedEntityMapper_t *hit;
int i, num;
int contents, c2;
clipHandle_t clipHandle;
@@ -900,11 +897,11 @@ int SV_PointContents( const vec3_t p, int passEntityNum ) {
if ( touch[i] == passEntityNum ) {
continue;
}
- hit = SV_GentityNum( touch[i] );
+ hit = SV_GentityMapperNum( touch[i] );
// might intersect, so do an exact clip
clipHandle = SV_ClipHandleForEntity( hit );
- c2 = CM_TransformedPointContents (p, clipHandle, hit->r.currentOrigin, hit->r.currentAngles);
+ c2 = CM_TransformedPointContents (p, clipHandle, hit->r->currentOrigin, hit->r->currentAngles);
contents |= c2;
}
diff --git a/codemp/win32/q3asm.asm b/codemp/win32/q3asm.asm
new file mode 100644
index 0000000000..b64f07a51e
--- /dev/null
+++ b/codemp/win32/q3asm.asm
@@ -0,0 +1,83 @@
+; ===========================================================================
+; Copyright (C) 2011 Thilo Schulz
+;
+; 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
+; ===========================================================================
+
+; MASM ftol conversion functions using SSE or FPU
+; assume __cdecl calling convention is being used for x86, __fastcall for x64
+
+IFNDEF WIN64
+.686p
+.xmm
+.model flat, c
+ENDIF
+
+.data
+ALIGN 16
+ssemask DWORD 0FFFFFFFFh, 0FFFFFFFFh, 0FFFFFFFFh, 00000000h
+
+.code
+
+; dummy
+IFNDEF WIN64
+ .safeseh SEH_handler
+ SEH_handler proc
+ ret
+ SEH_handler endp
+ENDIF
+
+IFDEF WIN64
+ Q_VMftol PROC
+ movss xmm0, dword ptr [rdi + rbx * 4]
+ cvttss2si eax, xmm0
+ ret
+ Q_VMftol ENDP
+
+ qvmcall64 PROC
+ push rsi ; push non-volatile registers to stack
+ push rdi
+ push rbx
+ push rcx ; need to save pointer in rcx so we can write back the programData value to caller
+
+ ; registers r8 and r9 have correct value already thanx to __fastcall
+ xor rbx, rbx ; opStackOfs starts out being 0
+ mov rdi, rdx ; opStack
+ mov esi, dword ptr [rcx] ; programStack
+
+ call qword ptr [r8] ; instructionPointers[0] is also the entry point
+
+ pop rcx
+
+ mov dword ptr [rcx], esi ; write back the programStack value
+ mov al, bl ; return opStack offset
+
+ pop rbx
+ pop rdi
+ pop rsi
+
+ ret
+ qvmcall64 ENDP
+ELSE
+ Q_VMftol PROC
+ movss xmm0, dword ptr [edi + ebx * 4]
+ cvttss2si eax, xmm0
+ ret
+ Q_VMftol ENDP
+ENDIF
+
+end
diff --git a/shared/sys/sys_main.cpp b/shared/sys/sys_main.cpp
index 7c8dcf9127..76edc30082 100644
--- a/shared/sys/sys_main.cpp
+++ b/shared/sys/sys_main.cpp
@@ -744,6 +744,7 @@ int main ( int argc, char* argv[] )
{
int i;
char commandLine[ MAX_STRING_CHARS ] = { 0 };
+ int missingFuncs = Sys_FindFunctions();
Sys_PlatformInit( argc, argv );
CON_Init();
@@ -777,6 +778,22 @@ int main ( int argc, char* argv[] )
Com_Init (commandLine);
+ if ( missingFuncs ) {
+ static const char *missingFuncsError =
+ "Your system is missing functions this application relies on.\n"
+ "\n"
+ "Some features may be unavailable or their behavior may be incorrect.";
+
+ // Set the error cvar (the main menu should pick this up and display an error box to the user)
+ Cvar_Get( "com_errorMessage", missingFuncsError, CVAR_ROM );
+ Cvar_Set( "com_errorMessage", missingFuncsError );
+
+ // Print the error into the console, because we can't always display the main menu (dedicated servers, ...)
+ Com_Printf( "********************\n" );
+ Com_Printf( "ERROR: %s\n", missingFuncsError );
+ Com_Printf( "********************\n" );
+ }
+
#ifndef DEDICATED
SDL_version compiled;
SDL_version linked;
diff --git a/shared/sys/sys_public.h b/shared/sys/sys_public.h
index 62e2244508..ba2b79eba3 100644
--- a/shared/sys/sys_public.h
+++ b/shared/sys/sys_public.h
@@ -156,6 +156,12 @@ qboolean Sys_LowPhysicalMemory();
void Sys_SetProcessorAffinity( void );
+int Sys_PID( void );
+
+const char *Sys_ResolvePath( const char *path );
+const char *Sys_RealPath( const char *path );
+int Sys_FindFunctions( void );
+
typedef enum graphicsApi_e
{
GRAPHICS_API_GENERIC,
diff --git a/shared/sys/sys_unix.cpp b/shared/sys/sys_unix.cpp
index 849d11b219..18252a4e86 100644
--- a/shared/sys/sys_unix.cpp
+++ b/shared/sys/sys_unix.cpp
@@ -86,6 +86,11 @@ void Sys_PlatformExit( void )
{
}
+int Sys_PID( void )
+{
+ return getpid( );
+}
+
/*
================
Sys_Milliseconds
@@ -636,3 +641,43 @@ void Sys_AnsiColorPrint( const char *msg )
fputs( buffer, stderr );
}
}
+
+/*
+===============
+Sys_ResolvePath
+===============
+*/
+const char *Sys_ResolvePath( const char *path )
+{ // There seems to be no function to resolve paths of files that don't exist
+ // on unix, so we just return the input path. This shouldn't be an issue,
+ // as we just need to resolve paths for those on windows anyway.
+
+ return path;
+}
+
+/*
+===============
+Sys_RealPath
+===============
+*/
+const char *Sys_RealPath( const char *path )
+{
+ static char realPath[PATH_MAX+1];
+ if ( realpath(path, realPath) )
+ return realPath;
+ return path;
+}
+
+/*
+===============
+Sys_FindFunctions
+===============
+*/
+int Sys_FindFunctions( void )
+{
+ // We only use this function on Windows to find functions that might not be
+ // available on all OS versions, but that may be required for some Sys_
+ // functions to be fully operational. On unix we can just return 0.
+
+ return 0;
+}
diff --git a/shared/sys/sys_win32.cpp b/shared/sys/sys_win32.cpp
index 226c71e530..95a91d4056 100644
--- a/shared/sys/sys_win32.cpp
+++ b/shared/sys/sys_win32.cpp
@@ -611,3 +611,72 @@ void Sys_Sleep( int msec )
Sleep( msec );
#endif
}
+
+/*
+===============
+Sys_ResolvePath
+===============
+*/
+const char *Sys_ResolvePath( const char *path )
+{
+ static char resolvedPath[MAX_PATH];
+
+ if ( !GetFullPathNameA((LPCSTR)path, sizeof(resolvedPath), (LPSTR)resolvedPath, NULL) )
+ return "";
+
+ return resolvedPath;
+}
+
+/*
+===============
+Sys_RealPath
+===============
+*/
+
+typedef DWORD (WINAPI *_GetFinalPathNameByHandleA)( _In_ HANDLE, _Out_writes_(cchFilePath) LPSTR, _In_ DWORD, _In_ DWORD );
+_GetFinalPathNameByHandleA Win_GetFinalPathNameByHandleA;
+const char *Sys_RealPath( const char *path )
+{
+ static char realPath[MAX_PATH];
+ HANDLE fileHandle;
+ DWORD len;
+
+ // Return the original path if the system doesn't support GetFinalPathNameByHandleA
+ if ( !Win_GetFinalPathNameByHandleA ) return path;
+
+ // Get a handle to later resolve symlinks
+ fileHandle = CreateFileA( (LPCSTR)path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ // If we can't access it return the original instead
+ if( fileHandle == INVALID_HANDLE_VALUE )
+ return path;
+
+ // Get the resolvedName from the handle
+ len = Win_GetFinalPathNameByHandleA( fileHandle, (LPSTR)realPath, sizeof(realPath), VOLUME_NAME_NT );
+
+ // Close the handle
+ CloseHandle( fileHandle );
+
+ // If it's longer than we can store return "" to disable access
+ if( len >= sizeof(realPath) )
+ return "";
+
+ return realPath;
+}
+
+/*
+===============
+Sys_FindFunctions
+===============
+*/
+int Sys_FindFunctions( void )
+{
+ int missingFuncs = 0;
+
+ // Get the kernel32 handle and try to find the function "GetFinalPathNameByHandleA"
+ HINSTANCE handle = GetModuleHandleA( "KERNEL32" );
+ if ( handle ) Win_GetFinalPathNameByHandleA = (_GetFinalPathNameByHandleA)GetProcAddress( handle, "GetFinalPathNameByHandleA" );
+ if ( !Win_GetFinalPathNameByHandleA ) missingFuncs++;
+
+ return missingFuncs;
+}