NorthstarLauncher/NorthstarDLL/convar.cpp

500 lines
14 KiB
C++

#include "pch.h"
#include "bits.h"
#include "cvar.h"
#include "convar.h"
#include "sourceinterface.h"
#include <float.h>
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;
//-----------------------------------------------------------------------------
// Purpose: ConVar interface initialization
//-----------------------------------------------------------------------------
ON_DLL_LOAD("engine.dll", ConVar, (CModule module))
{
conVarMalloc = module.Offset(0x415C20).As<ConVarMallocType>();
conVarRegister = module.Offset(0x417230).As<ConVarRegisterType>();
g_pConVar_Vtable = module.Offset(0x67FD28);
g_pIConVar_Vtable = module.Offset(0x67FDC8);
R2::g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
R2::g_pCVar = *R2::g_pCVarInterface;
}
//-----------------------------------------------------------------------------
// 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);
}
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
ConVar::ConVar(
const char* pszName,
const char* pszDefaultValue,
int nFlags,
const char* pszHelpString,
bool bMin,
float fMin,
bool bMax,
float fMax,
FnChangeCallback_t 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);
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
ConVar::~ConVar(void)
{
if (m_Value.m_pszString)
delete[] m_Value.m_pszString;
}
//-----------------------------------------------------------------------------
// 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)
{
if (nValue == m_Value.m_nValue)
{
return;
}
float flValue = (float)nValue;
// Check bounds.
if (ClampValue(flValue))
{
nValue = (int)(flValue);
}
// 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);
}
}
//-----------------------------------------------------------------------------
// Purpose: sets the ConVar float value.
// Input : flValue -
//-----------------------------------------------------------------------------
void ConVar::SetValue(float flValue)
{
if (flValue == m_Value.m_fValue)
{
return;
}
// 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);
}
}
//-----------------------------------------------------------------------------
// 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 {};
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 : nFlags
// Output : False if change is permitted, true if not.
//-----------------------------------------------------------------------------
bool ConVar::IsFlagSet(int nFlags) const
{
return m_ConCommandBase.m_nFlags & nFlags;
}
//-----------------------------------------------------------------------------
// Purpose: Check whether to clamp and then perform clamp.
// Input : flValue -
// Output : Returns true if value changed.
//-----------------------------------------------------------------------------
bool ConVar::ClampValue(float& flValue)
{
if (m_bHasMin && (flValue < m_fMinVal))
{
flValue = m_fMinVal;
return true;
}
if (m_bHasMax && (flValue > m_fMaxVal))
{
flValue = m_fMaxVal;
return true;
}
return false;
}