sceKernelReadTsc only returns the CPU's RDTSC when the host RDTSC reader is
available (currently 64-bit Windows); on Linux and macOS it falls back to the
QPC-based Stopwatch. ResolveKernelTscFrequency, however, still consulted the
CPUID-reported hardware TSC frequency in that case, so sceKernelGetTscFrequency
reported a multi-GHz rate while ReadTsc was ticking at the Stopwatch frequency.
A guest computing elapsed = readTscDelta / frequency then gets the wrong time
on those platforms.
Gate the calibrated/CPUID frequencies on RDTSC actually being available (the
calibration path was already self-gated; the CPUID path was not) and otherwise
report the Stopwatch frequency, keeping ReadTsc and GetTscFrequency consistent.
The selection logic is extracted into a pure, host-independent helper so both
branches can be unit tested, including a regression test asserting that a host
without RDTSC reports the Stopwatch frequency rather than the hardware TSC.
The guest local tick was decoded into a DateTimeKind.Utc DateTime and then
passed to TimeZoneInfo.ConvertTimeToUtc together with TimeZoneInfo.Local.
That overload throws ArgumentException when a Utc-kind value is paired with a
source zone other than UTC, so on any machine whose local zone is not UTC the
export caught the exception and always returned INVALID_ARGUMENT.
Re-tag the decoded value as DateTimeKind.Unspecified so it is interpreted as
local wall-clock time and converted correctly. The reverse direction
(sceRtcConvertUtcToLocalTime) was already correct because ConvertTimeFromUtc
accepts a Utc-kind input.
Add a RtcExports unit-test suite covering the tick/calendar conversions, DOS
time packing, Win32 file time, leap-year and validation error codes, and a
regression test that round-trips a UTC tick through local time and back.