mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
fix(libSceSystemService): report the host time zone and summer time
sceSystemServiceParamGetInt answered 0 for the time zone (parameter 4) and summer time (parameter 5), so the console looked set to UTC while libSceRtc and libc convert local time with the host's zone. Titles add the time zone in minutes and 60 more minutes when summer time is 1 to UTC to get the local time (Apollo, GoldHEN's frame logger). Both now follow the host's local time zone: summer time is whether it is in effect, and the time zone is the current offset east of UTC less the summer time shift while it is. On Windows that shift is the C runtime's own summer time bias (_get_dstbias), the one localtime_s applied, not a fixed 60 minutes. The UCRT reads the system zone when libc.prx loads and keeps that zone's bias after TZ is set later, so on a host whose system zone has no summer time (the CI runner's UTC) a TZ of CET-1CEST shifted by 0 and the time zone came out as 0. The cause and the change are screamm's, from the review. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
ea4cc112a0
commit
ff1de9150d
4 files changed
+100
-3
No files matched your search
@@ -79,6 +79,15 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(guest_system_service_language PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_test_executable(guest_system_service_time_zone_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemServiceTimeZone.cpp)
|
||||
target_include_directories(guest_system_service_time_zone_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_system_service_time_zone_tests PRIVATE libSceSystemService libc)
|
||||
configure_windows_unwind(guest_system_service_time_zone_tests)
|
||||
add_test(NAME guest_system_service_time_zone COMMAND guest_system_service_time_zone_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_system_service_time_zone PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_test_executable(guest_system_kill_app_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemKillApp.cpp)
|
||||
target_include_directories(guest_system_kill_app_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_system_kill_app_tests PRIVATE libSceSystemService libc)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <charconv>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
@@ -40,6 +42,34 @@ int ConsoleLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
struct ConsoleTimeZone {
|
||||
int minutes;
|
||||
bool summer;
|
||||
};
|
||||
|
||||
ConsoleTimeZone HostTimeZone() {
|
||||
const std::time_t now = std::time(nullptr);
|
||||
std::tm local{};
|
||||
#if defined(_WIN32)
|
||||
const bool converted = localtime_s(&local, &now) == 0;
|
||||
#else
|
||||
const bool converted = localtime_r(&now, &local) != nullptr;
|
||||
#endif
|
||||
if (!converted) throw std::runtime_error("sceSystemServiceParamGetInt: the host local time is unavailable");
|
||||
const std::chrono::sys_days date = std::chrono::year{local.tm_year + 1900} / std::chrono::month{static_cast<unsigned>(local.tm_mon + 1)} / std::chrono::day{static_cast<unsigned>(local.tm_mday)};
|
||||
const auto localSeconds = date.time_since_epoch() + std::chrono::hours{local.tm_hour} + std::chrono::minutes{local.tm_min} + std::chrono::seconds{local.tm_sec};
|
||||
const auto offset = std::chrono::duration_cast<std::chrono::minutes>(localSeconds - std::chrono::seconds{now});
|
||||
const bool summer = local.tm_isdst > 0;
|
||||
#if defined(_WIN32)
|
||||
long daylightBias = 0;
|
||||
_get_dstbias(&daylightBias);
|
||||
const int summerMinutes = summer ? static_cast<int>(-daylightBias / 60) : 0;
|
||||
#else
|
||||
const int summerMinutes = summer ? 60 : 0;
|
||||
#endif
|
||||
return {static_cast<int>(offset.count()) - summerMinutes, summer};
|
||||
}
|
||||
|
||||
constexpr char MainExecutable[] = "/app0/eboot.bin";
|
||||
constexpr char ApplicationRoot[] = "/app0/";
|
||||
constexpr char MainExecutableVariable[] = "APS5_EBOOT";
|
||||
@@ -270,8 +300,8 @@ int APS5_VABI sceSystemServiceParamGetInt(int paramId, int* value) {
|
||||
case SYSTEM_SERVICE_PARAM_ID_400: *value = ConsoleLanguage(); break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT: *value = SYSTEM_SERVICE_PARAM_DATE_FORMAT_DDMMYYYY; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT: *value = SYSTEM_SERVICE_PARAM_TIME_FORMAT_24HOUR; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_TIME_ZONE: *value = 0; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_SUMMERTIME: *value = 0; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_TIME_ZONE: *value = HostTimeZone().minutes; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_SUMMERTIME: *value = HostTimeZone().summer ? 1 : 0; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_GAME_PARENTAL_LEVEL: *value = SYSTEM_SERVICE_PARAM_GAME_PARENTAL_OFF; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN: *value = SYSTEM_SERVICE_PARAM_ENTER_BUTTON_CROSS; break;
|
||||
default: *value = 0; break;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceSystemService/SystemService.hpp"
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
extern "C" int APS5_VABI sceSystemServiceParamGetInt(int paramId, int* value);
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
void SetTimeZone(const char* zone) {
|
||||
#ifdef _WIN32
|
||||
_putenv_s("TZ", zone);
|
||||
_tzset();
|
||||
#else
|
||||
setenv("TZ", zone, 1);
|
||||
tzset();
|
||||
#endif
|
||||
}
|
||||
|
||||
int ParamInt(int paramId) {
|
||||
int value = -1;
|
||||
Require(sceSystemServiceParamGetInt(paramId, &value) == SYSTEM_SERVICE_OK);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool HostSummerTime() {
|
||||
const std::time_t now = std::time(nullptr);
|
||||
std::tm local{};
|
||||
#ifdef _WIN32
|
||||
Require(localtime_s(&local, &now) == 0);
|
||||
#else
|
||||
Require(localtime_r(&now, &local) != nullptr);
|
||||
#endif
|
||||
return local.tm_isdst > 0;
|
||||
}
|
||||
|
||||
void RequireZone(const char* zone, int minutes, int summer) {
|
||||
SetTimeZone(zone);
|
||||
Require(ParamInt(SYSTEM_SERVICE_PARAM_ID_TIME_ZONE) == minutes);
|
||||
Require(ParamInt(SYSTEM_SERVICE_PARAM_ID_SUMMERTIME) == summer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
RequireZone("IST-5:30", 330, 0);
|
||||
RequireZone("BRT+3", -180, 0);
|
||||
RequireZone("UTC0", 0, 0);
|
||||
SetTimeZone("CET-1CEST");
|
||||
Require(ParamInt(SYSTEM_SERVICE_PARAM_ID_TIME_ZONE) == 60);
|
||||
Require(ParamInt(SYSTEM_SERVICE_PARAM_ID_SUMMERTIME) == (HostSummerTime() ? 1 : 0));
|
||||
#ifndef _WIN32
|
||||
RequireZone("CET-1CEST,0/0,J365/25", 60, 1);
|
||||
#endif
|
||||
}
|
||||
@@ -12,7 +12,8 @@
|
||||
- [sceSystemServiceLaunchApp](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 runs one title and cannot start another, so every call throws without reading its arguments. The (title ID, null-terminated argv, launch parameters) signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L141-L148), whose `AppLauncher` Prospero Explorer (PPSA99109) and Prospero Multi Tools (PPSA99110) import it through and which passes a 0x20-byte launch context or null; the console's argument checks and errors are unknown
|
||||
- [sceSystemServiceLaunchWebBrowser](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the console's error for an unavailable web browser is unknown; it returns the kernel's `EOPNOTSUPP` (0x8002002D) for any arguments. The (uri, param) signature follows [RommPS](https://github.com/s0liton/RommPS/blob/754d4d60f25938a5136b9e2efcd074313c4386c5/platform/ps5/app/rommps/examples/rommps/kit/credits.cpp#L86-L95), which passes a URL and a null param and only tests the result for zero; the param layout and the console's argument checks are unknown
|
||||
- [sceSystemServiceParamGetString](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the system name (parameter 6) is the fixed `PS5`, not the console's name; other parameters and buffers of 1 to 64 bytes throw. PPSA26344 at `0xA87935` passes parameter 6 with a 65-byte buffer
|
||||
- [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - console language numbers 0 to 29 follow the PS4 SDK enum in [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/06fcf7fd108fa080035cdd3259ed22eb07e0508c/include/orbis/_types/sys_service.h#L54-L83) (21 Arabic). 30 (Ukrainian) is not in it and follows PPSA99505 ([Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/app/i18n.cpp#L29-L60)) and PPSA99002 ([ProsperoLight](https://github.com/blackbearreloaded/ProsperoLight/blob/b708bbde3457bef1073a9ab6959957cb691e8e2f/include/i18n.hpp#L252-L309)), whose numbers agree with the enum. Not confirmed on a console, and neither title starts on `main` yet. Parameter 400 returns the same number as parameter 1: PPSA01325 asks for 400 first, asks for 1 when that fails, and uses either answer as an index into the same 31 languages; in the PS5 10.01 and 12.60 `libSceSystemService` modules parameter 400 reads the same registry value as parameter 1, without parameter 1's mapping of newer language numbers by SDK version, and returns 0x80a10003 when an imported check that is not identified returns 1; the 4.03 module has no parameter 400. Neither the mapping nor that error is modelled, and the parameter's name is not known. Without `ANYPS5_LANGUAGE` the language is English (United States); the date format, time format, time zone, summer time and enter button stay fixed
|
||||
- [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - console language numbers 0 to 29 follow the PS4 SDK enum in [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/06fcf7fd108fa080035cdd3259ed22eb07e0508c/include/orbis/_types/sys_service.h#L54-L83) (21 Arabic). 30 (Ukrainian) is not in it and follows PPSA99505 ([Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/app/i18n.cpp#L29-L60)) and PPSA99002 ([ProsperoLight](https://github.com/blackbearreloaded/ProsperoLight/blob/b708bbde3457bef1073a9ab6959957cb691e8e2f/include/i18n.hpp#L252-L309)), whose numbers agree with the enum. Not confirmed on a console, and neither title starts on `main` yet. Parameter 400 returns the same number as parameter 1: PPSA01325 asks for 400 first, asks for 1 when that fails, and uses either answer as an index into the same 31 languages; in the PS5 10.01 and 12.60 `libSceSystemService` modules parameter 400 reads the same registry value as parameter 1, without parameter 1's mapping of newer language numbers by SDK version, and returns 0x80a10003 when an imported check that is not identified returns 1; the 4.03 module has no parameter 400. Neither the mapping nor that error is modelled, and the parameter's name is not known. Without `ANYPS5_LANGUAGE` the language is English (United States); the date format, time format and enter button stay fixed
|
||||
- [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the time zone (parameter 4) and summer time (parameter 5) follow the host's local time zone at the moment of the call. Titles compute the local time as UTC plus the time zone in minutes plus 60 minutes when summer time is 1 ([Apollo](https://github.com/bucanero/apollo-ps4/blob/252a9d5a187c463994263009851a179f4f910c49/source/exec_cmd.c#L70-L92), [GoldHEN's frame logger](https://github.com/GoldHEN/GoldHEN_Plugins_Repository/blob/6c3a06f53d9caebadfb0aeed9bb94119012010a7/plugin_src/frame_logger/source/main.c#L60-L82), and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/413d2e3518fb435f40971d238fe0ecae7ee551da/src/SharpProspero/Platform/SystemParameters.cs#L75-L79) calls them the offset from UTC in minutes and whether summer time is in effect), so the time zone is the host's current offset east of UTC less the summer time shift while summer time is in effect: 60 minutes on Linux, and on Windows the C runtime's own summer time bias (`_get_dstbias`), which is also what shifted its local time. For a host zone whose summer time is not one hour, Linux keeps the sum right and the standard offset wrong, and Windows the opposite; how the console reports such a zone is unknown. libkernel's `sceKernelConvertUtcToLocaltime`, `sceKernelConvertLocaltimeToUtc` and `sceKernelGettimezone` still use UTC
|
||||
|
||||
### Functional
|
||||
|
||||
|
||||
Reference in new issue
Block a user