Implement player invitation dialog HLE stub
This commit is contained in:
parent
83358bec94
commit
bd3e41b28f
|
|
@ -0,0 +1,27 @@
|
|||
# Implement an HLE stub for libScePlayerInvitationDialog
|
||||
|
||||
## Why
|
||||
|
||||
Games that load `libScePlayerInvitationDialog` currently have no HLE implementation for the invitation dialog. This can prevent startup or leave games waiting indefinitely for the dialog to finish.
|
||||
|
||||
## Changes
|
||||
|
||||
- Add the PRX module and its `Initialize`, `Open`, `UpdateStatus`, `GetStatus`, `Close`, and `Terminate` exports.
|
||||
- Simulate the `None -> Initialized -> Running -> Finished` lifecycle: the first `UpdateStatus` after `Open` finishes the dialog without displaying UI or starting a network operation.
|
||||
- Handle repeated or out-of-order calls and protect shared state with a mutex.
|
||||
- Log calls and available arguments to the console without dereferencing the opening parameters, whose ABI layout has not been confirmed.
|
||||
- Add a focused test covering opening, polling, closing, reopening, and out-of-order calls.
|
||||
|
||||
## Verification
|
||||
|
||||
```text
|
||||
cmake --build build_ninja --target player_invitation_dialog_tests
|
||||
ctest --test-dir build_ninja -R "^player_invitation_dialog$" --output-on-failure
|
||||
ninja -C build_ninja core/libs/libs/libScePlayerInvitationDialog.prx
|
||||
```
|
||||
|
||||
The focused test passes, and the NID-patched PRX builds successfully.
|
||||
|
||||
## Limitations
|
||||
|
||||
This stub does not display system UI or send invitations. `ScePlayerInvitationDialogParam` is opaque; its exact layout and any additional exports still need to be confirmed against traces or a compatible SDK.
|
||||
|
|
@ -122,6 +122,15 @@ list(APPEND PATCHED_OUTPUTS ${LIBCOHTML_PROSPERO_PATCHED})
|
|||
add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(player_invitation_dialog_tests tests/PlayerInvitationDialog.cpp)
|
||||
target_include_directories(player_invitation_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(player_invitation_dialog_tests PRIVATE libScePlayerInvitationDialog libc)
|
||||
configure_windows_unwind(player_invitation_dialog_tests)
|
||||
add_test(NAME player_invitation_dialog COMMAND player_invitation_dialog_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(player_invitation_dialog PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_library(host_thread_local_test_library SHARED tests/HostThreadLocalLibrary.cpp)
|
||||
target_include_directories(host_thread_local_test_library PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_executable(host_thread_local_tests tests/HostThreadLocal.cpp)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
add_library(libScePlayerInvitationDialog SHARED EXCLUDE_FROM_ALL libScePlayerInvitationDialog.cpp)
|
||||
target_include_directories(libScePlayerInvitationDialog PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(libScePlayerInvitationDialog)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#include "prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h"
|
||||
|
||||
#include <mutex>
|
||||
#include "prx/libc/include/general/LogMacros.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex g_dialog_mutex;
|
||||
PlayerInvitationDialogStatus g_dialog_status = PlayerInvitationDialogStatus::None;
|
||||
|
||||
std::int32_t status() {
|
||||
return static_cast<std::int32_t>(g_dialog_status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogInitialize(void) {
|
||||
std::lock_guard lock(g_dialog_mutex);
|
||||
APS5_LOG_OUT("status=%d", status());
|
||||
if (g_dialog_status == PlayerInvitationDialogStatus::None) {
|
||||
g_dialog_status = PlayerInvitationDialogStatus::Initialized;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogOpen(const ScePlayerInvitationDialogParam* param) {
|
||||
std::lock_guard lock(g_dialog_mutex);
|
||||
APS5_LOG_OUT("param=%p status=%d", static_cast<const void*>(param), status());
|
||||
if (g_dialog_status == PlayerInvitationDialogStatus::None) {
|
||||
g_dialog_status = PlayerInvitationDialogStatus::Initialized;
|
||||
}
|
||||
g_dialog_status = PlayerInvitationDialogStatus::Running;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogUpdateStatus(void) {
|
||||
std::lock_guard lock(g_dialog_mutex);
|
||||
APS5_LOG_OUT("status=%d", status());
|
||||
if (g_dialog_status == PlayerInvitationDialogStatus::Running) {
|
||||
g_dialog_status = PlayerInvitationDialogStatus::Finished;
|
||||
}
|
||||
return status();
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogGetStatus(void) {
|
||||
std::lock_guard lock(g_dialog_mutex);
|
||||
APS5_LOG_OUT("status=%d", status());
|
||||
return status();
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogClose(void) {
|
||||
std::lock_guard lock(g_dialog_mutex);
|
||||
APS5_LOG_OUT("status=%d", status());
|
||||
if (g_dialog_status == PlayerInvitationDialogStatus::Running) {
|
||||
g_dialog_status = PlayerInvitationDialogStatus::Finished;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogTerminate(void) {
|
||||
std::lock_guard lock(g_dialog_mutex);
|
||||
APS5_LOG_OUT("status=%d", status());
|
||||
g_dialog_status = PlayerInvitationDialogStatus::None;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEPLAYERINVITATIONDIALOG_LIBSCEPLAYERINVITATIONDIALOG_H
|
||||
#define CORE_LIBS_PRX_LIBSCEPLAYERINVITATIONDIALOG_LIBSCEPLAYERINVITATIONDIALOG_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
enum class PlayerInvitationDialogStatus : std::int32_t {
|
||||
None = 0,
|
||||
Initialized = 1,
|
||||
Running = 2,
|
||||
Finished = 3,
|
||||
};
|
||||
|
||||
// Placeholder only: the guest ABI layout is not known and is never read by this HLE stub.
|
||||
struct ScePlayerInvitationDialogParam {
|
||||
std::uint8_t reserved[1];
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogInitialize(void);
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogOpen(const ScePlayerInvitationDialogParam* param);
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogUpdateStatus(void);
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogGetStatus(void);
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogClose(void);
|
||||
std::int32_t APS5_VABI scePlayerInvitationDialogTerminate(void);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace {
|
||||
|
||||
void expect(std::int32_t actual, std::int32_t expected) {
|
||||
if (actual != expected) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
using enum PlayerInvitationDialogStatus;
|
||||
expect(scePlayerInvitationDialogUpdateStatus(), static_cast<std::int32_t>(None));
|
||||
expect(scePlayerInvitationDialogClose(), 0);
|
||||
expect(scePlayerInvitationDialogTerminate(), 0);
|
||||
|
||||
expect(scePlayerInvitationDialogInitialize(), 0);
|
||||
expect(scePlayerInvitationDialogInitialize(), 0);
|
||||
expect(scePlayerInvitationDialogGetStatus(), static_cast<std::int32_t>(Initialized));
|
||||
expect(scePlayerInvitationDialogUpdateStatus(), static_cast<std::int32_t>(Initialized));
|
||||
|
||||
ScePlayerInvitationDialogParam param{};
|
||||
expect(scePlayerInvitationDialogOpen(¶m), 0);
|
||||
expect(scePlayerInvitationDialogGetStatus(), static_cast<std::int32_t>(Running));
|
||||
expect(scePlayerInvitationDialogUpdateStatus(), static_cast<std::int32_t>(Finished));
|
||||
expect(scePlayerInvitationDialogUpdateStatus(), static_cast<std::int32_t>(Finished));
|
||||
expect(scePlayerInvitationDialogOpen(nullptr), 0);
|
||||
expect(scePlayerInvitationDialogClose(), 0);
|
||||
expect(scePlayerInvitationDialogGetStatus(), static_cast<std::int32_t>(Finished));
|
||||
expect(scePlayerInvitationDialogTerminate(), 0);
|
||||
expect(scePlayerInvitationDialogGetStatus(), static_cast<std::int32_t>(None));
|
||||
|
||||
expect(scePlayerInvitationDialogOpen(nullptr), 0);
|
||||
expect(scePlayerInvitationDialogUpdateStatus(), static_cast<std::int32_t>(Finished));
|
||||
expect(scePlayerInvitationDialogTerminate(), 0);
|
||||
}
|
||||
Loading…
Reference in New Issue