Skip to content

Commit

Permalink
cvar system
Browse files Browse the repository at this point in the history
  • Loading branch information
Force67 committed Jan 12, 2022
1 parent 085fc9c commit c529d56
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 174 deletions.
67 changes: 36 additions & 31 deletions Code/base/IniSettingsProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#include <base/Setting.h>
#include <base/simpleini/SimpleIni.h>

#include <fstream>
#include <charconv>
#include <fstream>

#include <TiltedCore/Filesystem.hpp>

Expand All @@ -24,34 +24,29 @@ static void ShittyFileWrite(const std::filesystem::path& path, const std::string
}

template <typename T, typename TVal>
static SI_Error SetIniValue(CSimpleIni& ini, const T* a_pSection, const T* a_pKey, TVal a_nValue,
static SI_Error SetIniValue(CSimpleIni& ini, const T* a_pSection, const T* a_pKey, const TVal a_nValue,
const T* a_pComment = nullptr)
{
if (!a_pSection || !a_pKey)
return SI_FAIL;

// TODO: switch to to_chars
auto buf = fmt::format("{}", a_nValue);
char szValue[64]{};
std::to_chars(szValue, szValue + sizeof(szValue), a_nValue);

// convert to output text
T szOutput[256];
CSimpleIni::Converter c(ini.IsUnicode());
c.ConvertFromStore(buf.c_str(), buf.length() + 1, szOutput, sizeof(szOutput) / sizeof(T));
c.ConvertFromStore(szValue, std::strlen(szValue) + 1, szOutput, sizeof(szOutput) / sizeof(T));

// actually add it
return ini.AddEntry(a_pSection, a_pKey, szOutput, a_pComment, false, true);
}

template <typename T, typename TVal>
TVal GetIniValue(CSimpleIni& ini, const T* a_pSection, const T* a_pKey, const TVal a_nDefault, bool& a_pHasMultiple)
{
// return the default if we don't have a value
const T* pszValue = ini.GetValue(a_pSection, a_pKey, nullptr, &a_pHasMultiple);
if (!pszValue || !*pszValue)
return a_nDefault;

// convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
char szValue[64] = {0};
char szValue[64]{};
CSimpleIni::Converter c(ini.IsUnicode());
if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue)))
{
Expand All @@ -67,7 +62,7 @@ TVal GetIniValue(CSimpleIni& ini, const T* a_pSection, const T* a_pKey, const TV
return a_nDefault;
}

std::pair<std::string, const char*> SplitSection(const SettingBase *setting)
std::pair<std::string, std::string> SplitSection(const SettingBase* setting)
{
std::string nameProperty(setting->name);
size_t pos = nameProperty.find_first_of(':');
Expand All @@ -77,7 +72,7 @@ std::pair<std::string, const char*> SplitSection(const SettingBase *setting)

return {section, name};
}
}
} // namespace

