Custom vision file parser (#249)

This commit is contained in:
Edo 2022-06-30 21:37:47 +02:00 committed by GitHub
parent 060abd6251
commit 769ff3fd5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 207 additions and 13 deletions

View File

@ -31,8 +31,8 @@ namespace Components
Loader::Register(new Flags());
Loader::Register(new Singleton());
Loader::Register(new Exception()); // install our exception handler as early as posssible to get better debug dumps from startup crashes
// Install our exception handler as early as posssible to get better debug dumps from startup crashes
Loader::Register(new Exception());
Loader::Register(new Auth());
Loader::Register(new Bans());
Loader::Register(new Bots());
@ -104,6 +104,7 @@ namespace Components
Loader::Register(new Movement());
Loader::Register(new Elevators());
Loader::Register(new ClientCommand());
Loader::Register(new VisionFile());
Loader::Register(new ScriptExtension());
Loader::Register(new Branding());
Loader::Register(new Debug());

View File

@ -134,6 +134,7 @@ namespace Components
#include "Modules/Movement.hpp"
#include "Modules/Elevators.hpp"
#include "Modules/ClientCommand.hpp"
#include "Modules/VisionFile.hpp"
#include "Modules/Gamepad.hpp"
#include "Modules/ScriptExtension.hpp"
#include "Modules/Branding.hpp"

View File

@ -0,0 +1,111 @@
#include <STDInclude.hpp>
namespace Components
{
const char* VisionFile::DvarExceptions[] =
{
"r_pretess",
};
void VisionFile::ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName)
{
for (std::size_t i = 0; i < std::extent_v<decltype(DvarExceptions)>; ++i)
{
if (dvarName == DvarExceptions[i])
{
const auto* dvar = Game::Dvar_FindVar(dvarName.data());
assert(dvar != nullptr);
const auto* currentVal = Game::Dvar_DisplayableValue(dvar);
const auto* parsedValue = Game::Com_ParseOnLine(&buffer);
if (std::strcmp(parsedValue, currentVal) == 0)
{
// The dvar is already set to the value we want
return;
}
Game::Dvar_SetFromStringFromSource(dvar, parsedValue, Game::DvarSetSource::DVAR_SOURCE_INTERNAL);
Logger::Print("Overriding '{}' from '{}'\n", dvarName, fileName);
// Successfully found and tried to apply the string value to the dvar
return;
}
}
Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_SYSTEM,
"WARNING: unknown dvar \'%s\' in file \'%s\'\n", dvarName.data(), fileName.data());
}
// Gets the dvar name and value and attemps to apply it to the vision settings
void VisionFile::ApplyValueToSettings(const std::string& key, const char* buffer,
const std::string& fileName, Game::visionSetVars_t* settings)
{
for (std::size_t i = 0; i < 21; ++i)
{
// Must be case insensitive comparison
if (key == Utils::String::ToLower(Game::visionDefFields[i].name))
{
auto* const dvarValue = Game::Com_ParseOnLine(&buffer);
if (!Game::ApplyTokenToField(i, dvarValue, settings))
{
Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_SYSTEM,
"WARNING: malformed dvar \'%s\' in file \'%s\'\n", dvarValue, fileName.data());
// Failed to apply the value. Check that sscanf can actually parse the value
return;
}
// Successfully found and applied the value to the settings
return;
}
}
// Dvar not found in visionDefFields, let's try to see if it's a 'patched' dvar
ApplyExemptDvar(key, buffer, fileName);
}
bool VisionFile::LoadVisionSettingsFromBuffer(const char* buffer, const char* fileName, Game::visionSetVars_t* settings)
{
assert(settings != nullptr);
assert(fileName != nullptr);
Game::Com_BeginParseSession(fileName);
// Will split the buffer into tokens using the following delimiters: space, newlines (more?)
for (auto i = Game::Com_Parse(&buffer); *i != '\0'; i = Game::Com_Parse(&buffer))
{
// Converting 'key' to lower case as it will be needed later
ApplyValueToSettings(Utils::String::ToLower(i), buffer, fileName, settings);
Game::Com_SkipRestOfLine(&buffer);
}
Game::Com_EndParseSession();
return true;
}
__declspec(naked) bool VisionFile::LoadVisionSettingsFromBuffer_Stub()
{
// No need for push/pop ad guards, I have checked :)
__asm
{
push [esp + 0x8] // settings
push ebx // filename
push [esp + 0xC] // buffer
call VisionFile::LoadVisionSettingsFromBuffer
add esp, 0xC
ret
}
}
VisionFile::VisionFile()
{
AssertSize(Game::visField_t, 12);
// Place hook in LoadVisionFile function
Utils::Hook(0x59A98A, LoadVisionSettingsFromBuffer_Stub, HOOK_CALL).install()->quick();
}
}

