mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
Merge pull request #9433 from Get-fix/ps4/limbo-libs
feat: PS4 NpTrophy, save data v1, NpWebApi offline and Limbo's other imports
This commit is contained in:
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