2
0
mirror of https://github.com/rehlds/metamod-r.git synced 2025-01-26 05:28:19 +03:00

Implemented extension dll to avoid a list of entities e.g (linkgame.cpp).

Minor refactoring
This commit is contained in:
s1lent 2017-07-31 22:41:03 +07:00
parent d90f336a75
commit a6ab7cea5d
No known key found for this signature in database
GPG Key ID: 0FE401DC73916B5C
34 changed files with 757 additions and 4365 deletions

2
.gitignore vendored
View File

@ -2,6 +2,7 @@
**/.gradle
.idea
*.iml
*.bat
**/msvc/Debug*
**/msvc/Release*
**/msvc/*.sdf
@ -14,7 +15,6 @@
**/msvc/*.pch
**/msvc/*.txt
**/msvc/.vs
**/msvc/start*.bat
**/msvc/ipch
**/PublishPath*.txt
**/*.log

View File

@ -48,7 +48,7 @@ void setupToolchain(NativeBinarySpec b)
pchSourceSet: 'rmod_pch'
)
cfg.extraLibs 'psapi.lib'
cfg.extraLibs 'psapi.lib', 'user32.lib'
cfg.singleDefines('_CRT_SECURE_NO_WARNINGS')
} else if (cfg instanceof GccToolchainConfig) {
cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions(

View File

@ -36,7 +36,6 @@
#include "archtypes.h"
#include "maintypes.h"
#include "regamedll_common.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN

View File

@ -1,96 +0,0 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#pragma once
#ifndef _WIN32
#define _strlwr(p) for (int i = 0; p[i] != 0; i++) p[i] = tolower(p[i]);
#endif
#define Q_isspace isspace
#define Q_isalnum isalnum
#define Q_isalpha isalpha
#define Q_malloc malloc
#define Q_calloc calloc
#define Q_alloca alloca
#define Q_free free
#define Q_min min
#define Q_max max
#define Q_clamp clamp
#define Q_access _access
#define Q_close _close
#define Q_write _write
#define Q_memset memset
#define Q_memcpy memcpy
#define Q_strlen strlen
#define Q_memcmp memcmp
#define Q_strcpy strcpy
#define Q_strncpy strncpy
#define Q_strrchr strrchr
#define Q_strcat strcat
#define Q_strncat strncat
#define Q_strcmp strcmp
#define Q_strncmp strncmp
#define Q_sscanf sscanf
#define Q_strdup _strdup
#define Q_stricmp _stricmp
#define Q_strnicmp _strnicmp
#define Q_strstr strstr
#define Q_strchr strchr
#define Q_strrchr strrchr
#define Q_strlwr _strlwr
#define Q_sprintf sprintf
#define Q_snprintf _snprintf
#define Q_atoi atoi
#define Q_atof atof
#define Q_toupper toupper
#define Q_memmove memmove
#define Q_vsnprintf _vsnprintf
#define Q_vsnwprintf _vsnwprintf
#define Q_abs abs
#define Q_fabs fabs
#define Q_tan tan
#define Q_atan atan
#define Q_atan2 atan2
#define Q_acos acos
#define Q_cos cos
#define Q_sin sin
#define Q_pow pow
#define Q_fmod fmod
#define Q_fopen fopen
#define Q_fwrite fwrite
#define Q_fprintf fprintf
#define Q_fclose fclose
#ifdef REGAMEDLL_FIXES
#define Q_sqrt M_sqrt
#else
#define Q_sqrt sqrt
#endif

View File

@ -36,7 +36,6 @@
#include "archtypes.h"
#include "maintypes.h"
#include "regamedll_common.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN

View File

@ -1,96 +0,0 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#pragma once
#ifndef _WIN32
#define _strlwr(p) for (int i = 0; p[i] != 0; i++) p[i] = tolower(p[i]);
#endif
#define Q_isspace isspace
#define Q_isalnum isalnum
#define Q_isalpha isalpha
#define Q_malloc malloc
#define Q_calloc calloc
#define Q_alloca alloca
#define Q_free free
#define Q_min min
#define Q_max max
#define Q_clamp clamp
#define Q_access _access
#define Q_close _close
#define Q_write _write
#define Q_memset memset
#define Q_memcpy memcpy
#define Q_strlen strlen
#define Q_memcmp memcmp
#define Q_strcpy strcpy
#define Q_strncpy strncpy
#define Q_strrchr strrchr
#define Q_strcat strcat
#define Q_strncat strncat
#define Q_strcmp strcmp
#define Q_strncmp strncmp
#define Q_sscanf sscanf
#define Q_strdup _strdup
#define Q_stricmp _stricmp
#define Q_strnicmp _strnicmp
#define Q_strstr strstr
#define Q_strchr strchr
#define Q_strrchr strrchr
#define Q_strlwr _strlwr
#define Q_sprintf sprintf
#define Q_snprintf _snprintf
#define Q_atoi atoi
#define Q_atof atof
#define Q_toupper toupper
#define Q_memmove memmove
#define Q_vsnprintf _vsnprintf
#define Q_vsnwprintf _vsnwprintf
#define Q_abs abs
#define Q_fabs fabs
#define Q_tan tan
#define Q_atan atan
#define Q_atan2 atan2
#define Q_acos acos
#define Q_cos cos
#define Q_sin sin
#define Q_pow pow
#define Q_fmod fmod
#define Q_fopen fopen
#define Q_fwrite fwrite
#define Q_fprintf fprintf
#define Q_fclose fclose
#ifdef REGAMEDLL_FIXES
#define Q_sqrt M_sqrt
#else
#define Q_sqrt sqrt
#endif

View File

@ -10,6 +10,7 @@ set repodir=%~2
set old_version=
set version_major=0
set version_minor=0
set version_maintenance=0
set version_modifed=
set commitSHA=
@ -65,6 +66,7 @@ IF EXIST "%srcdir%\version.h" (
IF %%i==#define (
IF %%j==VERSION_MAJOR set version_major=%%k
IF %%j==VERSION_MINOR set version_minor=%%k
IF %%j==VERSION_MAINTENANCE set version_maintenance=%%k
)
)
) ELSE (
@ -72,6 +74,7 @@ IF EXIST "%srcdir%\version.h" (
IF NOT [%%j] == [] (
IF %%i==majorVersion set version_major=%%j
IF %%i==minorVersion set version_minor=%%j
IF %%i==maintenanceVersion set version_maintenance=%%j
)
)
)
@ -165,7 +168,7 @@ IF [%localChanged%]==[1] (
:: Now form full version string like 1.0.0.1
::
set new_version=%version_major%.%version_minor%.%commitCount%%version_modifed%
set new_version=%version_major%.%version_minor%.%version_maintenance%.%commitCount%%version_modifed%
::
:: Update appversion.h if version has changed or modifications/mixed revisions detected
@ -194,8 +197,8 @@ echo.>>"%srcdir%\appversion.h"
echo // Version defines>>"%srcdir%\appversion.h"
echo #define APP_VERSION "%new_version%">>"%srcdir%\appversion.h"
>>"%srcdir%\appversion.h" echo #define APP_VERSION_C %version_major%,%version_minor%,%commitCount%
echo #define APP_VERSION_STRD "%version_major%.%version_minor%.%commitCount%">>"%srcdir%\appversion.h"
>>"%srcdir%\appversion.h" echo #define APP_VERSION_C %version_major%,%version_minor%,%version_maintenance%,%commitCount%
echo #define APP_VERSION_STRD "%version_major%.%version_minor%.%version_maintenance%.%commitCount%">>"%srcdir%\appversion.h"
echo #define APP_VERSION_FLAGS 0x0L>>"%srcdir%\appversion.h"
echo.>>"%srcdir%\appversion.h"

View File

@ -206,14 +206,15 @@
<ClCompile Include="..\src\linkgame.cpp" />
<ClCompile Include="..\src\log_meta.cpp" />
<ClCompile Include="..\src\mdebug.cpp" />
<ClCompile Include="..\src\metamod_rehlds_api.cpp" />
<ClCompile Include="..\src\mem_utils.cpp" />
<ClCompile Include="..\src\meta_rehlds_api.cpp" />
<ClCompile Include="..\src\metamod.cpp" />
<ClCompile Include="..\src\mextdll.cpp" />
<ClCompile Include="..\src\mlist.cpp" />
<ClCompile Include="..\src\mplayer.cpp" />
<ClCompile Include="..\src\mplugin.cpp" />
<ClCompile Include="..\src\mreg.cpp" />
<ClCompile Include="..\src\mutil.cpp" />
<ClCompile Include="..\src\osdep.cpp" />
<ClCompile Include="..\src\precompiled.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
@ -222,6 +223,7 @@
<ClCompile Include="..\src\reg_support.cpp" />
<ClCompile Include="..\src\sdk_util.cpp" />
<ClCompile Include="..\src\studioapi.cpp" />
<ClCompile Include="..\src\sys_module.cpp" />
<ClCompile Include="..\src\utils.cpp" />
</ItemGroup>
<ItemGroup>
@ -239,9 +241,11 @@
<ClInclude Include="..\src\linkent.h" />
<ClInclude Include="..\src\log_meta.h" />
<ClInclude Include="..\src\mdebug.h" />
<ClInclude Include="..\src\metamod_rehlds_api.h" />
<ClInclude Include="..\src\mem_utils.h" />
<ClInclude Include="..\src\meta_rehlds_api.h" />
<ClInclude Include="..\src\meta_api.h" />
<ClInclude Include="..\src\metamod.h" />
<ClInclude Include="..\src\mextdll.h" />
<ClInclude Include="..\src\mlist.h" />
<ClInclude Include="..\src\mplayer.h" />
<ClInclude Include="..\src\mplugin.h" />
@ -253,6 +257,7 @@
<ClInclude Include="..\src\reg_support.h" />
<ClInclude Include="..\src\sdk_util.h" />
<ClInclude Include="..\src\studioapi.h" />
<ClInclude Include="..\src\sys_module.h" />
<ClInclude Include="..\src\utils.h" />
</ItemGroup>
<ItemGroup>

View File

@ -62,9 +62,6 @@
<ClCompile Include="..\src\mutil.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\osdep.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\precompiled.cpp">
<Filter>src</Filter>
</ClCompile>
@ -83,15 +80,24 @@
<ClCompile Include="..\src\callback_jit.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\metamod_rehlds_api.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\public_amalgamation.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\mdebug.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\mem_utils.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\sys_module.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\mextdll.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\meta_rehlds_api.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\api_info.h">
@ -175,10 +181,19 @@
<ClInclude Include="..\src\jitasm.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\metamod_rehlds_api.h">
<ClInclude Include="..\src\mdebug.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\mdebug.h">
<ClInclude Include="..\src\mem_utils.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\sys_module.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\mextdll.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\meta_rehlds_api.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>

View File

@ -114,7 +114,7 @@ void cmd_meta_version()
return;
}
META_CONS("Metamod-r v%s, API (%s)", APP_VERSION_STRD, META_INTERFACE_VERSION);
META_CONS("Metamod-r v%s, API (%s)", APP_VERSION, META_INTERFACE_VERSION);
META_CONS("Metamod-r build: " __TIME__ " " __DATE__ "");
META_CONS("Metamod-r from: " APP_COMMIT_URL APP_COMMIT_SHA "");
}
@ -127,7 +127,7 @@ void client_meta_version(edict_t *pEntity)
return;
}
META_CONS("Metamod-r v%s, API (%s)", APP_VERSION_STRD, META_INTERFACE_VERSION);
META_CONS("Metamod-r v%s, API (%s)", APP_VERSION, META_INTERFACE_VERSION);
META_CONS("Metamod-r build: " __TIME__ " " __DATE__ "");
META_CONS("Metamod-r from: " APP_COMMIT_URL APP_COMMIT_SHA "");
}
@ -336,7 +336,7 @@ void cmd_doplug(PLUG_CMD pcmd)
}
// Allow chance to read the message, before any window closes.
do_exit(1);
sleep(3);
}
if (findp && !unique) {

View File

@ -62,21 +62,22 @@ bool MConfig::set(option_t* setp, const char* setstr)
*optval = FALSE;
}
else {
META_ERROR("option '%s' invalid format '%s'", setp->name,
setstr);
META_ERROR("option '%s' invalid format '%s'", setp->name, setstr);
return false;
}
META_DEBUG(3, "set config bool: %s = %s", setp->name, *optval ? "true" : "false");
break;
case CF_STR:
if (*optstr)
Q_free(*optstr);
if (*optstr) {
free(*optstr);
}
*optstr = Q_strdup(setstr);
META_DEBUG(3, "set config string: %s = %s", setp->name, *optstr);
break;
case CF_PATH:
if (*optstr)
Q_free(*optstr);
if (*optstr) {
free(*optstr);
}
full_gamedir_path(setstr, pathbuf);
*optstr = Q_strdup(pathbuf);
META_DEBUG(3, "set config path: %s = %s", setp->name, *optstr);

View File

@ -2,7 +2,7 @@
#define CDATA_DLL_H(x, p, h) CDATA_ENTRY(DLL_FUNCTIONS, x, p, size_t(h))
#define CDATA_DLL(x) CDATA_ENTRY(DLL_FUNCTIONS, x, P_PRE, 0u)
#define CDATA_NEWDLL_H(x, p, h) CDATA_ENTRY(NEW_DLL_FUNCTIONS, x, p, uint8(h))
#define CDATA_NEWDLL_H(x, p, h) CDATA_ENTRY(NEW_DLL_FUNCTIONS, x, p, size_t(h))
#define CDATA_NEWDLL(x) CDATA_ENTRY(NEW_DLL_FUNCTIONS, x, P_PRE, 0u)
DLL_FUNCTIONS sFunctionTable;
@ -12,6 +12,13 @@ NEW_DLL_FUNCTIONS sNewFunctionTable;
NEW_DLL_FUNCTIONS sNewFunctionTable_jit;
NEW_DLL_FUNCTIONS *pHookedNewDllFunctions = &sNewFunctionTable;
void MM_PRE_HOOK EXT_FUNC mm_GameShutdown()
{
g_meta_extdll.unload();
g_GameDLL.sys_module.unload();
g_engine.sys_module.unload();
}
void MM_PRE_HOOK EXT_FUNC mm_ClientConnect(edict_t *pEntity, const char *, const char *, char[128])
{
g_players.clear_player_cvar_query(pEntity);
@ -58,76 +65,76 @@ void EXT_FUNC mm_ServerDeactivate()
compile_data_t g_dllfunc_cdata[] =
{
CDATA_DLL(pfnGameInit), // pfnGameInit() Initialize the game (one-time call after loading of game .dll)
CDATA_DLL(pfnSpawn), // pfnSpawn()
CDATA_DLL(pfnThink), // pfnThink()
CDATA_DLL(pfnUse), // pfnUse()
CDATA_DLL(pfnTouch), // pfnTouch()
CDATA_DLL(pfnBlocked), // pfnBlocked()
CDATA_DLL(pfnKeyValue), // pfnKeyValue()
CDATA_DLL(pfnSave), // pfnSave()
CDATA_DLL(pfnRestore), // pfnRestore()
CDATA_DLL(pfnSetAbsBox), // pfnSetAbsBox()
CDATA_DLL(pfnGameInit), // pfnGameInit() Initialize the game (one-time call after loading of game .dll)
CDATA_DLL(pfnSpawn), // pfnSpawn()
CDATA_DLL(pfnThink), // pfnThink()
CDATA_DLL(pfnUse), // pfnUse()
CDATA_DLL(pfnTouch), // pfnTouch()
CDATA_DLL(pfnBlocked), // pfnBlocked()
CDATA_DLL(pfnKeyValue), // pfnKeyValue()
CDATA_DLL(pfnSave), // pfnSave()
CDATA_DLL(pfnRestore), // pfnRestore()
CDATA_DLL(pfnSetAbsBox), // pfnSetAbsBox()
CDATA_DLL(pfnSaveWriteFields), // pfnSaveWriteFields()
CDATA_DLL(pfnSaveReadFields), // pfnSaveReadFields()
CDATA_DLL(pfnSaveWriteFields), // pfnSaveWriteFields()
CDATA_DLL(pfnSaveReadFields), // pfnSaveReadFields()
CDATA_DLL(pfnSaveGlobalState), // pfnSaveGlobalState()
CDATA_DLL(pfnRestoreGlobalState), // pfnRestoreGlobalState()
CDATA_DLL(pfnResetGlobalState), // pfnResetGlobalState()
CDATA_DLL(pfnSaveGlobalState), // pfnSaveGlobalState()
CDATA_DLL(pfnRestoreGlobalState), // pfnRestoreGlobalState()
CDATA_DLL(pfnResetGlobalState), // pfnResetGlobalState()
CDATA_DLL_H(pfnClientConnect, P_PRE, mm_ClientConnect), // pfnClientConnect() (wd) Client has connected
CDATA_DLL_H(pfnClientDisconnect, P_PRE, mm_ClientDisconnect), // pfnClientDisconnect() (wd) Player has left the game
CDATA_DLL(pfnClientKill), // pfnClientKill() (wd) Player has typed "kill"
CDATA_DLL(pfnClientPutInServer), // pfnClientPutInServer() (wd) Client is entering the game
CDATA_DLL_H(pfnClientConnect, P_PRE, mm_ClientConnect), // pfnClientConnect() (wd) Client has connected
CDATA_DLL_H(pfnClientDisconnect, P_PRE, mm_ClientDisconnect), // pfnClientDisconnect() (wd) Player has left the game
CDATA_DLL(pfnClientKill), // pfnClientKill() (wd) Player has typed "kill"
CDATA_DLL(pfnClientPutInServer), // pfnClientPutInServer() (wd) Client is entering the game
CDATA_DLL_H(pfnClientCommand, P_PRE, mm_ClientCommand),
CDATA_DLL(pfnClientUserInfoChanged), // pfnClientUserInfoChanged() (wd) Client has updated their setinfo structure
CDATA_DLL(pfnServerActivate), // pfnServerActivate() (wd) Server is starting a new map
CDATA_DLL(pfnServerDeactivate), // pfnServerDeactivate() (wd) Server is leaving the map (shutdown), or changelevel); SDK2
CDATA_DLL(pfnClientUserInfoChanged), // pfnClientUserInfoChanged() (wd) Client has updated their setinfo structure
CDATA_DLL(pfnServerActivate), // pfnServerActivate() (wd) Server is starting a new map
CDATA_DLL(pfnServerDeactivate), // pfnServerDeactivate() (wd) Server is leaving the map (shutdown), or changelevel); SDK2
CDATA_DLL(pfnPlayerPreThink), // pfnPlayerPreThink()
CDATA_DLL(pfnPlayerPostThink), // pfnPlayerPostThink()
CDATA_DLL(pfnPlayerPreThink), // pfnPlayerPreThink()
CDATA_DLL(pfnPlayerPostThink), // pfnPlayerPostThink()
CDATA_DLL(pfnStartFrame), // pfnStartFrame()
CDATA_DLL(pfnParmsNewLevel), // pfnParmsNewLevel()
CDATA_DLL(pfnParmsChangeLevel), // pfnParmsChangeLevel()
CDATA_DLL(pfnStartFrame), // pfnStartFrame()
CDATA_DLL(pfnParmsNewLevel), // pfnParmsNewLevel()
CDATA_DLL(pfnParmsChangeLevel), // pfnParmsChangeLevel()
CDATA_DLL(pfnGetGameDescription), // pfnGetGameDescription() Returns string describing current .dll. E.g. "TeamFotrress 2"), "Half-Life"
CDATA_DLL(pfnPlayerCustomization), // pfnPlayerCustomization() Notifies .dll of new customization for player.
CDATA_DLL(pfnGetGameDescription), // pfnGetGameDescription() Returns string describing current .dll. E.g. "TeamFotrress 2"), "Half-Life"
CDATA_DLL(pfnPlayerCustomization), // pfnPlayerCustomization() Notifies .dll of new customization for player.
CDATA_DLL(pfnSpectatorConnect), // pfnSpectatorConnect() Called when spectator joins server
CDATA_DLL(pfnSpectatorDisconnect), // pfnSpectatorDisconnect() Called when spectator leaves the server
CDATA_DLL(pfnSpectatorThink), // pfnSpectatorThink() Called when spectator sends a command packet (usercmd_t)
CDATA_DLL(pfnSpectatorConnect), // pfnSpectatorConnect() Called when spectator joins server
CDATA_DLL(pfnSpectatorDisconnect), // pfnSpectatorDisconnect() Called when spectator leaves the server
CDATA_DLL(pfnSpectatorThink), // pfnSpectatorThink() Called when spectator sends a command packet (usercmd_t)
CDATA_DLL(pfnSys_Error), // pfnSys_Error() Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. SDK2
CDATA_DLL(pfnSys_Error), // pfnSys_Error() Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. SDK2
CDATA_DLL(pfnPM_Move), // pfnPM_Move() (wd) SDK2
CDATA_DLL(pfnPM_Init), // pfnPM_Init() Server version of player movement initialization; (wd) SDK2
CDATA_DLL(pfnPM_FindTextureType), // pfnPM_FindTextureType() (wd) SDK2
CDATA_DLL(pfnPM_Move), // pfnPM_Move() (wd) SDK2
CDATA_DLL(pfnPM_Init), // pfnPM_Init() Server version of player movement initialization; (wd) SDK2
CDATA_DLL(pfnPM_FindTextureType), // pfnPM_FindTextureType() (wd) SDK2
CDATA_DLL(pfnSetupVisibility), // pfnSetupVisibility() Set up PVS and PAS for networking for this client; (wd) SDK2
CDATA_DLL(pfnUpdateClientData), // pfnUpdateClientData() Set up data sent only to specific client; (wd) SDK2
CDATA_DLL(pfnAddToFullPack), // pfnAddToFullPack() (wd) SDK2
CDATA_DLL(pfnCreateBaseline), // pfnCreateBaseline() Tweak entity baseline for network encoding), allows setup of player baselines), too.; (wd) SDK2
CDATA_DLL(pfnRegisterEncoders), // pfnRegisterEncoders() Callbacks for network encoding; (wd) SDK2
CDATA_DLL(pfnGetWeaponData), // pfnGetWeaponData() (wd) SDK2
CDATA_DLL(pfnCmdStart), // pfnCmdStart() (wd) SDK2
CDATA_DLL(pfnCmdEnd), // pfnCmdEnd() (wd) SDK2
CDATA_DLL(pfnConnectionlessPacket), // pfnConnectionlessPacket() (wd) SDK2
CDATA_DLL(pfnGetHullBounds), // pfnGetHullBounds() (wd) SDK2
CDATA_DLL(pfnCreateInstancedBaselines), // pfnCreateInstancedBaselines() (wd) SDK2
CDATA_DLL(pfnInconsistentFile), // pfnInconsistentFile() (wd) SDK2
CDATA_DLL(pfnAllowLagCompensation), // pfnAllowLagCompensation() (wd) SDK2
CDATA_DLL(pfnSetupVisibility), // pfnSetupVisibility() Set up PVS and PAS for networking for this client; (wd) SDK2
CDATA_DLL(pfnUpdateClientData), // pfnUpdateClientData() Set up data sent only to specific client; (wd) SDK2
CDATA_DLL(pfnAddToFullPack), // pfnAddToFullPack() (wd) SDK2
CDATA_DLL(pfnCreateBaseline), // pfnCreateBaseline() Tweak entity baseline for network encoding), allows setup of player baselines), too.; (wd) SDK2
CDATA_DLL(pfnRegisterEncoders), // pfnRegisterEncoders() Callbacks for network encoding; (wd) SDK2
CDATA_DLL(pfnGetWeaponData), // pfnGetWeaponData() (wd) SDK2
CDATA_DLL(pfnCmdStart), // pfnCmdStart() (wd) SDK2
CDATA_DLL(pfnCmdEnd), // pfnCmdEnd() (wd) SDK2
CDATA_DLL(pfnConnectionlessPacket), // pfnConnectionlessPacket() (wd) SDK2
CDATA_DLL(pfnGetHullBounds), // pfnGetHullBounds() (wd) SDK2
CDATA_DLL(pfnCreateInstancedBaselines), // pfnCreateInstancedBaselines()(wd) SDK2
CDATA_DLL(pfnInconsistentFile), // pfnInconsistentFile() (wd) SDK2
CDATA_DLL(pfnAllowLagCompensation), // pfnAllowLagCompensation() (wd) SDK2
};
compile_data_t g_newdllfunc_cdata[] =
{
CDATA_NEWDLL(pfnOnFreeEntPrivateData), // pfnOnFreeEntPrivateData() Called right before the object's memory is freed. Calls its destructor.
CDATA_NEWDLL(pfnGameShutdown), // pfnGameShutdown()
CDATA_NEWDLL(pfnShouldCollide), // pfnShouldCollide()
CDATA_NEWDLL(pfnOnFreeEntPrivateData), // pfnOnFreeEntPrivateData() Called right before the object's memory is freed. Calls its destructor.
CDATA_NEWDLL_H(pfnGameShutdown, P_PRE, mm_GameShutdown), // pfnGameShutdown()
CDATA_NEWDLL(pfnShouldCollide), // pfnShouldCollide()
CDATA_NEWDLL(pfnCvarValue), // pfnCvarValue() (fz) Use mm_CvarValue2 instead
CDATA_NEWDLL(pfnCvarValue2) // pfnCvarValue2() (fz) When pfnQueryClientCvarValue2() completes it will call
CDATA_NEWDLL(pfnCvarValue), // pfnCvarValue() (fz) Use mm_CvarValue2 instead
CDATA_NEWDLL(pfnCvarValue2) // pfnCvarValue2() (fz) When pfnQueryClientCvarValue2() completes it will call
// pfnCvarValue2() with the request ID supplied earlier, the name of the cvar requested and the value of that cvar.
};
@ -202,6 +209,8 @@ C_DLLEXPORT int GetNewDLLFunctions(NEW_DLL_FUNCTIONS *pNewFunctionTable, int *in
*interfaceVersion = NEW_DLL_FUNCTIONS_VERSION;
return FALSE;
}
g_meta_extdll.load();
memcpy(pNewFunctionTable, &sNewFunctionTable, sizeof(NEW_DLL_FUNCTIONS));
return TRUE;
}

View File

@ -8,10 +8,12 @@ struct engine_t
memset(&pl_funcs, 0, sizeof pl_funcs);
}
enginefuncs_t* funcs; // engine funcs
globalvars_t* globals; // engine globals
enginefuncs_t* funcs; // engine funcs
globalvars_t* globals; // engine globals
// Our modified version of the engine funcs, to give to plugins.
enginefuncs_t pl_funcs; // "modified" eng funcs we give to plugins
enginefuncs_t pl_funcs; // "modified" eng funcs we give to plugins
CSysModule sys_module;
};
extern engine_t g_engine;

View File

@ -47,7 +47,7 @@ bool lookup_game_postfixes(gamedll_t *gamedll)
char pathname[PATH_MAX];
static char postfix_path[PATH_MAX] = "";
strlcpy(pathname, gamedll->pathname);
Q_strlcpy(pathname, gamedll->pathname);
// find extensions and skip
char *pos = strrchr(pathname, '.');
@ -58,12 +58,12 @@ bool lookup_game_postfixes(gamedll_t *gamedll)
for (size_t i = 0; i < arraysize(g_platform_postfixes); i++)
{
postfix_path[0] = '\0';
strlcat(postfix_path, pathname);
strlcat(postfix_path, g_platform_postfixes[i]);
Q_strlcat(postfix_path, pathname);
Q_strlcat(postfix_path, g_platform_postfixes[i]);
if (is_file_exists_in_gamedir(postfix_path)) {
strlcpy(gamedll->pathname, postfix_path);
strlcpy(gamedll->real_pathname, postfix_path);
Q_strlcpy(gamedll->pathname, postfix_path);
Q_strlcpy(gamedll->real_pathname, postfix_path);
gamedll->file = postfix_path;
return true;
@ -94,7 +94,7 @@ bool install_gamedll(char *from, const char *to)
return false;
}
int length_out = Q_write(fd, cachefile, length_in);
int length_out = write(fd, cachefile, length_in);
FREE_FILE(cachefile);
close(fd);
@ -146,7 +146,7 @@ bool setup_gamedll(gamedll_t *gamedll)
// Use override-dll if specified.
if (g_config->m_gamedll) {
strlcpy(gamedll->pathname, g_config->m_gamedll);
Q_strlcpy(gamedll->pathname, g_config->m_gamedll);
// If the path is relative, the gamedll file will be missing and
// it might be found in the cache file.
@ -157,7 +157,7 @@ bool setup_gamedll(gamedll_t *gamedll)
// If we could successfully install the gamedll from the cache we
// rectify the pathname to be a full pathname.
if (install_gamedll(gamedll->pathname, szInstallPath)) {
strlcpy(gamedll->pathname, szInstallPath);
Q_strlcpy(gamedll->pathname, szInstallPath);
}
}
@ -187,7 +187,7 @@ bool setup_gamedll(gamedll_t *gamedll)
Q_snprintf(gamedll->real_pathname, sizeof(gamedll->real_pathname), "%s/dlls/%s", gamedll->gamedir, knownfn);
}
else {
strlcpy(gamedll->real_pathname, gamedll->pathname);
Q_strlcpy(gamedll->real_pathname, gamedll->pathname);
}
if (override) {
@ -198,7 +198,7 @@ bool setup_gamedll(gamedll_t *gamedll)
META_LOG("Overriding game '%s' with dllfile '%s'", gamedll->name, gamedll->file);
}
else if (known) {
strlcpy(gamedll->desc, known->desc);
Q_strlcpy(gamedll->desc, known->desc);
#if !defined(_WIN32)
if (!is_file_exists_in_gamedir(gamedll->pathname))

View File

@ -15,6 +15,8 @@ void WINAPI GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t* pG
gpGlobals = pGlobals;
g_engine.funcs = &g_engfuncs;
g_engine.globals = pGlobals;
g_engfuncs = *pengfuncsFromEngine;
g_engine.sys_module.load(pengfuncsFromEngine);
g_engfuncs = *pengfuncsFromEngine;
flush_ALERT_buffer();

File diff suppressed because it is too large Load Diff

118
metamod/src/mem_utils.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "precompiled.h"
static_allocator::static_allocator(memory_protection protection) : m_protection(protection)
{
}
char *static_allocator::allocate(const size_t n)
{
if (!m_pages.size() || m_used + n > Pagesize)
allocate_page();
auto ptr = reinterpret_cast<char *>(m_pages.back()) + m_used;
m_used += n;
return ptr;
}
char *static_allocator::strdup(const char *string)
{
size_t len = Q_strlen(string) + 1;
return (char *)Q_memcpy(allocate(len), string, len);
}
void static_allocator::deallocate_all()
{
for (auto page : m_pages)
#ifdef WIN32
VirtualFree(page, 0, MEM_RELEASE);
#else
munmap(page, Pagesize);
#endif
m_pages.clear();
}
size_t static_allocator::memory_used() const
{
return (m_pages.size() - 1) * Pagesize + m_used;
}
bool static_allocator::contain(uint32 addr)
{
for (auto p : m_pages) {
if (uint32(p) <= addr && addr < uint32(p) + Pagesize)
return true;
}
return false;
}
char *static_allocator::find_pattern(char *pattern, size_t len)
{
for (auto p : m_pages) {
for (char *c = (char *)p, *e = c + Pagesize - len; c < e; c++) {
if (mem_compare(c, pattern, len))
return c;
}
}
return nullptr;
}
void static_allocator::allocate_page()
{
#ifdef WIN32
auto page = VirtualAlloc(nullptr, Pagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
auto page = mmap(nullptr, Pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
#endif
m_used = 0;
m_pages.push_back(page);
}
bool mem_compare(const char *addr, const char *pattern, size_t len)
{
for (auto c = pattern, pattern_end = pattern + len; c < pattern_end; ++c, ++addr) {
if (*c == *addr || *c == '\x2A')
continue;
return false;
}
return true;
}
char *mem_find_pattern(char *pos, int range, const char *pattern, size_t len)
{
for (auto c = pos + range - len; pos < c; ++pos) {
if (mem_compare(pos, pattern, len))
return pos;
}
return nullptr;
}
char *mem_find_ref(char *pos, char *end, char opcode, uint32 ref, bool relative)
{
for (; pos < end; ++pos)
{
if (*pos == opcode)
{
if (relative)
{
if ((uint32)pos + 5 + *(uint32 *)(pos + 1) == ref)
return pos;
}
else
{
if (*(uint32 *)(pos + 1) == ref)
return pos;
}
}
}
return nullptr;
}
char *mem_find_string_push(char *addr, const char *string, size_t len)
{
char *ptr = mem_find_pattern(addr, len, string, Q_strlen(string) + 1);
return mem_find_ref(addr, addr + len - 5, '\x68', (uint32)ptr, false);
}

49
metamod/src/mem_utils.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
class static_allocator
{
public:
enum memory_protection : uint8
{
#ifdef _WIN32
mp_readwrite = PAGE_READWRITE,
mp_rwx = PAGE_EXECUTE_READWRITE
#else
mp_readwrite = PROT_READ | PROT_WRITE,
mp_rwx = PROT_READ | PROT_WRITE | PROT_EXEC
#endif
};
static_allocator(memory_protection protection);
char *allocate(const size_t n);
char *strdup(const char *string);
void deallocate_all();
size_t memory_used() const;
bool contain(uint32 addr);
char *find_pattern(char *pattern, size_t len);
template<typename T>
T *allocate()
{
return (T *)allocate(sizeof(T));
}
private:
void allocate_page();
enum
{
Pagesize = 4096
};
size_t m_used = 0;
std::vector<void *> m_pages;
memory_protection m_protection;
friend class CJit;
};
bool mem_compare(const char *addr, const char *pattern, size_t len);
char *mem_find_pattern(char *pos, int range, const char *pattern, size_t len);
char *mem_find_ref(char *pos, char *end, char opcode, uint32 ref, bool relative);
char *mem_find_string_push(char *addr, const char *string, size_t len);

View File

@ -0,0 +1,55 @@
#include "precompiled.h"
IRehldsApi* g_RehldsApi;
const RehldsFuncs_t* g_RehldsFuncs;
IRehldsHookchains* g_RehldsHookchains;
IRehldsServerStatic* g_RehldsSvs;
bool rehlds_api_init(CSysModule* engineModule)
{
if (!engineModule) {
META_ERROR("Failed to locate engine module\n");
return false;
}
CreateInterfaceFn ifaceFactory = Sys_GetFactory(engineModule);
if (!ifaceFactory) {
META_ERROR("Failed to locate interface factory in engine module\n");
return false;
}
int retCode = 0;
g_RehldsApi = (IRehldsApi*)ifaceFactory(VREHLDS_HLDS_API_VERSION, &retCode);
if (!g_RehldsApi) {
return false;
}
int majorVersion = g_RehldsApi->GetMajorVersion();
int minorVersion = g_RehldsApi->GetMinorVersion();
if (majorVersion != REHLDS_API_VERSION_MAJOR) {
META_ERROR("REHLDS Api major version mismatch; expected %d, real %d\n", REHLDS_API_VERSION_MAJOR, majorVersion);
return false;
}
if (minorVersion < REHLDS_API_VERSION_MINOR) {
META_ERROR("REHLDS Api minor version mismatch; expected at least %d, real %d\n", REHLDS_API_VERSION_MINOR, minorVersion);
return false;
}
g_RehldsFuncs = g_RehldsApi->GetFuncs();
g_RehldsHookchains = g_RehldsApi->GetHookchains();
g_RehldsSvs = g_RehldsApi->GetServerStatic();
return true;
}
bool meta_init_rehlds_api()
{
CSysModule* engineModule = Sys_LoadModule(ENGINE_LIB);
if (!rehlds_api_init(engineModule)) {
return false;
}
return true;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "engine_hlds_api.h"
extern IRehldsApi* g_RehldsApi;
extern const RehldsFuncs_t* g_RehldsFuncs;
extern IRehldsHookchains* g_RehldsHookchains;

View File

@ -1,6 +1,6 @@
#include "precompiled.h"
cvar_t g_meta_version = { "metamod_version", APP_VERSION_STRD, FCVAR_SERVER, 0, nullptr };
cvar_t g_meta_version = { "metamod_version", APP_VERSION, FCVAR_SERVER, 0, nullptr };
MConfig g_static_config;
MConfig *g_config = &g_static_config;
@ -44,21 +44,20 @@ void metamod_startup()
Q_snprintf(execFile, sizeof execFile, "%s/%s", g_config->directory(), EXEC_CFG);
META_CONS(" ");
META_CONS(" Metamod-r version %s Copyright (c) 2016-2017 ReHLDS Team (rebuild of original Metamod by Will Day and Jussi Kivilinna)", APP_VERSION_STRD);
META_CONS(" Metamod-r version %s Copyright (c) 2016-2017 ReHLDS Team (rebuild of original Metamod by Will Day and Jussi Kivilinna)", APP_VERSION);
META_CONS(" Metamod-r comes with ABSOLUTELY NO WARRANTY; for details type `meta gpl'.");
META_CONS(" This is free software, and you are welcome to redistribute it");
META_CONS(" under certain conditions; type `meta gpl' for details.");
META_CONS(" ");
META_CONS("Metamod-r v%s, API (%s)", APP_VERSION_STRD, META_INTERFACE_VERSION);
META_CONS("Metamod-r v%s, API (%s)", APP_VERSION, META_INTERFACE_VERSION);
META_CONS("Metamod-r build: " __TIME__ " " __DATE__ "");
META_CONS("Metamod-r from: " APP_COMMIT_URL APP_COMMIT_SHA "");
// Get gamedir, very early on, because it seems we need it all over the
// place here at the start.
if (!meta_init_gamedll()) {
META_ERROR("Failure to init game DLL; exiting...");
do_exit(1);
Sys_Error("Failure to init game DLL; exiting...");
}
// Register various console commands and cvars.
@ -201,8 +200,7 @@ void metamod_startup()
g_plugins = new MPluginList(pluginFile);
if (!meta_load_gamedll()) {
META_ERROR("Failure to load game DLL; exiting...");
do_exit(1);
Sys_Error("Failure to load game DLL; exiting...");
}
if (!g_plugins->load()) {
@ -212,6 +210,10 @@ void metamod_startup()
meta_init_rehlds_api();
if (!g_meta_extdll.init(&g_engine.sys_module)) {
Sys_Error("Failure to init extension DLL; exiting...");
}
// Allow for commands to metamod plugins at startup. Autoexec.cfg is
// read too early, and server.cfg is read too late.
//
@ -294,7 +296,7 @@ bool get_function_table(const char* ifname, int ifvers_mm, table_t*& table, size
auto pfnGetFuncs = (getfunc_t)g_GameDLL.sys_module.getsym(ifname);
if (pfnGetFuncs) {
table = (table_t *)Q_calloc(1, table_size);
table = (table_t *)calloc(1, table_size);
int ifvers_gamedll = ifvers_mm;
@ -304,7 +306,7 @@ bool get_function_table(const char* ifname, int ifvers_mm, table_t*& table, size
}
META_ERROR("dll: Failure calling %s in game '%s'", ifname, g_GameDLL.name);
Q_free(table);
free(table);
table = nullptr;
if (ifvers_gamedll != ifvers_mm) {
@ -336,7 +338,7 @@ bool get_function_table_old(const char* ifname, int ifvers_mm, table_t*& table,
auto pfnGetFuncs = (getfunc_t)g_GameDLL.sys_module.getsym(ifname);
if (pfnGetFuncs) {
table = (table_t *)Q_calloc(1, table_size);
table = (table_t *)calloc(1, table_size);
if (pfnGetFuncs(table, ifvers_mm)) {
META_DEBUG(3, "dll: Game '%s': Found %s", g_GameDLL.name, ifname);
@ -344,7 +346,7 @@ bool get_function_table_old(const char* ifname, int ifvers_mm, table_t*& table,
}
META_ERROR("dll: Failure calling %s in game '%s'", ifname, g_GameDLL.name);
Q_free(table);
free(table);
table = nullptr;
}
else {
@ -478,8 +480,7 @@ static void meta_apply_fix_data(std::vector<fixdata_t>& data)
char* ptr = g_jit.find_callback_pattern(pattern, sizeof pattern);
if (!ptr) {
META_ERROR("Failed to fix callback retaddr.\n Bye bye...\n");
do_exit(666);
Sys_Error("Failed to fix callback retaddr.\n Bye bye...\n");
}
// FF D1 call ecx

View File

@ -1,70 +0,0 @@
#include "precompiled.h"
IRehldsApi* g_RehldsApi;
const RehldsFuncs_t* g_RehldsFuncs;
IRehldsHookchains* g_RehldsHookchains;
IRehldsServerStatic* g_RehldsSvs;
bool rehlds_api_try_init(CSysModule* engineModule, char* failureReason)
{
if (!engineModule) {
META_ERROR("Failed to locate engine module\n");
return false;
}
CreateInterfaceFn ifaceFactory = Sys_GetFactory(engineModule);
if (!ifaceFactory) {
sprintf(failureReason, "Failed to locate interface factory in engine module\n");
return false;
}
int retCode = 0;
g_RehldsApi = (IRehldsApi*)ifaceFactory(VREHLDS_HLDS_API_VERSION, &retCode);
if (!g_RehldsApi) {
sprintf(failureReason, "Failed to locate retrieve rehlds api interface from engine module, return code is %d\n", retCode);
return false;
}
int majorVersion = g_RehldsApi->GetMajorVersion();
int minorVersion = g_RehldsApi->GetMinorVersion();
if (majorVersion != REHLDS_API_VERSION_MAJOR) {
sprintf(failureReason, "REHLDS Api major version mismatch; expected %d, real %d\n", REHLDS_API_VERSION_MAJOR, majorVersion);
return false;
}
if (minorVersion < REHLDS_API_VERSION_MINOR) {
sprintf(failureReason, "REHLDS Api minor version mismatch; expected at least %d, real %d\n", REHLDS_API_VERSION_MINOR, minorVersion);
return false;
}
g_RehldsFuncs = g_RehldsApi->GetFuncs();
g_RehldsHookchains = g_RehldsApi->GetHookchains();
g_RehldsSvs = g_RehldsApi->GetServerStatic();
return true;
}
bool meta_init_rehlds_api()
{
char failReason[2048];
#ifdef WIN32
CSysModule* engineModule = Sys_LoadModule("swds.dll");
if (!rehlds_api_try_init(engineModule, failReason)) {
engineModule = Sys_LoadModule("filesystem_stdio.dll");
if (!rehlds_api_try_init(engineModule, failReason)) {
META_ERROR("%s", failReason);
return false;
}
}
#else
CSysModule* engineModule = Sys_LoadModule("engine_i486.so");
if (!rehlds_api_try_init(engineModule, failReason)) {
META_ERROR("%s", failReason);
return false;
}
#endif
return true;
}

96
metamod/src/mextdll.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "precompiled.h"
CExtDll g_meta_extdll;
CExtDll::CExtDll()
: m_dlls(nullptr), m_count(nullptr), m_hGameDLL(CSysModule::INVALID_HANDLE)
{
}
void CExtDll::load()
{
if (g_RehldsFuncs)
{
if (!g_RehldsFuncs->AddExtDll(m_hGameDLL)) {
Sys_Error("Failure to add extension DLL; exiting...\n");
}
return;
}
auto extdll = &m_dlls[(*m_count)++];
Q_memset(extdll, 0, sizeof(*extdll));
extdll->lDLLHandle = m_hGameDLL;
}
void CExtDll::unload()
{
if (g_RehldsFuncs) {
g_RehldsFuncs->RemoveExtDll(m_hGameDLL);
return;
}
for (auto i = 0u; i < *m_count; i++)
{
if (m_dlls[i].lDLLHandle == m_hGameDLL)
{
--(*m_count);
if (*m_count != i)
{
Q_memmove(&m_dlls[i], &m_dlls[i + 1], (*m_count - i) * sizeof(m_dlls[0]));
i = *m_count;
}
Q_memset(&m_dlls[i], 0, sizeof(m_dlls[0]));
return;
}
}
}
bool CExtDll::init(CSysModule *module)
{
m_hGameDLL = g_GameDLL.sys_module.gethandle();
if (g_RehldsFuncs) {
return true;
}
m_dlls = (extensiondll_t *)module->getsym("g_rgextdll");
m_count = (size_t *)module->getsym("g_iextdllMac");
if (m_dlls && m_count) {
return true;
}
// HACK HACK MORE HACK
// HLDS SHIT
auto pos = module->find_string_push("SV_SaveGameComment");
if (!pos) {
return false;
}
const char patterns[2][2][14] = {
{ "\x56\x33\xF6\x39\x2A\x2A\x2A\x2A\x2A\x7E\x2A\x53\x8B", "\x5B\xC3\x90\x90\xA1\x2A\x2A\x2A\x2A\x53\x56\x57\x33" },
{ "\x53\x8B\x2A\x2A\x2A\x2A\x2A\x57\xBF\x2A\x2A\x2A\x2A", "\x7E\x2A\x8B\x2A\x2A\x2A\x2A\x2A\xBE\x2A\x2A\x2A\x2A" },
};
for (auto i = 0u; i < ARRAYSIZE(patterns); i++)
{
auto addr = mem_find_pattern(pos - 33, 20, patterns[0][i], sizeof(patterns[0][i]) - 1);
if (!addr) {
continue;
}
m_count = *(size_t **)(addr + 5);
addr = mem_find_pattern(addr, 30, patterns[1][i], sizeof(patterns[1][i]) - 1);
if (!addr) {
return false;
}
m_dlls = *(extensiondll_t **)(addr + 9);
return true;
}
return false;
}

19
metamod/src/mextdll.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "pr_dlls.h"
class CExtDll
{
public:
CExtDll();
bool init(CSysModule *module);
void load();
void unload();
private:
module_handle_t m_hGameDLL;
extensiondll_t *m_dlls;
size_t *m_count;
};
extern CExtDll g_meta_extdll;

View File

@ -629,7 +629,7 @@ template<typename T>
void free_table(T& table)
{
if (table) {
Q_free(table);
free(table);
table = nullptr;
}
}
@ -861,7 +861,7 @@ bool get_function_table_from_plugin(const char* pl_desc, int ifvers_mm, getter_t
if (getter) {
if (!table)
table = (table_t *)Q_calloc(1, table_size);
table = (table_t *)calloc(1, table_size);
if (getter(table, &ifvers_pl)) {
META_DEBUG(3, "dll: Plugin '%s': Found %s", pl_desc, getter_name);
return true;
@ -874,7 +874,7 @@ bool get_function_table_from_plugin(const char* pl_desc, int ifvers_mm, getter_t
else {
META_DEBUG(5, "dll: Plugin '%s': No %s", pl_desc, getter_name);
if (table)
Q_free(table);
free(table);
table = nullptr;
}
@ -886,7 +886,7 @@ bool get_function_table_from_plugin_old(const char* pl_desc, int ifvers_mm, gett
{
if (getter) {
if (!table)
table = (table_t *)Q_calloc(1, table_size);
table = (table_t *)calloc(1, table_size);
if (getter(table, ifvers_mm)) {
META_DEBUG(3, "dll: Plugin '%s': Found %s", pl_desc, getter_name);
return true;
@ -897,7 +897,7 @@ bool get_function_table_from_plugin_old(const char* pl_desc, int ifvers_mm, gett
else {
META_DEBUG(5, "dll: Plugin '%s': No %s", pl_desc, getter_name);
if (table)
Q_free(table);
free(table);
table = nullptr;
}
@ -924,7 +924,7 @@ bool MPlugin::attach(PLUG_LOADTIME now)
// Make copy of gameDLL's function tables for each plugin, so we don't
// risk the plugins screwing with the tables everyone uses.
if (g_GameDLL.funcs.dllapi_table && !m_gamedll_funcs.dllapi_table) {
m_gamedll_funcs.dllapi_table = (DLL_FUNCTIONS *)Q_malloc(sizeof(DLL_FUNCTIONS));
m_gamedll_funcs.dllapi_table = (DLL_FUNCTIONS *)malloc(sizeof(DLL_FUNCTIONS));
if (!m_gamedll_funcs.dllapi_table) {
META_ERROR("dll: Failed attach plugin '%s': Failed malloc() for dllapi_table");
return false;
@ -932,7 +932,7 @@ bool MPlugin::attach(PLUG_LOADTIME now)
Q_memcpy(m_gamedll_funcs.dllapi_table, g_GameDLL.funcs.dllapi_table, sizeof(DLL_FUNCTIONS));
}
if (g_GameDLL.funcs.newapi_table && !m_gamedll_funcs.newapi_table) {
m_gamedll_funcs.newapi_table = (NEW_DLL_FUNCTIONS *)Q_malloc(sizeof(NEW_DLL_FUNCTIONS));
m_gamedll_funcs.newapi_table = (NEW_DLL_FUNCTIONS *)malloc(sizeof(NEW_DLL_FUNCTIONS));
if (!m_gamedll_funcs.newapi_table) {
META_ERROR("dll: Failed attach plugin '%s': Failed malloc() for newapi_table");
return false;

View File

@ -2,6 +2,12 @@
#include "utils.h"
#if defined(_WIN32)
constexpr char *PLATFORM_DLEXT = ".dll";
#else
constexpr char *PLATFORM_DLEXT = ".so";
#endif
// Flags to indicate current "load" state of plugin.
// NOTE: order is important, as greater/less comparisons are made.
enum PLUG_STATUS : uint8

View File

@ -7,7 +7,7 @@ MRegCmd::MRegCmd(const char* cmd_name, REG_CMD_FN cmd_handler, MPlugin* cmd_plug
MRegCmd::~MRegCmd()
{
Q_free(m_name);
free(m_name);
}
bool MRegCmd::call() const
@ -157,8 +157,8 @@ MRegCvar::MRegCvar(cvar_t* cv_ptr, MPlugin* cv_plugin) : m_cvar(cv_ptr), m_plugi
MRegCvar::~MRegCvar()
{
Q_free((void *)m_cvar->name);
Q_free(m_cvar->string);
free((void *)m_cvar->name);
free(m_cvar->string);
delete m_cvar;
}

View File

@ -1,153 +0,0 @@
#include "precompiled.h"
CSysModule::CSysModule() : m_handle(0), m_base(0), m_size(0)
{
}
#ifdef _WIN32
module_handle_t CSysModule::load(const char* filepath)
{
if (!m_handle) {
m_handle = LoadLibrary(filepath);
MODULEINFO module_info;
if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof module_info)) {
m_base = (uintptr_t)module_info.lpBaseOfDll;
m_size = module_info.SizeOfImage;
}
}
return m_handle;
}
bool CSysModule::unload()
{
bool ret = true;
if (m_handle) {
ret = FreeLibrary(m_handle) != ERROR;
m_handle = 0;
m_base = 0;
m_size = 0;
}
return ret;
}
void* CSysModule::getsym(const char* name) const
{
return m_handle ? GetProcAddress(m_handle, name) : nullptr;
}
#else
static ElfW(Addr) dlsize(void* base)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr;
ElfW(Addr) end;
ehdr = (ElfW(Ehdr) *)base;
/* Find the first program header */
phdr = (ElfW(Phdr)*)((ElfW(Addr))ehdr + ehdr->e_phoff);
/* Find the final PT_LOAD segment's extent */
for (int i = 0; i < ehdr->e_phnum; ++i)
if (phdr[i].p_type == PT_LOAD)
end = phdr[i].p_vaddr + phdr[i].p_memsz;
/* The start (virtual) address is always zero, so just return end.*/
return end;
}
module_handle_t CSysModule::load(const char* filepath)
{
if (!m_handle) {
m_handle = dlopen(filepath, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
char buf[1024], dummy[1024], path[260];
sprintf(buf, "/proc/%i/maps", getpid());
FILE* fp = fopen(buf, "r");
while (fgets(buf, sizeof buf, fp)) {
uintptr_t start, end;
int args = sscanf(buf, "%x-%x %128s %128s %128s %128s %255s", &start, &end, dummy, dummy, dummy, dummy, path);
if (args != 7) {
continue;
}
if (!Q_stricmp(path, filepath)) {
m_base = start;
m_size = end - start;
break;
}
}
fclose(fp);
}
return m_handle;
}
bool CSysModule::unload()
{
bool ret = true;
if (m_handle) {
ret = dlclose(m_handle) != 0;
m_handle = 0;
m_base = 0;
m_size = 0;
}
return ret;
}
void* CSysModule::getsym(const char* name) const
{
return m_handle ? dlsym(m_handle, name) : nullptr;
}
#endif
module_handle_t CSysModule::gethandle() const
{
return m_handle;
}
bool CSysModule::contain(void* addr) const
{
return addr && uintptr_t(addr) > m_base && uintptr_t(addr) < m_base + m_size;
}
const char* CSysModule::getloaderror()
{
#ifdef _WIN32
return str_GetLastError();
#else
return dlerror();
#endif
}
#ifdef _WIN32
// Windows doesn't provide a functon analagous to dlerr() that returns a
// string describing the error, so we include one here, as exampled at:
// http://msdn.microsoft.com/library/en-us/debug/errors_0sdh.asp
// except without FORMAT_MESSAGE_ALLOCATE_BUFFER, since we use a local
// static buffer.
static const char* str_GetLastError()
{
static char buf[MAX_STRBUF_LEN];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, MAX_STRBUF_LEN - 1, nullptr);
return buf;
}
#endif
const char* str_os_error()
{
#ifdef _WIN32
return str_GetLastError();
#else
return strerror(errno);
#endif
}

