From 954d6f19b1e5b035c06e2871c89a322eaec2f781 Mon Sep 17 00:00:00 2001 From: Karan Shrivastava Date: Wed, 7 Oct 2026 17:11:46 -0400 Subject: [PATCH] fix(libSceRtc): answer out-of-range years in sceRtcGetDosTime A year before 1980 or after 2107 cannot be packed into the DOS time, so GetDosTime threw. Following the disassembly-derived fpPS4 reimplementation of the PS4 library, it now returns INVALID_YEAR after writing 0 for an earlier year and 0xff9fbf7d, the largest encodable time, for a later one. The TechnicalDebt entry now records fpPS4 as the source for the SetDosTime year decode as well. --- core/libs/prx/libSceRtc/Export.cpp | 2 +- core/libs/tests/GuestRtc.cpp | 6 +++--- docs/dev/TechnicalDebt.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceRtc/Export.cpp b/core/libs/prx/libSceRtc/Export.cpp index a8986f228..7ea574fd7 100644 --- a/core/libs/prx/libSceRtc/Export.cpp +++ b/core/libs/prx/libSceRtc/Export.cpp @@ -373,7 +373,7 @@ int APS5_VABI sceRtcGetDosTime(const RtcDateTime* time, uint32_t* dos_time) { if (!dos_time) return SCE_RTC_ERROR_INVALID_POINTER; if (const int result = validate(time); result != 0) return result; if (time->year < 1980 || time->year > 2107) { - NotImplemented_nid_no_patch(__func__); + *dos_time = time->year < 1980 ? 0 : 0xff9fbf7du; return SCE_RTC_ERROR_INVALID_YEAR; } *dos_time = static_cast(time->year - 1980) << 25 | static_cast(time->month) << 21 diff --git a/core/libs/tests/GuestRtc.cpp b/core/libs/tests/GuestRtc.cpp index 576dacbb8..b556c13cd 100644 --- a/core/libs/tests/GuestRtc.cpp +++ b/core/libs/tests/GuestRtc.cpp @@ -120,9 +120,9 @@ int main() { const RtcDateTime dosBadMonth{2024, 13, 1, 0, 0, 0, 0}; Require(sceRtcGetDosTime(&dosBadMonth, &dosTime) == invalidMonth); const RtcDateTime dosEarly{1979, 12, 31, 0, 0, 0, 0}; - bool dosEarlyThrew = false; - try { sceRtcGetDosTime(&dosEarly, &dosTime); } catch (const std::exception&) { dosEarlyThrew = true; } - Require(dosEarlyThrew); + Require(sceRtcGetDosTime(&dosEarly, &dosTime) == invalidYear && dosTime == 0); + const RtcDateTime dosLate{2108, 1, 1, 0, 0, 0, 0}; + Require(sceRtcGetDosTime(&dosLate, &dosTime) == invalidYear && dosTime == 0xff9fbf7du); converted = RtcDateTime{1, 1, 1, 1, 1, 1, 1}; Require(sceRtcSetDosTime(&converted, 0x585d645cu) == 0 && Equal(converted, RtcDateTime{2024, 2, 29, 12, 34, 56, 0})); Require(sceRtcSetDosTime(&converted, 0x7f9fbf7du) == 0 && Equal(converted, RtcDateTime{2043, 12, 31, 23, 59, 58, 0})); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 084451064..171cf1b64 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -203,7 +203,7 @@ Throughout the project, every function at every stage either **does exactly what - [Resource registration](../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) (libSceAgcDriver) - registration always fails with `RESOURCE_REGISTRATION_UNAVAILABLE`, so the owner and resource queries, `sceAgcDriverSetResourceUserData`, `sceAgcDriverFindResourcesPublic` and `sceAgcDriverUnregisterAllResourcesForOwner` return the same error without reading their arguments. Their parameter lists follow the libSceGnmDriver counterparts and are unverified. `sceAgcDriverGetResourceRegistrationMaxNameLength` writes 0xfc and returns 0, as [prosper](https://github.com/mattias800/prosper) does from a title's registration configuration; the `uint32_t*` parameter matches the SDK wrapper `sce::Agc::ResourceRegistration::getMaxNameLength(unsigned int*)`. sharpemu writes 256 and KytyPS5 returns `RESOURCE_REGISTRATION_UNAVAILABLE`, so the value is unverified; a null output throws - [Shader registration](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - a shader header is accepted at any address: PPSA26344 passes headers 4 and 1 bytes past an 8-byte boundary; whether the console refuses an alignment is unknown - [sceNgs2VoiceControl sampler filter](../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - param `0x1000000a`, its layout and meaning follow KytyPS5 only: location 1 type 1 is an RBJ low-pass biquad at the system sample rate after resampling, scaled by `level`, a set `channel_mask` bit bypasses that channel, type 0 turns the filter off. Other locations and types throw. The voice keeps playing a filter tail after its last block until the history is zero, with outputs below 1e-20 flushed to zero (a KytyPS5 choice); the console's tail length is unknown -- [sceRtcGetDosTime, sceRtcSetDosTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - bit layout from shadPS4 (PS4, the standard FAT date/time). Years outside 1980-2107 throw in `GetDosTime`, since no reference says what the library returns for them. `SetDosTime` decodes the year as the unsigned 7-bit FAT field (1980-2107), where shadPS4 sign-extends the 16-bit date to years before 1980. `SetDosTime` does not validate the fields, as in shadPS4 +- [sceRtcGetDosTime, sceRtcSetDosTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - bit layout from shadPS4 (PS4, the standard FAT date/time). A year outside 1980-2107 makes `GetDosTime` return `INVALID_YEAR` after writing 0 for an earlier one and `0xff9fbf7d` (2107-12-31 23:59:58, the largest encodable time) for a later one, and `SetDosTime` decodes the year as the unsigned 7-bit FAT field (1980-2107) without validating the fields, as the disassembly-derived [fpPS4](https://github.com/red-prig/fpPS4) reimplementation of the PS4 library does; not confirmed on a PS5 title, and shadPS4 instead masks out-of-range years into the packed value and sign-extends the 16-bit date - [sce::Json::InitParameter2](../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - layout inferred, not known: assumed to start with the allocator, its user data and the file buffer size, in that order, and the whole struct is assumed to fit in 40 bytes; `Initializer::initialize(InitParameter2)` ignores them as `initialize(InitParameter)` does - [sceAjmBatchJobSetResampleParameters, sceAjmBatchJobGetResampleInfo](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm) - the ratio is taken as decoded samples per output sample and num_samples as the decoded samples the resampler holds, both inferred from FMOD (Hades II); the console's interpolation is unknown, a 4-point Catmull-Rom is used. The flags argument is ignored, as in shadPS4 and Kyty