/* * 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 s_storage = {}; static constinit bool s_is_initialized = false; static constinit bool s_is_loaded = false; /* Get pointer to the map. */ Map *map = util::GetPointer(s_storage); /* Construct the map, if we haven't already. */ if (AMS_UNLIKELY(!s_is_initialized)) { /* Construct the instance. */ util::ConstructAt(s_storage); /* Note that we constructed. */ s_is_initialized = true; } /* Load the map, if we haven't already. */ if (AMS_UNLIKELY(!s_is_loaded)) { /* Attempt to load the map, allowing for failure if acceptable. */ const auto result = LoadKeyValueStoreMap(map); if (!force_load) { R_TRY(result); } /* Note that the map is loaded. */ s_is_loaded = true; } /* Set the output pointer. */ *out = map; return ResultSuccess(); } Result GetKeyValueStoreMapForciblyForDebug(Map **out) { /* Check preconditions. */ AMS_ASSERT(out != nullptr); /* Get the map. */ return GetKeyValueStoreMap(out, true); } Result GetMapValueOfKeyValueStoreItemForDebug(MapValue *out, const KeyValueStoreItemForDebug &item) { /* Check preconditions. */ AMS_ASSERT(out != nullptr); /* Create the map value. */ MapValue map_value = { .type = item.type, .current_value_size = item.current_value_size, .default_value_size = item.default_value_size, .current_value = nullptr, .default_value = nullptr, }; /* Ensure we free any buffers we allocate, if we fail. */ ON_SCOPE_EXIT { FreeMapValueToHeap(map_value); }; /* If the default value size is > 0, copy it to the map value. */ if (map_value.default_value_size > 0) { /* Allocate the default value if there is sufficient memory available. */ R_UNLESS(GetHeapAllocatableSize() >= map_value.default_value_size, settings::ResultSettingsItemValueAllocationFailed()); map_value.default_value = AllocateFromHeap(map_value.default_value_size); AMS_ASSERT(map_value.default_value != nullptr); /* Copy the default value from the item. */ std::memcpy(map_value.default_value, item.default_value, map_value.default_value_size); } /* If the current value and the default values are identical, set the map value to the default value. */ if (CompareValue(item.current_value, item.current_value_size, item.default_value, item.default_value_size)) { map_value.current_value_size = map_value.default_value_size; map_value.current_value = map_value.default_value; } else if (map_value.current_value_size > 0) { /* Allocate the current value if there is sufficient memory available. */ R_UNLESS(GetHeapAllocatableSize() >= map_value.current_value_size, settings::ResultSettingsItemValueAllocationFailed()); map_value.current_value = AllocateFromHeap(map_value.current_value_size); AMS_ASSERT(map_value.current_value != nullptr); /* Copy the current value from the item. */ std::memcpy(map_value.current_value, item.current_value, map_value.current_value_size); } /* Set the output map value. */ *out = map_value; /* Ensure we don't free the value buffers we returned. */ map_value.current_value = nullptr; map_value.default_value = nullptr; return ResultSuccess(); } template const char *GetSystemDataMountName(); template<> const char *GetSystemDataMountName() { return FwdbgSystemDataMountName; } template<> const char *GetSystemDataMountName() { return PfCfgSystemDataMountName; } template Result GetSystemData(SystemData **out_data, ncm::SystemDataId id) { /* Check pre-conditions. */ AMS_ASSERT(out_data != nullptr); /* Declare static instance variables. */ static constinit util::TypedStorage s_storage = {}; static constinit bool s_initialized = false; static constinit bool s_mounted = false; /* Get pointer to the system data. */ SystemData *data = util::GetPointer(s_storage); /* Construct the system data, if we haven't already. */ if (AMS_UNLIKELY(!s_initialized)) { /* Construct the instance. */ util::ConstructAt(s_storage); /* Setup system data. */ data->SetSystemDataId(id); data->SetMountName(GetSystemDataMountName()); /* Note that we constructed. */ s_initialized = true; } /* Mount the system data, if we haven't already. */ if (AMS_UNLIKELY(!s_mounted)) { /* Mount the system data. */ R_TRY(data->Mount()); /* Note that we mounted. */ s_mounted = true; } /* Set the output pointer. */ *out_data = data; return ResultSuccess(); } Result GetSystemSaveData(SystemSaveData **out_data, bool create_save) { /* Check pre-conditions. */ AMS_ASSERT(out_data != nullptr); /* Declare static instance variables. */ static constinit util::TypedStorage s_storage = {}; static constinit bool s_initialized = false; static constinit bool s_mounted = false; /* Get pointer to the system data. */ SystemSaveData *data = util::GetPointer(s_storage); /* Construct the system data, if we haven't already. */ if (AMS_UNLIKELY(!s_initialized)) { /* Construct the instance. */ util::ConstructAt(s_storage); /* Setup system data. */ data->SetSystemSaveDataId(SystemSaveDataId); data->SetTotalSize(SystemSaveDataSize); data->SetJournalSize(SystemSaveDataJournalSize); data->SetFlags(SystemSaveDataFlags); data->SetMountName(SystemSaveDataMountName); /* Note that we constructed. */ s_initialized = true; } /* Mount the system data, if we haven't already. */ if (AMS_UNLIKELY(!s_mounted)) { /* Mount the system data. */ R_TRY(data->Mount(create_save)); /* Note that we mounted. */ s_mounted = true; } /* Set the output pointer. */ *out_data = data; return ResultSuccess(); } Result LoadKeyValueStoreMap(Map *out) { /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); /* Clear the key value store map. */ ClearKeyValueStoreMap(*out); /* Get the firmware debug system data. */ SystemData *system_data = nullptr; R_TRY(GetSystemData(std::addressof(system_data), ncm::SystemDataId::FirmwareDebugSettings)); AMS_ASSERT(system_data != nullptr); /* Load the default keys/values for the firmware debug system data. */ R_TRY(LoadKeyValueStoreMapDefault(out, *system_data)); /* Load the keys/values based on the hardware type. */ R_TRY(LoadKeyValueStoreMap(out, GetSplHardwareType())); if (IsSplDevelopment()) { /* Get the system save data. */ SystemSaveData *system_save_data = nullptr; R_SUCCEED_IF(R_FAILED(GetSystemSaveData(std::addressof(system_save_data), false))); AMS_ASSERT(system_save_data != nullptr); /* Attempt to load the current keys/values from the system save data. */ if (const auto result = LoadKeyValueStoreMapCurrent(out, *system_save_data); R_FAILED(result)) { /* Reset all values to their defaults. */ for (auto &kv_pair : *out) { MapValue &map_value = kv_pair.second; /* 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); } /* Reset the current value to the default value. */ map_value.current_value_size = map_value.default_value_size; map_value.current_value = map_value.default_value; } /* Log failure to load system save data. TODO: Make this a warning. */ AMS_LOG("[firmware debug settings] Warning: Failed to load the system save data. (%08x, %d%03d-%04d)\n", result.GetInnerValue(), 2, result.GetModule(), result.GetDescription()); } } return ResultSuccess(); } Result LoadKeyValueStoreMap(Map *out, SplHardwareType hardware_type) { SystemData *data = nullptr; /* Get the platform configuration system data for the hardware type. */ switch (hardware_type) { case SplHardwareType_None: return ResultSuccess(); case SplHardwareType_Icosa: R_TRY(GetSystemData(std::addressof(data), ncm::SystemDataId::PlatformConfigIcosa)); break; case SplHardwareType_IcosaMariko: R_TRY(GetSystemData(std::addressof(data), ncm::SystemDataId::PlatformConfigIcosaMariko)); break; case SplHardwareType_Copper: R_TRY(GetSystemData(std::addressof(data), ncm::SystemDataId::PlatformConfigCopper)); break; case SplHardwareType_Hoag: R_TRY(GetSystemData(std::addressof(data), ncm::SystemDataId::PlatformConfigHoag)); break; case SplHardwareType_Calcio: R_TRY(GetSystemData(std::addressof(data), ncm::SystemDataId::PlatformConfigCalcio)); break; case SplHardwareType_Aula: R_TRY(GetSystemData(std::addressof(data), ncm::SystemDataId::PlatformConfigAula)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } /* Ensure data is not null. */ AMS_ASSERT(data != nullptr); /* Load the key value store map. */ return LoadKeyValueStoreMapDefault(out, *data); } template Result LoadKeyValueStoreMapCurrent(Map *out, T &data) { /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); /* Open the data for reading. */ R_TRY(data.OpenToRead()); ON_SCOPE_EXIT { data.Close(); }; /* Load the map entries. */ R_TRY(LoadKeyValueStoreMapEntries(out, data, [](Map &map, const MapKey &key, u8 type, const void *value_buffer, u32 value_size) -> Result { AMS_UNUSED(type); /* Find the key in the map. */ if (auto it = map.find(key); it != map.end()) { MapValue &map_value = it->second; size_t current_value_size = value_size; void *current_value_buffer = nullptr; if (current_value_size > 0) { /* Ensure there is sufficient memory for the value. */ R_UNLESS(GetHeapAllocatableSize() >= current_value_size, settings::ResultSettingsItemValueAllocationFailed()); /* Allocate the value buffer. */ current_value_buffer = AllocateFromHeap(current_value_size); AMS_ASSERT(current_value_buffer != nullptr); /* Copy the value to the current value buffer. */ std::memcpy(current_value_buffer, value_buffer, current_value_size); } /* Replace the current value buffer with a new one. */ std::swap(map_value.current_value_size, current_value_size); std::swap(map_value.current_value, current_value_buffer); /* Free the old buffer if it is no longer in use. */ if (current_value_buffer != nullptr && current_value_buffer != map_value.default_value) { FreeToHeap(current_value_buffer, current_value_size); } } return ResultSuccess(); })); return ResultSuccess(); } template Result LoadKeyValueStoreMapDefault(Map *out, T &data) { /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); /* Open the data for reading. */ R_TRY(data.OpenToRead()); ON_SCOPE_EXIT { data.Close(); }; /* Load the map entries. */ R_TRY(LoadKeyValueStoreMapEntries(out, data, [](Map &map, const MapKey &key, u8 type, const void *value_buffer, u32 value_size) -> Result { /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Copy the map key. */ MapKey default_key = key; void *default_value_buffer = nullptr; ON_SCOPE_EXIT { /* Free the value buffer if allocated. */ if (default_value_buffer != nullptr) { FreeToHeap(default_value_buffer, value_size); } }; if (value_size > 0) { /* Ensure there is sufficient memory for the value. */ R_UNLESS(GetHeapAllocatableSize() >= value_size, settings::ResultSettingsItemValueAllocationFailed()); /* Allocate the value buffer. */ default_value_buffer = AllocateFromHeap(value_size); AMS_ASSERT(default_value_buffer != nullptr); /* Copy the value to the new value buffer. */ std::memcpy(default_value_buffer, value_buffer, value_size); } /* Create the map value. */ MapValue default_value { .type = type, .current_value_size = value_size, .default_value_size = value_size, .current_value = default_value_buffer, .default_value = default_value_buffer, }; /* Ensure there is sufficient memory for the value. */ R_UNLESS(GetHeapAllocatableSize() >= MapEntryBufferSize, settings::ResultSettingsItemValueAllocationFailed()); /* Insert the value into the map. */ map[std::move(default_key)] = default_value; return ResultSuccess(); })); return ResultSuccess(); } template Result LoadKeyValueStoreMapEntries(Map *out, T &data, F load) { /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(load != nullptr); /* Read the number of entries. */ s64 offset = 0; u32 total_size = 0; R_TRY(ReadData(data, offset, std::addressof(total_size), sizeof(total_size))); /* Iterate through all entries. NOTE: The offset is updated within LoadKeyValueStoreMapEntry. */ while (offset < total_size) { R_TRY(LoadKeyValueStoreMapEntry(out, data, offset, load)); } return ResultSuccess(); } template Result LoadKeyValueStoreMapEntry(Map *out, T &data, s64 &offset, F load) { /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(load != nullptr); /* Read the size of the key. */ u32 key_size = 0; R_TRY(ReadData(data, offset, std::addressof(key_size), sizeof(key_size))); AMS_ASSERT(key_size > 1); /* Ensure there is sufficient memory for this key. */ R_UNLESS(GetHeapAllocatableSize() >= key_size, settings::ResultSettingsItemKeyAllocationFailed()); /* Read the key. */ void *key_buffer = nullptr; R_TRY(ReadDataToHeap(data, offset, std::addressof(key_buffer), key_size)); AMS_ASSERT(key_buffer != nullptr); ON_SCOPE_EXIT { FreeToHeap(key_buffer, key_size); }; /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); const MapKey key(static_cast(key_buffer), key_size - 1); /* Read the type from the data. */ u8 type = 0; R_TRY(ReadData(data, offset, std::addressof(type), sizeof(type))); /* Read the size of the value. */ u32 value_size = 0; R_TRY(ReadData(data, offset, std::addressof(value_size), sizeof(value_size))); void *value_buffer = nullptr; ON_SCOPE_EXIT { if (value_buffer != nullptr) { FreeToHeap(value_buffer, value_size); } }; if (value_size > 0) { /* Ensure there is sufficient memory for the value. */ R_UNLESS(GetHeapAllocatableSize() >= value_size, settings::ResultSettingsItemValueAllocationFailed()); /* Read the value to the buffer. */ R_TRY(ReadDataToHeap(data, offset, std::addressof(value_buffer), value_size)); } /* Load the value. */ return load(*out, key, type, value_buffer, value_size); } Result LoadKeyValueStoreMapForDebug(Map *out, SystemSaveData *system_save_data, SystemSaveData *fwdbg_system_save_data, SystemSaveData *pfcfg_system_save_data) { /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(system_save_data != nullptr); AMS_ASSERT(fwdbg_system_save_data != nullptr); AMS_ASSERT(pfcfg_system_save_data != nullptr); /* Clear the map. */ ClearKeyValueStoreMap(*out); /* Load the default keys/values for the firmware debug system save data. */ R_TRY(LoadKeyValueStoreMapDefault(out, *fwdbg_system_save_data)); /* Load the default keys/values for the platform configuration system save data. */ R_TRY(LoadKeyValueStoreMapDefault(out, *pfcfg_system_save_data)); /* Load the current values for the system save data. */ R_TRY(LoadKeyValueStoreMapCurrent(out, *system_save_data)); return ResultSuccess(); } template Result ReadData(T &data, s64 &offset, void *buffer, size_t size) { AMS_ASSERT(buffer != nullptr); /* Read the data. */ R_TRY(data.Read(offset, buffer, size)); /* Increment the offset. */ offset += static_cast(size); return ResultSuccess(); } template Result ReadDataToHeap(T &data, s64 &offset, void **buffer, size_t size) { /* Check pre-conditions. */ AMS_ASSERT(buffer != nullptr); AMS_ASSERT(size > 0); /* Allocate a buffer from the heap. */ *buffer = AllocateFromHeap(size); AMS_ASSERT(*buffer != nullptr); /* Ensure we free the buffer if we fail. */ auto alloc_guard = SCOPE_GUARD { FreeToHeap(*buffer, size); *buffer = nullptr; }; /* Read data to the buffer. */ R_TRY(ReadData(data, offset, *buffer, size)); /* We succeeded. */ alloc_guard.Cancel(); return ResultSuccess(); } template Result ReadAllBytes(T *data, u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(data != nullptr); AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer != nullptr); /* Open data for reading. */ R_TRY(data->OpenToRead()); ON_SCOPE_EXIT { data->Close(); }; /* Read the data size. */ u32 size = 0; R_TRY(data->Read(0, std::addressof(size), sizeof(size))); /* Ensure the data size does not exceed the buffer size. */ size = std::min(size, static_cast(out_buffer_size)); /* Read the data. */ R_TRY(data->Read(0, out_buffer, size)); /* Set the count. */ *out_count = size; return ResultSuccess(); } Result ReadSystemDataFirmwareDebug(u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer); /* Attempt to get the firmware debug system data. */ SystemData *system_data = nullptr; if (R_SUCCEEDED(GetSystemData(std::addressof(system_data), ncm::SystemDataId::FirmwareDebugSettings))) { AMS_ASSERT(system_data != nullptr); /* Read the data. */ R_TRY(ReadAllBytes(system_data, out_count, out_buffer, out_buffer_size)); } else { /* Set the output count to 0. */ *out_count = 0; } return ResultSuccess(); } Result ReadSystemDataPlatformConfiguration(u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer); ncm::SystemDataId system_data_id; switch (GetSplHardwareType()) { case SplHardwareType_None: *out_count = 0; return ResultSuccess(); case SplHardwareType_Icosa: system_data_id = ncm::SystemDataId::PlatformConfigIcosa; break; case SplHardwareType_IcosaMariko: system_data_id = ncm::SystemDataId::PlatformConfigIcosaMariko; break; case SplHardwareType_Copper: system_data_id = ncm::SystemDataId::PlatformConfigCopper; break; case SplHardwareType_Hoag: system_data_id = ncm::SystemDataId::PlatformConfigHoag; break; case SplHardwareType_Calcio: system_data_id = ncm::SystemDataId::PlatformConfigCalcio; break; case SplHardwareType_Aula: system_data_id = ncm::SystemDataId::PlatformConfigAula; break; AMS_UNREACHABLE_DEFAULT_CASE(); } /* Attempt to get the platform configuration system data. */ SystemData *system_data = nullptr; if (R_SUCCEEDED(GetSystemData(std::addressof(system_data), system_data_id))) { AMS_ASSERT(system_data != nullptr); /* Read the data. */ R_TRY(ReadAllBytes(system_data, out_count, out_buffer, out_buffer_size)); } else { /* Set the output count to 0. */ *out_count = 0; } return ResultSuccess(); } Result ReadSystemSaveData(u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer); /* Attempt to get the system save data. */ SystemSaveData *system_save_data = nullptr; if (R_SUCCEEDED(GetSystemSaveData(std::addressof(system_save_data), false))) { AMS_ASSERT(system_save_data != nullptr); /* Read the data. */ R_TRY(ReadAllBytes(system_save_data, out_count, out_buffer, out_buffer_size)); } else { /* Set the output count to 0. */ *out_count = 0; } return ResultSuccess(); } Result SaveKeyValueStoreMap(const Map &map) { /* Get the system save data. */ SystemSaveData *system_save_data = nullptr; R_TRY(GetSystemSaveData(std::addressof(system_save_data), false)); AMS_ASSERT(system_save_data != nullptr); /* Save the current values of the key value store map. */ return SaveKeyValueStoreMapCurrent(*system_save_data, map); } template Result SaveKeyValueStoreMap(T &data, const Map &map, F test) { /* Check preconditions. */ AMS_ASSERT(test != nullptr); /* Create the save data if necessary. */ R_TRY_CATCH(data.Create(HeapMemorySize)) { R_CATCH(fs::ResultPathAlreadyExists) { /* It's okay if the save data already exists. */ } } R_END_TRY_CATCH; { /* Open the save data for writing. */ R_TRY(data.OpenToWrite()); ON_SCOPE_EXIT { /* Flush and close the save data. NOTE: Nintendo only does this if SetFileSize succeeds. */ R_ABORT_UNLESS(data.Flush()); data.Close(); }; /* Set the file size of the save data. */ R_TRY(data.SetFileSize(HeapMemorySize)); /* Write the data size, which includes itself. */ u32 data_size = sizeof(data_size); R_TRY(data.Write(0, std::addressof(data_size), sizeof(data_size))); /* Set the current offset to after the data size. */ s64 current_offset = sizeof(data_size); /* Iterate through map entries. */ for (const auto &kv_pair : map) { /* Declare variables for test. */ u8 type = 0; const void *value_buffer = nullptr; u32 value_size = 0; /* Test if the map value varies from the default. */ if (test(std::addressof(type), std::addressof(value_buffer), std::addressof(value_size), kv_pair.second)) { R_TRY(SaveKeyValueStoreMapEntry(data, current_offset, kv_pair.first, type, value_buffer, value_size)); } } /* Write the updated save data size. */ data_size = static_cast(current_offset); R_TRY(data.Write(0, std::addressof(data_size), sizeof(data_size))); } /* Commit the save data. */ return data.Commit(false); } template Result SaveKeyValueStoreMapCurrent(T &data, const Map &map) { /* Save the current values in the map to the data. */ return SaveKeyValueStoreMap(data, map, [](u8 *out_type, const void **out_value_buffer, u32 *out_value_size, const MapValue &map_value) -> bool { /* Check preconditions. */ AMS_ASSERT(out_type != nullptr); AMS_ASSERT(out_value_buffer != nullptr); AMS_ASSERT(out_value_size != nullptr); /* Check if the current value matches the default value. */ if (CompareValue(map_value.current_value, map_value.current_value_size, map_value.default_value, map_value.default_value_size)) { return false; } /* Output the map value type, current value and current value size. */ *out_type = map_value.type; *out_value_buffer = map_value.current_value; *out_value_size = map_value.current_value_size; return true; }); } template Result SaveKeyValueStoreMapDefault(T &data, const Map &map) { /* Save the default values in the map to the data. */ return SaveKeyValueStoreMap(data, map, [](u8 *out_type, const void **out_value_buffer, u32 *out_value_size, const MapValue &map_value) -> bool { /* Check preconditions. */ AMS_ASSERT(out_type != nullptr); AMS_ASSERT(out_value_buffer != nullptr); AMS_ASSERT(out_value_size != nullptr); /* Output the map value type, default value and default value size. */ *out_type = map_value.type; *out_value_buffer = map_value.default_value; *out_value_size = map_value.default_value_size; return true; }); } Result SaveKeyValueStoreMapDefaultForDebug(SystemSaveData &data, const Map &map) { return SaveKeyValueStoreMapDefault(data, map); } template Result SaveKeyValueStoreMapEntry(T &data, s64 &offset, const MapKey &key, u8 type, const void *value_buffer, u32 value_size) { /* Write the key size and increment the offset. */ const u32 key_size = key.GetCount() + 1; R_TRY(data.Write(offset, std::addressof(key_size), sizeof(key_size))); offset += static_cast(sizeof(key_size)); /* Write the key string and increment the offset. */ R_TRY(data.Write(offset, key.GetString(), key_size)); offset += static_cast(key_size); /* Write the type and increment the offset. */ R_TRY(data.Write(offset, std::addressof(type), sizeof(type))); offset += static_cast(sizeof(type)); /* Write the value size and increment the offset. */ R_TRY(data.Write(offset, std::addressof(value_size), sizeof(value_size))); offset += static_cast(sizeof(value_size)); /* If the value is larger than 0, write it to the data. */ if (value_size > 0) { /* Check preconditions. */ AMS_ASSERT(value_buffer != nullptr); R_TRY(data.Write(offset, value_buffer, value_size)); offset += static_cast(value_size); } return ResultSuccess(); } } Result KeyValueStore::CreateKeyIterator(KeyValueStoreKeyIterator *out) { /* Check preconditions. */ AMS_ASSERT(out != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Create a map key from the key value store's name. */ MapKey map_key_header(m_name.value); /* Append the settings name separator. */ map_key_header.Append(SettingsNameSeparator); /* Define the item map key. */ const MapKey *item_map_key = nullptr; /* Find an item map key with the name as a prefix. */ for (const auto &kv_pair : *map) { const MapKey &map_key = kv_pair.first; /* Check if the name map key is smaller than the current map key, and the current map key contains the name map key. */ if (map_key_header < map_key && map_key.Find(map_key_header)) { item_map_key = std::addressof(map_key); break; } } /* Ensure we have located an item map key. */ R_UNLESS(item_map_key != nullptr, settings::ResultSettingsItemNotFound()); /* Ensure there is sufficient memory for the item map key. */ const size_t item_map_key_size = item_map_key->GetCount() + 1; R_UNLESS(GetHeapAllocatableSize() >= item_map_key_size, settings::ResultSettingsItemKeyIteratorAllocationFailed()); /* Allocate the key buffer. */ char *buffer = static_cast(AllocateFromHeap(item_map_key_size)); AMS_ASSERT(buffer != nullptr); /* Copy the item map key's string to the buffer. */ std::memcpy(buffer, item_map_key->GetString(), item_map_key_size); /* Output the iterator. */ *out = { .header_size = static_cast(map_key_header.GetCount()), .entire_size = item_map_key_size, .map_key = buffer, }; return ResultSuccess(); } Result KeyValueStore::GetValue(u64 *out_count, char *out_buffer, size_t out_buffer_size, const SettingsItemKey &item_key) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Find the key in the map. */ const Map::const_iterator it = map->find(MakeMapKey(m_name, item_key)); R_UNLESS(it != map->end(), settings::ResultSettingsItemNotFound()); /* Get the map value from the iterator. */ const MapValue &map_value = it->second; /* Calculate the current value size. */ const size_t current_value_size = std::min(map_value.current_value_size, out_buffer_size); /* If the current value size is > 0, copy to the output buffer. */ if (current_value_size > 0) { AMS_ASSERT(map_value.current_value != nullptr); std::memcpy(out_buffer, map_value.current_value, current_value_size); } /* Set the output count. */ *out_count = current_value_size; return ResultSuccess(); } Result KeyValueStore::GetValueSize(u64 *out_value_size, const SettingsItemKey &item_key) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Find the key in the map. */ const Map::const_iterator it = map->find(MakeMapKey(m_name, item_key)); R_UNLESS(it != map->end(), settings::ResultSettingsItemNotFound()); /* Output the value size. */ *out_value_size = it->second.current_value_size; return ResultSuccess(); } Result KeyValueStore::ResetValue(const SettingsItemKey &item_key) { /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Find the key in the map. */ const Map::iterator it = map->find(MakeMapKey(m_name, item_key)); R_UNLESS(it != map->end(), settings::ResultSettingsItemNotFound()); /* Get the map value from the iterator. */ MapValue &map_value = it->second; /* Succeed if the map value has already been reset. */ R_SUCCEED_IF(map_value.current_value == map_value.default_value); /* Store the previous value and its size. */ size_t prev_value_size = map_value.current_value_size; void *prev_value = map_value.current_value; /* Reset the current value to default. */ map_value.current_value_size = map_value.default_value_size; map_value.current_value = map_value.default_value; /* Attempt to save the key value store map. */ if (const auto result = SaveKeyValueStoreMap(*map); R_FAILED(result)) { /* Revert to the previous value. */ map_value.current_value_size = prev_value_size; map_value.current_value = prev_value; /* Attempt to save the map again. Nintendo does not check the result of this. */ SaveKeyValueStoreMap(*map); return result; } /* If present, free the previous value. */ if (prev_value != nullptr && prev_value != map_value.default_value) { FreeToHeap(prev_value, prev_value_size); } return ResultSuccess(); } Result KeyValueStore::SetValue(const SettingsItemKey &item_key, const void *buffer, size_t buffer_size) { /* Check preconditions. */ AMS_ASSERT(buffer != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Find the key in the map. */ const Map::iterator it = map->find(MakeMapKey(m_name, item_key)); R_UNLESS(it != map->end(), settings::ResultSettingsItemNotFound()); /* Get the map value from the iterator. */ MapValue &map_value = it->second; /* Succeed if the map value is already set to the new value. */ R_SUCCEED_IF(CompareValue(map_value.current_value, map_value.current_value_size, buffer, buffer_size)); /* Define the value buffer and size variables. */ size_t value_size = buffer_size; void *value_buffer = nullptr; /* Set the value buffer to the default value if the new value is the same. */ if (CompareValue(map_value.default_value, map_value.default_value_size, buffer, buffer_size)) { value_buffer = map_value.default_value; } else if (buffer_size > 0) { /* Allocate the new value if there is sufficient memory available. */ R_UNLESS(GetHeapAllocatableSize() >= value_size, settings::ResultSettingsItemValueAllocationFailed()); value_buffer = AllocateFromHeap(value_size); AMS_ASSERT(value_buffer != nullptr); /* Copy the value to the value buffer. */ std::memcpy(value_buffer, buffer, value_size); } /* Swap the current value with the new value. */ std::swap(map_value.current_value_size, value_size); std::swap(map_value.current_value, value_buffer); /* Attempt to save the key value store map. */ const auto result = SaveKeyValueStoreMap(*map); /* If we failed, revert to the previous value. */ if (R_FAILED(result)) { std::swap(map_value.current_value_size, value_size); std::swap(map_value.current_value, value_buffer); } /* Free the now unused value. */ if (value_buffer != nullptr && value_buffer != map_value.default_value) { FreeToHeap(value_buffer, value_size); } /* If we failed, attempt to save the map again. Note that Nintendo does not check the result of this. */ if (R_FAILED(result)) { SaveKeyValueStoreMap(*map); return result; } return ResultSuccess(); } Result AddKeyValueStoreItemForDebug(const KeyValueStoreItemForDebug * const items, size_t items_count) { /* Check preconditions. */ AMS_ASSERT(items != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMapForciblyForDebug(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Iterate through each item. */ for (size_t i = 0; i < items_count; i++) { const KeyValueStoreItemForDebug &item = items[i]; /* Create a map value for our scope. */ MapValue map_value = {}; ON_SCOPE_EXIT { FreeMapValueToHeap(map_value); }; /* Get the map value for the item. */ R_TRY(GetMapValueOfKeyValueStoreItemForDebug(std::addressof(map_value), item)); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Create the map key. */ MapKey map_key(item.key); /* Replace the existing value in the map if it already exists. */ if (const Map::iterator it = map->find(map_key); it != map->end()) { /* Free the existing map value. */ FreeMapValueToHeap(it->second); /* Replace the existing map value. */ it->second = map_value; } else { /* Ensure there is sufficient memory for the value. */ R_UNLESS(GetHeapAllocatableSize() >= MapEntryBufferSize, settings::ResultSettingsItemValueAllocationFailed()); /* Assign the map value to the map key in the map. */ (*map)[std::move(map_key)] = map_value; } /* Ensure we don't free the value buffers we added. */ map_value.current_value = nullptr; map_value.default_value = nullptr; } return ResultSuccess(); } Result AdvanceKeyValueStoreKeyIterator(KeyValueStoreKeyIterator *out) { /* Check preconditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(out->header_size > 0); AMS_ASSERT(out->header_size < out->entire_size); AMS_ASSERT(out->map_key != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Ensure there is sufficient memory for two keys. */ R_UNLESS(GetHeapAllocatableSize() >= MapKeyBufferSize, settings::ResultSettingsItemKeyAllocationFailed()); /* Locate the iterator's current key. */ Map::const_iterator it = map->find(MapKey(out->map_key, static_cast(out->entire_size) - 1)); R_UNLESS(it != map->end(), settings::ResultNotFoundSettingsItemKeyIterator()); /* Increment the iterator, ensuring we aren't at the end of the map. */ R_UNLESS((++it) != map->end(), settings::ResultStopIteration()); /* Get the map key. */ const MapKey &map_key = it->first; /* Ensure the advanced iterator retains the required name. */ R_UNLESS(std::strncmp(map_key.GetString(), out->map_key, out->header_size) == 0, settings::ResultStopIteration()); /* Ensure there is sufficient memory for the map key. */ const size_t map_key_size = map_key.GetCount() + 1; R_UNLESS(GetHeapAllocatableSize() >= map_key_size, settings::ResultSettingsItemKeyIteratorAllocationFailed()); /* Free the iterator's old map key. */ FreeToHeap(out->map_key, out->entire_size); /* Allocate the new map key. */ char *buffer = static_cast(AllocateFromHeap(map_key_size)); AMS_ASSERT(buffer != nullptr); /* Copy the new map key to the buffer. */ std::memcpy(buffer, map_key.GetString(), map_key_size); /* Set the output map key. */ out->entire_size = map_key_size; out->map_key = buffer; return ResultSuccess(); } Result DestroyKeyValueStoreKeyIterator(KeyValueStoreKeyIterator *out) { /* Check preconditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(out->header_size > 0); AMS_ASSERT(out->header_size < out->entire_size); AMS_ASSERT(out->map_key != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Free the key to the heap. */ FreeToHeap(out->map_key, out->entire_size); /* Reset the name and key. */ out->header_size = 0; out->entire_size = 0; out->map_key = nullptr; return ResultSuccess(); } Result GetKeyValueStoreItemCountForDebug(u64 *out_count) { /* Check preconditions. */ AMS_ASSERT(out != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Output the item count. */ *out_count = map->size(); return ResultSuccess(); } Result GetKeyValueStoreItemForDebug(u64 *out_count, KeyValueStoreItemForDebug * const out_items, size_t out_items_count) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_items != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Define the count variable. */ size_t count = 0; /* Iterate through each value in the map and output kvs items. */ for (const auto &kv_pair : *map) { /* Get the current map value. */ const MapValue &map_value = kv_pair.second; /* Break if the count exceeds the output items count. */ if (count >= out_items_count) { break; } /* Get the current item. */ KeyValueStoreItemForDebug &item = out_items[count++]; /* Copy the map key and value to the item. */ item.key = kv_pair.first.GetString(); item.type = map_value.type; item.current_value_size = map_value.current_value_size; item.default_value_size = map_value.default_value_size; item.current_value = map_value.current_value; item.default_value = map_value.default_value; } /* Set the output count. */ *out_count = count; return ResultSuccess(); } Result GetKeyValueStoreKeyIteratorKey(u64 *out_count, char *out_buffer, size_t out_buffer_size, const KeyValueStoreKeyIterator &iterator) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer != nullptr); AMS_ASSERT(iterator.header_size > 0); AMS_ASSERT(iterator.header_size < iterator.entire_size); AMS_ASSERT(iterator.map_key != nullptr); /* Copy the key from the iterator to the output buffer. */ const size_t key_size = std::min(out_buffer_size, std::min(iterator.entire_size - iterator.header_size, SettingsItemKeyLengthMax + 1)); std::strncpy(out_buffer, iterator.map_key + iterator.header_size, key_size); /* Set the end of the key to null. */ if (key_size > 0) { out_buffer[key_size - 1] = '\x00'; } /* Output the key size. */ *out_count = key_size; return ResultSuccess(); } Result GetKeyValueStoreKeyIteratorKeySize(u64 *out_count, const KeyValueStoreKeyIterator &iterator) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(iterator.header_size > 0); AMS_ASSERT(iterator.header_size < iterator.entire_size); AMS_ASSERT(iterator.map_key != nullptr); /* Output the key size. */ *out_count = std::min(iterator.entire_size - iterator.header_size, SettingsItemKeyLengthMax + 1); return ResultSuccess(); } Result ReadKeyValueStoreFirmwareDebug(u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Read the firmware debug system data. */ return ReadSystemDataFirmwareDebug(out_count, out_buffer, out_buffer_size); } Result ReadKeyValueStorePlatformConfiguration(u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Read the platform configuration system data. */ return ReadSystemDataPlatformConfiguration(out_count, out_buffer, out_buffer_size); } Result ReadKeyValueStoreSaveData(u64 *out_count, char * const out_buffer, size_t out_buffer_size) { /* Check preconditions. */ AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_buffer != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Read the system save data. */ return ReadSystemSaveData(out_count, out_buffer, out_buffer_size); } Result ReloadKeyValueStoreForDebug(SystemSaveData *system_save_data, SystemSaveData *fwdbg_system_data, SystemSaveData *pfcfg_system_data) { /* Check preconditions. */ AMS_ASSERT(system_save_data != nullptr); AMS_ASSERT(fwdbg_system_data != nullptr); AMS_ASSERT(pfcfg_system_data != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMapForciblyForDebug(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Load the key value store map. */ return LoadKeyValueStoreMapForDebug(map, system_save_data, fwdbg_system_data, pfcfg_system_data); } Result ReloadKeyValueStoreForDebug() { /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Load the key value store map. */ return LoadKeyValueStoreMap(map); } Result ResetKeyValueStoreSaveData() { /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Reset all values in the map. */ for (auto &kv_pair : *map) { /* Get the map value. */ MapValue &map_value = kv_pair.second; /* If the current value isn't the default value, reset it. */ if (map_value.current_value != map_value.default_value) { /* Store the previous value and size. */ size_t prev_value_size = map_value.current_value_size; void *prev_value = map_value.current_value; /* Reset the current value to the default value. */ map_value.current_value_size = map_value.default_value_size; map_value.current_value = map_value.default_value; /* Free the current value if present. */ if (prev_value != nullptr) { FreeToHeap(prev_value, prev_value_size); } } } /* Save the key value store map. */ return SaveKeyValueStoreMap(*map); } Result SaveKeyValueStoreAllForDebug(SystemSaveData *data) { /* Check preconditions. */ AMS_ASSERT(data != nullptr); /* Acquire exclusive access to global state. */ std::scoped_lock lk(g_key_value_store_mutex); /* Get the key value store map. */ Map *map = nullptr; R_TRY(GetKeyValueStoreMap(std::addressof(map))); AMS_ASSERT(map != nullptr); /* Save the key value store map default values. */ R_TRY(SaveKeyValueStoreMapDefaultForDebug(*data, *map)); /* Save the key value store map. */ return SaveKeyValueStoreMap(*map); } }