2022-11-02 08:21:25 +01:00
|
|
|
/* Copyright (C) 2022 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2004-06-09 16:58:33 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
#include "ConfigDB.h"
|
|
|
|
|
2011-04-29 21:10:34 +02:00
|
|
|
#include "lib/allocators/shared_ptr.h"
|
2018-08-22 18:02:05 +02:00
|
|
|
#include "lib/file/vfs/vfs_path.h"
|
2015-07-29 03:07:23 +02:00
|
|
|
#include "ps/CLogger.h"
|
2018-08-22 18:02:05 +02:00
|
|
|
#include "ps/CStr.h"
|
2015-07-29 03:07:23 +02:00
|
|
|
#include "ps/Filesystem.h"
|
2004-06-09 15:53:32 +02:00
|
|
|
|
2021-02-14 03:10:46 +01:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2019-06-06 21:30:48 +02:00
|
|
|
#include <mutex>
|
2018-08-22 18:02:05 +02:00
|
|
|
#include <unordered_set>
|
|
|
|
|
2021-02-14 03:10:46 +01:00
|
|
|
namespace
|
|
|
|
{
|
2020-11-21 12:57:14 +01:00
|
|
|
|
|
|
|
void TriggerAllHooks(const std::multimap<CStr, std::function<void()>>& hooks, const CStr& name)
|
|
|
|
{
|
|
|
|
std::for_each(hooks.lower_bound(name), hooks.upper_bound(name), [](const std::pair<CStr, std::function<void()>>& hook) { hook.second(); });
|
|
|
|
}
|
|
|
|
|
2018-08-22 18:02:05 +02:00
|
|
|
// These entries will not be printed to logfiles, so that logfiles can be shared without leaking personal or sensitive data
|
2021-02-14 03:10:46 +01:00
|
|
|
const std::unordered_set<std::string> g_UnloggedEntries = {
|
2018-06-07 00:37:20 +02:00
|
|
|
"lobby.password",
|
2018-08-22 18:02:05 +02:00
|
|
|
"lobby.buddies",
|
|
|
|
"userreport.id" // authentication token for GDPR personal data requests
|
2018-06-07 00:37:20 +02:00
|
|
|
};
|
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
#define CHECK_NS(rval)\
|
|
|
|
do {\
|
|
|
|
if (ns < 0 || ns >= CFG_LAST)\
|
|
|
|
{\
|
|
|
|
debug_warn(L"CConfigDB: Invalid ns value");\
|
|
|
|
return rval;\
|
|
|
|
}\
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
template<typename T> void Get(const CStr& value, T& ret)
|
|
|
|
{
|
|
|
|
std::stringstream ss(value);
|
|
|
|
ss >> ret;
|
|
|
|
}
|
2021-02-14 03:10:46 +01:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
template<> void Get<>(const CStr& value, bool& ret)
|
|
|
|
{
|
|
|
|
ret = value == "true";
|
|
|
|
}
|
2021-02-14 03:10:46 +01:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
template<> void Get<>(const CStr& value, std::string& ret)
|
|
|
|
{
|
|
|
|
ret = value;
|
|
|
|
}
|
2021-02-14 03:10:46 +01:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
std::string EscapeString(const CStr& str)
|
|
|
|
{
|
|
|
|
std::string ret;
|
|
|
|
for (size_t i = 0; i < str.length(); ++i)
|
|
|
|
{
|
|
|
|
if (str[i] == '\\')
|
|
|
|
ret += "\\\\";
|
|
|
|
else if (str[i] == '"')
|
|
|
|
ret += "\\\"";
|
|
|
|
else
|
|
|
|
ret += str[i];
|
2013-12-30 00:56:18 +01:00
|
|
|
}
|
2014-11-17 02:03:59 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2021-02-14 03:10:46 +01:00
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
typedef std::map<CStr, CConfigValueSet> TConfigMap;
|
2014-11-17 02:03:59 +01:00
|
|
|
|
|
|
|
#define GETVAL(type)\
|
|
|
|
void CConfigDB::GetValue(EConfigNamespace ns, const CStr& name, type& value)\
|
|
|
|
{\
|
2015-01-11 22:37:53 +01:00
|
|
|
CHECK_NS(;);\
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);\
|
2014-11-17 02:03:59 +01:00
|
|
|
TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name);\
|
|
|
|
if (it != m_Map[CFG_COMMAND].end())\
|
|
|
|
{\
|
2021-01-15 09:05:10 +01:00
|
|
|
if (!it->second.empty())\
|
|
|
|
Get(it->second[0], value);\
|
2014-11-17 02:03:59 +01:00
|
|
|
return;\
|
|
|
|
}\
|
2015-07-29 03:07:23 +02:00
|
|
|
for (int search_ns = ns; search_ns >= 0; --search_ns)\
|
2014-11-17 02:03:59 +01:00
|
|
|
{\
|
|
|
|
it = m_Map[search_ns].find(name);\
|
|
|
|
if (it != m_Map[search_ns].end())\
|
|
|
|
{\
|
2021-01-15 09:05:10 +01:00
|
|
|
if (!it->second.empty())\
|
|
|
|
Get(it->second[0], value);\
|
2014-11-17 02:03:59 +01:00
|
|
|
return;\
|
|
|
|
}\
|
|
|
|
}\
|
|
|
|
}
|
|
|
|
GETVAL(bool)
|
|
|
|
GETVAL(int)
|
2018-06-06 19:55:08 +02:00
|
|
|
GETVAL(u32)
|
2014-11-17 02:03:59 +01:00
|
|
|
GETVAL(float)
|
|
|
|
GETVAL(double)
|
|
|
|
GETVAL(std::string)
|
2013-12-30 00:56:18 +01:00
|
|
|
#undef GETVAL
|
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::unique_ptr<CConfigDB> g_ConfigDBPtr;
|
|
|
|
|
|
|
|
void CConfigDB::Initialise()
|
|
|
|
{
|
|
|
|
g_ConfigDBPtr = std::make_unique<CConfigDB>();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CConfigDB::Shutdown()
|
|
|
|
{
|
|
|
|
g_ConfigDBPtr.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CConfigDB::IsInitialised()
|
|
|
|
{
|
|
|
|
return !!g_ConfigDBPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CConfigDB* CConfigDB::Instance()
|
|
|
|
{
|
|
|
|
return g_ConfigDBPtr.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CConfigDB::CConfigDB()
|
|
|
|
{
|
2021-06-16 17:54:55 +02:00
|
|
|
m_HasChanges.fill(false);
|
2021-04-27 09:45:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CConfigDB::~CConfigDB()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-02-07 16:10:44 +01:00
|
|
|
bool CConfigDB::HasChanges(EConfigNamespace ns) const
|
|
|
|
{
|
|
|
|
CHECK_NS(false);
|
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2016-02-07 16:10:44 +01:00
|
|
|
return m_HasChanges[ns];
|
|
|
|
}
|
|
|
|
|
2016-02-07 20:06:15 +01:00
|
|
|
void CConfigDB::SetChanges(EConfigNamespace ns, bool value)
|
2016-02-07 16:10:44 +01:00
|
|
|
{
|
|
|
|
CHECK_NS(;);
|
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2016-02-07 16:10:44 +01:00
|
|
|
m_HasChanges[ns] = value;
|
|
|
|
}
|
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
void CConfigDB::GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values) const
|
2004-07-21 18:34:07 +02:00
|
|
|
{
|
2015-01-11 22:37:53 +01:00
|
|
|
CHECK_NS(;);
|
2004-10-23 16:39:28 +02:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
|
|
|
TConfigMap::const_iterator it = m_Map[CFG_COMMAND].find(name);
|
2010-11-17 00:00:52 +01:00
|
|
|
if (it != m_Map[CFG_COMMAND].end())
|
2013-12-30 00:56:18 +01:00
|
|
|
{
|
|
|
|
values = it->second;
|
|
|
|
return;
|
|
|
|
}
|
2004-10-23 16:39:28 +02:00
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
for (int search_ns = ns; search_ns >= 0; --search_ns)
|
2004-10-23 16:39:28 +02:00
|
|
|
{
|
2013-12-30 00:56:18 +01:00
|
|
|
it = m_Map[search_ns].find(name);
|
2004-10-23 16:39:28 +02:00
|
|
|
if (it != m_Map[search_ns].end())
|
2013-12-30 00:56:18 +01:00
|
|
|
{
|
|
|
|
values = it->second;
|
|
|
|
return;
|
|
|
|
}
|
2004-10-23 16:39:28 +02:00
|
|
|
}
|
2012-12-25 23:49:18 +01:00
|
|
|
}
|
2004-06-09 15:53:32 +02:00
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
EConfigNamespace CConfigDB::GetValueNamespace(EConfigNamespace ns, const CStr& name) const
|
2011-03-03 02:49:49 +01:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
CHECK_NS(CFG_LAST);
|
2011-03-03 02:49:49 +01:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
|
|
|
TConfigMap::const_iterator it = m_Map[CFG_COMMAND].find(name);
|
2011-03-03 02:49:49 +01:00
|
|
|
if (it != m_Map[CFG_COMMAND].end())
|
|
|
|
return CFG_COMMAND;
|
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
for (int search_ns = ns; search_ns >= 0; --search_ns)
|
2011-03-03 02:49:49 +01:00
|
|
|
{
|
2013-12-30 00:56:18 +01:00
|
|
|
it = m_Map[search_ns].find(name);
|
2011-03-03 02:49:49 +01:00
|
|
|
if (it != m_Map[search_ns].end())
|
|
|
|
return (EConfigNamespace)search_ns;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CFG_LAST;
|
|
|
|
}
|
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
std::map<CStr, CConfigValueSet> CConfigDB::GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix) const
|
2010-10-23 04:37:00 +02:00
|
|
|
{
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2012-04-17 15:14:34 +02:00
|
|
|
std::map<CStr, CConfigValueSet> ret;
|
2010-10-23 04:37:00 +02:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
CHECK_NS(ret);
|
2010-10-23 04:37:00 +02:00
|
|
|
|
2012-04-17 15:14:34 +02:00
|
|
|
// Loop upwards so that values in later namespaces can override
|
|
|
|
// values in earlier namespaces
|
2015-07-29 03:07:23 +02:00
|
|
|
for (int search_ns = 0; search_ns <= ns; ++search_ns)
|
2020-12-31 15:27:02 +01:00
|
|
|
for (const std::pair<const CStr, CConfigValueSet>& p : m_Map[search_ns])
|
2015-07-29 03:07:23 +02:00
|
|
|
if (boost::algorithm::starts_with(p.first, prefix))
|
|
|
|
ret[p.first] = p.second;
|
2010-10-23 04:37:00 +02:00
|
|
|
|
2020-12-31 15:27:02 +01:00
|
|
|
for (const std::pair<const CStr, CConfigValueSet>& p : m_Map[CFG_COMMAND])
|
2015-07-29 03:07:23 +02:00
|
|
|
if (boost::algorithm::starts_with(p.first, prefix))
|
|
|
|
ret[p.first] = p.second;
|
2013-12-30 00:56:18 +01:00
|
|
|
|
2010-10-23 04:37:00 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-30 00:56:18 +01:00
|
|
|
void CConfigDB::SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value)
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2015-01-11 22:37:53 +01:00
|
|
|
CHECK_NS(;);
|
2012-12-25 23:49:18 +01:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2012-12-25 23:49:18 +01:00
|
|
|
TConfigMap::iterator it = m_Map[ns].find(name);
|
2013-12-30 00:56:18 +01:00
|
|
|
if (it == m_Map[ns].end())
|
2015-08-19 05:32:47 +02:00
|
|
|
it = m_Map[ns].insert(m_Map[ns].begin(), make_pair(name, CConfigValueSet(1)));
|
2012-12-25 23:49:18 +01:00
|
|
|
|
2021-02-14 03:10:46 +01:00
|
|
|
if (!it->second.empty())
|
|
|
|
it->second[0] = value;
|
|
|
|
else
|
|
|
|
it->second.emplace_back(value);
|
2020-11-21 12:57:14 +01:00
|
|
|
|
|
|
|
TriggerAllHooks(m_Hooks, name);
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
|
|
|
|
2016-02-07 12:24:09 +01:00
|
|
|
void CConfigDB::SetValueBool(EConfigNamespace ns, const CStr& name, const bool value)
|
|
|
|
{
|
|
|
|
CStr valueString = value ? "true" : "false";
|
|
|
|
SetValueString(ns, name, valueString);
|
|
|
|
}
|
|
|
|
|
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
|
|
|
void CConfigDB::SetValueList(EConfigNamespace ns, const CStr& name, std::vector<CStr> values)
|
|
|
|
{
|
|
|
|
CHECK_NS(;);
|
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
Let players remap hotkeys in-game, fix default hotkeys being qwerty-specific.
- Provide a "Hotkey" screen to let players remap hotkeys in-game using a
convenient setup.
- Make all .cfg hotkeys refer to scancodes (i.e. position on the
keyboard), so that default hotkeys now translate correctly for AZERTY,
QWERTZ and other layouts.
- 'BackSpace' is now an alias for 'Delete', and works for killing units.
This fixes #1917, as macs don't have a proper delete key and would need
to use Fn+Del otherwise. This shifts "timewarp" to Shift+BackSpace.
Functionally, this switches hotkeys to scancodes, as that makes more
sense (they are combinations of key positions, not actual text output).
SDL includes are cleaned and key names are reused.
Fixes #2850, Fixes #2604, Refs #1810, Fixes #1917.
Follows work in 3d7784d2af.
Various diffs tested by: Angen, Stan, Freagarach
Comments by: Nescio
Differential Revision: https://code.wildfiregames.com/D2814
This was SVN commit r24215.
2020-11-19 10:27:26 +01:00
|
|
|
TConfigMap::iterator it = m_Map[ns].find(name);
|
|
|
|
if (it == m_Map[ns].end())
|
|
|
|
it = m_Map[ns].insert(m_Map[ns].begin(), make_pair(name, CConfigValueSet(1)));
|
|
|
|
|
|
|
|
it->second = values;
|
|
|
|
}
|
|
|
|
|
2022-11-02 08:21:25 +01:00
|
|
|
bool CConfigDB::RemoveValue(EConfigNamespace ns, const CStr& name)
|
2016-02-07 16:10:44 +01:00
|
|
|
{
|
2022-11-02 08:21:25 +01:00
|
|
|
CHECK_NS(false);
|
2016-02-07 16:10:44 +01:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2016-02-07 16:10:44 +01:00
|
|
|
TConfigMap::iterator it = m_Map[ns].find(name);
|
|
|
|
if (it == m_Map[ns].end())
|
2022-11-02 08:21:25 +01:00
|
|
|
return false;
|
2016-02-07 16:10:44 +01:00
|
|
|
m_Map[ns].erase(it);
|
2020-11-21 12:57:14 +01:00
|
|
|
|
|
|
|
TriggerAllHooks(m_Hooks, name);
|
2022-11-02 08:21:25 +01:00
|
|
|
return true;
|
2016-02-07 16:10:44 +01:00
|
|
|
}
|
|
|
|
|
2011-02-16 21:40:15 +01:00
|
|
|
void CConfigDB::SetConfigFile(EConfigNamespace ns, const VfsPath& path)
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2015-01-11 22:37:53 +01:00
|
|
|
CHECK_NS(;);
|
2012-12-25 23:49:18 +01:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2014-11-17 02:03:59 +01:00
|
|
|
m_ConfigFile[ns] = path;
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CConfigDB::Reload(EConfigNamespace ns)
|
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
CHECK_NS(false);
|
2011-02-16 21:40:15 +01:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2013-12-17 20:58:39 +01:00
|
|
|
|
2021-05-22 21:28:40 +02:00
|
|
|
std::shared_ptr<u8> buffer;
|
2014-11-17 02:03:59 +01:00
|
|
|
size_t buflen;
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2009-06-23 19:48:34 +02:00
|
|
|
// Handle missing files quietly
|
2009-08-02 13:07:42 +02:00
|
|
|
if (g_VFS->GetFileInfo(m_ConfigFile[ns], NULL) < 0)
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGMESSAGE("Cannot find config file \"%s\" - ignoring", m_ConfigFile[ns].string8());
|
2004-06-09 15:53:32 +02:00
|
|
|
return false;
|
|
|
|
}
|
2014-11-17 02:03:59 +01:00
|
|
|
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGMESSAGE("Loading config file \"%s\"", m_ConfigFile[ns].string8());
|
2014-11-17 02:03:59 +01:00
|
|
|
Status ret = g_VFS->LoadFile(m_ConfigFile[ns], buffer, buflen);
|
|
|
|
if (ret != INFO::OK)
|
2009-06-23 19:48:34 +02:00
|
|
|
{
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGERROR("CConfigDB::Reload(): vfs_load for \"%s\" failed: return was %lld", m_ConfigFile[ns].string8(), (long long)ret);
|
2014-11-17 02:03:59 +01:00
|
|
|
return false;
|
2009-06-23 19:48:34 +02:00
|
|
|
}
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
2015-02-12 18:31:10 +01:00
|
|
|
|
2004-06-09 15:53:32 +02:00
|
|
|
TConfigMap newMap;
|
2014-11-17 02:03:59 +01:00
|
|
|
char *filebuf = (char*)buffer.get();
|
|
|
|
char *filebufend = filebuf+buflen;
|
2015-02-12 18:31:10 +01:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
bool quoted = false;
|
2014-11-18 04:57:07 +01:00
|
|
|
CStr header;
|
2014-11-17 02:03:59 +01:00
|
|
|
CStr name;
|
|
|
|
CStr value;
|
|
|
|
int line = 1;
|
|
|
|
std::vector<CStr> values;
|
|
|
|
for (char* pos = filebuf; pos < filebufend; ++pos)
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
switch (*pos)
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
case '\n':
|
|
|
|
case ';':
|
|
|
|
break; // We finished parsing this line
|
2004-07-21 18:34:07 +02:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
case ' ':
|
|
|
|
case '\r':
|
2015-02-12 18:31:10 +01:00
|
|
|
case '\t':
|
2014-11-17 02:03:59 +01:00
|
|
|
continue; // ignore
|
2004-07-21 18:34:07 +02:00
|
|
|
|
2014-11-18 04:57:07 +01:00
|
|
|
case '[':
|
|
|
|
header.clear();
|
|
|
|
for (++pos; pos < filebufend && *pos != '\n' && *pos != ']'; ++pos)
|
|
|
|
header.push_back(*pos);
|
|
|
|
|
|
|
|
if (pos == filebufend || *pos == '\n')
|
|
|
|
{
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGERROR("Config header with missing close tag encountered on line %d in '%s'", line, m_ConfigFile[ns].string8());
|
2014-11-18 04:57:07 +01:00
|
|
|
header.clear();
|
|
|
|
++line;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGMESSAGE("Found config header '%s'", header.c_str());
|
2014-11-18 04:57:07 +01:00
|
|
|
header.push_back('.');
|
|
|
|
while (++pos < filebufend && *pos != '\n' && *pos != ';')
|
|
|
|
if (*pos != ' ' && *pos != '\r')
|
|
|
|
{
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGERROR("Config settings on the same line as a header on line %d in '%s'", line, m_ConfigFile[ns].string8());
|
2014-11-18 04:57:07 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
while (pos < filebufend && *pos != '\n')
|
|
|
|
++pos;
|
|
|
|
++line;
|
|
|
|
continue;
|
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
case '=':
|
|
|
|
// Parse parameters (comma separated, possibly quoted)
|
|
|
|
for (++pos; pos < filebufend && *pos != '\n' && *pos != ';'; ++pos)
|
2004-07-21 18:34:07 +02:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
switch (*pos)
|
|
|
|
{
|
|
|
|
case '"':
|
|
|
|
quoted = true;
|
|
|
|
// parse until not quoted anymore
|
|
|
|
for (++pos; pos < filebufend && *pos != '\n' && *pos != '"'; ++pos)
|
|
|
|
{
|
|
|
|
if (*pos == '\\' && ++pos == filebufend)
|
|
|
|
{
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGERROR("Escape character at end of input (line %d in '%s')", line, m_ConfigFile[ns].string8());
|
2014-11-17 02:03:59 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
value.push_back(*pos);
|
|
|
|
}
|
|
|
|
if (pos < filebufend && *pos == '"')
|
|
|
|
quoted = false;
|
|
|
|
else
|
|
|
|
--pos; // We should terminate the outer loop too
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ' ':
|
2015-02-12 18:31:10 +01:00
|
|
|
case '\r':
|
|
|
|
case '\t':
|
2014-11-17 02:03:59 +01:00
|
|
|
break; // ignore
|
|
|
|
|
|
|
|
case ',':
|
|
|
|
if (!value.empty())
|
|
|
|
values.push_back(value);
|
|
|
|
value.clear();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
value.push_back(*pos);
|
|
|
|
break;
|
|
|
|
}
|
2004-07-21 18:34:07 +02:00
|
|
|
}
|
2014-11-17 02:03:59 +01:00
|
|
|
if (quoted) // We ignore the invalid parameter
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGERROR("Unmatched quote while parsing config file '%s' on line %d", m_ConfigFile[ns].string8(), line);
|
2014-11-17 02:03:59 +01:00
|
|
|
else if (!value.empty())
|
|
|
|
values.push_back(value);
|
|
|
|
value.clear();
|
|
|
|
quoted = false;
|
|
|
|
break; // We are either at the end of the line, or we still have a comment to parse
|
|
|
|
|
|
|
|
default:
|
|
|
|
name.push_back(*pos);
|
|
|
|
continue;
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
2015-02-12 18:31:10 +01:00
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
// Consume the rest of the line
|
|
|
|
while (pos < filebufend && *pos != '\n')
|
|
|
|
++pos;
|
|
|
|
// Store the setting
|
2021-01-15 09:05:10 +01:00
|
|
|
if (!name.empty())
|
2014-11-17 02:03:59 +01:00
|
|
|
{
|
2014-11-18 04:57:07 +01:00
|
|
|
CStr key(header + name);
|
|
|
|
newMap[key] = values;
|
2018-06-07 00:37:20 +02:00
|
|
|
if (g_UnloggedEntries.find(key) != g_UnloggedEntries.end())
|
2015-01-22 21:37:38 +01:00
|
|
|
LOGMESSAGE("Loaded config string \"%s\"", key);
|
2021-01-15 09:05:10 +01:00
|
|
|
else if (values.empty())
|
|
|
|
LOGMESSAGE("Loaded config string \"%s\" = (empty)", key);
|
2014-11-17 02:03:59 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string vals;
|
2021-01-15 09:05:10 +01:00
|
|
|
for (size_t i = 0; i + 1 < newMap[key].size(); ++i)
|
2014-11-18 04:57:07 +01:00
|
|
|
vals += "\"" + EscapeString(newMap[key][i]) + "\", ";
|
|
|
|
vals += "\"" + EscapeString(newMap[key][values.size()-1]) + "\"";
|
2015-01-22 21:37:38 +01:00
|
|
|
LOGMESSAGE("Loaded config string \"%s\" = %s", key, vals);
|
2014-11-17 02:03:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
name.clear();
|
|
|
|
values.clear();
|
|
|
|
++line;
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
2014-11-17 02:03:59 +01:00
|
|
|
|
|
|
|
if (!name.empty())
|
2015-01-22 21:37:38 +01:00
|
|
|
LOGERROR("Config file does not have a new line after the last config setting '%s'", name);
|
2014-11-17 02:03:59 +01:00
|
|
|
|
2004-06-09 15:53:32 +02:00
|
|
|
m_Map[ns].swap(newMap);
|
2021-06-16 17:54:55 +02:00
|
|
|
m_HasChanges[ns] = false;
|
2004-06-09 16:58:33 +02:00
|
|
|
|
|
|
|
return true;
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
bool CConfigDB::WriteFile(EConfigNamespace ns) const
|
2004-06-09 15:53:32 +02:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
CHECK_NS(false);
|
2011-02-16 21:40:15 +01:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2011-02-16 21:40:15 +01:00
|
|
|
return WriteFile(ns, m_ConfigFile[ns]);
|
|
|
|
}
|
2007-12-20 21:21:45 +01:00
|
|
|
|
2015-07-29 03:07:23 +02:00
|
|
|
bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path) const
|
2011-02-16 21:40:15 +01:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
CHECK_NS(false);
|
2004-07-07 20:06:02 +02:00
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2021-05-22 21:28:40 +02:00
|
|
|
std::shared_ptr<u8> buf;
|
2011-04-29 21:10:34 +02:00
|
|
|
AllocateAligned(buf, 1*MiB, maxSectorSize);
|
2007-12-20 21:21:45 +01:00
|
|
|
char* pos = (char*)buf.get();
|
2020-12-31 15:27:02 +01:00
|
|
|
|
|
|
|
for (const std::pair<const CStr, CConfigValueSet>& p : m_Map[ns])
|
2004-07-07 20:06:02 +02:00
|
|
|
{
|
2014-11-17 02:03:59 +01:00
|
|
|
size_t i;
|
2015-07-29 03:07:23 +02:00
|
|
|
pos += sprintf(pos, "%s = ", p.first.c_str());
|
2021-01-15 09:05:10 +01:00
|
|
|
for (i = 0; i + 1 < p.second.size(); ++i)
|
2015-07-29 03:07:23 +02:00
|
|
|
pos += sprintf(pos, "\"%s\", ", EscapeString(p.second[i]).c_str());
|
2021-01-15 09:05:10 +01:00
|
|
|
if (!p.second.empty())
|
|
|
|
pos += sprintf(pos, "\"%s\"\n", EscapeString(p.second[i]).c_str());
|
|
|
|
else
|
|
|
|
pos += sprintf(pos, "\"\"\n");
|
2004-07-07 20:06:02 +02:00
|
|
|
}
|
2007-12-20 21:21:45 +01:00
|
|
|
const size_t len = pos - (char*)buf.get();
|
2004-07-07 20:06:02 +02:00
|
|
|
|
2011-05-03 14:38:42 +02:00
|
|
|
Status ret = g_VFS->CreateFile(path, buf, len);
|
2014-11-17 02:03:59 +01:00
|
|
|
if (ret < 0)
|
2004-07-07 20:06:02 +02:00
|
|
|
{
|
2015-01-22 21:36:24 +01:00
|
|
|
LOGERROR("CConfigDB::WriteFile(): CreateFile \"%s\" failed (error: %d)", path.string8(), (int)ret);
|
2007-12-20 21:21:45 +01:00
|
|
|
return false;
|
2004-07-07 20:06:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2004-06-09 15:53:32 +02:00
|
|
|
}
|
2014-11-17 02:03:59 +01:00
|
|
|
|
2016-02-07 12:31:23 +01:00
|
|
|
bool CConfigDB::WriteValueToFile(EConfigNamespace ns, const CStr& name, const CStr& value)
|
|
|
|
{
|
|
|
|
CHECK_NS(false);
|
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2016-02-07 12:31:23 +01:00
|
|
|
return WriteValueToFile(ns, name, value, m_ConfigFile[ns]);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CConfigDB::WriteValueToFile(EConfigNamespace ns, const CStr& name, const CStr& value, const VfsPath& path)
|
|
|
|
{
|
|
|
|
CHECK_NS(false);
|
|
|
|
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2016-02-07 12:31:23 +01:00
|
|
|
|
|
|
|
TConfigMap newMap;
|
|
|
|
m_Map[ns].swap(newMap);
|
2016-11-18 19:06:01 +01:00
|
|
|
Reload(ns);
|
2016-02-07 12:31:23 +01:00
|
|
|
|
|
|
|
SetValueString(ns, name, value);
|
|
|
|
bool ret = WriteFile(ns, path);
|
|
|
|
m_Map[ns].swap(newMap);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
Implement quality levels for actors & corresponding setting.
An actor file, as referenced by the VisualActor, can now define
different actors for different "quality level" setting.
In this initial version, the quality is handled directly by the object
manager.
Actor format impact:
- '<qualitylevels>' may be used as the root node, containing actor nodes
as children.
- such actor nodes can refer to a file, or to an inline actor, or
simply be inlined.
- such actor nodes may have a 'quality' attribute, specifying the
maximum quality level of this actor. By default, 255 (the maximum) is
implied.
- The actor format remains valid, but 'groups', 'variants', 'material',
'castshadow' and 'float' can be given a [minquality, maxquality[ range
via XML attributes. Outside of this range, the XML node is ignored
(making it possible to define, in a single actor file, several quality
levels).
Quality is a 0-255 value, with:
- Range 0-99 intended for lower level-of-detail actors (billboards,
etc.)
- Range 100-200 the 'normal' range for models. 100 is "low", 150
"medium", and 200 "high".
- Range 201-255 used for higher quality actors that might be used for
e.g. cinematics.
The range is wide to make it easier to add intermediate levels in the
future and it seemed easier given that an integer value of some kind was
required anyways.
Engine impacts:
- A new CActorDef class is introduced, wrapping an art/actors XML file
and its different quality levels. ObjectBase remains the definition of a
given 'actor', now at a given quality level.
- CActorDef imposes a maximal # of quality level for a particular actor
definition (5 currently).
- CUnit is made to refer to an Actor Definition explicitly, not a
particular ObjectBase.
- As a minor optimisation, variation keys are calculated on
pointer-to-sets-of-selections, instead of raw sets-of-selections, as
this reduces copying.
- some refactoring, including better const-correctness and hotloading
support via std::shared_ptr.
Differential Revision: https://code.wildfiregames.com/D3787
This was SVN commit r25210.
2021-04-08 09:22:24 +02:00
|
|
|
CConfigDBHook CConfigDB::RegisterHookAndCall(const CStr& name, std::function<void()> hook)
|
2020-11-21 12:57:14 +01:00
|
|
|
{
|
|
|
|
hook();
|
2021-04-27 09:45:40 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
Implement quality levels for actors & corresponding setting.
An actor file, as referenced by the VisualActor, can now define
different actors for different "quality level" setting.
In this initial version, the quality is handled directly by the object
manager.
Actor format impact:
- '<qualitylevels>' may be used as the root node, containing actor nodes
as children.
- such actor nodes can refer to a file, or to an inline actor, or
simply be inlined.
- such actor nodes may have a 'quality' attribute, specifying the
maximum quality level of this actor. By default, 255 (the maximum) is
implied.
- The actor format remains valid, but 'groups', 'variants', 'material',
'castshadow' and 'float' can be given a [minquality, maxquality[ range
via XML attributes. Outside of this range, the XML node is ignored
(making it possible to define, in a single actor file, several quality
levels).
Quality is a 0-255 value, with:
- Range 0-99 intended for lower level-of-detail actors (billboards,
etc.)
- Range 100-200 the 'normal' range for models. 100 is "low", 150
"medium", and 200 "high".
- Range 201-255 used for higher quality actors that might be used for
e.g. cinematics.
The range is wide to make it easier to add intermediate levels in the
future and it seemed easier given that an integer value of some kind was
required anyways.
Engine impacts:
- A new CActorDef class is introduced, wrapping an art/actors XML file
and its different quality levels. ObjectBase remains the definition of a
given 'actor', now at a given quality level.
- CActorDef imposes a maximal # of quality level for a particular actor
definition (5 currently).
- CUnit is made to refer to an Actor Definition explicitly, not a
particular ObjectBase.
- As a minor optimisation, variation keys are calculated on
pointer-to-sets-of-selections, instead of raw sets-of-selections, as
this reduces copying.
- some refactoring, including better const-correctness and hotloading
support via std::shared_ptr.
Differential Revision: https://code.wildfiregames.com/D3787
This was SVN commit r25210.
2021-04-08 09:22:24 +02:00
|
|
|
return CConfigDBHook(*this, m_Hooks.emplace(name, hook));
|
2020-11-21 12:57:14 +01:00
|
|
|
}
|
|
|
|
|
Implement quality levels for actors & corresponding setting.
An actor file, as referenced by the VisualActor, can now define
different actors for different "quality level" setting.
In this initial version, the quality is handled directly by the object
manager.
Actor format impact:
- '<qualitylevels>' may be used as the root node, containing actor nodes
as children.
- such actor nodes can refer to a file, or to an inline actor, or
simply be inlined.
- such actor nodes may have a 'quality' attribute, specifying the
maximum quality level of this actor. By default, 255 (the maximum) is
implied.
- The actor format remains valid, but 'groups', 'variants', 'material',
'castshadow' and 'float' can be given a [minquality, maxquality[ range
via XML attributes. Outside of this range, the XML node is ignored
(making it possible to define, in a single actor file, several quality
levels).
Quality is a 0-255 value, with:
- Range 0-99 intended for lower level-of-detail actors (billboards,
etc.)
- Range 100-200 the 'normal' range for models. 100 is "low", 150
"medium", and 200 "high".
- Range 201-255 used for higher quality actors that might be used for
e.g. cinematics.
The range is wide to make it easier to add intermediate levels in the
future and it seemed easier given that an integer value of some kind was
required anyways.
Engine impacts:
- A new CActorDef class is introduced, wrapping an art/actors XML file
and its different quality levels. ObjectBase remains the definition of a
given 'actor', now at a given quality level.
- CActorDef imposes a maximal # of quality level for a particular actor
definition (5 currently).
- CUnit is made to refer to an Actor Definition explicitly, not a
particular ObjectBase.
- As a minor optimisation, variation keys are calculated on
pointer-to-sets-of-selections, instead of raw sets-of-selections, as
this reduces copying.
- some refactoring, including better const-correctness and hotloading
support via std::shared_ptr.
Differential Revision: https://code.wildfiregames.com/D3787
This was SVN commit r25210.
2021-04-08 09:22:24 +02:00
|
|
|
void CConfigDB::UnregisterHook(CConfigDBHook&& hook)
|
2020-11-21 12:57:14 +01:00
|
|
|
{
|
2021-04-16 18:04:01 +02:00
|
|
|
if (hook.m_Ptr != m_Hooks.end())
|
2021-04-27 09:45:40 +02:00
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> s(m_Mutex);
|
2021-04-16 18:04:01 +02:00
|
|
|
m_Hooks.erase(hook.m_Ptr);
|
2021-04-27 09:45:40 +02:00
|
|
|
hook.m_Ptr = m_Hooks.end();
|
|
|
|
}
|
2020-11-21 12:57:14 +01:00
|
|
|
}
|
|
|
|
|
2021-04-16 18:04:01 +02:00
|
|
|
void CConfigDB::UnregisterHook(std::unique_ptr<CConfigDBHook> hook)
|
Implement quality levels for actors & corresponding setting.
An actor file, as referenced by the VisualActor, can now define
different actors for different "quality level" setting.
In this initial version, the quality is handled directly by the object
manager.
Actor format impact:
- '<qualitylevels>' may be used as the root node, containing actor nodes
as children.
- such actor nodes can refer to a file, or to an inline actor, or
simply be inlined.
- such actor nodes may have a 'quality' attribute, specifying the
maximum quality level of this actor. By default, 255 (the maximum) is
implied.
- The actor format remains valid, but 'groups', 'variants', 'material',
'castshadow' and 'float' can be given a [minquality, maxquality[ range
via XML attributes. Outside of this range, the XML node is ignored
(making it possible to define, in a single actor file, several quality
levels).
Quality is a 0-255 value, with:
- Range 0-99 intended for lower level-of-detail actors (billboards,
etc.)
- Range 100-200 the 'normal' range for models. 100 is "low", 150
"medium", and 200 "high".
- Range 201-255 used for higher quality actors that might be used for
e.g. cinematics.
The range is wide to make it easier to add intermediate levels in the
future and it seemed easier given that an integer value of some kind was
required anyways.
Engine impacts:
- A new CActorDef class is introduced, wrapping an art/actors XML file
and its different quality levels. ObjectBase remains the definition of a
given 'actor', now at a given quality level.
- CActorDef imposes a maximal # of quality level for a particular actor
definition (5 currently).
- CUnit is made to refer to an Actor Definition explicitly, not a
particular ObjectBase.
- As a minor optimisation, variation keys are calculated on
pointer-to-sets-of-selections, instead of raw sets-of-selections, as
this reduces copying.
- some refactoring, including better const-correctness and hotloading
support via std::shared_ptr.
Differential Revision: https://code.wildfiregames.com/D3787
This was SVN commit r25210.
2021-04-08 09:22:24 +02:00
|
|
|
{
|
|
|
|
UnregisterHook(std::move(*hook.get()));
|
|
|
|
}
|
|
|
|
|
2014-11-17 02:03:59 +01:00
|
|
|
#undef CHECK_NS
|