From 9a4c8eab8bf56cbb9738029d8283c241d74306e2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Thu, 24 Sep 2026 22:57:49 +0300 Subject: [PATCH] fix(libc): update locale signatures + add sysv abi to math funcs --- README.md | 4 +- core/libs/prx/libc/src/LocaleSupport.cpp | 52 +++++++++++----------- core/libs/prx/libc/src/Math.cpp | 56 ++++++++++++------------ core/libs/tests/GuestLocale.cpp | 45 ++++++++++++++++--- 4 files changed, 94 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index fa7f3cc7b..371663a4d 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ Execution reaches `_start`, [stack unwinding](core/libs/prx/libc/src/exception/U The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces validated via [Spirv-Tools](3rdparty/SPIRV-Tools) SPIR-V. -The real game successfully presents the logo and loads the main menu, then crashes before the menu buttons appear. +The real game reaches the main menu, which renders correctly with buttons and audio. -Current error: Access Violation at ~frame 168. +Current stop: [libScePad](core/libs/prx/libScePad) does not record the input, so the button cannot be pressed. ## Build diff --git a/core/libs/prx/libc/src/LocaleSupport.cpp b/core/libs/prx/libc/src/LocaleSupport.cpp index c98a5658a..b16a1bb7c 100644 --- a/core/libs/prx/libc/src/LocaleSupport.cpp +++ b/core/libs/prx/libc/src/LocaleSupport.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "prx/libc/include/General.hpp" #include "prx/libc/include/ApplicationHeap.hpp" @@ -50,14 +51,14 @@ constexpr std::array MakeClassificationTable() { std::array table{}; for (unsigned int value = 0; value < 256; ++value) { short mask = 0; - if (value < 32 || value == 127) mask |= 0x20; - if (value == ' ' || (value >= '\t' && value <= '\r')) mask |= 0x08; - if (value == ' ' || value == '\t') mask |= 0x40; - if (value >= 'A' && value <= 'Z') mask |= 0x101; - if (value >= 'a' && value <= 'z') mask |= 0x102; - if (value >= '0' && value <= '9') mask |= 0x04; - if ((value >= '0' && value <= '9') || (value >= 'A' && value <= 'F') || (value >= 'a' && value <= 'f')) mask |= 0x80; - if (value >= 33 && value <= 126 && (mask & 0x107) == 0) mask |= 0x10; + if (value < 32 || value == 127) mask |= 0x80; + if (value == ' ') mask |= 0x04; + if (value >= '\t' && value <= '\r') mask |= 0x40; + if (value >= 'A' && value <= 'Z') mask |= 0x02; + if (value >= 'a' && value <= 'z') mask |= 0x10; + if (value >= '0' && value <= '9') mask |= 0x20; + if ((value >= '0' && value <= '9') || (value >= 'A' && value <= 'F') || (value >= 'a' && value <= 'f')) mask |= 0x01; + if (value >= 33 && value <= 126 && (mask & 0x232) == 0) mask |= 0x08; table[value + 1] = mask; } return table; @@ -102,11 +103,12 @@ void APS5_VABI _ZNSt8ios_baseD2Ev_nid_postfix(GuestLocale::IosBase* self) { self->locale = nullptr; } -void APS5_VABI _ZNSt6locale5_InitEv_nid_postfix() { +GuestLocale::Implementation* APS5_VABI _ZNSt6locale5_InitEv_nid_postfix() { std::lock_guard lock(g_localeInitMutex); if (!g_localeInitialized) { g_localeInitialized = true; } + return &g_classicLocale; } void APS5_VABI _ZNSt6locale5facet9_RegisterEv_nid_postfix(GuestLocale::Facet* self) { @@ -135,25 +137,23 @@ void APS5_VABI _ZNSt8_LocinfoD1Ev_nid_postfix(GuestLocale::LocinfoStorage* self) if (self == nullptr) throw std::invalid_argument("_Locinfo destructor: null object"); } -wchar_t* APS5_VABI _Mbtowcx_nid_postfix(wchar_t* dst, const char* src, std::size_t count, mbstate_t* st) { - while (count > 0) { - std::size_t result = std::mbrtowc(dst, src, count, st); - if (result == static_cast(-1) || result == static_cast(-2)) - return nullptr; - if (result == 0) - return dst; - src += result; - count -= result; - ++dst; - } - return dst; +int APS5_VABI _Mbtowcx_nid_postfix(std::uint16_t* dst, const char* src, std::size_t count, mbstate_t* st) { + if (dst == nullptr || src == nullptr || st == nullptr || count == 0) throw std::invalid_argument("_Mbtowcx: invalid conversion arguments"); + wchar_t converted{}; + const auto result = std::mbrtowc(&converted, src, count, st); + if (result == static_cast(-1)) throw std::runtime_error("_Mbtowcx: invalid multibyte character"); + if (result == static_cast(-2)) throw std::runtime_error("_Mbtowcx: incomplete multibyte character"); + if (result > static_cast(std::numeric_limits::max()) || static_cast(converted) > 0xffff) throw std::runtime_error("_Mbtowcx: conversion exceeds guest character limits"); + *dst = static_cast(converted); + return static_cast(result); } -char* APS5_VABI _Wctombx_nid_postfix(char* dst, wchar_t src, mbstate_t* st) { - std::size_t result = std::wcrtomb(dst, src, st); - if (result == static_cast(-1)) - return nullptr; - return dst + result; +int APS5_VABI _Wctombx_nid_postfix(char* dst, std::uint16_t src, mbstate_t* st) { + if (dst == nullptr || st == nullptr) throw std::invalid_argument("_Wctombx: invalid conversion arguments"); + const auto result = std::wcrtomb(dst, static_cast(src), st); + if (result == static_cast(-1)) throw std::runtime_error("_Wctombx: invalid wide character"); + if (result > static_cast(std::numeric_limits::max())) throw std::runtime_error("_Wctombx: conversion size exceeds guest limits"); + return static_cast(result); } const short* APS5_VABI _Getpctype_nid_postfix() { diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 6091e0e98..87253bcf0 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -4,47 +4,47 @@ extern "C" { -double cbrt_nid_postfix(double x) { return std::cbrt(x); } -double asin_nid_postfix(double x) { return std::asin(x); } -double acos_nid_postfix(double x) { return std::acos(x); } -double exp_nid_postfix(double x) { return std::exp(x); } -double atan_nid_postfix(double x) { return std::atan(x); } -double tan_nid_postfix(double x) { return std::tan(x); } -double log2_nid_postfix(double x) { return std::log2(x); } -double log_nid_postfix(double x) { return std::log(x); } +double APS5_VABI cbrt_nid_postfix(double x) { return std::cbrt(x); } +double APS5_VABI asin_nid_postfix(double x) { return std::asin(x); } +double APS5_VABI acos_nid_postfix(double x) { return std::acos(x); } +double APS5_VABI exp_nid_postfix(double x) { return std::exp(x); } +double APS5_VABI atan_nid_postfix(double x) { return std::atan(x); } +double APS5_VABI tan_nid_postfix(double x) { return std::tan(x); } +double APS5_VABI log2_nid_postfix(double x) { return std::log2(x); } +double APS5_VABI log_nid_postfix(double x) { return std::log(x); } -float sinf_nid_postfix(float x) { return std::sin(x); } -float cosf_nid_postfix(float x) { return std::cos(x); } +float APS5_VABI sinf_nid_postfix(float x) { return std::sin(x); } +float APS5_VABI cosf_nid_postfix(float x) { return std::cos(x); } void APS5_VABI sincosf_nid_postfix(float x, float* sinp, float* cosp) { *sinp = std::sin(x); *cosp = std::cos(x); } -double sin_nid_postfix(double x) { return std::sin(x); } -double cos_nid_postfix(double x) { return std::cos(x); } +double APS5_VABI sin_nid_postfix(double x) { return std::sin(x); } +double APS5_VABI cos_nid_postfix(double x) { return std::cos(x); } void APS5_VABI sincos_nid_postfix(double x, double* sinp, double* cosp) { *sinp = std::sin(x); *cosp = std::cos(x); } -float atanf_nid_postfix(float x) { return std::atan(x); } -double atan2_nid_postfix(double y, double x) { return std::atan2(y, x); } -float powf_nid_postfix(float base, float exp) { return std::pow(base, exp); } -double pow_nid_postfix(double base, double exp) { return std::pow(base, exp); } -float expf_nid_postfix(float x) { return std::exp(x); } -float exp2f_nid_postfix(float x) { return std::exp2(x); } -float logf_nid_postfix(float x) { return std::log(x); } -float log2f_nid_postfix(float x) { return std::log2(x); } -double log10_nid_postfix(double x) { return std::log10(x); } -float ldexpf_nid_postfix(float x, int exp) { return std::ldexp(x, exp); } -double fmod_nid_postfix(double x, double y) { return std::fmod(x, y); } -float roundf_nid_postfix(float x) { return std::round(x); } -double round_nid_postfix(double x) { return std::round(x); } +float APS5_VABI atanf_nid_postfix(float x) { return std::atan(x); } +double APS5_VABI atan2_nid_postfix(double y, double x) { return std::atan2(y, x); } +float APS5_VABI powf_nid_postfix(float base, float exp) { return std::pow(base, exp); } +double APS5_VABI pow_nid_postfix(double base, double exp) { return std::pow(base, exp); } +float APS5_VABI expf_nid_postfix(float x) { return std::exp(x); } +float APS5_VABI exp2f_nid_postfix(float x) { return std::exp2(x); } +float APS5_VABI logf_nid_postfix(float x) { return std::log(x); } +float APS5_VABI log2f_nid_postfix(float x) { return std::log2(x); } +double APS5_VABI log10_nid_postfix(double x) { return std::log10(x); } +float APS5_VABI ldexpf_nid_postfix(float x, int exp) { return std::ldexp(x, exp); } +double APS5_VABI fmod_nid_postfix(double x, double y) { return std::fmod(x, y); } +float APS5_VABI roundf_nid_postfix(float x) { return std::round(x); } +double APS5_VABI round_nid_postfix(double x) { return std::round(x); } -int __isfinite_nid_postfix(double x) { return std::isfinite(x) ? 1 : 0; } -int __isnan_nid_postfix(double x) { return std::isnan(x) ? 1 : 0; } -int __signbit_nid_postfix(double x) { return std::signbit(x) ? 1 : 0; } +int APS5_VABI __isfinite_nid_postfix(double x) { return std::isfinite(x) ? 1 : 0; } +int APS5_VABI __isnan_nid_postfix(double x) { return std::isnan(x) ? 1 : 0; } +int APS5_VABI __signbit_nid_postfix(double x) { return std::signbit(x) ? 1 : 0; } } diff --git a/core/libs/tests/GuestLocale.cpp b/core/libs/tests/GuestLocale.cpp index 4651478de..dc1635dd9 100644 --- a/core/libs/tests/GuestLocale.cpp +++ b/core/libs/tests/GuestLocale.cpp @@ -1,6 +1,8 @@ #include "prx/libc/include/GuestLocale.hpp" #include #include +#include +#include #include #include #include @@ -9,7 +11,7 @@ extern "C" { extern GuestLocale::Implementation* _ZSt21_sceLibcClassicLocale_nid_postfix; GuestLocale::Implementation* APS5_VABI _ZNSt6locale16_GetgloballocaleEv_nid_postfix(); -void APS5_VABI _ZNSt6locale5_InitEv_nid_postfix(); +GuestLocale::Implementation* APS5_VABI _ZNSt6locale5_InitEv_nid_postfix(); void APS5_VABI _ZNSt6locale5facet9_RegisterEv_nid_postfix(GuestLocale::Facet* self); void APS5_VABI _ZNSt8_LocinfoC1EPKc_nid_postfix(GuestLocale::LocinfoStorage* self, const char* localeName); void APS5_VABI _ZNSt8_LocinfoD1Ev_nid_postfix(GuestLocale::LocinfoStorage* self); @@ -17,6 +19,8 @@ void APS5_VABI _ZNSt8ios_baseD2Ev_nid_postfix(GuestLocale::IosBase* self); const short* APS5_VABI _Getpctype_nid_postfix(); const short* APS5_VABI _Getptolower_nid_postfix(); const short* APS5_VABI _Getptoupper_nid_postfix(); +int APS5_VABI _Mbtowcx_nid_postfix(std::uint16_t* dst, const char* src, std::size_t count, std::mbstate_t* st); +int APS5_VABI _Wctombx_nid_postfix(char* dst, std::uint16_t src, std::mbstate_t* st); void APS5_VABI _Locksyslock_nid_postfix(); void APS5_VABI _Unlocksyslock_nid_postfix(); @@ -40,8 +44,8 @@ static void RequireException(TAction action) { static void CheckGuestCalls() { auto* locale = _ZSt21_sceLibcClassicLocale_nid_postfix; Require(locale != nullptr && locale == _ZNSt6locale16_GetgloballocaleEv_nid_postfix()); - _ZNSt6locale5_InitEv_nid_postfix(); - _ZNSt6locale5_InitEv_nid_postfix(); + Require(_ZNSt6locale5_InitEv_nid_postfix() == locale); + Require(_ZNSt6locale5_InitEv_nid_postfix() == locale); const std::byte* table = nullptr; std::memcpy(&table, locale, sizeof(table)); void (APS5_VABI *retain)(GuestLocale::Facet*) = nullptr; @@ -88,10 +92,10 @@ static void CheckCharacterTables() { const auto* lower = _Getptolower_nid_postfix(); const auto* upper = _Getptoupper_nid_postfix(); Require(classification[-1] == 0 && lower[-1] == -1 && upper[-1] == -1); - Require(classification['A'] == 0x181 && classification['a'] == 0x182); - Require(classification['G'] == 0x101 && classification['0'] == 0x84); - Require(classification[' '] == 0x48 && classification['\t'] == 0x68); - Require(classification['\n'] == 0x28 && classification['!'] == 0x10); + Require(classification['A'] == 0x03 && classification['a'] == 0x11); + Require(classification['G'] == 0x02 && classification['0'] == 0x21); + Require(classification[' '] == 0x04 && classification['\t'] == 0xc0); + Require(classification['\n'] == 0xc0 && classification['!'] == 0x08); for (int value = 0; value < 256; ++value) { Require(lower[value] == (value >= 'A' && value <= 'Z' ? value + 32 : value)); Require(upper[value] == (value >= 'a' && value <= 'z' ? value - 32 : value)); @@ -99,6 +103,32 @@ static void CheckCharacterTables() { } } +static void CheckCharacterConversions() { + const auto* classification = _Getpctype_nid_postfix(); + for (std::uint16_t value = 0; value < 128; ++value) { + std::array bytes{}; + bytes.fill('!'); + std::mbstate_t encodeState{}; + Require(_Wctombx_nid_postfix(bytes.data(), value, &encodeState) == 1); + Require(bytes[0] == static_cast(value) && bytes[1] == '!'); + std::array wide{0xffff, 0x1234}; + std::mbstate_t decodeState{}; + Require(_Mbtowcx_nid_postfix(wide.data(), bytes.data(), 2, &decodeState) == (value == 0 ? 0 : 1)); + Require(wide[0] == value && wide[1] == 0x1234); + const bool alnum = (value >= '0' && value <= '9') || (value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z'); + Require(((classification[static_cast(bytes[0])] & 0x232) != 0) == alnum); + } + std::mbstate_t state{}; + char byte{}; + std::uint16_t wide{}; + RequireException([&] { _Mbtowcx_nid_postfix(&wide, "]", 0, &state); }); + RequireException([&] { _Mbtowcx_nid_postfix(nullptr, "]", 1, &state); }); + RequireException([&] { _Mbtowcx_nid_postfix(&wide, nullptr, 1, &state); }); + RequireException([&] { _Mbtowcx_nid_postfix(&wide, "]", 1, nullptr); }); + RequireException([&] { _Wctombx_nid_postfix(nullptr, ']', &state); }); + RequireException([&] { _Wctombx_nid_postfix(&byte, ']', nullptr); }); +} + static void CheckStreamDestruction() { struct GuardedStream { std::uint64_t before = 0x12345678; @@ -118,6 +148,7 @@ int main() { CheckGuestCalls(); CheckLocinfoAlignment(); CheckCharacterTables(); + CheckCharacterConversions(); CheckStreamDestruction(); _Locksyslock_nid_postfix(); _Locksyslock_nid_postfix();