diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs index f47dc4b3..812dc2c3 100644 --- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 4; + public const int CurrentVersion = 5; public int Version { get; set; } @@ -93,6 +93,11 @@ namespace Ryujinx.Configuration /// public string SystemTimeZone { get; set; } + /// + /// Change System Time Offset in seconds + /// + public long SystemTimeOffset { get; set; } + /// /// Enables or disables Docked Mode /// diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs index 67628aa1..d2826d36 100644 --- a/Ryujinx.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Common/Configuration/ConfigurationState.cs @@ -158,6 +158,11 @@ namespace Ryujinx.Configuration /// public ReactiveObject TimeZone { get; private set; } + /// + /// System Time Offset in seconds + /// + public ReactiveObject SystemTimeOffset { get; private set; } + /// /// Enables or disables Docked Mode /// @@ -188,6 +193,7 @@ namespace Ryujinx.Configuration Language = new ReactiveObject(); Region = new ReactiveObject(); TimeZone = new ReactiveObject(); + SystemTimeOffset = new ReactiveObject(); EnableDockedMode = new ReactiveObject(); EnableMulticoreScheduling = new ReactiveObject(); EnableFsIntegrityChecks = new ReactiveObject(); @@ -322,6 +328,7 @@ namespace Ryujinx.Configuration SystemLanguage = System.Language, SystemRegion = System.Region, SystemTimeZone = System.TimeZone, + SystemTimeOffset = System.SystemTimeOffset, DockedMode = System.EnableDockedMode, EnableDiscordIntegration = EnableDiscordIntegration, EnableVsync = Graphics.EnableVsync, @@ -370,6 +377,7 @@ namespace Ryujinx.Configuration System.Language.Value = Language.AmericanEnglish; System.Region.Value = Region.USA; System.TimeZone.Value = "UTC"; + System.SystemTimeOffset.Value = 0; System.EnableDockedMode.Value = false; EnableDiscordIntegration.Value = true; Graphics.EnableVsync.Value = true; @@ -504,6 +512,15 @@ namespace Ryujinx.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 5) + { + Common.Logging.Logger.PrintWarning(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 5."); + + configurationFileFormat.SystemTimeOffset = 0; + + configurationFileUpdated = true; + } + Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy; Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath; Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug; @@ -518,6 +535,7 @@ namespace Ryujinx.Configuration System.Language.Value = configurationFileFormat.SystemLanguage; System.Region.Value = configurationFileFormat.SystemRegion; System.TimeZone.Value = configurationFileFormat.SystemTimeZone; + System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset; System.EnableDockedMode.Value = configurationFileFormat.DockedMode; System.EnableDockedMode.Value = configurationFileFormat.DockedMode; EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index f6021ec7..ccb67edf 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -9,6 +9,7 @@ using LibHac.Ns; using LibHac.Spl; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Configuration; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Kernel.Common; @@ -224,8 +225,24 @@ namespace Ryujinx.HLE.HOS // We assume the rtc is system time. TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue); + // Configure and setup internal offset + TimeSpanType internalOffset = TimeSpanType.FromSeconds(ConfigurationState.Instance.System.SystemTimeOffset); + + TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds); + + if (systemTime.IsDaylightSavingTime() && !systemTimeOffset.IsDaylightSavingTime()) + { + internalOffset = internalOffset.AddSeconds(3600L); + } + else if (!systemTime.IsDaylightSavingTime() && systemTimeOffset.IsDaylightSavingTime()) + { + internalOffset = internalOffset.AddSeconds(-3600L); + } + + internalOffset = new TimeSpanType(-internalOffset.NanoSeconds); + // First init the standard steady clock - TimeServiceManager.Instance.SetupStandardSteadyClock(null, clockSourceId, systemTime, TimeSpanType.Zero, TimeSpanType.Zero, false); + TimeServiceManager.Instance.SetupStandardSteadyClock(null, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false); TimeServiceManager.Instance.SetupStandardLocalSystemClock(null, new SystemClockContext(), systemTime.ToSeconds()); if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs index c336ad41..89497d82 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.Clock { @@ -9,6 +10,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public static readonly TimeSpanType Zero = new TimeSpanType(0); + private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public long NanoSeconds; public TimeSpanType(long nanoSeconds) @@ -21,6 +24,16 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return NanoSeconds / NanoSecondsPerSecond; } + public TimeSpanType AddSeconds(long seconds) + { + return new TimeSpanType(NanoSeconds + (seconds * NanoSecondsPerSecond)); + } + + public bool IsDaylightSavingTime() + { + return UnixEpoch.AddSeconds(ToSeconds()).ToLocalTime().IsDaylightSavingTime(); + } + public static TimeSpanType FromSeconds(long seconds) { return new TimeSpanType(seconds * NanoSecondsPerSecond); diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json index 8a365550..7a5fe972 100644 --- a/Ryujinx/Config.json +++ b/Ryujinx/Config.json @@ -1,5 +1,5 @@ { - "version": 4, + "version": 5, "max_anisotropy": -1, "graphics_shaders_dump_path": "", "logging_enable_debug": false, @@ -14,6 +14,7 @@ "system_language": "AmericanEnglish", "system_region": "USA", "system_time_zone": "UTC", + "system_time_offset": 0, "docked_mode": false, "enable_discord_integration": true, "enable_vsync": true, diff --git a/Ryujinx/Ui/SwitchSettings.cs b/Ryujinx/Ui/SwitchSettings.cs index 8ff40427..700d0596 100644 --- a/Ryujinx/Ui/SwitchSettings.cs +++ b/Ryujinx/Ui/SwitchSettings.cs @@ -20,6 +20,8 @@ namespace Ryujinx.Ui private static bool _listeningForKeypress; + private long _systemTimeOffset; + #pragma warning disable CS0649 #pragma warning disable IDE0044 [GUI] Window _settingsWin; @@ -42,6 +44,16 @@ namespace Ryujinx.Ui [GUI] ComboBoxText _systemLanguageSelect; [GUI] ComboBoxText _systemRegionSelect; [GUI] ComboBoxText _systemTimeZoneSelect; + [GUI] SpinButton _systemTimeYearSpin; + [GUI] SpinButton _systemTimeMonthSpin; + [GUI] SpinButton _systemTimeDaySpin; + [GUI] SpinButton _systemTimeHourSpin; + [GUI] SpinButton _systemTimeMinuteSpin; + [GUI] Adjustment _systemTimeYearSpinAdjustment; + [GUI] Adjustment _systemTimeMonthSpinAdjustment; + [GUI] Adjustment _systemTimeDaySpinAdjustment; + [GUI] Adjustment _systemTimeHourSpinAdjustment; + [GUI] Adjustment _systemTimeMinuteSpinAdjustment; [GUI] CheckButton _custThemeToggle; [GUI] Entry _custThemePath; [GUI] ToggleButton _browseThemePath; @@ -248,6 +260,7 @@ namespace Ryujinx.Ui _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath; _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath; _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; + _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0); _gameDirsBoxStore = new ListStore(typeof(string)); @@ -266,9 +279,71 @@ namespace Ryujinx.Ui } _listeningForKeypress = false; + + //Setup system time spinners + UpdateSystemTimeSpinners(); + } + + private void UpdateSystemTimeSpinners() + { + //Unbind system time spin events + _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged; + + //Apply actual system time + SystemTimeOffset to system time spin buttons + DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset); + + _systemTimeYearSpinAdjustment.Value = systemTime.Year; + _systemTimeMonthSpinAdjustment.Value = systemTime.Month; + _systemTimeDaySpinAdjustment.Value = systemTime.Day; + _systemTimeHourSpinAdjustment.Value = systemTime.Hour; + _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute; + + //Format spin buttons text to include leading zeros + _systemTimeYearSpin.Text = systemTime.Year.ToString("0000"); + _systemTimeMonthSpin.Text = systemTime.Month.ToString("00"); + _systemTimeDaySpin.Text = systemTime.Day.ToString("00"); + _systemTimeHourSpin.Text = systemTime.Hour.ToString("00"); + _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00"); + + //Bind system time spin button events + _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged; + _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged; } //Events + private void SystemTimeSpin_ValueChanged(Object sender, EventArgs e) + { + int year = _systemTimeYearSpin.ValueAsInt; + int month = _systemTimeMonthSpin.ValueAsInt; + int day = _systemTimeDaySpin.ValueAsInt; + int hour = _systemTimeHourSpin.ValueAsInt; + int minute = _systemTimeMinuteSpin.ValueAsInt; + + if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime)) + { + UpdateSystemTimeSpinners(); + + return; + } + + newTime = newTime.AddSeconds(DateTime.Now.Second).AddMilliseconds(DateTime.Now.Millisecond); + + long systemTimeOffset = (long)Math.Ceiling((newTime - DateTime.Now).TotalMinutes) * 60L; + + if (_systemTimeOffset != systemTimeOffset) + { + _systemTimeOffset = systemTimeOffset; + UpdateSystemTimeSpinners(); + } + } + private void Button_Pressed(object sender, EventArgs args, ToggleButton button) { if (_listeningForKeypress == false) @@ -467,7 +542,8 @@ namespace Ryujinx.Ui ConfigurationState.Instance.Ui.GameDirs.Value = gameDirs; ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value; - ConfigurationState.Instance.System.TimeZone.Value = _systemTimeZoneSelect.ActiveId; + ConfigurationState.Instance.System.TimeZone.Value = _systemTimeZoneSelect.ActiveId; + ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset; MainWindow.SaveConfig(); MainWindow.ApplyTheme(); diff --git a/Ryujinx/Ui/SwitchSettings.glade b/Ryujinx/Ui/SwitchSettings.glade index f9c8b1c9..7415e76e 100644 --- a/Ryujinx/Ui/SwitchSettings.glade +++ b/Ryujinx/Ui/SwitchSettings.glade @@ -7,6 +7,36 @@ 1 10 + + 2000 + 2060 + 1 + 10 + + + 1 + 12 + 1 + 5 + + + 1 + 31 + 1 + 5 + + + 0 + 23 + 1 + 5 + + + 0 + 59 + 1 + 5 + False Ryujinx - Settings @@ -260,6 +290,148 @@ 1 + + + True + False + + + True + False + end + System Time: + + + False + True + 5 + 0 + + + + + True + False + 5 + vertical + _systemTimeYearSpinAdjustment + True + + + False + True + 1 + + + + + True + False + end + - + + + False + True + 5 + 2 + + + + + True + False + 0 + vertical + _systemTimeMonthSpinAdjustment + True + + + False + True + 3 + + + + + True + False + end + - + + + False + True + 5 + 4 + + + + + True + False + 0 + vertical + _systemTimeDaySpinAdjustment + True + + + False + True + 5 + + + + + True + False + 25 + vertical + _systemTimeHourSpinAdjustment + True + + + False + True + 6 + + + + + True + False + end + : + + + False + True + 5 + 7 + + + + + True + False + 0 + vertical + _systemTimeMinuteSpinAdjustment + True + + + False + True + 8 + + + + + False + True + 2 + + Enable Discord Rich Presence @@ -274,7 +446,7 @@ False True 5 - 2 + 3 diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json index 143716b6..f075b608 100644 --- a/Ryujinx/_schema.json +++ b/Ryujinx/_schema.json @@ -422,6 +422,18 @@ "USA" ] }, + "system_time_offset": { + "$id": "#/properties/system_time_offset", + "type": "integer", + "title": "System Time Offset", + "description": "System time offset in seconds.", + "default": 0, + "examples": [ + -3600, + 0, + 3600 + ] + }, "docked_mode": { "$id": "#/properties/docked_mode", "type": "boolean",