Files
ppsspp/Core/HLE/sceNp.cpp
T
libretroadmin f1208a7a5a Make threading lock-free
The core and the headless tools no longer use mutexes, condition variables,
spinlocks, std::atomic, std::thread or volatile flags. Threads hand off through
libretro-common's retro_atomic and rthreads plus a few small pieces on top of
them in Common/Thread: Thread, MpscQueue, EventCounter, ParkingLot waits and
ReaderGate. Every wait blocks until it's woken; nothing polls on a timer.

State that only one thread touches just loses its lock. The PSP filesystem,
sceIo and the savedata dialog now belong to the emulation thread: async reads
and writes run when they're queued and complete later in emulated time, and
the IO and savedata threads are gone. The friend finder only talks to the
adhoc server and hands what it gets to the emulation thread. GPU init always
runs on the thread that starts the game.

The Vulkan render and compile queues, pipeline and render pass caches, delete
list and SPIR-V cache, the GL render queue, the software renderer's jit caches,
the file loader caches, the web debugger, UPnP, input, savestates, logging and
the GE debugger all hand off through queues and atomics. The IR jit's spinning
profiler thread goes.

A CPU thread with nothing to run now sleeps until work is queued instead of
spinning. Headless with --debugger waits for a client while stopped, and ends
the run once every client that connected has left. Web server shutdown keeps
servicing the CPU queue until the server has finished.
2026-10-09 04:56:16 +02:00