void SaveSettingsToIni(const std::filesystem::path& path)
{
Expand All @@ -88,30 +83,30 @@ void SaveSettingsToIni(const std::filesystem::path& path)
SettingBase::VisitAll([&](SettingBase* setting) {
auto items = SplitSection(setting);
auto& section = items.first;
auto name = items.second;
auto& name = items.second;

switch (setting->type)
{
case SettingBase::Type::kBoolean:
error = ini.SetBoolValue(section.c_str(), name, setting->data.as_boolean);
error = ini.SetBoolValue(section.c_str(), name.c_str(), setting->data.as_boolean);
break;
case SettingBase::Type::kInt:
error = SetIniValue(ini, section.c_str(), name, setting->data.as_int32);
error = SetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_int32);
break;
case SettingBase::Type::kUInt:
error = SetIniValue(ini, section.c_str(), name, setting->data.as_uint32);
error = SetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_uint32);
break;
case SettingBase::Type::kInt64:
error = SetIniValue(ini, section.c_str(), name, setting->data.as_int64);
error = SetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_int64);
break;
case SettingBase::Type::kUInt64:
error = SetIniValue(ini, section.c_str(), name, setting->data.as_uint64);
error = SetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_uint64);
break;
case SettingBase::Type::kFloat:
error = SetIniValue(ini, section.c_str(), name, setting->data.as_float);
error = SetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_float);
break;
case SettingBase::Type::kString:
error = ini.SetValue(section.c_str(), name, setting->c_str());
error = ini.SetValue(section.c_str(), name.c_str(), setting->c_str());
break;
default:
BASE_ASSERT(true, "SaveSettingsToIni(): Unknown type index for {}", setting->name);
Expand Down Expand Up @@ -141,33 +136,43 @@ void LoadSettingsFromIni(const std::filesystem::path& path)
SettingBase::VisitAll([&](SettingBase* setting) {
auto items = SplitSection(setting);
auto& section = items.first;
auto name = items.second;
auto& name = items.second;

bool multiMatch = false;
switch (setting->type)
{
// With scalar types we don't expect the size to change...
// However, they should all call StoreValue in the future.
case SettingBase::Type::kBoolean:
setting->data.as_boolean = ini.GetBoolValue(section.c_str(), name, setting->data.as_boolean);
setting->data.as_boolean = ini.GetBoolValue(section.c_str(), name.c_str(), setting->data.as_boolean);
break;
case SettingBase::Type::kInt:
setting->data.as_int32 = GetIniValue(ini, section.c_str(), name, setting->data.as_int32, multiMatch);
setting->data.as_int32 =
GetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_int32, multiMatch);
break;
case SettingBase::Type::kUInt:
setting->data.as_uint32 = GetIniValue(ini, section.c_str(), name, setting->data.as_uint32, multiMatch);
setting->data.as_uint32 =
GetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_uint32, multiMatch);
break;
case SettingBase::Type::kInt64:
setting->data.as_int64 = GetIniValue(ini, section.c_str(), name, setting->data.as_int64, multiMatch);
setting->data.as_int64 =
GetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_int64, multiMatch);
break;
case SettingBase::Type::kUInt64:
setting->data.as_uint64 = GetIniValue(ini, section.c_str(), name, setting->data.as_uint64, multiMatch);
setting->data.as_uint64 =
GetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_uint64, multiMatch);
break;
case SettingBase::Type::kFloat:
setting->data.as_float = GetIniValue(ini, section.c_str(), name, setting->data.as_float, multiMatch);
setting->data.as_float =
GetIniValue(ini, section.c_str(), name.c_str(), setting->data.as_float, multiMatch);
break;
case SettingBase::Type::kString:
// TODO: string_storage
//setting->data.as_string = GetIniValue(ini, section.c_str(), name, setting->data.as_float, multiMatch);
// Strings however are a special case, as it has its own allocator.
case SettingBase::Type::kString: {
// This is not nearly how i want it to be :/
const char* c = ini.GetValue(section.c_str(), name.c_str(), setting->c_str());
static_cast<StringSetting*>(setting)->StoreValue(*setting, c);
break;
}
default:
BASE_ASSERT(true, "LoadSettingsFromIni(): Unknown type index for {}", setting->name);
break;
Expand Down
86 changes: 63 additions & 23 deletions Code/base/Setting.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// For licensing information see LICENSE at the root of this distribution.
#pragma once

#include <TiltedCore/Stl.hpp>
#include <Base/Check.h>
#include <TiltedCore/Stl.hpp>

namespace base
{
Expand Down Expand Up @@ -32,12 +32,12 @@ struct SettingBase
kLocked = 1 << 1,
};

SettingBase(SettingBase*& parent) : next(parent)
SettingBase(SettingBase*& parent, const char* n, const char* d, Type t) : next(parent), name(n), desc(d), type(t)
{
parent = this;
}

SettingBase() : SettingBase(ROOT())
explicit SettingBase(const char* n, const char* d, Type t) : SettingBase(ROOT(), n, d, t)
{
}

