aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDedicatedTest/convar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDedicatedTest/convar.cpp')
-rw-r--r--NorthstarDedicatedTest/convar.cpp474
1 files changed, 448 insertions, 26 deletions
diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp
index 4157b42f..0f4eeba7 100644
--- a/NorthstarDedicatedTest/convar.cpp
+++ b/NorthstarDedicatedTest/convar.cpp
@@ -1,56 +1,478 @@
#include "pch.h"
+#include "bits.h"
+#include "cvar.h"
#include "convar.h"
#include "hookutils.h"
#include "gameutils.h"
#include "sourceinterface.h"
-#include <set>
// should this be in modmanager?
std::unordered_map<std::string, ConVar*>
g_CustomConvars; // this is used in modloading code to determine whether we've registered a mod convar already
-SourceInterface<ICvar>* g_pCvar;
-typedef void (*ConVarConstructorType)(ConVar* newVar, const char* name, const char* defaultValue, int flags, const char* helpString);
-ConVarConstructorType conVarConstructor;
+typedef void (*ConVarRegisterType)(
+ ConVar* pConVar, const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin,
+ bool bMax, float fMax, void* pCallback);
+ConVarRegisterType conVarRegister;
+
+typedef void (*ConVarMallocType)(void* pConVarMaloc, int a2, int a3);
+ConVarMallocType conVarMalloc;
+
+void* g_pConVar_Vtable = nullptr;
+void* g_pIConVar_Vtable = nullptr;
+
typedef bool (*CvarIsFlagSetType)(ConVar* self, int flags);
CvarIsFlagSetType CvarIsFlagSet;
-ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, const char* helpString)
+//-----------------------------------------------------------------------------
+// Purpose: ConVar interface initialization
+//-----------------------------------------------------------------------------
+void InitialiseConVars(HMODULE baseAddress)
+{
+ conVarMalloc = (ConVarMallocType)((char*)baseAddress + 0x415C20);
+ conVarRegister = (ConVarRegisterType)((char*)baseAddress + 0x417230);
+
+ g_pConVar_Vtable = (char*)baseAddress + 0x67FD28;
+ g_pIConVar_Vtable = (char*)baseAddress + 0x67FDC8;
+
+ g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
+ g_pCVar = *g_pCVarInterface;
+
+ HookEnabler hook;
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &ConVar::IsFlagSet, reinterpret_cast<LPVOID*>(&CvarIsFlagSet));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+ConVar::ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString)
+{
+ spdlog::info("Registering Convar {}", pszName);
+
+ this->m_ConCommandBase.m_pConCommandBaseVTable = g_pConVar_Vtable;
+ this->m_ConCommandBase.s_pConCommandBases = (ConCommandBase*)g_pIConVar_Vtable;
+
+ conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
+ conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, 0, 0, 0, 0, 0);
+
+ g_CustomConvars.emplace(pszName, this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+ConVar::ConVar(
+ const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin, bool bMax, float fMax,
+ void* pCallback)
+{
+ spdlog::info("Registering Convar {}", pszName);
+
+ this->m_ConCommandBase.m_pConCommandBaseVTable = g_pConVar_Vtable;
+ this->m_ConCommandBase.s_pConCommandBases = (ConCommandBase*)g_pIConVar_Vtable;
+
+ conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
+ conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, bMin, fMin, bMax, fMax, pCallback);
+
+ g_CustomConvars.emplace(pszName, this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: destructor
+//-----------------------------------------------------------------------------
+ConVar::~ConVar(void)
+{
+ if (m_Value.m_pszString)
+ {
+ delete[] m_Value.m_pszString;
+ m_Value.m_pszString = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the base ConVar name.
+// Output : const char*
+//-----------------------------------------------------------------------------
+const char* ConVar::GetBaseName(void) const { return m_ConCommandBase.m_pszName; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ConVar help text.
+// Output : const char*
+//-----------------------------------------------------------------------------
+const char* ConVar::GetHelpText(void) const { return m_ConCommandBase.m_pszHelpString; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Add's flags to ConVar.
+// Input : nFlags -
+//-----------------------------------------------------------------------------
+void ConVar::AddFlags(int nFlags) { m_ConCommandBase.m_nFlags |= nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes flags from ConVar.
+// Input : nFlags -
+//-----------------------------------------------------------------------------
+void ConVar::RemoveFlags(int nFlags) { m_ConCommandBase.m_nFlags &= ~nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a boolean.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::GetBool(void) const { return !!GetInt(); }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a float.
+// Output : float
+//-----------------------------------------------------------------------------
+float ConVar::GetFloat(void) const { return m_Value.m_fValue; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as an integer.
+// Output : int
+//-----------------------------------------------------------------------------
+int ConVar::GetInt(void) const { return m_Value.m_nValue; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a color.
+// Output : Color
+//-----------------------------------------------------------------------------
+Color ConVar::GetColor(void) const
+{
+ unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
+ return Color(pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3]);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a string.
+// Output : const char *
+//-----------------------------------------------------------------------------
+const char* ConVar::GetString(void) const
+{
+ if (m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING)
+ {
+ return "FCVAR_NEVER_AS_STRING";
+ }
+
+ char const* str = m_Value.m_pszString;
+ return str ? str : "";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMinVal -
+// Output : true if there is a min set.
+//-----------------------------------------------------------------------------
+bool ConVar::GetMin(float& flMinVal) const
+{
+ flMinVal = m_fMinVal;
+ return m_bHasMin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMaxVal -
+// Output : true if there is a max set.
+//-----------------------------------------------------------------------------
+bool ConVar::GetMax(float& flMaxVal) const
+{
+ flMaxVal = m_fMaxVal;
+ return m_bHasMax;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the min value.
+// Output : float
+//-----------------------------------------------------------------------------
+float ConVar::GetMinValue(void) const { return m_fMinVal; }
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the max value.
+// Output : float
+//-----------------------------------------------------------------------------
+float ConVar::GetMaxValue(void) const
+{
+ return m_fMaxVal;
+ ;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: checks if ConVar has min value.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::HasMin(void) const { return m_bHasMin; }
+
+//-----------------------------------------------------------------------------
+// Purpose: checks if ConVar has max value.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::HasMax(void) const { return m_bHasMax; }
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar int value.
+// Input : nValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(int nValue)
{
- spdlog::info("Registering Convar {}", name);
+ if (nValue == m_Value.m_nValue)
+ {
+ return;
+ }
+
+ // Only valid for root ConVars.
+ assert(m_pParent == this);
- // no need to free this ever really, it should exist as long as game does
- ConVar* newVar = new ConVar;
- conVarConstructor(newVar, name, defaultValue, flags, helpString);
+ float flValue = (float)nValue;
- g_CustomConvars.emplace(name, newVar);
+ // Check bounds.
+ if (ClampValue(flValue))
+ {
+ nValue = (int)(flValue);
+ }
- return newVar;
+ // Redetermine value.
+ float flOldValue = m_Value.m_fValue;
+ m_Value.m_fValue = flValue;
+ m_Value.m_nValue = nValue;
+
+ if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
+ {
+ char szTempValue[32];
+ snprintf(szTempValue, sizeof(szTempValue), "%d", m_Value.m_nValue);
+ ChangeStringValue(szTempValue, flOldValue);
+ }
}
-ConVar* FindConVar(const char* name)
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar float value.
+// Input : flValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(float flValue)
{
- ICvar* icvar =
- *g_pCvar; // hellish call because i couldn't get icvar vtable stuff in convar.h to get the right offset for whatever reason
- typedef ConVar* (*FindConVarType)(ICvar * self, const char* varName);
- FindConVarType FindConVarInternal = *(FindConVarType*)((*(char**)icvar) + 128);
- return FindConVarInternal(icvar, name);
+ if (flValue == m_Value.m_fValue)
+ {
+ return;
+ }
+
+ // Only valid for root ConVars.
+ assert(m_pParent == this);
+
+ // Check bounds.
+ ClampValue(flValue);
+
+ // Redetermine value.
+ float flOldValue = m_Value.m_fValue;
+ m_Value.m_fValue = flValue;
+ m_Value.m_nValue = (int)m_Value.m_fValue;
+
+ if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
+ {
+ char szTempValue[32];
+ snprintf(szTempValue, sizeof(szTempValue), "%f", m_Value.m_fValue);
+ ChangeStringValue(szTempValue, flOldValue);
+ }
}
-bool CvarIsFlagSetHook(ConVar* self, int flags)
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar string value.
+// Input : *szValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(const char* pszValue)
+{
+ if (strcmp(this->m_Value.m_pszString, pszValue) == 0)
+ {
+ return;
+ }
+ this->m_Value.m_pszString = pszValue;
+
+ char szTempValue[32]{};
+ const char* pszNewValue{};
+
+ // Only valid for root convars.
+ assert(m_pParent == this);
+
+ float flOldValue = m_Value.m_fValue;
+ pszNewValue = (char*)pszValue;
+ if (!pszNewValue)
+ {
+ pszNewValue = "";
+ }
+
+ if (!SetColorFromString(pszValue))
+ {
+ // Not a color, do the standard thing
+ float flNewValue = (float)atof(pszValue);
+ if (!IsFinite(flNewValue))
+ {
+ spdlog::warn("Warning: ConVar '{}' = '{}' is infinite, clamping value.\n", GetBaseName(), pszValue);
+ flNewValue = FLT_MAX;
+ }
+
+ if (ClampValue(flNewValue))
+ {
+ snprintf(szTempValue, sizeof(szTempValue), "%f", flNewValue);
+ pszNewValue = szTempValue;
+ }
+
+ // Redetermine value
+ m_Value.m_fValue = flNewValue;
+ m_Value.m_nValue = (int)(m_Value.m_fValue);
+ }
+
+ if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
+ {
+ ChangeStringValue(pszNewValue, flOldValue);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar color value.
+// Input : clValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(Color clValue)
+{
+ std::string svResult = "";
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (!(clValue.GetValue(i) == 0 && svResult.size() == 0))
+ {
+ svResult += std::to_string(clValue.GetValue(i));
+ svResult.append(" ");
+ }
+ }
+
+ this->m_Value.m_pszString = svResult.c_str();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes the ConVar string value.
+// Input : *pszTempVal - flOldValue
+//-----------------------------------------------------------------------------
+void ConVar::ChangeStringValue(const char* pszTempVal, float flOldValue)
+{
+ assert(!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING));
+
+ char* pszOldValue = (char*)_malloca(m_Value.m_iStringLength);
+ if (pszOldValue != NULL)
+ {
+ memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength);
+ }
+
+ if (pszTempVal)
+ {
+ int len = strlen(pszTempVal) + 1;
+
+ if (len > m_Value.m_iStringLength)
+ {
+ if (m_Value.m_pszString)
+ {
+ // !TODO: Causes issues in tier0.dll, but doesn't in apex.
+ // Not a big issue since we are creating a new string below
+ // anyways to prevent buffer overflow if string is longer
+ // then the old string.
+ // delete[] m_Value.m_pszString;
+ }
+
+ m_Value.m_pszString = new char[len];
+ m_Value.m_iStringLength = len;
+ }
+
+ memcpy((char*)m_Value.m_pszString, pszTempVal, len);
+ }
+ else
+ {
+ m_Value.m_pszString = NULL;
+ }
+
+ pszOldValue = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar color value from string.
+// Input : *pszValue -
+//-----------------------------------------------------------------------------
+bool ConVar::SetColorFromString(const char* pszValue)
+{
+ bool bColor = false;
+
+ // Try pulling RGBA color values out of the string.
+ int nRGBA[4]{};
+ int nParamsRead = sscanf_s(pszValue, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]));
+
+ if (nParamsRead >= 3)
+ {
+ // This is probably a color!
+ if (nParamsRead == 3)
+ {
+ // Assume they wanted full alpha.
+ nRGBA[3] = 255;
+ }
+
+ if (nRGBA[0] >= 0 && nRGBA[0] <= 255 && nRGBA[1] >= 0 && nRGBA[1] <= 255 && nRGBA[2] >= 0 && nRGBA[2] <= 255 && nRGBA[3] >= 0 &&
+ nRGBA[3] <= 255)
+ {
+ // printf("*** WOW! Found a color!! ***\n");
+
+ // This is definitely a color!
+ bColor = true;
+
+ // Stuff all the values into each byte of our int.
+ unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
+ pColorElement[0] = nRGBA[0];
+ pColorElement[1] = nRGBA[1];
+ pColorElement[2] = nRGBA[2];
+ pColorElement[3] = nRGBA[3];
+
+ // Copy that value into our float.
+ m_Value.m_fValue = (float)(m_Value.m_nValue);
+ }
+ }
+
+ return bColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if ConVar is registered.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::IsRegistered(void) const { return m_ConCommandBase.m_bRegistered; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if this is a command
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::IsCommand(void) const { return false; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Test each ConVar query before setting the value.
+// Input : *pConVar - nFlags
+// Output : False if change is permitted, true if not.
+//-----------------------------------------------------------------------------
+bool ConVar::IsFlagSet(ConVar* pConVar, int nFlags)
{
// unrestrict FCVAR_DEVELOPMENTONLY and FCVAR_HIDDEN
- if (self && (flags == FCVAR_DEVELOPMENTONLY || flags == FCVAR_HIDDEN))
+ if (pConVar && (nFlags == FCVAR_DEVELOPMENTONLY || nFlags == FCVAR_HIDDEN))
return false;
- return CvarIsFlagSet(self, flags);
+ return CvarIsFlagSet(pConVar, nFlags);
}
-void InitialiseConVars(HMODULE baseAddress)
+//-----------------------------------------------------------------------------
+// Purpose: Check whether to clamp and then perform clamp.
+// Input : flValue -
+// Output : Returns true if value changed.
+//-----------------------------------------------------------------------------
+bool ConVar::ClampValue(float& flValue)
{
- conVarConstructor = (ConVarConstructorType)((char*)baseAddress + 0x416200);
- g_pCvar = new SourceInterface<ICvar>("vstdlib.dll", "VEngineCvar007");
+ if (m_bHasMin && (flValue < m_fMinVal))
+ {
+ flValue = m_fMinVal;
+ return true;
+ }
- HookEnabler hook;
- ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &CvarIsFlagSetHook, reinterpret_cast<LPVOID*>(&CvarIsFlagSet));
-} \ No newline at end of file
+ if (m_bHasMax && (flValue > m_fMaxVal))
+ {
+ flValue = m_fMaxVal;
+ return true;
+ }
+
+ return false;
+}