docs: add plugin setup guide
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
#pragma once
|
||||
|
||||
#include "RE/B/BSFixedString.h"
|
||||
#include "RE/B/BSSimpleList.h"
|
||||
#include "RE/B/BSTBTree.h"
|
||||
#include "RE/M/MemoryManager.h"
|
||||
|
||||
namespace RE
|
||||
{
|
||||
union SETTING_VALUE
|
||||
{
|
||||
std::int8_t c;
|
||||
bool b;
|
||||
float f;
|
||||
std::uint8_t h;
|
||||
std::int32_t i;
|
||||
char* s;
|
||||
std::uint32_t u;
|
||||
std::uint32_t r;
|
||||
std::uint32_t a;
|
||||
};
|
||||
|
||||
class __declspec(novtable) Setting
|
||||
{
|
||||
public:
|
||||
static constexpr auto RTTI{ RTTI::Setting };
|
||||
static constexpr auto VTABLE{ VTABLE::Setting };
|
||||
|
||||
enum class SETTING_TYPE
|
||||
{
|
||||
kBinary, // b
|
||||
kChar, // c
|
||||
kUChar, // h
|
||||
kInt, // i
|
||||
kUInt, // u
|
||||
kFloat, // f
|
||||
kString, // s/S
|
||||
kRGB, // r
|
||||
kRGBA, // a
|
||||
kNone
|
||||
};
|
||||
|
||||
Setting(const char* a_key, char a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.c = a_value;
|
||||
}
|
||||
|
||||
Setting(const char* a_key, bool a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.b = a_value;
|
||||
}
|
||||
|
||||
Setting(const char* a_key, float a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.f = a_value;
|
||||
}
|
||||
|
||||
Setting(const char* a_key, std::uint8_t a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.h = a_value;
|
||||
}
|
||||
|
||||
Setting(const char* a_key, std::int32_t a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.i = a_value;
|
||||
}
|
||||
|
||||
Setting(const char* a_key, const char* a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.s = _strdup(a_value);
|
||||
}
|
||||
|
||||
Setting(const char* a_key, std::uint32_t a_value)
|
||||
{
|
||||
_key = a_key;
|
||||
_value.u = a_value;
|
||||
}
|
||||
|
||||
virtual ~Setting() // 00
|
||||
{
|
||||
if (_key && _key[0] == 'S') {
|
||||
free(const_cast<char*>(_key));
|
||||
_key = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// add
|
||||
[[nodiscard]] virtual bool IsPrefSetting() { return false; } // 01
|
||||
|
||||
F4_HEAP_REDEFINE_NEW(Setting);
|
||||
|
||||
[[nodiscard]] bool GetBinary() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kBinary);
|
||||
return _value.b;
|
||||
}
|
||||
|
||||
[[nodiscard]] char GetChar() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kChar);
|
||||
return _value.c;
|
||||
}
|
||||
|
||||
[[nodiscard]] float GetFloat() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kFloat);
|
||||
return _value.f;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::int32_t GetInt() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kInt);
|
||||
return _value.i;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view GetKey() const noexcept { return _key ? _key : ""sv; }
|
||||
|
||||
[[nodiscard]] std::span<const std::uint8_t, 3> GetRGB() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kRGB);
|
||||
return std::span<const std::uint8_t, 3>{
|
||||
reinterpret_cast<const std::uint8_t(&)[3]>(*std::addressof(_value.r))
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<const std::uint8_t, 4> GetRGBA() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kRGBA);
|
||||
return std::span<const std::uint8_t, 4>{
|
||||
reinterpret_cast<const std::uint8_t(&)[4]>(*std::addressof(_value.a))
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view GetString() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kString);
|
||||
return _value.s ? _value.s : ""sv;
|
||||
}
|
||||
|
||||
[[nodiscard]] SETTING_TYPE GetType() const noexcept
|
||||
{
|
||||
if (_key) {
|
||||
switch (_key[0]) {
|
||||
case 'b':
|
||||
return SETTING_TYPE::kBinary;
|
||||
case 'c':
|
||||
return SETTING_TYPE::kChar;
|
||||
case 'h':
|
||||
return SETTING_TYPE::kUChar;
|
||||
case 'i':
|
||||
return SETTING_TYPE::kInt;
|
||||
case 'u':
|
||||
return SETTING_TYPE::kUInt;
|
||||
case 'f':
|
||||
return SETTING_TYPE::kFloat;
|
||||
case 's':
|
||||
case 'S':
|
||||
return SETTING_TYPE::kString;
|
||||
case 'r':
|
||||
return SETTING_TYPE::kRGB;
|
||||
case 'a':
|
||||
return SETTING_TYPE::kRGBA;
|
||||
default:
|
||||
return SETTING_TYPE::kNone;
|
||||
}
|
||||
} else {
|
||||
return SETTING_TYPE::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint8_t GetUChar() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kUChar);
|
||||
return _value.h;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t GetUInt() const noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kUInt);
|
||||
return _value.u;
|
||||
}
|
||||
|
||||
void SetBinary(bool a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kBinary);
|
||||
_value.b = a_value;
|
||||
}
|
||||
|
||||
void SetChar(char a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kChar);
|
||||
_value.c = a_value;
|
||||
}
|
||||
|
||||
void SetFloat(float a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kFloat);
|
||||
_value.f = a_value;
|
||||
}
|
||||
|
||||
void SetInt(std::int32_t a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kInt);
|
||||
_value.i = a_value;
|
||||
}
|
||||
|
||||
void SetRGB(std::span<const std::uint8_t, 3> a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kRGB);
|
||||
std::copy(
|
||||
a_value.begin(),
|
||||
a_value.end(),
|
||||
std::addressof(_value.r));
|
||||
}
|
||||
|
||||
void SetRGBA(std::span<const std::uint8_t, 4> a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kRGBA);
|
||||
std::copy(
|
||||
a_value.begin(),
|
||||
a_value.end(),
|
||||
std::addressof(_value.r));
|
||||
}
|
||||
|
||||
void SetString(char* a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kString);
|
||||
_value.s = a_value;
|
||||
}
|
||||
|
||||
void SetUChar(std::uint8_t a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kUChar);
|
||||
_value.h = a_value;
|
||||
}
|
||||
|
||||
void SetUInt(std::uint32_t a_value) noexcept
|
||||
{
|
||||
assert(GetType() == SETTING_TYPE::kUInt);
|
||||
_value.u = a_value;
|
||||
}
|
||||
|
||||
private:
|
||||
// members
|
||||
SETTING_VALUE _value; // 08
|
||||
const char* _key; // 10
|
||||
};
|
||||
static_assert(sizeof(Setting) == 0x18);
|
||||
|
||||
template <class T>
|
||||
class __declspec(novtable) SettingT :
|
||||
public Setting // 00
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
class GameSettingCollection;
|
||||
class INIPrefSettingCollection;
|
||||
class INISettingCollection;
|
||||
class LipSynchroSettingCollection;
|
||||
class RegSettingCollection;
|
||||
|
||||
extern template class SettingT<GameSettingCollection>;
|
||||
extern template class SettingT<INIPrefSettingCollection>;
|
||||
extern template class SettingT<INISettingCollection>;
|
||||
extern template class SettingT<LipSynchroSettingCollection>;
|
||||
extern template class SettingT<RegSettingCollection>;
|
||||
|
||||
template <class T>
|
||||
class __declspec(novtable) SettingCollection
|
||||
{
|
||||
public:
|
||||
virtual ~SettingCollection() = default; // 00
|
||||
|
||||
// add
|
||||
virtual void Add(T* a_setting) = 0; // 01
|
||||
virtual void Remove(T* a_setting) = 0; // 02
|
||||
virtual bool WriteSetting(T& a_setting) = 0; // 03
|
||||
virtual bool ReadSetting(T& a_setting) = 0; // 04
|
||||
virtual bool Open([[maybe_unused]] bool a_write) { return false; } // 05
|
||||
virtual bool Close() { return true; } // 06
|
||||
virtual bool ReadSettingsFromProfile() { return false; } // 07
|
||||
virtual bool WriteSettings() { return handle != nullptr; } // 08
|
||||
virtual bool ReadSettings() { return handle != nullptr; } // 09
|
||||
|
||||
// members
|
||||
char settingFile[260]; // 008
|
||||
void* handle; // 110
|
||||
};
|
||||
|
||||
extern template class SettingCollection<Setting>;
|
||||
|
||||
template <class T>
|
||||
class __declspec(novtable) SettingCollectionList :
|
||||
public SettingCollection<T>
|
||||
{
|
||||
public:
|
||||
// members
|
||||
BSSimpleList<T*> settings; // 118
|
||||
};
|
||||
|
||||
extern template class SettingCollectionList<Setting>;
|
||||
|
||||
class __declspec(novtable) INISettingCollection :
|
||||
public SettingCollectionList<Setting> // 000
|
||||
{
|
||||
public:
|
||||
static constexpr auto RTTI{ RTTI::INISettingCollection };
|
||||
static constexpr auto VTABLE{ VTABLE::INISettingCollection };
|
||||
|
||||
[[nodiscard]] static INISettingCollection* GetSingleton()
|
||||
{
|
||||
static REL::Relocation<INISettingCollection**> singleton{ ID::Setting::INISettingCollection::Singleton };
|
||||
return *singleton;
|
||||
}
|
||||
|
||||
[[nodiscard]] Setting* GetSetting(std::string_view a_name)
|
||||
{
|
||||
for (auto& setting : settings) {
|
||||
if (setting->GetKey() == a_name) {
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(INISettingCollection) == 0x128);
|
||||
|
||||
class __declspec(novtable) INIPrefSettingCollection :
|
||||
public INISettingCollection // 000
|
||||
{
|
||||
public:
|
||||
static constexpr auto RTTI{ RTTI::INIPrefSettingCollection };
|
||||
static constexpr auto VTABLE{ VTABLE::INIPrefSettingCollection };
|
||||
|
||||
[[nodiscard]] static INIPrefSettingCollection* GetSingleton()
|
||||
{
|
||||
static REL::Relocation<INIPrefSettingCollection**> singleton{ ID::Setting::INIPrefSettingCollection::Singleton };
|
||||
return *singleton;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(INIPrefSettingCollection) == 0x128);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class SettingCollectionMapCompare
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] bool operator()(const RE::BSFixedString& a_lhs, const RE::BSFixedString& a_rhs) const noexcept
|
||||
{
|
||||
return a_lhs.c_str() < a_rhs.c_str();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class __declspec(novtable) SettingCollectionMap :
|
||||
public SettingCollection<T> // 000
|
||||
{
|
||||
public:
|
||||
// members
|
||||
BSTBTree<BSFixedString, T*, detail::SettingCollectionMapCompare> settings; // 118
|
||||
};
|
||||
|
||||
extern template class SettingCollectionMap<Setting>;
|
||||
|
||||
class __declspec(novtable) GameSettingCollection :
|
||||
public SettingCollectionMap<Setting> // 000
|
||||
{
|
||||
public:
|
||||
static constexpr auto RTTI{ RTTI::GameSettingCollection };
|
||||
static constexpr auto VTABLE{ VTABLE::GameSettingCollection };
|
||||
|
||||
static void InitCollection()
|
||||
{
|
||||
using func_t = decltype(&GameSettingCollection::InitCollection);
|
||||
static REL::Relocation<func_t> func{ ID::Setting::GameSettingCollection::InitCollection };
|
||||
return func();
|
||||
}
|
||||
|
||||
[[nodiscard]] static GameSettingCollection* GetSingleton()
|
||||
{
|
||||
static REL::Relocation<GameSettingCollection**> singleton{ ID::Setting::GameSettingCollection::Singleton };
|
||||
return *singleton;
|
||||
}
|
||||
|
||||
[[nodiscard]] Setting* GetSetting(std::string_view a_name)
|
||||
{
|
||||
auto it = settings.find(a_name);
|
||||
return it != settings.end() ? it->second : nullptr;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(GameSettingCollection) == 0x138);
|
||||
|
||||
inline Setting* GetINISetting(std::string_view a_name)
|
||||
{
|
||||
Setting* setting = nullptr;
|
||||
|
||||
auto iniPrefs = INIPrefSettingCollection::GetSingleton();
|
||||
setting = iniPrefs ? iniPrefs->GetSetting(a_name) : nullptr;
|
||||
if (!setting) {
|
||||
auto ini = INISettingCollection::GetSingleton();
|
||||
setting = ini ? ini->GetSetting(a_name) : nullptr;
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user