From 4f25a96928269b9ed4b2ef5e4e072d09698c2c05 Mon Sep 17 00:00:00 2001 From: gibbed Date: Sat, 21 Nov 2020 21:31:08 -0600 Subject: [PATCH] [Kernel] Overhaul Rtl time functions. - Add date third party submodule. - [Kernel] Add xclock. - [Kernel] Reimplement RtlTimeToTimeFields using std::chrono/date/xclock. - [Kernel] Reimplement RtlTimeFieldsToTime using std::chrono/date/xclock. - Supersedes #1612. --- .gitmodules | 3 + src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc | 69 ++++++++++---------- src/xenia/kernel/xclock.h | 78 +++++++++++++++++++++++ third_party/date | 1 + 4 files changed, 118 insertions(+), 33 deletions(-) create mode 100644 src/xenia/kernel/xclock.h create mode 160000 third_party/date diff --git a/.gitmodules b/.gitmodules index 597affeb8..b8f139b8a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -70,3 +70,6 @@ [submodule "third_party/premake-androidmk"] path = third_party/premake-androidmk url = https://github.com/Triang3l/premake-androidmk.git +[submodule "third_party/date"] + path = third_party/date + url = https://github.com/HowardHinnant/date.git diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index e06daba48..db3cef5db 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -21,14 +21,10 @@ #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" +#include "xenia/kernel/xclock.h" #include "xenia/kernel/xevent.h" #include "xenia/kernel/xthread.h" -#if XE_PLATFORM_WIN32 -#include "xenia/base/platform_win.h" -#define timegm _mkgmtime -#endif - namespace xe { namespace kernel { namespace xboxkrnl { @@ -507,44 +503,51 @@ struct X_TIME_FIELDS { xe::be milliseconds; xe::be weekday; }; -static_assert(sizeof(X_TIME_FIELDS) == 16, "Must be LARGEINTEGER"); +static_assert_size(X_TIME_FIELDS, 16); -// https://support.microsoft.com/en-us/kb/167296 +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimetotimefields void RtlTimeToTimeFields(lpqword_t time_ptr, pointer_t time_fields_ptr) { - int64_t time_ms = time_ptr.value() / 10000 - 11644473600000LL; - time_t timet = time_ms / 1000; - struct tm* tm = gmtime(&timet); - - time_fields_ptr->year = tm->tm_year + 1900; - time_fields_ptr->month = tm->tm_mon + 1; - time_fields_ptr->day = tm->tm_mday; - time_fields_ptr->hour = tm->tm_hour; - time_fields_ptr->minute = tm->tm_min; - time_fields_ptr->second = tm->tm_sec; - time_fields_ptr->milliseconds = time_ms % 1000; - time_fields_ptr->weekday = tm->tm_wday; + auto tp = XClock::to_sys(XClock::from_file_time(time_ptr.value())); + auto dp = date::floor(tp); + auto year_month_day = date::year_month_day{dp}; + auto weekday = date::weekday{dp}; + auto time = date::hh_mm_ss{date::floor(tp - dp)}; + time_fields_ptr->year = static_cast(year_month_day.year()); + time_fields_ptr->month = static_cast(year_month_day.month()); + time_fields_ptr->day = static_cast(year_month_day.day()); + time_fields_ptr->weekday = weekday.c_encoding(); + time_fields_ptr->hour = time.hours().count(); + time_fields_ptr->minute = time.minutes().count(); + time_fields_ptr->second = static_cast(time.seconds().count()); + time_fields_ptr->milliseconds = + static_cast(time.subseconds().count()); } DECLARE_XBOXKRNL_EXPORT1(RtlTimeToTimeFields, kNone, kImplemented); +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimefieldstotime dword_result_t RtlTimeFieldsToTime(pointer_t time_fields_ptr, lpqword_t time_ptr) { - struct tm tm; - tm.tm_year = time_fields_ptr->year - 1900; - tm.tm_mon = time_fields_ptr->month - 1; - tm.tm_mday = time_fields_ptr->day; - tm.tm_hour = time_fields_ptr->hour; - tm.tm_min = time_fields_ptr->minute; - tm.tm_sec = time_fields_ptr->second; - tm.tm_isdst = 0; - time_t timet = timegm(&tm); - if (timet == -1) { - // set last error = ERROR_INVALID_PARAMETER + if (time_fields_ptr->year < 1601 || time_fields_ptr->month < 1 || + time_fields_ptr->month > 11 || time_fields_ptr->day < 1 || + time_fields_ptr->hour > 23 || time_fields_ptr->minute > 59 || + time_fields_ptr->second > 59 || time_fields_ptr->milliseconds > 999) { return 0; } - uint64_t time = - ((timet + 11644473600LL) * 1000 + time_fields_ptr->milliseconds) * 10000; - *time_ptr = time; + auto year = date::year{time_fields_ptr->year}; + auto month = date::month{time_fields_ptr->month}; + auto day = date::day{time_fields_ptr->day}; + auto year_month_day = date::year_month_day{year, month, day}; + if (!year_month_day.ok()) { + return 0; + } + auto dp = static_cast(year_month_day); + std::chrono::system_clock::time_point time = dp; + time += std::chrono::hours{time_fields_ptr->hour}; + time += std::chrono::minutes{time_fields_ptr->minute}; + time += std::chrono::seconds{time_fields_ptr->second}; + time += std::chrono::milliseconds{time_fields_ptr->milliseconds}; + *time_ptr = XClock::to_file_time(XClock::from_sys(time)); return 1; } DECLARE_XBOXKRNL_EXPORT1(RtlTimeFieldsToTime, kNone, kImplemented); diff --git a/src/xenia/kernel/xclock.h b/src/xenia/kernel/xclock.h new file mode 100644 index 000000000..809dfd775 --- /dev/null +++ b/src/xenia/kernel/xclock.h @@ -0,0 +1,78 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XCLOCK_H_ +#define XENIA_KERNEL_XCLOCK_H_ + +#include + +#include "xenia/base/clock.h" + +#include "third_party/date/include/date/date.h" + +namespace xe { +namespace kernel { + +struct XClock { + using rep = int64_t; + using period = std::ratio_multiply, std::nano>; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = false; + + static time_point now() noexcept { + return from_file_time(Clock::QueryGuestSystemTime()); + } + + static uint64_t to_file_time(time_point const& tp) noexcept { + return static_cast(tp.time_since_epoch().count()); + } + + static time_point from_file_time(uint64_t const& tp) noexcept { + return time_point{duration{tp}}; + } + + static std::chrono::system_clock::time_point to_sys(time_point const& tp) { + // TODO(gibbed): verify behavior under Linux + using sys_duration = std::chrono::system_clock::duration; + using sys_time = std::chrono::system_clock::time_point; + auto dp = tp; + dp += system_clock_delta(); + auto cdp = std::chrono::time_point_cast(dp); + return sys_time{cdp.time_since_epoch()}; + } + + static time_point from_sys(std::chrono::system_clock::time_point const& tp) { + // TODO(gibbed): verify behavior under Linux + auto ctp = std::chrono::time_point_cast(tp); + auto dp = time_point{ctp.time_since_epoch()}; + dp -= system_clock_delta(); + return dp; + } + + private: + // The delta between std::chrono::system_clock (Jan 1 1970) and Xenon file + // time (Jan 1 1601), in seconds. In the spec std::chrono::system_clock's + // epoch is undefined, but C++20 cements it as Jan 1 1970. + static constexpr std::chrono::seconds system_clock_delta() { + auto filetime_epoch = date::year{1601} / date::month{1} / date::day{1}; + auto system_clock_epoch = date::year{1970} / date::month{1} / date::day{1}; + std::chrono::system_clock::time_point fp{ + static_cast(filetime_epoch)}; + std::chrono::system_clock::time_point sp{ + static_cast(system_clock_epoch)}; + return std::chrono::floor(fp.time_since_epoch() - + sp.time_since_epoch()); + } +}; + +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XCLOCK_H_ diff --git a/third_party/date b/third_party/date new file mode 160000 index 000000000..97246a638 --- /dev/null +++ b/third_party/date @@ -0,0 +1 @@ +Subproject commit 97246a638a6d8f0269f4555c5e31106a86e3fd94