feat(libs): offline services GTA V reaches in Story Mode

Sign-in, web browser and player dialogs finish cancelled; the message dialog finishes at once;
NpAuth, entitlement consumption, reachability, account age and the NpUtility bandwidth test
report signed out; sceKernelMtypeprotect changes the protection (the memory type has no host
equivalent); the APR ForEach resolvers resolve every path and report each failure in its own
result entry. vswprintf's stub is dropped in favor of the existing wide-formatter
implementation, which already covers it on every platform.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Michon 2026-10-01 23:21:53 +02:00
parent 709d7fe0fd
commit 04691c0797
16 changed files with 162 additions and 93 deletions

View File

@ -1142,6 +1142,13 @@ struct ImeDialogResult {
using Result = ImeDialogResult;
struct MsgDialogResult {
std::int32_t mode;
std::int32_t result;
std::int32_t button_id;
char reserved[32];
};
struct PositionAndForm {
std::uint32_t type;
float posx;

View File

@ -7,15 +7,27 @@
namespace {
std::atomic<int> g_status{0};
constexpr int COMMON_DIALOG_STATUS_FINISHED = 3;
constexpr int COMMON_DIALOG_RESULT_OK = 0;
constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast<int>(0x80B80005u);
constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
constexpr int BUTTON_ID_OK = 1;
}
extern "C" {
int SceMsgDialogNativeModuleLoaded_nid_no_patch = 1;
int APS5_VABI sceMsgDialogGetResult(void* result) {
(void)result;
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceMsgDialogGetResult(MsgDialogResult* result) {
const int status = g_status.load();
if (status == 0) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED;
*result = MsgDialogResult{};
result->result = COMMON_DIALOG_RESULT_OK;
result->button_id = BUTTON_ID_OK;
return 0;
}
@ -26,13 +38,17 @@ int APS5_VABI sceMsgDialogInitialize(void) {
}
int APS5_VABI sceMsgDialogOpen(const void* param) {
(void)param;
NotImplemented_nid_no_patch(__func__);
const int status = g_status.load();
if (status == 0) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
g_status = COMMON_DIALOG_STATUS_FINISHED;
return 0;
}
int APS5_VABI sceMsgDialogTerminate(void) {
int expected = 1;
if (g_status.compare_exchange_strong(expected, 0)) return 0;
expected = COMMON_DIALOG_STATUS_FINISHED;
if (!g_status.compare_exchange_strong(expected, 0)) throw std::logic_error("sceMsgDialogTerminate: not initialized or still running");
return 0;
}

View File

@ -1,20 +1,25 @@
#include <cstdint>
#include <cstddef>
#include <atomic>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
static constexpr int SCE_NP_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80550003);
static constexpr int SCE_NP_ERROR_SIGNED_OUT = static_cast<int>(0x80550006);
static constexpr int NP_POLL_ASYNC_FINISHED = 0;
static std::atomic<int> g_nextRequest{1};
extern "C" {
int APS5_VABI sceNpAuthAbortRequest(int req_id) {
(void)req_id;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpAuthCreateAsyncRequest(const void* param) {
(void)param;
NotImplemented_nid_no_patch(__func__);
return 0;
return g_nextRequest.fetch_add(1, std::memory_order_relaxed);
}
int APS5_VABI sceNpAuthCreateRequest(void) {
@ -24,17 +29,14 @@ int APS5_VABI sceNpAuthCreateRequest(void) {
int APS5_VABI sceNpAuthDeleteRequest(int req_id) {
(void)req_id;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpAuthGetAuthorizationCodeV3(int req_id, const void* param, void* auth_code, int* issuer_id) {
(void)req_id;
(void)param;
(void)auth_code;
(void)issuer_id;
NotImplemented_nid_no_patch(__func__);
return 0;
if (!param || !auth_code) return SCE_NP_ERROR_INVALID_ARGUMENT;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpAuthGetIdTokenV3(int req_id, const void* param, void* id_token) {
@ -47,9 +49,8 @@ int APS5_VABI sceNpAuthGetIdTokenV3(int req_id, const void* param, void* id_toke
int APS5_VABI sceNpAuthPollAsync(int req_id, int* result) {
(void)req_id;
(void)result;
NotImplemented_nid_no_patch(__func__);
return 0;
if (result) *result = SCE_NP_ERROR_SIGNED_OUT;
return NP_POLL_ASYNC_FINISHED;
}
int APS5_VABI sceNpAuthWaitAsync(int req_id, int* result) {

View File

@ -66,12 +66,11 @@ int APS5_VABI sceNpCommerceDialogTerminate() {
}
int APS5_VABI sceNpCommerceHidePsStoreIcon(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpCommerceShowPsStoreIcon(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceNpCommerceShowPsStoreIcon(int pos) {
(void)pos;
return 0;
}

View File

@ -16,6 +16,7 @@
static constexpr int SCE_NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER = static_cast<int>(0x80558003);
static constexpr int SCE_NP_ENTITLEMENT_ACCESS_ERROR_NOT_FOUND = static_cast<int>(0x80558007);
static constexpr int SCE_NP_ERROR_SIGNED_OUT = static_cast<int>(0x80550006);
static constexpr uint32_t SKU_FLAG_FULL = 3;
namespace {
@ -97,28 +98,24 @@ int APS5_VABI sceNpEntitlementAccessInitialize(const NpEntitlementAccessInitPara
}
int APS5_VABI sceNpEntitlementAccessAbortRequest(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpEntitlementAccessDeleteRequest(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpEntitlementAccessGenerateTransactionId(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpEntitlementAccessGenerateTransactionId(void* transaction_id) {
if (!transaction_id) return SCE_NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpEntitlementAccessPollConsumeEntitlement(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpEntitlementAccessRequestConsumeUnifiedEntitlement(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return SCE_NP_ERROR_SIGNED_OUT;
}
}

View File

@ -35,8 +35,7 @@ int APS5_VABI sceNpCheckNpAvailability(int req_id, const char* user, void* resul
int APS5_VABI sceNpCheckNpReachability(int req_id, int user_id) {
(void)req_id;
(void)user_id;
NotImplemented_nid_no_patch(__func__);
return 0;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpCheckPremium(int req_id, const NpCheckPremiumParameter* param, NpCheckPremiumResult* result) {
@ -63,9 +62,8 @@ int APS5_VABI sceNpDeleteRequest(int req_id) {
int APS5_VABI sceNpGetAccountAge(int req_id, int user_id, uint8_t* age) {
(void)req_id;
(void)user_id;
(void)age;
NotImplemented_nid_no_patch(__func__);
return 0;
if (!age) return SCE_NP_ERROR_INVALID_ARGUMENT;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpGetAccountCountryA(int user_id, void* country_code) {

View File

@ -3,33 +3,38 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
static constexpr int SCE_NP_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80550003);
static constexpr int SCE_NP_ERROR_SIGNED_OUT = static_cast<int>(0x80550006);
extern "C" {
int SceNpUtilityModuleLoaded_nid_no_patch = 1;
int APS5_VABI sceNpBandwidthTestAbort(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpBandwidthTestAbort(int context_id) {
(void)context_id;
return SCE_NP_ERROR_INVALID_ARGUMENT;
}
int APS5_VABI sceNpBandwidthTestGetStatus(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpBandwidthTestGetStatus(int context_id, int* status) {
(void)context_id;
if (status) *status = 0;
return SCE_NP_ERROR_INVALID_ARGUMENT;
}
int APS5_VABI sceNpBandwidthTestInitStartDownload(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpBandwidthTestInitStartDownload(const void* param) {
(void)param;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpBandwidthTestInitStartUpload(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpBandwidthTestInitStartUpload(const void* param) {
(void)param;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpBandwidthTestShutdown(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpBandwidthTestShutdown(int context_id, void* result) {
(void)context_id;
(void)result;
return SCE_NP_ERROR_INVALID_ARGUMENT;
}
}

View File

@ -6,12 +6,10 @@
extern "C" {
int APS5_VABI scePlayerInvitationDialogTerminate(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI scePlayerInvitationDialogUpdateStatus(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

View File

@ -6,7 +6,6 @@
extern "C" {
int APS5_VABI scePlayerSelectionDialogTerminate(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

View File

@ -1,8 +1,24 @@
#include <atomic>
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr int COMMON_DIALOG_STATUS_NONE = 0;
constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1;
constexpr int COMMON_DIALOG_STATUS_RUNNING = 2;
constexpr int COMMON_DIALOG_STATUS_FINISHED = 3;
constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80B80004u);
constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast<int>(0x80B80007u);
constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
std::atomic<int> g_status{COMMON_DIALOG_STATUS_NONE};
}
extern "C" {
int APS5_VABI sceSigninDialogClose(void) {
@ -22,24 +38,27 @@ int APS5_VABI sceSigninDialogGetStatus(void) {
}
int APS5_VABI sceSigninDialogInitialize(void) {
NotImplemented_nid_no_patch(__func__);
int expected = COMMON_DIALOG_STATUS_NONE;
if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED;
return 0;
}
int APS5_VABI sceSigninDialogOpen(const void* param) {
(void)param;
NotImplemented_nid_no_patch(__func__);
const int status = g_status.load();
if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY;
if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
g_status = COMMON_DIALOG_STATUS_FINISHED;
return 0;
}
int APS5_VABI sceSigninDialogTerminate(void) {
NotImplemented_nid_no_patch(__func__);
if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
return 0;
}
int APS5_VABI sceSigninDialogUpdateStatus(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return g_status.load();
}
}

View File

@ -110,14 +110,14 @@ int APS5_VABI sceSystemServiceSetNoticeScreenSkipFlag(void) {
return SYSTEM_SERVICE_OK;
}
int APS5_VABI sceSystemServiceInitializePlayerDialogParam(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceSystemServiceInitializePlayerDialogParam(void* param) {
if (param == nullptr) return SYSTEM_SERVICE_ERROR_PARAMETER;
return SYSTEM_SERVICE_OK;
}
int APS5_VABI sceSystemServiceLaunchPlayerDialog(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceSystemServiceLaunchPlayerDialog(const void* param) {
if (param == nullptr) return SYSTEM_SERVICE_ERROR_PARAMETER;
return SYSTEM_SERVICE_OK;
}
}

View File

@ -7,6 +7,15 @@
namespace {
std::atomic<int> g_status{0};
constexpr int COMMON_DIALOG_STATUS_NONE = 0;
constexpr int COMMON_DIALOG_STATUS_RUNNING = 2;
constexpr int COMMON_DIALOG_STATUS_FINISHED = 3;
constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1;
constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast<int>(0x80B80005u);
constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast<int>(0x80B80007u);
constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
}
extern "C" {
@ -24,17 +33,25 @@ int APS5_VABI sceWebBrowserDialogTerminate(void) {
}
int APS5_VABI sceWebBrowserDialogClose(void) {
NotImplemented_nid_no_patch(__func__);
if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
return 0;
}
int APS5_VABI sceWebBrowserDialogGetResult(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceWebBrowserDialogGetResult(void* result) {
const int status = g_status.load();
if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED;
*static_cast<std::int32_t*>(result) = COMMON_DIALOG_RESULT_USER_CANCELED;
return 0;
}
int APS5_VABI sceWebBrowserDialogOpen(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceWebBrowserDialogOpen(const void* param) {
const int status = g_status.load();
if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY;
if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
g_status = COMMON_DIALOG_STATUS_FINISHED;
return 0;
}

View File

@ -121,6 +121,23 @@ AprFile _file(std::uint32_t id) {
return g_files[id];
}
constexpr std::uint32_t INVALID_FILE_ID = 0xFFFFFFFFu;
constexpr int SCE_KERNEL_ERROR_ENOENT = static_cast<int>(0x80020002);
int _resolveForEach(const char* prefix, const char** paths, uint32_t count, uint32_t* ids, uint64_t* sizes, int* results) {
if (!paths || !ids) return _fail(GUEST_EINVAL);
for (uint32_t index = 0; index < count; ++index) {
const std::string path = prefix ? (paths[index] ? std::string(prefix) + paths[index] : std::string()) : (paths[index] ? std::string(paths[index]) : std::string());
const bool resolved = !path.empty() && _resolve(path.c_str(), &ids[index], sizes ? &sizes[index] : nullptr);
if (!resolved) {
ids[index] = INVALID_FILE_ID;
if (sizes) sizes[index] = 0;
}
if (results) results[index] = resolved ? 0 : SCE_KERNEL_ERROR_ENOENT;
}
return 0;
}
void _readFile(const Apr::ReadFileCommand& command) {
const auto file = _file(command.fileId);
static const bool trace = std::getenv("APS5_TRACE_APR") != nullptr;
@ -308,4 +325,23 @@ int APS5_VABI sceKernelAprWaitCommandBuffer(uint32_t id) {
return 0;
}
int APS5_VABI sceKernelAprResolveFilepathsToIdsForEach(const char** paths, uint32_t count, uint32_t* ids, int* results) {
return _resolveForEach(nullptr, paths, count, ids, nullptr, results);
}
int APS5_VABI sceKernelAprResolveFilepathsToIdsAndFileSizesForEach(const char** paths, uint32_t count, uint32_t* ids, uint64_t* sizes, int* results) {
if (!sizes) return _fail(GUEST_EINVAL);
return _resolveForEach(nullptr, paths, count, ids, sizes, results);
}
int APS5_VABI sceKernelAprResolveFilepathsWithPrefixToIdsForEach(const char* prefix, const char** paths, uint32_t count, uint32_t* ids, int* results) {
if (!prefix) return _fail(GUEST_EINVAL);
return _resolveForEach(prefix, paths, count, ids, nullptr, results);
}
int APS5_VABI sceKernelAprResolveFilepathsWithPrefixToIdsAndFileSizesForEach(const char* prefix, const char** paths, uint32_t count, uint32_t* ids, uint64_t* sizes, int* results) {
if (!prefix || !sizes) return _fail(GUEST_EINVAL);
return _resolveForEach(prefix, paths, count, ids, sizes, results);
}
}

View File

@ -282,12 +282,8 @@ int APS5_VABI sceKernelCheckedReleaseDirectMemory(int64_t start, size_t len) {
}
int APS5_VABI sceKernelMtypeprotect(const void* addr, size_t len, int type, int prot) {
(void)addr;
(void)len;
(void)type;
(void)prot;
NotImplemented_nid_no_patch(__func__);
return 0;
return DoMprotect(addr, len, prot);
}
int APS5_VABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, int* prot) {

View File

@ -10,24 +10,4 @@ int APS5_VABI sceCoredumpWriteUserData() {
return 0;
}
int APS5_VABI sceKernelAprResolveFilepathsToIdsAndFileSizesForEach() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelAprResolveFilepathsToIdsForEach() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelAprResolveFilepathsWithPrefixToIdsAndFileSizesForEach() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelAprResolveFilepathsWithPrefixToIdsForEach() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -14,6 +14,7 @@ Throughout the project, every function at every stage either **does exactly what
- [libSceSaveDataDialog.native](../../core/libs/prx/libSceSaveDataDialog.native/Export.cpp)
- [libSceCommonDialog](../../core/libs/prx/libSceCommonDialog/Export.cpp)
- [libSceHmd](../../core/libs/prx/libSceHmd/Export.cpp) implements only the disconnected-headset path: initialization succeeds, device queries report `NotDetected`, and opening a device returns `DeviceDisconnected`. Headset support, tracking, rendering and additional HMD exports are not implemented; SDK-level ABI compatibility and in-game behaviour remain unverified.
- [libSceNpCommerce](../../core/libs/prx/libSceNpCommerce/Export.cpp) - the PS Store icon show/hide calls do nothing
- The shader recompiler [skips baryctric coordinates](../../core/shader/recompiler/Recompiler.cpp) (is not even passed to SpirvTargetOptions at row 212).
### Unknown function info