Skip to content

Commit d8131f4

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

File tree

5 files changed

+79
-58
lines changed

5 files changed

+79
-58
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: 71 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,88 @@ 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", configParams2.daylightSavings ? "Summer" : "Winter");
930+
DevCon.WriteLn(Color_StrongGreen, "Time Format: %s-Hour", configParams2.timeFormat ? "12" : "24");
931+
DevCon.WriteLn(Color_StrongGreen, "Date Format: %s", configParams2.dateFormat ? (configParams2.dateFormat == 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 + configParams2.daylightSavings * 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+
resulting_tm.tm_isdst = 0;
950+
951+
// Work backwards to input time by accounting for BIOS settings and GMT+9 defaultism.
952+
#if defined(_WIN32)
953+
const std::time_t input_time = _mkgmtime(&resulting_tm) + GMT9_OFFSET_SECONDS - bios_settings_offset_seconds;
954+
gmtime_s(&input_tm, &input_time);
955+
#else
956+
const std::time_t input_time = timegm(&resulting_tm) + GMT9_OFFSET_SECONDS - bios_settings_offset_seconds;
957+
gmtime_r(&input_time, &input_tm);
958+
#endif
959+
}
954960
else if (g_InputRecording.isActive())
955961
{
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;
962+
// Legacy default value if manual RTC is off to preserve compat with old input recordings (RNG seeding).
963+
// 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).
964+
input_tm.tm_sec = 0;
965+
input_tm.tm_min = 0;
966+
input_tm.tm_hour = 0;
967+
input_tm.tm_mday = 4;
968+
input_tm.tm_mon = 2;
969+
input_tm.tm_year = 120;
970+
input_tm.tm_isdst = 0;
971+
972+
#if defined(_WIN32)
973+
const std::time_t resulting_time = _mkgmtime(&input_tm) - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds;
974+
gmtime_s(&resulting_tm, &resulting_time);
975+
#else
976+
const std::time_t resulting_time = timegm(&input_tm) - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds;
977+
gmtime_r(&resulting_time, &resulting_tm);
978+
#endif
965979
}
966980
else
967981
{
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 = {};
982+
// User must set time zone and winter/summer DST in the BIOS for correct time.
983+
const std::time_t input_time = std::time(nullptr) + GMT9_OFFSET_SECONDS;
984+
const std::time_t resulting_time = input_time - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds;
985+
973986
#ifdef _MSC_VER
974-
gmtime_s(&curtime, &gmt9_time);
987+
gmtime_s(&input_tm, &input_time);
988+
gmtime_s(&resulting_tm, &resulting_time);
975989
#else
976-
gmtime_r(&gmt9_time, &curtime);
990+
gmtime_r(&input_time, &input_tm);
991+
gmtime_r(&resulting_time, &resulting_tm);
977992
#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
984993
}
985994

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

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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ void ReadOSDConfigParames()
6565
configParams1.version = (params[2] & 0xE0) >> 5; // OSD Ver (Not sure but best guess).
6666
configParams1.language = params[2] & 0x1F; // Language.
6767
configParams1.timezoneOffset = params[4] | ((u32)(params[3] & 0x7) << 8); // Timezone offset in minutes.
68-
6968
// Region settings for time/date and extended language
7069
configParams2.UC[1] = ((u32)params[3] & 0x78) << 1; // Daylight Savings, 24hr clock, Date format
70+
configParams2.daylightSavings = configParams2.UC[1] & 0x10 ? 1 : 0;
71+
configParams2.timeFormat = configParams2.UC[1] & 0x20 ? 1 : 0;
72+
configParams2.dateFormat = configParams2.UC[1] & 0x80 ? 2 : (configParams2.UC[1] & 0x40 ? 1 : 0);
7173
// FIXME: format, version and language are set manually by the bios. Not sure if any game needs them, but it seems to set version to 2 and duplicate the language value.
7274
configParams2.version = 2;
7375
configParams2.language = configParams1.language;

pcsx2/ps2/BiosTools.h

Lines changed: 2 additions & 2 deletions
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];
@@ -63,7 +63,7 @@ typedef struct
6363

6464
/*00*/ u8 reserved : 4;
6565
/** 0=standard(winter), 1=daylight savings(summer) */
66-
/*04*/ u8 daylightSaving : 1;
66+
/*04*/ u8 daylightSavings : 1;
6767
/** 0=24 hour, 1=12 hour */
6868
/*05*/ u8 timeFormat : 1;
6969
/** 0=YYYYMMDD, 1=MMDDYYYY, 2=DDMMYYYY */

0 commit comments

Comments
 (0)