From 4144c517a5df13b79f44d2e2e11b7ac6e06e276f Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Sun, 26 Nov 2023 07:36:08 +0000 Subject: [PATCH] Make system settings persistent across boots --- src/common/common_funcs.h | 6 + src/core/CMakeLists.txt | 8 + src/core/hle/service/set/appln_settings.cpp | 12 + src/core/hle/service/set/appln_settings.h | 35 + src/core/hle/service/set/device_settings.cpp | 12 + src/core/hle/service/set/device_settings.h | 53 ++ src/core/hle/service/set/private_settings.cpp | 12 + src/core/hle/service/set/private_settings.h | 72 ++ src/core/hle/service/set/set.h | 24 +- src/core/hle/service/set/set_sys.cpp | 666 +++++++++++++++-- src/core/hle/service/set/set_sys.h | 371 ++-------- src/core/hle/service/set/system_settings.cpp | 51 ++ src/core/hle/service/set/system_settings.h | 699 ++++++++++++++++++ 13 files changed, 1616 insertions(+), 405 deletions(-) create mode 100644 src/core/hle/service/set/appln_settings.cpp create mode 100644 src/core/hle/service/set/appln_settings.h create mode 100644 src/core/hle/service/set/device_settings.cpp create mode 100644 src/core/hle/service/set/device_settings.h create mode 100644 src/core/hle/service/set/private_settings.cpp create mode 100644 src/core/hle/service/set/private_settings.h create mode 100644 src/core/hle/service/set/system_settings.cpp create mode 100644 src/core/hle/service/set/system_settings.h diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 47d028d48..ba3081efb 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -123,6 +123,12 @@ namespace Common { return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; } +[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g, + char h) { + return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 | + u64(g) << 48 | u64(h) << 56; +} + // std::size() does not support zero-size C arrays. We're fixing that. template constexpr auto Size(const C& c) -> decltype(c.size()) { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7b9ed856f..1ff90bbaf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -784,6 +784,12 @@ add_library(core STATIC hle/service/service.h hle/service/set/set.cpp hle/service/set/set.h + hle/service/set/appln_settings.cpp + hle/service/set/appln_settings.h + hle/service/set/device_settings.cpp + hle/service/set/device_settings.h + hle/service/set/private_settings.cpp + hle/service/set/private_settings.h hle/service/set/set_cal.cpp hle/service/set/set_cal.h hle/service/set/set_fd.cpp @@ -792,6 +798,8 @@ add_library(core STATIC hle/service/set/set_sys.h hle/service/set/settings.cpp hle/service/set/settings.h + hle/service/set/system_settings.cpp + hle/service/set/system_settings.h hle/service/sm/sm.cpp hle/service/sm/sm.h hle/service/sm/sm_controller.cpp diff --git a/src/core/hle/service/set/appln_settings.cpp b/src/core/hle/service/set/appln_settings.cpp new file mode 100644 index 000000000..a5d802757 --- /dev/null +++ b/src/core/hle/service/set/appln_settings.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/appln_settings.h" + +namespace Service::Set { + +ApplnSettings DefaultApplnSettings() { + return {}; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/appln_settings.h b/src/core/hle/service/set/appln_settings.h new file mode 100644 index 000000000..b07df0ee7 --- /dev/null +++ b/src/core/hle/service/set/appln_settings.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Service::Set { +struct ApplnSettings { + std::array reserved_000; + + // nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0 + std::array mii_author_id; + + std::array reserved_020; + + // nn::settings::system::ServiceDiscoveryControlSettings + std::array service_discovery_control_settings; + + std::array reserved_054; + + bool in_repair_process_enable_flag; + + std::array pad_075; +}; +static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10); +static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50); +static_assert(offsetof(ApplnSettings, in_repair_process_enable_flag) == 0x74); +static_assert(sizeof(ApplnSettings) == 0x78, "ApplnSettings has the wrong size!"); + +ApplnSettings DefaultApplnSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/device_settings.cpp b/src/core/hle/service/set/device_settings.cpp new file mode 100644 index 000000000..e423ce38a --- /dev/null +++ b/src/core/hle/service/set/device_settings.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/device_settings.h" + +namespace Service::Set { + +DeviceSettings DefaultDeviceSettings() { + return {}; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/device_settings.h b/src/core/hle/service/set/device_settings.h new file mode 100644 index 000000000..b6cfe04f2 --- /dev/null +++ b/src/core/hle/service/set/device_settings.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Service::Set { +struct DeviceSettings { + std::array reserved_000; + + // nn::settings::BatteryLot + std::array ptm_battery_lot; + // nn::settings::system::PtmFuelGaugeParameter + std::array ptm_fuel_gauge_parameter; + u8 ptm_battery_version; + // nn::settings::system::PtmCycleCountReliability + u32 ptm_cycle_count_reliability; + + std::array reserved_048; + + // nn::settings::system::AnalogStickUserCalibration L + std::array analog_user_stick_calibration_l; + // nn::settings::system::AnalogStickUserCalibration R + std::array analog_user_stick_calibration_r; + + std::array reserved_0B0; + + // nn::settings::system::ConsoleSixAxisSensorAccelerationBias + std::array console_six_axis_sensor_acceleration_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias + std::array console_six_axis_sensor_angular_velocity_bias; + // nn::settings::system::ConsoleSixAxisSensorAccelerationGain + std::array console_six_axis_sensor_acceleration_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain + std::array console_six_axis_sensor_angular_velocity_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias + std::array console_six_axis_sensor_angular_velocity_time_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration + std::array console_six_axis_sensor_angular_acceleration; +}; +static_assert(offsetof(DeviceSettings, ptm_battery_lot) == 0x10); +static_assert(offsetof(DeviceSettings, ptm_cycle_count_reliability) == 0x44); +static_assert(offsetof(DeviceSettings, analog_user_stick_calibration_l) == 0x90); +static_assert(offsetof(DeviceSettings, console_six_axis_sensor_acceleration_bias) == 0xD0); +static_assert(offsetof(DeviceSettings, console_six_axis_sensor_angular_acceleration) == 0x13C); +static_assert(sizeof(DeviceSettings) == 0x160, "DeviceSettings has the wrong size!"); + +DeviceSettings DefaultDeviceSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/private_settings.cpp b/src/core/hle/service/set/private_settings.cpp new file mode 100644 index 000000000..70bf65727 --- /dev/null +++ b/src/core/hle/service/set/private_settings.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/private_settings.h" + +namespace Service::Set { + +PrivateSettings DefaultPrivateSettings() { + return {}; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h new file mode 100644 index 000000000..b63eaf45c --- /dev/null +++ b/src/core/hle/service/set/private_settings.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/uuid.h" +#include "core/hle/service/time/clock_types.h" + +namespace Service::Set { + +/// This is nn::settings::system::InitialLaunchFlag +struct InitialLaunchFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> InitialLaunchCompletionFlag; + BitField<8, 1, u32> InitialLaunchUserAdditionFlag; + BitField<16, 1, u32> InitialLaunchTimestampFlag; + }; +}; +static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size"); + +/// This is nn::settings::system::InitialLaunchSettings +struct InitialLaunchSettings { + InitialLaunchFlag flags; + INSERT_PADDING_BYTES(0x4); + Service::Time::Clock::SteadyClockTimePoint timestamp; +}; +static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); + +#pragma pack(push, 4) +struct InitialLaunchSettingsPacked { + InitialLaunchFlag flags; + Service::Time::Clock::SteadyClockTimePoint timestamp; +}; +#pragma pack(pop) +static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, + "InitialLaunchSettingsPacked is incorrect size"); + +struct PrivateSettings { + std::array reserved_00; + + // nn::settings::system::InitialLaunchSettings + InitialLaunchSettings initial_launch_settings; + + std::array reserved_30; + + Common::UUID external_clock_source_id; + s64 shutdown_rtc_value; + s64 external_steady_clock_internal_offset; + + std::array reserved_70; + + // nn::settings::system::PlatformRegion + std::array platform_region; + + std::array reserved_D4; +}; +static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10); +static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50); +static_assert(offsetof(PrivateSettings, reserved_70) == 0x70); +static_assert(offsetof(PrivateSettings, platform_region) == 0xD0); +static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!"); + +PrivateSettings DefaultPrivateSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index b61a3560d..6ef3da410 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -4,35 +4,13 @@ #pragma once #include "core/hle/service/service.h" +#include "core/hle/service/set/system_settings.h" namespace Core { class System; } namespace Service::Set { - -/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64. -enum class LanguageCode : u64 { - JA = 0x000000000000616A, - EN_US = 0x00000053552D6E65, - FR = 0x0000000000007266, - DE = 0x0000000000006564, - IT = 0x0000000000007469, - ES = 0x0000000000007365, - ZH_CN = 0x0000004E432D687A, - KO = 0x0000000000006F6B, - NL = 0x0000000000006C6E, - PT = 0x0000000000007470, - RU = 0x0000000000007572, - ZH_TW = 0x00000057542D687A, - EN_GB = 0x00000042472D6E65, - FR_CA = 0x00000041432D7266, - ES_419 = 0x00003931342D7365, - ZH_HANS = 0x00736E61482D687A, - ZH_HANT = 0x00746E61482D687A, - PT_BR = 0x00000052422D7470, -}; - enum class KeyboardLayout : u64 { Japanese = 0, EnglishUs = 1, diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 48304e6d1..0653779d5 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -1,7 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "common/logging/log.h" #include "common/settings.h" #include "common/string_util.h" @@ -19,6 +24,16 @@ namespace Service::Set { +namespace { +constexpr u32 SETTINGS_VERSION{1u}; +constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); +struct SettingsHeader { + u64 magic; + u32 version; + u32 reserved; +}; +} // Anonymous namespace + Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, GetFirmwareVersionType type) { constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; @@ -72,11 +87,120 @@ Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& return ResultSuccess; } +bool SET_SYS::LoadSettingsFile(std::filesystem::path& path, auto&& default_func) { + using settings_type = decltype(default_func()); + + if (!Common::FS::CreateDirs(path)) { + return false; + } + + auto settings_file = path / "settings.dat"; + auto exists = std::filesystem::exists(settings_file); + auto file_size_ok = exists && std::filesystem::file_size(settings_file) == + sizeof(SettingsHeader) + sizeof(settings_type); + + auto ResetToDefault = [&]() { + auto default_settings{default_func()}; + + SettingsHeader hdr{ + .magic = SETTINGS_MAGIC, + .version = SETTINGS_VERSION, + .reserved = 0u, + }; + + std::ofstream out_settings_file(settings_file, std::ios::out | std::ios::binary); + out_settings_file.write(reinterpret_cast(&hdr), sizeof(hdr)); + out_settings_file.write(reinterpret_cast(&default_settings), + sizeof(settings_type)); + out_settings_file.flush(); + out_settings_file.close(); + }; + + constexpr auto IsHeaderValid = [](std::ifstream& file) -> bool { + if (!file.is_open()) { + return false; + } + SettingsHeader hdr{}; + file.read(reinterpret_cast(&hdr), sizeof(hdr)); + return hdr.magic == SETTINGS_MAGIC && hdr.version == SETTINGS_VERSION; + }; + + if (!exists || !file_size_ok) { + ResetToDefault(); + } + + std::ifstream file(settings_file, std::ios::binary | std::ios::in); + if (!IsHeaderValid(file)) { + file.close(); + ResetToDefault(); + file = std::ifstream(settings_file, std::ios::binary | std::ios::in); + if (!IsHeaderValid(file)) { + return false; + } + } + + if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_private_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_device_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_appln_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_system_settings), sizeof(settings_type)); + } else { + UNREACHABLE(); + } + file.close(); + + return true; +} + +bool SET_SYS::StoreSettingsFile(std::filesystem::path& path, auto& settings) { + using settings_type = std::decay_t; + + if (!Common::FS::IsDir(path)) { + return false; + } + + auto settings_base = path / "settings"; + auto settings_tmp_file = settings_base; + settings_tmp_file = settings_tmp_file.replace_extension("tmp"); + std::ofstream file(settings_tmp_file, std::ios::binary | std::ios::out); + if (!file.is_open()) { + return false; + } + + SettingsHeader hdr{ + .magic = SETTINGS_MAGIC, + .version = SETTINGS_VERSION, + .reserved = 0u, + }; + file.write(reinterpret_cast(&hdr), sizeof(hdr)); + + if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_private_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_device_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_appln_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_system_settings), sizeof(settings_type)); + } else { + UNREACHABLE(); + } + file.close(); + + std::filesystem::rename(settings_tmp_file, settings_base.replace_extension("dat")); + + return true; +} + void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - language_code_setting = rp.PopEnum(); + m_system_settings.language_code = rp.PopEnum(); + SetSaveNeeded(); - LOG_INFO(Service_SET, "called, language_code={}", language_code_setting); + LOG_INFO(Service_SET, "called, language_code={}", m_system_settings.language_code); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -112,19 +236,68 @@ void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { rb.Push(result); } +void SET_SYS::GetExternalSteadyClockSourceId(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Common::UUID id{}; + auto res = GetExternalSteadyClockSourceId(id); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(id); +} + +void SET_SYS::SetExternalSteadyClockSourceId(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto id{rp.PopRaw()}; + + auto res = SetExternalSteadyClockSourceId(id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SET_SYS::GetUserSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Service::Time::Clock::SystemClockContext context{}; + auto res = GetUserSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, + 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(context); +} + +void SET_SYS::SetUserSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + auto res = SetUserSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw(account_settings); + rb.PushRaw(m_system_settings.account_settings); } void SET_SYS::SetAccountSettings(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - account_settings = rp.PopRaw(); + m_system_settings.account_settings = rp.PopRaw(); + SetSaveNeeded(); - LOG_INFO(Service_SET, "called, account_settings_flags={}", account_settings.flags); + LOG_INFO(Service_SET, "called, account_settings_flags={}", + m_system_settings.account_settings.flags); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -133,11 +306,11 @@ void SET_SYS::SetAccountSettings(HLERequestContext& ctx) { void SET_SYS::GetEulaVersions(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - ctx.WriteBuffer(eula_versions); + ctx.WriteBuffer(m_system_settings.eula_versions); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast(eula_versions.size())); + rb.Push(m_system_settings.eula_version_count); } void SET_SYS::SetEulaVersions(HLERequestContext& ctx) { @@ -145,13 +318,12 @@ void SET_SYS::SetEulaVersions(HLERequestContext& ctx) { const auto buffer_data = ctx.ReadBuffer(); LOG_INFO(Service_SET, "called, elements={}", elements); + ASSERT(elements <= m_system_settings.eula_versions.size()); - eula_versions.resize(elements); - for (std::size_t index = 0; index < elements; index++) { - const std::size_t start_index = index * sizeof(EulaVersion); - memcpy(eula_versions.data() + start_index, buffer_data.data() + start_index, - sizeof(EulaVersion)); - } + m_system_settings.eula_version_count = static_cast(elements); + std::memcpy(&m_system_settings.eula_versions, buffer_data.data(), + sizeof(EulaVersion) * elements); + SetSaveNeeded(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -162,14 +334,15 @@ void SET_SYS::GetColorSetId(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(color_set); + rb.PushEnum(m_system_settings.color_set_id); } void SET_SYS::SetColorSetId(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - color_set = rp.PopEnum(); + m_system_settings.color_set_id = rp.PopEnum(); + SetSaveNeeded(); - LOG_DEBUG(Service_SET, "called, color_set={}", color_set); + LOG_DEBUG(Service_SET, "called, color_set={}", m_system_settings.color_set_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -180,17 +353,21 @@ void SET_SYS::GetNotificationSettings(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 8}; rb.Push(ResultSuccess); - rb.PushRaw(notification_settings); + rb.PushRaw(m_system_settings.notification_settings); } void SET_SYS::SetNotificationSettings(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - notification_settings = rp.PopRaw(); + m_system_settings.notification_settings = rp.PopRaw(); + SetSaveNeeded(); LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}", - notification_settings.flags.raw, notification_settings.volume, - notification_settings.start_time.hour, notification_settings.start_time.minute, - notification_settings.stop_time.hour, notification_settings.stop_time.minute); + m_system_settings.notification_settings.flags.raw, + m_system_settings.notification_settings.volume, + m_system_settings.notification_settings.start_time.hour, + m_system_settings.notification_settings.start_time.minute, + m_system_settings.notification_settings.stop_time.hour, + m_system_settings.notification_settings.stop_time.minute); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -199,11 +376,11 @@ void SET_SYS::SetNotificationSettings(HLERequestContext& ctx) { void SET_SYS::GetAccountNotificationSettings(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - ctx.WriteBuffer(account_notifications); + ctx.WriteBuffer(m_system_settings.account_notification_settings); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast(account_notifications.size())); + rb.Push(m_system_settings.account_notification_settings_count); } void SET_SYS::SetAccountNotificationSettings(HLERequestContext& ctx) { @@ -212,12 +389,12 @@ void SET_SYS::SetAccountNotificationSettings(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, elements={}", elements); - account_notifications.resize(elements); - for (std::size_t index = 0; index < elements; index++) { - const std::size_t start_index = index * sizeof(AccountNotificationSettings); - memcpy(account_notifications.data() + start_index, buffer_data.data() + start_index, - sizeof(AccountNotificationSettings)); - } + ASSERT(elements <= m_system_settings.account_notification_settings.size()); + + m_system_settings.account_notification_settings_count = static_cast(elements); + std::memcpy(&m_system_settings.account_notification_settings, buffer_data.data(), + elements * sizeof(AccountNotificationSettings)); + SetSaveNeeded(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -244,6 +421,14 @@ static Settings GetSettings() { ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); + // Time + ret["time"]["notify_time_to_fs_interval_seconds"] = ToBytes(s32{600}); + ret["time"]["standard_network_clock_sufficient_accuracy_minutes"] = + ToBytes(s32{43200}); // 30 days + ret["time"]["standard_steady_clock_rtc_update_interval_minutes"] = ToBytes(s32{5}); + ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0}); + ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); + return ret; } @@ -273,8 +458,6 @@ void SET_SYS::GetSettingsItemValueSize(HLERequestContext& ctx) { } void SET_SYS::GetSettingsItemValue(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - // The category of the setting. This corresponds to the top-level keys of // system_settings.ini. const auto setting_category_buf{ctx.ReadBuffer(0)}; @@ -285,14 +468,13 @@ void SET_SYS::GetSettingsItemValue(HLERequestContext& ctx) { const auto setting_name_buf{ctx.ReadBuffer(1)}; const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; - auto settings{GetSettings()}; - Result response{ResultUnknown}; + std::vector value; + auto response = GetSettingsItemValue(value, setting_category, setting_name); - if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { - auto setting_value = settings[setting_category][setting_name]; - ctx.WriteBuffer(setting_value.data(), setting_value.size()); - response = ResultSuccess; - } + LOG_INFO(Service_SET, "called. category={}, name={} -- res=0x{:X}", setting_category, + setting_name, response.raw); + + ctx.WriteBuffer(value.data(), value.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(response); @@ -303,19 +485,23 @@ void SET_SYS::GetTvSettings(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 10}; rb.Push(ResultSuccess); - rb.PushRaw(tv_settings); + rb.PushRaw(m_system_settings.tv_settings); } void SET_SYS::SetTvSettings(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - tv_settings = rp.PopRaw(); + m_system_settings.tv_settings = rp.PopRaw(); + SetSaveNeeded(); LOG_INFO(Service_SET, "called, flags={}, cmu_mode={}, constrast_ratio={}, hdmi_content_type={}, " "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}", - tv_settings.flags.raw, tv_settings.cmu_mode, tv_settings.constrast_ratio, - tv_settings.hdmi_content_type, tv_settings.rgb_range, tv_settings.tv_gama, - tv_settings.tv_resolution, tv_settings.tv_underscan); + m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode, + m_system_settings.tv_settings.constrast_ratio, + m_system_settings.tv_settings.hdmi_content_type, + m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama, + m_system_settings.tv_settings.tv_resolution, + m_system_settings.tv_settings.tv_underscan); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -329,16 +515,87 @@ void SET_SYS::GetQuestFlag(HLERequestContext& ctx) { rb.PushEnum(QuestFlag::Retail); } +void SET_SYS::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called"); + + Service::Time::TimeZone::LocationName name{}; + auto res = GetDeviceTimeZoneLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(name); +} + +void SET_SYS::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw()}; + + auto res = SetDeviceTimeZoneLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + void SET_SYS::SetRegionCode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - region_code = rp.PopEnum(); + m_system_settings.region_code = rp.PopEnum(); + SetSaveNeeded(); - LOG_INFO(Service_SET, "called, region_code={}", region_code); + LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } +void SET_SYS::GetNetworkSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Service::Time::Clock::SystemClockContext context{}; + auto res = GetNetworkSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, + 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(context); +} + +void SET_SYS::SetNetworkSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + auto res = SetNetworkSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + bool enabled{}; + auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushRaw(enabled); +} + +void SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto enabled{rp.Pop()}; + + auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + void SET_SYS::GetPrimaryAlbumStorage(HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); @@ -352,16 +609,18 @@ void SET_SYS::GetSleepSettings(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.PushRaw(sleep_settings); + rb.PushRaw(m_system_settings.sleep_settings); } void SET_SYS::SetSleepSettings(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - sleep_settings = rp.PopRaw(); + m_system_settings.sleep_settings = rp.PopRaw(); + SetSaveNeeded(); LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", - sleep_settings.flags.raw, sleep_settings.handheld_sleep_plan, - sleep_settings.console_sleep_plan); + m_system_settings.sleep_settings.flags.raw, + m_system_settings.sleep_settings.handheld_sleep_plan, + m_system_settings.sleep_settings.console_sleep_plan); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -371,15 +630,20 @@ void SET_SYS::GetInitialLaunchSettings(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(ResultSuccess); - rb.PushRaw(launch_settings); + rb.PushRaw(m_system_settings.initial_launch_settings_packed); } void SET_SYS::SetInitialLaunchSettings(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - launch_settings = rp.PopRaw(); + auto inital_launch_settings = rp.PopRaw(); - LOG_INFO(Service_SET, "called, flags={}, timestamp={}", launch_settings.flags.raw, - launch_settings.timestamp.time_point); + m_system_settings.initial_launch_settings_packed.flags = inital_launch_settings.flags; + m_system_settings.initial_launch_settings_packed.timestamp = inital_launch_settings.timestamp; + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, flags={}, timestamp={}", + m_system_settings.initial_launch_settings_packed.flags.raw, + m_system_settings.initial_launch_settings_packed.timestamp.time_point); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -437,13 +701,37 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { u8 battery_percentage_flag{1}; - LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag); + LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", + battery_percentage_flag); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(battery_percentage_flag); } +void SET_SYS::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto offset{rp.Pop()}; + + auto res = SetExternalSteadyClockInternalOffset(offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SET_SYS::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called."); + + s64 offset{}; + auto res = GetExternalSteadyClockInternalOffset(offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(offset); +} + void SET_SYS::GetErrorReportSharePermission(HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); @@ -453,18 +741,19 @@ void SET_SYS::GetErrorReportSharePermission(HLERequestContext& ctx) { } void SET_SYS::GetAppletLaunchFlags(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called, applet_launch_flag={}", applet_launch_flag); + LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(applet_launch_flag); + rb.Push(m_system_settings.applet_launch_flag); } void SET_SYS::SetAppletLaunchFlags(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - applet_launch_flag = rp.Pop(); + m_system_settings.applet_launch_flag = rp.Pop(); + SetSaveNeeded(); - LOG_INFO(Service_SET, "called, applet_launch_flag={}", applet_launch_flag); + LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -489,6 +778,52 @@ void SET_SYS::GetKeyboardLayout(HLERequestContext& ctx) { rb.Push(static_cast(selected_keyboard_layout)); } +void SET_SYS::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + Service::Time::Clock::SteadyClockTimePoint time_point{}; + auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void SET_SYS::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto time_point{rp.PopRaw()}; + + auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + Service::Time::Clock::SteadyClockTimePoint time_point{}; + auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto time_point{rp.PopRaw()}; + + auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); @@ -508,7 +843,7 @@ void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) { .extra = 0xFF000000, }; - IPC::ResponseBuilder rb{ctx, 7}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(HomeMenuScheme) / sizeof(u32)}; rb.Push(ResultSuccess); rb.PushRaw(default_color); } @@ -520,6 +855,7 @@ void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) { rb.Push(ResultSuccess); rb.Push(0); } + void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); @@ -528,7 +864,7 @@ void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { rb.Push(false); } -SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { +SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"}, m_system{system} { // clang-format off static const FunctionInfo functions[] = { {0, &SET_SYS::SetLanguageCode, "SetLanguageCode"}, @@ -543,10 +879,10 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {10, nullptr, "SetBacklightSettings"}, {11, nullptr, "SetBluetoothDevicesSettings"}, {12, nullptr, "GetBluetoothDevicesSettings"}, - {13, nullptr, "GetExternalSteadyClockSourceId"}, - {14, nullptr, "SetExternalSteadyClockSourceId"}, - {15, nullptr, "GetUserSystemClockContext"}, - {16, nullptr, "SetUserSystemClockContext"}, + {13, &SET_SYS::GetExternalSteadyClockSourceId, "GetExternalSteadyClockSourceId"}, + {14, &SET_SYS::SetExternalSteadyClockSourceId, "SetExternalSteadyClockSourceId"}, + {15, &SET_SYS::GetUserSystemClockContext, "GetUserSystemClockContext"}, + {16, &SET_SYS::SetUserSystemClockContext, "SetUserSystemClockContext"}, {17, &SET_SYS::GetAccountSettings, "GetAccountSettings"}, {18, &SET_SYS::SetAccountSettings, "SetAccountSettings"}, {19, nullptr, "GetAudioVolume"}, @@ -581,15 +917,15 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {50, nullptr, "SetDataDeletionSettings"}, {51, nullptr, "GetInitialSystemAppletProgramId"}, {52, nullptr, "GetOverlayDispProgramId"}, - {53, nullptr, "GetDeviceTimeZoneLocationName"}, - {54, nullptr, "SetDeviceTimeZoneLocationName"}, + {53, &SET_SYS::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"}, + {54, &SET_SYS::SetDeviceTimeZoneLocationName, "SetDeviceTimeZoneLocationName"}, {55, nullptr, "GetWirelessCertificationFileSize"}, {56, nullptr, "GetWirelessCertificationFile"}, {57, &SET_SYS::SetRegionCode, "SetRegionCode"}, - {58, nullptr, "GetNetworkSystemClockContext"}, - {59, nullptr, "SetNetworkSystemClockContext"}, - {60, nullptr, "IsUserSystemClockAutomaticCorrectionEnabled"}, - {61, nullptr, "SetUserSystemClockAutomaticCorrectionEnabled"}, + {58, &SET_SYS::GetNetworkSystemClockContext, "GetNetworkSystemClockContext"}, + {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"}, + {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"}, + {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, {62, nullptr, "GetDebugModeFlag"}, {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, {64, nullptr, "SetPrimaryAlbumStorage"}, @@ -633,8 +969,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {102, nullptr, "SetExternalRtcResetFlag"}, {103, nullptr, "GetUsbFullKeyEnableFlag"}, {104, nullptr, "SetUsbFullKeyEnableFlag"}, - {105, nullptr, "SetExternalSteadyClockInternalOffset"}, - {106, nullptr, "GetExternalSteadyClockInternalOffset"}, + {105, &SET_SYS::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"}, + {106, &SET_SYS::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"}, {107, nullptr, "GetBacklightSettingsEx"}, {108, nullptr, "SetBacklightSettingsEx"}, {109, nullptr, "GetHeadphoneVolumeWarningCount"}, @@ -678,10 +1014,10 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, {149, nullptr, "GetRebootlessSystemUpdateVersion"}, - {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"}, - {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"}, - {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, - {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {150, &SET_SYS::GetDeviceTimeZoneLocationUpdatedTime, "GetDeviceTimeZoneLocationUpdatedTime"}, + {151, &SET_SYS::SetDeviceTimeZoneLocationUpdatedTime, "SetDeviceTimeZoneLocationUpdatedTime"}, + {152, &SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {153, &SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, {154, nullptr, "GetAccountOnlineStorageSettings"}, {155, nullptr, "SetAccountOnlineStorageSettings"}, {156, nullptr, "GetPctlReadyFlag"}, @@ -743,8 +1079,184 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { // clang-format on RegisterHandlers(functions); + + SetupSettings(); + m_save_thread = + std::jthread([this](std::stop_token stop_token) { StoreSettingsThreadFunc(stop_token); }); } -SET_SYS::~SET_SYS() = default; +SET_SYS::~SET_SYS() { + SetSaveNeeded(); + m_save_thread.request_stop(); +} + +void SET_SYS::SetupSettings() { + auto system_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; + if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) { + ASSERT(false); + } + + auto private_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; + if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) { + ASSERT(false); + } + + auto device_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; + if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) { + ASSERT(false); + } + + auto appln_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; + if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) { + ASSERT(false); + } +} + +void SET_SYS::StoreSettings() { + auto system_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; + if (!StoreSettingsFile(system_dir, m_system_settings)) { + LOG_ERROR(HW_GPU, "Failed to store System settings"); + } + + auto private_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; + if (!StoreSettingsFile(private_dir, m_private_settings)) { + LOG_ERROR(HW_GPU, "Failed to store Private settings"); + } + + auto device_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; + if (!StoreSettingsFile(device_dir, m_device_settings)) { + LOG_ERROR(HW_GPU, "Failed to store Device settings"); + } + + auto appln_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; + if (!StoreSettingsFile(appln_dir, m_appln_settings)) { + LOG_ERROR(HW_GPU, "Failed to store ApplLn settings"); + } +} + +void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) { + while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) { + std::scoped_lock l{m_save_needed_mutex}; + if (!std::exchange(m_save_needed, false)) { + continue; + } + StoreSettings(); + } +} + +void SET_SYS::SetSaveNeeded() { + std::scoped_lock l{m_save_needed_mutex}; + m_save_needed = true; +} + +Result SET_SYS::GetSettingsItemValue(std::vector& out_value, const std::string& category, + const std::string& name) { + auto settings{GetSettings()}; + R_UNLESS(settings.contains(category) && settings[category].contains(name), ResultUnknown); + + out_value = settings[category][name]; + R_SUCCEED(); +} + +Result SET_SYS::GetExternalSteadyClockSourceId(Common::UUID& out_id) { + out_id = m_private_settings.external_clock_source_id; + R_SUCCEED(); +} + +Result SET_SYS::SetExternalSteadyClockSourceId(Common::UUID id) { + m_private_settings.external_clock_source_id = id; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context) { + out_context = m_system_settings.user_system_clock_context; + R_SUCCEED(); +} + +Result SET_SYS::SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context) { + m_system_settings.user_system_clock_context = context; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name) { + out_name = m_system_settings.device_time_zone_location_name; + R_SUCCEED(); +} + +Result SET_SYS::SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name) { + m_system_settings.device_time_zone_location_name = name; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::GetNetworkSystemClockContext( + Service::Time::Clock::SystemClockContext& out_context) { + out_context = m_system_settings.network_system_clock_context; + R_SUCCEED(); +} + +Result SET_SYS::SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context) { + m_system_settings.network_system_clock_context = context; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) { + out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; + R_SUCCEED(); +} + +Result SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled(bool enabled) { + m_system_settings.user_system_clock_automatic_correction_enabled = enabled; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::SetExternalSteadyClockInternalOffset(s64 offset) { + m_private_settings.external_steady_clock_internal_offset = offset; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::GetExternalSteadyClockInternalOffset(s64& out_offset) { + out_offset = m_private_settings.external_steady_clock_internal_offset; + R_SUCCEED(); +} + +Result SET_SYS::GetDeviceTimeZoneLocationUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint& out_time_point) { + out_time_point = m_system_settings.device_time_zone_location_updated_time; + R_SUCCEED(); +} + +Result SET_SYS::SetDeviceTimeZoneLocationUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint& time_point) { + m_system_settings.device_time_zone_location_updated_time = time_point; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint& out_time_point) { + out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; + R_SUCCEED(); +} + +Result SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint out_time_point) { + m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; + SetSaveNeeded(); + R_SUCCEED(); +} } // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 5f770fd32..3785d93d8 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -3,17 +3,27 @@ #pragma once +#include +#include +#include +#include + +#include "common/polyfill_thread.h" #include "common/uuid.h" #include "core/hle/result.h" #include "core/hle/service/service.h" +#include "core/hle/service/set/appln_settings.h" +#include "core/hle/service/set/device_settings.h" +#include "core/hle/service/set/private_settings.h" +#include "core/hle/service/set/system_settings.h" #include "core/hle/service/time/clock_types.h" +#include "core/hle/service/time/time_zone_types.h" namespace Core { class System; } namespace Service::Set { -enum class LanguageCode : u64; enum class GetFirmwareVersionType { Version1, Version2, @@ -42,270 +52,38 @@ public: explicit SET_SYS(Core::System& system_); ~SET_SYS() override; + Result GetSettingsItemValue(std::vector& out_value, const std::string& category, + const std::string& name); + + Result GetExternalSteadyClockSourceId(Common::UUID& out_id); + Result SetExternalSteadyClockSourceId(Common::UUID id); + Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); + Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context); + Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name); + Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name); + Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); + Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context); + Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); + Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); + Result SetExternalSteadyClockInternalOffset(s64 offset); + Result GetExternalSteadyClockInternalOffset(s64& out_offset); + Result GetDeviceTimeZoneLocationUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint& out_time_point); + Result SetDeviceTimeZoneLocationUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint& time_point); + Result GetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint& out_time_point); + Result SetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::Time::Clock::SteadyClockTimePoint time_point); + private: - /// Indicates the current theme set by the system settings - enum class ColorSet : u32 { - BasicWhite = 0, - BasicBlack = 1, - }; - - /// Indicates the current console is a retail or kiosk unit - enum class QuestFlag : u8 { - Retail = 0, - Kiosk = 1, - }; - - /// This is nn::settings::system::TvResolution - enum class TvResolution : u32 { - Auto, - Resolution1080p, - Resolution720p, - Resolution480p, - }; - - /// This is nn::settings::system::HdmiContentType - enum class HdmiContentType : u32 { - None, - Graphics, - Cinema, - Photo, - Game, - }; - - /// This is nn::settings::system::RgbRange - enum class RgbRange : u32 { - Auto, - Full, - Limited, - }; - - /// This is nn::settings::system::CmuMode - enum class CmuMode : u32 { - None, - ColorInvert, - HighContrast, - GrayScale, - }; - - /// This is nn::settings::system::PrimaryAlbumStorage - enum class PrimaryAlbumStorage : u32 { - Nand, - SdCard, - }; - - /// This is nn::settings::system::NotificationVolume - enum class NotificationVolume : u32 { - Mute, - Low, - High, - }; - - /// This is nn::settings::system::ChineseTraditionalInputMethod - enum class ChineseTraditionalInputMethod : u32 { - Unknown0 = 0, - Unknown1 = 1, - Unknown2 = 2, - }; - - /// This is nn::settings::system::ErrorReportSharePermission - enum class ErrorReportSharePermission : u32 { - NotConfirmed, - Granted, - Denied, - }; - - /// This is nn::settings::system::FriendPresenceOverlayPermission - enum class FriendPresenceOverlayPermission : u8 { - NotConfirmed, - NoDisplay, - FavoriteFriends, - Friends, - }; - - /// This is nn::settings::system::HandheldSleepPlan - enum class HandheldSleepPlan : u32 { - Sleep1Min, - Sleep3Min, - Sleep5Min, - Sleep10Min, - Sleep30Min, - Never, - }; - - /// This is nn::settings::system::ConsoleSleepPlan - enum class ConsoleSleepPlan : u32 { - Sleep1Hour, - Sleep2Hour, - Sleep3Hour, - Sleep6Hour, - Sleep12Hour, - Never, - }; - - /// This is nn::settings::system::RegionCode - enum class RegionCode : u32 { - Japan, - Usa, - Europe, - Australia, - HongKongTaiwanKorea, - China, - }; - - /// This is nn::settings::system::EulaVersionClockType - enum class EulaVersionClockType : u32 { - NetworkSystemClock, - SteadyClock, - }; - - /// This is nn::settings::system::SleepFlag - struct SleepFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> SleepsWhilePlayingMedia; - BitField<1, 1, u32> WakesAtPowerStateChange; - }; - }; - static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size"); - - /// This is nn::settings::system::TvFlag - struct TvFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> Allows4k; - BitField<1, 1, u32> Allows3d; - BitField<2, 1, u32> AllowsCec; - BitField<3, 1, u32> PreventsScreenBurnIn; - }; - }; - static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size"); - - /// This is nn::settings::system::InitialLaunchFlag - struct InitialLaunchFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> InitialLaunchCompletionFlag; - BitField<8, 1, u32> InitialLaunchUserAdditionFlag; - BitField<16, 1, u32> InitialLaunchTimestampFlag; - }; - }; - static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size"); - - /// This is nn::settings::system::NotificationFlag - struct NotificationFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> RingtoneFlag; - BitField<1, 1, u32> DownloadCompletionFlag; - BitField<8, 1, u32> EnablesNews; - BitField<9, 1, u32> IncomingLampFlag; - }; - }; - static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); - - /// This is nn::settings::system::AccountNotificationFlag - struct AccountNotificationFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> FriendOnlineFlag; - BitField<1, 1, u32> FriendRequestFlag; - BitField<8, 1, u32> CoralInvitationFlag; - }; - }; - static_assert(sizeof(AccountNotificationFlag) == 4, - "AccountNotificationFlag is an invalid size"); - - /// This is nn::settings::system::TvSettings - struct TvSettings { - TvFlag flags; - TvResolution tv_resolution; - HdmiContentType hdmi_content_type; - RgbRange rgb_range; - CmuMode cmu_mode; - u32 tv_underscan; - f32 tv_gama; - f32 constrast_ratio; - }; - static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size"); - - /// This is nn::settings::system::NotificationTime - struct NotificationTime { - u32 hour; - u32 minute; - }; - static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size"); - - /// This is nn::settings::system::NotificationSettings - struct NotificationSettings { - NotificationFlag flags; - NotificationVolume volume; - NotificationTime start_time; - NotificationTime stop_time; - }; - static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size"); - - /// This is nn::settings::system::AccountSettings - struct AccountSettings { - u32 flags; - }; - static_assert(sizeof(AccountSettings) == 0x4, "AccountSettings is an invalid size"); - - /// This is nn::settings::system::AccountNotificationSettings - struct AccountNotificationSettings { - Common::UUID uid; - AccountNotificationFlag flags; - FriendPresenceOverlayPermission friend_presence_permission; - FriendPresenceOverlayPermission friend_invitation_permission; - INSERT_PADDING_BYTES(0x2); - }; - static_assert(sizeof(AccountNotificationSettings) == 0x18, - "AccountNotificationSettings is an invalid size"); - - /// This is nn::settings::system::InitialLaunchSettings - struct SleepSettings { - SleepFlag flags; - HandheldSleepPlan handheld_sleep_plan; - ConsoleSleepPlan console_sleep_plan; - }; - static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size"); - - /// This is nn::settings::system::InitialLaunchSettings - struct InitialLaunchSettings { - InitialLaunchFlag flags; - INSERT_PADDING_BYTES(0x4); - Time::Clock::SteadyClockTimePoint timestamp; - }; - static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); - - /// This is nn::settings::system::InitialLaunchSettings - struct EulaVersion { - u32 version; - RegionCode region_code; - EulaVersionClockType clock_type; - INSERT_PADDING_BYTES(0x4); - s64 posix_time; - Time::Clock::SteadyClockTimePoint timestamp; - }; - static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); - - /// This is nn::settings::system::HomeMenuScheme - struct HomeMenuScheme { - u32 main; - u32 back; - u32 sub; - u32 bezel; - u32 extra; - }; - static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"); - void SetLanguageCode(HLERequestContext& ctx); void GetFirmwareVersion(HLERequestContext& ctx); void GetFirmwareVersion2(HLERequestContext& ctx); + void GetExternalSteadyClockSourceId(HLERequestContext& ctx); + void SetExternalSteadyClockSourceId(HLERequestContext& ctx); + void GetUserSystemClockContext(HLERequestContext& ctx); + void SetUserSystemClockContext(HLERequestContext& ctx); void GetAccountSettings(HLERequestContext& ctx); void SetAccountSettings(HLERequestContext& ctx); void GetEulaVersions(HLERequestContext& ctx); @@ -321,7 +99,13 @@ private: void GetTvSettings(HLERequestContext& ctx); void SetTvSettings(HLERequestContext& ctx); void GetQuestFlag(HLERequestContext& ctx); + void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); + void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); void SetRegionCode(HLERequestContext& ctx); + void GetNetworkSystemClockContext(HLERequestContext& ctx); + void SetNetworkSystemClockContext(HLERequestContext& ctx); + void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); void GetPrimaryAlbumStorage(HLERequestContext& ctx); void GetSleepSettings(HLERequestContext& ctx); void SetSleepSettings(HLERequestContext& ctx); @@ -333,59 +117,36 @@ private: void GetMiiAuthorId(HLERequestContext& ctx); void GetAutoUpdateEnableFlag(HLERequestContext& ctx); void GetBatteryPercentageFlag(HLERequestContext& ctx); + void SetExternalSteadyClockInternalOffset(HLERequestContext& ctx); + void GetExternalSteadyClockInternalOffset(HLERequestContext& ctx); void GetErrorReportSharePermission(HLERequestContext& ctx); void GetAppletLaunchFlags(HLERequestContext& ctx); void SetAppletLaunchFlags(HLERequestContext& ctx); void GetKeyboardLayout(HLERequestContext& ctx); + void GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx); + void SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx); + void GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); void GetChineseTraditionalInputMethod(HLERequestContext& ctx); - void GetFieldTestingFlag(HLERequestContext& ctx); void GetHomeMenuScheme(HLERequestContext& ctx); void GetHomeMenuSchemeModel(HLERequestContext& ctx); + void GetFieldTestingFlag(HLERequestContext& ctx); - AccountSettings account_settings{ - .flags = {}, - }; + bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func); + bool StoreSettingsFile(std::filesystem::path& path, auto& settings); + void SetupSettings(); + void StoreSettings(); + void StoreSettingsThreadFunc(std::stop_token stop_token); + void SetSaveNeeded(); - ColorSet color_set = ColorSet::BasicWhite; - - NotificationSettings notification_settings{ - .flags = {0x300}, - .volume = NotificationVolume::High, - .start_time = {.hour = 9, .minute = 0}, - .stop_time = {.hour = 21, .minute = 0}, - }; - - std::vector account_notifications{}; - - TvSettings tv_settings{ - .flags = {0xc}, - .tv_resolution = TvResolution::Auto, - .hdmi_content_type = HdmiContentType::Game, - .rgb_range = RgbRange::Auto, - .cmu_mode = CmuMode::None, - .tv_underscan = {}, - .tv_gama = 1.0f, - .constrast_ratio = 0.5f, - }; - - InitialLaunchSettings launch_settings{ - .flags = {0x10001}, - .timestamp = {}, - }; - - SleepSettings sleep_settings{ - .flags = {0x3}, - .handheld_sleep_plan = HandheldSleepPlan::Sleep10Min, - .console_sleep_plan = ConsoleSleepPlan::Sleep1Hour, - }; - - u32 applet_launch_flag{}; - - std::vector eula_versions{}; - - RegionCode region_code; - - LanguageCode language_code_setting; + Core::System& m_system; + SystemSettings m_system_settings{}; + PrivateSettings m_private_settings{}; + DeviceSettings m_device_settings{}; + ApplnSettings m_appln_settings{}; + std::jthread m_save_thread; + std::mutex m_save_needed_mutex; + bool m_save_needed{false}; }; } // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/system_settings.cpp new file mode 100644 index 000000000..2723417ad --- /dev/null +++ b/src/core/hle/service/set/system_settings.cpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/system_settings.h" + +namespace Service::Set { + +SystemSettings DefaultSystemSettings() { + SystemSettings settings{}; + + settings.version = 0x140000; + settings.flags = 7; + + settings.color_set_id = ColorSet::BasicWhite; + + settings.notification_settings = { + .flags{0x300}, + .volume = NotificationVolume::High, + .start_time = {.hour = 9, .minute = 0}, + .stop_time = {.hour = 21, .minute = 0}, + }; + + settings.tv_settings = { + .flags = {0xC}, + .tv_resolution = TvResolution::Auto, + .hdmi_content_type = HdmiContentType::Game, + .rgb_range = RgbRange::Auto, + .cmu_mode = CmuMode::None, + .tv_underscan = {}, + .tv_gama = 1.0f, + .constrast_ratio = 0.5f, + }; + + settings.initial_launch_settings_packed = { + .flags = {0x10001}, + .timestamp = {}, + }; + + settings.sleep_settings = { + .flags = {0x3}, + .handheld_sleep_plan = HandheldSleepPlan::Sleep10Min, + .console_sleep_plan = ConsoleSleepPlan::Sleep1Hour, + }; + + settings.device_time_zone_location_name = {"UTC"}; + settings.user_system_clock_automatic_correction_enabled = false; + + return settings; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/system_settings.h new file mode 100644 index 000000000..ded2906ad --- /dev/null +++ b/src/core/hle/service/set/system_settings.h @@ -0,0 +1,699 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/set/private_settings.h" +#include "core/hle/service/time/clock_types.h" + +namespace Service::Set { + +/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64. +enum class LanguageCode : u64 { + JA = 0x000000000000616A, + EN_US = 0x00000053552D6E65, + FR = 0x0000000000007266, + DE = 0x0000000000006564, + IT = 0x0000000000007469, + ES = 0x0000000000007365, + ZH_CN = 0x0000004E432D687A, + KO = 0x0000000000006F6B, + NL = 0x0000000000006C6E, + PT = 0x0000000000007470, + RU = 0x0000000000007572, + ZH_TW = 0x00000057542D687A, + EN_GB = 0x00000042472D6E65, + FR_CA = 0x00000041432D7266, + ES_419 = 0x00003931342D7365, + ZH_HANS = 0x00736E61482D687A, + ZH_HANT = 0x00746E61482D687A, + PT_BR = 0x00000052422D7470, +}; + +/// This is nn::settings::system::ErrorReportSharePermission +enum class ErrorReportSharePermission : u32 { + NotConfirmed, + Granted, + Denied, +}; + +/// This is nn::settings::system::ChineseTraditionalInputMethod +enum class ChineseTraditionalInputMethod : u32 { + Unknown0 = 0, + Unknown1 = 1, + Unknown2 = 2, +}; + +/// This is nn::settings::system::HomeMenuScheme +struct HomeMenuScheme { + u32 main; + u32 back; + u32 sub; + u32 bezel; + u32 extra; +}; +static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"); + +/// Indicates the current theme set by the system settings +enum class ColorSet : u32 { + BasicWhite = 0, + BasicBlack = 1, +}; + +/// Indicates the current console is a retail or kiosk unit +enum class QuestFlag : u8 { + Retail = 0, + Kiosk = 1, +}; + +/// This is nn::settings::system::RegionCode +enum class RegionCode : u32 { + Japan, + Usa, + Europe, + Australia, + HongKongTaiwanKorea, + China, +}; + +/// This is nn::settings::system::AccountSettings +struct AccountSettings { + u32 flags; +}; +static_assert(sizeof(AccountSettings) == 4, "AccountSettings is an invalid size"); + +/// This is nn::settings::system::NotificationVolume +enum class NotificationVolume : u32 { + Mute, + Low, + High, +}; + +/// This is nn::settings::system::NotificationFlag +struct NotificationFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> RingtoneFlag; + BitField<1, 1, u32> DownloadCompletionFlag; + BitField<8, 1, u32> EnablesNews; + BitField<9, 1, u32> IncomingLampFlag; + }; +}; +static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); + +/// This is nn::settings::system::NotificationTime +struct NotificationTime { + u32 hour; + u32 minute; +}; +static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size"); + +/// This is nn::settings::system::NotificationSettings +struct NotificationSettings { + NotificationFlag flags; + NotificationVolume volume; + NotificationTime start_time; + NotificationTime stop_time; +}; +static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size"); + +/// This is nn::settings::system::AccountNotificationFlag +struct AccountNotificationFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> FriendOnlineFlag; + BitField<1, 1, u32> FriendRequestFlag; + BitField<8, 1, u32> CoralInvitationFlag; + }; +}; +static_assert(sizeof(AccountNotificationFlag) == 4, "AccountNotificationFlag is an invalid size"); + +/// This is nn::settings::system::FriendPresenceOverlayPermission +enum class FriendPresenceOverlayPermission : u8 { + NotConfirmed, + NoDisplay, + FavoriteFriends, + Friends, +}; + +/// This is nn::settings::system::AccountNotificationSettings +struct AccountNotificationSettings { + Common::UUID uid; + AccountNotificationFlag flags; + FriendPresenceOverlayPermission friend_presence_permission; + FriendPresenceOverlayPermission friend_invitation_permission; + INSERT_PADDING_BYTES(0x2); +}; +static_assert(sizeof(AccountNotificationSettings) == 0x18, + "AccountNotificationSettings is an invalid size"); + +/// This is nn::settings::system::TvFlag +struct TvFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> Allows4k; + BitField<1, 1, u32> Allows3d; + BitField<2, 1, u32> AllowsCec; + BitField<3, 1, u32> PreventsScreenBurnIn; + }; +}; +static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size"); + +/// This is nn::settings::system::TvResolution +enum class TvResolution : u32 { + Auto, + Resolution1080p, + Resolution720p, + Resolution480p, +}; + +/// This is nn::settings::system::HdmiContentType +enum class HdmiContentType : u32 { + None, + Graphics, + Cinema, + Photo, + Game, +}; + +/// This is nn::settings::system::RgbRange +enum class RgbRange : u32 { + Auto, + Full, + Limited, +}; + +/// This is nn::settings::system::CmuMode +enum class CmuMode : u32 { + None, + ColorInvert, + HighContrast, + GrayScale, +}; + +/// This is nn::settings::system::TvSettings +struct TvSettings { + TvFlag flags; + TvResolution tv_resolution; + HdmiContentType hdmi_content_type; + RgbRange rgb_range; + CmuMode cmu_mode; + u32 tv_underscan; + f32 tv_gama; + f32 constrast_ratio; +}; +static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size"); + +/// This is nn::settings::system::PrimaryAlbumStorage +enum class PrimaryAlbumStorage : u32 { + Nand, + SdCard, +}; + +/// This is nn::settings::system::HandheldSleepPlan +enum class HandheldSleepPlan : u32 { + Sleep1Min, + Sleep3Min, + Sleep5Min, + Sleep10Min, + Sleep30Min, + Never, +}; + +/// This is nn::settings::system::ConsoleSleepPlan +enum class ConsoleSleepPlan : u32 { + Sleep1Hour, + Sleep2Hour, + Sleep3Hour, + Sleep6Hour, + Sleep12Hour, + Never, +}; + +/// This is nn::settings::system::SleepFlag +struct SleepFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> SleepsWhilePlayingMedia; + BitField<1, 1, u32> WakesAtPowerStateChange; + }; +}; +static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size"); + +/// This is nn::settings::system::SleepSettings +struct SleepSettings { + SleepFlag flags; + HandheldSleepPlan handheld_sleep_plan; + ConsoleSleepPlan console_sleep_plan; +}; +static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size"); + +/// This is nn::settings::system::EulaVersionClockType +enum class EulaVersionClockType : u32 { + NetworkSystemClock, + SteadyClock, +}; + +/// This is nn::settings::system::EulaVersion +struct EulaVersion { + u32 version; + RegionCode region_code; + EulaVersionClockType clock_type; + INSERT_PADDING_BYTES(0x4); + s64 posix_time; + Time::Clock::SteadyClockTimePoint timestamp; +}; +static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); + +struct SystemSettings { + // 0/unwritten (1.0.0), 0x20000 (2.0.0), 0x30000 (3.0.0-3.0.1), 0x40001 (4.0.0-4.1.0), 0x50000 + // (5.0.0-5.1.0), 0x60000 (6.0.0-6.2.0), 0x70000 (7.0.0), 0x80000 (8.0.0-8.1.1), 0x90000 + // (9.0.0-10.0.4), 0x100100 (10.1.0+), 0x120000 (12.0.0-12.1.0), 0x130000 (13.0.0-13.2.1), + // 0x140000 (14.0.0+) + u32 version; + // 0/unwritten (1.0.0), 1 (6.0.0-8.1.0), 2 (8.1.1), 7 (9.0.0+). + // if (flags & 2), defaults are written for AnalogStickUserCalibration + u32 flags; + + std::array reserved_00008; + + // nn::settings::LanguageCode + LanguageCode language_code; + + std::array reserved_00018; + + // nn::settings::system::NetworkSettings + u32 network_setting_count; + bool wireless_lan_enable_flag; + std::array pad_00055; + + std::array reserved_00058; + + // nn::settings::system::NetworkSettings + std::array, 32> network_settings_1B0; + + // nn::settings::system::BluetoothDevicesSettings + std::array bluetooth_device_settings_count; + bool bluetooth_enable_flag; + std::array pad_08065; + bool bluetooth_afh_enable_flag; + std::array pad_08069; + bool bluetooth_boost_enable_flag; + std::array pad_0806D; + std::array, 10> bluetooth_device_settings_first_10; + + s32 ldn_channel; + + std::array reserved_09474; + + // nn::util::Uuid MiiAuthorId + std::array mii_author_id; + + std::array reserved_094C0; + + // nn::settings::system::NxControllerSettings + u32 nx_controller_settings_count; + + std::array reserved_094F4; + + // nn::settings::system::NxControllerSettings, + // nn::settings::system::NxControllerLegacySettings on 13.0.0+ + std::array, 10> nx_controller_legacy_settings; + + std::array reserved_09780; + + bool external_rtc_reset_flag; + std::array pad_098F1; + + std::array reserved_098F4; + + s32 push_notification_activity_mode_on_sleep; + + std::array reserved_09934; + + // nn::settings::system::ErrorReportSharePermission + ErrorReportSharePermission error_report_share_permssion; + + std::array reserved_09974; + + // nn::settings::KeyboardLayout + std::array keyboard_layout; + + std::array reserved_099B4; + + bool web_inspector_flag; + std::array pad_099F1; + + // nn::settings::system::AllowedSslHost + u32 allowed_ssl_host_count; + + bool memory_usage_rate_flag; + std::array pad_099F9; + + std::array reserved_099FC; + + // nn::settings::system::HostFsMountPoint + std::array host_fs_mount_point; + + // nn::settings::system::AllowedSslHost + std::array, 8> allowed_ssl_hosts; + + std::array reserved_0A330; + + // nn::settings::system::BlePairingSettings + u32 ble_pairing_settings_count; + std::array reserved_0A9F4; + std::array, 10> ble_pairing_settings; + + // nn::settings::system::AccountOnlineStorageSettings + u32 account_online_storage_settings_count; + std::array reserved_0AF04; + std::array, 8> account_online_storage_settings; + + bool pctl_ready_flag; + std::array pad_0B111; + + std::array reserved_0B114; + + // nn::settings::system::ThemeId + std::array theme_id_type0; + std::array theme_id_type1; + + std::array reserved_0B250; + + // nn::settings::ChineseTraditionalInputMethod + ChineseTraditionalInputMethod chinese_traditional_input_method; + + std::array reserved_0B354; + + bool zoom_flag; + std::array pad_0B391; + + std::array reserved_0B394; + + // nn::settings::system::ButtonConfigRegisteredSettings + u32 button_config_registered_settings_count; + std::array reserved_0B3D4; + + // nn::settings::system::ButtonConfigSettings + u32 button_config_settings_count; + std::array reserved_0B3E4; + std::array, 5> button_config_settings; + std::array reserved_0D030; + u32 button_config_settings_embedded_count; + std::array reserved_0E3E4; + std::array, 5> button_config_settings_embedded; + std::array reserved_10030; + u32 button_config_settings_left_count; + std::array reserved_113E4; + std::array, 5> button_config_settings_left; + std::array reserved_13030; + u32 button_config_settings_right_count; + std::array reserved_143E4; + std::array, 5> button_config_settings_right; + std::array reserved_16030; + // nn::settings::system::ButtonConfigRegisteredSettings + std::array button_config_registered_settings_embedded; + std::array, 10> button_config_registered_settings; + + std::array reserved_21378; + + // nn::settings::system::ConsoleSixAxisSensorAccelerationBias + std::array console_six_axis_sensor_acceleration_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias + std::array console_six_axis_sensor_angular_velocity_bias; + // nn::settings::system::ConsoleSixAxisSensorAccelerationGain + std::array console_six_axis_sensor_acceleration_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain + std::array console_six_axis_sensor_angular_velocity_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias + std::array console_six_axis_sensor_angular_velocity_time_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration + std::array console_six_axis_sensor_angular_velocity_acceleration; + + std::array reserved_29400; + + bool lock_screen_flag; + std::array pad_29471; + + std::array reserved_249274; + + ColorSet color_set_id; + + QuestFlag quest_flag; + + // nn::settings::system::RegionCode + RegionCode region_code; + + // Different to nn::settings::system::InitialLaunchSettings? + InitialLaunchSettingsPacked initial_launch_settings_packed; + + bool battery_percentage_flag; + std::array pad_294A1; + + // BitFlagSet<32, nn::settings::system::AppletLaunchFlag> + u32 applet_launch_flag; + + // nn::settings::system::ThemeSettings + std::array theme_settings; + // nn::fssystem::ArchiveMacKey + std::array theme_key; + + bool field_testing_flag; + std::array pad_294C1; + + s32 panel_crc_mode; + + std::array reserved_294C8; + + // nn::settings::system::BacklightSettings + std::array backlight_settings_mixed_up; + + std::array reserved_2951C; + + // nn::time::SystemClockContext + Service::Time::Clock::SystemClockContext user_system_clock_context; + Service::Time::Clock::SystemClockContext network_system_clock_context; + bool user_system_clock_automatic_correction_enabled; + std::array pad_295C1; + std::array reserved_295C4; + // nn::time::SteadyClockTimePoint + Service::Time::Clock::SteadyClockTimePoint + user_system_clock_automatic_correction_updated_time_point; + + std::array reserved_295E0; + + // nn::settings::system::AccountSettings + AccountSettings account_settings; + + std::array reserved_295F4; + + // nn::settings::system::AudioVolume + std::array audio_volume_type0; + std::array audio_volume_type1; + // nn::settings::system::AudioOutputMode + s32 audio_output_mode_type0; + s32 audio_output_mode_type1; + s32 audio_output_mode_type2; + bool force_mute_on_headphone_removed; + std::array pad_2970D; + s32 headphone_volume_warning_count; + bool heaphone_volume_update_flag; + std::array pad_29715; + // nn::settings::system::AudioVolume + std::array audio_volume_type2; + // nn::settings::system::AudioOutputMode + s32 audio_output_mode_type3; + s32 audio_output_mode_type4; + bool hearing_protection_safeguard_flag; + std::array pad_29729; + std::array reserved_2972C; + s64 hearing_protection_safeguard_remaining_time; + std::array reserved_29738; + + bool console_information_upload_flag; + std::array pad_29771; + + std::array reserved_29774; + + bool automatic_application_download_flag; + std::array pad_297B1; + + std::array reserved_297B4; + + // nn::settings::system::NotificationSettings + NotificationSettings notification_settings; + + std::array reserved_297D0; + + // nn::settings::system::AccountNotificationSettings + u32 account_notification_settings_count; + std::array reserved_29834; + std::array account_notification_settings; + + std::array reserved_29900; + + f32 vibration_master_volume; + + bool usb_full_key_enable_flag; + std::array pad_29A45; + + // nn::settings::system::AnalogStickUserCalibration + std::array analog_stick_user_calibration_left; + std::array analog_stick_user_calibration_right; + + // nn::settings::system::TouchScreenMode + s32 touch_screen_mode; + + std::array reserved_29A6C; + + // nn::settings::system::TvSettings + TvSettings tv_settings; + + // nn::settings::system::Edid + std::array edid; + + std::array reserved_29BA0; + + // nn::settings::system::DataDeletionSettings + std::array data_deletion_settings; + + std::array reserved_29E88; + + // nn::ncm::ProgramId + std::array initial_system_applet_program_id; + std::array overlay_disp_program_id; + + std::array reserved_29ED0; + + bool requires_run_repair_time_reviser; + + std::array reserved_29ED5; + + // nn::time::LocationName + Service::Time::TimeZone::LocationName device_time_zone_location_name; + std::array reserved_29F64; + // nn::time::SteadyClockTimePoint + Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; + + std::array reserved_29F80; + + // nn::settings::system::PrimaryAlbumStorage + PrimaryAlbumStorage primary_album_storage; + + std::array reserved_2A044; + + bool usb_30_enable_flag; + std::array pad_2A081; + bool usb_30_host_enable_flag; + std::array pad_2A085; + bool usb_30_device_enable_flag; + std::array pad_2A089; + + std::array reserved_2A08C; + + bool nfc_enable_flag; + std::array pad_2A0C1; + + std::array reserved_2A0C4; + + // nn::settings::system::SleepSettings + SleepSettings sleep_settings; + + std::array reserved_2A10C; + + // nn::settings::system::EulaVersion + u32 eula_version_count; + std::array reserved_2A144; + std::array eula_versions; + + std::array reserved_2A750; + + // nn::settings::system::DeviceNickName + std::array device_nick_name; + + std::array reserved_2A9D0; + + bool auto_update_enable_flag; + std::array pad_2AA51; + + std::array reserved_2AA54; + + // nn::settings::system::BluetoothDevicesSettings + std::array, 14> bluetooth_device_settings_last_14; + + std::array reserved_2C6A0; + + // nn::settings::system::NxControllerSettings + std::array, 10> nx_controller_settings_data_from_offset_30; +}; + +static_assert(offsetof(SystemSettings, language_code) == 0x10); +static_assert(offsetof(SystemSettings, network_setting_count) == 0x50); +static_assert(offsetof(SystemSettings, network_settings_1B0) == 0x60); +static_assert(offsetof(SystemSettings, bluetooth_device_settings_count) == 0x8060); +static_assert(offsetof(SystemSettings, bluetooth_enable_flag) == 0x8064); +static_assert(offsetof(SystemSettings, bluetooth_device_settings_first_10) == 0x8070); +static_assert(offsetof(SystemSettings, ldn_channel) == 0x9470); +static_assert(offsetof(SystemSettings, mii_author_id) == 0x94B0); +static_assert(offsetof(SystemSettings, nx_controller_settings_count) == 0x94F0); +static_assert(offsetof(SystemSettings, nx_controller_legacy_settings) == 0x9500); +static_assert(offsetof(SystemSettings, external_rtc_reset_flag) == 0x98F0); +static_assert(offsetof(SystemSettings, push_notification_activity_mode_on_sleep) == 0x9930); +static_assert(offsetof(SystemSettings, allowed_ssl_host_count) == 0x99F4); +static_assert(offsetof(SystemSettings, host_fs_mount_point) == 0x9A30); +static_assert(offsetof(SystemSettings, allowed_ssl_hosts) == 0x9B30); +static_assert(offsetof(SystemSettings, ble_pairing_settings_count) == 0xA9F0); +static_assert(offsetof(SystemSettings, ble_pairing_settings) == 0xAA00); +static_assert(offsetof(SystemSettings, account_online_storage_settings_count) == 0xAF00); +static_assert(offsetof(SystemSettings, account_online_storage_settings) == 0xAF10); +static_assert(offsetof(SystemSettings, pctl_ready_flag) == 0xB110); +static_assert(offsetof(SystemSettings, theme_id_type0) == 0xB150); +static_assert(offsetof(SystemSettings, chinese_traditional_input_method) == 0xB350); +static_assert(offsetof(SystemSettings, button_config_registered_settings_count) == 0xB3D0); +static_assert(offsetof(SystemSettings, button_config_settings_count) == 0xB3E0); +static_assert(offsetof(SystemSettings, button_config_settings) == 0xB3E8); +static_assert(offsetof(SystemSettings, button_config_registered_settings_embedded) == 0x1D3E0); +static_assert(offsetof(SystemSettings, console_six_axis_sensor_acceleration_bias) == 0x29370); +static_assert(offsetof(SystemSettings, lock_screen_flag) == 0x29470); +static_assert(offsetof(SystemSettings, battery_percentage_flag) == 0x294A0); +static_assert(offsetof(SystemSettings, field_testing_flag) == 0x294C0); +static_assert(offsetof(SystemSettings, backlight_settings_mixed_up) == 0x294F0); +static_assert(offsetof(SystemSettings, user_system_clock_context) == 0x29580); +static_assert(offsetof(SystemSettings, network_system_clock_context) == 0x295A0); +static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_enabled) == 0x295C0); +static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_updated_time_point) == + 0x295C8); +static_assert(offsetof(SystemSettings, account_settings) == 0x295F0); +static_assert(offsetof(SystemSettings, audio_volume_type0) == 0x296F0); +static_assert(offsetof(SystemSettings, hearing_protection_safeguard_remaining_time) == 0x29730); +static_assert(offsetof(SystemSettings, automatic_application_download_flag) == 0x297B0); +static_assert(offsetof(SystemSettings, notification_settings) == 0x297B8); +static_assert(offsetof(SystemSettings, account_notification_settings) == 0x29840); +static_assert(offsetof(SystemSettings, vibration_master_volume) == 0x29A40); +static_assert(offsetof(SystemSettings, analog_stick_user_calibration_left) == 0x29A48); +static_assert(offsetof(SystemSettings, touch_screen_mode) == 0x29A68); +static_assert(offsetof(SystemSettings, edid) == 0x29AA0); +static_assert(offsetof(SystemSettings, data_deletion_settings) == 0x29E80); +static_assert(offsetof(SystemSettings, requires_run_repair_time_reviser) == 0x29ED4); +static_assert(offsetof(SystemSettings, device_time_zone_location_name) == 0x29F40); +static_assert(offsetof(SystemSettings, nfc_enable_flag) == 0x2A0C0); +static_assert(offsetof(SystemSettings, eula_version_count) == 0x2A140); +static_assert(offsetof(SystemSettings, device_nick_name) == 0x2A950); +static_assert(offsetof(SystemSettings, bluetooth_device_settings_last_14) == 0x2AAA0); +static_assert(offsetof(SystemSettings, nx_controller_settings_data_from_offset_30) == 0x2E6A0); + +static_assert(sizeof(SystemSettings) == 0x336A0, "SystemSettings has the wrong size!"); + +SystemSettings DefaultSystemSettings(); + +} // namespace Service::Set