View File

@ -1,52 +1,8 @@
#pragma once
// String describing platform/DLL-type, for matching lines in plugins.ini.
#ifdef _WIN32
#define UNUSED /**/
#define PLATFORM "mswin"
#define PLATFORM_SPC "win32"
#define PLATFORM_DLEXT ".dll"
#define likely(x) (x)
#define unlikely(x) (x)
#else
#define UNUSED __attribute__((unused))
#define PLATFORM "linux"
#define PLATFORM_SPC "lin32"
#define PLATFORM_DLEXT ".so"
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#ifdef _WIN32
typedef HINSTANCE module_handle_t;
#else
typedef void* module_handle_t;
#endif
class CSysModule
{
public:
CSysModule();
module_handle_t load(const char* filename);
bool unload();
void* getsym(const char* name) const;
module_handle_t gethandle() const;
bool contain(void* addr) const;
static const char* getloaderror();
private:
module_handle_t m_handle;
uintptr_t m_base;
uintptr_t m_size;
};
// Windows doesn't have an strtok_r() routine, so we write our own.
#ifdef _WIN32
#define strtok_r(s, delim, ptrptr) mm_strtok_r(s, delim, ptrptr)
#define strtok_r strtok_s
#endif // _WIN32
// Set filename and pathname maximum lengths. Note some windows compilers
@ -110,8 +66,4 @@ private:
#ifndef S_IWGRP
#define S_IWGRP S_IWUSR
#endif
const char *str_GetLastError();
#endif // _WIN32
const char* str_os_error();