662 lines
29 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// This is pretty much a stub implementation. Doesn't actually do anything, just tries to return values
// to keep games happy anyway.
#include <deque>
#include "Common/System/OSD.h"
#include "Common/Data/Text/I18n.h"
#include "Common/StringUtils.h"
#include "Core/MemMapHelpers.h"
#include "Core/CoreTiming.h"
#include "Core/Config.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLEUtil.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceNp.h"
bool npAuthInited = false;
int npSigninState = NP_SIGNIN_STATUS_NONE;
SceNpAuthMemoryStat npAuthMemStat = {};
PSPTimeval npSigninTimestamp{};
// TODO: These should probably be grouped in a struct, since they're used to generate an auth ticket
constexpr int npParentalControl = PARENTAL_CONTROL_ENABLED;
constexpr int npUserAge = 24; // faking user Age to 24 yo
constexpr int npChatRestriction = 0; // default/initial value on Patapon 3 is 1 (restricted boolean?)
static const SceNpMyLanguages npMyLangList = { 1033, 2057, 1036 }; // Languages the user is assumed to know. No known games make use of this.
// Fields are 4-sized, so the data needs to be too.
static const char npCountryCode[4] = "us"; // dummy data taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket. Mainly affects what EULA is downloaded.
static const char npRegionCode[4] = "c9"; // not sure what "c9" meant, since it was close to country code data, might be region-related data?
std::string npOnlineId = "DummyOnlineId"; // SceNpOnlineId struct?
std::string npServiceId = ""; // UNO game uses EP2006-NPEH00020_00
std::string npAvatarUrl = "http://DummyAvatarUrl"; // SceNpAvatarUrl struct?
// Game-specific ID, I guess we can use this to auto-choose DNS?
SceNpCommunicationId npTitleId;
std::deque<NpAuthArgs> npAuthEvents;
std::map<int, NpAuthHandler> npAuthHandlers;
void __NpInit() {
npAuthInited = false;
npSigninState = NP_SIGNIN_STATUS_NONE;
npAuthMemStat = {};
npSigninTimestamp = {};
npTitleId = {};
}
// Tickets data are in big-endian based on captured packets
static int writeTicketParam(u8* buffer, const u16_be type, const char* data = nullptr, const u16_be size = 0) {
if (buffer == nullptr) return 0;
u16_be sz = (data == nullptr)? static_cast<u16_be>(0): size;
memcpy(buffer, &type, 2);
memcpy(buffer + 2, &sz, 2);
if (sz > 0 && data != nullptr)
memcpy(buffer + 4, data, sz);
return sz + 4;
}
static int writeTicketStringParam(u8* buffer, const u16_be type, const char* data = nullptr, const u16_be size = 0) {
if (buffer == nullptr) return 0;
u16_be sz = (data == nullptr) ? static_cast<u16_be>(0) : size;
memcpy(buffer, &type, 2);
memcpy(buffer + 2, &sz, 2);
if (sz > 0) {
// Yes, we want to use strncpy. Do not change to truncate_cpy.
if (data)
strncpy((char *)buffer + 4, data, sz);
else
memset(buffer + 4, 0, sz);
}
return sz + 4;
}
static int writeTicketU32Param(u8* buffer, const u16_be type, const u32_be data) {
if (buffer == nullptr) return 0;
u16_be sz = 4;
memcpy(buffer, &type, 2);
memcpy(buffer + 2, &sz, 2);
memcpy(buffer + 4, &data, 4);
return sz + 4;
}
static int writeTicketU64Param(u8* buffer, const u16_be type, const u64_be data) {
if (buffer == nullptr) return 0;
u16_be sz = 8;
memcpy(buffer, &type, 2);
memcpy(buffer + 2, &sz, 2);
memcpy(buffer + 4, &data, sz);
return sz + 4;
}
static void notifyNpAuthHandlers(u32 id, u32 result, u32 argAddr) {
npAuthEvents.push_back({ { id, result, argAddr } });
}
bool NpAuthProcessEvents() {
if (npAuthEvents.empty()) {
return false;
}
auto& args = npAuthEvents.front();
auto& id = args.data[0];
auto& result = args.data[1];
auto& argAddr = args.data[2];
npAuthEvents.pop_front();
int handlerID = id - 1;
for (std::map<int, NpAuthHandler>::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); ++it) {
if (it->first == handlerID) {
DEBUG_LOG(Log::sceNet, "NpAuthCallback [HandlerID=%i][RequestID=%d][Result=%d][ArgsPtr=%08x]", it->first, id, result, it->second.argument);
// TODO: Update result / args.data[1] with the actual ticket length (or error code?)
hleEnqueueCall(it->second.entryPoint, 3, args.data);
}
}
return true;
}
static int sceNpInit()
{
ERROR_LOG(Log::sceNet, "UNIMPL %s()", __FUNCTION__);
// We'll sanitize an extra time here, just to be safe from ini modifications.
if (g_Config.sInfrastructureUsername == SanitizeString(g_Config.sInfrastructureUsername, StringRestriction::AlphaNumDashUnderscore, 3, 16)) {
npOnlineId = g_Config.sInfrastructureUsername;
} else {
npOnlineId.clear();
}
// NOTE: Checking validity and returning -1 here doesn't seem to work. Instead, we will fail to generate a ticket.
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
static int sceNpTerm()
{
// Reset sign in state.
npSigninState = NP_SIGNIN_STATUS_NONE;
// No parameters
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
static int sceNpGetContentRatingFlag(u32 parentalControlAddr, u32 userAgeAddr)
{
if (!Memory::IsValidAddress(parentalControlAddr) || !Memory::IsValidAddress(userAgeAddr))
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
INFO_LOG(Log::sceNet, "%s - Parental Control: %d", __FUNCTION__, npParentalControl);
INFO_LOG(Log::sceNet, "%s - User Age: %d", __FUNCTION__, npUserAge);
Memory::WriteOrException_U32(npParentalControl, parentalControlAddr);
Memory::WriteOrException_U32(npUserAge, userAgeAddr);
return hleLogWarning(Log::sceNet, 0, "UNTESTED");
}
static int sceNpGetChatRestrictionFlag(u32 flagAddr)
{
if (!Memory::IsValidAddress(flagAddr))
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
Memory::WriteOrException_U32(npChatRestriction, flagAddr);
return hleLogWarning(Log::sceNet, 0, "Chat restriction: %d", npChatRestriction);
}
static int sceNpGetOnlineId(u32 idPtr)
{
WARN_LOG(Log::sceNet, "UNTESTED %s(%08x)", __FUNCTION__, idPtr);
auto id = PSPPointer<SceNpOnlineId>::Create(idPtr);
if (!id.IsValid())
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
id.FillWithZero();
strncpy(id->data, npOnlineId.c_str(), sizeof(id->data));
id.NotifyWrite("NpGetOnlineId");
return hleLogWarning(Log::sceNet, 0, "Online ID: %s", id->data);
}
int NpGetNpId(SceNpId* npid) {
// Callers make sure that the rest of npid is zero filled, which takes care of the terminator.
strncpy(npid->handle.data, npOnlineId.c_str(), sizeof(npid->handle.data));
return 0;
}
static int sceNpGetNpId(u32 idPtr)
{
auto id = PSPPointer<SceNpId>::Create(idPtr);
if (!id.IsValid())
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
SceNpId dummyNpId{};
id.FillWithZero();
int retval = NpGetNpId(id);
if (retval < 0)
return hleLogError(Log::sceNet, retval);
std::string datahex;
DataToHexString(id->opt, sizeof(id->opt), &datahex);
id.NotifyWrite("NpGetNpId");
return hleLogWarning(Log::sceNet, 0, "Online ID: %s Options: %s", id->handle.data, datahex.c_str());
}
static int sceNpGetAccountRegion(u32 countryCodePtr, u32 regionCodePtr)
{
WARN_LOG(Log::sceNet, "UNTESTED %s(%08x, %08x)", __FUNCTION__, countryCodePtr, regionCodePtr);
auto countryCode = PSPPointer<SceNpCountryCode>::Create(countryCodePtr);
auto regionCode = PSPPointer<SceNpCountryCode>::Create(regionCodePtr);
if (!countryCode.IsValid() || !regionCode.IsValid())
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
countryCode.FillWithZero();
memcpy(countryCode->data, npCountryCode, sizeof(countryCode->data));
regionCode.FillWithZero();
memcpy(regionCode->data, npRegionCode, sizeof(regionCode->data));
INFO_LOG(Log::sceNet, "%s - Country Code: %s", __FUNCTION__, countryCode->data);
INFO_LOG(Log::sceNet, "%s - Region? Code: %s", __FUNCTION__, regionCode->data);
countryCode.NotifyWrite("NpGetAccountRegion");
regionCode.NotifyWrite("NpGetAccountRegion");
return hleNoLog(0);
}
static int sceNpGetMyLanguages(u32 langListPtr)
{
WARN_LOG(Log::sceNet, "UNTESTED %s(%08x)", __FUNCTION__, langListPtr);
auto langList = PSPPointer<SceNpMyLanguages>::Create(langListPtr);
if (!langList.IsValid())
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
INFO_LOG(Log::sceNet, "%s - Language1 Code: %d", __FUNCTION__, npMyLangList.language1);
INFO_LOG(Log::sceNet, "%s - Language2 Code: %d", __FUNCTION__, npMyLangList.language2);
INFO_LOG(Log::sceNet, "%s - Language3 Code: %d", __FUNCTION__, npMyLangList.language3);
*langList = npMyLangList;
langList.NotifyWrite("NpGetMyLanguages");
return hleNoLog(0);
}
static int sceNpGetUserProfile(u32 profilePtr)
{
WARN_LOG(Log::sceNet, "UNTESTED %s(%08x)", __FUNCTION__, profilePtr);
auto profile = PSPPointer<SceNpUserInformation>::Create(profilePtr);
if (!Memory::IsValidAddress(profilePtr))
return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
profile.FillWithZero();
strncpy(profile->userId.handle.data, npOnlineId.c_str(), sizeof(profile->userId.handle.data));
truncate_cpy(profile->icon.data, sizeof(profile->icon.data), npAvatarUrl);
INFO_LOG(Log::sceNet, "%s - Online ID: %s", __FUNCTION__, profile->userId.handle.data);
std::string datahex;
DataToHexString(profile->userId.opt, sizeof(profile->userId.opt), &datahex);
INFO_LOG(Log::sceNet, "%s - Options?: %s", __FUNCTION__, datahex.c_str());
INFO_LOG(Log::sceNet, "%s - Avatar URL: %s", __FUNCTION__, profile->icon.data);
profile.NotifyWrite("NpGetUserProfile");
return hleNoLog(0);
}
const HLEFunction sceNp[] = {
{0X857B47D3, &WrapI_V<sceNpInit>, "sceNpInit", 'i', "" },
{0X37E1E274, &WrapI_V<sceNpTerm>, "sceNpTerm", 'i', "" },
{0XBB069A87, &WrapI_UU<sceNpGetContentRatingFlag>, "sceNpGetContentRatingFlag", 'i', "xx" },
{0X1D60AE4B, &WrapI_U<sceNpGetChatRestrictionFlag>, "sceNpGetChatRestrictionFlag", 'i', "x" },
{0x4B5C71C8, &WrapI_U<sceNpGetOnlineId>, "sceNpGetOnlineId", 'i', "x" },
{0x633B5F71, &WrapI_U<sceNpGetNpId>, "sceNpGetNpId", 'i', "x" },
{0x7E0864DF, &WrapI_U<sceNpGetUserProfile>, "sceNpGetUserProfile", 'i', "x" },
{0xA0BE3C4B, &WrapI_UU<sceNpGetAccountRegion>, "sceNpGetAccountRegion", 'i', "xx" },
{0xCDCC21D3, &WrapI_U<sceNpGetMyLanguages>, "sceNpGetMyLanguages", 'i', "x" },
};
void Register_sceNp()
{
RegisterHLEModule("sceNp", ARRAY_SIZE(sceNp), sceNp);
}
static int sceNpAuthTerm()
{
// No parameters
npAuthInited = false;
return hleLogWarning(Log::sceNet, 0, "UNIMPL");
}
static int sceNpAuthInit(u32 poolSize, u32 stackSize, u32 threadPrio)
{
npAuthMemStat.npMemSize = poolSize - 0x20;
npAuthMemStat.npMaxMemSize = 0x4050; // Dummy maximum foot print
npAuthMemStat.npFreeMemSize = npAuthMemStat.npMemSize;
npAuthEvents.clear();
npAuthInited = true;
return hleLogWarning(Log::sceNet, 0);
}
int sceNpAuthGetMemoryStat(u32 memStatAddr)
{
ERROR_LOG(Log::sceNet, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr);
auto memStat = PSPPointer<SceNpAuthMemoryStat>::Create(memStatAddr);
if (!memStat.IsValid())
return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
*memStat = npAuthMemStat;
memStat.NotifyWrite("NpAuthGetMemoryStat");
return hleLogWarning(Log::sceNet, 0);
}
/*
"Authenticating matching server usage license" on Patapon 3. Could be waiting for a state change for eternity? probably need to trigger a callback handler?
TODO: Login to "https://auth.np.ac.playstation.net/nav/auth" based on https://www.psdevwiki.com/ps3/Online_Connections
param seems to be a struct where offset:
+00: 32-bit is the size of the struct (ie. 36 bytes),
+04: 32-bit is also a small number (ie. 3) a mode/event/flag/version may be?,
+08: 32-bit is a pointer to a productId? (ie. "EP9000-UCES01421_00"),
+0C: 4x 32-bit reserved? all zero
+1C: 32-bit callback handler? optional handler? seems to be a valid pointer and pointing to a starting point of a function (have a label on the disassembly)
+20: 32-bit a pointer to a random data (4 to 8-bytes data max? both 2x 32-bit seems to be a valid pointer). optional handler args?
return value >= 0 and <0 seems to be stored at a different location by the game (valid result vs error code?)
*/
int sceNpAuthCreateStartRequest(u32 paramAddr) {
SceNpAuthRequestParameter params = {};
if (!ReadVariableSizedStruct(paramAddr, &params)) {
return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
}
npServiceId = Memory::GetCharPointer(params.serviceIdAddr);
INFO_LOG(Log::sceNet, "%s - Max Version: %u.%u", __FUNCTION__, params.version.major, params.version.minor);
INFO_LOG(Log::sceNet, "%s - Service ID: %s", __FUNCTION__, Memory::GetCharPointer(params.serviceIdAddr));
INFO_LOG(Log::sceNet, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(params.entitlementIdAddr));
INFO_LOG(Log::sceNet, "%s - Consumed Count: %d", __FUNCTION__, params.consumedCount);
INFO_LOG(Log::sceNet, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, Memory::GetCharPointer(params.cookieAddr));
u32 retval = 0;
if (params.size >= 32 && params.ticketCbAddr != 0) {
bool foundHandler = false;
struct NpAuthHandler handler;
memset(&handler, 0, sizeof(handler));
while (npAuthHandlers.find(retval) != npAuthHandlers.end())
++retval;
handler.entryPoint = params.ticketCbAddr;
handler.argument = params.cbArgAddr;
for (std::map<int, NpAuthHandler>::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); it++) {
if (it->second.entryPoint == handler.entryPoint) {
foundHandler = true;
retval = it->first;
break;
}
}
if (!foundHandler && Memory::IsValidAddress(handler.entryPoint)) {
npAuthHandlers[retval] = handler;
WARN_LOG(Log::sceNet, "%s - Added handler(%08x, %08x) : %d", __FUNCTION__, handler.entryPoint, handler.argument, retval);
}
else {
ERROR_LOG(Log::sceNet, "%s - Same handler(%08x, %08x) already exists", __FUNCTION__, handler.entryPoint, handler.argument);
}
// Patapon 3 will only Abort & Destroy AuthRequest if the ID is larger than 0. Is 0 a valid request id?
retval++;
// 1st Arg usually either an ID returned from Create/AddHandler function or an Event ID if the game is expecting a sequence of events.
// 2nd Arg seems to be used if not a negative number and exits the handler if it's negative (error code?)
// 3rd Arg seems to be a data (ie. 92 bytes of data?) pointer and tested for null within callback handler (optional callback args?)
u32 ticketLength = 248; // default ticket length? should be updated using the ticket length returned from login
notifyNpAuthHandlers(retval, ticketLength, (params.size >= 36) ? params.cbArgAddr : 0);
}
//hleDelayResult(0, "give time", 500000);
return hleLogWarning(Log::sceNet, retval);
}
// Used within callback of sceNpAuthCreateStartRequest (arg1 = callback's args[0], arg2 = output structPtr?, arg3 = callback's args[1])
// Is this using request id for Arg1 or cbId?
// JPCSP is using length = 248 for dummy ticket
int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) {
if (!Memory::IsValidAddress(bufferAddr)) {
return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
}
// We have validated, and this will be empty if the ID is bad.
if (npOnlineId.empty()) {
auto n = GetI18NCategory(I18NCat::NETWORKING);
// Temporary message.
g_OSD.Show(OSDType::MESSAGE_ERROR, n->T("To play in Infrastructure Mode, you must enter a username"), 5.0f);
return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_UNKNOWN, "Missing npOnlineId");
}
int result = length;
Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket");
SceNpTicket ticket = {};
// Dummy Login ticket returned as Login response. Dummy ticket contents were taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket
ticket.header.version = TICKET_VER_2_1;
ticket.header.size = 0xF0; // size excluding the header
u8* buf = Memory::GetPointerWriteOrException(bufferAddr + sizeof(ticket));
int ofs = 0;
ofs += writeTicketParam(buf, PARAM_TYPE_STRING_ASCII, "\x4c\x47\x56\x3b\x81\x39\x4a\x22\xd8\x6b\xc1\x57\x71\x6e\xfd\xb8\xab\x63\xcc\x51", 20); // 20 random letters, token key or SceNpSignature?
ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, 0x0100); // a flags?
PSPTimeval tv; //npSigninTimestamp
__RtcTimeOfDay(&tv);
u64 now = 1000ULL*tv.tv_sec + tv.tv_usec/1000ULL; // in milliseconds, since 1900?
ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_DATE, now);
ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_DATE, now + 10 * 60 * 1000); // now + 10 minutes, expired time?
ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_LONG, 0x592e71c546e86859); // seems to be consistent, 8-bytes password hash may be? or related to entitlement? or console id?
ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, npOnlineId.c_str(), 32); // username (pre-cut to 16 chars)
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, npCountryCode, 4); // SceNpCountryCode ? ie. "fr" + 00 02
ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, npRegionCode, 4); // 2-char code? related to country/lang code? ie. "c9" + 00 00
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, npServiceId.c_str(), 24);
int status = 0;
if (npParentalControl == PARENTAL_CONTROL_ENABLED) {
status |= STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED;
}
status |= (npUserAge & 0x7F) << 24;
ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, status);
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL);
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL);
ticket.section.type = SECTION_TYPE_BODY;
ticket.section.size = ofs;
Memory::Memcpy(bufferAddr, &ticket, sizeof(SceNpTicket), "NpAuthGetTicket");
SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on version 2.1 containing 4-chars ASCII + 20-chars ASCII
Memory::Memcpy(bufferAddr + sizeof(ticket) + ofs, &footer, sizeof(SceNpTicketSection), "NpAuthGetTicket");
ofs += sizeof(footer);
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4);
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); // 20 random letters, token key or SceNpSignature?
// includes Language list?
Memory::Memset(bufferAddr + sizeof(ticket) + ofs, 0, 36);
result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes
return hleLogWarning(Log::sceNet, result);
}
// Used within callback of sceNpAuthCreateStartRequest (arg1 = structPtr?, arg2 = callback's args[1], arg3 = DLCcode? ie. "EP9000-UCES01421_00-DLL001", arg4 = Patapon 3 always set to 0?)
// Patapon 3 will loop (for each DLC?) through an array of 4+4 bytes, ID addr (pchar) + result (int). Each loop calls this function using the same ticket addr but use different ID addr (arg3) and store the return value in result field (default/initial = -1)
int sceNpAuthGetEntitlementById(u32 ticketBufferAddr, u32 ticketLength, u32 entitlementIdAddr, u32 arg4)
{
const char *entitlementID = Memory::GetCharPointer(entitlementIdAddr);
// Do we return the entitlement through function result? or update the ticket content? or replace the arg3 data with SceNpEntitlement struct?
// dummy value 1 assuming it's a boolean/flag, since we don't know how to return the entitlement result yet
return hleLogWarning(Log::sceNet, 1, "Entitlement ID: %s", entitlementID ? entitlementID : "N/A");
}
int sceNpAuthAbortRequest(int requestId)
{
// TODO: Disconnect HTTPS connection & cancel the callback event
for (auto it = npAuthEvents.begin(); it != npAuthEvents.end(); ) {
(it->data[0] == requestId) ? it = npAuthEvents.erase(it) : ++it;
}
return hleLogWarning(Log::sceNet, 0);
}
int sceNpAuthDestroyRequest(int requestId)
{
WARN_LOG(Log::sceNet, "UNTESTED %s(%i)", __FUNCTION__, requestId);
// Remove callback handler
int handlerID = requestId - 1;
if (npAuthHandlers.find(handlerID) != npAuthHandlers.end()) {
npAuthHandlers.erase(handlerID);
WARN_LOG(Log::sceNet, "%s: Deleted handler %d", __FUNCTION__, handlerID);
}
else {
ERROR_LOG(Log::sceNet, "%s: Invalid request ID %d", __FUNCTION__, requestId);
}
// Patapon 3 is checking for error code 0x80550402
return hleNoLog(0);
}
int sceNpAuthGetTicketParam(u32 ticketBufPtr, int ticketLen, int paramNum, u32 bufferPtr)
{
const u32 PARAM_BUFFER_MAX_SIZE = 256;
Memory::Memset(bufferPtr, 0, PARAM_BUFFER_MAX_SIZE); // JPCSP: This clear is always done, even when an error is returned
if (paramNum < 0 || paramNum >= NUMBER_PARAMETERS) {
return hleLogError(Log::sceNet, SCE_NP_MANAGER_ERROR_INVALID_ARGUMENT);
}
SceNpTicket* ticket = (SceNpTicket*)Memory::GetPointerOrException(ticketBufPtr);
u32 inbuf = ticketBufPtr;
inbuf += sizeof(ticket->header);
inbuf += ticket->section.size + sizeof(ticket->section);
u32 outbuf = bufferPtr;
for (int i = 0; i < paramNum; i++) {
SceNpTicketParamData* ticketParam = (SceNpTicketParamData*)Memory::GetPointerOrException(inbuf);
u32 sz = (u32)sizeof(SceNpTicketParamData) + ticketParam->length;
Memory::Memcpy(outbuf, inbuf, sz);
DEBUG_LOG(Log::sceNet, "%s - Param #%d: Type = %04x, Length = %u", __FUNCTION__, i, static_cast<unsigned int>(ticketParam->type), static_cast<unsigned int>(ticketParam->length));
outbuf += sz;
inbuf += sz;
if (outbuf - bufferPtr >= PARAM_BUFFER_MAX_SIZE || inbuf - ticketBufPtr >= (u32)ticketLen)
break;
}
return hleLogWarning(Log::sceNet, 0);
}
const HLEFunction sceNpAuth[] = {
{0X4EC1F667, &WrapI_V<sceNpAuthTerm>, "sceNpAuthTerm", 'i', "" },
{0XA1DE86F8, &WrapI_UUU<sceNpAuthInit>, "sceNpAuthInit", 'i', "xxx" },
{0XF4531ADC, &WrapI_U<sceNpAuthGetMemoryStat>, "sceNpAuthGetMemoryStat", 'i', "x" },
{0XCD86A656, &WrapI_U<sceNpAuthCreateStartRequest>, "sceNpAuthCreateStartRequest", 'i', "x" },
{0X3F1C1F70, &WrapI_UUU<sceNpAuthGetTicket>, "sceNpAuthGetTicket", 'i', "xxx" },
{0X6900F084, &WrapI_UUUU<sceNpAuthGetEntitlementById>, "sceNpAuthGetEntitlementById", 'i', "xxxx" },
{0XD99455DD, &WrapI_I<sceNpAuthAbortRequest>, "sceNpAuthAbortRequest", 'i', "i" },
{0X72BB0467, &WrapI_I<sceNpAuthDestroyRequest>, "sceNpAuthDestroyRequest", 'i', "i" },
{0x5A3CB57A, &WrapI_UIIU<sceNpAuthGetTicketParam>, "sceNpAuthGetTicketParam", 'i', "xiix" },
{0x75FB0AE3, nullptr, "sceNpAuthGetEntitlementIdList", 'i', "" },
};
void Register_sceNpAuth()
{
RegisterHLEModule("sceNpAuth", ARRAY_SIZE(sceNpAuth), sceNpAuth);
}
static int sceNpServiceTerm()
{
// No parameters
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
static int sceNpServiceInit(u32 poolSize, u32 stackSize, u32 threadPrio)
{
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
// FIXME: On Patapon 3 the Arg is pointing to a String, but based on RPCS3 the Arg is an Id integer ?
static int sceNpLookupCreateTransactionCtx(u32 lookupTitleCtxIdAddr) {
const char *titleID = Memory::GetCharPointer(lookupTitleCtxIdAddr);
// Patapon 3 will only Destroy if returned Id > 0. Is 0 a valid id? Probably not.
return hleLogWarning(Log::sceNet, 1, "UNIMPL - title ID: %s", titleID ? titleID : "N/A"); // returning dummy transaction id
}
// transId: id returned from sceNpLookupCreateTransactionCtx
static int sceNpLookupDestroyTransactionCtx(s32 transId) {
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
// transId: id returned from sceNpLookupCreateTransactionCtx
// Patapon 3 always set Arg5 to 0
// Addr args have something to do with GameUpdate?
// FIXME: maxSize and contentLength are u64 based on https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/sceNp.cpp ? But on Patapon 3 optionAddr will be deadbeef if maxSize is u64 ?
static int sceNpLookupTitleSmallStorage(s32 transId, u32 dataAddr, u32 maxSize, u32 contentLengthAddr, u32 optionAddr) {
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
// On Resistance Retribution:
// unknownAddr pointing to a struct of:
// 32-bit pointer (ie. 08efc6c4)? or a timestamp combined with the next 32-bit value?
// 32-bit pointer (ie. 08f9d101)? but unaligned (the lowest byte seems to be intentionally set to 1)? so probably not a pointer, may be a timestamp combined with previous 32-bit value?
// 32-bit pointer? Seems to be pointing to dummy ticket data generated by sceNpAuthGetTicket
// 32-bit value (248) dummy ticket length from NpAuth Ticket?
// There could be more data in the struct? (at least 48-bytes?)
static int sceNpRosterCreateRequest(u32 unknownAddr) {
// returning dummy roster id
return hleLogError(Log::sceNet, 1, "UNIMPL");
}
// On Resistance Retribution:
// unknown1 set to 50 (max entries?),
// unknown2 set to 0,
// unknown3Addr pointing to unset buffer? (output entry data? usually located right after number of entries?),
// unknown4Addr pointing to 32-bit value set to 0 (output number of entries?),
// unknown5Addr pointing to zeroed buffer?,
// unknown6 set to 0
static int sceNpRosterGetFriendListEntry(s32 rosterId, u32 unknown1, u32 unknown2, u32 unknown3Addr, u32 unknown4Addr, u32 unknown5Addr, u32 unknown6) {
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
static int sceNpRosterAbort(s32 rosterId) {
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
static int sceNpRosterDeleteRequest(s32 rosterId) {
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
const HLEFunction sceNpService[] = {
{0X00ACFAC3, &WrapI_V<sceNpServiceTerm>, "sceNpServiceTerm", 'i', "" },
{0X0F8F5821, &WrapI_UUU<sceNpServiceInit>, "sceNpServiceInit", 'i', "xxx" },
{0X5494274B, &WrapI_U<sceNpLookupCreateTransactionCtx>, "sceNpLookupCreateTransactionCtx", 'i', "x" },
{0XA670D3A3, &WrapI_I<sceNpLookupDestroyTransactionCtx>, "sceNpLookupDestroyTransactionCtx", 'i', "i" },
{0XC76F55ED, &WrapI_IUUUU<sceNpLookupTitleSmallStorage>, "sceNpLookupTitleSmallStorage", 'i', "ixxxx" },
{0XBE22EEA3, &WrapI_U<sceNpRosterCreateRequest>, "sceNpRosterCreateRequest", 'i', "x" },
{0X4E851B10, &WrapI_IUUUUUU<sceNpRosterGetFriendListEntry>, "sceNpRosterGetFriendListEntry", 'i', "ixxxxxx"},
{0X5F5E32AF, &WrapI_I<sceNpRosterAbort>, "sceNpRosterAbort", 'i', "i" },
{0X66C64821, &WrapI_I<sceNpRosterDeleteRequest>, "sceNpRosterDeleteRequest", 'i', "i" },
{0X506C318D, nullptr, "sceNpRosterGetBlockListEntry", 'i', "" },
{0X58251346, nullptr, "sceNpRosterGetFriendListEntryCount", 'i', "" },
{0X788F2B5E, nullptr, "sceNpRosterAddFriendListEntry", 'i', "" },
{0XA01443AA, nullptr, "sceNpRosterGetBlockListEntryCount", 'i', "" },
{0X250488F9, nullptr, "sceNpServiceGetMemoryStat", 'i', "" },
{0X4B4E4E71, nullptr, "sceNpLookupAbortTransaction ", 'i', "" },
};
void Register_sceNpService()
{
RegisterHLEModule("sceNpService", ARRAY_SIZE(sceNpService), sceNpService);
}
// TODO: Move NpCommerce2-related stuff to sceNpCommerce2.cpp?
const HLEFunction sceNpCommerce2[] = {
{0X005B5F20, nullptr, "sceNpCommerce2GetProductInfoStart", '?', "" },
{0X0E9956E3, nullptr, "sceNpCommerce2Init", '?', "" },
{0X1888A9FE, nullptr, "sceNpCommerce2DestroyReq", '?', "" },
{0X1C952DCB, nullptr, "sceNpCommerce2GetGameProductInfo", '?', "" },
{0X2B25F6E9, nullptr, "sceNpCommerce2CreateSessionStart", '?', "" },
{0X3371D5F1, nullptr, "sceNpCommerce2GetProductInfoCreateReq", '?', "" },
{0X4ECD4503, nullptr, "sceNpCommerce2CreateSessionCreateReq", '?', "" },
{0X590A3229, nullptr, "sceNpCommerce2GetSessionInfo", '?', "" },
{0X6F1FE37F, nullptr, "sceNpCommerce2CreateCtx", '?', "" },
{0XA5A34EA4, nullptr, "sceNpCommerce2Term", '?', "" },
{0XAA4A1E3D, nullptr, "sceNpCommerce2GetProductInfoGetResult", '?', "" },
{0XBC61FFC8, nullptr, "sceNpCommerce2CreateSessionGetResult", '?', "" },
{0XC7F32242, nullptr, "sceNpCommerce2AbortReq", '?', "" },
{0XF2278B90, nullptr, "sceNpCommerce2GetGameSkuInfoFromGameProductInfo", '?', "" },
{0XF297AB9C, nullptr, "sceNpCommerce2DestroyCtx", '?', "" },
{0XFC30C19E, nullptr, "sceNpCommerce2InitGetProductInfoResult", '?', "" },
};
void Register_sceNpCommerce2()
{
RegisterHLEModule("sceNpCommerce2", ARRAY_SIZE(sceNpCommerce2), sceNpCommerce2);
}