diff --git a/src/core/core.cpp b/src/core/core.cpp index 7151727710..31c5908669 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -30,6 +30,7 @@ #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" +#include "core/settings.h" #include "core/telemetry_session.h" #include "frontend/applets/profile_select.h" #include "frontend/applets/software_keyboard.h" @@ -96,6 +97,11 @@ struct System::Impl { CoreTiming::Init(); kernel.Initialize(); + const auto current_time = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + Settings::values.custom_rtc_differential = + Settings::values.custom_rtc.value_or(current_time) - current_time; + // Create a default fs if one doesn't already exist. if (virtual_filesystem == nullptr) virtual_filesystem = std::make_shared(); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 16564de241..c13640ad8f 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -12,9 +12,16 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/service/time/interface.h" #include "core/hle/service/time/time.h" +#include "core/settings.h" namespace Service::Time { +static std::chrono::seconds GetSecondsSinceEpoch() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + + Settings::values.custom_rtc_differential; +} + static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, CalendarAdditionalInfo& additional_info, [[maybe_unused]] const TimeZoneRule& /*rule*/) { @@ -68,9 +75,7 @@ public: private: void GetCurrentTime(Kernel::HLERequestContext& ctx) { - const s64 time_since_epoch{std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()}; + const s64 time_since_epoch{GetSecondsSinceEpoch().count()}; LOG_DEBUG(Service_Time, "called"); IPC::ResponseBuilder rb{ctx, 4}; @@ -266,10 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto initial_type = rp.PopRaw(); - const s64 time_since_epoch{std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()}; - + const s64 time_since_epoch{GetSecondsSinceEpoch().count()}; const std::time_t time(time_since_epoch); const std::tm* tm = std::localtime(&time); if (tm == nullptr) { diff --git a/src/core/settings.h b/src/core/settings.h index de01b05c0a..29ce989830 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -350,6 +351,11 @@ struct Values { bool use_docked_mode; bool enable_nfc; std::optional rng_seed; + // Measured in seconds since epoch + std::optional custom_rtc; + // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` + std::chrono::seconds custom_rtc_differential; + s32 current_user; s32 language_index; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 165d70e9cc..ddf4cf552a 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -419,13 +419,21 @@ void Config::ReadValues() { Settings::values.language_index = qt_config->value("language_index", 1).toInt(); - const auto enabled = qt_config->value("rng_seed_enabled", false).toBool(); - if (enabled) { + const auto rng_seed_enabled = qt_config->value("rng_seed_enabled", false).toBool(); + if (rng_seed_enabled) { Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong(); } else { Settings::values.rng_seed = std::nullopt; } + const auto custom_rtc_enabled = qt_config->value("custom_rtc_enabled", false).toBool(); + if (custom_rtc_enabled) { + Settings::values.custom_rtc = + std::chrono::seconds(qt_config->value("custom_rtc", 0).toULongLong()); + } else { + Settings::values.custom_rtc = std::nullopt; + } + qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); @@ -653,6 +661,11 @@ void Config::SaveValues() { qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0)); + qt_config->setValue("custom_rtc_enabled", Settings::values.custom_rtc.has_value()); + qt_config->setValue("custom_rtc", + QVariant::fromValue( + Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count())); + qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 445d01ca01..94e27349da 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -51,6 +51,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: ui->rng_seed_edit->setText(QStringLiteral("00000000")); }); + connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { + ui->custom_rtc_edit->setEnabled(checked); + if (!checked) + ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); + }); + this->setConfiguration(); } @@ -67,6 +73,13 @@ void ConfigureSystem::setConfiguration() { const auto rng_seed = QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper(); ui->rng_seed_edit->setText(rng_seed); + + ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); + ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); + + const auto rtc_time = Settings::values.custom_rtc.value_or( + std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); + ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); } void ConfigureSystem::ReadSystemSettings() {} @@ -82,6 +95,12 @@ void ConfigureSystem::applyConfiguration() { else Settings::values.rng_seed = std::nullopt; + if (ui->custom_rtc_checkbox->isChecked()) + Settings::values.custom_rtc = + std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); + else + Settings::values.custom_rtc = std::nullopt; + Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 74e800c2a1..0733272987 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -22,6 +22,13 @@ System Settings + + + + Sound output mode + + + @@ -114,27 +121,6 @@ - - - - Console ID: - - - - - - - Sound output mode - - - - - - - Birthday - - - @@ -206,6 +192,20 @@ + + + + Console ID: + + + + + + + Birthday + + + @@ -241,6 +241,13 @@ + + + + RNG Seed + + + @@ -248,14 +255,7 @@ - - - - RNG Seed - - - - + @@ -276,6 +276,27 @@ + + + + Custom RTC + + + + + + + + 1970 + 1 + 1 + + + + d MMM yyyy h:mm:ss AP + + + diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index fe0d1eebf5..7a77f76e83 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -325,13 +325,21 @@ void Config::ReadValues() { Settings::values.current_user = std::clamp( sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); - const auto enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); - if (enabled) { + const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); + if (rng_seed_enabled) { Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); } else { Settings::values.rng_seed = std::nullopt; } + const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); + if (custom_rtc_enabled) { + Settings::values.custom_rtc = + std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); + } else { + Settings::values.custom_rtc = std::nullopt; + } + // Core Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 0f3f8da502..ba51a4a512 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -183,6 +183,12 @@ enable_nfc = rng_seed_enabled = rng_seed = +# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service +# This will auto-increment, with the time set being the time the game is started +# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used +custom_rtc_enabled = +custom_rtc = + # Sets the account username, max length is 32 characters # yuzu (default) username = yuzu