#include "console/CVar.hpp" #include "console/Command.hpp" #include "console/Types.hpp" #include "console/Line.hpp" #include "util/SFile.hpp" #include #include const char* s_filename = nullptr; bool CVar::m_needsSave; TSHashTable CVar::s_registeredCVars; CVar* CVar::Lookup(const char* name) { return name ? CVar::s_registeredCVars.Ptr(name) : nullptr; } CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const char* value, bool (*fcn)(CVar*, const char*, const char*, void*), uint32_t category, bool a7, void* arg, bool a9) { CVar* var = CVar::s_registeredCVars.Ptr(name); if (var) { bool setReset = var->m_resetValue.GetString() == nullptr; bool setDefault = var->m_defaultValue.GetString() == nullptr; var->m_flags |= (var->m_flags & 0xFFFFFFCF); var->m_callback = fcn; var->m_arg = arg; bool setValue = false; if (fcn && !fcn(var, var->GetString(), var->GetString(), arg)) { setValue = true; } var->Set(value, setValue, setReset, setDefault, false); if (!a7) { var->m_flags |= 0x80000000; } if (a9 && var->m_flags) { var->m_flags |= 0x80; } } else { var = CVar::s_registeredCVars.New(name, 0, 0); var->m_stringValue.Copy(nullptr); var->m_floatValue = 0.0f; var->m_intValue = 0; var->m_modified = 0; var->m_category = category; var->m_defaultValue.Copy(nullptr); var->m_resetValue.Copy(nullptr); var->m_latchedValue.Copy(nullptr); var->m_callback = fcn; var->m_flags = 0; var->m_arg = arg; var->m_help.Copy(help); if (a7) { var->Set(value, true, true, false, false); } else { var->Set(value, true, false, true, false); } var->m_flags = flags | 0x1; if (!a7) { var->m_flags |= 0x8000000; } if (a9 && var->m_flags) { var->m_flags |= 0x80; } ConsoleCommandRegister(name, CvarCommandHandler, CATEGORY(category), help); } return var; } CVar::CVar() : TSHashObject() { // TODO } int32_t CVar::GetInt() { return this->m_intValue; } const char* CVar::GetString() { return this->m_stringValue.GetString(); } void CVar::InternalSet(const char* value, bool setValue, bool setReset, bool setDefault, bool a6) { if (this->m_flags & 0x4 || !value) { return; } bool modified = false; const char* existingValue = this->m_stringValue.GetString(); if (setValue && (!existingValue || SStrCmpI(value, existingValue, 0x7FFFFFFF))) { modified = true; this->m_stringValue.Copy(value); this->m_intValue = SStrToInt(value); this->m_floatValue = SStrToFloat(value); } if (setReset && !this->m_resetValue.GetString()) { modified = true; this->m_resetValue.Copy(value); } if (setDefault && !this->m_defaultValue.GetString()) { this->m_defaultValue.Copy(value); } else if (!modified) { return; } if (a6) { CVar::m_needsSave = 1; } } bool CVar::Set(const char* value, bool setValue, bool setReset, bool setDefault, bool a6) { if (setValue) { if (this->m_callback) { // TODO // sub_86B5A0(this->m_callback); if (!this->m_callback(this, this->m_stringValue.GetString(), value, this->m_arg)) { return true; } } this->m_modified++; if (this->m_flags & 0x2) { this->m_latchedValue.Copy(value); CVar::m_needsSave = 1; return true; } } this->InternalSet(value, setValue, setReset, setDefault, a6); return true; } bool CVar::Reset() { auto value = this->m_resetValue; if (value.GetString() == nullptr) { value = this->m_defaultValue; } this->InternalSet(value.GetString(), true, false, false, true); return true; } bool CVar::Default() { auto value = this->m_defaultValue; if (value.GetString() == nullptr) { value = this->m_resetValue; } this->InternalSet(value.GetString(), true, false, false, true); return true; } int32_t CVar::Update() { if (!(this->m_flags & 0x2)) { return 0; } if (!this->m_latchedValue.GetString()) { return 0; } this->InternalSet(this->m_latchedValue.GetString(), true, false, false, true); this->m_latchedValue.Copy(nullptr); return 1; } static int32_t s_CreatePathDirectories(const char* szPath) { return true == OsCreateDirectory(szPath, 1); } int32_t CVar::Load(HOSFILE file) { char fastData[2048] = {0}; char line[2048] = {0}; auto size = OsGetFileSize(file); char* data = nullptr; if (0x1fff < size) { data = reinterpret_cast(SMemAlloc(size + 1, __FILE__, __LINE__, 0)); } else { data = fastData; } auto grown = 0x1FFF >= size; int32_t result = 0; size_t bytesRead = 0; if (OsReadFile(file, data, size, &bytesRead) == 0) { result = 0; } else { data[size] = '\0'; const char* curr = data; // Skip over UTF-8 byte order mark if ((((data != nullptr) && (2 < bytesRead)) && (data[0] == 0xef)) && ((data[1] == 0xbb && (data[2] == 0xbf)))) { curr = data + 3; } do { SStrTokenize(&curr, line, 0x800, "\r\n", 0); // Do not execute commands other than "set ..." if (SStrCmpI(line, "SET ", 4) == 0) { // Execute without adding to history ConsoleCommandExecute(line, 0); } result = 1; } while ((curr != nullptr) && (*curr != '\0')); } if (grown) { SMemFree(data, __FILE__, __LINE__, 0); } return result; } int32_t CVar::Load(const char* filename) { char path[STORM_MAX_PATH] = {0}; auto file = OsCreateFile(filename, OS_GENERIC_READ, 0, OS_CREATE_NEW | OS_CREATE_ALWAYS, OS_FILE_ATTRIBUTE_NORMAL, 0x3f3f3f3f); if (file == HOSFILE_INVALID) { SStrPrintf(path, STORM_MAX_PATH, "WTF\\%s", filename); file = OsCreateFile(filename, OS_GENERIC_READ, 0, OS_CREATE_NEW | OS_CREATE_ALWAYS, OS_FILE_ATTRIBUTE_NORMAL, 0x3f3f3f3f); if (file == HOSFILE_INVALID) { return 0; } } auto result = CVar::Load(file); OsCloseFile(file); return result; } void CVar::Initialize(const char* filename) { STORM_ASSERT(filename); s_filename = filename; // Get data path char path[STORM_MAX_PATH] = {0}; SFile::GetBasePath(path, STORM_MAX_PATH); SStrPrintf(path, STORM_MAX_PATH, "%s%s\\", path, "WTF"); s_CreatePathDirectories(path); static ConsoleCommandList baseCommands[] = { { "set", SetCommandHandler, "Set the value of a CVar" }, { "cvar_reset", CvarResetCommandHandler, "Set the value of a CVar to it's startup value" }, { "cvar_default", CvarDefaultCommandHandler, "Set the value of a CVar to it's coded default value" }, { "cvarlist", CvarListCommandHandler, "List cvars" } }; CONSOLE_REGISTER_LIST(DEFAULT, baseCommands); CVar::Load(s_filename); } int32_t CvarResetCommandHandler(const char* command, const char* arguments) { char cvarName[256] = {0}; auto string = arguments; SStrTokenize(&string, cvarName, sizeof(cvarName), " ,;\t\"\r\n", nullptr); if (*cvarName) { // reset a specific cvar auto cvar = CVar::Lookup(cvarName); if (cvar) { cvar->Reset(); } else { ConsoleWriteA("No such cvar \"%s\"\n", ERROR_COLOR, cvarName); } return 1; } else { // reset all cvars ConsoleWrite("Resetting all cvars\n", DEFAULT_COLOR); for (auto cvar = CVar::s_registeredCVars.Head(); cvar != nullptr; cvar = CVar::s_registeredCVars.Next(cvar)) { cvar->Reset(); } return 1; } } int32_t CvarDefaultCommandHandler(const char* command, const char* arguments) { char cvarName[256] = {0}; auto string = arguments; SStrTokenize(&string, cvarName, sizeof(cvarName), " ,;\t\"\r\n", nullptr); if (*cvarName) { // restore a specific cvar auto cvar = CVar::Lookup(cvarName); if (cvar) { cvar->Default(); } else { ConsoleWriteA("No such cvar \"%s\"\n", ERROR_COLOR, cvarName); } return 1; } else { // restore all cvars ConsoleWrite("Restoring all cvars\n", DEFAULT_COLOR); for (auto cvar = CVar::s_registeredCVars.Head(); cvar != nullptr; cvar = CVar::s_registeredCVars.Next(cvar)) { cvar->Default(); } return 1; } } int32_t CvarCommandHandler(const char* command, const char* arguments) { auto cvar = CVar::Lookup(command); STORM_ASSERT(cvar); while (*arguments == ' ') { arguments++; } if (arguments[0] != '\0') { cvar->Set(arguments, true, true, false, false); return 1; } auto value = cvar->m_stringValue.GetString(); ConsoleWriteA("CVar \"%s\" is \"%s\"", DEFAULT_COLOR, command, value ? value : ""); return 1; } int32_t SetCommandHandler(const char* command, const char* arguments) { char cvarValue[2048] = {0}; char cvarName[256] = {0}; auto str = arguments; SStrTokenize(&str, cvarName, sizeof(cvarName), " ,;\t\"\r\n", nullptr); SStrTokenize(&str, cvarValue, sizeof(cvarValue), " ,;\t\"\r\n", nullptr); auto var = CVar::Lookup(cvarName); if (var) { var->Set(cvarValue, true, false, false, true); } else { CVar::Register(cvarName, "", 0, cvarValue, nullptr, DEFAULT, true, nullptr, false); } return 1; } int32_t CvarListCommandHandler(const char* command, const char* arguments) { char text[256]; char text2[256]; for (auto cvar = CVar::s_registeredCVars.Head(); cvar != nullptr; cvar = CVar::s_registeredCVars.Next(cvar)) { SStrPrintf(text, sizeof(text), " \"%s\" is \"%s\"", cvar->m_key.m_str, cvar->m_stringValue.GetString()); if (cvar->m_defaultValue.GetString()) { if (SStrCmp(cvar->m_stringValue.GetString(), cvar->m_defaultValue.GetString(), STORM_MAX_STR)) { SStrPrintf(text2, sizeof(text2), " (default \"%s\")", cvar->m_defaultValue.GetString()); SStrPack(text, text2, sizeof(text)); } } if (cvar->m_resetValue.GetString()) { if (SStrCmp(cvar->m_stringValue.GetString(), cvar->m_resetValue.GetString(), STORM_MAX_STR)) { SStrPrintf(text2, sizeof(text2), " (reset \"%s\")", cvar->m_resetValue.GetString()); SStrPack(text, text2, sizeof(text)); } } ConsoleWrite(text, DEFAULT_COLOR); } return 1; }