Skip to content

Commit 581b50c

Browse files
RTC: Automatically detect timezone
1 parent 8fb2940 commit 581b50c

File tree

5 files changed

+95
-56
lines changed

5 files changed

+95
-56
lines changed

pcsx2-qt/Settings/EmulationSettingsWidget.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
159159
dialog()->registerWidgetHelp(m_ui.manuallySetRealTimeClock, tr("Manually Set Real-Time Clock"), tr("Unchecked"),
160160
tr("Manually set a real-time clock to use for the virtual PlayStation 2 instead of using your OS' system clock."));
161161
dialog()->registerWidgetHelp(m_ui.rtcDateTime, tr("Real-Time Clock"), tr("Current date and time"),
162-
tr("Real-time clock (RTC) used by the virtual PlayStation 2. Date format is the same as the one used by your OS. "
163-
"This time is only applied upon booting the PS2; changing it while in-game will have no effect. "
164-
"NOTE: This assumes you have your PS2 set to the default timezone of GMT+0 and default DST of Summer Time. "
162+
tr("Real-time clock (RTC) used by the virtual PlayStation 2. Date format is the same as the one used by your OS.<br>"
163+
"This time is only applied upon booting the PS2; changing it while in-game will have no effect.<br>"
165164
"Some games require an RTC date/time set after their release date."));
166165

167166
updateOptimalFramePacing();

pcsx2/CDVD/CDVD.cpp

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
cdvdStruct cdvd;
3737

3838
s64 PSXCLK = 36864000;
39+
const s32 GMT9_OFFSET_SECONDS = 32400; // 9 * 60 * 60
3940

4041
static constexpr u8 monthmap[13] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
4142

@@ -920,69 +921,86 @@ void cdvdReset()
920921
cdvd.ReadTime = cdvdBlockReadTime(MODE_DVDROM);
921922
cdvd.RotSpeed = cdvdRotationTime(MODE_DVDROM);
922923

924+
ReadOSDConfigParames();
925+
926+
// Print time zone offset, DST, date format, time format, and system time basis.
927+
DevCon.WriteLn(Color_StrongGreen, configParams1.timezoneOffset < 0 ? "Time Zone Offset: GMT%03d:%02d" : "Time Zone Offset: GMT+%02d:%02d",
928+
configParams1.timezoneOffset / 60, std::abs(configParams1.timezoneOffset % 60));
929+
DevCon.WriteLn(Color_StrongGreen, "DST: %s Time", GetDaylightSavings() ? "Summer" : "Winter");
930+
DevCon.WriteLn(Color_StrongGreen, "Time Format: %s-Hour", GetTimeFormat() ? "12" : "24");
931+
DevCon.WriteLn(Color_StrongGreen, "Date Format: %s", GetDateFormat() ? (GetDateFormat() == 2 ? "DD/MM/YYYY" : "MM/DD/YYYY") : "YYYY/MM/DD");
932+
DevCon.WriteLn(Color_StrongGreen, "System Time Basis: %s",
933+
EmuConfig.ManuallySetRealTimeClock ? "Manual RTC" : g_InputRecording.isActive() ? "Default Input Recording Time" : "Operating System Time");
934+
935+
std::tm input_tm{};
936+
std::tm resulting_tm{};
937+
938+
const int bios_settings_offset_seconds = 60 * (configParams1.timezoneOffset + GetDaylightSavings() * 60);
939+
940+
// CDVD internally uses GMT+9, 1-indexed months, and year offset of 2000 instead of 1900.
923941
if (EmuConfig.ManuallySetRealTimeClock)
924942
{
925-
// Convert to GMT+9 (assumes GMT+0)
926-
std::tm tm{};
927-
tm.tm_sec = EmuConfig.RtcSecond;
928-
tm.tm_min = EmuConfig.RtcMinute;
929-
tm.tm_hour = EmuConfig.RtcHour;
930-
tm.tm_mday = EmuConfig.RtcDay;
931-
tm.tm_mon = EmuConfig.RtcMonth - 1;
932-
tm.tm_year = EmuConfig.RtcYear + 100; // 2000 - 1900
933-
tm.tm_isdst = 1;
934-
935-
// Need this instead of mktime for timezone independence
936-
std::time_t t = 0;
937-
#if defined(_WIN32)
938-
t = _mkgmtime(&tm) + 32400; //60 * 60 * 9 for GMT+9
939-
gmtime_s(&tm, &t);
940-
#else
941-
t = timegm(&tm) + 32400;
942-
gmtime_r(&t, &tm);
943-
#endif
944-
945-
cdvd.RTC.second = tm.tm_sec;
946-
cdvd.RTC.minute = tm.tm_min;
947-
cdvd.RTC.hour = tm.tm_hour;
948-
cdvd.RTC.day = tm.tm_mday;
949-
cdvd.RTC.month = tm.tm_mon + 1;
950-
cdvd.RTC.year = tm.tm_year - 100;
951-
}
952-
// If we are recording, always use the same RTC setting
953-
// for games that use the RTC to seed their RNG -- this is very important to be the same everytime!
943+
resulting_tm.tm_sec = EmuConfig.RtcSecond;
944+
resulting_tm.tm_min = EmuConfig.RtcMinute;
945+
resulting_tm.tm_hour = EmuConfig.RtcHour;
946+
resulting_tm.tm_mday = EmuConfig.RtcDay;
947+
resulting_tm.tm_mon = EmuConfig.RtcMonth - 1;
948+
resulting_tm.tm_year = EmuConfig.RtcYear + 100;
949+
950+
// Work backwards to input time by accounting for BIOS settings and GMT+9 defaultism.
951+
#if defined(_WIN32)
952+
const std::time_t input_time = _mkgmtime(&resulting_tm) + GMT9_OFFSET_SECONDS - bios_settings_offset_seconds;
953+
gmtime_s(&input_tm, &input_time);
954+
#else
955+
const std::time_t input_time = timegm(&resulting_tm) + GMT9_OFFSET_SECONDS - bios_settings_offset_seconds;
956+
gmtime_r(&input_time, &input_tm);
957+
#endif
958+
}
954959
else if (g_InputRecording.isActive())
955960
{
956-
Console.WriteLn("Input Recording Active - Using Constant RTC of 04-03-2020 (DD-MM-YYYY)");
957-
// Why not just 0 everything? Some games apparently require the date to be valid in terms of when
958-
// the PS2 / Game actually came out. (MGS3). So set it to a value well beyond any PS2 game's release date.
959-
cdvd.RTC.second = 0;
960-
cdvd.RTC.minute = 0;
961-
cdvd.RTC.hour = 0;
962-
cdvd.RTC.day = 4;
963-
cdvd.RTC.month = 3;
964-
cdvd.RTC.year = 20;
961+
// Legacy default value if manual RTC is off to preserve compat with old input recordings (RNG seeding).
962+
// Well beyond any PS2 game's release date because some games require a valid date in terms of when the PS2 / Game actually came out (MGS3).
963+
input_tm.tm_sec = 0;
964+
input_tm.tm_min = 0;
965+
input_tm.tm_hour = 0;
966+
input_tm.tm_mday = 4;
967+
input_tm.tm_mon = 2;
968+
input_tm.tm_year = 120;
969+
970+
#if defined(_WIN32)
971+
const std::time_t resulting_time = _mkgmtime(&input_tm) - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds;
972+
gmtime_s(&resulting_tm, &resulting_time);
973+
#else
974+
const std::time_t resulting_time = timegm(&input_tm) - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds;
975+
gmtime_r(&resulting_time, &resulting_tm);
976+
#endif
965977
}
966978
else
967979
{
968-
// CDVD internally uses GMT+9. If you think the time's wrong, you're wrong.
969-
// Set up your time zone and winter/summer in the BIOS. No PS2 BIOS I know of features automatic DST.
970-
const std::time_t utc_time = std::time(nullptr);
971-
const std::time_t gmt9_time = (utc_time + 32400); //60 * 60 * 9
972-
struct tm curtime = {};
980+
// User must set time zone and winter/summer DST in the BIOS for correct time.
981+
const std::time_t input_time = std::time(nullptr) + GMT9_OFFSET_SECONDS;
982+
const std::time_t resulting_time = input_time - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds;
983+
973984
#ifdef _MSC_VER
974-
gmtime_s(&curtime, &gmt9_time);
985+
gmtime_s(&input_tm, &input_time);
986+
gmtime_s(&resulting_tm, &resulting_time);
975987
#else
976-
gmtime_r(&gmt9_time, &curtime);
988+
gmtime_r(&input_time, &input_tm);
989+
gmtime_r(&resulting_time, &resulting_tm);
977990
#endif
978-
cdvd.RTC.second = static_cast<u8>(curtime.tm_sec);
979-
cdvd.RTC.minute = static_cast<u8>(curtime.tm_min);
980-
cdvd.RTC.hour = static_cast<u8>(curtime.tm_hour);
981-
cdvd.RTC.day = static_cast<u8>(curtime.tm_mday);
982-
cdvd.RTC.month = static_cast<u8>(curtime.tm_mon + 1); // WX returns Jan as "0"
983-
cdvd.RTC.year = static_cast<u8>(curtime.tm_year - 100); // offset from 2000
984991
}
985992

