mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
feat: PS4 NpTrophy, save data v1, NpWebApi offline and Limbo's other imports
NpTrophy reads the trophy count and icons from the title's trophy package (names are encrypted and stay not implemented), PS4 v1 save data calls wrap the native v2/v3 ones, and NpWebApi library contexts, handles and push event filters work offline (service filters fail without a signed-in user). libSceAjm is built from the libSceAjm.native sources for PS4 titles. Limbo's (CUSA01664) remaining PS4 imports, including the synchronous PS4 TUS calls and the Facebook dialog calls next to the existing libraries, are exported as not implemented.
This commit is contained in:
1 parent
cecb540a63
commit
f04d16d702
25 files changed
+1371
-13
No files matched your search
@@ -0,0 +1,15 @@
|
||||
function(add_sce_ajm_library target)
|
||||
set(ajmDir ${CMAKE_CURRENT_FUNCTION_LIST_DIR})
|
||||
add_library(${target} SHARED EXCLUDE_FROM_ALL
|
||||
${ajmDir}/Export.cpp
|
||||
${ajmDir}/src/Ajm.cpp
|
||||
${ARGN}
|
||||
)
|
||||
target_include_directories(${target} PRIVATE
|
||||
${LIBS_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/LibAtrac9/C/src
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/vgmstream/src/coding/libs
|
||||
)
|
||||
target_link_libraries(${target} PRIVATE atrac9 atrac9_vibration ffmpeg libc libkernel)
|
||||
configure_windows_unwind(${target})
|
||||
endfunction()
|
||||
@@ -29,18 +29,8 @@ add_library(atrac9_vibration STATIC EXCLUDE_FROM_ALL
|
||||
)
|
||||
set_target_properties(atrac9_vibration PROPERTIES POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden)
|
||||
|
||||
add_library(libSceAjm.native SHARED EXCLUDE_FROM_ALL
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Ajm.cpp
|
||||
)
|
||||
|
||||
target_include_directories(libSceAjm.native PRIVATE
|
||||
${LIBS_INCLUDE_DIR}
|
||||
${LIBATRAC9_DIR}
|
||||
${VGMSTREAM_ATRAC9_DIR}
|
||||
)
|
||||
target_link_libraries(libSceAjm.native PRIVATE atrac9 atrac9_vibration ffmpeg libc libkernel)
|
||||
configure_windows_unwind(libSceAjm.native)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/AjmLibrary.cmake)
|
||||
add_sce_ajm_library(libSceAjm.native)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(atrac9_gradient_tests tests/Atrac9Gradient.c ${LIBATRAC9_DIR}/bit_reader.c)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../libSceAjm.native/AjmLibrary.cmake)
|
||||
add_sce_ajm_library(libSceAjm ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp)
|
||||
@@ -5,6 +5,19 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
APS5_DUMMY_FUN
|
||||
int APS5_VABI sceAjmBatchJobControlBufferRa() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobRunSplitBufferRa() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchStartBuffer() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceGameCustomDataDialogGetResult() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameCustomDataDialogInitialize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameCustomDataDialogOpen() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameCustomDataDialogTerminate() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameCustomDataDialogUpdateStatus() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceInvitationDialogGetResult() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceInvitationDialogInitialize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceInvitationDialogOpen() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceInvitationDialogTerminate() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceInvitationDialogUpdateStatus() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,6 +115,11 @@ int APS5_VABI sceNpCloseSema(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpCmpNpId(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpCmpNpIdInOrder(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
|
||||
@@ -981,3 +981,52 @@ int APS5_VABI sceNpCheckCallbackForLib() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpInGameMessageCreateHandle() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpInGameMessageDeleteHandle() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpInGameMessageInitialize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpInGameMessagePrepare() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpInGameMessageSendData() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpInGameMessageTerminate() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookCreateRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookDeleteRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookGetAccessToken() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpMatching2ContextStart() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2ContextStop() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2CreateContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2CreateJoinRoom() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2DestroyContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2GetRoomDataExternalList() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2GetRoomDataInternal() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2GetRoomMemberDataInternal() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2GetServerId() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2GetUserInfoList() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2GetWorldInfoList() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2Initialize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2JoinRoom() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2KickoutRoomMember() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2LeaveRoom() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2RegisterContextCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2RegisterRoomEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2RegisterRoomMessageCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2RegisterSignalingCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SearchRoom() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SendRoomChatMessage() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SendRoomMessage() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SetDefaultRequestOptParam() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SetRoomDataExternal() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SetRoomDataInternal() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SetRoomMemberDataInternal() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SetUserInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2SignalingGetConnectionInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpMatching2Terminate() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpScoreAbortRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreCreateNpTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreCreateRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreDeleteNpTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreDeleteRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetBoardInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetFriendsRanking() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetFriendsRankingAsync() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetRankingByNpId() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetRankingByNpIdAsync() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetRankingByRange() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreGetRankingByRangeAsync() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScorePollAsync() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreRecordScore() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpScoreRecordScoreAsync() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpSignalingActivateConnection() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingCreateContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingDeleteContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingGetConnectionInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingInitialize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingSetContextOption() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingTerminate() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSignalingTerminateConnection() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,4 +15,29 @@ int APS5_VABI sceNpSnsFacebookDialogGetStatus(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookDialogGetResult() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookDialogInitialize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookDialogOpen() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookDialogTerminate() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpSnsFacebookDialogUpdateStatus() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
add_library(libSceNpTrophy SHARED EXCLUDE_FROM_ALL Export.cpp)
|
||||
target_include_directories(libSceNpTrophy PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(libSceNpTrophy)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_np_trophy_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpTrophy.cpp)
|
||||
target_include_directories(guest_np_trophy_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_np_trophy_tests PRIVATE libSceNpTrophy)
|
||||
configure_windows_unwind(guest_np_trophy_tests)
|
||||
add_test(NAME guest_np_trophy COMMAND guest_np_trophy_tests)
|
||||
set_tests_properties(guest_np_trophy PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_np_trophy PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpTrophy>")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,268 @@
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int NP_TROPHY_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80551604u);
|
||||
constexpr int NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = static_cast<int>(0x80551605u);
|
||||
constexpr int NP_TROPHY_ERROR_INVALID_HANDLE = static_cast<int>(0x80551608u);
|
||||
constexpr int NP_TROPHY_ERROR_INVALID_CONTEXT = static_cast<int>(0x80551609u);
|
||||
constexpr int NP_TROPHY_ERROR_INVALID_TROPHY_ID = static_cast<int>(0x8055160au);
|
||||
constexpr int NP_TROPHY_ERROR_INVALID_GROUP_ID = static_cast<int>(0x8055160bu);
|
||||
constexpr int NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = static_cast<int>(0x8055160cu);
|
||||
constexpr int NP_TROPHY_ERROR_NOT_REGISTERED = static_cast<int>(0x8055160fu);
|
||||
constexpr int NP_TROPHY_ERROR_ALREADY_REGISTERED = static_cast<int>(0x80551610u);
|
||||
constexpr int NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = static_cast<int>(0x80551613u);
|
||||
constexpr int NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = static_cast<int>(0x80551614u);
|
||||
constexpr int NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = static_cast<int>(0x80551616u);
|
||||
constexpr int NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = static_cast<int>(0x8055161au);
|
||||
constexpr int NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = static_cast<int>(0x80551626u);
|
||||
constexpr int NP_TROPHY_INVALID_ID = -1;
|
||||
constexpr int NP_TROPHY_NUM_MAX = 128;
|
||||
constexpr std::uint32_t TrpMagic = 0xdca24d00u;
|
||||
constexpr std::size_t TrpHeaderBytes = 0x60;
|
||||
constexpr std::size_t TrpEntryBytes = 0x40;
|
||||
|
||||
struct TrophyFlagArray {
|
||||
std::uint32_t bits[NP_TROPHY_NUM_MAX / 32];
|
||||
};
|
||||
|
||||
struct TrpEntry {
|
||||
std::uint64_t offset;
|
||||
std::uint64_t size;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
int userId;
|
||||
std::uint32_t serviceLabel;
|
||||
bool registered = false;
|
||||
std::filesystem::path package;
|
||||
std::map<std::string, TrpEntry> entries;
|
||||
int trophies = 0;
|
||||
std::filesystem::path state;
|
||||
std::set<int> unlocked;
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::map<int, Context> g_contexts;
|
||||
std::set<int> g_handles;
|
||||
int g_nextContext = 1;
|
||||
int g_nextHandle = 1;
|
||||
|
||||
std::uint32_t BigEndian32(const std::uint8_t* bytes) {
|
||||
return (std::uint32_t{bytes[0]} << 24) | (std::uint32_t{bytes[1]} << 16) | (std::uint32_t{bytes[2]} << 8) | bytes[3];
|
||||
}
|
||||
|
||||
std::uint64_t BigEndian64(const std::uint8_t* bytes) {
|
||||
return (std::uint64_t{BigEndian32(bytes)} << 32) | BigEndian32(bytes + 4);
|
||||
}
|
||||
|
||||
std::optional<std::map<std::string, TrpEntry>> ReadPackage(const std::filesystem::path& path) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file) return std::nullopt;
|
||||
std::array<std::uint8_t, TrpHeaderBytes> header{};
|
||||
if (!file.read(reinterpret_cast<char*>(header.data()), header.size()) || BigEndian32(header.data()) != TrpMagic) return std::map<std::string, TrpEntry>{};
|
||||
const auto count = BigEndian32(header.data() + 0x10);
|
||||
if (BigEndian32(header.data() + 0x14) != TrpEntryBytes) return std::map<std::string, TrpEntry>{};
|
||||
std::map<std::string, TrpEntry> entries;
|
||||
for (std::uint32_t i = 0; i < count; ++i) {
|
||||
std::array<std::uint8_t, TrpEntryBytes> entry{};
|
||||
if (!file.read(reinterpret_cast<char*>(entry.data()), entry.size())) return std::map<std::string, TrpEntry>{};
|
||||
const std::string name(reinterpret_cast<const char*>(entry.data()), strnlen(reinterpret_cast<const char*>(entry.data()), 32));
|
||||
entries[name] = {BigEndian64(entry.data() + 32), BigEndian64(entry.data() + 40)};
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
void LoadState(Context& context) {
|
||||
std::ifstream file(context.state);
|
||||
int id = 0;
|
||||
while (file >> id) {
|
||||
if (id >= 0 && id < context.trophies) context.unlocked.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveState(const Context& context) {
|
||||
std::filesystem::create_directories(context.state.parent_path());
|
||||
const auto temporary = context.state.string() + ".tmp";
|
||||
{
|
||||
std::ofstream file(temporary, std::ios::trunc);
|
||||
for (const auto id : context.unlocked) file << id << '\n';
|
||||
}
|
||||
std::filesystem::rename(temporary, context.state);
|
||||
}
|
||||
|
||||
int Lookup(int contextId, int handle, Context*& context) {
|
||||
if (contextId == NP_TROPHY_INVALID_ID) return NP_TROPHY_ERROR_INVALID_CONTEXT;
|
||||
if (handle == NP_TROPHY_INVALID_ID) return NP_TROPHY_ERROR_INVALID_HANDLE;
|
||||
const auto found = g_contexts.find(contextId);
|
||||
if (found == g_contexts.end()) return NP_TROPHY_ERROR_INVALID_CONTEXT;
|
||||
if (!g_handles.contains(handle)) return NP_TROPHY_ERROR_INVALID_HANDLE;
|
||||
context = &found->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CopyIcon(const Context& context, const std::string& name, void* buffer, std::uint64_t* size) {
|
||||
const auto entry = context.entries.find(name);
|
||||
if (entry == context.entries.end()) return NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND;
|
||||
if (buffer == nullptr) {
|
||||
*size = entry->second.size;
|
||||
return 0;
|
||||
}
|
||||
if (*size < entry->second.size) return NP_TROPHY_ERROR_INSUFFICIENT_BUFFER;
|
||||
std::ifstream file(context.package, std::ios::binary);
|
||||
file.seekg(static_cast<std::streamoff>(entry->second.offset));
|
||||
if (!file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(entry->second.size))) return NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT;
|
||||
*size = entry->second.size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpTrophyCreateContext(int* context, int userId, std::uint32_t serviceLabel, std::uint64_t options) {
|
||||
if (!context || options != 0) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
for (const auto& [id, existing] : g_contexts) {
|
||||
if (existing.userId == userId && existing.serviceLabel == serviceLabel) return NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS;
|
||||
}
|
||||
const int id = g_nextContext++;
|
||||
g_contexts[id] = Context{userId, serviceLabel};
|
||||
*context = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyDestroyContext(int context) {
|
||||
std::lock_guard lock(g_mutex);
|
||||
return g_contexts.erase(context) == 1 ? 0 : NP_TROPHY_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyCreateHandle(int* handle) {
|
||||
if (!handle) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
const int id = g_nextHandle++;
|
||||
g_handles.insert(id);
|
||||
*handle = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyDestroyHandle(int handle) {
|
||||
std::lock_guard lock(g_mutex);
|
||||
return g_handles.erase(handle) == 1 ? 0 : NP_TROPHY_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyRegisterContext(int contextId, int handle, std::uint64_t options) {
|
||||
if (options != 0) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
Context* context = nullptr;
|
||||
if (const int error = Lookup(contextId, handle, context)) return error;
|
||||
if (context->registered) return NP_TROPHY_ERROR_ALREADY_REGISTERED;
|
||||
char name[32];
|
||||
std::snprintf(name, sizeof(name), "trophy%02u.trp", context->serviceLabel);
|
||||
context->package = ResolvePath_nid_no_patch((std::string("/app0/sce_sys/trophy/") + name).c_str());
|
||||
const auto entries = ReadPackage(context->package);
|
||||
if (!entries) return NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED;
|
||||
if (entries->empty()) return NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT;
|
||||
context->entries = *entries;
|
||||
for (int id = 0; id < NP_TROPHY_NUM_MAX; ++id) {
|
||||
char icon[16];
|
||||
std::snprintf(icon, sizeof(icon), "TROP%03d.PNG", id);
|
||||
if (!context->entries.contains(icon)) break;
|
||||
context->trophies = id + 1;
|
||||
}
|
||||
std::snprintf(name, sizeof(name), "trophy%02u.%d", context->serviceLabel, context->userId);
|
||||
context->state = std::filesystem::path("_trophy") / name;
|
||||
LoadState(*context);
|
||||
context->registered = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetTrophyUnlockState(int contextId, int handle, TrophyFlagArray* flags, std::uint32_t* count) {
|
||||
if (!flags || !count) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
Context* context = nullptr;
|
||||
if (const int error = Lookup(contextId, handle, context)) return error;
|
||||
if (!context->registered) return NP_TROPHY_ERROR_NOT_REGISTERED;
|
||||
std::memset(flags, 0, sizeof(*flags));
|
||||
for (const auto id : context->unlocked) flags->bits[id >> 5] |= 1u << (id & 31);
|
||||
*count = static_cast<std::uint32_t>(context->trophies);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyUnlockTrophy(int contextId, int handle, int trophyId, int* platinumId) {
|
||||
if (!platinumId) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
Context* context = nullptr;
|
||||
if (const int error = Lookup(contextId, handle, context)) return error;
|
||||
if (!context->registered) return NP_TROPHY_ERROR_NOT_REGISTERED;
|
||||
if (trophyId < 0 || trophyId >= context->trophies) return NP_TROPHY_ERROR_INVALID_TROPHY_ID;
|
||||
if (context->unlocked.contains(trophyId)) return NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
|
||||
context->unlocked.insert(trophyId);
|
||||
SaveState(*context);
|
||||
*platinumId = NP_TROPHY_INVALID_ID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetGameIcon(int contextId, int handle, void* buffer, std::uint64_t* size) {
|
||||
if (!size) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
Context* context = nullptr;
|
||||
if (const int error = Lookup(contextId, handle, context)) return error;
|
||||
if (!context->registered) return NP_TROPHY_ERROR_NOT_REGISTERED;
|
||||
return CopyIcon(*context, "ICON0.PNG", buffer, size);
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetGroupIcon(int contextId, int handle, int groupId, void* buffer, std::uint64_t* size) {
|
||||
if (!size) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
Context* context = nullptr;
|
||||
if (const int error = Lookup(contextId, handle, context)) return error;
|
||||
if (!context->registered) return NP_TROPHY_ERROR_NOT_REGISTERED;
|
||||
if (groupId < 0) return NP_TROPHY_ERROR_INVALID_GROUP_ID;
|
||||
char icon[16];
|
||||
std::snprintf(icon, sizeof(icon), "GR%03d.PNG", groupId);
|
||||
return CopyIcon(*context, icon, buffer, size);
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetTrophyIcon(int contextId, int handle, int trophyId, void* buffer, std::uint64_t* size) {
|
||||
if (!size) return NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(g_mutex);
|
||||
Context* context = nullptr;
|
||||
if (const int error = Lookup(contextId, handle, context)) return error;
|
||||
if (!context->registered) return NP_TROPHY_ERROR_NOT_REGISTERED;
|
||||
if (trophyId < 0 || trophyId >= context->trophies) return NP_TROPHY_ERROR_INVALID_TROPHY_ID;
|
||||
if (!context->unlocked.contains(trophyId)) return NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED;
|
||||
char icon[16];
|
||||
std::snprintf(icon, sizeof(icon), "TROP%03d.PNG", trophyId);
|
||||
return CopyIcon(*context, icon, buffer, size);
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetGameInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetGroupInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTrophyGetTrophyInfo() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -782,4 +782,54 @@ int APS5_VABI sceNpTusTryAndSetVariableForCrossSaveVUserAsync(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTssGetData() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusCreateNpTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusGetData() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusGetDataVUser() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusGetMultiSlotVariable() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusGetMultiSlotVariableVUser() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusSetData() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusSetDataVUser() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusSetMultiSlotVariable() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpTusSetMultiSlotVariableVUser() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,3 +38,82 @@ int APS5_VABI sceNpBandwidthTestShutdown(int context_id, void* result) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpBandwidthTestInitStart() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpLookupCreateRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpLookupCreateTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpLookupDeleteRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpLookupDeleteTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpLookupNpId() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterAbortRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterCensorComment() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterCreateAsyncRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterCreateRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterCreateTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterDeleteRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterDeleteTitleCtx() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterPollAsync() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWordFilterSanitizeComment() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
add_library(libSceNpWebApi SHARED EXCLUDE_FROM_ALL Export.cpp)
|
||||
target_include_directories(libSceNpWebApi PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(libSceNpWebApi)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_np_web_api_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpWebApi.cpp)
|
||||
target_include_directories(guest_np_web_api_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_np_web_api_tests PRIVATE libSceNpWebApi)
|
||||
configure_windows_unwind(guest_np_web_api_tests)
|
||||
add_test(NAME guest_np_web_api COMMAND guest_np_web_api_tests)
|
||||
set_tests_properties(guest_np_web_api PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_np_web_api PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpWebApi>")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,181 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80552902);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND = static_cast<int>(0x80552904);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = static_cast<int>(0x8055290B);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND = static_cast<int>(0x8055290D);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND = static_cast<int>(0x8055290E);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND = static_cast<int>(0x80552910);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX = static_cast<int>(0x8055291A);
|
||||
|
||||
namespace {
|
||||
|
||||
struct LibraryContext {
|
||||
int libHttpCtxId;
|
||||
std::size_t poolSize;
|
||||
std::unordered_set<int> handles;
|
||||
std::unordered_set<int> pushEventFilters;
|
||||
};
|
||||
|
||||
std::mutex contextsMutex;
|
||||
std::unordered_map<int, LibraryContext> contexts;
|
||||
int lastContext = 0;
|
||||
int lastObject = 0;
|
||||
|
||||
int nextObject() {
|
||||
lastObject = lastObject % 0xefffffff + 1;
|
||||
return lastObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpWebApiAbortRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateHandle(int libCtxId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
const auto handle = nextObject();
|
||||
context->second.handles.insert(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreatePushEventFilter(int libCtxId, const void* filterParam, std::size_t filterParamNum) {
|
||||
if (!filterParam || filterParamNum == 0) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
const auto filter = nextObject();
|
||||
context->second.pushEventFilters.insert(filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateServicePushEventFilter(int libCtxId, int handleId, const char* npServiceName, std::uint32_t npServiceLabel, const void* filterParam, std::size_t filterParamNum) {
|
||||
(void)npServiceLabel;
|
||||
if (!npServiceName || !filterParam || filterParamNum == 0) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
if (!context->second.handles.contains(handleId)) return SCE_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND;
|
||||
return SCE_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeleteContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeleteHandle(int libCtxId, int handleId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
return context->second.handles.erase(handleId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeletePushEventFilter(int libCtxId, int filterId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
return context->second.pushEventFilters.erase(filterId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeleteRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeleteServicePushEventFilter(int libCtxId, int filterId) {
|
||||
(void)filterId;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
if (!contexts.contains(libCtxId)) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
return SCE_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiGetHttpResponseHeaderValue() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiGetHttpResponseHeaderValueLength() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiGetHttpStatusCode() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiInitialize(int libHttpCtxId, std::size_t poolSize) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
for (int attempt = 0; attempt < 0x7fff; ++attempt) {
|
||||
lastContext = lastContext % 0x7fff + 1;
|
||||
if (contexts.contains(lastContext)) continue;
|
||||
contexts[lastContext] = {libHttpCtxId, poolSize, {}, {}};
|
||||
return lastContext;
|
||||
}
|
||||
return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiReadData() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiRegisterPushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiRegisterServicePushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiSendRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiTerminate(int libCtxId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
return contexts.erase(libCtxId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiUnregisterPushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiUnregisterServicePushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiUtilityParseNpId() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1031,3 +1031,77 @@ int APS5_VABI sceSaveDataDebugTarget() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct SaveDataMountPs4 {
|
||||
std::int32_t user_id;
|
||||
std::int32_t pad;
|
||||
const void* title_id;
|
||||
const SceSaveDataDirName* dir_name;
|
||||
const void* fingerprint;
|
||||
std::uint64_t blocks;
|
||||
std::uint32_t mount_mode;
|
||||
std::uint8_t reserved[32];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceSaveDataInitialize(const void* init) {
|
||||
return sceSaveDataInitialize3(init);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataMount(const SaveDataMountPs4* mount, SaveDataMountResult* mount_result) {
|
||||
if (mount == nullptr || mount->dir_name == nullptr) return SAVE_DATA_ERROR_PARAMETER;
|
||||
if (mount->title_id != nullptr) NotImplemented_nid_no_patch("sceSaveDataMount of another title's save data");
|
||||
SaveDataMount3 converted{};
|
||||
converted.user_id = mount->user_id;
|
||||
converted.dir_name = mount->dir_name;
|
||||
converted.blocks = mount->blocks;
|
||||
converted.mount_mode = mount->mount_mode;
|
||||
return sceSaveDataMount3(&converted, mount_result);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataUmount(const SaveDataMountPoint* mount_point) {
|
||||
return sceSaveDataUmount2(0, mount_point);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSetupSaveDataMemory(std::int32_t user_id, std::size_t memory_size, const SaveDataParam* param) {
|
||||
SaveDataMemorySetup2 setup{};
|
||||
setup.user_id = user_id;
|
||||
setup.memory_size = memory_size;
|
||||
SaveDataMemorySetupResult result{};
|
||||
if (const int rc = sceSaveDataSetupSaveDataMemory2(&setup, &result); rc != SAVE_DATA_OK) return rc;
|
||||
if (param == nullptr) return SAVE_DATA_OK;
|
||||
SaveDataMemorySet2 set{};
|
||||
set.user_id = user_id;
|
||||
set.param = param;
|
||||
return sceSaveDataSetSaveDataMemory2(&set);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataGetSaveDataMemory(std::int32_t user_id, void* buf, std::size_t buf_size, std::int64_t offset) {
|
||||
SaveDataMemoryData data{};
|
||||
data.buf = buf;
|
||||
data.buf_size = buf_size;
|
||||
data.offset = static_cast<std::size_t>(offset);
|
||||
SaveDataMemoryGet2 get{};
|
||||
get.user_id = user_id;
|
||||
get.data = &data;
|
||||
return sceSaveDataGetSaveDataMemory2(&get);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSaveDataSetSaveDataMemory(std::int32_t user_id, void* buf, std::size_t buf_size, std::int64_t offset) {
|
||||
SaveDataMemoryData data{};
|
||||
data.buf = buf;
|
||||
data.buf_size = buf_size;
|
||||
data.offset = static_cast<std::size_t>(offset);
|
||||
SaveDataMemorySet2 set{};
|
||||
set.user_id = user_id;
|
||||
set.data = &data;
|
||||
set.data_num = 1;
|
||||
return sceSaveDataSetSaveDataMemory2(&set);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,3 +133,17 @@ int APS5_VABI sceKernelGetProcessAbiType() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI __Ux86_64_setcontext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sigreturn() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceNpTrophyCreateContext(int* context, int userId, std::uint32_t serviceLabel, std::uint64_t options);
|
||||
int APS5_VABI sceNpTrophyDestroyContext(int context);
|
||||
int APS5_VABI sceNpTrophyCreateHandle(int* handle);
|
||||
int APS5_VABI sceNpTrophyDestroyHandle(int handle);
|
||||
int APS5_VABI sceNpTrophyRegisterContext(int context, int handle, std::uint64_t options);
|
||||
int APS5_VABI sceNpTrophyGetTrophyUnlockState(int context, int handle, std::uint32_t* flags, std::uint32_t* count);
|
||||
int APS5_VABI sceNpTrophyUnlockTrophy(int context, int handle, int trophyId, int* platinumId);
|
||||
int APS5_VABI sceNpTrophyGetGameIcon(int context, int handle, void* buffer, std::uint64_t* size);
|
||||
int APS5_VABI sceNpTrophyGetTrophyIcon(int context, int handle, int trophyId, void* buffer, std::uint64_t* size);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int InvalidArgument = static_cast<int>(0x80551604u);
|
||||
constexpr int InsufficientBuffer = static_cast<int>(0x80551605u);
|
||||
constexpr int InvalidContext = static_cast<int>(0x80551609u);
|
||||
constexpr int InvalidTrophyId = static_cast<int>(0x8055160au);
|
||||
constexpr int AlreadyUnlocked = static_cast<int>(0x8055160cu);
|
||||
constexpr int NotRegistered = static_cast<int>(0x8055160fu);
|
||||
constexpr int AlreadyRegistered = static_cast<int>(0x80551610u);
|
||||
constexpr int ContextAlreadyExists = static_cast<int>(0x80551613u);
|
||||
constexpr int TitleConfNotInstalled = static_cast<int>(0x80551626u);
|
||||
constexpr int TrophyNotUnlocked = static_cast<int>(0x8055161au);
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (!condition) {
|
||||
std::fprintf(stderr, "NpTrophy: %s\n", message);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void PutBigEndian(std::vector<std::uint8_t>& bytes, std::size_t at, std::uint64_t value, int width) {
|
||||
for (int i = 0; i < width; ++i) bytes[at + i] = static_cast<std::uint8_t>(value >> (8 * (width - 1 - i)));
|
||||
}
|
||||
|
||||
void WritePackage(const std::filesystem::path& path, const std::vector<std::pair<std::string, std::string>>& files) {
|
||||
const std::size_t header = 0x60, entry = 0x40;
|
||||
std::vector<std::uint8_t> bytes(header + entry * files.size());
|
||||
PutBigEndian(bytes, 0, 0xdca24d00u, 4);
|
||||
PutBigEndian(bytes, 0x10, files.size(), 4);
|
||||
PutBigEndian(bytes, 0x14, entry, 4);
|
||||
for (std::size_t i = 0; i < files.size(); ++i) {
|
||||
const auto at = header + entry * i;
|
||||
std::memcpy(bytes.data() + at, files[i].first.data(), files[i].first.size());
|
||||
PutBigEndian(bytes, at + 32, bytes.size(), 8);
|
||||
PutBigEndian(bytes, at + 40, files[i].second.size(), 8);
|
||||
bytes.insert(bytes.end(), files[i].second.begin(), files[i].second.end());
|
||||
}
|
||||
std::filesystem::create_directories(path.parent_path());
|
||||
std::ofstream(path, std::ios::binary).write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
const auto root = std::filesystem::temp_directory_path() / ("anyps5-np-trophy-" + std::to_string(std::random_device{}()));
|
||||
std::filesystem::remove_all(root);
|
||||
WritePackage(root / "app0/sce_sys/trophy/trophy00.trp", {{"TROPCONF.ESFM", "conf"}, {"ICON0.PNG", "game-icon"}, {"TROP000.PNG", "first"}, {"TROP001.PNG", "second"}});
|
||||
std::filesystem::current_path(root);
|
||||
|
||||
int context = 0, handle = 0, missing = 0, platinum = 0;
|
||||
Require(sceNpTrophyCreateContext(nullptr, 1, 0, 0) == InvalidArgument, "null context");
|
||||
Require(sceNpTrophyCreateContext(&context, 1, 0, 1) == InvalidArgument, "context options");
|
||||
Require(sceNpTrophyCreateContext(&context, 1, 0, 0) == 0, "create the context");
|
||||
Require(sceNpTrophyCreateContext(&missing, 1, 0, 0) == ContextAlreadyExists, "second context for the same user and label");
|
||||
Require(sceNpTrophyCreateHandle(&handle) == 0, "create the handle");
|
||||
|
||||
std::uint32_t flags[4] = {}, count = 0;
|
||||
Require(sceNpTrophyGetTrophyUnlockState(context, handle, flags, &count) == NotRegistered, "state before registration");
|
||||
Require(sceNpTrophyRegisterContext(context, handle, 0) == 0, "register the context");
|
||||
Require(sceNpTrophyRegisterContext(context, handle, 0) == AlreadyRegistered, "register twice");
|
||||
Require(sceNpTrophyGetTrophyUnlockState(context, handle, flags, &count) == 0 && count == 2 && flags[0] == 0, "two trophies, none unlocked");
|
||||
|
||||
std::uint64_t size = 0;
|
||||
Require(sceNpTrophyGetGameIcon(context, handle, nullptr, &size) == 0 && size == 9, "game icon size");
|
||||
char icon[16] = {};
|
||||
size = 4;
|
||||
Require(sceNpTrophyGetGameIcon(context, handle, icon, &size) == InsufficientBuffer, "game icon into a short buffer");
|
||||
size = sizeof(icon);
|
||||
Require(sceNpTrophyGetGameIcon(context, handle, icon, &size) == 0 && size == 9 && std::memcmp(icon, "game-icon", 9) == 0, "game icon bytes");
|
||||
|
||||
size = sizeof(icon);
|
||||
Require(sceNpTrophyGetTrophyIcon(context, handle, 1, icon, &size) == TrophyNotUnlocked, "icon of a locked trophy");
|
||||
Require(sceNpTrophyUnlockTrophy(context, handle, 2, &platinum) == InvalidTrophyId, "unlock a trophy past the count");
|
||||
Require(sceNpTrophyUnlockTrophy(context, handle, 1, &platinum) == 0 && platinum == -1, "unlock trophy 1");
|
||||
Require(sceNpTrophyUnlockTrophy(context, handle, 1, &platinum) == AlreadyUnlocked, "unlock trophy 1 twice");
|
||||
Require(sceNpTrophyGetTrophyUnlockState(context, handle, flags, &count) == 0 && flags[0] == 2u, "trophy 1 unlocked");
|
||||
size = sizeof(icon);
|
||||
Require(sceNpTrophyGetTrophyIcon(context, handle, 1, icon, &size) == 0 && size == 6 && std::memcmp(icon, "second", 6) == 0, "icon of the unlocked trophy");
|
||||
int saved = -1;
|
||||
{
|
||||
std::ifstream state(root / "_trophy/trophy00.1");
|
||||
state >> saved;
|
||||
}
|
||||
Require(saved == 1, "unlock state saved for the user");
|
||||
|
||||
Require(sceNpTrophyCreateContext(&missing, 1, 1, 0) == 0, "context for a label without a package");
|
||||
Require(sceNpTrophyRegisterContext(missing, handle, 0) == TitleConfNotInstalled, "register without a trophy package");
|
||||
Require(sceNpTrophyDestroyContext(missing) == 0, "destroy the second context");
|
||||
Require(sceNpTrophyDestroyHandle(handle) == 0 && sceNpTrophyDestroyContext(context) == 0, "destroy the handle and context");
|
||||
Require(sceNpTrophyDestroyContext(context) == InvalidContext, "destroy the context twice");
|
||||
|
||||
std::filesystem::current_path(root.parent_path());
|
||||
std::error_code error;
|
||||
std::filesystem::remove_all(root, error);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceNpWebApiInitialize(int libHttpCtxId, std::size_t poolSize);
|
||||
int APS5_VABI sceNpWebApiTerminate(int libCtxId);
|
||||
int APS5_VABI sceNpWebApiCreateHandle(int libCtxId);
|
||||
int APS5_VABI sceNpWebApiDeleteHandle(int libCtxId, int handleId);
|
||||
int APS5_VABI sceNpWebApiCreatePushEventFilter(int libCtxId, const void* filterParam, std::size_t filterParamNum);
|
||||
int APS5_VABI sceNpWebApiDeletePushEventFilter(int libCtxId, int filterId);
|
||||
int APS5_VABI sceNpWebApiCreateServicePushEventFilter(int libCtxId, int handleId, const char* npServiceName, unsigned npServiceLabel, const void* filterParam, std::size_t filterParamNum);
|
||||
int APS5_VABI sceNpWebApiDeleteServicePushEventFilter(int libCtxId, int filterId);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int InvalidArgument = static_cast<int>(0x80552902u);
|
||||
constexpr int LibContextNotFound = static_cast<int>(0x80552904u);
|
||||
constexpr int PushEventFilterNotFound = static_cast<int>(0x8055290Bu);
|
||||
constexpr int HandleNotFound = static_cast<int>(0x8055290Du);
|
||||
constexpr int ServicePushEventFilterNotFound = static_cast<int>(0x8055290Eu);
|
||||
constexpr int SignedInUserNotFound = static_cast<int>(0x80552910u);
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (!condition) {
|
||||
std::fprintf(stderr, "NpWebApi: %s\n", message);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
const int context = sceNpWebApiInitialize(1, 0x10000);
|
||||
Require(context > 0 && context <= 0x7fff, "library context id out of range");
|
||||
const int other = sceNpWebApiInitialize(1, 0x10000);
|
||||
Require(other > 0 && other != context, "a second library context must get its own id");
|
||||
Require(sceNpWebApiCreateHandle(0x7fff + 1) == LibContextNotFound, "handle of an unknown context");
|
||||
|
||||
const int handle = sceNpWebApiCreateHandle(context);
|
||||
Require(handle > 0 && static_cast<unsigned>(handle) < 0xf0000000u, "handle id out of range");
|
||||
const char filterParam[16] = {};
|
||||
Require(sceNpWebApiCreatePushEventFilter(context, nullptr, 1) == InvalidArgument, "push event filter without its parameter");
|
||||
Require(sceNpWebApiCreatePushEventFilter(context, filterParam, 0) == InvalidArgument, "push event filter with no parameters");
|
||||
const int filter = sceNpWebApiCreatePushEventFilter(context, filterParam, 1);
|
||||
Require(filter > 0 && filter != handle, "push event filter id");
|
||||
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(context, handle, nullptr, 0, filterParam, 1) == InvalidArgument, "service filter without a service name");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(0x7fff + 1, handle, "service", 0, filterParam, 1) == LibContextNotFound, "service filter of an unknown context");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(other, handle, "service", 0, filterParam, 1) == HandleNotFound, "service filter with another context's handle");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(context, handle, "service", 0, filterParam, 1) == SignedInUserNotFound, "service filter without a signed-in user");
|
||||
Require(sceNpWebApiDeleteServicePushEventFilter(context, 1) == ServicePushEventFilterNotFound, "no service filter exists");
|
||||
|
||||
Require(sceNpWebApiDeletePushEventFilter(other, filter) == PushEventFilterNotFound, "filter deleted through another context");
|
||||
Require(sceNpWebApiDeletePushEventFilter(context, filter) == 0, "delete the push event filter");
|
||||
Require(sceNpWebApiDeletePushEventFilter(context, filter) == PushEventFilterNotFound, "push event filter deleted twice");
|
||||
Require(sceNpWebApiDeleteHandle(context, handle) == 0, "delete the handle");
|
||||
Require(sceNpWebApiDeleteHandle(context, handle) == HandleNotFound, "handle deleted twice");
|
||||
|
||||
Require(sceNpWebApiTerminate(context) == 0, "terminate the library context");
|
||||
Require(sceNpWebApiTerminate(context) == LibContextNotFound, "library context terminated twice");
|
||||
Require(sceNpWebApiCreateHandle(context) == LibContextNotFound, "handle of a terminated context");
|
||||
Require(sceNpWebApiTerminate(other) == 0, "terminate the second library context");
|
||||
return 0;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -10,10 +11,16 @@
|
||||
extern "C" int APS5_VABI sceSaveDataTransferringMountPs4(const SaveDataTransferringMount*, SaveDataMountResult*);
|
||||
extern "C" int APS5_VABI sceSaveDataDirNameSearchPs4(const SaveDataDirNameSearchCond*, SaveDataDirNameSearchResult*);
|
||||
extern "C" int APS5_VABI sceSaveDataDirNameSearch(const SaveDataDirNameSearchCond*, SaveDataDirNameSearchResult*);
|
||||
extern "C" int APS5_VABI sceSaveDataInitialize(const void*);
|
||||
extern "C" int APS5_VABI sceSaveDataTerminate();
|
||||
extern "C" int APS5_VABI sceSaveDataSetupSaveDataMemory(std::int32_t, std::size_t, const SaveDataParam*);
|
||||
extern "C" int APS5_VABI sceSaveDataSetSaveDataMemory(std::int32_t, void*, std::size_t, std::int64_t);
|
||||
extern "C" int APS5_VABI sceSaveDataGetSaveDataMemory(std::int32_t, void*, std::size_t, std::int64_t);
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int SaveDataErrorNotFound = -2137063416;
|
||||
constexpr int SaveDataErrorNotInitialized = -2137063423;
|
||||
|
||||
int failures = 0;
|
||||
|
||||
@@ -73,6 +80,22 @@ void TestDirNameSearchPs4() {
|
||||
Check(ThrowsRuntimeError([&] { sceSaveDataDirNameSearchPs4(&cond, nullptr); }), "null result throws");
|
||||
}
|
||||
|
||||
void TestSaveDataMemoryV1() {
|
||||
Check(sceSaveDataSetupSaveDataMemory(7, 0x1000, nullptr) == SaveDataErrorNotInitialized, "v1 SetupSaveDataMemory before v1 Initialize");
|
||||
Check(sceSaveDataInitialize(nullptr) == 0, "v1 Initialize");
|
||||
Check(sceSaveDataSetupSaveDataMemory(7, 0x1000, nullptr) == 0, "v1 SetupSaveDataMemory");
|
||||
char written[16] = "settings-v1";
|
||||
Check(sceSaveDataSetSaveDataMemory(7, written, sizeof(written), 0x100) == 0, "v1 SetSaveDataMemory at an offset");
|
||||
char read[16] = {};
|
||||
Check(sceSaveDataGetSaveDataMemory(7, read, sizeof(read), 0x100) == 0, "v1 GetSaveDataMemory");
|
||||
Check(std::memcmp(read, written, sizeof(read)) == 0, "v1 GetSaveDataMemory returns the bytes v1 set");
|
||||
std::ifstream slot("_sd_mem/u7/slot0.bin", std::ios::binary);
|
||||
slot.seekg(0x100);
|
||||
char stored[16] = {};
|
||||
Check(static_cast<bool>(slot.read(stored, sizeof(stored))) && std::memcmp(stored, written, sizeof(stored)) == 0, "v1 SetSaveDataMemory stores into slot 0 of the user");
|
||||
Check(sceSaveDataTerminate() == 0, "Terminate after v1 Initialize");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
@@ -83,6 +106,7 @@ int main() {
|
||||
|
||||
TestTransferringMountPs4();
|
||||
TestDirNameSearchPs4();
|
||||
TestSaveDataMemoryV1();
|
||||
|
||||
std::filesystem::current_path(previous);
|
||||
std::error_code error;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# libSceNpTrophy technical debt
|
||||
|
||||
### Unknown function info
|
||||
|
||||
- [sceNpTrophyRegisterContext, sceNpTrophyGetTrophyUnlockState, sceNpTrophyUnlockTrophy](../../../core/libs/prx/libSceNpTrophy/Export.cpp) (libSceNpTrophy, PS4) - the trophy definitions in `TROPCONF.ESFM` are encrypted, so the trophy count is the number of consecutive `TROPnnn.PNG` icons in the title's `/app0/sce_sys/trophy/trophyNN.trp`, and the calls that return names and details (`sceNpTrophyGetGameInfo`, `sceNpTrophyGetGroupInfo`, `sceNpTrophyGetTrophyInfo`) throw. Unlocks are kept per user and service label in `_trophy/trophyNN.<user id>` under the working directory, no platinum trophy is reported (`platinumId` is -1), and a locked trophy's icon fails with `TROPHY_NOT_UNLOCKED`. None of it is checked on a console.
|
||||
@@ -0,0 +1,6 @@
|
||||
# libSceNpWebApi technical debt
|
||||
|
||||
### Unknown function info
|
||||
|
||||
- [sceNpWebApiInitialize, sceNpWebApiTerminate, sceNpWebApiCreateHandle, sceNpWebApiDeleteHandle, sceNpWebApiCreatePushEventFilter, sceNpWebApiDeletePushEventFilter](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - signatures, id ranges (library contexts 1 to 0x7fff, handles and filters below 0xf0000000) and errors (`LIB_CONTEXT_NOT_FOUND`, `HANDLE_NOT_FOUND`, `PUSH_EVENT_FILTER_NOT_FOUND`, `INVALID_ARGUMENT` for a missing filter parameter) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api/np_web_api_internal.cpp); they are not checked on a console. The libHttp context and pool size are kept but not used, since no request is ever sent. Limbo (CUSA01664) creates a context, a handle and filters when its game starts.
|
||||
- [sceNpWebApiCreateServicePushEventFilter](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - a filter for a PSN service needs the signed-in user, and no user is signed in to PSN, so a valid call returns `SIGNED_IN_USER_NOT_FOUND` after the context and handle checks, as shadPS4 does with its network disabled. shadPS4 also rejects an invalid service label for titles built with SDK 2.00 or later; AnyPS5 has no compiled-SDK version for libraries yet, so such a call returns `SIGNED_IN_USER_NOT_FOUND` instead of `INVALID_ARGUMENT`. `sceNpWebApiDeleteServicePushEventFilter` therefore never finds a filter.
|
||||
Reference in new issue
Block a user