/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** * * @modified Tom Clay, 2026 - Adapted for ReXGlue runtime */ #pragma once #include #include #include #include #if !defined(__cpp_lib_chrono) || __cpp_lib_chrono < 201907L namespace std::chrono { template struct clock_time_conversion; template auto clock_cast(const std::chrono::time_point& t) { return clock_time_conversion{}(t); } } // namespace std::chrono #endif namespace rex { using hundrednano = std::ratio<1, 10000000>; namespace chrono { using hundrednanoseconds = std::chrono::duration; // TODO(JoelLinn) define xstead_clock xsystem_clock etc. namespace detail { // Implementation detail: NtSystemClock template for Host/Guest time domains. // Trick to reduce code duplication and keep all the chrono template magic // working. enum class Domain { // boring host clock: Host, // adheres to guest scaling (differrent speed, changing clock drift etc): Guest }; template struct NtSystemClock { using rep = int64_t; using period = hundrednano; using duration = hundrednanoseconds; using time_point = std::chrono::time_point>; // This really depends on the context the clock is used in: // static constexpr bool is_steady = false; public: // The delta between std::chrono::system_clock (Jan 1 1970) and NT 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 unix_epoch_delta() { using std::chrono::steady_clock; auto filetime_epoch = std::chrono::year{1601} / std::chrono::month{1} / std::chrono::day{1}; auto system_clock_epoch = std::chrono::year{1970} / std::chrono::month{1} / std::chrono::day{1}; auto fp = static_cast(static_cast(filetime_epoch)); auto sp = static_cast( static_cast(system_clock_epoch)); return fp.time_since_epoch() - sp.time_since_epoch(); } public: static constexpr uint64_t to_file_time(time_point const& tp) noexcept { return static_cast(tp.time_since_epoch().count()); } static constexpr time_point from_file_time(uint64_t const& tp) noexcept { return time_point{duration{tp}}; } // To convert XSystemClock to sys, do clock_cast(tp) first // Only available for Host domain (Guest time must be converted via clock_cast) static constexpr std::chrono::system_clock::time_point to_sys(const time_point& tp) requires(domain_ == Domain::Host) { using sys_duration = std::chrono::system_clock::duration; using sys_time = std::chrono::system_clock::time_point; auto dp = tp; dp += unix_epoch_delta(); auto cdp = std::chrono::time_point_cast(dp); return sys_time{cdp.time_since_epoch()}; } static constexpr time_point from_sys(const std::chrono::system_clock::time_point& tp) requires(domain_ == Domain::Host) { auto ctp = std::chrono::time_point_cast(tp); auto dp = time_point{ctp.time_since_epoch()}; dp -= unix_epoch_delta(); return dp; } [[nodiscard]] static time_point now() noexcept { if constexpr (domain_ == Domain::Host) { // QueryHostSystemTime() returns windows epoch times even on POSIX return from_file_time(Clock::QueryHostSystemTime()); } else if constexpr (domain_ == Domain::Guest) { return from_file_time(Clock::QueryGuestSystemTime()); } } }; } // namespace detail // Unscaled system clock which can be used for filetime <-> system_clock // conversion using WinSystemClock = detail::NtSystemClock; // Guest system clock, scaled using XSystemClock = detail::NtSystemClock; } // namespace chrono } // namespace rex namespace std::chrono { template <> struct clock_time_conversion<::rex::chrono::WinSystemClock, ::rex::chrono::XSystemClock> { using WClock_ = ::rex::chrono::WinSystemClock; using XClock_ = ::rex::chrono::XSystemClock; template typename WClock_::time_point operator()( const std::chrono::time_point& t) const { // Consult chrono_steady_cast.h for explanation on this: std::atomic_thread_fence(std::memory_order_acq_rel); auto w_now = WClock_::now(); auto x_now = XClock_::now(); std::atomic_thread_fence(std::memory_order_acq_rel); auto delta = (t - x_now); if (!REXCVAR_GET(clock_no_scaling)) { delta = std::chrono::floor(delta * rex::chrono::Clock::guest_time_scalar()); } return w_now + delta; } }; template <> struct clock_time_conversion<::rex::chrono::XSystemClock, ::rex::chrono::WinSystemClock> { using WClock_ = ::rex::chrono::WinSystemClock; using XClock_ = ::rex::chrono::XSystemClock; template typename XClock_::time_point operator()( const std::chrono::time_point& t) const { // Consult chrono_steady_cast.h for explanation on this: std::atomic_thread_fence(std::memory_order_acq_rel); auto w_now = WClock_::now(); auto x_now = XClock_::now(); std::atomic_thread_fence(std::memory_order_acq_rel); rex::chrono::hundrednanoseconds delta = (t - w_now); if (!REXCVAR_GET(clock_no_scaling)) { delta = std::chrono::floor(delta / rex::chrono::Clock::guest_time_scalar()); } return x_now + delta; } }; } // namespace std::chrono