View File

@ -18,6 +18,7 @@
#include "osconfig.h"
#include "jitasm.h"
#include "strtools.h"
#define CreateInterface mm_CreateInterface
@ -27,6 +28,7 @@
#include <rehlds_api.h>
#include "osdep.h"
#include "sys_module.h"
#include "mdebug.h"
#include "api_info.h"
#include "commands_meta.h"
@ -38,6 +40,7 @@
#include "game_support.h"
#include "mreg.h"
#include "meta_api.h"
#include "mextdll.h"
#include "mutil.h"
#include "reg_support.h"
#include "mlist.h"
@ -47,8 +50,9 @@
#include "sdk_util.h"
#include "enginecallbacks.h"
#include "utils.h"
#include "mem_utils.h"
#include "callback_jit.h"
#include "metamod_rehlds_api.h"
#include "meta_rehlds_api.h"
#undef CreateInterface
#include "linkent.h"

207
metamod/src/sys_module.cpp Normal file
View File

@ -0,0 +1,207 @@
#include "precompiled.h"
const module_handle_t CSysModule::INVALID_HANDLE = (module_handle_t)0;
CSysModule::CSysModule() : m_handle(INVALID_HANDLE), m_base(0), m_size(0), m_free(true)
{
}
bool CSysModule::is_opened() const
{
return m_handle != INVALID_HANDLE;
}
char *CSysModule::find_string_push(const char *string)
{
return mem_find_string_push((char *)getbase(), string, getsize());
}
char *CSysModule::find_pattern(char *pos, int range, const char *pattern, size_t len)
{
for (auto c = pos + range - len; pos < c; ++pos) {
if (mem_compare(pos, pattern, len))
return pos;
}
return nullptr;
}
#ifdef _WIN32
module_handle_t CSysModule::load(void *addr)
{
MEMORY_BASIC_INFORMATION mem;
VirtualQuery(addr, &mem, sizeof(mem));
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)mem.AllocationBase;
IMAGE_NT_HEADERS *pe = (IMAGE_NT_HEADERS *)((uintptr_t)dos + (uintptr_t)dos->e_lfanew);
if (pe->Signature != IMAGE_NT_SIGNATURE)
return INVALID_HANDLE;
m_free = false;
m_base = (uintptr_t)mem.AllocationBase;
m_size = (size_t)pe->OptionalHeader.SizeOfImage;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<LPCTSTR>(addr), &m_handle);
return m_handle;
}
module_handle_t CSysModule::load(const char *filepath)
{
if (!m_handle) {
m_handle = LoadLibrary(filepath);
MODULEINFO module_info;
if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof(module_info))) {
m_base = (uintptr_t)module_info.lpBaseOfDll;
m_size = module_info.SizeOfImage;
}
}
return m_handle;
}
bool CSysModule::unload()
{
if (m_handle == INVALID_HANDLE) {
return false;
}
bool ret = true;
if (m_free) {
ret = FreeLibrary(m_handle) != ERROR;
}
m_handle = INVALID_HANDLE;
m_base = 0;
m_size = 0;
return ret;
}
void *CSysModule::getsym(const char *name) const
{
return m_handle ? GetProcAddress(m_handle, name) : nullptr;
}
#else
static ElfW(Addr) dlsize(void *base)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr;
ElfW(Addr) end;
ehdr = (ElfW(Ehdr) *)base;
// Find the first program header
phdr = (ElfW(Phdr)*)((ElfW(Addr))ehdr + ehdr->e_phoff);
// Find the final PT_LOAD segment's extent
for (int i = 0; i < ehdr->e_phnum; ++i)
if (phdr[i].p_type == PT_LOAD)
end = phdr[i].p_vaddr + phdr[i].p_memsz;
// The start (virtual) address is always zero, so just return end.
return end;
}
module_handle_t CSysModule::load(void *addr)
{
Dl_info dlinfo;
if ((!dladdr(addr, &dlinfo) && !dlinfo.dli_fbase) || !dlinfo.dli_fname) {
return INVALID_HANDLE;
}
m_free = false;
m_base = (uintptr_t)dlinfo.dli_fbase;
m_size = (size_t)dlsize(dlinfo.dli_fbase);
m_handle = dlopen(dlinfo.dli_fname, RTLD_NOW | RTLD_NOLOAD);
}
module_handle_t CSysModule::load(const char *filepath)
{
if (!m_handle) {
m_handle = dlopen(filepath, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
char buf[1024], dummy[1024], path[260];
sprintf(buf, "/proc/%i/maps", getpid());
FILE* fp = fopen(buf, "r");
while (fgets(buf, sizeof buf, fp)) {
uintptr_t start, end;
int args = sscanf(buf, "%x-%x %128s %128s %128s %128s %255s", &start, &end, dummy, dummy, dummy, dummy, path);
if (args != 7) {
continue;
}
if (!Q_stricmp(path, filepath)) {
m_base = start;
m_size = end - start;
break;
}
}
fclose(fp);
}
return m_handle;
}
bool CSysModule::unload()
{
if (m_handle == INVALID_HANDLE) {
return false;
}
bool ret = true;
if (m_free) {
ret = dlclose(m_handle) != 0;
}
m_handle = INVALID_HANDLE;
m_base = 0;
m_size = 0;
return ret;
}
void* CSysModule::getsym(const char *name) const
{
return m_handle ? dlsym(m_handle, name) : nullptr;
}
#endif
module_handle_t CSysModule::gethandle() const
{
return m_handle;
}
uintptr_t CSysModule::getbase() const
{
return m_base;
}
size_t CSysModule::getsize() const
{
return m_size;
}
bool CSysModule::contain(void *addr) const
{
return addr && uintptr_t(addr) > m_base && uintptr_t(addr) < m_base + m_size;
}
const char *CSysModule::getloaderror()
{
#ifdef _WIN32
static char buf[1024];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, sizeof(buf) - 1, nullptr);
return buf;
#else
return dlerror();
#endif
}

