From 5349b1300b595949e01f2df4bd8499398f24e0b7 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:52:44 +0000 Subject: [PATCH 01/27] feat(libSceIme): parse the ANYPS5_PSN_DIALOGS mode Add ParsePsnDialogNoticeMode, which reads the opt-in that decides whether a PSN-only system dialog shows a host notice. Unset or empty and silent select the current behaviour (no notice); show selects the notice. Any other value throws an error that names the variable and the value, so the title stops at first use instead of guessing. --- .../prx/libSceIme/include/PsnDialogNotice.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/libs/prx/libSceIme/include/PsnDialogNotice.hpp diff --git a/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp b/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp new file mode 100644 index 0000000000..c63e27c062 --- /dev/null +++ b/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp @@ -0,0 +1,18 @@ +#ifndef CORE_LIBS_PRX_LIBSCEIME_INCLUDE_PSNDIALOGNOTICE_HPP +#define CORE_LIBS_PRX_LIBSCEIME_INCLUDE_PSNDIALOGNOTICE_HPP + +#include +#include +#include + +enum class PsnDialogNoticeMode : std::uint8_t { Silent, Show }; + +inline PsnDialogNoticeMode ParsePsnDialogNoticeMode(const char* text) { + if (text == nullptr || *text == '\0') return PsnDialogNoticeMode::Silent; + const std::string value(text); + if (value == "silent") return PsnDialogNoticeMode::Silent; + if (value == "show") return PsnDialogNoticeMode::Show; + throw std::runtime_error("ANYPS5_PSN_DIALOGS must be show or silent, not '" + value + "'"); +} + +#endif From 4b95eb3028b3f9b501b5e0855acb6237d2311126 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:53:31 +0000 Subject: [PATCH 02/27] test(libSceIme): cover the ANYPS5_PSN_DIALOGS mode parser Check that an unset or empty variable and silent select no notice, show selects it, and that the spellings that are not exactly show or silent (case, spaces, numbers, yes/on) are refused with an error naming the variable and the value. --- core/libs/prx/libSceIme/CMakeLists.txt | 5 +++ core/libs/tests/PsnDialogNoticeMode.cpp | 43 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 core/libs/tests/PsnDialogNoticeMode.cpp diff --git a/core/libs/prx/libSceIme/CMakeLists.txt b/core/libs/prx/libSceIme/CMakeLists.txt index 4f5c70898d..95125d3814 100644 --- a/core/libs/prx/libSceIme/CMakeLists.txt +++ b/core/libs/prx/libSceIme/CMakeLists.txt @@ -53,6 +53,11 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() + add_test_executable(psn_dialog_notice_mode_tests ${LIBS_INCLUDE_DIR}/tests/PsnDialogNoticeMode.cpp) + target_include_directories(psn_dialog_notice_mode_tests PRIVATE ${LIBS_INCLUDE_DIR}) + configure_host_test_executable(psn_dialog_notice_mode_tests) + add_test(NAME psn_dialog_notice_mode COMMAND psn_dialog_notice_mode_tests) + add_test_executable(guest_ime_open_tests ${LIBS_INCLUDE_DIR}/tests/GuestImeOpen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libSceVideoOut/src/ImeTextInput.cpp) target_include_directories(guest_ime_open_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_SDL2_INCLUDE_DIR}) diff --git a/core/libs/tests/PsnDialogNoticeMode.cpp b/core/libs/tests/PsnDialogNoticeMode.cpp new file mode 100644 index 0000000000..ca5ed31da6 --- /dev/null +++ b/core/libs/tests/PsnDialogNoticeMode.cpp @@ -0,0 +1,43 @@ +#include "prx/libSceIme/include/PsnDialogNotice.hpp" +#include +#include +#include +#include + +namespace { + +void Require(bool value, const char* what) { + if (!value) { + std::fprintf(stderr, "psn_dialog_notice_mode: %s\n", what); + std::abort(); + } +} + +void RefusesNamingVariable(const char* text) { + try { + ParsePsnDialogNoticeMode(text); + } catch (const std::runtime_error& error) { + const std::string message = error.what(); + Require(message.find("ANYPS5_PSN_DIALOGS") != std::string::npos, "the refusal does not name the variable"); + Require(message.find(std::string("'") + text + "'") != std::string::npos, "the refusal does not name the value"); + return; + } + Require(false, "an invalid value was accepted"); +} + +} + +int main() { + Require(ParsePsnDialogNoticeMode(nullptr) == PsnDialogNoticeMode::Silent, "an unset variable is not silent"); + Require(ParsePsnDialogNoticeMode("") == PsnDialogNoticeMode::Silent, "an empty value is not the same as unset"); + Require(ParsePsnDialogNoticeMode("silent") == PsnDialogNoticeMode::Silent, "silent is not silent"); + Require(ParsePsnDialogNoticeMode("show") == PsnDialogNoticeMode::Show, "show is not show"); + RefusesNamingVariable("Show"); + RefusesNamingVariable("SHOW"); + RefusesNamingVariable("show "); + RefusesNamingVariable("1"); + RefusesNamingVariable("0"); + RefusesNamingVariable("on"); + RefusesNamingVariable("yes"); + return 0; +} From 567de2af3794e42b287a1eaa4ae96f6f1683cc41 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:55:12 +0000 Subject: [PATCH 03/27] feat(libSceIme): offer a notice when a PSN-only dialog opens Add PsnDialogNoticeOffer, the one place the PSN-only dialog libraries call when their Open succeeds. With ANYPS5_PSN_DIALOGS unset or silent it does nothing and never touches the modal dialog. With show it opens an OK notice on the modal dialog of PR #12379, which is refused when no presenter is registered, when another modal dialog is showing, and when a notice was offered less than two seconds before. Refused offers leave the rate-limit state alone, so a later offer can still show. The notice text says the feature needs PlayStation Network, or, for the generic kind, that the system feature is not available; it never names a title. --- core/libs/prx/libSceIme/CMakeLists.txt | 2 +- .../prx/libSceIme/include/ModalDialog.hpp | 1 + .../prx/libSceIme/include/PsnDialogNotice.hpp | 24 +++++++++ .../prx/libSceIme/src/PsnDialogNotice.cpp | 49 +++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 core/libs/prx/libSceIme/src/PsnDialogNotice.cpp diff --git a/core/libs/prx/libSceIme/CMakeLists.txt b/core/libs/prx/libSceIme/CMakeLists.txt index 95125d3814..a2a611fba2 100644 --- a/core/libs/prx/libSceIme/CMakeLists.txt +++ b/core/libs/prx/libSceIme/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(libSceIme SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ModalDialog.cpp) +add_library(libSceIme SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ModalDialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/PsnDialogNotice.cpp) target_include_directories(libSceIme PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceIme) diff --git a/core/libs/prx/libSceIme/include/ModalDialog.hpp b/core/libs/prx/libSceIme/include/ModalDialog.hpp index 3d6efdcecb..21a2eeec56 100644 --- a/core/libs/prx/libSceIme/include/ModalDialog.hpp +++ b/core/libs/prx/libSceIme/include/ModalDialog.hpp @@ -14,6 +14,7 @@ inline constexpr std::size_t ModalDialogMaxButtons = 3; inline constexpr std::uint32_t ModalDialogAnyOwner = 0; inline constexpr std::uint32_t ModalDialogOwnerMsgDialog = 1; inline constexpr std::uint32_t ModalDialogOwnerErrorDialog = 2; +inline constexpr std::uint32_t ModalDialogOwnerPsnNotice = 3; struct ModalDialogRequest { std::uint32_t owner = ModalDialogAnyOwner; diff --git a/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp b/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp index c63e27c062..c12b228f42 100644 --- a/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp +++ b/core/libs/prx/libSceIme/include/PsnDialogNotice.hpp @@ -5,8 +5,14 @@ #include #include +#include "prx/libSceIme/include/ModalDialog.hpp" + enum class PsnDialogNoticeMode : std::uint8_t { Silent, Show }; +enum class PsnDialogNoticeKind : std::uint8_t { PsnFeature, GenericFeature }; + +inline constexpr std::uint64_t PsnDialogNoticeIntervalMilliseconds = 2000; + inline PsnDialogNoticeMode ParsePsnDialogNoticeMode(const char* text) { if (text == nullptr || *text == '\0') return PsnDialogNoticeMode::Silent; const std::string value(text); @@ -15,4 +21,22 @@ inline PsnDialogNoticeMode ParsePsnDialogNoticeMode(const char* text) { throw std::runtime_error("ANYPS5_PSN_DIALOGS must be show or silent, not '" + value + "'"); } +inline ModalDialogRequest PsnDialogNoticeRequest(PsnDialogNoticeKind kind) { + ModalDialogRequest request; + request.owner = ModalDialogOwnerPsnNotice; + if (kind == PsnDialogNoticeKind::PsnFeature) { + request.message = u"This feature needs PlayStation Network, which AnyPS5 does not provide, so it is not available."; + } else { + request.message = u"This system feature is not available in AnyPS5."; + } + request.buttons = {u"OK"}; + request.focus = 0; + request.cancel = 0; + return request; +} + +extern "C" void PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode mode); +extern "C" bool PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind kind); +extern "C" bool PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind kind, std::uint64_t nowMilliseconds); + #endif diff --git a/core/libs/prx/libSceIme/src/PsnDialogNotice.cpp b/core/libs/prx/libSceIme/src/PsnDialogNotice.cpp new file mode 100644 index 0000000000..ee5d3fc00b --- /dev/null +++ b/core/libs/prx/libSceIme/src/PsnDialogNotice.cpp @@ -0,0 +1,49 @@ +#include "prx/libSceIme/include/PsnDialogNotice.hpp" + +#include +#include +#include +#include + +namespace { + +std::mutex g_mutex; +std::optional g_mode; +bool g_shown = false; +std::uint64_t g_lastShown = 0; + +PsnDialogNoticeMode Mode() { + if (!g_mode) g_mode = ParsePsnDialogNoticeMode(std::getenv("ANYPS5_PSN_DIALOGS")); + return *g_mode; +} + +std::uint64_t NowMilliseconds() { + return static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); +} + +} + +extern "C" { + +void PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode mode) { + std::lock_guard lock(g_mutex); + g_mode = mode; + g_shown = false; + g_lastShown = 0; +} + +bool PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind kind, std::uint64_t nowMilliseconds) { + std::lock_guard lock(g_mutex); + if (Mode() != PsnDialogNoticeMode::Show) return false; + if (g_shown && nowMilliseconds >= g_lastShown && nowMilliseconds - g_lastShown < PsnDialogNoticeIntervalMilliseconds) return false; + if (!ModalDialogOpen_nid_postfix(PsnDialogNoticeRequest(kind))) return false; + g_shown = true; + g_lastShown = nowMilliseconds; + return true; +} + +bool PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind kind) { + return PsnDialogNoticeOfferAt_nid_postfix(kind, NowMilliseconds()); +} + +} From 92db033650bbe64015e9ea54f1807f086935f749 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:55:38 +0000 Subject: [PATCH 04/27] test(libSceIme): cover the PSN notice offer Check that no notice is shown without a presenter and in silent mode, that the PSN and generic texts reach the modal dialog with one OK button, that a notice is owned by its own library and captures input, that a second offer is refused while one shows, that a notice is refused within two seconds of the last one and allowed at two seconds, and that a refused offer does not start the interval. --- core/libs/prx/libSceIme/CMakeLists.txt | 10 ++++ core/libs/tests/PsnDialogNotice.cpp | 80 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 core/libs/tests/PsnDialogNotice.cpp diff --git a/core/libs/prx/libSceIme/CMakeLists.txt b/core/libs/prx/libSceIme/CMakeLists.txt index a2a611fba2..e91845fe5a 100644 --- a/core/libs/prx/libSceIme/CMakeLists.txt +++ b/core/libs/prx/libSceIme/CMakeLists.txt @@ -58,6 +58,16 @@ if(BUILD_TESTING) configure_host_test_executable(psn_dialog_notice_mode_tests) add_test(NAME psn_dialog_notice_mode COMMAND psn_dialog_notice_mode_tests) + add_test_executable(psn_dialog_notice_tests ${LIBS_INCLUDE_DIR}/tests/PsnDialogNotice.cpp) + target_include_directories(psn_dialog_notice_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(psn_dialog_notice_tests PRIVATE libSceIme libc) + configure_windows_unwind(psn_dialog_notice_tests) + add_test(NAME psn_dialog_notice COMMAND psn_dialog_notice_tests) + if(WIN32) + set_tests_properties(psn_dialog_notice PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() + add_test_executable(guest_ime_open_tests ${LIBS_INCLUDE_DIR}/tests/GuestImeOpen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libSceVideoOut/src/ImeTextInput.cpp) target_include_directories(guest_ime_open_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_SDL2_INCLUDE_DIR}) diff --git a/core/libs/tests/PsnDialogNotice.cpp b/core/libs/tests/PsnDialogNotice.cpp new file mode 100644 index 0000000000..ca0ca2a5ee --- /dev/null +++ b/core/libs/tests/PsnDialogNotice.cpp @@ -0,0 +1,80 @@ +#include "prx/libSceIme/include/PsnDialogNotice.hpp" +#include +#include + +namespace { + +void Require(bool value, const char* what) { + if (!value) { + std::fprintf(stderr, "psn_dialog_notice: %s\n", what); + std::abort(); + } +} + +const char16_t* const kPsnText = u"This feature needs PlayStation Network, which AnyPS5 does not provide, so it is not available."; +const char16_t* const kGenericText = u"This system feature is not available in AnyPS5."; + +ModalDialogState StateFor(std::uint32_t owner, std::size_t& chosen) { + chosen = 7; + return ModalDialogPoll_nid_postfix(chosen, owner); +} + +bool ShownMessageIs(const char16_t* text) { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == text && view.buttons.size() == 1 && view.buttons[0].label == u"OK" && view.buttons[0].selected; +} + +void Clear() { + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); +} + +} + +int main() { + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + std::size_t chosen = 0; + + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 0), "an offer was shown without a presenter"); + Require(StateFor(ModalDialogAnyOwner, chosen) == ModalDialogState::Idle, "a refused offer left a modal dialog open"); + + ModalDialogSetPresenter_nid_postfix(true); + Require(PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 0), "the first offer with a presenter was refused"); + Require(ModalDialogCapturesInput_nid_postfix(), "the notice does not capture input while it shows"); + Require(ShownMessageIs(kPsnText), "the PSN notice text or its OK button is wrong"); + Require(StateFor(ModalDialogOwnerPsnNotice, chosen) == ModalDialogState::Showing, "the notice is not owned by its library"); + Require(StateFor(ModalDialogOwnerErrorDialog, chosen) == ModalDialogState::Idle, "another owner sees the notice"); + + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::GenericFeature, 2500), "an offer replaced a notice that is still showing"); + Require(ShownMessageIs(kPsnText), "a refused offer changed the notice that is showing"); + + ModalDialogPublishInput_nid_postfix(ModalDialogInputKind::Confirm); + Require(StateFor(ModalDialogOwnerPsnNotice, chosen) == ModalDialogState::Answered, "OK does not end the notice"); + Clear(); + + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 1000), "a second notice came inside the two-second interval"); + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::GenericFeature, 1999), "a notice came one millisecond inside the interval"); + Require(PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::GenericFeature, 2000), "a notice was refused at exactly the interval"); + Require(ShownMessageIs(kGenericText), "the generic notice text is wrong"); + Clear(); + + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 2001), "the interval does not count from the last shown notice"); + Clear(); + + ModalDialogSetPresenter_nid_postfix(false); + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 9000), "an offer was shown without a presenter after a reset"); + ModalDialogSetPresenter_nid_postfix(true); + Require(PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 9001), "a refused offer started the interval"); + Clear(); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(!PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 20000), "silent mode showed a notice"); + Require(StateFor(ModalDialogAnyOwner, chosen) == ModalDialogState::Idle, "silent mode opened a modal dialog"); + Require(!ModalDialogCapturesInput_nid_postfix(), "silent mode captures input"); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + Require(PsnDialogNoticeOfferAt_nid_postfix(PsnDialogNoticeKind::PsnFeature, 20000), "show mode is not restored after silent"); + Clear(); + ModalDialogSetPresenter_nid_postfix(false); + return 0; +} From c4cd5b087119c5a21a2d95dcec3c4c23b1beeb0f Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:14:58 +0000 Subject: [PATCH 05/27] feat(libSceNpProfileDialog): show the PSN notice when the profile opens Open and OpenA send the notice after the parameter checks pass and the dialog moves to FINISHED, so a refused call shows nothing. The status and the result are unchanged. With ANYPS5_PSN_DIALOGS unset the notice is not shown, as before. The library links libSceIme for the shared notice. --- core/libs/prx/libSceNpProfileDialog/CMakeLists.txt | 3 ++- core/libs/prx/libSceNpProfileDialog/Export.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt b/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt index 6406591808..b15cb715fd 100644 --- a/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceNpProfileDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceNpProfileDialog PRIVATE libSceIme) target_include_directories(libSceNpProfileDialog PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceNpProfileDialog PRIVATE libc) configure_windows_unwind(libSceNpProfileDialog) @@ -11,6 +12,6 @@ if(BUILD_TESTING) add_test(NAME guest_np_profile_dialog COMMAND guest_np_profile_dialog_tests) if(WIN32) set_tests_properties(guest_np_profile_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/prx/libSceNpProfileDialog/Export.cpp b/core/libs/prx/libSceNpProfileDialog/Export.cpp index 3ece49618f..0cb54f9a1a 100644 --- a/core/libs/prx/libSceNpProfileDialog/Export.cpp +++ b/core/libs/prx/libSceNpProfileDialog/Export.cpp @@ -7,6 +7,7 @@ #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" namespace { @@ -74,6 +75,7 @@ int OpenDialog(const TParam* param, const char* function) { } dialog.userData = param->userData; dialog.status = COMMON_DIALOG_STATUS_FINISHED; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return COMMON_DIALOG_OK; } From 5e4d168601a86a730b1d37e39fba88ad04cffc72 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:49:35 +0000 Subject: [PATCH 06/27] test(libSceNpProfileDialog): cover the PSN notice on Open and OpenA Check that silent mode and an unset variable show nothing, that show mode shows the PSN notice once for Open and once for OpenA, that a refused null parameter and an Open that throws show nothing, and that no notice is shown when no presenter is registered. The guest test links libSceIme. --- .../prx/libSceNpProfileDialog/CMakeLists.txt | 2 +- core/libs/tests/GuestNpProfileDialog.cpp | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt b/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt index b15cb715fd..d0c19a4614 100644 --- a/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpProfileDialog/CMakeLists.txt @@ -7,7 +7,7 @@ configure_windows_unwind(libSceNpProfileDialog) if(BUILD_TESTING) add_test_executable(guest_np_profile_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpProfileDialog.cpp) target_include_directories(guest_np_profile_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_profile_dialog_tests PRIVATE libSceNpProfileDialog libc) + target_link_libraries(guest_np_profile_dialog_tests PRIVATE libSceNpProfileDialog libSceIme libc) configure_windows_unwind(guest_np_profile_dialog_tests) add_test(NAME guest_np_profile_dialog COMMAND guest_np_profile_dialog_tests) if(WIN32) diff --git a/core/libs/tests/GuestNpProfileDialog.cpp b/core/libs/tests/GuestNpProfileDialog.cpp index 0295a28ba9..49ba4eb805 100644 --- a/core/libs/tests/GuestNpProfileDialog.cpp +++ b/core/libs/tests/GuestNpProfileDialog.cpp @@ -1,4 +1,6 @@ #include "prx/libSceNpProfileDialog/include/Api.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -67,6 +69,56 @@ void RequireCanceled(const std::uint8_t* result, void* userData) { for (std::size_t i = kResultSize; i < kResultSize + 16; ++i) Require(result[i] == kSentinel); } +bool PsnNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::PsnFeature).message; +} + +void RequireNotices(std::uint8_t* param, std::uint8_t* paramA) { + ModalDialogSetPresenter_nid_postfix(true); + Require(sceNpProfileDialogInitialize() == 0); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpProfileDialogOpen(reinterpret_cast(param)) == 0); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + Require(sceNpProfileDialogGetStatus() == kStatusFinished); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpProfileDialogOpen(reinterpret_cast(param)) == 0); + Require(PsnNoticeShown()); + Require(sceNpProfileDialogGetStatus() == kStatusFinished); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpProfileDialogOpenA(reinterpret_cast(paramA)) == 0); + Require(PsnNoticeShown()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpProfileDialogOpen(nullptr) == kErrArgNull); + Require(!PsnNoticeShown()); + + Store(param, 56, 9); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + RequireThrows([&] { sceNpProfileDialogOpen(reinterpret_cast(param)); }); + Require(!PsnNoticeShown()); + Store(param, 56, 1); + + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpProfileDialogOpen(reinterpret_cast(param)) == 0); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpProfileDialogTerminate() == 0); +} + } int main() { @@ -164,5 +216,6 @@ int main() { Require(sceNpProfileDialogGetResult(nullptr) == kErrNotInitialized); Require(sceNpProfileDialogGetStatus() == kStatusNone); Require(sceNpProfileDialogUpdateStatus() == kStatusNone); + RequireNotices(param, paramA); return 0; } From 0ca49992968cfd0aedcc74374fe5162dd4e3afcc Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:49:35 +0000 Subject: [PATCH 07/27] docs(ENVIRONMENT): list ANYPS5_PSN_DIALOGS Add the row for the mode that shows a notice when a PlayStation Network dialog opens, with its unset and invalid behaviour. The row is in its own libSceIme section because the variable is read in that library. --- docs/dev/ENVIRONMENT.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/dev/ENVIRONMENT.md b/docs/dev/ENVIRONMENT.md index af605afcff..d4ecfd4caa 100644 --- a/docs/dev/ENVIRONMENT.md +++ b/docs/dev/ENVIRONMENT.md @@ -524,6 +524,12 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti |------|------|--------|---------|-------------| | `ANYPS5_SYSTEM_FONTS` | string | `core/libs/prx/libSceFont/src/SystemFont.cpp` | | directory holding the system fonts, defaulting to anyps5-fonts beside the executable | +### libSceIme + +| Name | Kind | Source | Default | Description | +|------|------|--------|---------|-------------| +| `ANYPS5_PSN_DIALOGS` | string | `core/libs/prx/libSceIme/src/PsnDialogNotice.cpp` | silent | when a dialog that needs PlayStation Network opens (profile, friend list, store, invitations, Facebook link, authorized apps), `show` displays a host notice that the feature is not available; `silent`, unset or empty shows nothing, as before; any other value stops the title at its first such dialog with an error naming the variable | + ### libSceNpEntitlementAccess | Name | Kind | Source | Default | Description | From 878a41b37ba12e0d5ddcfbbe9694d2b32b27fce6 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:20:40 +0000 Subject: [PATCH 08/27] feat(libSceNpFriendListDialog): show the PSN notice when the friend list opens OpenA sends the notice after its null check passes and the dialog moves to FINISHED. Status and result are unchanged, and nothing is shown unless ANYPS5_PSN_DIALOGS is show. --- core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt | 1 + core/libs/prx/libSceNpFriendListDialog/Export.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt b/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt index 219499b30b..16d58a3b5d 100644 --- a/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceNpFriendListDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceNpFriendListDialog PRIVATE libSceIme) target_include_directories(libSceNpFriendListDialog PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceNpFriendListDialog) diff --git a/core/libs/prx/libSceNpFriendListDialog/Export.cpp b/core/libs/prx/libSceNpFriendListDialog/Export.cpp index aee6fb1edf..776d707121 100644 --- a/core/libs/prx/libSceNpFriendListDialog/Export.cpp +++ b/core/libs/prx/libSceNpFriendListDialog/Export.cpp @@ -5,6 +5,7 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" namespace { @@ -37,6 +38,7 @@ int APS5_VABI sceNpFriendListDialogOpenA(const void* param) { 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; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return 0; } From 52765d01ee2cbb3c4c507dc29934613182882e06 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:20:41 +0000 Subject: [PATCH 09/27] feat(libSceNpSnsFacebookDialog): show the PSN notice when the Facebook dialog opens Open sends the notice after its null check passes and the dialog moves to FINISHED. Status and result are unchanged, and nothing is shown unless ANYPS5_PSN_DIALOGS is show. --- core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt | 1 + core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt b/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt index 5445d0dddd..8c4d0e4fd1 100644 --- a/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceNpSnsFacebookDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceNpSnsFacebookDialog PRIVATE libSceIme) target_include_directories(libSceNpSnsFacebookDialog PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceNpSnsFacebookDialog) diff --git a/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp b/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp index ebad76b2f2..c5010ded30 100644 --- a/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp +++ b/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp @@ -5,6 +5,7 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" namespace { @@ -37,6 +38,7 @@ int APS5_VABI sceNpSnsFacebookDialogOpen(const void* param) { 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; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return 0; } From 68c390da33dcb3b1c22fa48424a384f430fc516b Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:20:41 +0000 Subject: [PATCH 10/27] feat(libSceNpCommerce): show the PSN notice when the store opens Open and Open2 send the notice after their parameter checks pass and the dialog moves to FINISHED, so a refused call shows nothing. Status, result and the store icon calls are unchanged. The store is the PlayStation Store, which needs PlayStation Network; nothing is shown unless ANYPS5_PSN_DIALOGS is show. --- core/libs/prx/libSceNpCommerce/CMakeLists.txt | 1 + core/libs/prx/libSceNpCommerce/Export.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/core/libs/prx/libSceNpCommerce/CMakeLists.txt b/core/libs/prx/libSceNpCommerce/CMakeLists.txt index 1c59d83752..ff1fc6f872 100644 --- a/core/libs/prx/libSceNpCommerce/CMakeLists.txt +++ b/core/libs/prx/libSceNpCommerce/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceNpCommerce SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceNpCommerce PRIVATE libSceIme) target_include_directories(libSceNpCommerce PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceNpCommerce) target_link_libraries(libSceNpCommerce PRIVATE libc libSceUserService) diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index a94c5c9e82..e964c0be97 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -8,6 +8,7 @@ #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" namespace { @@ -109,6 +110,7 @@ int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param) { if (!validateParam(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_userData = param->userData; g_status = COMMON_DIALOG_STATUS_FINISHED; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return 0; } @@ -166,6 +168,7 @@ int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param) { if (!validateParam2(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_userData = param->userData; g_status = COMMON_DIALOG_STATUS_FINISHED; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return 0; } From 276fa3d0b79a276206a86e8067e50449aeccf3d6 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:23:00 +0000 Subject: [PATCH 11/27] feat(libSceNpAuthAuthorizedAppDialog): show the PSN notice when the dialog opens Open sends the notice after its checks pass and the dialog moves to FINISHED. Status and result are unchanged, and nothing is shown unless ANYPS5_PSN_DIALOGS is show. --- core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt | 1 + core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt index 15dcbb4f2e..2be8062bf7 100644 --- a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceNpAuthAuthorizedAppDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceNpAuthAuthorizedAppDialog PRIVATE libSceIme) target_include_directories(libSceNpAuthAuthorizedAppDialog PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceNpAuthAuthorizedAppDialog) diff --git a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp index e483fc0a9d..55718664f4 100644 --- a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp +++ b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp @@ -7,6 +7,7 @@ #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" namespace { @@ -46,6 +47,7 @@ int APS5_VABI sceNpAuthAuthorizedAppDialogOpen(const NpAuthAuthorizedAppDialogPa if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; g_status = COMMON_DIALOG_STATUS_FINISHED; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return 0; } From d6f82110bc9f11aa6b5f50ae25c4c85645e9348f Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:23:00 +0000 Subject: [PATCH 12/27] feat(libSceInvitationDialog): show the PSN notice when the invitation dialog opens Open sends the notice after its parameter checks pass and the dialog moves to FINISHED. The recipients are PlayStation Network online or account ids (shadPS4 invitation_dialog.cpp, friend picker). Status and result are unchanged, and nothing is shown unless ANYPS5_PSN_DIALOGS is show. --- core/libs/prx/libSceInvitationDialog/CMakeLists.txt | 1 + core/libs/prx/libSceInvitationDialog/Export.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/libs/prx/libSceInvitationDialog/CMakeLists.txt b/core/libs/prx/libSceInvitationDialog/CMakeLists.txt index 56b44db4a6..1ebabe86ec 100644 --- a/core/libs/prx/libSceInvitationDialog/CMakeLists.txt +++ b/core/libs/prx/libSceInvitationDialog/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceInvitationDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceInvitationDialog PRIVATE libSceIme) target_include_directories(libSceInvitationDialog PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceInvitationDialog PRIVATE libSceCommonDialog libc) configure_windows_unwind(libSceInvitationDialog) diff --git a/core/libs/prx/libSceInvitationDialog/Export.cpp b/core/libs/prx/libSceInvitationDialog/Export.cpp index 0b41f35ebe..5b10dbd0fa 100644 --- a/core/libs/prx/libSceInvitationDialog/Export.cpp +++ b/core/libs/prx/libSceInvitationDialog/Export.cpp @@ -2,6 +2,7 @@ #include "prx/libSceCommonDialog/include/DialogUse.hpp" #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include #include #include @@ -85,6 +86,7 @@ int APS5_VABI sceInvitationDialogOpen(const InvitationDialogParam* param) { } dialog.callbackArg = param->callbackArg; dialog.status = COMMON_DIALOG_STATUS_FINISHED; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::PsnFeature); return COMMON_DIALOG_OK; } From a15c4bd908a263c7f7fc6bb2ab9d85e2055cba48 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:23:00 +0000 Subject: [PATCH 13/27] feat(libSceWebBrowserDialog): show a generic notice when the browser opens Open and OpenForPredeterminedContent send the generic notice after their checks pass and the dialog moves to FINISHED. The browser's open-parameter layout has no public source and nothing ties this dialog to PlayStation Network, so the notice says only that the system feature is not available. Status and result are unchanged, and nothing is shown unless ANYPS5_PSN_DIALOGS is show. --- core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt | 1 + core/libs/prx/libSceWebBrowserDialog/Export.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt b/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt index 641f60463e..162243d2f3 100644 --- a/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt +++ b/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(libSceWebBrowserDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_link_libraries(libSceWebBrowserDialog PRIVATE libSceIme) target_include_directories(libSceWebBrowserDialog PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceWebBrowserDialog) diff --git a/core/libs/prx/libSceWebBrowserDialog/Export.cpp b/core/libs/prx/libSceWebBrowserDialog/Export.cpp index d5961d6211..35a34fbe30 100644 --- a/core/libs/prx/libSceWebBrowserDialog/Export.cpp +++ b/core/libs/prx/libSceWebBrowserDialog/Export.cpp @@ -6,6 +6,7 @@ #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" static_assert(sizeof(WebBrowserDialogCookieParam) == 280); static_assert(offsetof(WebBrowserDialogCookieParam, address) == 8); @@ -100,6 +101,7 @@ int APS5_VABI sceWebBrowserDialogOpen(const void* param) { if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; g_status = COMMON_DIALOG_STATUS_FINISHED; g_open = true; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::GenericFeature); return 0; } @@ -137,6 +139,7 @@ int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param, if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; g_status = COMMON_DIALOG_STATUS_FINISHED; g_open = true; + PsnDialogNoticeOffer_nid_postfix(PsnDialogNoticeKind::GenericFeature); return 0; } From 652119fca3497e0ba908dbfa47d9496f2589e9fc Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:26:17 +0000 Subject: [PATCH 14/27] test(libSceNpFriendListDialog): cover the PSN notice on OpenA Check that silent mode and an unset variable show nothing, that show mode shows the PSN notice once when OpenA succeeds, that a null parameter shows nothing, and that no notice is shown without a presenter. The guest test links libSceIme. --- core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt b/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt index 16d58a3b5d..c235b1f317 100644 --- a/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpFriendListDialog/CMakeLists.txt @@ -6,10 +6,10 @@ configure_windows_unwind(libSceNpFriendListDialog) if(BUILD_TESTING) add_test_executable(guest_np_friend_list_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpFriendListDialog.cpp) target_include_directories(guest_np_friend_list_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_friend_list_dialog_tests PRIVATE libSceNpFriendListDialog) + target_link_libraries(guest_np_friend_list_dialog_tests PRIVATE libSceNpFriendListDialog libSceIme) add_test(NAME guest_np_friend_list_dialog COMMAND guest_np_friend_list_dialog_tests) if(WIN32) set_tests_properties(guest_np_friend_list_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() From e654d730cc2570ad71f039b4b14adb47fe795d9d Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:26:17 +0000 Subject: [PATCH 15/27] test(libSceNpSnsFacebookDialog): cover the PSN notice on Open Check that silent mode shows nothing, that show mode shows the PSN notice when Open succeeds, that a null parameter shows nothing, and that no notice is shown without a presenter. The guest test links libSceIme. --- core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt b/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt index 8c4d0e4fd1..b7cfa38eee 100644 --- a/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt @@ -6,10 +6,10 @@ configure_windows_unwind(libSceNpSnsFacebookDialog) if(BUILD_TESTING) add_test_executable(guest_np_sns_facebook_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpSnsFacebookDialog.cpp) target_include_directories(guest_np_sns_facebook_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_sns_facebook_dialog_tests PRIVATE libSceNpSnsFacebookDialog) + target_link_libraries(guest_np_sns_facebook_dialog_tests PRIVATE libSceNpSnsFacebookDialog libSceIme) add_test(NAME guest_np_sns_facebook_dialog COMMAND guest_np_sns_facebook_dialog_tests) if(WIN32) set_tests_properties(guest_np_sns_facebook_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() From e1ed04d496435cc159ea8057900fd0ef0330d3e9 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:28:07 +0000 Subject: [PATCH 16/27] test(libSceNpCommerce): cover the PSN notice on Open and Open2 Check that silent mode shows nothing, that show mode shows the PSN notice when Open and Open2 succeed with a valid store parameter, that a null parameter shows nothing, and that no notice is shown without a presenter. The guest test links libSceIme. --- core/libs/prx/libSceNpCommerce/CMakeLists.txt | 4 +- core/libs/tests/GuestNpCommerceDialog.cpp | 55 +++++++++++++++++++ core/libs/tests/GuestNpFriendListDialog.cpp | 32 +++++++++++ core/libs/tests/GuestNpSnsFacebookDialog.cpp | 33 +++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpCommerce/CMakeLists.txt b/core/libs/prx/libSceNpCommerce/CMakeLists.txt index ff1fc6f872..dfd5e7d90a 100644 --- a/core/libs/prx/libSceNpCommerce/CMakeLists.txt +++ b/core/libs/prx/libSceNpCommerce/CMakeLists.txt @@ -7,11 +7,11 @@ target_link_libraries(libSceNpCommerce PRIVATE libc libSceUserService) if(BUILD_TESTING) add_test_executable(guest_np_commerce_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpCommerceDialog.cpp) target_include_directories(guest_np_commerce_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_commerce_dialog_tests PRIVATE libSceNpCommerce libc libSceUserService) + target_link_libraries(guest_np_commerce_dialog_tests PRIVATE libSceNpCommerce libSceIme libc libSceUserService) configure_windows_unwind(guest_np_commerce_dialog_tests) add_test(NAME guest_np_commerce_dialog COMMAND guest_np_commerce_dialog_tests) if(WIN32) set_tests_properties(guest_np_commerce_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 28a329dd02..12cd7fa4bf 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -1,6 +1,8 @@ #include "prx/libSceNpCommerce/include/Api.hpp" #include "ProcessParametersFixture.hpp" #include "prx/libSceUserService/UserService.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include #include #include "SceTypes.hpp" @@ -33,6 +35,58 @@ void RequireCanceledResult(std::uint64_t userData, bool clearsReserved) { for (const auto value : result.reserved) Require(value == (clearsReserved ? 0 : 0x33)); } + +bool PsnNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::PsnFeature).message; +} + +void RequireNotices() { + std::uint32_t sdkVersion = 0x03500000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + NpCommerceDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = USER_SERVICE_INITIAL_USER_ID; + param.mode = 5; + param.argument = 1; + NpCommerceDialogParam2 param2{}; + param2.base.size = sizeof(param2.base); + param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; + param2.size = sizeof(param2); + param2.userId = USER_SERVICE_INITIAL_USER_ID; + param2.mode = 5; + param2.argument = 1; + + ModalDialogSetPresenter_nid_postfix(true); + sceNpCommerceDialogTerminate(); + Require(sceNpCommerceDialogInitialize() == 0); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpCommerceDialogOpen(¶m) == 0); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpCommerceDialogOpen(nullptr) == static_cast(0x80B8000Du)); + Require(!PsnNoticeShown()); + Require(sceNpCommerceDialogOpen(¶m) == 0); + Require(PsnNoticeShown()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpCommerceDialogOpen2(¶m2) == 0); + Require(PsnNoticeShown()); + + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(sceNpCommerceDialogTerminate() == 0); +} + } int main() { @@ -204,6 +258,7 @@ int main() { Require(sceNpCommerceDialogGetResult(&result) == static_cast(0x80B80003u)); Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceNpCommerceDialogUpdateStatus() == statusNone); + RequireNotices(); Require(sceUserServiceTerminate() == 0); return 0; } diff --git a/core/libs/tests/GuestNpFriendListDialog.cpp b/core/libs/tests/GuestNpFriendListDialog.cpp index 897d26ebe2..b71297ccdc 100644 --- a/core/libs/tests/GuestNpFriendListDialog.cpp +++ b/core/libs/tests/GuestNpFriendListDialog.cpp @@ -1,5 +1,7 @@ #include "prx/libSceNpFriendListDialog/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include #include @@ -16,6 +18,35 @@ constexpr int kErrNotFinished = static_cast(0x80B80005); constexpr int kErrArgNull = static_cast(0x80B8000D); constexpr int kResultUserCanceled = 1; +bool PsnNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::PsnFeature).message; +} + +void RequireNotices() { + std::uint64_t param = 0; + ModalDialogSetPresenter_nid_postfix(true); + Require(sceNpFriendListDialogInitialize() == 0); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpFriendListDialogOpenA(¶m) == 0); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpFriendListDialogOpenA(nullptr) == kErrArgNull); + Require(!PsnNoticeShown()); + Require(sceNpFriendListDialogOpenA(¶m) == 0); + Require(PsnNoticeShown()); + + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(sceNpFriendListDialogTerminate() == 0); +} + } int main() { @@ -75,4 +106,5 @@ int main() { Require(sceNpFriendListDialogGetResultA(result) == kErrNotInitialized); Require(sceNpFriendListDialogGetStatus() == kStatusNone); Require(sceNpFriendListDialogUpdateStatus() == kStatusNone); + RequireNotices(); } diff --git a/core/libs/tests/GuestNpSnsFacebookDialog.cpp b/core/libs/tests/GuestNpSnsFacebookDialog.cpp index 507569039f..ce6f93b7d2 100644 --- a/core/libs/tests/GuestNpSnsFacebookDialog.cpp +++ b/core/libs/tests/GuestNpSnsFacebookDialog.cpp @@ -1,5 +1,7 @@ #include "prx/libSceNpSnsFacebookDialog/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include #include @@ -16,6 +18,36 @@ constexpr int kErrNotFinished = static_cast(0x80B80005); constexpr int kErrArgNull = static_cast(0x80B8000D); constexpr int kResultUserCanceled = 1; + +bool PsnNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::PsnFeature).message; +} + +void RequireNotices() { + std::uint64_t param = 0; + ModalDialogSetPresenter_nid_postfix(true); + Require(sceNpSnsFacebookDialogInitialize() == 0); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpSnsFacebookDialogOpen(¶m) == 0); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpSnsFacebookDialogOpen(nullptr) == kErrArgNull); + Require(!PsnNoticeShown()); + Require(sceNpSnsFacebookDialogOpen(¶m) == 0); + Require(PsnNoticeShown()); + + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(sceNpSnsFacebookDialogTerminate() == 0); +} + } int main() { @@ -75,4 +107,5 @@ int main() { Require(sceNpSnsFacebookDialogGetResult(result) == kErrNotInitialized); Require(sceNpSnsFacebookDialogGetStatus() == kStatusNone); Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusNone); + RequireNotices(); } From 1bbf054cee7ba03c384386ed30e134836f22309f Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:31:22 +0000 Subject: [PATCH 17/27] test(libSceInvitationDialog): cover the PSN notice on Open Check that silent mode shows nothing, that show mode shows the PSN notice once when Open succeeds, that a null parameter shows nothing, and that the dialog still finishes with the same result. The guest test links libSceIme. --- .../prx/libSceInvitationDialog/CMakeLists.txt | 4 +-- core/libs/tests/GuestInvitationDialog.cpp | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceInvitationDialog/CMakeLists.txt b/core/libs/prx/libSceInvitationDialog/CMakeLists.txt index 1ebabe86ec..a5a4393458 100644 --- a/core/libs/prx/libSceInvitationDialog/CMakeLists.txt +++ b/core/libs/prx/libSceInvitationDialog/CMakeLists.txt @@ -7,11 +7,11 @@ configure_windows_unwind(libSceInvitationDialog) if(BUILD_TESTING) add_test_executable(guest_invitation_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestInvitationDialog.cpp) target_include_directories(guest_invitation_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_invitation_dialog_tests PRIVATE libSceInvitationDialog libSceCommonDialog libc) + target_link_libraries(guest_invitation_dialog_tests PRIVATE libSceInvitationDialog libSceCommonDialog libSceIme libc) configure_windows_unwind(guest_invitation_dialog_tests) add_test(NAME guest_invitation_dialog COMMAND guest_invitation_dialog_tests) if(WIN32) set_tests_properties(guest_invitation_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/tests/GuestInvitationDialog.cpp b/core/libs/tests/GuestInvitationDialog.cpp index 37978bac59..f7caafb0fc 100644 --- a/core/libs/tests/GuestInvitationDialog.cpp +++ b/core/libs/tests/GuestInvitationDialog.cpp @@ -1,4 +1,6 @@ #include "prx/libSceInvitationDialog/include/Api.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include "prx/libSceCommonDialog/include/Api.hpp" #include "prx/libSceCommonDialog/include/DialogUse.hpp" #include "prx/libc/include/general/VabiMacros.hpp" @@ -173,6 +175,36 @@ void TestStatusAndPending() { Require(sceInvitationDialogGetStatus_nid_postfix() == StatusNone, "status after terminate"); } + +bool PsnNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::PsnFeature).message; +} + +void RequireNotices() { + const InvitationDialogParam send = MakeParam(ModeSend, nullptr); + ModalDialogSetPresenter_nid_postfix(true); + Require(sceInvitationDialogInitialize() == 0, "initialize for the notice check"); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceInvitationDialogOpen(&send) == 0, "silent open"); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix(), "silent mode showed a notice"); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceInvitationDialogOpen(nullptr) == ArgNull, "null open"); + Require(!PsnNoticeShown(), "a refused open showed the notice"); + Require(sceInvitationDialogOpen(&send) == 0, "show open"); + Require(PsnNoticeShown(), "open did not show the PSN notice"); + + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(sceInvitationDialogTerminate() == 0, "terminate after the notice check"); +} + } int main() { @@ -181,5 +213,6 @@ int main() { TestOpenChecks(); TestCanceled(); TestStatusAndPending(); + RequireNotices(); return 0; } From ada749dda2406adb9d236f847fb9fddd1576af98 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:31:23 +0000 Subject: [PATCH 18/27] test(libSceNpAuthAuthorizedAppDialog): cover the PSN notice on Open Check that silent mode shows nothing, that show mode shows the PSN notice when Open succeeds with a valid parameter, and that a null parameter shows nothing. The guest test links libSceIme. --- .../CMakeLists.txt | 4 +- .../tests/GuestNpAuthAuthorizedAppDialog.cpp | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt index 2be8062bf7..e8d6b871fd 100644 --- a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt @@ -8,10 +8,10 @@ target_link_libraries(libSceNpAuthAuthorizedAppDialog PRIVATE libSceUserService) if(BUILD_TESTING) add_test_executable(guest_np_auth_authorized_app_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpAuthAuthorizedAppDialog.cpp) target_include_directories(guest_np_auth_authorized_app_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_auth_authorized_app_dialog_tests PRIVATE libSceNpAuthAuthorizedAppDialog libSceUserService) + target_link_libraries(guest_np_auth_authorized_app_dialog_tests PRIVATE libSceNpAuthAuthorizedAppDialog libSceIme libSceUserService) add_test(NAME guest_np_auth_authorized_app_dialog COMMAND guest_np_auth_authorized_app_dialog_tests) if(WIN32) set_tests_properties(guest_np_auth_authorized_app_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp b/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp index 793b7cc3c5..e39943224a 100644 --- a/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp +++ b/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp @@ -1,4 +1,6 @@ #include "prx/libSceNpAuthAuthorizedAppDialog/include/Api.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -19,6 +21,45 @@ constexpr int kErrNotFinished = static_cast(0x80B80005); constexpr int kErrArgNull = static_cast(0x80B8000D); constexpr int kResultUserCanceled = 1; + +bool PsnNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::PsnFeature).message; +} + +void RequireNotices() { + NpAuthAuthorizedAppDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = USER_SERVICE_INITIAL_USER_ID; + char application[136] = "test"; + std::string text(16, 'a'); + param.application = application; + param.text = text.c_str(); + + ModalDialogSetPresenter_nid_postfix(true); + Require(sceNpAuthAuthorizedAppDialogInitialize() == 0); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == 0); + Require(!PsnNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceNpAuthAuthorizedAppDialogOpen(nullptr) == static_cast(0x80B8000Du)); + Require(!PsnNoticeShown()); + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == 0); + Require(PsnNoticeShown()); + + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(sceNpAuthAuthorizedAppDialogTerminate() == 0); +} + } int main() { @@ -138,6 +179,7 @@ int main() { Require(sceNpAuthAuthorizedAppDialogGetResult(result) == kErrNotFinished); Require(sceNpAuthAuthorizedAppDialogTerminate() == 0); Require(sceNpAuthAuthorizedAppDialogTerminate() == kErrNotInitialized); + RequireNotices(); Require(sceUserServiceTerminate() == 0); return 0; } From 3ee33308f3fd048220087c12d03a316e8771e389 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:31:24 +0000 Subject: [PATCH 19/27] test(libSceWebBrowserDialog): cover the generic notice on Open Check that silent mode shows nothing, that show mode shows the generic notice for Open and OpenForPredeterminedContent, and that a null parameter shows nothing. The guest test links libSceIme. --- .../prx/libSceWebBrowserDialog/CMakeLists.txt | 4 +- core/libs/tests/GuestWebBrowserDialog.cpp | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt b/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt index 162243d2f3..3e3933240a 100644 --- a/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt +++ b/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt @@ -6,11 +6,11 @@ configure_windows_unwind(libSceWebBrowserDialog) if(BUILD_TESTING) add_test_executable(guest_web_browser_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestWebBrowserDialog.cpp) target_include_directories(guest_web_browser_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_web_browser_dialog_tests PRIVATE libSceWebBrowserDialog) + target_link_libraries(guest_web_browser_dialog_tests PRIVATE libSceWebBrowserDialog libSceIme) add_test(NAME guest_web_browser_dialog COMMAND guest_web_browser_dialog_tests) if(WIN32) set_tests_properties(guest_web_browser_dialog PROPERTIES ENVIRONMENT_MODIFICATION - "PATH=path_list_prepend:$") + "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/tests/GuestWebBrowserDialog.cpp b/core/libs/tests/GuestWebBrowserDialog.cpp index d9afba0bd3..ec1c57803f 100644 --- a/core/libs/tests/GuestWebBrowserDialog.cpp +++ b/core/libs/tests/GuestWebBrowserDialog.cpp @@ -1,4 +1,6 @@ #include "prx/libSceWebBrowserDialog/include/Api.hpp" +#include "prx/libSceIme/include/ModalDialog.hpp" +#include "prx/libSceIme/include/PsnDialogNotice.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include @@ -18,6 +20,43 @@ constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); void Require(bool value) { if (!value) std::abort(); } + +bool GenericNoticeShown() { + ModalDialogView view; + if (!ModalDialogReadView_nid_postfix(view)) return false; + return view.message == PsnDialogNoticeRequest(PsnDialogNoticeKind::GenericFeature).message; +} + +void RequireNotices(const void* param) { + WebBrowserDialogAllowList allowList{}; + allowList.size = sizeof(allowList); + allowList.addresses[0] = "test.invalid"; + ModalDialogSetPresenter_nid_postfix(true); + Require(sceWebBrowserDialogInitialize() == 0); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceWebBrowserDialogOpen(param) == 0); + Require(!GenericNoticeShown() && !ModalDialogCapturesInput_nid_postfix()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceWebBrowserDialogOpen(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); + Require(!GenericNoticeShown()); + Require(sceWebBrowserDialogOpen(param) == 0); + Require(GenericNoticeShown()); + + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Show); + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == 0); + Require(GenericNoticeShown()); + + ModalDialogReset_nid_postfix(ModalDialogAnyOwner); + ModalDialogSetPresenter_nid_postfix(false); + PsnDialogNoticeSetMode_nid_postfix(PsnDialogNoticeMode::Silent); + Require(sceWebBrowserDialogTerminate() == 0); +} + } int main() { @@ -146,5 +185,6 @@ int main() { Require(sceWebBrowserDialogGetResult(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); Require(sceWebBrowserDialogUpdateStatus() == COMMON_DIALOG_STATUS_NONE); + RequireNotices(param); return 0; } From 7fd88596080d39366989d8e49d57b7323f104d72 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:49:59 +0000 Subject: [PATCH 20/27] docs(USAGE): describe the PSN dialog notice Add a section on ANYPS5_PSN_DIALOGS: which dialogs finish silently without PlayStation Network, what show displays, how the notice takes input and how often it appears, and what the other values do. --- docs/user/USAGE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/user/USAGE.md b/docs/user/USAGE.md index 4ebc887dbe..1cbaf2e1c9 100644 --- a/docs/user/USAGE.md +++ b/docs/user/USAGE.md @@ -524,6 +524,12 @@ A line is the player number (the controller's slot: a controller that reconnects The panel uses the font and scale of the [performance overlay](#performance-overlay) and sits in the corner mirrored horizontally from `ANYPS5_OVERLAY_POS`, the top right by default, so it never covers that overlay. While the system on-screen keyboard is shown the panel is not drawn, and its 4 seconds keep running. Screenshots do not include it. While it is hidden, which is always so unless F9 or `ANYPS5_PAD_STATUS=1` shows it, nothing is drawn, no render pass or memory is allocated for it and the presented image is unchanged. What still runs is a lock and a clock read in each present and in each video-out loop pass, to see whether the panel is wanted. With `ANYPS5_PAD_STATUS` unset or 0 and no F9 press, no controller name is copied and no battery level is read for it; they are read on every loop pass with `ANYPS5_PAD_STATUS=1`, and while the panel is shown after F9. +### PSN-only dialogs + +Some system dialogs need PlayStation Network: the profile, the friend list, the store, the invitation dialog, the Facebook link and the authorized-apps dialog. AnyPS5 does not provide PlayStation Network, so these dialogs finish at once with the same answer as a cancel, and nothing is shown. The web browser dialog is not known to need PlayStation Network, so its notice only says that the system feature is not available. + +Set `ANYPS5_PSN_DIALOGS=show` to show a notice on the game's screen when one of these dialogs opens. The notice has one OK button and takes the controller until it is answered. It is not shown again within two seconds of the previous one, and it is not shown while another dialog or notice is showing. The game's result is the same either way. `silent`, an unset variable and an empty one show nothing. Any other value stops the game at its first such dialog with an error that names `ANYPS5_PSN_DIALOGS`. + ### Windows crash diagnostics When a register points at readable memory that starts with at least four printable ASCII characters, either as UTF-16LE or as single bytes, the crash report prints that text after the register memory dump, as `[r13] u"..."` for UTF-16LE or `[rdi] "..."` for single bytes. The text ends at a NUL, at the first character that is not printable ASCII, a tab, a line feed or a carriage return, after 512 characters or at the end of readable memory. Tabs, line breaks, quotes and backslashes are escaped, and `...` after the closing quote means that no NUL ended the text within those limits. From 3960481e253b6934831df792637bf3e6980d3b8b Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:09 +0000 Subject: [PATCH 21/27] docs(libSceNpProfileDialog): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceNpProfileDialog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceNpProfileDialog.md b/docs/dev/debt/libSceNpProfileDialog.md index 54a1f1b591..5d274e8544 100644 --- a/docs/dev/debt/libSceNpProfileDialog.md +++ b/docs/dev/debt/libSceNpProfileDialog.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceNpProfileDialogOpen, sceNpProfileDialogOpenA](../../../core/libs/prx/libSceNpProfileDialog/Export.cpp) (libSceNpProfileDialog) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's notice that the feature needs PlayStation Network; the console's own display for this call without PlayStation Network is not measured, and the notice wording is AnyPS5's. - [sceNpProfileDialogOpen, sceNpProfileDialogOpenA, sceNpProfileDialogGetResult](../../../core/libs/prx/libSceNpProfileDialog/Export.cpp) (libSceNpProfileDialog) - no profile is shown: `Open` and `OpenA` move the dialog straight to `FINISHED`, and `GetResult` reports `result` 0, `userAction` `USER_CANCELED` (1) and the caller's `userData`, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/c2814041e546bf75c12756dbec9da911c721ce1c/src/core/libraries/np/np_profile_dialog/np_profile_dialog.cpp#L21-L180) does when its window is dismissed. Not measured on a console. ### Unknown function info From f66baaf45bbf5a0da8c3fd987e4f4c2c7b02f8c9 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:09 +0000 Subject: [PATCH 22/27] docs(libSceNpFriendListDialog): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceNpFriendListDialog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceNpFriendListDialog.md b/docs/dev/debt/libSceNpFriendListDialog.md index 2d5b73e230..20d29a05ed 100644 --- a/docs/dev/debt/libSceNpFriendListDialog.md +++ b/docs/dev/debt/libSceNpFriendListDialog.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceNpFriendListDialogOpenA](../../../core/libs/prx/libSceNpFriendListDialog/Export.cpp) (libSceNpFriendListDialog) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's notice that the feature needs PlayStation Network; the console's own display without PlayStation Network is not measured, and the notice wording is AnyPS5's. - [libSceNpFriendListDialog](../../../core/libs/prx/libSceNpFriendListDialog/Export.cpp) - the friend list dialog (`sceNpFriendListDialogOpenA` and `sceNpFriendListDialogGetResultA`) runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no PSN friend list. The signatures are from [Orbital's type database](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json); the result layout beyond the leading `int32_t` result code is not known, so nothing else is written. ### Unknown function info From a9eab48ee46cd3a96b9d44527dabf2e4fca654f3 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:09 +0000 Subject: [PATCH 23/27] docs(libSceNpCommerce): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceNpCommerce.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index 762eed1207..a4bb61b2f1 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceNpCommerceDialogOpen, sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's notice that the feature needs PlayStation Network; the console's own display without PlayStation Network is not measured, and the notice wording is AnyPS5's. - [libSceNpCommerce](../../../core/libs/prx/libSceNpCommerce/Export.cpp) - the PS Store icon show/hide calls and `sceNpCommerceSetPsStoreIconLayout` do nothing ### Unknown function info From 2b2487edf46e9209e33690c05465a0a1734163eb Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:10 +0000 Subject: [PATCH 24/27] docs(libSceNpSnsFacebookDialog): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceNpSnsFacebookDialog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceNpSnsFacebookDialog.md b/docs/dev/debt/libSceNpSnsFacebookDialog.md index 5ec4954b21..627dee78b0 100644 --- a/docs/dev/debt/libSceNpSnsFacebookDialog.md +++ b/docs/dev/debt/libSceNpSnsFacebookDialog.md @@ -2,4 +2,5 @@ ### Silent stubs +- [sceNpSnsFacebookDialogOpen](../../../core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp) (libSceNpSnsFacebookDialog) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's notice that the feature needs PlayStation Network; the console's own display without PlayStation Network is not measured, and the notice wording is AnyPS5's. - [libSceNpSnsFacebookDialog](../../../core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp) - the Facebook dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no Facebook account link. The signatures are from [Orbital's type database](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json); the result layout beyond the leading `int32_t` result code is not known, so nothing else is written. From b5a69dcf889d29872cbdd2009e1037cd8af1c40e Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:10 +0000 Subject: [PATCH 25/27] docs(libSceInvitationDialog): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceInvitationDialog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceInvitationDialog.md b/docs/dev/debt/libSceInvitationDialog.md index 796df23dc8..e8ed9744c1 100644 --- a/docs/dev/debt/libSceInvitationDialog.md +++ b/docs/dev/debt/libSceInvitationDialog.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceInvitationDialogOpen](../../../core/libs/prx/libSceInvitationDialog/Export.cpp) (libSceInvitationDialog) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's notice that the feature needs PlayStation Network; the console's own display without PlayStation Network is not measured, and the notice wording is AnyPS5's. - [sceInvitationDialogOpen, sceInvitationDialogGetResult](../../../core/libs/prx/libSceInvitationDialog/Export.cpp) (libSceInvitationDialog, PS4) - no dialog is shown and no invitation is sent: `Open` moves the dialog straight to `FINISHED`, and `GetResult` returns 0 with `errorCode` 0, `result` `USER_CANCELED` (1), the `callbackArg` of the last `Open` and a recipient count of 0, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/2cdefaa1298c6e4ece6c74bd84e90f3906282d51/src/core/libraries/invitation_dialog/invitation_dialog.cpp) reports when its window is dismissed and as [libSceNpProfileDialog](libSceNpProfileDialog.md) does. Not measured on a console. ### Unknown function info From ac59598cc99ee638a34dc230b97817346ec68c2c Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:10 +0000 Subject: [PATCH 26/27] docs(libSceNpAuthAuthorizedAppDialog): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md b/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md index ff59e27255..009a232b17 100644 --- a/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md +++ b/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceNpAuthAuthorizedAppDialogOpen](../../../core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp) (libSceNpAuthAuthorizedAppDialog) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's notice that the feature needs PlayStation Network; the console's own display without PlayStation Network is not measured, and the notice wording is AnyPS5's. - [libSceNpAuthAuthorizedAppDialog](../../../core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp) - the authorized-app dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no PSN sign-in to authorize an app against. Parameter fields at offsets 64 and 72 feed a 136-byte application record and a text value of at most 1024 bytes; their service-side meaning remains incomplete. ### Unknown function info From 8ddc18f4917533e68857c87b1c4d9f0c4add49fb Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:50:10 +0000 Subject: [PATCH 27/27] docs(libSceWebBrowserDialog): record the unmeasured PSN notice The debt entry says that the notice shown with ANYPS5_PSN_DIALOGS=show is AnyPS5's, and that what the console displays for this call without PlayStation Network is not measured. --- docs/dev/debt/libSceWebBrowserDialog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceWebBrowserDialog.md b/docs/dev/debt/libSceWebBrowserDialog.md index ba44c2159d..85a618d7cf 100644 --- a/docs/dev/debt/libSceWebBrowserDialog.md +++ b/docs/dev/debt/libSceWebBrowserDialog.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceWebBrowserDialogOpen, sceWebBrowserDialogOpenForPredeterminedContent](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) (libSceWebBrowserDialog) - with `ANYPS5_PSN_DIALOGS=show`, a successful open shows AnyPS5's generic notice that the system feature is not available; no PlayStation Network link is claimed, since no source ties this dialog to it, and the console's own display is not measured. - [libSceWebBrowserDialog](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) - `sceWebBrowserDialogOpen` and `sceWebBrowserDialogOpenForPredeterminedContent` run the common-dialog state machine without displaying an interactive browser, and `sceWebBrowserDialogGetResult` reports that the user cancelled. Minecraft Bedrock (PPSA17221) imports `sceWebBrowserDialogOpenForPredeterminedContent` (#5744). The 424-byte allow-list parameter and its twenty optional address pointers are validated, but address filtering is not modelled without an interactive browser. Main open-parameter validation remains incomplete. - [sceWebBrowserDialogSetCookie, sceWebBrowserDialogResetCookie](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) (libSceWebBrowserDialog) - there is no browser cookie store. Both calls check their parameters in the same order and with the same codes as the PS5 13.60 module, then return 0 without storing or clearing anything. On the console they then forward the request to the browser service. While an opened dialog has not been updated to finished, closed or terminated, they return `0x80B80006` (the console's open-dialog flag). ### Unknown function info