1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 // This is pretty much a stub implementation. Doesn't actually do anything, just tries to return values
19 // to keep games happy anyway.
20 
21 #include <mutex>
22 #include <deque>
23 #include <map>
24 #include <StringUtils.h>
25 #include "Core/MemMapHelpers.h"
26 #include <Core/CoreTiming.h>
27 #include "Core/HLE/HLE.h"
28 #include "Core/HLE/FunctionWrappers.h"
29 
30 #include "Core/HLE/sceNp.h"
31 #include <Core/HLE/sceRtc.h>
32 
33 
34 bool npAuthInited = false;
35 SceNpAuthMemoryStat npAuthMemStat = {};
36 std::string serviceId = "";
37 
38 int parentalControl = PARENTAL_CONTROL_ENABLED;
39 int userAge = 24; // faking user Age to 24 yo
40 int chatRestriction = 0; // default/initial value on Patapon 3 is 1 (restricted boolean?)
41 std::string onlineId = "DummyOnlineId";
42 std::string avatarUrl = "http://DummyAvatarUrl";
43 
44 std::recursive_mutex npAuthEvtMtx;
45 std::deque<NpAuthArgs> npAuthEvents;
46 std::map<int, NpAuthHandler> npAuthHandlers;
47 
48 
49 // Tickets data are in big-endian based on captured packets
writeTicketParam(u8 * buffer,const u16_be type,const char * data=nullptr,const u16_be size=0)50 int writeTicketParam(u8* buffer, const u16_be type, const char* data = nullptr, const u16_be size = 0) {
51 	if (buffer == nullptr) return 0;
52 
53 	u16_be sz = (data == nullptr)? static_cast<u16_be>(0): size;
54 	memcpy(buffer, &type, 2);
55 	memcpy(buffer + 2, &sz, 2);
56 	if (sz>0) memcpy(buffer + 4, data, sz);
57 
58 	return sz + 4;
59 }
60 
writeTicketStringParam(u8 * buffer,const u16_be type,const char * data=nullptr,const u16_be size=0)61 int writeTicketStringParam(u8* buffer, const u16_be type, const char* data = nullptr, const u16_be size = 0) {
62 	if (buffer == nullptr) return 0;
63 
64 	u16_be sz = (data == nullptr) ? static_cast<u16_be>(0) : size;
65 	memcpy(buffer, &type, 2);
66 	memcpy(buffer + 2, &sz, 2);
67 	if (sz > 0) {
68 		memset(buffer + 4, 0, sz);
69 		truncate_cpy((char*)buffer + 4, sz, data);
70 	}
71 	return sz + 4;
72 }
73 
writeTicketU32Param(u8 * buffer,const u16_be type,const u32_be data)74 int writeTicketU32Param(u8* buffer, const u16_be type, const u32_be data) {
75 	if (buffer == nullptr) return 0;
76 
77 	u16_be sz = 4;
78 	memcpy(buffer, &type, 2);
79 	memcpy(buffer + 2, &sz, 2);
80 	memcpy(buffer + 4, &data, 4);
81 
82 	return sz + 4;
83 }
84 
writeTicketU64Param(u8 * buffer,const u16_be type,const u64_be data)85 int writeTicketU64Param(u8* buffer, const u16_be type, const u64_be data) {
86 	if (buffer == nullptr) return 0;
87 
88 	u16_be sz = 8;
89 	memcpy(buffer, &type, 2);
90 	memcpy(buffer + 2, &sz, 2);
91 	memcpy(buffer + 4, &data, sz);
92 
93 	return sz + 4;
94 }
95 
notifyNpAuthHandlers(u32 id,u32 result,u32 argAddr)96 void notifyNpAuthHandlers(u32 id, u32 result, u32 argAddr) {
97 	std::lock_guard<std::recursive_mutex> npAuthGuard(npAuthEvtMtx);
98 	npAuthEvents.push_back({ id, result, argAddr });
99 }
100 
sceNpInit()101 static int sceNpInit()
102 {
103 	ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__);
104 	return 0;
105 }
106 
sceNpTerm()107 static int sceNpTerm()
108 {
109 	// No parameters
110 	ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__);
111 	return 0;
112 }
113 
sceNpGetContentRatingFlag(u32 parentalControlAddr,u32 userAgeAddr)114 static int sceNpGetContentRatingFlag(u32 parentalControlAddr, u32 userAgeAddr)
115 {
116 	WARN_LOG(HLE, "UNTESTED %s(%08x, %08x)", __FUNCTION__, parentalControlAddr, userAgeAddr);
117 
118 	if (!Memory::IsValidAddress(parentalControlAddr) || !Memory::IsValidAddress(userAgeAddr))
119 		return hleLogError(HLE, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
120 
121 	Memory::Write_U32(parentalControl, parentalControlAddr);
122 	Memory::Write_U32(userAge, userAgeAddr);
123 
124 	return 0;
125 }
126 
sceNpGetChatRestrictionFlag(u32 flagAddr)127 static int sceNpGetChatRestrictionFlag(u32 flagAddr)
128 {
129 	WARN_LOG(HLE, "UNTESTED %s(%08x)", __FUNCTION__, flagAddr);
130 
131 	if (!Memory::IsValidAddress(flagAddr))
132 		return hleLogError(HLE, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
133 
134 	Memory::Write_U32(chatRestriction, flagAddr);
135 
136 	return 0;
137 }
138 
139 const HLEFunction sceNp[] = {
140 	{0X857B47D3, &WrapI_V<sceNpInit>,					"sceNpInit",					'i', ""   },
141 	{0X37E1E274, &WrapI_V<sceNpTerm>,					"sceNpTerm",					'i', ""   },
142 	{0XBB069A87, &WrapI_UU<sceNpGetContentRatingFlag>,	"sceNpGetContentRatingFlag",	'i', "xx" },
143 	{0X1D60AE4B, &WrapI_U<sceNpGetChatRestrictionFlag>,	"sceNpGetChatRestrictionFlag",	'i', "x"  },
144 };
145 
Register_sceNp()146 void Register_sceNp()
147 {
148 	RegisterModule("sceNp", ARRAY_SIZE(sceNp), sceNp);
149 }
150 
sceNpAuthTerm()151 static int sceNpAuthTerm()
152 {
153 	// No parameters
154 	ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__);
155 	npAuthInited = false;
156 	return 0;
157 }
158 
sceNpAuthInit(u32 poolSize,u32 stackSize,u32 threadPrio)159 static int sceNpAuthInit(u32 poolSize, u32 stackSize, u32 threadPrio)
160 {
161 	ERROR_LOG(HLE, "UNIMPL %s(%d, %d, %d)", __FUNCTION__, poolSize, stackSize, threadPrio);
162 	npAuthMemStat.npMemSize = poolSize;
163 	npAuthMemStat.npMaxMemSize = poolSize / 2;    // Dummy
164 	npAuthMemStat.npFreeMemSize = poolSize - 16;  // Dummy.
165 	npAuthEvents.clear();
166 
167 	npAuthInited = true;
168 	return 0;
169 }
170 
sceNpAuthGetMemoryStat(u32 memStatAddr)171 static int sceNpAuthGetMemoryStat(u32 memStatAddr)
172 {
173 	ERROR_LOG(HLE, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr);
174 
175 	if (!Memory::IsValidAddress(memStatAddr))
176 		return hleLogError(HLE, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
177 
178 	Memory::WriteStruct(memStatAddr, &npAuthMemStat);
179 
180 	return 0;
181 }
182 
183 /*
184 "Authenticating matching server usage license" on Patapon 3. Could be waiting for a state change for eternity? probably need to trigger a callback handler?
185 TODO: Login to "https://auth.np.ac.playstation.net/nav/auth" based on https://www.psdevwiki.com/ps3/Online_Connections
186 param seems to be a struct where offset:
187 	+00: 32-bit is the size of the struct (ie. 36 bytes),
188 	+04: 32-bit is also a small number (ie. 3) a mode/event/flag/version may be?,
189 	+08: 32-bit is a pointer to a productId? (ie. "EP9000-UCES01421_00"),
190 	+0C: 4x 32-bit reserved? all zero
191 	+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)
192 	+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?
193 return value >= 0 and <0 seems to be stored at a different location by the game (valid result vs error code?)
194 */
sceNpAuthCreateStartRequest(u32 paramAddr)195 static int sceNpAuthCreateStartRequest(u32 paramAddr)
196 {
197 	WARN_LOG(HLE, "UNTESTED %s(%08x) at %08x", __FUNCTION__, paramAddr, currentMIPS->pc);
198 
199 	if (!Memory::IsValidAddress(paramAddr))
200 		return hleLogError(HLE, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
201 
202 	SceNpAuthRequestParameter params = {};
203 	int size = Memory::Read_U32(paramAddr);
204 	Memory::Memcpy(&params, paramAddr, size);
205 	serviceId = Memory::GetCharPointer(params.serviceIdAddr);
206 
207 	INFO_LOG(HLE, "%s - Max Version: %u.%u", __FUNCTION__, params.version.major, params.version.minor);
208 	INFO_LOG(HLE, "%s - Service ID: %s", __FUNCTION__, Memory::GetCharPointer(params.serviceIdAddr));
209 	INFO_LOG(HLE, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(params.entitlementIdAddr));
210 	INFO_LOG(HLE, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, Memory::GetCharPointer(params.cookieAddr));
211 
212 	u32 retval = 0;
213 	if (params.size >= 32 && params.ticketCbAddr != 0) {
214 		bool foundHandler = false;
215 
216 		struct NpAuthHandler handler;
217 		memset(&handler, 0, sizeof(handler));
218 
219 		while (npAuthHandlers.find(retval) != npAuthHandlers.end())
220 			++retval;
221 
222 		handler.entryPoint = params.ticketCbAddr;
223 		handler.argument = params.cbArgAddr;
224 
225 		for (std::map<int, NpAuthHandler>::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); it++) {
226 			if (it->second.entryPoint == handler.entryPoint) {
227 				foundHandler = true;
228 				retval = it->first;
229 				break;
230 			}
231 		}
232 
233 		if (!foundHandler && Memory::IsValidAddress(handler.entryPoint)) {
234 			npAuthHandlers[retval] = handler;
235 			WARN_LOG(SCENET, "%s - Added handler(%08x, %08x) : %d", __FUNCTION__, handler.entryPoint, handler.argument, retval);
236 		}
237 		else {
238 			ERROR_LOG(SCENET, "%s - Same handler(%08x, %08x) already exists", __FUNCTION__, handler.entryPoint, handler.argument);
239 		}
240 		// Patapon 3 will only Abort & Destroy AuthRequest if the ID is larger than 0. Is 0 a valid request id?
241 		retval++;
242 
243 		// 1st Arg usually either an ID returned from Create/AddHandler function or an Event ID if the game is expecting a sequence of events.
244 		// 2nd Arg seems to be used if not a negative number and exits the handler if it's negative (error code?)
245 		// 3rd Arg seems to be a data (ie. 92 bytes of data?) pointer and tested for null within callback handler (optional callback args?)
246 		u32 ticketLength = 248; // default ticket length? should be updated using the ticket length returned from login
247 		notifyNpAuthHandlers(retval, ticketLength, (params.size >= 36) ? params.cbArgAddr : 0);
248 	}
249 
250 	//hleDelayResult(0, "give time", 500000);
251 	return retval;
252 }
253 
254 // Used within callback of sceNpAuthCreateStartRequest (arg1 = callback's args[0], arg2 = output structPtr?, arg3 = callback's args[1])
255 // Is this using request id for Arg1 or cbId?
256 // JPCSP is using length = 248 for dummy ticket
sceNpAuthGetTicket(u32 requestId,u32 bufferAddr,u32 length)257 static int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length)
258 {
259 	ERROR_LOG(HLE, "UNIMPL %s(%d, %08x, %d) at %08x", __FUNCTION__, requestId, bufferAddr, length, currentMIPS->pc);
260 
261 	if (!Memory::IsValidAddress(bufferAddr))
262 		return hleLogError(HLE, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
263 
264 	int result = length;
265 	Memory::Memset(bufferAddr, 0, length);
266 	SceNpTicket ticket = {};
267 	// Dummy Login ticket returned as Login response. Dummy ticket contents were taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket
268 	ticket.header.version = TICKET_VER_2_1;
269 	ticket.header.size = 0xF0; // size excluding the header
270 	u8* buf = Memory::GetPointer(bufferAddr + sizeof(ticket));
271 	int ofs = 0;
272 	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?
273 	ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, 0x0100); // a flags?
274 	PSPTimeval tv;
275 	__RtcTimeOfDay(&tv);
276 	u64 now = 1000ULL*tv.tv_sec + tv.tv_usec/1000ULL; // in milliseconds, since 1900?
277 	ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_DATE, now);
278 	ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_DATE, now + 10 * 60 * 1000); // now + 10 minutes, expired time?
279 	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?
280 	ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, onlineId.c_str(), 32); // username
281 	ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "fr\0\2", 4); // SceNpCountryCode ? ie. "fr" + 00 02
282 	ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, "c9", 4); // 2-char code? related to country/lang code? ie. "c9" + 00 00
283 	ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, serviceId.c_str(), 24);
284 	int status = 0;
285 	if (parentalControl == PARENTAL_CONTROL_ENABLED) {
286 		status |= STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED;
287 	}
288 	status |= (userAge & 0x7F) << 24;
289 	ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, status);
290 	ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL);
291 	ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL);
292 	ticket.section.type = SECTION_TYPE_BODY;
293 	ticket.section.size = ofs;
294 	Memory::WriteStruct(bufferAddr, &ticket);
295 	SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on ver 2.1 containing 4-chars ASCII + 20-chars ASCII
296 	Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, &footer);
297 	ofs += sizeof(footer);
298 	ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4);
299 	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);
300 	u8 unknownBytes[36] = {};
301 	Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, unknownBytes);
302 
303 	result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes
304 
305 	return result;
306 }
307 
308 // 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?)
309 // 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)
sceNpAuthGetEntitlementById(u32 ticketBufferAddr,u32 ticketLength,u32 entitlementIdAddr,u32 arg4)310 static int sceNpAuthGetEntitlementById(u32 ticketBufferAddr, u32 ticketLength, u32 entitlementIdAddr, u32 arg4)
311 {
312 	ERROR_LOG(HLE, "UNIMPL %s(%08x, %d, %08x, %d)", __FUNCTION__, ticketBufferAddr, ticketLength, entitlementIdAddr, arg4);
313 	INFO_LOG(HLE, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(entitlementIdAddr));
314 	// Do we return the entitlement through function result? or update the ticket content? or replace the arg3 data with SceNpEntitlement struct?
315 	return 1; // dummy value assuming it's a boolean/flag, since we don't know how to return the entitlement result yet
316 }
317 
sceNpAuthAbortRequest(int requestId)318 static int sceNpAuthAbortRequest(int requestId)
319 {
320 	WARN_LOG(HLE, "UNTESTED %s(%i)", __FUNCTION__, requestId);
321 	// TODO: Disconnect HTTPS connection & cancel the callback event
322 	std::lock_guard<std::recursive_mutex> npAuthGuard(npAuthEvtMtx);
323 	for (auto it = npAuthEvents.begin(); it != npAuthEvents.end(); ) {
324 		(it->data[0] == requestId) ? it = npAuthEvents.erase(it) : ++it;
325 	}
326 	return 0;
327 }
328 
sceNpAuthDestroyRequest(int requestId)329 static int sceNpAuthDestroyRequest(int requestId)
330 {
331 	WARN_LOG(HLE, "UNTESTED %s(%i)", __FUNCTION__, requestId);
332 	// Remove callback handler
333 	int handlerID = requestId - 1;
334 	if (npAuthHandlers.find(handlerID) != npAuthHandlers.end()) {
335 		npAuthHandlers.erase(handlerID);
336 		WARN_LOG(SCENET, "%s: Deleted handler %d", __FUNCTION__, handlerID);
337 	}
338 	else {
339 		ERROR_LOG(SCENET, "%s: Invalid request ID %d", __FUNCTION__, requestId);
340 	}
341 	// Patapon 3 is checking for error code 0x80550402
342 	return 0;
343 }
344 
345 
346 const HLEFunction sceNpAuth[] = {
347 	{0X4EC1F667, &WrapI_V<sceNpAuthTerm>,						"sceNpAuthTerm",				'i', ""     },
348 	{0XA1DE86F8, &WrapI_UUU<sceNpAuthInit>,						"sceNpAuthInit",				'i', "xxx"  },
349 	{0XF4531ADC, &WrapI_U<sceNpAuthGetMemoryStat>,				"sceNpAuthGetMemoryStat",		'i', "x"    },
350 	{0XCD86A656, &WrapI_U<sceNpAuthCreateStartRequest>,			"sceNpAuthCreateStartRequest",	'i', "x"    },
351 	{0X3F1C1F70, &WrapI_UUU<sceNpAuthGetTicket>,				"sceNpAuthGetTicket",			'i', "xxx"  },
352 	{0X6900F084, &WrapI_UUUU<sceNpAuthGetEntitlementById>,		"sceNpAuthGetEntitlementById",	'i', "xxxx" },
353 	{0XD99455DD, &WrapI_I<sceNpAuthAbortRequest>,				"sceNpAuthAbortRequest",		'i', "i"    },
354 	{0X72BB0467, &WrapI_I<sceNpAuthDestroyRequest>,				"sceNpAuthDestroyRequest",		'i', "i"    },
355 };
356 
Register_sceNpAuth()357 void Register_sceNpAuth()
358 {
359 	RegisterModule("sceNpAuth", ARRAY_SIZE(sceNpAuth), sceNpAuth);
360 }
361 
sceNpServiceTerm()362 static int sceNpServiceTerm()
363 {
364 	// No parameters
365 	ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__);
366 	return 0;
367 }
368 
sceNpServiceInit(u32 poolSize,u32 stackSize,u32 threadPrio)369 static int sceNpServiceInit(u32 poolSize, u32 stackSize, u32 threadPrio)
370 {
371 	ERROR_LOG(HLE, "UNIMPL %s(%08x, %08x, %08x)", __FUNCTION__, poolSize, stackSize, threadPrio);
372 	return 0;
373 }
374 
sceNpLookupCreateTransactionCtx(u32 lookupTitleCtxIdAddr)375 static int sceNpLookupCreateTransactionCtx(u32 lookupTitleCtxIdAddr)
376 {
377 	ERROR_LOG(HLE, "UNIMPL %s(%08x)", __FUNCTION__, lookupTitleCtxIdAddr);
378 	INFO_LOG(SCENET, "%s - Title ID: %s", __FUNCTION__, Memory::GetCharPointer(lookupTitleCtxIdAddr));
379 	// Patapon 3 will only Destroy if returned Id > 0. Is 0 a valid id?
380 	return 1; // returning dummy transaction id
381 }
382 
383 // transId: id returned from sceNpLookupCreateTransactionCtx
sceNpLookupDestroyTransactionCtx(u32 transId)384 static int sceNpLookupDestroyTransactionCtx(u32 transId)
385 {
386 	ERROR_LOG(HLE, "UNIMPL %s(%d)", __FUNCTION__, transId);
387 	return 0;
388 }
389 
390 // transId: id returned from sceNpLookupCreateTransactionCtx
391 // Patapon 3 always set Arg5 to 0
392 // Addr args have something to do with GameUpdate?
sceNpLookupTitleSmallStorage(u32 transId,u32 arg2Addr,u32 arg3,u32 arg4Addr,u32 arg5)393 static int sceNpLookupTitleSmallStorage(u32 transId, u32 arg2Addr, u32 arg3, u32 arg4Addr, u32 arg5)
394 {
395 	ERROR_LOG(HLE, "UNIMPL %s(%d, %08x, %08x, %08x, %08x)", __FUNCTION__, transId, arg2Addr, arg3, arg4Addr, arg5);
396 	return 0;
397 }
398 
399 const HLEFunction sceNpService[] = {
400 	{0X00ACFAC3, &WrapI_V<sceNpServiceTerm>,					"sceNpServiceTerm",						'i', ""      },
401 	{0X0F8F5821, &WrapI_UUU<sceNpServiceInit>,					"sceNpServiceInit",						'i', "xxx"   },
402 	{0X5494274B, &WrapI_U<sceNpLookupCreateTransactionCtx>,		"sceNpLookupCreateTransactionCtx",		'i', "x"     },
403 	{0XA670D3A3, &WrapI_U<sceNpLookupDestroyTransactionCtx>,	"sceNpLookupDestroyTransactionCtx",		'i', "x"     },
404 	{0XC76F55ED, &WrapI_UUUUU<sceNpLookupTitleSmallStorage>,	"sceNpLookupTitleSmallStorage",			'i', "xxxxx" },
405 };
406 
Register_sceNpService()407 void Register_sceNpService()
408 {
409 	RegisterModule("sceNpService", ARRAY_SIZE(sceNpService), sceNpService);
410 }
411 
412 const HLEFunction sceNpCommerce2[] = {
413 	{0X005B5F20, nullptr,                            "sceNpCommerce2GetProductInfoStart",				'?', ""   },
414 	{0X0E9956E3, nullptr,                            "sceNpCommerce2Init",								'?', ""   },
415 	{0X1888A9FE, nullptr,                            "sceNpCommerce2DestroyReq",						'?', ""   },
416 	{0X1C952DCB, nullptr,                            "sceNpCommerce2GetGameProductInfo",				'?', ""   },
417 	{0X2B25F6E9, nullptr,                            "sceNpCommerce2CreateSessionStart",				'?', ""   },
418 	{0X3371D5F1, nullptr,                            "sceNpCommerce2GetProductInfoCreateReq",			'?', ""   },
419 	{0X4ECD4503, nullptr,                            "sceNpCommerce2CreateSessionCreateReq",			'?', ""   },
420 	{0X590A3229, nullptr,                            "sceNpCommerce2GetSessionInfo",					'?', ""   },
421 	{0X6F1FE37F, nullptr,                            "sceNpCommerce2CreateCtx",							'?', ""   },
422 	{0XA5A34EA4, nullptr,                            "sceNpCommerce2Term",								'?', ""   },
423 	{0XAA4A1E3D, nullptr,                            "sceNpCommerce2GetProductInfoGetResult",			'?', ""   },
424 	{0XBC61FFC8, nullptr,                            "sceNpCommerce2CreateSessionGetResult",			'?', ""   },
425 	{0XC7F32242, nullptr,                            "sceNpCommerce2AbortReq",							'?', ""   },
426 	{0XF2278B90, nullptr,                            "sceNpCommerce2GetGameSkuInfoFromGameProductInfo",	'?', ""   },
427 	{0XF297AB9C, nullptr,                            "sceNpCommerce2DestroyCtx",						'?', ""   },
428 	{0XFC30C19E, nullptr,                            "sceNpCommerce2InitGetProductInfoResult",			'?', ""   },
429 };
430 
Register_sceNpCommerce2()431 void Register_sceNpCommerce2()
432 {
433 	RegisterModule("sceNpCommerce2", ARRAY_SIZE(sceNpCommerce2), sceNpCommerce2);
434 }
435