35
metamod/src/sys_module.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#ifdef _WIN32
typedef HINSTANCE module_handle_t;
#else
typedef void* module_handle_t;
#endif
class CSysModule
{
public:
CSysModule();
module_handle_t load(void *addr);
module_handle_t load(const char *filename);
bool unload();
void *getsym(const char *name) const;
module_handle_t gethandle() const;
uintptr_t getbase() const;
size_t getsize() const;
bool contain(void *addr) const;
bool is_opened() const;
char *find_string_push(const char *string);
char *find_pattern(char *pos, int range, const char *pattern, size_t len);
static const char *getloaderror();
static const module_handle_t INVALID_HANDLE;
private:
module_handle_t m_handle;
uintptr_t m_base;
size_t m_size;
bool m_free; // m_handle should be released
};

View File

@ -28,104 +28,6 @@ const char* LOCALINFO(char* key)
return ENTITY_KEYVALUE(nullptr, key);
}
static_allocator::static_allocator(memory_protection protection) : m_protection(protection)
{
}
char* static_allocator::allocate(const size_t n)
{
if (!m_pages.size() || m_used + n > Pagesize)
allocate_page();
auto ptr = reinterpret_cast<char *>(m_pages.back()) + m_used;
m_used += n;
return ptr;
}
char* static_allocator::strdup(const char* string)
{
size_t len = strlen(string) + 1;
return (char *)memcpy(allocate(len), string, len);
}
void static_allocator::deallocate_all()
{
for (auto page : m_pages)
#ifdef WIN32
VirtualFree(page, 0, MEM_RELEASE);
#else
munmap(page, Pagesize);
#endif
m_pages.clear();
}
size_t static_allocator::memory_used() const
{
return (m_pages.size() - 1) * Pagesize + m_used;
}
bool static_allocator::contain(uint32 addr)
{
for (auto p : m_pages) {
if (uint32(p) <= addr && addr < uint32(p) + Pagesize)
return true;
}
return false;
}
char* static_allocator::find_pattern(char* pattern, size_t len)
{
for (auto p : m_pages) {
for (char* c = (char *)p, *e = c + Pagesize - len; c < e; c++) {
if (mem_compare(c, pattern, len))
return c;
}
}
return nullptr;
}
void static_allocator::allocate_page()
{
#ifdef WIN32
auto page = VirtualAlloc(nullptr, Pagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
auto page = mmap(nullptr, Pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
#endif
m_used = 0;
m_pages.push_back(page);
}
#ifdef _WIN32
// Since windows doesn't provide a verison of strtok_r(), we include one
// here. This may or may not operate exactly like strtok_r(), but does
// what we need it it do.
char* mm_strtok_r(char* s, const char* delim, char** ptrptr)
{
char* begin = nullptr;
char* end = nullptr;
char* rest = nullptr;
if (s)
begin = s;
else
begin = *ptrptr;
if (!begin)
return nullptr;
end = strpbrk(begin, delim);
if (end) {
*end = '\0';
rest = end + 1;
*ptrptr = rest + strspn(rest, delim);
}
else
*ptrptr = nullptr;
return begin;
}
#endif // _WIN32
char* trimbuf(char* str)
{
char* ibuf;
@ -212,12 +114,6 @@ char* realpath(const char* file_name, char* resolved_name)
}
#endif // _WIN32
void __declspec(noreturn) do_exit(int exitval)
{
//Allahu Akbar!!
*((int *)nullptr) = 0;
}
// Checks for a non-empty file, relative to the gamedir if necessary.
// Formerly used LOAD_FILE_FOR_ME, which provided a simple way to check for
// a file under the gamedir, but which would _also_ look in the sibling
@ -286,7 +182,7 @@ char* full_gamedir_path(const char* path, char* fullpath)
// Remove relative path components, if possible.
if (!realpath(buf, fullpath)) {
META_DEBUG(4, "Unable to get realpath for '%s': %s", buf, str_os_error());
META_DEBUG(4, "Unable to get realpath for '%s': %s", buf, strerror(errno));
Q_strncpy(fullpath, path, sizeof fullpath - 1);
fullpath[sizeof fullpath - 1] = '\0';
}
@ -296,16 +192,6 @@ char* full_gamedir_path(const char* path, char* fullpath)
return fullpath;
}
bool mem_compare(const char* addr, const char* pattern, size_t len)
{
for (auto c = pattern, pattern_end = pattern + len; c < pattern_end; ++c, ++addr) {
if (*c == *addr || *c == '\x2A')
continue;
return false;
}
return true;
}
void NORETURN Sys_Error(const char *error, ...)
{
va_list argptr;
@ -316,8 +202,16 @@ void NORETURN Sys_Error(const char *error, ...)
va_end(argptr);
META_CONS("FATAL ERROR (shutting down): %s\n", text);
META_ERROR(text);
int *null = 0;
#ifdef _WIN32
MessageBox(GetForegroundWindow(), text, "Fatal error - Metamod", MB_ICONERROR | MB_OK);
#endif // _WIN32
// Allow chance to read the message, before any window closes.
sleep(3);
int *null = nullptr;
*null = 0;
exit(-1);
}

View File

@ -16,88 +16,11 @@ template <typename T, size_t N>
char(&ArraySizeHelper(T(&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
class static_allocator
{
public:
enum memory_protection : uint8
{
#ifdef _WIN32
mp_readwrite = PAGE_READWRITE,
mp_rwx = PAGE_EXECUTE_READWRITE
#else
mp_readwrite = PROT_READ | PROT_WRITE,
mp_rwx = PROT_READ | PROT_WRITE | PROT_EXEC
#endif
};
static_allocator(memory_protection protection);
char* allocate(const size_t n);
char* strdup(const char* string);
void deallocate_all();
size_t memory_used() const;
bool contain(uint32 addr);
char* find_pattern(char* pattern, size_t len);
template<typename T>
T* allocate()
{
return (T *)allocate(sizeof(T));
}
private:
void allocate_page();
enum
{
Pagesize = 4096
};
size_t m_used = 0;
std::vector<void *> m_pages;
memory_protection m_protection;
friend class CJit;
};
bool is_yes(const char* str);
bool is_no(const char* str);
const char* LOCALINFO(char* key);
template <size_t N>
char *strlcpy(char (&dest)[N], const char *src) {
Q_strncpy(dest, src, N - 1);
dest[N - 1] = '\0';
return dest;
}
inline char *strnlcpy(char *dest, const char *src, size_t n) {
Q_strncpy(dest, src, n - 1);
dest[n - 1] = '\0';
return dest;
}
template <size_t N>
size_t strlcat(char (&dest)[N], const char *src)
{
size_t dstlen = Q_strlen(dest);
size_t size = N - dstlen + 1;
if (!size) {
return dstlen;
}
size_t srclen = Q_strlen(src);
if (srclen > size) {
srclen = size;
}
Q_memcpy(dest + dstlen, src, srclen);
dest[dstlen + srclen] = '\0';
return dstlen + srclen;
}
#ifdef _WIN32
char *mm_strtok_r(char *s, const char *delim, char **ptrptr);
char *realpath(const char *file_name, char *resolved_name);
@ -108,9 +31,6 @@ void normalize_path(char *path);
bool is_abs_path(const char *path);
bool is_valid_path(const char *path);
bool is_platform_postfix(const char *pf);
void __declspec(noreturn) do_exit(int exitval);
bool is_file_exists_in_gamedir(const char *path);
char *full_gamedir_path(const char *path, char *fullpath);
bool mem_compare(const char* addr, const char* pattern, size_t len);