Fix crash in Dvar_EnumToString (#548)
This commit is contained in:
parent
ed3217794e
commit
314e4efd55
@ -4,6 +4,8 @@ namespace Components
|
||||
{
|
||||
const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg";
|
||||
|
||||
Dvar::Var Dvar::Name;
|
||||
|
||||
Dvar::Var::Var(const std::string& dvarName)
|
||||
{
|
||||
this->dvar_ = Game::Dvar_FindVar(dvarName.data());
|
||||
@ -11,8 +13,7 @@ namespace Components
|
||||
// If the dvar can't be found it will be registered as an empty string dvar
|
||||
if (this->dvar_ == nullptr)
|
||||
{
|
||||
this->dvar_ = const_cast<Game::dvar_t*>(Game::Dvar_SetFromStringByNameFromSource(dvarName.data(), "",
|
||||
Game::DvarSetSource::DVAR_SOURCE_INTERNAL));
|
||||
this->dvar_ = const_cast<Game::dvar_t*>(Game::Dvar_SetFromStringByNameFromSource(dvarName.data(), "", Game::DVAR_SOURCE_INTERNAL));
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,8 +27,7 @@ namespace Components
|
||||
if (this->dvar_ == nullptr)
|
||||
return "";
|
||||
|
||||
if (this->dvar_->type == Game::DVAR_TYPE_STRING
|
||||
|| this->dvar_->type == Game::DVAR_TYPE_ENUM)
|
||||
if (this->dvar_->type == Game::DVAR_TYPE_STRING || this->dvar_->type == Game::DVAR_TYPE_ENUM)
|
||||
{
|
||||
if (this->dvar_->current.string != nullptr)
|
||||
return this->dvar_->current.string;
|
||||
@ -82,8 +82,8 @@ namespace Components
|
||||
if (this->dvar_ == nullptr)
|
||||
return vector;
|
||||
|
||||
if (this->dvar_->type == Game::DVAR_TYPE_FLOAT_2 || this->dvar_->type == Game::DVAR_TYPE_FLOAT_3
|
||||
|| this->dvar_->type == Game::DVAR_TYPE_FLOAT_4)
|
||||
if (this->dvar_->type == Game::DVAR_TYPE_FLOAT_2 || this->dvar_->type == Game::DVAR_TYPE_FLOAT_3 ||
|
||||
this->dvar_->type == Game::DVAR_TYPE_FLOAT_4)
|
||||
{
|
||||
return this->dvar_->current.vector;
|
||||
}
|
||||
@ -186,37 +186,37 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, bool value, Dvar::Flag flag, const char* description)
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, bool value, Flag flag, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterBool(dvarName, value, flag.val, description);
|
||||
}
|
||||
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, const char* value, Dvar::Flag flag, const char* description)
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, const char* value, Flag flag, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterString(dvarName, value, flag.val, description);
|
||||
}
|
||||
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, int value, int min, int max, Dvar::Flag flag, const char* description)
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, int value, int min, int max, Flag flag, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterInt(dvarName, value, min, max, flag.val, description);
|
||||
}
|
||||
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, float value, float min, float max, Dvar::Flag flag, const char* description)
|
||||
template<> Dvar::Var Dvar::Register(const char* dvarName, float value, float min, float max, Flag flag, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag.val, description);
|
||||
}
|
||||
|
||||
void Dvar::ResetDvarsValue()
|
||||
{
|
||||
if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath))
|
||||
if (!Utils::IO::FileExists(ArchiveDvarPath))
|
||||
return;
|
||||
|
||||
Command::Execute("exec archivedvars.cfg", true);
|
||||
// Clean up
|
||||
Utils::IO::RemoveFile(Dvar::ArchiveDvarPath);
|
||||
Utils::IO::RemoveFile(ArchiveDvarPath);
|
||||
}
|
||||
|
||||
Game::dvar_t* Dvar::Dvar_RegisterName(const char* name, const char* /*default*/, unsigned __int16 flags, const char* description)
|
||||
Game::dvar_t* Dvar::Dvar_RegisterName(const char* name, const char* /*default*/, std::uint16_t flags, const char* description)
|
||||
{
|
||||
// Name watcher
|
||||
if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
|
||||
@ -224,7 +224,7 @@ namespace Components
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
static std::string lastValidName = "Unknown Soldier";
|
||||
auto name = Dvar::Var("name").get<std::string>();
|
||||
auto name = Name.get<std::string>();
|
||||
|
||||
// Don't perform any checks if name didn't change
|
||||
if (name == lastValidName) return;
|
||||
@ -232,8 +232,8 @@ namespace Components
|
||||
std::string saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(Utils::String::Trim(name)));
|
||||
if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{'))
|
||||
{
|
||||
Logger::Print("Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name);
|
||||
Dvar::Var("name").set(lastValidName);
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name);
|
||||
Name.set(lastValidName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -249,18 +249,19 @@ namespace Components
|
||||
{
|
||||
const char* steamName = Steam::Proxy::SteamFriends->GetPersonaName();
|
||||
|
||||
if (steamName && !std::string(steamName).empty())
|
||||
if (steamName && *steamName != '\0')
|
||||
{
|
||||
username = steamName;
|
||||
}
|
||||
}
|
||||
|
||||
return Dvar::Register<const char*>(name, username.data(), flags | Game::DVAR_ARCHIVE, description).get<Game::dvar_t*>();
|
||||
Name = Register<const char*>(name, username.data(), flags | Game::DVAR_ARCHIVE, description);
|
||||
return Name.get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string)
|
||||
{
|
||||
static const char* exceptions[] =
|
||||
static std::array<const char*, 8> exceptions =
|
||||
{
|
||||
"ui_showEndOfGame",
|
||||
"systemlink",
|
||||
@ -272,21 +273,21 @@ namespace Components
|
||||
"ui_mptype",
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < ARRAYSIZE(exceptions); ++i)
|
||||
for (const auto& entry : exceptions)
|
||||
{
|
||||
if (Utils::String::ToLower(dvarName) == Utils::String::ToLower(exceptions[i]))
|
||||
if (Utils::String::Compare(dvarName, entry))
|
||||
{
|
||||
Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DvarSetSource::DVAR_SOURCE_INTERNAL);
|
||||
Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DVAR_SOURCE_INTERNAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Dvar::SetFromStringByNameExternal(dvarName, string);
|
||||
SetFromStringByNameExternal(dvarName, string);
|
||||
}
|
||||
|
||||
void Dvar::SetFromStringByNameExternal(const char* dvarName, const char* string)
|
||||
{
|
||||
Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
|
||||
Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DVAR_SOURCE_EXTERNAL);
|
||||
}
|
||||
|
||||
bool Dvar::AreArchiveDvarsProtected()
|
||||
@ -303,23 +304,21 @@ namespace Components
|
||||
|
||||
void Dvar::SaveArchiveDvar(const Game::dvar_t* var)
|
||||
{
|
||||
if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath))
|
||||
if (!Utils::IO::FileExists(ArchiveDvarPath))
|
||||
{
|
||||
Utils::IO::WriteFile(Dvar::ArchiveDvarPath,
|
||||
"// generated by IW4x, do not modify\n");
|
||||
Utils::IO::WriteFile(ArchiveDvarPath, "// generated by IW4x, do not modify\n");
|
||||
}
|
||||
|
||||
Utils::IO::WriteFile(Dvar::ArchiveDvarPath,
|
||||
Utils::String::VA("seta %s \"%s\"\n", var->name, Game::Dvar_DisplayableValue(var)), true);
|
||||
Utils::IO::WriteFile(ArchiveDvarPath, Utils::String::VA("seta %s \"%s\"\n", var->name, Game::Dvar_DisplayableValue(var)), true);
|
||||
}
|
||||
|
||||
void Dvar::DvarSetFromStringByNameStub(const char* dvarName, const char* value)
|
||||
void Dvar::DvarSetFromStringByName_Stub(const char* dvarName, const char* value)
|
||||
{
|
||||
// Save the dvar original value if it has the archive flag
|
||||
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
||||
if (dvar != nullptr && dvar->flags & Game::DVAR_ARCHIVE)
|
||||
{
|
||||
if (Dvar::AreArchiveDvarsProtected())
|
||||
if (AreArchiveDvarsProtected())
|
||||
{
|
||||
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvarName);
|
||||
return;
|
||||
@ -328,7 +327,7 @@ namespace Components
|
||||
#ifdef DEBUG_DVARS
|
||||
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Server is overriding saved dvar '{}'\n", dvarName);
|
||||
#endif
|
||||
Dvar::SaveArchiveDvar(dvar);
|
||||
SaveArchiveDvar(dvar);
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value);
|
||||
@ -348,7 +347,7 @@ namespace Components
|
||||
pushad
|
||||
|
||||
push eax
|
||||
call Dvar::OnRegisterVariant
|
||||
call OnRegisterVariant
|
||||
add esp, 0x4
|
||||
|
||||
popad
|
||||
@ -362,28 +361,45 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
const char* Dvar::Dvar_EnumToString_Stub(const Game::dvar_t* dvar)
|
||||
{
|
||||
assert(dvar);
|
||||
assert(dvar->name);
|
||||
assert(dvar->type == Game::DVAR_TYPE_ENUM);
|
||||
assert(dvar->domain.enumeration.strings);
|
||||
assert(dvar->current.integer >= 0 && dvar->current.integer < dvar->domain.enumeration.stringCount || dvar->current.integer == 0);
|
||||
|
||||
// Fix nullptr crash
|
||||
if (!dvar || dvar->domain.enumeration.stringCount == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return dvar->domain.enumeration.strings[dvar->current.integer];
|
||||
}
|
||||
|
||||
Dvar::Dvar()
|
||||
{
|
||||
// set flags of cg_drawFPS to archive
|
||||
Utils::Hook::Or<BYTE>(0x4F8F69, Game::DVAR_ARCHIVE);
|
||||
Utils::Hook::Or<std::uint8_t>(0x4F8F69, Game::DVAR_ARCHIVE);
|
||||
|
||||
// un-cheat camera_thirdPersonCrosshairOffset and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x447B41, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x447B41, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
|
||||
// un-cheat cg_fov and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x4F8E35, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
|
||||
// un-cheat cg_fovscale and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8E68, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x4F8E68, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
|
||||
// un-cheat cg_debugInfoCornerOffset and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||
|
||||
// remove archive flags for cg_hudchatposition
|
||||
Utils::Hook::Xor<BYTE>(0x4F9992, Game::DVAR_ARCHIVE);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x4F9992, Game::DVAR_ARCHIVE);
|
||||
|
||||
// remove write protection from fs_game
|
||||
Utils::Hook::Xor<DWORD>(0x6431EA, Game::DVAR_INIT);
|
||||
Utils::Hook::Xor<std::uint32_t>(0x6431EA, Game::DVAR_INIT);
|
||||
|
||||
// set cg_fov max to 160.0
|
||||
// because that's the max on SP
|
||||
@ -395,57 +411,57 @@ namespace Components
|
||||
Utils::Hook::Set<float*>(0x408078, &volume);
|
||||
|
||||
// Uncheat ui_showList
|
||||
Utils::Hook::Xor<BYTE>(0x6310DC, Game::DVAR_CHEAT);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x6310DC, Game::DVAR_CHEAT);
|
||||
|
||||
// Uncheat ui_debugMode
|
||||
Utils::Hook::Xor<BYTE>(0x6312DE, Game::DVAR_CHEAT);
|
||||
Utils::Hook::Xor<std::uint8_t>(0x6312DE, Game::DVAR_CHEAT);
|
||||
|
||||
// Hook dvar 'name' registration
|
||||
Utils::Hook(0x40531C, Dvar::Dvar_RegisterName, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x40531C, Dvar_RegisterName, HOOK_CALL).install()->quick();
|
||||
|
||||
// un-cheat safeArea_* and add archive flags
|
||||
Utils::Hook::Xor<INT>(0x42E3F5, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_horizontal
|
||||
Utils::Hook::Xor<INT>(0x42E423, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_vertical
|
||||
Utils::Hook::Xor<BYTE>(0x42E398, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_horizontal
|
||||
Utils::Hook::Xor<BYTE>(0x42E3C4, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_vertical
|
||||
Utils::Hook::Xor<std::uint32_t>(0x42E3F5, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_horizontal
|
||||
Utils::Hook::Xor<std::uint32_t>(0x42E423, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_vertical
|
||||
Utils::Hook::Xor<std::uint8_t>(0x42E398, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_horizontal
|
||||
Utils::Hook::Xor<std::uint8_t>(0x42E3C4, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_vertical
|
||||
|
||||
// Don't allow setting cheat protected dvars via menus
|
||||
Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63CA96, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63CDB5, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x635E47, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63C897, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63CA96, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63CDB5, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x635E47, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
|
||||
// Script_SetDvar
|
||||
Utils::Hook(0x63444C, Dvar::SetFromStringByNameSafeExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63444C, SetFromStringByNameSafeExternal, HOOK_CALL).install()->quick();
|
||||
|
||||
// Slider
|
||||
Utils::Hook(0x636159, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636189, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x6364EA, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636159, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636189, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x6364EA, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x636207, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636608, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636695, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
|
||||
// Entirely block setting cheat dvars internally without sv_cheats
|
||||
//Utils::Hook(0x4F52EC, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636207, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636608, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x636695, SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
|
||||
// Hook Dvar_SetFromStringByName inside CG_SetClientDvarFromServer so we can reset dvars when the player leaves the server
|
||||
Utils::Hook(0x59386A, Dvar::DvarSetFromStringByNameStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x59386A, DvarSetFromStringByName_Stub, HOOK_CALL).install()->quick();
|
||||
|
||||
// If the game closed abruptly, the dvars would not have been restored
|
||||
Scheduler::Once(Dvar::ResetDvarsValue, Scheduler::Pipeline::MAIN);
|
||||
Scheduler::Once(ResetDvarsValue, Scheduler::Pipeline::MAIN);
|
||||
|
||||
// Reset archive dvars when client leaves a server
|
||||
Events::OnSteamDisconnect(Dvar::ResetDvarsValue);
|
||||
Events::OnSteamDisconnect(ResetDvarsValue);
|
||||
|
||||
// For debugging
|
||||
Utils::Hook(0x6483FA, Dvar::Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x648438, Dvar::Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x6483FA, Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x648438, Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Fix crash
|
||||
Utils::Hook(0x4B7120, Dvar_EnumToString_Stub, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
|
||||
Dvar::~Dvar()
|
||||
{
|
||||
Utils::IO::RemoveFile(Dvar::ArchiveDvarPath);
|
||||
Utils::IO::RemoveFile(ArchiveDvarPath);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
Flag(Game::DvarFlags flag) : val(flag) {}
|
||||
Flag(unsigned __int16 flag) : Flag(static_cast<Game::DvarFlags>(flag)) {}
|
||||
Flag(std::uint16_t flag) : Flag(static_cast<Game::DvarFlags>(flag)) {}
|
||||
|
||||
Game::DvarFlags val;
|
||||
};
|
||||
@ -51,17 +51,20 @@ namespace Components
|
||||
|
||||
private:
|
||||
static const char* ArchiveDvarPath;
|
||||
static Var Name;
|
||||
|
||||
static Game::dvar_t* Dvar_RegisterName(const char* name, const char* defaultVal, unsigned __int16 flags, const char* description);
|
||||
static Game::dvar_t* Dvar_RegisterName(const char* name, const char* defaultVal, std::uint16_t flags, const char* description);
|
||||
|
||||
static void SetFromStringByNameExternal(const char* dvar, const char* value);
|
||||
static void SetFromStringByNameSafeExternal(const char* dvar, const char* value);
|
||||
static void SetFromStringByNameExternal(const char* dvarName, const char* string);
|
||||
static void SetFromStringByNameSafeExternal(const char* dvarName, const char* string);
|
||||
|
||||
static bool AreArchiveDvarsProtected();
|
||||
static void SaveArchiveDvar(const Game::dvar_t* var);
|
||||
static void DvarSetFromStringByNameStub(const char* dvarName, const char* value);
|
||||
static void DvarSetFromStringByName_Stub(const char* dvarName, const char* value);
|
||||
|
||||
static void OnRegisterVariant(Game::dvar_t* dvar);
|
||||
static void Dvar_RegisterVariant_Stub();
|
||||
|
||||
static const char* Dvar_EnumToString_Stub(const Game::dvar_t* dvar);
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user