/*
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.Allegrex.Common._s0;
import static jpcsp.HLE.modules.sceNetAdhocctl.fillNextPointersInLinkedList;
import static jpcsp.util.Utilities.writeBytes;
import java.util.HashMap;
import java.util.List;
import jpcsp.HLE.BufferInfo;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer32;
import jpcsp.Processor;
import jpcsp.HLE.Modules;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.pspNetMacAddress;
import jpcsp.network.INetworkAdapter;
import jpcsp.network.adhoc.MatchingObject;
import jpcsp.util.Utilities;
import org.apache.log4j.Logger;
public class sceNetAdhocMatching extends HLEModule {
public static Logger log = Modules.getLogger("sceNetAdhocMatching");
protected HashMap<Integer, MatchingObject> matchingObjects;
public static final int loopThreadRegisterArgument = _s0; // $s0 is preserved across calls
private boolean isInitialized;
/**
* Matching events used in pspAdhocMatchingCallback
*/
/** Hello event. optdata contains data if optlen > 0. */
public static final int PSP_ADHOC_MATCHING_EVENT_HELLO = 1;
/** Join request. optdata contains data if optlen > 0. */
public static final int PSP_ADHOC_MATCHING_EVENT_JOIN = 2;
/** Target left matching. */
public static final int PSP_ADHOC_MATCHING_EVENT_LEFT = 3;
/** Join request rejected. */
public static final int PSP_ADHOC_MATCHING_EVENT_REJECT = 4;
/** Join request cancelled. */
public static final int PSP_ADHOC_MATCHING_EVENT_CANCEL = 5;
/** Join request accepted. optdata contains data if optlen > 0. */
public static final int PSP_ADHOC_MATCHING_EVENT_ACCEPT = 6;
/** Matching is complete. */
public static final int PSP_ADHOC_MATCHING_EVENT_COMPLETE = 7;
/** Ping timeout event. */
public static final int PSP_ADHOC_MATCHING_EVENT_TIMEOUT = 8;
/** Error event. */
public static final int PSP_ADHOC_MATCHING_EVENT_ERROR = 9;
/** Peer disconnect event. */
public static final int PSP_ADHOC_MATCHING_EVENT_DISCONNECT = 10;
/** Data received event. optdata contains data if optlen > 0. */
public static final int PSP_ADHOC_MATCHING_EVENT_DATA = 11;
/** Data acknowledged event. */
public static final int PSP_ADHOC_MATCHING_EVENT_DATA_CONFIRM = 12;
/** Data timeout event. */
public static final int PSP_ADHOC_MATCHING_EVENT_DATA_TIMEOUT = 13;
/** Internal ping message. */
public static final int PSP_ADHOC_MATCHING_EVENT_INTERNAL_PING = 100;
/**
* Matching modes used in sceNetAdhocMatchingCreate
*/
/** Host */
public static final int PSP_ADHOC_MATCHING_MODE_HOST = 1;
/** Client */
public static final int PSP_ADHOC_MATCHING_MODE_CLIENT = 2;
/** Peer to peer */
public static final int PSP_ADHOC_MATCHING_MODE_PTP = 3;
@Override
public void start() {
matchingObjects = new HashMap<Integer, MatchingObject>();
isInitialized = false;
super.start();
}
protected INetworkAdapter getNetworkAdapter() {
return Modules.sceNetModule.getNetworkAdapter();
}
public int checkMatchingId(int matchingId) {
checkInitialized();
if (!matchingObjects.containsKey(matchingId)) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_NET_ADHOC_INVALID_MATCHING_ID);
}
return matchingId;
}
public void hleNetAdhocMatchingEventThread(Processor processor) {
int matchingId = processor.cpu.getRegister(loopThreadRegisterArgument);
if (log.isTraceEnabled()) {
log.trace(String.format("hleNetAdhocMatchingEventThread matchingId=%d", matchingId));
}
MatchingObject matchingObject = matchingObjects.get(matchingId);
if (matchingObject != null && matchingObject.eventLoop()) {
Modules.ThreadManForUserModule.hleKernelDelayThread(10000, false);
} else {
// Exit thread with status 0
processor.cpu._v0 = 0;
Modules.ThreadManForUserModule.hleKernelExitDeleteThread();
}
}
public void hleNetAdhocMatchingInputThread(Processor processor) {
int matchingId = processor.cpu.getRegister(loopThreadRegisterArgument);
if (log.isTraceEnabled()) {
log.trace(String.format("hleNetAdhocMatchingInputThread matchingId=%d", matchingId));
}
MatchingObject matchingObject = matchingObjects.get(matchingId);
if (matchingObject != null && matchingObject.inputLoop()) {
Modules.ThreadManForUserModule.hleKernelDelayThread(10000, false);
} else {
// Exit thread with status 0
processor.cpu._v0 = 0;
Modules.ThreadManForUserModule.hleKernelExitDeleteThread();
}
}
private static String getModeName(int mode) {
switch (mode) {
case PSP_ADHOC_MATCHING_MODE_HOST: return "HOST";
case PSP_ADHOC_MATCHING_MODE_CLIENT: return "CLIENT";
case PSP_ADHOC_MATCHING_MODE_PTP: return "PTP";
}
return String.format("Unknown mode %d", mode);
}
protected void checkInitialized() {
if (!isInitialized) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED);
}
}
/**
* Initialize the Adhoc matching library
*
* @param memsize - Internal memory pool size. Lumines uses 0x20000
*
* @return 0 on success, < 0 on error
*/
@HLEFunction(nid = 0x2A2A1E07, version = 150)
public int sceNetAdhocMatchingInit(int memsize) {
if (isInitialized) {
return SceKernelErrors.ERROR_NET_ADHOC_MATCHING_ALREADY_INITIALIZED;
}
isInitialized = true;
return 0;
}
/**
* Terminate the Adhoc matching library
*
* @return 0 on success, < 0 on error
*/
@HLEFunction(nid = 0x7945ECDA, version = 150)
public int sceNetAdhocMatchingTerm() {
isInitialized = false;
return 0;
}
/**
* Create an Adhoc matching object
*
* @param mode - One of ::pspAdhocMatchingModes
* @param maxpeers - Maximum number of peers to match (only used when mode is PSP_ADHOC_MATCHING_MODE_HOST)
* @param port - Port. Lumines uses 0x22B
* @param bufsize - Receiving buffer size
* @param hellodelay - Hello message send delay in microseconds (only used when mode is PSP_ADHOC_MATCHING_MODE_HOST or PSP_ADHOC_MATCHING_MODE_PTP)
* @param pingdelay - Ping send delay in microseconds. Lumines uses 0x5B8D80 (only used when mode is PSP_ADHOC_MATCHING_MODE_HOST or PSP_ADHOC_MATCHING_MODE_PTP)
* @param initcount - Initial count of the of the resend counter. Lumines uses 3
* @param msgdelay - Message send delay in microseconds
* @param callback - Callback to be called for matching
*
* @return ID of object on success, < 0 on error.
*/
@HLEFunction(nid = 0xCA5EDA6F, version = 150)
public int sceNetAdhocMatchingCreate(int mode, int maxPeers, int port, int bufSize, int helloDelay, int pingDelay, int initCount, int msgDelay, @CanBeNull TPointer callback) {
checkInitialized();
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetAdhocMatchingCreate mode=%s", getModeName(mode)));
}
MatchingObject matchingObject = getNetworkAdapter().createMatchingObject();
matchingObject.setMode(mode);
matchingObject.setMaxPeers(maxPeers);
matchingObject.setPort(port);
matchingObject.setBufSize(bufSize);
matchingObject.setHelloDelay(helloDelay);
matchingObject.setPingDelay(pingDelay);
matchingObject.setInitCount(initCount);
matchingObject.setMsgDelay(msgDelay);
matchingObject.setCallback(callback.getAddress());
matchingObject.create();
matchingObjects.put(matchingObject.getId(), matchingObject);
return matchingObject.getId();
}
/**
* Start a matching object
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param evthpri - Priority of the event handler thread. Lumines uses 0x10
* @param evthstack - Stack size of the event handler thread. Lumines uses 0x2000
* @param inthpri - Priority of the input handler thread. Lumines uses 0x10
* @param inthstack - Stack size of the input handler thread. Lumines uses 0x2000
* @param optlen - Size of hellodata
* @param optdata - Pointer to block of data passed to callback
*
* @return 0 on success, < 0 on error
*/
@HLEFunction(nid = 0x93EF3843, version = 150)
public int sceNetAdhocMatchingStart(@CheckArgument("checkMatchingId") int matchingId, int evthPri, int evthStack, int inthPri, int inthStack, int optLen, @CanBeNull TPointer optData) {
if (log.isTraceEnabled()) {
log.trace(String.format("Matching opt data: %s", Utilities.getMemoryDump(optData.getAddress(), optLen)));
}
return matchingObjects.get(matchingId).start(evthPri, evthStack, inthPri, inthStack, optLen, optData.getAddress());
}
/**
* Stop a matching object
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0x32B156B3, version = 150)
public int sceNetAdhocMatchingStop(@CheckArgument("checkMatchingId") int matchingId) {
return matchingObjects.get(matchingId).stop();
}
/**
* Delete an Adhoc matching object
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0xF16EAF4F, version = 150)
public int sceNetAdhocMatchingDelete(@CheckArgument("checkMatchingId") int matchingId) {
matchingObjects.remove(matchingId).delete();
return 0;
}
/**
* Send data to a matching target
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param mac - The MAC address to send the data to
* @param datalen - Length of the data
* @param data - Pointer to the data
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0xF79472D7, version = 150)
public int sceNetAdhocMatchingSendData(@CheckArgument("checkMatchingId") int matchingId, pspNetMacAddress macAddress, int dataLen, TPointer data) {
if (log.isTraceEnabled()) {
log.trace(String.format("Send data: %s", Utilities.getMemoryDump(data.getAddress(), dataLen)));
}
return matchingObjects.get(matchingId).send(macAddress, dataLen, data.getAddress());
}
/**
* Abort a data send to a matching target
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param mac - The MAC address to send the data to
*
* @return 0 on success, < 0 on error.
*/
@HLEUnimplemented
@HLEFunction(nid = 0xEC19337D, version = 150)
public int sceNetAdhocMatchingAbortSendData(@CheckArgument("checkMatchingId") int matchingId, pspNetMacAddress macAddress) {
return 0;
}
/**
* Select a matching target
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param mac - MAC address to select
* @param optlen - Optional data length
* @param optdata - Pointer to the optional data
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0x5E3D4B79, version = 150)
public int sceNetAdhocMatchingSelectTarget(@CheckArgument("checkMatchingId") int matchingId, pspNetMacAddress macAddress, int optLen, @CanBeNull TPointer optData) {
return matchingObjects.get(matchingId).selectTarget(macAddress, optLen, optData.getAddress());
}
/**
* Cancel a matching target
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param mac - The MAC address to cancel
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0xEA3C6108, version = 150)
public int sceNetAdhocMatchingCancelTarget(@CheckArgument("checkMatchingId") int matchingId, pspNetMacAddress macAddress) {
return matchingObjects.get(matchingId).cancelTarget(macAddress);
}
/**
* Cancel a matching target (with optional data)
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param mac - The MAC address to cancel
* @param optlen - Optional data length
* @param optdata - Pointer to the optional data
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0x8F58BEDF, version = 150)
public int sceNetAdhocMatchingCancelTargetWithOpt(@CheckArgument("checkMatchingId") int matchingId, pspNetMacAddress macAddress, int optLen, @CanBeNull TPointer optData) {
if (log.isTraceEnabled()) {
log.trace(String.format("Opt data: %s", Utilities.getMemoryDump(optData.getAddress(), optLen)));
}
return matchingObjects.get(matchingId).cancelTarget(macAddress, optLen, optData.getAddress());
}
/**
* Get the optional hello message
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param optlenAddr - Length of the hello data (input/output)
* @param optdata - Pointer to the hello data
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0xB5D96C2A, version = 150)
public int sceNetAdhocMatchingGetHelloOpt(@CheckArgument("checkMatchingId") int matchingId, TPointer32 optLenAddr, @CanBeNull TPointer optData) {
MatchingObject matchingObject = matchingObjects.get(matchingId);
int helloOptLen = matchingObject.getHelloOptLen();
int bufSize = optLenAddr.getValue();
optLenAddr.setValue(helloOptLen);
if (helloOptLen > 0 && optData.getAddress() != 0 && bufSize > 0) {
int length = Math.min(bufSize, helloOptLen);
writeBytes(optData.getAddress(), length, matchingObject.getHelloOptData(), 0);
}
return 0;
}
/**
* Set the optional hello message
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param optlen - Length of the hello data
* @param optdata - Pointer to the hello data
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0xB58E61B7, version = 150)
public int sceNetAdhocMatchingSetHelloOpt(@CheckArgument("checkMatchingId") int matchingId, int optLen, @CanBeNull TPointer optData) {
if (log.isTraceEnabled()) {
log.trace(String.format("Hello opt data: %s", Utilities.getMemoryDump(optData.getAddress(), optLen)));
}
matchingObjects.get(matchingId).setHelloOpt(optLen, optData.getAddress());
return 0;
}
/**
* Get a list of matching members
*
* @param matchingid - The ID returned from ::sceNetAdhocMatchingCreate
* @param length - The length of the list.
* @param buf - An allocated area of size length.
*
* @return 0 on success, < 0 on error.
*/
@HLEFunction(nid = 0xC58BCD9E, version = 150)
public int sceNetAdhocMatchingGetMembers(@CheckArgument("checkMatchingId") int matchingId, @BufferInfo(usage=Usage.inout) TPointer32 sizeAddr, @CanBeNull @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=12, usage=Usage.out) TPointer buf) {
final int matchingMemberSize = 12;
MatchingObject matchingObject = matchingObjects.get(matchingId);
List<pspNetMacAddress> members = matchingObject.getMembers();
int size = sizeAddr.getValue();
sizeAddr.setValue(matchingMemberSize * members.size());
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetAdhocMatchingGetMembers returning size=%d", sizeAddr.getValue()));
}
if (buf.isNotNull()) {
int offset = 0;
for (pspNetMacAddress member : members) {
// Check if enough space available to write the next structure
if (offset + matchingMemberSize > size || member == null) {
break;
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceNetAdhocMatchingGetMembers returning %s at 0x%08X", member, buf.getAddress() + offset));
}
/** Pointer to next Member structure in list: will be written later */
offset += 4;
/** MAC address */
member.write(buf, offset);
offset += member.sizeof();
/** Padding */
buf.setValue16(offset, (short) 0);
offset += 2;
}
fillNextPointersInLinkedList(buf, offset, matchingMemberSize);
}
return 0;
}
/**
* Get the status of the memory pool used by the matching library
*
* @param poolstat - A ::pspAdhocPoolStat.
*
* @return 0 on success, < 0 on error.
*/
@HLEUnimplemented
@HLEFunction(nid = 0x9C5CFB7D, version = 150)
public int sceNetAdhocMatchingGetPoolStat() {
checkInitialized();
return 0;
}
/**
* Get the maximum memory usage by the matching library
*
* @return The memory usage on success, < 0 on error.
*/
@HLEUnimplemented
@HLEFunction(nid = 0x40F8F435, version = 150)
public int sceNetAdhocMatchingGetPoolMaxAlloc() {
checkInitialized();
return 0;
}
}