993+
cdvd.RTC.second = static_cast<u8>(input_tm.tm_sec);
994+
cdvd.RTC.minute = static_cast<u8>(input_tm.tm_min);
995+
cdvd.RTC.hour = static_cast<u8>(input_tm.tm_hour);
996+
cdvd.RTC.day = static_cast<u8>(input_tm.tm_mday);
997+
cdvd.RTC.month = static_cast<u8>(input_tm.tm_mon + 1);
998+
cdvd.RTC.year = static_cast<u8>(input_tm.tm_year - 100); // offset from 2000
999+
1000+
DevCon.WriteLn(Color_StrongGreen, "Resulting System Time: 20%02u-%02u-%02u %02u:%02u:%02u",
1001+
resulting_tm.tm_year - 100, resulting_tm.tm_mon + 1, resulting_tm.tm_mday,
1002+
resulting_tm.tm_hour, resulting_tm.tm_min, resulting_tm.tm_sec);
1003+
9861004
cdvdCtrlTrayClose();
9871005
}
9881006

pcsx2/Config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,7 @@ struct Pcsx2Config
13141314
InhibitScreensaver : 1,
13151315
BackupSavestate : 1,
13161316
McdFolderAutoManage : 1,
1317-
ManuallySetRealTimeClock : 1,
1317+
ManuallySetRealTimeClock : 1, // passes user-set real-time clock information to cdvd at startup
13181318

