Merge pull request #4104 from hugodearaujo/fix/username-console

fix(libSceUserService): answer sceUserServiceGetUserName as the console does
This commit is contained in:
Dean 2026-10-12 21:40:46 +13:00 committed by GitHub
commit 7ea2022227
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 67 additions and 1 deletions

View File

@ -12,3 +12,14 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceUserService>")
endif()
endif()
if(BUILD_TESTING)
add_test_executable(guest_user_service_user_name_tests ${LIBS_INCLUDE_DIR}/tests/GuestUserServiceUserName.cpp)
target_include_directories(guest_user_service_user_name_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_user_service_user_name_tests PRIVATE libSceUserService)
add_test(NAME guest_user_service_user_name COMMAND guest_user_service_user_name_tests)
if(WIN32)
set_tests_properties(guest_user_service_user_name PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceUserService>")
endif()
endif()

View File

@ -121,10 +121,14 @@ int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* user_
}
int APS5_VABI sceUserServiceGetUserName(int user_id, char* name, size_t size) {
if (name == nullptr || user_id != USER_SERVICE_INITIAL_USER_ID) {
if (name == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != USER_SERVICE_INITIAL_USER_ID) {
return USER_SERVICE_ERROR_UNKNOWN_USER;
}
if (size < USER_SERVICE_MAX_USER_NAME_LENGTH + 1) {
std::memset(name, 0, size);
return USER_SERVICE_ERROR_BUFFER_TOO_SHORT;
}
std::strncpy(name, USER_SERVICE_INITIAL_USER_NAME, size);

View File

@ -11,6 +11,7 @@ constexpr int USER_SERVICE_ERROR_NOT_LOGGED_IN = -2137653239;
constexpr int USER_SERVICE_USER_ID_INVALID = -1;
constexpr int USER_SERVICE_INITIAL_USER_ID = 0x10000000;
constexpr int USER_SERVICE_ERROR_BUFFER_TOO_SHORT = -2137653238;
constexpr int USER_SERVICE_ERROR_UNKNOWN_USER = static_cast<int>(0x80960105u);
constexpr std::size_t USER_SERVICE_MAX_USER_NAME_LENGTH = 16;
constexpr const char* USER_SERVICE_INITIAL_USER_NAME = "Player";

View File

@ -0,0 +1,49 @@
#include "SceTypes.hpp"
#include "prx/libSceUserService/UserService.hpp"
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
extern "C" {
int APS5_VABI sceUserServiceGetUserName(int user_id, char* name, std::size_t size);
}
namespace {
void Require(bool value) { if (!value) std::abort(); }
bool Filled(const char* buffer, std::size_t from, std::size_t to, char value) {
for (std::size_t i = from; i < to; ++i) {
if (buffer[i] != value) return false;
}
return true;
}
}
int main() {
char name[32];
const std::size_t length = std::strlen(USER_SERVICE_INITIAL_USER_NAME);
for (std::size_t size = 0; size <= USER_SERVICE_MAX_USER_NAME_LENGTH; ++size) {
std::memset(name, 'A', sizeof(name));
Require(sceUserServiceGetUserName(USER_SERVICE_INITIAL_USER_ID, name, size) == USER_SERVICE_ERROR_BUFFER_TOO_SHORT);
Require(Filled(name, 0, size, 0) && Filled(name, size, sizeof(name), 'A'));
}
for (std::size_t size : {USER_SERVICE_MAX_USER_NAME_LENGTH + 1, sizeof(name)}) {
std::memset(name, 'A', sizeof(name));
Require(sceUserServiceGetUserName(USER_SERVICE_INITIAL_USER_ID, name, size) == USER_SERVICE_OK);
Require(std::strcmp(name, USER_SERVICE_INITIAL_USER_NAME) == 0);
Require(Filled(name, length, size, 0) && Filled(name, size, sizeof(name), 'A'));
}
for (int user : {0, USER_SERVICE_USER_ID_INVALID, 1, 0xff, 0x12345678}) {
std::memset(name, 'A', sizeof(name));
Require(sceUserServiceGetUserName(user, name, sizeof(name)) == USER_SERVICE_ERROR_UNKNOWN_USER);
Require(sceUserServiceGetUserName(user, name, 1) == USER_SERVICE_ERROR_UNKNOWN_USER);
Require(Filled(name, 0, sizeof(name), 'A'));
Require(sceUserServiceGetUserName(user, nullptr, sizeof(name)) == USER_SERVICE_ERROR_INVALID_ARGUMENT);
}
Require(sceUserServiceGetUserName(USER_SERVICE_INITIAL_USER_ID, nullptr, sizeof(name)) == USER_SERVICE_ERROR_INVALID_ARGUMENT);
}

View File

@ -303,6 +303,7 @@ Throughout the project, every function at every stage either **does exactly what
- [sceHttpSetRedirectCallback, sceHttpSetCookieRecvCallback, sceHttpsSetSslVersion, sceHttpsGetSslError](../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp) (PS4), which only logs and returns 0; a null `errNum` or `detail` in `sceHttpsGetSslError` is assumed to return `SCE_HTTP_ERROR_INVALID_VALUE` like `sceHttpGetLastErrno`
- [sceHttpSetAuthInfoCallback](../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature and callback type follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.h) (PS4); no PS5 reference has them. The `id` is not checked, as in the other libSceHttp setters
- [libSceHttp2 cookie boxes](../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - `sceHttp2CreateCookieBox` is taken to take the library context id first, as in the [prosper](https://github.com/mattias800/prosper) reimplementation; further arguments are unknown and ignored. `sceHttp2SetRequestNoContentLength` is assumed to take only the request id. Cookies are never stored, so `sceHttp2CookieFlush` has nothing to discard
- [sceUserServiceGetUserName](../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - `0x80960105`, which a PS5 (firmware 13.60) returns for a user id it does not know, has no known name; it is `USER_SERVICE_ERROR_UNKNOWN_USER` here. On the console a registered user who is not logged in still gets their name; here only the initial user exists
- [sceNpSessionSignalingGetMemoryInfo](../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - unknown signature
- [sceNpSessionSignalingGetConnectionStatistics](../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - unknown signature
- [sceUserServiceGetAgeLevel](../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - on a PS5 (firmware 13.60) the value is the age set in the user's parental restriction (0, 7, 12 and 15 were measured) and `0xFFFFFFFF` without one; a user who is not logged in returns `0x80960009` with or without an output. Parental restrictions are not modelled, so the initial user is always reported as unrestricted. PPSA12544 (Unity) only stores the value for its user profile