metaforce/hecl/lib/CVarManager.cpp

307 lines
10 KiB
C++
Raw Normal View History

2016-03-04 23:02:44 +00:00
#include "hecl/CVarManager.hpp"
2018-01-11 09:35:27 +00:00
#include "hecl/Console.hpp"
2016-03-04 23:02:44 +00:00
#include <athena/FileWriter.hpp>
#include <athena/Utility.hpp>
#include <hecl/Runtime.hpp>
2018-04-30 03:11:37 +00:00
#include <hecl/hecl.hpp>
2015-12-02 21:11:10 +00:00
#include <memory>
2018-04-30 03:11:37 +00:00
#include <regex>
2015-12-02 21:11:10 +00:00
2018-12-08 05:18:42 +00:00
namespace hecl {
2015-12-02 21:11:10 +00:00
2018-01-22 02:40:11 +00:00
CVar* com_developer = nullptr;
CVar* com_configfile = nullptr;
CVar* com_enableCheats = nullptr;
2015-12-02 21:11:10 +00:00
static const std::regex cmdLineRegex("\\+([\\w\\.]+)=([\\w\\.\\-]+)");
2015-12-04 01:43:29 +00:00
CVarManager* CVarManager::m_instance = nullptr;
2018-01-14 07:37:00 +00:00
static logvisor::Module CVarLog("CVarManager");
2016-03-04 23:02:44 +00:00
CVarManager::CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary)
2018-12-08 05:18:42 +00:00
: m_store(store), m_useBinary(useBinary) {
m_instance = this;
com_configfile = newCVar("config", "File to store configuration", std::string("config"), CVar::EFlags::System);
com_developer = newCVar("developer", "Enables developer mode", false,
(CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::InternalArchivable));
com_enableCheats =
newCVar("cheats", "Enable cheats", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::Hidden));
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
CVarManager::~CVarManager() {}
2015-12-02 21:11:10 +00:00
2018-12-08 05:18:42 +00:00
CVar* CVarManager::registerCVar(std::unique_ptr<CVar>&& cvar) {
std::string tmp(cvar->name());
athena::utility::tolower(tmp);
if (m_cvars.find(tmp) != m_cvars.end())
return nullptr;
2015-12-02 21:11:10 +00:00
2018-12-08 05:18:42 +00:00
CVar* ret = cvar.get();
m_cvars[tmp] = std::move(cvar);
return ret;
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
CVar* CVarManager::findCVar(std::string_view name) {
std::string lower(name);
athena::utility::tolower(lower);
auto search = m_cvars.find(lower);
if (search == m_cvars.end())
return nullptr;
2015-12-02 21:11:10 +00:00
2018-12-08 05:18:42 +00:00
return search->second.get();
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
std::vector<CVar*> CVarManager::archivedCVars() const {
std::vector<CVar*> ret;
for (const auto& pair : m_cvars)
if (pair.second->isArchive())
ret.push_back(pair.second.get());
2015-12-02 21:11:10 +00:00
2018-12-08 05:18:42 +00:00
return ret;
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
std::vector<CVar*> CVarManager::cvars(CVar::EFlags filter) const {
std::vector<CVar*> ret;
for (const auto& pair : m_cvars)
if (filter == CVar::EFlags::None || (pair.second->flags() & filter) != 0)
ret.push_back(pair.second.get());
2015-12-02 21:11:10 +00:00
2018-12-08 05:18:42 +00:00
return ret;
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::deserialize(CVar* cvar) {
if (!cvar)
return;
/* First let's check for a deferred value */
std::string lowName = cvar->name().data();
athena::utility::tolower(lowName);
if (m_deferedCVars.find(lowName) != m_deferedCVars.end()) {
std::string val = m_deferedCVars[lowName];
m_deferedCVars.erase(lowName);
if (cvar->fromLiteralToType(val))
return;
}
2018-06-13 03:19:08 +00:00
2018-12-08 05:18:42 +00:00
/* We were either unable to find a deferred value or got an invalid value */
if (!cvar->isArchive() && !cvar->isInternalArchivable())
return;
2015-12-02 21:11:10 +00:00
#if _WIN32
2018-12-08 05:18:42 +00:00
hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toWideLiteral();
2015-12-02 21:11:10 +00:00
#else
2018-12-08 05:18:42 +00:00
hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toLiteral();
2015-12-02 21:11:10 +00:00
#endif
2018-12-08 05:18:42 +00:00
hecl::Sstat st;
if (m_useBinary) {
CVarContainer container;
filename += _SYS_STR(".bin");
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
return;
athena::io::FileReader reader(filename);
if (reader.isOpen())
container.read(reader);
if (container.cvars.size() > 0) {
auto serialized = std::find_if(container.cvars.begin(), container.cvars.end(),
[&cvar](const DNACVAR::CVar& c) { return c.m_name == cvar->name(); });
if (serialized != container.cvars.end()) {
DNACVAR::CVar& tmp = *serialized;
if (cvar->m_value != tmp.m_value) {
cvar->unlock();
cvar->fromLiteralToType(tmp.m_value, true);
cvar->m_wasDeserialized = true;
cvar->lock();
}
2018-12-08 05:18:42 +00:00
}
2018-01-22 02:40:11 +00:00
}
2018-12-08 05:18:42 +00:00
} else {
filename += _SYS_STR(".yaml");
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
return;
athena::io::FileReader reader(filename);
if (reader.isOpen()) {
athena::io::YAMLDocReader docReader;
if (docReader.parse(&reader)) {
std::unique_ptr<athena::io::YAMLNode> root = docReader.releaseRootNode();
auto serialized = std::find_if(root->m_mapChildren.begin(), root->m_mapChildren.end(),
[&cvar](const auto& c) { return c.first == cvar->name(); });
if (serialized != root->m_mapChildren.end()) {
const std::unique_ptr<athena::io::YAMLNode>& tmp = serialized->second;
if (cvar->m_value != tmp->m_scalarString) {
cvar->unlock();
cvar->fromLiteralToType(tmp->m_scalarString, true);
cvar->m_wasDeserialized = true;
cvar->lock();
}
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
}
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
}
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::serialize() {
2015-12-02 21:11:10 +00:00
#if _WIN32
2018-12-08 05:18:42 +00:00
hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toWideLiteral();
2015-12-02 21:11:10 +00:00
#else
2018-12-08 05:18:42 +00:00
hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toLiteral();
2015-12-02 21:11:10 +00:00
#endif
2018-12-08 05:18:42 +00:00
if (m_useBinary) {
CVarContainer container;
for (const auto& pair : m_cvars)
if (pair.second->isArchive() ||
(pair.second->isInternalArchivable() && pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
container.cvars.push_back(*pair.second);
container.cvarCount = atUint32(container.cvars.size());
filename += _SYS_STR(".bin");
athena::io::FileWriter writer(filename);
if (writer.isOpen())
container.write(writer);
} else {
filename += _SYS_STR(".yaml");
athena::io::FileReader r(filename);
athena::io::YAMLDocWriter docWriter(nullptr, r.isOpen() ? &r : nullptr);
r.close();
docWriter.setStyle(athena::io::YAMLNodeStyle::Block);
for (const auto& pair : m_cvars)
if (pair.second->isArchive() ||
(pair.second->isInternalArchivable() && pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
docWriter.writeString(pair.second->name().data(), pair.second->toLiteral());
athena::io::FileWriter w(filename);
if (w.isOpen())
docWriter.finish(&w);
}
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
CVarManager* CVarManager::instance() { return m_instance; }
2015-12-04 01:43:29 +00:00
2018-12-08 05:18:42 +00:00
void CVarManager::list(Console* con, const std::vector<std::string>& /*args*/) {
for (const auto& cvar : m_cvars) {
if (!cvar.second->isHidden())
con->report(Console::Level::Info, "%s: %s", cvar.second->name().data(), cvar.second->help().c_str());
}
2018-01-11 09:35:27 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::setCVar(Console* con, const std::vector<std::string>& args) {
if (args.size() < 2) {
con->report(Console::Level::Info, "Usage setCvar <cvar> <value>");
return;
}
std::string cvName = args[0];
athena::utility::tolower(cvName);
if (m_cvars.find(cvName) == m_cvars.end()) {
con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str());
return;
}
const auto& cv = m_cvars[cvName];
std::string oldVal = cv->value();
std::string value = args[1];
auto it = args.begin() + 2;
for (; it != args.end(); ++it)
value += " " + *it;
/* Check to make sure we're not redundantly assigning the value */
if (cv->value() == value)
return;
if (!cv->fromLiteralToType(value))
con->report(Console::Level::Warning, "Unable to set cvar '%s' to value '%s'", cv->name().data(), value.c_str());
else
con->report(Console::Level::Info, "Set '%s' from '%s' to '%s'", cv->name().data(), oldVal.c_str(), value.c_str());
2018-01-11 09:35:27 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::getCVar(Console* con, const std::vector<std::string>& args) {
if (args.empty()) {
con->report(Console::Level::Info, "Usage getCVar <cvar>");
return;
}
std::string cvName = args[0];
athena::utility::tolower(cvName);
if (m_cvars.find(cvName) == m_cvars.end()) {
con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str());
return;
}
const auto& cv = m_cvars[cvName];
con->report(Console::Level::Info, "'%s' = '%s'", cv->name().data(), cv->value().c_str());
2018-01-22 02:40:11 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::setDeveloperMode(bool v, bool setDeserialized) {
com_developer->unlock();
com_developer->fromBoolean(v);
if (setDeserialized)
com_developer->m_wasDeserialized = true;
com_developer->lock();
com_developer->setModified();
2018-01-11 09:35:27 +00:00
}
2018-12-08 05:18:42 +00:00
bool CVarManager::restartRequired() const {
for (const auto& cv : m_cvars) {
if (cv.second->isModified() && cv.second->modificationRequiresRestart())
return true;
}
2018-01-11 09:35:27 +00:00
2018-12-08 05:18:42 +00:00
return false;
2018-01-11 09:35:27 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::parseCommandLine(const std::vector<SystemString>& args) {
bool oldDeveloper = suppressDeveloper();
std::string developerName = com_developer->name().data();
athena::utility::tolower(developerName);
for (const SystemString& arg : args) {
if (arg[0] == _SYS_STR('+')) {
std::string tmp = SystemUTF8Conv(arg).c_str();
std::smatch matches;
if (std::regex_match(tmp, matches, cmdLineRegex)) {
std::string cvarName = matches[1].str();
std::string cvarValue = matches[2].str();
if (CVar* cv = findCVar(cvarName)) {
cv->fromLiteralToType(cvarValue);
athena::utility::tolower(cvarName);
if (developerName == cvarName)
/* Make sure we're not overriding developer mode when we restore */
oldDeveloper = com_developer->toBoolean();
} else {
/* Unable to find an existing CVar, let's defer for the time being 8 */
athena::utility::tolower(cvarName);
m_deferedCVars[cvarName] = cvarValue;
2018-04-30 03:11:37 +00:00
}
2018-12-08 05:18:42 +00:00
}
2018-04-30 03:11:37 +00:00
}
2018-12-08 05:18:42 +00:00
}
2018-04-30 03:11:37 +00:00
2018-12-08 05:18:42 +00:00
restoreDeveloper(oldDeveloper);
2018-04-30 03:11:37 +00:00
}
2018-12-08 05:18:42 +00:00
bool CVarManager::suppressDeveloper() {
bool oldDeveloper = com_developer->toBoolean();
CVarUnlocker unlock(com_developer);
com_developer->fromBoolean(true);
2018-01-15 12:34:02 +00:00
2018-12-08 05:18:42 +00:00
return oldDeveloper;
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
void CVarManager::restoreDeveloper(bool oldDeveloper) {
CVarUnlocker unlock(com_developer);
com_developer->fromBoolean(oldDeveloper);
2015-12-02 21:11:10 +00:00
}
2018-12-08 05:18:42 +00:00
} // namespace hecl