Expand Down Expand Up @@ -72,15 +72,12 @@ struct SettingBase
return Type::kFloat;
if constexpr (std::is_same_v<T, const char*>)
return Type::kString;
if constexpr (std::is_same_v<T, char*>)
return Type::kString;

return Type::kNone;
}

const char* c_str() const
{
BASE_ASSERT(type == Type::kString, "Must be a string");
//BASE_ASSERT(type == Type::kString, "Must be a string");
return data.as_string;
}

Expand All @@ -91,6 +88,7 @@ struct SettingBase
// descriptor
const char* name{nullptr};
const char* desc{nullptr};

// Gets aligned to 8 bytes anyway
size_t dataLength;
union {
Expand All @@ -107,30 +105,59 @@ struct SettingBase
SettingBase* next;
};

// TODO: this would really benefit from CXX 20 requires keyword
template <typename T> struct Setting : SettingBase
namespace detail
{
// This could deserve a constinit
Setting(const char* acName, const char* acDesc, const T acDefault)
template <typename T> struct FixedStorage
{
constexpr FixedStorage(SettingBase& b, const T acValue)
{
SettingBase::name = acName;
SettingBase::desc = acDesc;
b.dataLength = sizeof(T);
b.data.as_int64 = 0;

StoreValue(b, acValue);
}

if constexpr (std::is_pointer<T>())
SettingBase::dataLength = std::strlen(acDefault);
else
SettingBase::dataLength = sizeof(Type);
inline constexpr void StoreValue(SettingBase& b, const T acValue)
{
// The Size is never gonna change, so we only need to update the actual
// data
std::memcpy(&b.data.as_int64, &acValue, sizeof(T));
}
};

SettingBase::type = ToTypeIndex<T>();
data.as_int64 = 0;
template <typename T> struct DynamicStringStorage
{
DynamicStringStorage(SettingBase& b, const T* acValue)
{
StoreValue(b, acValue);
}

// poor mans bit cast
std::memcpy(&SettingBase::data.as_int64, &acDefault, sizeof(T));
inline void StoreValue(SettingBase& b, const T* acValue)
{
m_data = acValue;
b.dataLength = m_data.length() * sizeof(T);
b.data.as_string = m_data.c_str();
}

private:
std::basic_string<T, std::char_traits<T>, StlAllocator<T>> m_data;
};
}

// Settings can have their own custom storage spaces.
// However, make sure to make the providers aware of this.
template <typename T, class TStorage = detail::FixedStorage<T>> class Setting : public SettingBase, public TStorage
{
public:
Setting(const char* acName, const char* acDesc, const T acDefault)
: SettingBase(acName, acDesc, ToTypeIndex<T>()), TStorage(*this, acDefault)
{
}

Setting() = delete;

T& value()
{
// TODO: bitcast?
return reinterpret_cast<T&>(data.as_uint64);
}

Expand All @@ -139,10 +166,23 @@ template <typename T> struct Setting : SettingBase
return static_cast<Tas>(data.as_uint64);
}

template <typename = typename std::enable_if<true>::type> operator bool()
operator bool()
{
static_assert(std::is_same_v<T, bool>, "Must be a boolean");
return data.as_boolean;
}

bool empty()
{
return dataLength == 0;
}

void operator=(const T value)
{
TStorage::StoreValue(*this, value);
}
};

using StringSetting = Setting<const char*, detail::DynamicStringStorage<char>>;
// NOTE: Wide strings are not supported, since our ini cant handle them.
} // namespace base
2 changes: 1 addition & 1 deletion Code/base/simpleini/SimpleIni.h
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
, m_bStoreIsUtf8(a_bIsUtf8)
, m_bAllowMultiKey(a_bAllowMultiKey)
, m_bAllowMultiLine(a_bAllowMultiLine)
, m_bSpaces(true)
, m_bSpaces(false)
, m_nOrder(0)
{ }

Expand Down
Loading

0 comments on commit c529d56

Please sign in to comment.