Merge pull request #558 from Phi1ow/implement-error-dialog
feat(errordialog): implement the libSceErrorDialog state machine
This commit is contained in:
commit
d5cf8a0add
|
|
@ -0,0 +1,20 @@
|
|||
add_library(libSceErrorDialog SHARED EXCLUDE_FROM_ALL Export.cpp)
|
||||
target_include_directories(libSceErrorDialog PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(libSceErrorDialog)
|
||||
|
||||
if(MINGW)
|
||||
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
|
||||
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_error_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestErrorDialog.cpp)
|
||||
target_include_directories(guest_error_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_error_dialog_tests PRIVATE libSceErrorDialog)
|
||||
add_test(NAME guest_error_dialog COMMAND guest_error_dialog_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_error_dialog PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceErrorDialog>")
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1,39 +1,95 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
#include "HitLog.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
constexpr int kStatusNone = 0;
|
||||
constexpr int kStatusInitialized = 1;
|
||||
constexpr int kStatusRunning = 2;
|
||||
constexpr int kStatusFinished = 3;
|
||||
|
||||
constexpr int kErrOk = 0;
|
||||
constexpr int kErrNotInitialized = static_cast<int>(0x80ED0001u);
|
||||
constexpr int kErrAlreadyInitialized = static_cast<int>(0x80ED0002u);
|
||||
constexpr int kErrParam = static_cast<int>(0x80ED0003u);
|
||||
constexpr int kErrInvalidState = static_cast<int>(0x80ED0005u);
|
||||
|
||||
constexpr std::size_t kParamSize = 16;
|
||||
|
||||
std::mutex g_lock;
|
||||
int g_status = kStatusNone;
|
||||
|
||||
bool Readable(const void* p, std::size_t n) {
|
||||
#ifdef _WIN32
|
||||
if (p == nullptr) return false;
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery(p, &mbi, sizeof(mbi)) == 0 || mbi.State != MEM_COMMIT) return false;
|
||||
if ((mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD)) != 0) return false;
|
||||
const auto end = reinterpret_cast<std::uintptr_t>(mbi.BaseAddress) + mbi.RegionSize;
|
||||
return reinterpret_cast<std::uintptr_t>(p) + n <= end;
|
||||
#else
|
||||
return p != nullptr;
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceErrorDialogClose(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogGetStatus(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogInitialize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::lock_guard g(g_lock);
|
||||
if (g_status != kStatusNone) return kErrAlreadyInitialized;
|
||||
g_status = kStatusInitialized;
|
||||
return kErrOk;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogOpen(const void* param) {
|
||||
(void)param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogTerminate(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::lock_guard g(g_lock);
|
||||
if (g_status != kStatusInitialized && g_status != kStatusFinished) return kErrInvalidState;
|
||||
if (!Readable(param, kParamSize)) return kErrParam;
|
||||
std::int32_t size = 0;
|
||||
std::memcpy(&size, param, sizeof(size));
|
||||
if (static_cast<std::size_t>(size) != kParamSize) return kErrParam;
|
||||
std::int32_t errorCode = 0;
|
||||
std::int32_t userId = 0;
|
||||
std::memcpy(&errorCode, static_cast<const std::uint8_t*>(param) + 4, sizeof(errorCode));
|
||||
std::memcpy(&userId, static_cast<const std::uint8_t*>(param) + 8, sizeof(userId));
|
||||
APS5_HIT("ERRORDIALOG", "open: error_code=0x%08X user_id=%d", errorCode, userId);
|
||||
g_status = kStatusRunning;
|
||||
return kErrOk;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogUpdateStatus(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::lock_guard g(g_lock);
|
||||
if (g_status == kStatusRunning) g_status = kStatusFinished;
|
||||
return g_status;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogGetStatus(void) {
|
||||
std::lock_guard g(g_lock);
|
||||
return g_status;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogClose(void) {
|
||||
std::lock_guard g(g_lock);
|
||||
if (g_status != kStatusRunning) return kErrInvalidState;
|
||||
g_status = kStatusFinished;
|
||||
return kErrOk;
|
||||
}
|
||||
|
||||
int APS5_VABI sceErrorDialogTerminate(void) {
|
||||
std::lock_guard g(g_lock);
|
||||
if (g_status == kStatusNone) return kErrNotInitialized;
|
||||
g_status = kStatusNone;
|
||||
return kErrOk;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceErrorDialogInitialize(void);
|
||||
int APS5_VABI sceErrorDialogOpen(const void* param);
|
||||
int APS5_VABI sceErrorDialogUpdateStatus(void);
|
||||
int APS5_VABI sceErrorDialogGetStatus(void);
|
||||
int APS5_VABI sceErrorDialogClose(void);
|
||||
int APS5_VABI sceErrorDialogTerminate(void);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
constexpr int kStatusRunning = 2;
|
||||
constexpr int kStatusFinished = 3;
|
||||
constexpr int kErrNotInitialized = static_cast<int>(0x80ED0001);
|
||||
constexpr int kErrAlreadyInitialized = static_cast<int>(0x80ED0002);
|
||||
constexpr int kErrParam = static_cast<int>(0x80ED0003);
|
||||
constexpr int kErrInvalidState = static_cast<int>(0x80ED0005);
|
||||
|
||||
struct ErrorDialogParam {
|
||||
std::int32_t size;
|
||||
std::int32_t error_code;
|
||||
std::int32_t user_id;
|
||||
std::int32_t reserved;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
ErrorDialogParam param{16, static_cast<std::int32_t>(0x80020001), 0, 0};
|
||||
|
||||
Require(sceErrorDialogTerminate() == kErrNotInitialized);
|
||||
Require(sceErrorDialogOpen(¶m) == kErrInvalidState);
|
||||
|
||||
Require(sceErrorDialogInitialize() == 0);
|
||||
Require(sceErrorDialogInitialize() == kErrAlreadyInitialized);
|
||||
|
||||
Require(sceErrorDialogClose() == kErrInvalidState);
|
||||
|
||||
Require(sceErrorDialogOpen(nullptr) == kErrParam);
|
||||
ErrorDialogParam bad{8, 0, 0, 0};
|
||||
Require(sceErrorDialogOpen(&bad) == kErrParam);
|
||||
|
||||
Require(sceErrorDialogOpen(¶m) == 0);
|
||||
Require(sceErrorDialogGetStatus() == kStatusRunning);
|
||||
Require(sceErrorDialogOpen(¶m) == kErrInvalidState);
|
||||
|
||||
Require(sceErrorDialogUpdateStatus() == kStatusFinished);
|
||||
Require(sceErrorDialogGetStatus() == kStatusFinished);
|
||||
|
||||
Require(sceErrorDialogOpen(¶m) == 0);
|
||||
Require(sceErrorDialogGetStatus() == kStatusRunning);
|
||||
Require(sceErrorDialogClose() == 0);
|
||||
Require(sceErrorDialogGetStatus() == kStatusFinished);
|
||||
|
||||
Require(sceErrorDialogTerminate() == 0);
|
||||
Require(sceErrorDialogTerminate() == kErrNotInitialized);
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
Throughout the project, every function at every stage either **does exactly what it's supposed to or throws an exception**. Everywhere... except:
|
||||
- [libSceSaveDataDialog.native](../../core/libs/prx/libSceSaveDataDialog.native/Export.cpp)
|
||||
- [libSceCommonDialog](../../core/libs/prx/libSceCommonDialog/Export.cpp)
|
||||
- [libSceErrorDialog](../../core/libs/prx/libSceErrorDialog/Export.cpp) - the error dialog runs the state machine but shows nothing; the error code passed to `sceErrorDialogOpen` is only logged
|
||||
- [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.
|
||||
- [libSceAudioPropagation](../../core/libs/prx/libSceAudioPropagation/Export.cpp) implements only the no-acoustic-propagation path: the system keeps a 16-byte header in the title's CPU memory (no GPU memory), rooms, portals, sources and materials are tracked handles, attributes are validated and have no effect, no rays are requested and sources report no audio paths. Ray results, path calculation and audio paths are accepted only when empty and throw otherwise; `sceAudioPropagationSourceRender` writes silence (zeroes) to each source's output buffer for the given size, without a dry signal. Objects still alive when their system is destroyed become unusable, and their later destroy or unregister is a no-op: PPSA21567 releases its reference-counted room, portal and source owners just before `sceAudioPropagationSystemDestroy`, so the order for every object is not proven. Occlusion, reflections and reverb from level geometry are absent; in-game behaviour remains unverified.
|
||||
- [libSceNpCommerce](../../core/libs/prx/libSceNpCommerce/Export.cpp) - the PS Store icon show/hide calls do nothing
|
||||
|
|
|
|||
Loading…
Reference in New Issue