/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 this program. If not, see .
*/
#include
#include "settings_key_value_store.hpp"
#include "settings_spl.hpp"
#include "settings_system_data.hpp"
namespace ams::settings::impl {
namespace {
constexpr fs::SystemSaveDataId SystemSaveDataId = 0x8000000000000051;
constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterRefurbishment | fs::SaveDataFlags_KeepAfterResettingSystemSaveData;
constexpr s64 SystemSaveDataSize = 544_KB;
constexpr s64 SystemSaveDataJournalSize = 544_KB;
constexpr inline const char FwdbgSystemDataMountName[] = "FwdbgSettingsD";
constexpr inline const char PfCfgSystemDataMountName[] = "PfcfgSettingsD";
constexpr inline const char SystemSaveDataMountName[] = "FwdbgSettingsS";
constexpr inline const char SettingsNameSeparator = '!';
/* Type forward declarations. */
class MapKey;
struct MapValue;
template
class Allocator;
using Map = std::map, Allocator>>;
/* Function forward declarations. */
void FreeMapValueToHeap(const MapValue &value);
void *AllocateFromHeap(size_t size);
void FreeToHeap(void *block, size_t size);
lmem::HeapHandle &GetHeapHandle();
Result GetKeyValueStoreMap(Map **out);
Result GetKeyValueStoreMap(Map **out, bool force_load);
Result GetKeyValueStoreMapForciblyForDebug(Map **out);
Result LoadKeyValueStoreMap(Map *out);
Result LoadKeyValueStoreMap(Map *out, SplHardwareType hardware_type);
template
Result LoadKeyValueStoreMapCurrent(Map *out, T &data);
template
Result LoadKeyValueStoreMapDefault(Map *out, T &data);
template
Result LoadKeyValueStoreMapEntries(Map *out, T &data, F load);
template
Result LoadKeyValueStoreMapEntry(Map *out, T &data, s64 &offset, F load);
Result LoadKeyValueStoreMapForDebug(Map *out, SystemSaveData *system_save_data, SystemSaveData *fwdbg_system_data, SystemSaveData *pfcfg_system_data);
template
Result ReadData(T &data, s64 &offset, void *buffer, size_t size);
template
Result ReadDataToHeap(T &data, s64 &offset, void **buffer, size_t size);
template
Result ReadAllBytes(T *data, u64 *out_count, char * const out_buffer, size_t out_buffer_size);
template
Result SaveKeyValueStoreMapCurrent(T &data, const Map &map);
struct SystemDataTag {
struct Fwdbg{};
struct PfCfg{};
};
class MapKey {
public:
static constexpr size_t MaxKeySize = sizeof(SettingsName) + sizeof(SettingsItemKey);
private:
char *m_chars;
size_t m_count;
public:
MapKey(const char * const chars) : m_chars(nullptr), m_count(0) {
AMS_ASSERT(chars != nullptr);
this->Assign(chars, util::Strnlen(chars, MaxKeySize));
}
MapKey(const char * const chars, s32 count) : m_chars(nullptr), m_count(0) {
AMS_ASSERT(chars != nullptr);
AMS_ASSERT(count >= 0);
this->Assign(chars, count);
}
MapKey(MapKey &&other) : m_chars(nullptr), m_count(0) {
std::swap(m_chars, other.m_chars);
std::swap(m_count, other.m_count);
}
MapKey(const MapKey &other) : m_chars(nullptr), m_count(0) {
this->Assign(other.GetString(), other.GetCount());
}
~MapKey() {
this->Reset();
}
MapKey &Append(char c) {
const char chars[2] = { c, 0 };
return this->Append(chars, 1);
}
MapKey &Append(const char * const chars, s32 count) {
AMS_ASSERT(chars != nullptr);
AMS_ASSERT(count >= 0);
/* Allocate the new key. */
const size_t new_count = m_count + count;
char *new_heap = static_cast(AllocateFromHeap(new_count));
/* Copy the existing string to the new heap. */
std::memcpy(new_heap, this->GetString(), this->GetCount());
/* Copy the string to append to the new heap. */
std::memcpy(new_heap + this->GetCount(), chars, count);
/* Null-terminate the new string. */
new_heap[new_count - 1] = '\x00';
/* Reset and update the key. */
this->Reset();
m_count = new_count;
m_chars = new_heap;
return *this;
}
MapKey &Assign(const char * const chars, s32 count) {
AMS_ASSERT(chars != nullptr);
AMS_ASSERT(count >= 0);
/* Reset the key. */
this->Reset();
/* Update the count and allocate the buffer. */
m_count = count + 1;
m_chars = static_cast(AllocateFromHeap(m_count));
/* Copy the characters to the buffer. */
std::memcpy(m_chars, chars, count);
m_chars[count] = '\x00';
return *this;
}
size_t Find(const MapKey &other) const {
return std::search(this->GetString(), this->GetString() + this->GetCount(), other.GetString(), other.GetString() + other.GetCount()) - this->GetString();
}
s32 GetCount() const {
return static_cast(m_count) - 1;
}
const char *GetString() const {
return m_chars;
}
private:
void Reset() {
if (m_chars != nullptr) {
FreeToHeap(m_chars, m_count);
m_chars = nullptr;
m_count = 0;
}
}
};
inline bool operator<(const MapKey &lhs, const MapKey &rhs) {
return std::strncmp(lhs.GetString(), rhs.GetString(), std::max(lhs.GetCount(), rhs.GetCount())) < 0;
}
MapKey MakeMapKey(const SettingsName &name, const SettingsItemKey &item_key) {
/* Create a map key. */
MapKey key(name.value, util::Strnlen(name.value, util::size(name.value)));
/* Append the settings name separator followed by the item key. */
key.Append(SettingsNameSeparator);
key.Append(item_key.value, util::Strnlen(item_key.value, util::size(item_key.value)));
/* Output the map key. */
return key;
}
struct MapValue {
public:
u8 type;
size_t current_value_size;
size_t default_value_size;
void *current_value;
void *default_value;
};
static_assert(sizeof(MapValue) == 0x28);
template
class Allocator {
public:
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
public:
Allocator() noexcept = default;
~Allocator() noexcept = default;
Allocator(const Allocator &) noexcept = default;
Allocator(Allocator &&) noexcept = default;
T *allocate(size_t n) noexcept {
return static_cast(AllocateFromHeap(sizeof(T) * n));
}
void deallocate(T *p, size_t n) noexcept {
FreeToHeap(p, sizeof(T) * n);
}
private:
Allocator &operator=(const Allocator &) noexcept = default;
Allocator &operator=(Allocator &&) noexcept = default;
};
template
constexpr inline bool operator==(const Allocator &, const Allocator &) {
return true;
}
constexpr inline size_t MapKeyBufferSize = MapKey::MaxKeySize * 2;
constexpr inline size_t MapEntryBufferSize = 0x40 + sizeof(Map::value_type);
constexpr inline size_t HeapMemorySize = 512_KB;
constinit os::SdkMutex g_key_value_store_mutex;
void ClearKeyValueStoreMap(Map &map) {
/* Free all values to the heap. */
for (const auto &kv_pair : map) {
FreeMapValueToHeap(kv_pair.second);
}
/* Clear the map. */
map.clear();
}
bool CompareValue(const void *lhs, size_t lhs_size, const void *rhs, size_t rhs_size) {
/* Check if both buffers are the same. */
if (lhs == rhs) {
return true;
}
/* If the value sizes don't match, return false. */
if (lhs_size != rhs_size) {
return false;
}
/* If the value sizes are 0, they are considered to match. */
if (lhs_size == 0) {
return true;
}
/* Compare the two values if they are non-null. */
return lhs != nullptr && rhs != nullptr && std::memcmp(lhs, rhs, lhs_size) == 0;
}
void FreeMapValueToHeap(const MapValue &map_value) {
/* Free the current value. */
if (map_value.current_value != nullptr && map_value.current_value != map_value.default_value) {
FreeToHeap(map_value.current_value, map_value.current_value_size);
}
/* Free the default value. */
if (map_value.default_value != nullptr) {
FreeToHeap(map_value.default_value, map_value.default_value_size);
}
}
void FreeToHeap(void *block, size_t size) {
AMS_UNUSED(size);
lmem::FreeToExpHeap(GetHeapHandle(), block);
}
void *AllocateFromHeap(size_t size) {
return lmem::AllocateFromExpHeap(GetHeapHandle(), size);
}
size_t GetHeapAllocatableSize() {
return lmem::GetExpHeapAllocatableSize(GetHeapHandle(), sizeof(void *));
}
lmem::HeapHandle &GetHeapHandle() {
static constinit bool s_is_initialized = false;
static constinit lmem::HeapHandle s_heap_handle;
static constinit u8 s_heap_memory[HeapMemorySize];
if (!s_is_initialized) {
s_heap_handle = lmem::CreateExpHeap(s_heap_memory, sizeof(s_heap_memory), lmem::CreateOption_None);
s_is_initialized = true;
}
return s_heap_handle;
}
Result GetKeyValueStoreMap(Map **out) {
/* Check preconditions. */
AMS_ASSERT(out != nullptr);
/* Get the map. */
return GetKeyValueStoreMap(out, false);
}
Result GetKeyValueStoreMap(Map **out, bool force_load) {
/* Check preconditions. */
AMS_ASSERT(out != nullptr);
/* Declare static instance variables. */
static constinit util::TypedStorage