13191319
HostFs : 1,
13201320

pcsx2/ps2/BiosTools.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,21 @@ void CopyBIOSToMemory()
382382

383383
CurrentBiosInformation.eeThreadListAddr = 0;
384384
}
385+
386+
// Summer = True; Winter = False
387+
bool GetDaylightSavings()
388+
{
389+
return configParams2.UC[1] & 0x10 ? true : false;
390+
}
391+
392+
// 12-Hour = True; 24-Hour = False
393+
bool GetTimeFormat()
394+
{
395+
return configParams2.UC[1] & 0x20 ? true : false;
396+
}
397+
398+
// DD/MM/YYYY = 2; MM/DD/YYYY = 1; YYYY/MM/DD = 0
399+
u8 GetDateFormat()
400+
{
401+
return configParams2.UC[1] & 0x80 ? 2 : (configParams2.UC[1] & 0x40 ? 1 : 0);
402+
}

pcsx2/ps2/BiosTools.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ typedef struct
4343
/** LANGUAGE_??? value */
4444
/*16*/ u32 language : 5;
4545
/** timezone minutes offset from gmt */
46-
/*21*/ u32 timezoneOffset : 11;
46+
/*21*/ s32 timezoneOffset : 11;
4747
};
4848

4949
u8 UC[4];
@@ -117,3 +117,7 @@ extern bool IsBIOSAvailable(const std::string& full_path);
117117

118118
extern bool LoadBIOS();
119119
extern void CopyBIOSToMemory();
120+
121+
extern bool GetDaylightSavings();
122+
extern bool GetTimeFormat();
123+
extern u8 GetDateFormat();

0 commit comments

Comments
 (0)