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; +}