feat(libScePlayerSelectionDialog): run common-dialog state machine without UI

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
notaxeltvandCursor committed 2026-10-14 23:00:30 +02:00
1 parent fa539dd9ba
commit 784cb4d3db
4 files changed
+118

No files matched your search

@@ -0,0 +1,14 @@
add_library(libScePlayerSelectionDialog SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libScePlayerSelectionDialog PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libScePlayerSelectionDialog)
if(BUILD_TESTING)
add_test_executable(guest_player_selection_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestPlayerSelectionDialog.cpp)
target_include_directories(guest_player_selection_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_player_selection_dialog_tests PRIVATE libScePlayerSelectionDialog)
add_test(NAME guest_player_selection_dialog COMMAND guest_player_selection_dialog_tests)
if(WIN32)
set_tests_properties(guest_player_selection_dialog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libScePlayerSelectionDialog>")
endif()
endif()
@@ -1,11 +1,60 @@
#include <atomic>
#include <cstdint>
#include <cstddef>
#include <cstring>
#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_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);
constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1;
std::atomic<int> g_status{COMMON_DIALOG_STATUS_NONE};
}
extern "C" {
int APS5_VABI scePlayerSelectionDialogInitialize(void) {
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 scePlayerSelectionDialogOpen(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;
}
int APS5_VABI scePlayerSelectionDialogUpdateStatus(void) {
return g_status.load();
}
int APS5_VABI scePlayerSelectionDialogGetResult(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;
const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED;
std::memcpy(result, &canceled, sizeof(canceled));
return 0;
}
int APS5_VABI scePlayerSelectionDialogTerminate(void) {
if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
return 0;
}
@@ -0,0 +1,50 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
extern "C" {
int APS5_VABI scePlayerSelectionDialogInitialize(void);
int APS5_VABI scePlayerSelectionDialogOpen(const void* param);
int APS5_VABI scePlayerSelectionDialogUpdateStatus(void);
int APS5_VABI scePlayerSelectionDialogGetResult(void* result);
int APS5_VABI scePlayerSelectionDialogTerminate(void);
}
namespace {
void Require(bool value) { if (!value) std::abort(); }
constexpr int kStatusFinished = 3;
constexpr int kErrNotInitialized = static_cast<int>(0x80B80003);
constexpr int kErrAlreadyInitialized = static_cast<int>(0x80B80004);
constexpr int kErrNotFinished = static_cast<int>(0x80B80005);
constexpr int kErrArgNull = static_cast<int>(0x80B8000D);
constexpr int kResultUserCanceled = 1;
}
int main() {
std::int64_t param = 0;
std::int32_t result[2] = {-1, -1};
Require(scePlayerSelectionDialogTerminate() == kErrNotInitialized);
Require(scePlayerSelectionDialogOpen(&param) == kErrNotInitialized);
Require(scePlayerSelectionDialogGetResult(result) == kErrNotInitialized);
Require(scePlayerSelectionDialogInitialize() == 0);
Require(scePlayerSelectionDialogInitialize() == kErrAlreadyInitialized);
Require(scePlayerSelectionDialogOpen(nullptr) == kErrArgNull);
Require(scePlayerSelectionDialogGetResult(result) == kErrNotFinished);
Require(scePlayerSelectionDialogOpen(&param) == 0);
Require(scePlayerSelectionDialogUpdateStatus() == kStatusFinished);
Require(scePlayerSelectionDialogGetResult(nullptr) == kErrArgNull);
Require(scePlayerSelectionDialogGetResult(result) == 0);
Require(result[0] == kResultUserCanceled);
Require(result[1] == -1);
Require(scePlayerSelectionDialogTerminate() == 0);
Require(scePlayerSelectionDialogTerminate() == kErrNotInitialized);
}
@@ -0,0 +1,5 @@
# libScePlayerSelectionDialog technical debt
### Silent stubs
- [libScePlayerSelectionDialog](../../../core/libs/prx/libScePlayerSelectionDialog/Export.cpp) - the player-selection dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. `GetResult` writes only the first `int32` (`USER_CANCELED`); the rest of `ScePlayerSelectionDialogResult` (player list pointer and length) is not filled. EA SPORTS FC 27 (PPSA34015) fails to load `libScePlayerSelectionDialog.prx` without `Initialize`, `Open`, `UpdateStatus` and `GetResult` (`unresolved ELF import` / `GetLastError: 127`)