This commit is contained in:
Marcin Mikołajczyk 2026-03-17 17:01:24 +01:00
parent df32a2076b
commit 43bb7c4aa7
8 changed files with 686 additions and 99 deletions

3
.gitmodules vendored
View File

@ -133,3 +133,6 @@
[submodule "externals/openal-soft"]
path = externals/openal-soft
url = https://github.com/shadexternals/openal-soft.git
[submodule "externals/cppcodec"]
path = externals/cppcodec
url = https://github.com/tplgy/cppcodec.git

View File

@ -1137,7 +1137,7 @@ create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml)
target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib Cppcodec)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")

View File

@ -317,3 +317,6 @@ endif()
add_library(Cpp_Httplib INTERFACE)
target_include_directories(Cpp_Httplib INTERFACE cpp-httplib/)
# cppcodec
add_library(Cppcodec INTERFACE)
target_include_directories(Cppcodec INTERFACE cppcodec/)

1
externals/cppcodec vendored Submodule

@ -0,0 +1 @@
Subproject commit 8019b8b580f8573c33c50372baec7039dfe5a8ce

View File

@ -19,6 +19,7 @@ namespace Libraries::Np::NpManager {
static bool g_signed_in = false;
static s32 g_active_requests = 0;
static std::mutex g_request_mutex;
OrbisNpTitleId g_np_title_id = {};
static std::map<std::string, std::function<void()>> g_np_callbacks;
static std::mutex g_np_callbacks_mutex;
@ -686,6 +687,17 @@ sceNpGetUserIdByAccountId(u64 account_id, Libraries::UserService::OrbisUserServi
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpSetNpTitleId(OrbisNpTitleId* title_id, OrbisNpTitleSecret* title_secret) {
if (!title_id || !title_secret) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
LOG_DEBUG(Lib_NpManager, "titleId = {}", title_id->id);
g_np_title_id = *title_id;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpHasSignedUp(Libraries::UserService::OrbisUserServiceUserId user_id,
bool* has_signed_up) {
LOG_DEBUG(Lib_NpManager, "called");
@ -825,6 +837,7 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("XDncXQIJUSk", "libSceNpManager", 1, "libSceNpManager", sceNpGetOnlineId);
LIB_FUNCTION("eQH7nWPcAgc", "libSceNpManager", 1, "libSceNpManager", sceNpGetState);
LIB_FUNCTION("VgYczPGB5ss", "libSceNpManager", 1, "libSceNpManager", sceNpGetUserIdByAccountId);
LIB_FUNCTION("Ec63y59l9tw", "libSceNpManager", 1, "libSceNpManager", sceNpSetNpTitleId);
LIB_FUNCTION("Oad3rvY-NJQ", "libSceNpManager", 1, "libSceNpManager", sceNpHasSignedUp);
LIB_FUNCTION("3Zl8BePTh9Y", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallback);
LIB_FUNCTION("JELHf4xPufo", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallbackForLib);

View File

@ -4,6 +4,7 @@
#pragma once
#include <functional>
#include <string>
#include "common/types.h"
#include "core/libraries/np/np_error.h"
@ -90,6 +91,15 @@ struct OrbisNpCreateAsyncRequestParameter {
u8 padding[4];
};
struct OrbisNpTitleId {
char id[33];
u8 padding[3];
};
struct OrbisNpTitleSecret {
u8 data[128];
};
void RegisterNpCallback(std::string key, std::function<void()> cb);
void DeregisterNpCallback(std::string key);
@ -98,5 +108,7 @@ s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId use
s32 PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId user_id,
OrbisNpOnlineId* online_id);
extern OrbisNpTitleId g_np_title_id;
void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Np::NpManager

View File

@ -1,13 +1,228 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#ifndef NOGDI
#define NOGDI
#endif
#endif
#include <future>
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/np/np_error.h"
#include "core/libraries/np/np_manager.h"
#include "core/libraries/np/np_score.h"
#include "core/libraries/np/np_types.h"
#include "core/libraries/np/object_manager.h"
#include "core/libraries/system/userservice.h"
#include "cppcodec/base64_rfc4648.hpp"
#include "externals/cpp-httplib/httplib.h"
#include "nlohmann/json.hpp"
namespace Libraries::Np::NpScore {
using json = nlohmann::json;
using base64 = cppcodec::base64_rfc4648;
const std::string BASE_URL = "http://127.0.0.1:3000";
int PackReqId(int libCtxId, int reqId) {
return ((libCtxId & 0xFFFF) << 16) | (reqId & 0xFFFF);
}
std::pair<int, int> UnpackReqId(int reqId) {
return {reqId >> 16, reqId & 0xFFFF};
}
bool IsReqId(int id) {
return id > (1 << 16);
}
httplib::Client NewClient() {
httplib::Client client(BASE_URL);
client.set_default_headers({{"X-NP-TITLE-ID", NpManager::g_np_title_id.id}});
client.set_bearer_token_auth("BEARER_TOKEN"); // fix when auth manager
client.set_logger([](const httplib::Request& req, const httplib::Response& res) {
std::cout << "" << req.method << " " << req.path << " -> " << res.status << " ("
<< res.body.size() << ") bytes " << res.body << std::endl;
});
client.set_error_logger([](const httplib::Error& err, const httplib::Request* req) {
std::cerr << "";
if (req) {
std::cerr << req->method << " " << req->path << " ";
}
std::cerr << "failed: " << httplib::to_string(err);
// Add specific guidance based on error type
switch (err) {
case httplib::Error::Connection:
std::cerr << " (verify server is running and reachable)";
break;
case httplib::Error::SSLConnection:
std::cerr << " (check SSL certificate and TLS configuration)";
break;
case httplib::Error::ConnectionTimeout:
std::cerr << " (increase timeout or check network latency)";
break;
case httplib::Error::Read:
std::cerr << " (server may have closed connection prematurely)";
break;
default:
break;
}
std::cerr << std::endl;
});
return client;
}
struct NpScoreRequest {
u32 pcId;
std::future<int> requestFuture;
void GetRankingByRange(OrbisNpScoreBoardId scoreBoardId, OrbisNpScoreRankNumber startRank,
OrbisNpScoreRankDataA* ranks, u64 ranksLen,
OrbisNpScoreGameInfo* gameInfos, OrbisNpScoreComment* comments,
Rtc::OrbisRtcTick* lastUpdate, OrbisNpScoreRankNumber* totalRecords) {
requestFuture = std::async(std::launch::async, [=]() {
httplib::Client client = NewClient();
auto uri = std::format("/score/v1/ranking/{}/by-range?startRank={}&scores={}",
scoreBoardId, startRank, ranksLen);
auto res = client.Get(uri);
if (res && res->status == httplib::StatusCode::OK_200) {
auto json = json::parse(res->body);
auto card = json["card"].get<u32>();
if (totalRecords) {
*totalRecords = card;
}
auto json_ranks = json["ranks"];
auto ranks_obtained = 0;
for (auto r : json_ranks) {
strcpy(ranks[ranks_obtained].onlineId.data, "shadps4");
ranks[ranks_obtained].pcId = r["id"]["pcId"].get<u32>();
// most probably not correct but need more RE to figure out what are the
// different ranks here
ranks[ranks_obtained].rank1 = r["rank"].get<u32>();
ranks[ranks_obtained].rank2 = r["rank"].get<u32>();
ranks[ranks_obtained].rank3 = r["rank"].get<u32>();
ranks[ranks_obtained].hasGameData = false;
ranks[ranks_obtained].score = r["score"].get<s64>();
Libraries::Rtc::sceRtcGetCurrentTick(&ranks[ranks_obtained].recordTime);
ranks[ranks_obtained].accountId = r["id"]["accountId"].get<u64>();
if (gameInfos && r.contains("gameInfo")) {
std::vector<u8> decoded = base64::decode(r["gameInfo"].get<std::string>());
gameInfos[ranks_obtained].dataSize = decoded.size();
if (decoded.size() > sizeof(OrbisNpScoreGameInfo::data)) {
LOG_WARNING(Lib_NpScore, "gameInfo is too long! {} bytes",
decoded.size());
} else {
std::memcpy(gameInfos[ranks_obtained].data, decoded.data(),
decoded.size());
}
}
if (comments && r.contains("comment")) {
LOG_WARNING(Lib_NpScore, "score comment returned but not handled");
}
ranks_obtained++;
}
if (lastUpdate) {
Libraries::Rtc::sceRtcGetCurrentTick(lastUpdate);
}
return ranks_obtained;
}
return -1;
});
}
void RecordScore(OrbisNpScoreBoardId boardId, OrbisNpScoreValue score,
OrbisNpScoreComment* comment, OrbisNpScoreGameInfo* gameInfo) {
requestFuture = std::async(std::launch::async, [=, this]() {
httplib::Client client = NewClient();
auto uri = std::format("/score/v1/ranking/{}", boardId);
json payload;
payload["pcId"] = this->pcId;
payload["value"] = score;
if (comment) {
LOG_DEBUG(Lib_NpScore, "score comment {}", comment->comment);
payload["comment"] = base64::encode(comment->comment);
}
if (gameInfo) {
payload["gameInfo"] = base64::encode(gameInfo->data);
}
auto res = client.Post(uri, payload.dump(), "application/json");
return 0;
});
}
};
using NpScoreRequestManager =
ObjectManager<NpScoreRequest, 32, ORBIS_NP_COMMUNITY_ERROR_INVALID_ID,
ORBIS_NP_COMMUNITY_ERROR_INVALID_ID, ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS>;
struct NpScoreTitleContext {
u32 serviceLabel;
OrbisNpId npId;
u32 pcId;
NpScoreRequestManager requestManager;
s32 GetRequest(int reqId, NpScoreRequest** out) {
NpScoreRequest* req = nullptr;
if (auto ret = requestManager.GetObject(reqId, &req); ret < 0) {
return ret;
}
*out = req;
return ORBIS_OK;
}
s32 DeleteRequest(int reqId) {
return requestManager.DeleteObject(reqId);
}
};
using NpScoreContextManager =
ObjectManager<NpScoreTitleContext, 32, ORBIS_NP_COMMUNITY_ERROR_INVALID_ID,
ORBIS_NP_COMMUNITY_ERROR_INVALID_ID, ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS>;
static NpScoreContextManager ctxManager;
s32 GetRequest(int requestId, NpScoreRequest** out) {
auto [ctxId, reqId] = UnpackReqId(requestId);
NpScoreTitleContext* ctx = nullptr;
if (auto ret = ctxManager.GetObject(ctxId, &ctx); ret < 0) {
return ret;
}
NpScoreRequest* req = nullptr;
if (auto ret = ctx->GetRequest(reqId, &req); ret < 0) {
return ret;
}
*out = req;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreAbortRequest() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
@ -28,19 +243,46 @@ int PS4_SYSV_ABI sceNpScoreChangeModeForOtherSaveDataOwners() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreCreateNpTitleCtx() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreCreateNpTitleCtx(Libraries::Np::OrbisNpServiceLabel serviceLabel,
OrbisNpId* npId) {
if (!npId) {
return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (serviceLabel == ORBIS_NP_INVALID_SERVICE_LABEL) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
LOG_INFO(Lib_NpScore, "serviceLabel = {}, npId->data = {}", serviceLabel, npId->handle.data);
return ctxManager.CreateObject(serviceLabel, *npId);
}
int PS4_SYSV_ABI sceNpScoreCreateNpTitleCtxA() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreCreateNpTitleCtxA(
OrbisNpServiceLabel serviceLabel, Libraries::UserService::OrbisUserServiceUserId userId) {
LOG_INFO(Lib_NpScore, "serviceLabel = {}, userId = {}", serviceLabel, userId);
OrbisNpId npId;
auto ret = NpManager::sceNpGetNpId(userId, &npId);
if (ret < 0) {
return ret;
}
return sceNpScoreCreateNpTitleCtx(serviceLabel, &npId);
}
int PS4_SYSV_ABI sceNpScoreCreateRequest() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreCreateRequest(int libCtxId) {
LOG_INFO(Lib_NpScore, "libCtxId = {}", libCtxId);
NpScoreTitleContext* ctx = nullptr;
if (auto ret = ctxManager.GetObject(libCtxId, &ctx); ret < 0) {
return ret;
}
auto req = ctx->requestManager.CreateObject(ctx->pcId);
if (req < 0) {
return req;
}
return PackReqId(libCtxId, req);
}
int PS4_SYSV_ABI sceNpScoreCreateTitleCtx() {
@ -48,14 +290,23 @@ int PS4_SYSV_ABI sceNpScoreCreateTitleCtx() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreDeleteNpTitleCtx() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreDeleteNpTitleCtx(int libCtxId) {
LOG_INFO(Lib_NpScore, "libCtxId = {}", libCtxId);
return ctxManager.DeleteObject(libCtxId);
}
int PS4_SYSV_ABI sceNpScoreDeleteRequest() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreDeleteRequest(int requestId) {
LOG_INFO(Lib_NpScore, "requestId = {:#x}", requestId);
auto [ctxId, reqId] = UnpackReqId(requestId);
NpScoreTitleContext* ctx = nullptr;
if (auto ret = ctxManager.GetObject(ctxId, &ctx); ret < 0) {
return ret;
}
return ctx->DeleteRequest(reqId);
}
int PS4_SYSV_ABI sceNpScoreGetBoardInfo() {
@ -68,11 +319,67 @@ int PS4_SYSV_ABI sceNpScoreGetBoardInfoAsync() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetFriendsRanking() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingAsync(
int reqId, int scoreBoardId, int includeMe, OrbisNpScoreRankData* rankArray, u64 rankArraySize,
OrbisNpScoreComment* comments, u64 commentsBytes, OrbisNpScoreGameInfo* gameInfos,
u64 gameInfosBytes, u64 arrayLen, Libraries::Rtc::OrbisRtcTick* lastUpdate, u64* totalRecords,
void* option) {
LOG_ERROR(Lib_NpScore,
"(STUBBED) reqId = {:#x}, scoreBoardId = {}, includeMe = {}, rankArray = {}, "
"rankArraySize = {}, comments = {}, commentsBytes = {}, gameInfos = {}, "
"gameInfosBytes = {}, arrayLen = {}",
reqId, scoreBoardId, includeMe, fmt::ptr(rankArray), rankArraySize,
fmt::ptr(comments), commentsBytes, fmt::ptr(gameInfos), gameInfosBytes, arrayLen);
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(reqId, &req); ret < 0) {
return ret;
}
req->requestFuture = std::async([=]() {
// fake an empty response
if (totalRecords) {
*totalRecords = 0;
}
if (lastUpdate) {
Libraries::Rtc::sceRtcGetCurrentTick(lastUpdate);
}
return 0;
});
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetFriendsRanking(int reqId, int scoreBoardId, int includeMe,
OrbisNpScoreRankData* rankArray, u64 rankArraySize,
OrbisNpScoreComment* comments, u64 commentsBytes,
OrbisNpScoreGameInfo* gameInfos, u64 gameInfosBytes,
u64 arrayLen, Libraries::Rtc::OrbisRtcTick* lastUpdate,
u64* totalRecords, void* option) {
LOG_ERROR(Lib_NpScore,
"(STUBBED) reqId = {:#x}, scoreBoardId = {}, includeMe = {}, rankArray = {}, "
"rankArraySize = {}, comments = {}, commentsBytes = {}, gameInfos = {}, "
"gameInfosBytes = {}, arrayLen = {}",
reqId, scoreBoardId, includeMe, fmt::ptr(rankArray), rankArraySize,
fmt::ptr(comments), commentsBytes, fmt::ptr(gameInfos), gameInfosBytes, arrayLen);
auto ret = sceNpScoreGetFriendsRankingAsync(
reqId, scoreBoardId, includeMe, rankArray, rankArraySize, comments, commentsBytes,
gameInfos, gameInfosBytes, arrayLen, lastUpdate, totalRecords, option);
if (ret < 0) {
return ret;
}
int result = 0;
if (sceNpScoreWaitAsync(reqId, &result) == 0) {
return result;
}
return -1; //?
}
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingA() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
@ -83,11 +390,6 @@ int PS4_SYSV_ABI sceNpScoreGetFriendsRankingAAsync() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingForCrossSave() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
@ -163,19 +465,110 @@ int PS4_SYSV_ABI sceNpScoreGetRankingByNpId() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdPcIdAsync(
int reqId, OrbisNpScoreBoardId scoreBoardId, OrbisNpScoreNpIdPcId* accountIds,
u64 accountIdsBytes, OrbisNpScorePlayerRankData* ranks, u64 ranksBytes,
OrbisNpScoreComment* comments, u64 commentsBytes, OrbisNpScoreGameInfo* gameInfos,
u64 gameInfosBytes, u64 accountsLen, Rtc::OrbisRtcTick* lastUpdate,
OrbisNpScoreRankNumber* totalRecords, void* option) {
LOG_ERROR(
Lib_NpScore,
"(STUBBED) called, reqId = {:#x}, scoreBoardId = {}, accountIds = {}, accountIdsBytes = "
"{}, ranks = {}, "
"comments = {}, gameInfos = {}, accountsLen = {}",
reqId, scoreBoardId, fmt::ptr(accountIds), accountIdsBytes, fmt::ptr(ranks),
fmt::ptr(comments), fmt::ptr(gameInfos), accountsLen);
if (option) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (!accountIds || !ranks) {
return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (accountsLen > 101) {
return ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
}
if (accountsLen * sizeof(*accountIds) != accountIdsBytes ||
accountsLen * sizeof(*ranks) != ranksBytes) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (comments && accountsLen * sizeof(*comments) != commentsBytes) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (gameInfos && accountsLen * sizeof(*gameInfos) != gameInfosBytes) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
// if sdk version > 5.49 zero all of the provided buffers
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(reqId, &req); ret < 0) {
return ret;
}
req->requestFuture = std::async([=]() {
// fake an empty response
if (totalRecords) {
*totalRecords = 0;
}
if (lastUpdate) {
Libraries::Rtc::sceRtcGetCurrentTick(lastUpdate);
}
return 0;
});
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdPcId() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdAsync(
int reqId, OrbisNpScoreBoardId scoreBoardId, OrbisNpId* npIds, u64 npIdsBytes,
OrbisNpScorePlayerRankData* ranks, u64 ranksBytes, OrbisNpScoreComment* comments,
u64 commentsBytes, OrbisNpScoreGameInfo* gameInfos, u64 gameInfosBytes, u64 accountsLen,
Rtc::OrbisRtcTick* lastUpdate, OrbisNpScoreRankNumber* totalRecords, void* option) {
LOG_ERROR(Lib_NpScore,
"(STUBBED) called, reqId = {:#x}, scoreBoardId = {}, npIds = {}, npIdsBytes = "
"{}, ranks = {}, "
"comments = {}, gameInfos = {}, accountsLen = {}",
reqId, scoreBoardId, fmt::ptr(npIds), npIdsBytes, fmt::ptr(ranks), fmt::ptr(comments),
fmt::ptr(gameInfos), accountsLen);
std::vector<OrbisNpScoreNpIdPcId> npIdsPcIds;
std::ranges::transform(std::span(npIds, accountsLen), std::back_inserter(npIdsPcIds),
[](OrbisNpId npId) -> OrbisNpScoreNpIdPcId { return {npId, 0, {}}; });
return sceNpScoreGetRankingByNpIdPcIdAsync(
reqId, scoreBoardId, npIdsPcIds.data(), accountsLen * sizeof(OrbisNpScoreNpIdPcId), ranks,
ranksBytes, comments, commentsBytes, gameInfos, gameInfosBytes, accountsLen, lastUpdate,
totalRecords, option);
}
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdPcIdAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdPcId(
int reqId, OrbisNpScoreBoardId scoreBoardId, OrbisNpScoreNpIdPcId* accountIds,
u64 accountIdsBytes, OrbisNpScorePlayerRankData* ranks, u64 ranksBytes,
OrbisNpScoreComment* comments, u64 commentsBytes, OrbisNpScoreGameInfo* gameInfos,
u64 gameInfosBytes, u64 accountsLen, Rtc::OrbisRtcTick* lastUpdate,
OrbisNpScoreRankNumber* totalRecords, void* option) {
LOG_ERROR(Lib_NpScore,
"(STUBBED) called, reqId = {:#x}, scoreBoardId = {}, accountIds = {}, ranks = {}, "
"comments = {}, gameInfos = {}, accountsLen = {}",
reqId, scoreBoardId, fmt::ptr(accountIds), fmt::ptr(ranks), fmt::ptr(comments),
fmt::ptr(gameInfos), accountsLen);
int ret = sceNpScoreGetRankingByNpIdPcIdAsync(
reqId, scoreBoardId, accountIds, accountIdsBytes, ranks, ranksBytes, comments,
commentsBytes, gameInfos, gameInfosBytes, accountsLen, lastUpdate, totalRecords, option);
if (ret < 0) {
return ret;
}
int result = 0;
if (sceNpScoreWaitAsync(reqId, &result) == 0) {
return result;
}
return -1; //?
}
int PS4_SYSV_ABI sceNpScoreGetRankingByRange() {
@ -188,14 +581,58 @@ int PS4_SYSV_ABI sceNpScoreGetRankingByRangeA() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeAAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeAsync(
int reqId, OrbisNpScoreBoardId scoreBoardId, OrbisNpScoreRankNumber startRank,
OrbisNpScoreRankDataA* ranks, u64 ranksBytes, OrbisNpScoreComment* comments, u64 commentsBytes,
OrbisNpScoreGameInfo* gameInfos, u64 gameInfosBytes, u64 ranksLen,
Rtc::OrbisRtcTick* lastUpdate, OrbisNpScoreRankNumber* totalRecords, void* option) {
LOG_ERROR(Lib_NpScore,
"(STUBBED) called, reqId = {:#x}, scoreBoardId = {}, startRank = {}, ranks = {}, "
"comments = {}, gameInfos = {}, ranksLen = {}",
reqId, scoreBoardId, startRank, fmt::ptr(ranks), fmt::ptr(comments),
fmt::ptr(gameInfos), ranksLen);
if (ranksLen * sizeof(*ranks) != ranksBytes) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (startRank == 0 || option != nullptr || ranksLen > 100) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
// if sdk version > 1.70 zero all of the provided buffers
if (ranks == nullptr || ranksLen == 0) {
return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (comments) {
if (ranksLen * sizeof(*comments) != commentsBytes) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
}
if (gameInfos) {
if (ranksLen * sizeof(*gameInfos) != gameInfosBytes) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
}
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(reqId, &req); ret < 0) {
return ret;
}
req->GetRankingByRange(scoreBoardId, startRank, ranks, ranksLen, gameInfos, comments,
lastUpdate, totalRecords);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeAAsync(
int reqId, OrbisNpScoreBoardId scoreBoardId, OrbisNpScoreRankNumber startRank,
OrbisNpScoreRankDataA* ranks, u64 ranksBytes, OrbisNpScoreComment* comments, u64 commentsBytes,
OrbisNpScoreGameInfo* gameInfos, u64 gameInfosBytes, u64 ranksLen,
Rtc::OrbisRtcTick* lastUpdate, OrbisNpScoreRankNumber* totalRecords, void* option) {
// at least in 9.00 it's identical A vs non-A
return sceNpScoreGetRankingByRangeAsync(reqId, scoreBoardId, startRank, ranks, ranksBytes,
comments, commentsBytes, gameInfos, gameInfosBytes,
ranksLen, lastUpdate, totalRecords, option);
}
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeForCrossSave() {
@ -208,9 +645,27 @@ int PS4_SYSV_ABI sceNpScoreGetRankingByRangeForCrossSaveAsync() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScorePollAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScorePollAsync(int reqId, int* result) {
LOG_INFO(Lib_NpScore, "reqId = {:#x}", reqId);
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(reqId, &req); ret < 0) {
return ret;
}
if (!req->requestFuture.valid()) {
LOG_ERROR(Lib_NpScore, "request not started");
return 1;
}
if (req->requestFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
LOG_DEBUG(Lib_NpScore, "request finished");
if (result) {
*result = req->requestFuture.get();
}
return 0;
}
return 1;
}
int PS4_SYSV_ABI sceNpScoreRecordGameData() {
@ -223,14 +678,45 @@ int PS4_SYSV_ABI sceNpScoreRecordGameDataAsync() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreRecordScore() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
int PS4_SYSV_ABI sceNpScoreRecordScoreAsync(int reqId, OrbisNpScoreBoardId boardId,
OrbisNpScoreValue score, OrbisNpScoreComment* comment,
OrbisNpScoreGameInfo* gameInfo, void* unk,
Libraries::Rtc::OrbisRtcTick* date, void* option) {
LOG_ERROR(Lib_NpScore, "reqId = {:#x}, boardId = {}, score = {}", reqId, boardId, score);
if (option) {
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(reqId, &req); ret < 0) {
return ret;
}
req->RecordScore(boardId, score, comment, gameInfo);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreRecordScoreAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpScoreRecordScore(int reqId, OrbisNpScoreBoardId boardId,
OrbisNpScoreValue score, OrbisNpScoreComment* comment,
OrbisNpScoreGameInfo* gameInfo, void* unk,
Libraries::Rtc::OrbisRtcTick* date, void* option) {
LOG_ERROR(Lib_NpScore, "reqId = {:#x}, boardId = {}, score = {}", reqId, boardId, score);
int ret =
sceNpScoreRecordScoreAsync(reqId, boardId, score, comment, gameInfo, unk, date, option);
if (ret < 0) {
return ret;
}
int result = 0;
if (sceNpScoreWaitAsync(reqId, &result) == 0) {
return result;
}
return -1; //?
}
int PS4_SYSV_ABI sceNpScoreSanitizeComment() {
@ -243,8 +729,25 @@ int PS4_SYSV_ABI sceNpScoreSanitizeCommentAsync() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreSetPlayerCharacterId() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
int PS4_SYSV_ABI sceNpScoreSetPlayerCharacterId(int id, int pcId) {
LOG_INFO(Lib_NpScore, "id = {:#x}, pcId = {}", id, pcId);
if (IsReqId(id)) {
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(id, &req); ret < 0) {
return ret;
}
req->pcId = pcId;
} else {
NpScoreTitleContext* ctx = nullptr;
if (auto ret = ctxManager.GetObject(id, &ctx); ret < 0) {
return ret;
}
ctx->pcId = pcId;
}
return ORBIS_OK;
}
@ -258,8 +761,25 @@ int PS4_SYSV_ABI sceNpScoreSetTimeout() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpScoreWaitAsync() {
LOG_ERROR(Lib_NpScore, "(STUBBED) called");
int PS4_SYSV_ABI sceNpScoreWaitAsync(int reqId, int* result) {
LOG_INFO(Lib_NpScore, "reqId = {:#x}", reqId);
NpScoreRequest* req = nullptr;
if (auto ret = GetRequest(reqId, &req); ret < 0) {
return ret;
}
if (!req->requestFuture.valid()) {
LOG_ERROR(Lib_NpScore, "request not started");
return 1;
}
req->requestFuture.wait();
LOG_DEBUG(Lib_NpScore, "request finished");
if (result) {
*result = req->requestFuture.get();
}
return ORBIS_OK;
}

View File

@ -4,6 +4,8 @@
#pragma once
#include "common/types.h"
#include "core/libraries/np/np_types.h"
#include "core/libraries/rtc/rtc.h"
namespace Core::Loader {
class SymbolsResolver;
@ -11,57 +13,90 @@ class SymbolsResolver;
namespace Libraries::Np::NpScore {
int PS4_SYSV_ABI sceNpScoreAbortRequest();
int PS4_SYSV_ABI sceNpScoreCensorComment();
int PS4_SYSV_ABI sceNpScoreCensorCommentAsync();
int PS4_SYSV_ABI sceNpScoreChangeModeForOtherSaveDataOwners();
int PS4_SYSV_ABI sceNpScoreCreateNpTitleCtx();
int PS4_SYSV_ABI sceNpScoreCreateNpTitleCtxA();
int PS4_SYSV_ABI sceNpScoreCreateRequest();
int PS4_SYSV_ABI sceNpScoreCreateTitleCtx();
int PS4_SYSV_ABI sceNpScoreDeleteNpTitleCtx();
int PS4_SYSV_ABI sceNpScoreDeleteRequest();
int PS4_SYSV_ABI sceNpScoreGetBoardInfo();
int PS4_SYSV_ABI sceNpScoreGetBoardInfoAsync();
int PS4_SYSV_ABI sceNpScoreGetFriendsRanking();
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingA();
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingAAsync();
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingAsync();
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingForCrossSave();
int PS4_SYSV_ABI sceNpScoreGetFriendsRankingForCrossSaveAsync();
int PS4_SYSV_ABI sceNpScoreGetGameData();
int PS4_SYSV_ABI sceNpScoreGetGameDataAsync();
int PS4_SYSV_ABI sceNpScoreGetGameDataByAccountId();
int PS4_SYSV_ABI sceNpScoreGetGameDataByAccountIdAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountId();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdForCrossSave();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdForCrossSaveAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdPcId();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdPcIdAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdPcIdForCrossSave();
int PS4_SYSV_ABI sceNpScoreGetRankingByAccountIdPcIdForCrossSaveAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByNpId();
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdPcId();
int PS4_SYSV_ABI sceNpScoreGetRankingByNpIdPcIdAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByRange();
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeA();
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeAAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeAsync();
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeForCrossSave();
int PS4_SYSV_ABI sceNpScoreGetRankingByRangeForCrossSaveAsync();
int PS4_SYSV_ABI sceNpScorePollAsync();
int PS4_SYSV_ABI sceNpScoreRecordGameData();
int PS4_SYSV_ABI sceNpScoreRecordGameDataAsync();
int PS4_SYSV_ABI sceNpScoreRecordScore();
int PS4_SYSV_ABI sceNpScoreRecordScoreAsync();
int PS4_SYSV_ABI sceNpScoreSanitizeComment();
int PS4_SYSV_ABI sceNpScoreSanitizeCommentAsync();
int PS4_SYSV_ABI sceNpScoreSetPlayerCharacterId();
int PS4_SYSV_ABI sceNpScoreSetThreadParam();
int PS4_SYSV_ABI sceNpScoreSetTimeout();
int PS4_SYSV_ABI sceNpScoreWaitAsync();
using OrbisNpScoreBoardId = u32;
using OrbisNpScorePcId = u32;
using OrbisNpScoreRankNumber = u32;
using OrbisNpScoreValue = s64;
struct OrbisNpScoreNpIdPcId {
Np::OrbisNpId npId;
OrbisNpScorePcId pcId;
u8 pad[4];
};
static_assert(sizeof(OrbisNpScoreNpIdPcId) == 0x2c);
struct OrbisNpScoreAccountIdPcId {
OrbisNpAccountId accountId;
OrbisNpScorePcId pcId;
u8 pad[4];
};
struct OrbisNpScoreRankDataA {
OrbisNpOnlineId onlineId;
u8 reserved[68];
OrbisNpScorePcId pcId;
OrbisNpScoreRankNumber rank1;
OrbisNpScoreRankNumber rank2;
OrbisNpScoreRankNumber rank3;
int hasGameData;
u8 pad[4];
OrbisNpScoreValue score;
Rtc::OrbisRtcTick recordTime;
OrbisNpAccountId accountId;
u8 pad1[8];
};
static_assert(sizeof(OrbisNpScoreRankDataA) == 0x90);
struct OrbisNpScorePlayerRankDataA {
int hasData;
u8 pad[4];
OrbisNpScoreRankDataA rankData;
};
static_assert(sizeof(OrbisNpScorePlayerRankDataA) == 0x98);
struct OrbisNpScoreRankData {
OrbisNpOnlineId onlineId;
u8 reserved[52];
OrbisNpScorePcId pcId;
OrbisNpScoreRankNumber rank1;
OrbisNpScoreRankNumber rank2;
OrbisNpScoreRankNumber rank3;
int hasGameData;
u8 pad[4];
OrbisNpScoreValue score;
Rtc::OrbisRtcTick recordTime;
OrbisNpAccountId accountId;
u8 pad1[8];
};
static_assert(sizeof(OrbisNpScoreRankData) == 0x80);
struct OrbisNpScorePlayerRankData {
int hasData;
u8 pad[4];
OrbisNpScoreRankData rankData;
};
static_assert(sizeof(OrbisNpScorePlayerRankData) == 0x88);
struct OrbisNpScoreComment {
char comment[64];
};
static_assert(sizeof(OrbisNpScoreComment) == 64);
struct OrbisNpScoreGameInfo {
u64 dataSize;
u8 data[189];
u8 pad[3];
};
static_assert(sizeof(OrbisNpScoreGameInfo) == 200);
int PS4_SYSV_ABI sceNpScoreWaitAsync(int reqId, int* result);
void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Np::NpScore