309 lines
12 KiB
C++

#include <STDInclude.hpp>
#include "ConfigStrings.hpp"
namespace Components
{
// Patch game state
// The structure below is our own implementation of the gameState_t structure
static struct ReallocatedGameState_t
{
int stringOffsets[ConfigStrings::MAX_CONFIGSTRINGS];
char stringData[131072]; // MAX_GAMESTATE_CHARS
int dataCount;
} cl_gameState{};
static short sv_configStrings[ConfigStrings::MAX_CONFIGSTRINGS]{};
// New mapping (extra data)
constexpr auto EXTRA_WEAPONS_FIRST = ConfigStrings::BASEGAME_MAX_CONFIGSTRINGS;
constexpr auto EXTRA_WEAPONS_LAST = EXTRA_WEAPONS_FIRST + Weapon::ADDED_WEAPONS - 1;
constexpr auto EXTRA_MODELCACHE_FIRST = EXTRA_WEAPONS_LAST + 1;
constexpr auto EXTRA_MODELCACHE_LAST = EXTRA_MODELCACHE_FIRST + ModelCache::ADDITIONAL_GMODELS;
constexpr auto RUMBLE_FIRST = EXTRA_MODELCACHE_LAST + 1;
constexpr auto RUMBLE_LAST = RUMBLE_FIRST + 31; // TODO
void ConfigStrings::PatchConfigStrings()
{
// bump clientstate fields
Utils::Hook::Set<DWORD>(0x4347A7, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x4982F4, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x4F88B6, MAX_CONFIGSTRINGS); // Save file
Utils::Hook::Set<DWORD>(0x5A1FA7, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x5A210D, MAX_CONFIGSTRINGS); // Game state
Utils::Hook::Set<DWORD>(0x5A840E, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x5A853C, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x5AC392, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x5AC3F5, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x5AC542, MAX_CONFIGSTRINGS); // Game state
Utils::Hook::Set<DWORD>(0x5EADF0, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x625388, MAX_CONFIGSTRINGS);
Utils::Hook::Set<DWORD>(0x625516, MAX_CONFIGSTRINGS);
Utils::Hook::Set(0x405B72, sv_configStrings);
Utils::Hook::Set(0x468508, sv_configStrings);
Utils::Hook::Set(0x47FD7C, sv_configStrings);
Utils::Hook::Set(0x49830E, sv_configStrings);
Utils::Hook::Set(0x498371, sv_configStrings);
Utils::Hook::Set(0x4983D5, sv_configStrings);
Utils::Hook::Set(0x4A74AD, sv_configStrings);
Utils::Hook::Set(0x4BAE7C, sv_configStrings);
Utils::Hook::Set(0x4BAEC3, sv_configStrings);
Utils::Hook::Set(0x6252F5, sv_configStrings);
Utils::Hook::Set(0x625372, sv_configStrings);
Utils::Hook::Set(0x6253D3, sv_configStrings);
Utils::Hook::Set(0x625480, sv_configStrings);
Utils::Hook::Set(0x6254CB, sv_configStrings);
// TODO: Check if all of these actually mark the end of the array
// Only 2 actually mark the end, the rest is header data or so
Utils::Hook::Set(0x405B8F, &sv_configStrings[ARRAYSIZE(sv_configStrings)]);
Utils::Hook::Set(0x4A74C9, &sv_configStrings[ARRAYSIZE(sv_configStrings)]);
Utils::Hook(0x405BBE, ConfigStrings::SV_ClearConfigStrings, HOOK_CALL).install()->quick();
Utils::Hook(0x593CA4, ConfigStrings::CG_ParseConfigStrings, HOOK_CALL).install()->quick();
// Weapon
Utils::Hook(0x4BD52D, ConfigStrings::CL_GetWeaponConfigString, HOOK_CALL).install()->quick();
Utils::Hook(0x45D19E , ConfigStrings::SV_SetWeaponConfigString, HOOK_CALL).install()->quick();
// Cached Models
Utils::Hook(0x589908, ConfigStrings::CL_GetCachedModelConfigString, HOOK_CALL).install()->quick();
Utils::Hook(0x450A30, ConfigStrings::CL_GetCachedModelConfigString, HOOK_CALL).install()->quick();
Utils::Hook(0x4503F6, ConfigStrings::CL_GetCachedModelConfigString, HOOK_CALL).install()->quick();
Utils::Hook(0x4504A0, ConfigStrings::CL_GetCachedModelConfigString, HOOK_CALL).install()->quick();
Utils::Hook(0x450A30, ConfigStrings::CL_GetCachedModelConfigString, HOOK_CALL).install()->quick();
Utils::Hook(0x44F217, ConfigStrings::SV_GetCachedModelConfigStringConst, HOOK_CALL).install()->quick();
Utils::Hook(0X418F93, ConfigStrings::SV_GetCachedModelConfigStringConst, HOOK_CALL).install()->quick();
Utils::Hook(0x497B0A, ConfigStrings::SV_GetCachedModelConfigStringConst, HOOK_CALL).install()->quick();
Utils::Hook(0x4F4493, ConfigStrings::SV_GetCachedModelConfigStringConst, HOOK_CALL).install()->quick();
Utils::Hook(0x5FC46D, ConfigStrings::SV_GetCachedModelConfigStringConst, HOOK_JUMP).install()->quick();
Utils::Hook(0x44F282, ConfigStrings::SV_SetCachedModelConfigString, HOOK_CALL).install()->quick();
Utils::Hook::Set<DWORD>(0x44A333, sizeof(cl_gameState));
Utils::Hook::Set<DWORD>(0x5A1F56, sizeof(cl_gameState));
Utils::Hook::Set<DWORD>(0x5A2043, sizeof(cl_gameState));
Utils::Hook::Set<DWORD>(0x5A2053, sizeof(cl_gameState.stringOffsets));
Utils::Hook::Set<DWORD>(0x5A2098, sizeof(cl_gameState.stringOffsets));
Utils::Hook::Set<DWORD>(0x5AC32C, sizeof(cl_gameState.stringOffsets));
Utils::Hook::Set(0x4235AC, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x434783, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x44A339, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x44ADB7, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A1FE6, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A2048, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A205A, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A2077, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A2091, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A20D7, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A83FF, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A84B0, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5A84E5, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5AC333, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5AC44A, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5AC4F3, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x5AC57A, &cl_gameState.stringOffsets);
Utils::Hook::Set(0x4235B7, &cl_gameState.stringData);
Utils::Hook::Set(0x43478D, &cl_gameState.stringData);
Utils::Hook::Set(0x44ADBC, &cl_gameState.stringData);
Utils::Hook::Set(0x5A1FEF, &cl_gameState.stringData);
Utils::Hook::Set(0x5A20E6, &cl_gameState.stringData);
Utils::Hook::Set(0x5AC457, &cl_gameState.stringData);
Utils::Hook::Set(0x5AC502, &cl_gameState.stringData);
Utils::Hook::Set(0x5AC586, &cl_gameState.stringData);
Utils::Hook::Set(0x5A2071, &cl_gameState.dataCount);
Utils::Hook::Set(0x5A20CD, &cl_gameState.dataCount);
Utils::Hook::Set(0x5A20DC, &cl_gameState.dataCount);
Utils::Hook::Set(0x5A20F3, &cl_gameState.dataCount);
Utils::Hook::Set(0x5A2104, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC33F, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC43B, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC450, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC463, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC471, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC4C3, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC4E8, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC4F8, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC50F, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC528, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC56F, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC580, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC592, &cl_gameState.dataCount);
Utils::Hook::Set(0x5AC59F, &cl_gameState.dataCount);
}
void ConfigStrings::SV_SetConfigString(int index, const char* data, Game::ConfigString basegameLastPosition, int extendedFirstPosition)
{
if (index > basegameLastPosition)
{
// we jump straight to the reallocated part of the array
const auto relativeIndex = index - (basegameLastPosition + 1);
index = extendedFirstPosition + relativeIndex;
}
// This will go back to our reallocated game state anyway
return Game::SV_SetConfigstring(index, data);
}
void ConfigStrings::SV_SetWeaponConfigString(int index, const char* data)
{
SV_SetConfigString(index, data, Game::CS_WEAPONFILES_LAST, EXTRA_WEAPONS_FIRST);
}
void ConfigStrings::SV_SetCachedModelConfigString(int index, const char* data)
{
SV_SetConfigString(index, data, Game::CS_MODELS_LAST, EXTRA_MODELCACHE_FIRST);
}
unsigned int ConfigStrings::SV_GetConfigString(int index, Game::ConfigString basegameLastPosition, int extendedFirstPosition)
{
if (index > basegameLastPosition)
{
// It's out of range, because we're loading more weapons than the basegame has
// So we jump straight to the reallocated part of the array
const auto relativeIndex = index - (basegameLastPosition + 1);
index = extendedFirstPosition + relativeIndex;
}
// This will go back to our reallocated game state anyway
return Game::SV_GetConfigstringConst(index);
}
const char* ConfigStrings::CL_GetConfigString(int index, Game::ConfigString basegameLastPosition, int extendedFirstPosition)
{
if (index > basegameLastPosition)
{
// It's out of range, because we're loading more weapons than the basegame has
// So we jump straight to the reallocated part of the array
const auto relativeIndex = index - (basegameLastPosition + 1);
index = extendedFirstPosition + relativeIndex;
}
// This will go back to our reallocated game state anyway
return Game::CL_GetConfigString(index);
}
const char* ConfigStrings::CL_GetCachedModelConfigString(int index)
{
return CL_GetConfigString(index, Game::CS_MODELS_LAST, EXTRA_MODELCACHE_FIRST);
}
int ConfigStrings::SV_GetCachedModelConfigStringConst(int index)
{
return SV_GetConfigString(index, Game::CS_MODELS_LAST, EXTRA_MODELCACHE_FIRST);
}
const char* ConfigStrings::CL_GetWeaponConfigString(int index)
{
return CL_GetConfigString(index, Game::CS_WEAPONFILES_LAST, EXTRA_WEAPONS_FIRST);
}
int ConfigStrings::SV_GetWeaponConfigStringConst(int index)
{
return SV_GetConfigString(index, Game::CS_WEAPONFILES_LAST, EXTRA_WEAPONS_FIRST);
}
int ConfigStrings::CG_ParseExtraConfigStrings()
{
Command::ClientParams params;
if (params.size() <= 1)
return 0;
char* end;
const auto* input = params.get(1);
auto index = std::strtol(input, &end, 10);
if (input == end)
{
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "{} is not a valid input\nUsage: {} <weapon index>\n",
input, params.get(0));
return 0;
}
// If it's one of our extra data
// bypass parsing and handle it ourselves!
if (index >= BASEGAME_MAX_CONFIGSTRINGS)
{
// Handle extra weapons
if (index >= EXTRA_WEAPONS_FIRST && index <= EXTRA_WEAPONS_LAST)
{
// Recompute weapon index
index = index - EXTRA_WEAPONS_FIRST + Weapon::BASEGAME_WEAPON_LIMIT;
Game::CG_SetupWeaponConfigString(0, index);
}
// Handle extra models
else if (index >= EXTRA_MODELCACHE_FIRST && index <= EXTRA_MODELCACHE_LAST)
{
const auto name = Game::CL_GetConfigString(index);
const auto R_RegisterModel = 0x50FA00;
index = index - EXTRA_MODELCACHE_FIRST + ModelCache::BASE_GMODEL_COUNT;
const auto test = ModelCache::gameModels_reallocated[index];
ModelCache::gameModels_reallocated[index] = Utils::Hook::Call<Game::XModel*(const char*)>(R_RegisterModel)(name);
}
else
{
// Unknown for now?
// Pass it to the game
return 0;
}
// We handled it
return 1;
}
// Pass it to the game
return 0;
}
__declspec(naked) void ConfigStrings::CG_ParseConfigStrings()
{
__asm
{
push eax
pushad
call ConfigStrings::CG_ParseExtraConfigStrings
mov[esp + 20h], eax
popad
pop eax
test eax, eax
jz continueParsing
retn
continueParsing :
push 592960h
retn
}
}
int ConfigStrings::SV_ClearConfigStrings(void* dest, int value, int size)
{
memset(Utils::Hook::Get<void*>(0x405B72), value, MAX_CONFIGSTRINGS * sizeof(uint16_t));
return Utils::Hook::Call<int(void*, int, int)>(0x4C98D0)(dest, value, size); // Com_Memset
}
ConfigStrings::ConfigStrings()
{
PatchConfigStrings();
}
}