Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion pcsx2-qt/Settings/EmulationSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.<br>"
"This time is only applied upon booting the PS2; changing it while in-game will have no effect.<br>"
"NOTE: This assumes you have your PS2 set to the default timezone of GMT+0 and default DST of Summer Time.<br>"
"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."));
Expand Down
127 changes: 76 additions & 51 deletions pcsx2/CDVD/CDVD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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;

// 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;
}
// 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!
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;

// 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
}
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<u8>(curtime.tm_sec);
cdvd.RTC.minute = static_cast<u8>(curtime.tm_min);
cdvd.RTC.hour = static_cast<u8>(curtime.tm_hour);
cdvd.RTC.day = static_cast<u8>(curtime.tm_mday);
cdvd.RTC.month = static_cast<u8>(curtime.tm_mon + 1); // WX returns Jan as "0"
cdvd.RTC.year = static_cast<u8>(curtime.tm_year - 100); // offset from 2000
}

// Send completed input time to the CDVD.
cdvd.RTC.second = static_cast<u8>(input_tm.tm_sec);
cdvd.RTC.minute = static_cast<u8>(input_tm.tm_min);
cdvd.RTC.hour = static_cast<u8>(input_tm.tm_hour);
cdvd.RTC.day = static_cast<u8>(input_tm.tm_mday);
cdvd.RTC.month = static_cast<u8>(input_tm.tm_mon + 1);
cdvd.RTC.year = static_cast<u8>(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();
}

Expand Down
2 changes: 1 addition & 1 deletion pcsx2/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions pcsx2/ps2/BiosTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions pcsx2/ps2/BiosTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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 */
Expand Down