diff --git a/pcsx2-qt/Settings/EmulationSettingsWidget.cpp b/pcsx2-qt/Settings/EmulationSettingsWidget.cpp index 9d96cb9165..59e617775c 100644 --- a/pcsx2-qt/Settings/EmulationSettingsWidget.cpp +++ b/pcsx2-qt/Settings/EmulationSettingsWidget.cpp @@ -164,7 +164,6 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog dialog()->registerWidgetHelp(m_ui.rtcDateTime, tr("Real-Time Clock"), tr("Current date and time"), tr("Real-time clock (RTC) used by the virtual PlayStation 2.
" "This time is only applied upon booting the PS2; changing it while in-game will have no effect.
" - "NOTE: This assumes you have your PS2 set to the default timezone of GMT+0 and default DST of Summer Time.
" "Some games require an RTC date/time set after their release date.")); dialog()->registerWidgetHelp(m_ui.rtcUseSystemLocaleFormat, tr("Use System Locale Format"), tr("User Preference"), tr("Uses the operating system's date/time format rather than \"yyyy-MM-dd HH:mm:ss\". May exclude seconds.")); diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index ffc822fef0..f627bfc0c7 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -37,6 +37,8 @@ cdvdStruct cdvd; s64 PSXCLK = 36864000; +static constexpr s32 GMT9_OFFSET_SECONDS = 9 * 60 * 60; // 32400 + static constexpr u8 monthmap[13] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static constexpr u8 cdvdParamLength[16] = { 0, 0, 0, 0, 0, 4, 11, 11, 11, 1, 255, 255, 7, 2, 11, 1 }; @@ -920,69 +922,92 @@ void cdvdReset() cdvd.ReadTime = cdvdBlockReadTime(MODE_DVDROM); cdvd.RotSpeed = cdvdRotationTime(MODE_DVDROM); + ReadOSDConfigParames(); + + // Print time zone offset, DST, time format, date format, and system time basis. + DevCon.WriteLn(Color_StrongGreen, configParams1.timezoneOffset < 0 ? "Time Zone Offset: GMT%03d:%02d" : "Time Zone Offset: GMT+%02d:%02d", + configParams1.timezoneOffset / 60, std::abs(configParams1.timezoneOffset % 60)); + DevCon.WriteLn(Color_StrongGreen, "DST: %s Time", configParams2.daylightSavings ? "Summer" : "Winter"); + DevCon.WriteLn(Color_StrongGreen, "Time Format: %s-Hour", configParams2.timeFormat ? "12" : "24"); + DevCon.WriteLn(Color_StrongGreen, "Date Format: %s", configParams2.dateFormat ? (configParams2.dateFormat == 2 ? "DD/MM/YYYY" : "MM/DD/YYYY") : "YYYY/MM/DD"); + DevCon.WriteLn(Color_StrongGreen, "System Time Basis: %s", + EmuConfig.ManuallySetRealTimeClock ? "Manual RTC" : g_InputRecording.isActive() ? "Default Input Recording Time" : "Operating System Time"); + + std::tm input_tm{}; + std::tm resulting_tm{}; + + const int bios_settings_offset_seconds = 60 * (configParams1.timezoneOffset + configParams2.daylightSavings * 60); + + // CDVD internally uses GMT+9, 1-indexed months, and year offset of 2000 instead of 1900. + // tm struct uses 0-indexed months and year offset of 1900. if (EmuConfig.ManuallySetRealTimeClock) { - // Convert to GMT+9 (assumes GMT+0) - std::tm tm{}; - tm.tm_sec = EmuConfig.RtcSecond; - tm.tm_min = EmuConfig.RtcMinute; - tm.tm_hour = EmuConfig.RtcHour; - tm.tm_mday = EmuConfig.RtcDay; - tm.tm_mon = EmuConfig.RtcMonth - 1; - tm.tm_year = EmuConfig.RtcYear + 100; // 2000 - 1900 - tm.tm_isdst = 1; + resulting_tm.tm_sec = EmuConfig.RtcSecond; + resulting_tm.tm_min = EmuConfig.RtcMinute; + resulting_tm.tm_hour = EmuConfig.RtcHour; + resulting_tm.tm_mday = EmuConfig.RtcDay; + resulting_tm.tm_mon = EmuConfig.RtcMonth - 1; + resulting_tm.tm_year = EmuConfig.RtcYear + 100; + resulting_tm.tm_isdst = 0; - // Need this instead of mktime for timezone independence - std::time_t t = 0; - #if defined(_WIN32) - t = _mkgmtime(&tm) + 32400; //60 * 60 * 9 for GMT+9 - gmtime_s(&tm, &t); - #else - t = timegm(&tm) + 32400; - gmtime_r(&t, &tm); - #endif - - cdvd.RTC.second = tm.tm_sec; - cdvd.RTC.minute = tm.tm_min; - cdvd.RTC.hour = tm.tm_hour; - cdvd.RTC.day = tm.tm_mday; - cdvd.RTC.month = tm.tm_mon + 1; - cdvd.RTC.year = tm.tm_year - 100; + // Work backwards to input time by accounting for BIOS settings and GMT+9 defaultism. +#if defined(_WIN32) + const std::time_t input_time = _mkgmtime(&resulting_tm) + GMT9_OFFSET_SECONDS - bios_settings_offset_seconds; + gmtime_s(&input_tm, &input_time); +#else + const std::time_t input_time = timegm(&resulting_tm) + GMT9_OFFSET_SECONDS - bios_settings_offset_seconds; + gmtime_r(&input_time, &input_tm); +#endif } - // If we are recording, always use the same RTC setting - // for games that use the RTC to seed their RNG -- this is very important to be the same everytime! else if (g_InputRecording.isActive()) { - Console.WriteLn("Input Recording Active - Using Constant RTC of 04-03-2020 (DD-MM-YYYY)"); - // Why not just 0 everything? Some games apparently require the date to be valid in terms of when - // the PS2 / Game actually came out. (MGS3). So set it to a value well beyond any PS2 game's release date. - cdvd.RTC.second = 0; - cdvd.RTC.minute = 0; - cdvd.RTC.hour = 0; - cdvd.RTC.day = 4; - cdvd.RTC.month = 3; - cdvd.RTC.year = 20; + // Default input recording value (2020-03-04 00:00:00) if manual RTC is off. Well beyond any PS2 game's release date. + // Some games require a valid date in terms of when the PS2 / game actually came out (see: MGS3). + // Changing this will ruin compat with old input recordings (RNG seeding). + input_tm.tm_sec = 0; + input_tm.tm_min = 0; + input_tm.tm_hour = 0; + input_tm.tm_mday = 4; + input_tm.tm_mon = 2; + input_tm.tm_year = 120; + input_tm.tm_isdst = 0; + +#if defined(_WIN32) + const std::time_t resulting_time = _mkgmtime(&input_tm) - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds; + gmtime_s(&resulting_tm, &resulting_time); +#else + const std::time_t resulting_time = timegm(&input_tm) - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds; + gmtime_r(&resulting_time, &resulting_tm); +#endif } else { - // CDVD internally uses GMT+9. If you think the time's wrong, you're wrong. - // Set up your time zone and winter/summer in the BIOS. No PS2 BIOS I know of features automatic DST. - const std::time_t utc_time = std::time(nullptr); - const std::time_t gmt9_time = (utc_time + 32400); //60 * 60 * 9 - struct tm curtime = {}; + // User must set time zone and winter/summer DST in the BIOS for correct time. + const std::time_t input_time = std::time(nullptr) + GMT9_OFFSET_SECONDS; + const std::time_t resulting_time = input_time - GMT9_OFFSET_SECONDS + bios_settings_offset_seconds; + #ifdef _MSC_VER - gmtime_s(&curtime, &gmt9_time); + gmtime_s(&input_tm, &input_time); + gmtime_s(&resulting_tm, &resulting_time); #else - gmtime_r(&gmt9_time, &curtime); + gmtime_r(&input_time, &input_tm); + gmtime_r(&resulting_time, &resulting_tm); #endif - cdvd.RTC.second = static_cast(curtime.tm_sec); - cdvd.RTC.minute = static_cast(curtime.tm_min); - cdvd.RTC.hour = static_cast(curtime.tm_hour); - cdvd.RTC.day = static_cast(curtime.tm_mday); - cdvd.RTC.month = static_cast(curtime.tm_mon + 1); // WX returns Jan as "0" - cdvd.RTC.year = static_cast(curtime.tm_year - 100); // offset from 2000 } + // Send completed input time to the CDVD. + cdvd.RTC.second = static_cast(input_tm.tm_sec); + cdvd.RTC.minute = static_cast(input_tm.tm_min); + cdvd.RTC.hour = static_cast(input_tm.tm_hour); + cdvd.RTC.day = static_cast(input_tm.tm_mday); + cdvd.RTC.month = static_cast(input_tm.tm_mon + 1); + cdvd.RTC.year = static_cast(input_tm.tm_year - 100); + + // Print time that will appear in the user's BIOS rather than input time. + DevCon.WriteLn(Color_StrongGreen, "Resulting System Time: 20%02u-%02u-%02u %02u:%02u:%02u", + resulting_tm.tm_year - 100, resulting_tm.tm_mon + 1, resulting_tm.tm_mday, + resulting_tm.tm_hour, resulting_tm.tm_min, resulting_tm.tm_sec); + cdvdCtrlTrayClose(); } diff --git a/pcsx2/Config.h b/pcsx2/Config.h index f234b02ca9..86b1c4d225 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -1314,7 +1314,7 @@ struct Pcsx2Config InhibitScreensaver : 1, BackupSavestate : 1, McdFolderAutoManage : 1, - ManuallySetRealTimeClock : 1, + ManuallySetRealTimeClock : 1, // passes user-set real-time clock information to cdvd at startup UseSystemLocaleFormat : 1, // presents OS time format instead of yyyy-MM-dd HH:mm:ss for manual RTC HostFs : 1, diff --git a/pcsx2/ps2/BiosTools.cpp b/pcsx2/ps2/BiosTools.cpp index 7e92f11891..c83bff720e 100644 --- a/pcsx2/ps2/BiosTools.cpp +++ b/pcsx2/ps2/BiosTools.cpp @@ -68,6 +68,9 @@ void ReadOSDConfigParames() // Region settings for time/date and extended language configParams2.UC[1] = ((u32)params[3] & 0x78) << 1; // Daylight Savings, 24hr clock, Date format + configParams2.daylightSavings = configParams2.UC[1] & 0x10 ? 1 : 0; + configParams2.timeFormat = configParams2.UC[1] & 0x20 ? 1 : 0; + configParams2.dateFormat = configParams2.UC[1] & 0x80 ? 2 : (configParams2.UC[1] & 0x40 ? 1 : 0); // 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. configParams2.version = 2; configParams2.language = configParams1.language; diff --git a/pcsx2/ps2/BiosTools.h b/pcsx2/ps2/BiosTools.h index 139210b2c8..a738b64c10 100644 --- a/pcsx2/ps2/BiosTools.h +++ b/pcsx2/ps2/BiosTools.h @@ -43,7 +43,7 @@ typedef struct /** LANGUAGE_??? value */ /*16*/ u32 language : 5; /** timezone minutes offset from gmt */ - /*21*/ u32 timezoneOffset : 11; + /*21*/ s32 timezoneOffset : 11; }; u8 UC[4]; @@ -63,7 +63,7 @@ typedef struct /*00*/ u8 reserved : 4; /** 0=standard(winter), 1=daylight savings(summer) */ - /*04*/ u8 daylightSaving : 1; + /*04*/ u8 daylightSavings : 1; /** 0=24 hour, 1=12 hour */ /*05*/ u8 timeFormat : 1; /** 0=YYYYMMDD, 1=MMDDYYYY, 2=DDMMYYYY */