View File

@ -0,0 +1,19 @@
#pragma once
namespace Components
{
class VisionFile : public Component
{
public:
VisionFile();
private:
static const char* DvarExceptions[];
static void ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName);
static void ApplyValueToSettings(const std::string& dvarName, const char* buffer, const std::string& fileName, Game::visionSetVars_t* settings);
static bool LoadVisionSettingsFromBuffer(const char* buffer, const char* fileName, Game::visionSetVars_t* settings);
static bool LoadVisionSettingsFromBuffer_Stub();
};
}

View File

@ -95,7 +95,7 @@ namespace Components
void ZoneBuilder::Zone::Zone::build()
{
if(!this->dataMap.isValid())
if (!this->dataMap.isValid())
{
Logger::Print("Unable to load CSV for '{}'!\n", this->zoneName);
return;
@ -111,7 +111,7 @@ namespace Components
Logger::Print("Saving...\n");
this->saveData();
if(this->buffer.hasBlock())
if (this->buffer.hasBlock())
{
Logger::Error(Game::ERR_FATAL, "Non-popped blocks left!\n");
}
@ -267,7 +267,7 @@ namespace Components
const char* assetName = Game::DB_GetXAssetName(asset);
if (assetName[0] == ',') ++assetName;
if(this->getAssetName(type, assetName) == name)
if (this->getAssetName(type, assetName) == name)
{
return i;
}
@ -336,7 +336,7 @@ namespace Components
if (assetIndex == -1) // nested asset
{
// already written. find alias and store in ptr
if(this->hasAlias(asset))
if (this->hasAlias(asset))
{
header.data = reinterpret_cast<void*>(this->getAlias(asset));
}
@ -1054,7 +1054,7 @@ namespace Components
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader, const std::string&, bool* restrict)
{
//if (*static_cast<int*>(Game::DB_XAssetPool[type].data) == 0)
if(Game::g_poolSize[type] == 0)
if (Game::g_poolSize[type] == 0)
{
*restrict = true;
}
@ -1070,9 +1070,9 @@ namespace Components
{
int result = Utils::Hook::Call<int(Game::dvar_t*, Game::DvarValue)>(0x642FC0)(dvar, value);
if(result)
if (result)
{
if(std::string(value.string) != dvar->current.string)
if (std::string(value.string) != dvar->current.string)
{
dvar->current.string = value.string;
Game::FS_Restart(0, 0);
@ -1517,7 +1517,7 @@ namespace Components
{
*i = Utils::String::VA("images/%s", i->data());
if(FileSystem::File(*i).exists())
if (FileSystem::File(*i).exists())
{
i = images.erase(i);
continue;
@ -1552,7 +1552,7 @@ namespace Components
unsigned int integer = 0x80000000;
Utils::RotLeft(integer, 1);
if(integer != 1)
if (integer != 1)
{
printf("Error\n");
printf("Bit shifting failed: %X\n", integer);

View File

@ -78,6 +78,8 @@ namespace Game
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830);
Com_EndParseSession_t Com_EndParseSession = Com_EndParseSession_t(0x4B80B0);
Com_BeginParseSession_t Com_BeginParseSession = Com_BeginParseSession_t(0x4AAB80);
Com_ParseOnLine_t Com_ParseOnLine = Com_ParseOnLine_t(0x4C0350);
Com_SkipRestOfLine_t Com_SkipRestOfLine = Com_SkipRestOfLine_t(0x4B8300);
Com_SetSpaceDelimited_t Com_SetSpaceDelimited = Com_SetSpaceDelimited_t(0x4FC710);
Com_Parse_t Com_Parse = Com_Parse_t(0x474D60);
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
@ -128,6 +130,7 @@ namespace Game
Dvar_InfoString_Big_t Dvar_InfoString_Big = Dvar_InfoString_Big_t(0x4D98A0);
Dvar_SetCommand_t Dvar_SetCommand = Dvar_SetCommand_t(0x4EE430);
Dvar_DisplayableValue_t Dvar_DisplayableValue = Dvar_DisplayableValue_t(0x4B5530);
Dvar_Reset_t Dvar_Reset = Dvar_Reset_t(0x4FEFD0);
Encode_Init_t Encode_Init = Encode_Init_t(0x462AB0);
@ -535,6 +538,8 @@ namespace Game
FxElemField* s_elemFields = reinterpret_cast<FxElemField*>(0x73B848);
visField_t* visionDefFields = reinterpret_cast<visField_t*>(0x7982F0); // Count 21
infoParm_t* infoParams = reinterpret_cast<infoParm_t*>(0x79D260); // Count 0x1E
XZone* g_zones = reinterpret_cast<XZone*>(0x14C0F80);
@ -1639,8 +1644,8 @@ namespace Game
{
__asm
{
mov eax,[esp+0x4]
mov ebx,0x569950
mov eax, [esp+0x4]
mov ebx, 0x569950
call ebx
retn
}
@ -1680,6 +1685,48 @@ namespace Game
}
}
constexpr auto Dvar_SetFromStringFromSource_Func = 0x648580;
__declspec(naked) void Dvar_SetFromStringFromSource(const dvar_t* /*dvar*/, const char* /*string*/, DvarSetSource /*source*/)
{
__asm
{
pushad
mov esi, [esp + 0x20 + 0x4] // dvar
mov eax, [esp + 0x20 + 0x8] // string
push [esp + 0x20 + 0xC] // source
call Dvar_SetFromStringFromSource_Func
add esp, 0x4
popad
ret
}
}
constexpr auto ApplyTokenToField_Func = 0x59A760;
__declspec(naked) bool ApplyTokenToField(unsigned int /*fieldNum*/, const char* /*token*/, visionSetVars_t* /*settings*/)
{
__asm
{
push eax
pushad
mov eax, [esp + 0x24 + 0x4] // fieldNum
mov ecx, [esp + 0x24 + 0x8] // token
push [esp + 0x24 + 0xC] // settings
call ApplyTokenToField_Func
add esp, 0x4
movzx eax, al // Zero extend eax
mov [esp + 0x20], eax
popad
pop eax
ret
}
}
constexpr auto SV_BotUserMove_Addr = 0x626E50;
__declspec(naked) void SV_BotUserMove(client_t* /*client*/)
{

View File

@ -169,6 +169,12 @@ namespace Game
typedef void(__cdecl * Com_BeginParseSession_t)(const char* filename);
extern Com_BeginParseSession_t Com_BeginParseSession;
typedef char*(__cdecl * Com_ParseOnLine_t)(const char** data_p);
extern Com_ParseOnLine_t Com_ParseOnLine;
typedef void(__cdecl * Com_SkipRestOfLine_t)(const char** data);
extern Com_SkipRestOfLine_t Com_SkipRestOfLine;
typedef void(__cdecl * Com_SetSpaceDelimited_t)(int);
extern Com_SetSpaceDelimited_t Com_SetSpaceDelimited;
@ -325,6 +331,9 @@ namespace Game
typedef const char*(__cdecl * Dvar_DisplayableValue_t)(const dvar_t* dvar);
extern Dvar_DisplayableValue_t Dvar_DisplayableValue;
typedef void(__cdecl * Dvar_Reset_t)(const dvar_t* dvar, DvarSetSource setSource);
extern Dvar_Reset_t Dvar_Reset;
typedef bool(__cdecl * Encode_Init_t)(const char* );
extern Encode_Init_t Encode_Init;
@ -1177,6 +1186,8 @@ namespace Game
extern FxElemField* s_elemFields;
extern visField_t* visionDefFields;
extern infoParm_t* infoParams;
extern XZone* g_zones;
@ -1339,4 +1350,7 @@ namespace Game
void AimAssist_UpdateAdsLerp(const AimInput* input);
void Dvar_SetVariant(dvar_t* var, DvarValue value, DvarSetSource source);
void Dvar_SetFromStringFromSource(const dvar_t* dvar, const char* string, DvarSetSource source);
bool ApplyTokenToField(unsigned int fieldNum, const char* token, visionSetVars_t* settings);
}

View File

@ -1014,6 +1014,7 @@ namespace Game
{
const char* name;
int offset;
// 0 is for int, 1 is for float, otherwise it's a vector
int fieldType;
};