/*
This file is part of jpcsp.
Jpcsp 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, either version 3 of the License, or
(at your option) any later version.
Jpcsp 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 for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import static jpcsp.HLE.kernel.types.SceKernelThreadInfo.THREAD_CALLBACK_USER_DEFINED;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.Modules;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer16;
import jpcsp.HLE.TPointer32;
import jpcsp.HLE.kernel.managers.SceUidManager;
import jpcsp.HLE.kernel.types.pspBaseCallback;
import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo;
import jpcsp.util.Utilities;
// Information based on
// https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/PSP2/Modules/sceNpMatching.cpp
public class sceNpMatching2 extends HLEModule {
public static Logger log = Modules.getLogger("sceNpMatching2");
private static final String idContextPurpose = "sceNpMatching2Context";
protected int defaultRequestCallbackFunction;
protected int defaultRequestCallbackArgument;
protected int defaultRequestTimeout;
protected int defaultRequestAppReqId;
protected int defaultRoomEventCallbackFunction;
protected int defaultRoomEventCallbackArgument;
protected int defaultRoomMessageCallbackFunction;
protected int defaultRoomMessageCallbackArgument;
protected int signalingCallbackFunction;
protected int signalingCallbackArgument;
protected Map<Integer, MatchingContext> contextMap = new HashMap<Integer, sceNpMatching2.MatchingContext>();
protected SysMemInfo dataBuffer;
private static class MatchingContext {
private boolean started;
public boolean isStarted() {
return started;
}
public void setStarted(boolean started) {
this.started = started;
}
}
private void notifyRequestCallback(int ctxId) {
MatchingContext context = contextMap.get(ctxId);
if (context == null || !context.isStarted() || defaultRequestCallbackFunction == 0) {
return;
}
pspBaseCallback requestCallback = Modules.ThreadManForUserModule.hleKernelCreateCallback(defaultRequestCallbackFunction, 6);
if (Modules.ThreadManForUserModule.hleKernelRegisterCallback(THREAD_CALLBACK_USER_DEFINED, requestCallback)) {
if (dataBuffer == null) {
dataBuffer = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "sceNpMatching2-DataBuffer", SysMemUserForUser.PSP_SMEM_Low, 128, 0);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2.notifyRequestCallback allocated dataBuffer %s", dataBuffer));
}
}
Memory mem = Emulator.getMemory();
mem.memset(dataBuffer.addr, (byte) 0, dataBuffer.size);
int reqId = 0x00011111; // Dummy value for debugging
int event = 0x2222; // Dummy value for debugging
int data = dataBuffer.addr;
int errorCode = 0;
// Write unknown values to data buffer for debugging
// The reqId contains a special value in the upper 16-bit
// which seems to be in range [0..16].
switch (reqId >>> 16) {
case 0:
case 6:
case 7:
case 9:
case 10:
case 11:
case 13:
case 16: {
// In those cases, the data buffer doesn't seem to be used
data = 0;
break;
}
case 1: {
mem.write16(data, (short) 0x3333);
mem.write8(data + 2, (byte) 0x44);
break;
}
case 2: {
int ptr = data + 8;
mem.write32(data, ptr); // Pointer to 64 bytes
mem.write32(data + 4, 0);
mem.write32(ptr, 0); // Pointer to next 64 bytes or NULL
for (int i = 4; i < 64; i += 4) {
mem.write32(ptr + i, 0x12345600 + i);
}
break;
}
case 3: {
mem.write32(data + 0, 0x33333333);
mem.write32(data + 4, 0x44444444);
mem.write32(data + 8, 0x55555555);
mem.write32(data + 12, 0x66666666);
break;
}
case 4:
case 5: {
int ptr = data + 4;
mem.write32(data, ptr); // Pointer to 64 bytes
mem.write16(ptr, (short) 0x3333);
for (int i = 4; i < 64; i += 4) {
mem.write32(ptr + i, 0x12345600 + i);
}
int ptr2 = ptr + 64;
mem.write32(ptr + 44, ptr2); // Pointer to 58 bytes
mem.write16(ptr2 + 56, (short) 0x4444);
break;
}
case 8: {
mem.write32(data, 1); // Seems to be a flag having value 0 or 1
break;
}
case 12:
case 14:
case 15: {
// Two 32-bit values (a 64-bit timestamp maybe?)
mem.write32(data + 0, 0x12345678);
mem.write32(data + 4, 0x9ABCDEF0);
break;
}
}
requestCallback.setArgument(0, ctxId);
requestCallback.setArgument(1, reqId);
requestCallback.setArgument(2, event);
requestCallback.setArgument(3, errorCode);
requestCallback.setArgument(4, data);
requestCallback.setArgument(5, defaultRequestCallbackArgument);
Modules.ThreadManForUserModule.hleKernelNotifyCallback(THREAD_CALLBACK_USER_DEFINED, requestCallback);
}
}
private void notifySignalingCallback(int ctxId) {
MatchingContext context = contextMap.get(ctxId);
if (context == null || !context.isStarted() || signalingCallbackFunction == 0) {
return;
}
pspBaseCallback signalingCallback = Modules.ThreadManForUserModule.hleKernelCreateCallback(signalingCallbackFunction, 8);
if (Modules.ThreadManForUserModule.hleKernelRegisterCallback(THREAD_CALLBACK_USER_DEFINED, signalingCallback)) {
long roomId = 0x123456789ABCDEF0L;
int peerMemberId = 0x1111;
int event = 0x5101; // 0x5101 - 0x5106
int errorCode = 0;
signalingCallback.setArgument(0, ctxId);
signalingCallback.setArgument(2, (int) roomId);
signalingCallback.setArgument(3, (int) (roomId >>> 32));
signalingCallback.setArgument(4, peerMemberId);
signalingCallback.setArgument(5, event);
signalingCallback.setArgument(6, errorCode);
signalingCallback.setArgument(7, signalingCallbackArgument);
Modules.ThreadManForUserModule.hleKernelNotifyCallback(THREAD_CALLBACK_USER_DEFINED, signalingCallback);
}
}
private void notifyRoomEventCallback(int ctxId) {
MatchingContext context = contextMap.get(ctxId);
if (context == null || !context.isStarted() || defaultRoomEventCallbackFunction == 0) {
return;
}
pspBaseCallback roomEventCallback = Modules.ThreadManForUserModule.hleKernelCreateCallback(defaultRoomEventCallbackFunction, 7);
if (Modules.ThreadManForUserModule.hleKernelRegisterCallback(THREAD_CALLBACK_USER_DEFINED, roomEventCallback)) {
long roomId = 0x123456789ABCDEF0L;
int event = 0x1101; // 0x1101 - 0x1109
int errorCode = 0;
roomEventCallback.setArgument(0, ctxId);
roomEventCallback.setArgument(2, (int) roomId);
roomEventCallback.setArgument(3, (int) (roomId >>> 32));
roomEventCallback.setArgument(4, event);
roomEventCallback.setArgument(5, errorCode);
roomEventCallback.setArgument(6, defaultRoomEventCallbackArgument);
Modules.ThreadManForUserModule.hleKernelNotifyCallback(THREAD_CALLBACK_USER_DEFINED, roomEventCallback);
}
}
private void notifyRoomMessageCallback(int ctxId) {
MatchingContext context = contextMap.get(ctxId);
if (context == null || !context.isStarted() || defaultRoomMessageCallbackFunction == 0) {
return;
}
pspBaseCallback roomMessageCallback = Modules.ThreadManForUserModule.hleKernelCreateCallback(defaultRoomMessageCallbackFunction, 8);
if (Modules.ThreadManForUserModule.hleKernelRegisterCallback(THREAD_CALLBACK_USER_DEFINED, roomMessageCallback)) {
if (dataBuffer == null) {
dataBuffer = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "sceNpMatching2-DataBuffer", SysMemUserForUser.PSP_SMEM_Low, 128, 0);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2.notifyRoomMessageCallback allocated dataBuffer %s", dataBuffer));
}
}
Memory mem = Emulator.getMemory();
mem.memset(dataBuffer.addr, (byte) 0, dataBuffer.size);
long roomId = 0x123456789ABCDEF0L;
int srcMemberId = 0x1111;
int event = 0x2101; // 0x2101, 0x2102
int data = dataBuffer.addr;
String dummyString = "Hello, world!";
int stringData = data + 24;
mem.write32(data + 12, stringData);
mem.write32(data + 16, dummyString.length());
mem.write32(data + 20, 1); // Seems to be a flag having value 0 or 1
Utilities.writeStringZ(mem, stringData, dummyString);
roomMessageCallback.setArgument(0, ctxId);
roomMessageCallback.setArgument(2, (int) roomId);
roomMessageCallback.setArgument(3, (int) (roomId >>> 32));
roomMessageCallback.setArgument(4, srcMemberId);
roomMessageCallback.setArgument(5, event);
roomMessageCallback.setArgument(6, data);
roomMessageCallback.setArgument(7, defaultRoomMessageCallbackArgument);
Modules.ThreadManForUserModule.hleKernelNotifyCallback(THREAD_CALLBACK_USER_DEFINED, roomMessageCallback);
}
}
@HLEUnimplemented
@HLEFunction(nid = 0x2E61F6E1, version = 150)
public int sceNpMatching2Init(int poolSize, int threadPriority, int cpuAffinityMask, int threadStackSize) {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x8BF37D8C, version = 150)
public int sceNpMatching2Term() {
// No parameters
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x12C5A111, version = 150)
public int sceNpMatching2GetRoomDataExternalList() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x1421514B, version = 150)
public int sceNpMatching2SetDefaultRoomEventOptParam(int ctxId, TPointer optParam) {
int callbackFunction = optParam.getValue32(0);
int callbackArgument = optParam.getValue32(4);
boolean unknownFlag1 = optParam.getValue32(8) != 0;
boolean unknownFlag2 = optParam.getValue32(12) != 0;
boolean unknownFlag3 = optParam.getValue32(16) != 0;
boolean unknownFlag4 = optParam.getValue32(20) != 0;
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2SetDefaultRoomEventOptParam callbackFunction=0x%08X, callbackArgument=0x%X, unknownFlag1=%b, unknownFlag2=%b, unknownFlag3=%b, unknownFlag4=%b", callbackFunction, callbackArgument, unknownFlag1, unknownFlag2, unknownFlag3, unknownFlag4));
}
defaultRoomEventCallbackFunction = callbackFunction;
defaultRoomEventCallbackArgument = callbackArgument;
notifyRoomEventCallback(ctxId);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x190FF903, version = 150)
public int sceNpMatching2ContextStart(int ctxId) {
MatchingContext context = contextMap.get(ctxId);
if (context == null) {
return -1;
}
context.setStarted(true);
notifyRequestCallback(ctxId);
notifySignalingCallback(ctxId);
notifyRoomEventCallback(ctxId);
notifyRoomMessageCallback(ctxId);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x22F38DAF, version = 150)
public int sceNpMatching2GetMemoryStat() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x2B3892FC, version = 150)
public int sceNpMatching2ContextStop(int ctxId) {
MatchingContext context = contextMap.get(ctxId);
if (context == null) {
return -1;
}
context.setStarted(false);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x3892E9A6, version = 150)
public int sceNpMatching2SignalingGetConnectionInfo() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x3DE70241, version = 150)
public int sceNpMatching2DestroyContext(int ctxId) {
if (dataBuffer != null) {
Modules.SysMemUserForUserModule.free(dataBuffer);
dataBuffer = null;
}
if (!SceUidManager.releaseId(ctxId, idContextPurpose)) {
return -1;
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x495E97BD, version = 150)
public int sceNpMatching2GrantRoomOwner() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x4EE3A8EC, version = 150)
public int sceNpMatching2GetServerInfo(int ctxId, TPointer16 serverIdAddr, TPointer32 unknown1, TPointer32 unknown2) {
int serverId = serverIdAddr.getValue();
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2GetServerInfo serverId=0x%X, unknown1: %s, unknown2: %s", serverId, Utilities.getMemoryDump(unknown1.getAddress(), 16), Utilities.getMemoryDump(unknown2.getAddress(), 20)));
}
unknown2.setValue(0x00010000);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x5030CC53, version = 150)
public int sceNpMatching2CreateContext(TPointer communicationId, TPointer passPhrase, TPointer16 ctxId, int unknown) {
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2CreateContext communicationId=%s, passPhrase=%s", Utilities.getMemoryDump(communicationId.getAddress(), 12), Utilities.getMemoryDump(passPhrase.getAddress(), 128)));
}
// Returning a ctxId in range [1..7]
int uid = SceUidManager.getNewId(idContextPurpose, 1, 7);
if (uid == SceUidManager.INVALID_ID) {
return -1;
}
contextMap.put(uid, new MatchingContext());
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2CreateContext returning 0x%X", uid));
}
ctxId.setValue(uid);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x55F7837F, version = 150)
public int sceNpMatching2SendRoomChatMessage() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x5C7DB6A4, version = 150)
public int sceNpMatching2GetRoomMemberDataInternalList() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x631682CC, version = 150)
public int sceNpMatching2SetDefaultRequestOptParam(int ctxId, TPointer optParam) {
int callbackFunction = optParam.getValue32(0);
int callbackArgument = optParam.getValue32(4);
int timeout = optParam.getValue32(8);
int appReqId = optParam.getValue16(12);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2SetDefaultRequestOptParam callbackFunction=0x%08X, callbackArgument=0x%X, timeout=0x%X, appReqId=0x%X", callbackFunction, callbackArgument, timeout, appReqId));
}
defaultRequestCallbackFunction = callbackFunction;
defaultRequestCallbackArgument = callbackArgument;
defaultRequestTimeout = timeout;
defaultRequestAppReqId = appReqId;
notifyRequestCallback(ctxId);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x6D6D0C75, version = 150)
public int sceNpMatching2SignalingGetConnectionStatus() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x7BBFC427, version = 150)
public int sceNpMatching2JoinRoom() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x7D1D5F5E, version = 150)
public int sceNpMatching2SetUserInfo() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x7DAA8A90, version = 150)
public int sceNpMatching2SetRoomMemberDataInternal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x80F61558, version = 150)
public int sceNpMatching2GetRoomMemberIdListLocal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x81C13E6D, version = 150)
public int sceNpMatching2SearchRoom() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x8CD109E7, version = 150)
public int sceNpMatching2SignalingGetPeerNetInfo() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x9462C05A, version = 150)
public int sceNpMatching2SignalingCancelPeerNetInfo() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x97529ECC, version = 150)
public int sceNpMatching2KickoutRoomMember() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x9A67F5D0, version = 150)
public int sceNpMatching2SetSignalingOptParam() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xA3C298D1, version = 150)
public int sceNpMatching2RegisterSignalingCallback(int ctxId, TPointer callbackFunction, int callbackArgument) {
signalingCallbackFunction = callbackFunction.getAddress();
signalingCallbackArgument = callbackArgument;
notifySignalingCallback(ctxId);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xA53E7C69, version = 150)
public int sceNpMatching2GetWorldInfoList(int ctxId, TPointer16 serverIdAddr, @CanBeNull TPointer optParam, TPointer32 assignedReqId) {
if (optParam.isNotNull()) {
int callbackFunction = optParam.getValue32(0);
int callbackArgument = optParam.getValue32(4);
int timeout = optParam.getValue32(8);
int appReqId = optParam.getValue16(12);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2GetWorldInfoList callbackFunction=0x%08X, callbackArgument=0x%X, timeout=0x%X, appReqId=0x%X", callbackFunction, callbackArgument, timeout, appReqId));
}
}
int serverId = serverIdAddr.getValue();
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2GetWorldInfoList serverId=0x%X", serverId));
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xA5775DBF, version = 150)
public int sceNpMatching2GetRoomMemberDataInternal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xAAD0946A, version = 150)
public int sceNpMatching2CreateJoinRoom() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xC7E72EC5, version = 150)
public int sceNpMatching2GetSignalingOptParamLocal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xC870535A, version = 150)
public int sceNpMatching2LeaveRoom() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xC8FC5D41, version = 150)
public int sceNpMatching2GetUserInfoList() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xD13491AB, version = 150)
public int sceNpMatching2SetDefaultRoomMessageOptParam(int ctxId, TPointer optParam) {
int callbackFunction = optParam.getValue32(0);
int callbackArgument = optParam.getValue32(4);
if (log.isDebugEnabled()) {
log.debug(String.format("sceNpMatching2SetDefaultRoomMessageOptParam callbackFunction=0x%08X, callbackArgument=0x%X", callbackFunction, callbackArgument));
}
defaultRoomMessageCallbackFunction = callbackFunction;
defaultRoomMessageCallbackArgument = callbackArgument;
notifyRoomMessageCallback(ctxId);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xD7D4AEB2, version = 150)
public int sceNpMatching2SetRoomDataExternal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xDFEDB642, version = 150)
public int sceNpMatching2SignalingGetPeerNetInfoResult() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xE313E586, version = 150)
public int sceNpMatching2GetRoomDataInternal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xE6C93DBD, version = 150)
public int sceNpMatching2SetRoomDataInternal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xEF683F4F, version = 150)
public int sceNpMatching2GetRoomDataInternalLocal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xF22C7ADC, version = 150)
public int sceNpMatching2GetRoomMemberDataInternalLocal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xF47342FC, version = 150)
public int sceNpMatching2GetServerIdListLocal(int ctxId, TPointer16 serverIds, int maxServerIds) {
// Return dummy values for debugging
for (int i = 0; i < maxServerIds; i++) {
serverIds.setValue(i * 2, i + 0x1234);
}
return maxServerIds;
}
@HLEUnimplemented
@HLEFunction(nid = 0xF739BE92, version = 150)
public int sceNpMatching2GetRoomPasswordLocal() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xF940D9AD, version = 150)
public int sceNpMatching2SendRoomMessage() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xFADBA9DB, version = 150)
public int sceNpMatching2AbortRequest() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xFBF494C0, version = 150)
public int sceNpMatching2GetRoomMemberDataExternalList() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xFF32EA05, version = 150)
public int sceNpMatching2SignalingGetLocalNetInfo() {
return 0;
}
}