diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index edd1d4b378..752664c1b6 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -178,12 +178,10 @@ u64 Timer::GetTimeSinceJan1970() u64 Timer::GetLocalTimeSinceJan1970() { time_t sysTime, tzDiff, tzDST; - struct tm* gmTime; - time(&sysTime); + tm* gmTime = localtime(&sysTime); // Account for DST where needed - gmTime = localtime(&sysTime); if (gmTime->tm_isdst == 1) tzDST = 3600; else @@ -193,7 +191,7 @@ u64 Timer::GetLocalTimeSinceJan1970() gmTime = gmtime(&sysTime); tzDiff = sysTime - mktime(gmTime); - return (u64)(sysTime + tzDiff + tzDST); + return static_cast(sysTime + tzDiff + tzDST); } // Return the current time formatted as Minutes:Seconds:Milliseconds diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 5d2adf1e83..11343c4d62 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -275,6 +275,8 @@ void SConfig::SaveCoreSettings(IniFile& ini) core->Set("GFXBackend", m_strVideoBackend); core->Set("GPUDeterminismMode", m_strGPUDeterminismMode); core->Set("PerfMapDir", m_perfDir); + core->Set("EnableCustomRTC", bEnableCustomRTC); + core->Set("CustomRTCValue", m_customRTCValue); } void SConfig::SaveMovieSettings(IniFile& ini) @@ -553,6 +555,9 @@ void SConfig::LoadCoreSettings(IniFile& ini) core->Get("GFXBackend", &m_strVideoBackend, ""); core->Get("GPUDeterminismMode", &m_strGPUDeterminismMode, "auto"); core->Get("PerfMapDir", &m_perfDir, ""); + core->Get("EnableCustomRTC", &bEnableCustomRTC, false); + // Default to seconds between 1.1.1970 and 1.1.2000 + core->Get("CustomRTCValue", &m_customRTCValue, 946684800); } void SConfig::LoadMovieSettings(IniFile& ini) diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index e5a4f5cc89..1b424c7ec2 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -139,6 +139,10 @@ struct SConfig : NonCopyable // Fifo Player related settings bool bLoopFifoReplay; + // Custom RTC + bool bEnableCustomRTC; + u32 m_customRTCValue; + enum EBootBS2 { BOOT_DEFAULT, diff --git a/Source/Core/Core/HW/EXI_DeviceIPL.cpp b/Source/Core/Core/HW/EXI_DeviceIPL.cpp index e66a8dc378..c4e5b3834d 100644 --- a/Source/Core/Core/HW/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI_DeviceIPL.cpp @@ -421,6 +421,14 @@ u32 CEXIIPL::GetGCTime() // let's keep time moving forward, regardless of what it starts at ltime += CoreTiming::GetTicks() / SystemTimers::GetTicksPerSecond(); } + else if (SConfig::GetInstance().bEnableCustomRTC) + { + _assert_(!Core::g_want_determinism); + ltime = SConfig::GetInstance().m_customRTCValue; + + // let's keep time moving forward, regardless of what it starts at + ltime += Common::Timer::GetLocalTimeSinceJan1970() - SystemTimers::GetLocalTimeOnBoot(); + } else { _assert_(!Core::g_want_determinism); diff --git a/Source/Core/Core/HW/SystemTimers.cpp b/Source/Core/Core/HW/SystemTimers.cpp index cda4e32b98..f387df4fb2 100644 --- a/Source/Core/Core/HW/SystemTimers.cpp +++ b/Source/Core/Core/HW/SystemTimers.cpp @@ -83,6 +83,9 @@ static int s_audio_dma_period; // we can just increase this number. static int s_ipc_hle_period; +// Custom RTC +static u64 s_localtime_on_boot; + u32 GetTicksPerSecond() { return s_cpu_core_clock; @@ -157,6 +160,11 @@ u64 GetFakeTimeBase() ((CoreTiming::GetTicks() - CoreTiming::GetFakeTBStartTicks()) / TIMER_RATIO); } +u64 GetLocalTimeOnBoot() +{ + return s_localtime_on_boot; +} + static void PatchEngineCallback(u64 userdata, s64 cyclesLate) { // Patch mem and run the Action Replay @@ -220,6 +228,7 @@ void Init() Common::Timer::IncreaseResolution(); // store and convert localtime at boot to timebase ticks + s_localtime_on_boot = Common::Timer::GetLocalTimeSinceJan1970(); CoreTiming::SetFakeTBStartValue((u64)(s_cpu_core_clock / TIMER_RATIO) * (u64)CEXIIPL::GetGCTime()); CoreTiming::SetFakeTBStartTicks(CoreTiming::GetTicks()); @@ -249,6 +258,7 @@ void Init() void Shutdown() { Common::Timer::RestoreResolution(); + s_localtime_on_boot = 0; } } // namespace diff --git a/Source/Core/Core/HW/SystemTimers.h b/Source/Core/Core/HW/SystemTimers.h index 18b7557658..9f56d1da99 100644 --- a/Source/Core/Core/HW/SystemTimers.h +++ b/Source/Core/Core/HW/SystemTimers.h @@ -44,4 +44,6 @@ u32 GetFakeDecrementer(); void TimeBaseSet(); u64 GetFakeTimeBase(); +// Custom RTC +u64 GetLocalTimeOnBoot(); } diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index b42b008327..6a8b65a599 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -518,6 +518,10 @@ bool BeginRecordingInput(int controllers) s_bNetPlay = true; s_recordingStartTime = CEXIIPL::NetPlay_GetGCTime(); } + else if (SConfig::GetInstance().bEnableCustomRTC) + { + s_recordingStartTime = SConfig::GetInstance().m_customRTCValue; + } else { s_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970(); diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 44224ce588..3e3a6e16b4 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -792,7 +792,10 @@ bool NetPlayServer::StartGame() // no change, just update with clients AdjustPadBufferSize(m_target_buffer_size); - g_netplay_initial_gctime = Common::Timer::GetLocalTimeSinceJan1970(); + if (SConfig::GetInstance().bEnableCustomRTC) + g_netplay_initial_gctime = SConfig::GetInstance().m_customRTCValue; + else + g_netplay_initial_gctime = Common::Timer::GetLocalTimeSinceJan1970(); // tell clients to start game auto spac = std::make_unique(); diff --git a/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp b/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp index b5e70c82aa..40bb89ce12 100644 --- a/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp @@ -5,9 +5,14 @@ #include #include +#include +#include +#include #include #include #include +#include +#include #include "Core/ConfigManager.h" #include "DolphinWX/Config/AdvancedConfigPane.h" @@ -30,6 +35,17 @@ void AdvancedConfigPane::InitializeGUI() m_clock_override_slider->Bind(wxEVT_SLIDER, &AdvancedConfigPane::OnClockOverrideSliderChanged, this); + m_custom_rtc_checkbox = new wxCheckBox(this, wxID_ANY, _("Enable Custom RTC")); + m_custom_rtc_date_picker = new wxDatePickerCtrl(this, wxID_ANY); + m_custom_rtc_time_picker = new wxTimePickerCtrl(this, wxID_ANY); + + m_custom_rtc_checkbox->Bind(wxEVT_CHECKBOX, &AdvancedConfigPane::OnCustomRTCCheckBoxChanged, + this); + m_custom_rtc_date_picker->Bind(wxEVT_DATE_CHANGED, &AdvancedConfigPane::OnCustomRTCDateChanged, + this); + m_custom_rtc_time_picker->Bind(wxEVT_TIME_CHANGED, &AdvancedConfigPane::OnCustomRTCTimeChanged, + this); + wxStaticText* const clock_override_description = new wxStaticText(this, wxID_ANY, _("Higher values can make variable-framerate games " "run at a higher framerate, at the expense of CPU. " @@ -40,10 +56,17 @@ void AdvancedConfigPane::InitializeGUI() "Do so at your own risk. Please do not report " "bugs that occur with a non-default clock. ")); + wxStaticText* const custom_rtc_description = new wxStaticText( + this, wxID_ANY, + _("This setting allows you to set a custom real time clock (RTC) separate " + "from your current system time.\n\nIf you're unsure, leave this disabled.")); + #ifdef __APPLE__ clock_override_description->Wrap(550); + custom_rtc_description->Wrap(550); #else clock_override_description->Wrap(400); + custom_rtc_description->Wrap(400); #endif wxBoxSizer* const clock_override_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -62,8 +85,27 @@ void AdvancedConfigPane::InitializeGUI() cpu_options_sizer->Add(clock_override_slider_sizer); cpu_options_sizer->Add(clock_override_description_sizer); + wxBoxSizer* const custom_rtc_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + custom_rtc_checkbox_sizer->Add(m_custom_rtc_checkbox, 1, wxALL, 5); + + wxGridBagSizer* const custom_rtc_date_time_sizer = new wxGridBagSizer(); + custom_rtc_date_time_sizer->Add(m_custom_rtc_date_picker, wxGBPosition(0, 0), wxDefaultSpan, + wxEXPAND | wxALL, 5); + custom_rtc_date_time_sizer->Add(m_custom_rtc_time_picker, wxGBPosition(0, 1), wxDefaultSpan, + wxEXPAND | wxALL, 5); + + wxBoxSizer* const custom_rtc_description_sizer = new wxBoxSizer(wxHORIZONTAL); + custom_rtc_description_sizer->Add(custom_rtc_description, 1, wxALL, 5); + + wxStaticBoxSizer* const custom_rtc_sizer = + new wxStaticBoxSizer(wxVERTICAL, this, _("Custom RTC Options")); + custom_rtc_sizer->Add(custom_rtc_checkbox_sizer); + custom_rtc_sizer->Add(custom_rtc_date_time_sizer); + custom_rtc_sizer->Add(custom_rtc_description_sizer); + wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(cpu_options_sizer, 0, wxEXPAND | wxALL, 5); + main_sizer->Add(custom_rtc_sizer, 0, wxEXPAND | wxALL, 5); SetSizer(main_sizer); } @@ -76,6 +118,7 @@ void AdvancedConfigPane::LoadGUIValues() m_clock_override_slider->SetValue(ocFactor); m_clock_override_slider->Enable(oc_enabled); UpdateCPUClock(); + LoadCustomRTC(); } void AdvancedConfigPane::OnClockOverrideCheckBoxChanged(wxCommandEvent& event) @@ -93,6 +136,26 @@ void AdvancedConfigPane::OnClockOverrideSliderChanged(wxCommandEvent& event) UpdateCPUClock(); } +void AdvancedConfigPane::OnCustomRTCCheckBoxChanged(wxCommandEvent& event) +{ + const bool checked = m_custom_rtc_checkbox->IsChecked(); + SConfig::GetInstance().bEnableCustomRTC = checked; + m_custom_rtc_date_picker->Enable(checked); + m_custom_rtc_time_picker->Enable(checked); +} + +void AdvancedConfigPane::OnCustomRTCDateChanged(wxCommandEvent& event) +{ + m_temp_date = m_custom_rtc_date_picker->GetValue().GetTicks(); + UpdateCustomRTC(m_temp_date, m_temp_time); +} + +void AdvancedConfigPane::OnCustomRTCTimeChanged(wxCommandEvent& event) +{ + m_temp_time = m_custom_rtc_time_picker->GetValue().GetTicks() - m_temp_date; + UpdateCustomRTC(m_temp_date, m_temp_time); +} + void AdvancedConfigPane::UpdateCPUClock() { bool wii = SConfig::GetInstance().bWii; @@ -102,3 +165,35 @@ void AdvancedConfigPane::UpdateCPUClock() m_clock_override_text->SetLabel( SConfig::GetInstance().m_OCEnable ? wxString::Format("%d %% (%d mhz)", percent, clock) : ""); } + +void AdvancedConfigPane::LoadCustomRTC() +{ + wxDateTime custom_rtc(static_cast(SConfig::GetInstance().m_customRTCValue)); + custom_rtc = custom_rtc.ToUTC(); + bool custom_rtc_enabled = SConfig::GetInstance().bEnableCustomRTC; + m_custom_rtc_checkbox->SetValue(custom_rtc_enabled); + if (custom_rtc.IsValid()) + { + m_custom_rtc_date_picker->SetValue(custom_rtc); + m_custom_rtc_time_picker->SetValue(custom_rtc); + } + m_temp_date = m_custom_rtc_date_picker->GetValue().GetTicks(); + m_temp_time = m_custom_rtc_time_picker->GetValue().GetTicks() - m_temp_date; + // Limit dates to valid ranges (2000 to 2099 for GC, 2000 to 2035 for Wii) + if (SConfig::GetInstance().bWii) + m_custom_rtc_date_picker->SetRange(wxDateTime(1, wxDateTime::Jan, 2000), + wxDateTime(31, wxDateTime::Dec, 2035)); + else + m_custom_rtc_date_picker->SetRange(wxDateTime(1, wxDateTime::Jan, 2000), + wxDateTime(31, wxDateTime::Dec, 2099)); + m_custom_rtc_date_picker->Enable(custom_rtc_enabled); + m_custom_rtc_time_picker->Enable(custom_rtc_enabled); +} + +void AdvancedConfigPane::UpdateCustomRTC(time_t date, time_t time) +{ + wxDateTime custom_rtc(date + time); + SConfig::GetInstance().m_customRTCValue = custom_rtc.FromUTC().GetTicks(); + m_custom_rtc_date_picker->SetValue(custom_rtc); + m_custom_rtc_time_picker->SetValue(custom_rtc); +} diff --git a/Source/Core/DolphinWX/Config/AdvancedConfigPane.h b/Source/Core/DolphinWX/Config/AdvancedConfigPane.h index 114ddc2265..84b2e4e3f5 100644 --- a/Source/Core/DolphinWX/Config/AdvancedConfigPane.h +++ b/Source/Core/DolphinWX/Config/AdvancedConfigPane.h @@ -7,8 +7,10 @@ #include class wxCheckBox; +class wxDatePickerCtrl; class wxSlider; class wxStaticText; +class wxTimePickerCtrl; class AdvancedConfigPane final : public wxPanel { @@ -21,10 +23,22 @@ private: void OnClockOverrideCheckBoxChanged(wxCommandEvent&); void OnClockOverrideSliderChanged(wxCommandEvent&); + void OnCustomRTCCheckBoxChanged(wxCommandEvent&); + void OnCustomRTCDateChanged(wxCommandEvent&); + void OnCustomRTCTimeChanged(wxCommandEvent&); void UpdateCPUClock(); + // Custom RTC + void LoadCustomRTC(); + void UpdateCustomRTC(time_t date, time_t time); + u32 m_temp_date; + u32 m_temp_time; + wxCheckBox* m_clock_override_checkbox; wxSlider* m_clock_override_slider; wxStaticText* m_clock_override_text; + wxCheckBox* m_custom_rtc_checkbox; + wxDatePickerCtrl* m_custom_rtc_date_picker; + wxTimePickerCtrl* m_custom_rtc_time_picker; };