/*
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.network.jpcsp;
import static jpcsp.HLE.Modules.sceNetAdhocModule;
import static jpcsp.HLE.Modules.sceNetAdhocctlModule;
import static jpcsp.HLE.modules.sceNetAdhocctl.PSP_ADHOCCTL_MODE_GAMEMODE;
import static jpcsp.network.jpcsp.JpcspAdhocPtpMessage.PTP_MESSAGE_TYPE_DATA;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import jpcsp.HLE.kernel.types.pspNetMacAddress;
import jpcsp.HLE.modules.sceNetAdhoc;
import jpcsp.HLE.modules.sceNetAdhocctl;
import jpcsp.HLE.modules.sceNetInet;
import jpcsp.HLE.modules.sceUtility;
import jpcsp.HLE.modules.sceNetAdhoc.GameModeArea;
import jpcsp.hardware.Wlan;
import jpcsp.network.BaseNetworkAdapter;
import jpcsp.network.adhoc.AdhocMatchingEventMessage;
import jpcsp.network.adhoc.AdhocMessage;
import jpcsp.network.adhoc.MatchingObject;
import jpcsp.network.adhoc.PdpObject;
import jpcsp.network.adhoc.PtpObject;
/**
* @author gid15
*
*/
public class JpcspNetworkAdapter extends BaseNetworkAdapter {
private DatagramSocket adhocctlSocket;
// Try to use a port unused by other applications...
private static final int adhocctlBroadcastPort = 30004;
@Override
public void stop() {
if (adhocctlSocket != null) {
adhocctlSocket.close();
adhocctlSocket = null;
}
super.stop();
}
@Override
public void sceNetAdhocctlInit() {
}
@Override
public void sceNetAdhocctlTerm() {
}
@Override
public void sceNetAdhocctlConnect() {
}
@Override
public void sceNetAdhocctlCreate() {
}
@Override
public void sceNetAdhocctlJoin() {
}
@Override
public void sceNetAdhocctlDisconnect() {
}
@Override
public void sceNetAdhocctlScan() {
}
@Override
public AdhocMessage createAdhocPdpMessage(int address, int length, byte[] destMacAddress) {
return new JpcspAdhocPdpMessage(address, length, destMacAddress);
}
@Override
public AdhocMessage createAdhocPdpMessage(byte[] message, int length) {
return new JpcspAdhocPdpMessage(message, length);
}
@Override
public PdpObject createPdpObject() {
return new JpcspPdpObject(this);
}
@Override
public PtpObject createPtpObject() {
return new JpcspPtpObject(this);
}
@Override
public AdhocMessage createAdhocPtpMessage(int address, int length) {
return new JpcspAdhocPtpMessage(address, length, PTP_MESSAGE_TYPE_DATA);
}
@Override
public AdhocMessage createAdhocPtpMessage(byte[] message, int length) {
return new JpcspAdhocPtpMessage(message, length);
}
@Override
public AdhocMessage createAdhocGameModeMessage(GameModeArea gameModeArea) {
return new JpcspAdhocGameModeMessage(gameModeArea);
}
@Override
public AdhocMessage createAdhocGameModeMessage(byte[] message, int length) {
return new JpcspAdhocGameModeMessage(message, length);
}
@Override
public SocketAddress getSocketAddress(byte[] macAddress, int realPort) throws UnknownHostException {
if (sceNetAdhocModule.hasNetPortShiftActive()) {
return new InetSocketAddress(InetAddress.getLocalHost(), realPort);
}
return sceNetInet.getBroadcastInetSocketAddress(realPort)[0];
}
@Override
public SocketAddress[] getMultiSocketAddress(byte[] macAddress, int realPort) throws UnknownHostException {
if (sceNetAdhocModule.hasNetPortShiftActive()) {
return super.getMultiSocketAddress(macAddress, realPort);
}
return sceNetInet.getBroadcastInetSocketAddress(realPort);
}
@Override
public MatchingObject createMatchingObject() {
return new JpcspMatchingObject(this);
}
@Override
public AdhocMatchingEventMessage createAdhocMatchingEventMessage(MatchingObject matchingObject, int event) {
return new JpcspAdhocMatchingEventMessage(matchingObject, event);
}
@Override
public AdhocMatchingEventMessage createAdhocMatchingEventMessage(MatchingObject matchingObject, int event, int data, int dataLength, byte[] macAddress) {
return new JpcspAdhocMatchingEventMessage(matchingObject, event, data, dataLength, macAddress);
}
@Override
public AdhocMatchingEventMessage createAdhocMatchingEventMessage(MatchingObject matchingObject, byte[] message, int length) {
return new JpcspAdhocMatchingEventMessage(matchingObject, message, length);
}
@Override
public void sendChatMessage(String message) {
// TODO Implement Chat
log.warn(String.format("Chat functionality not supported: %s", message));
}
@Override
public boolean isConnectComplete() {
return sceNetAdhocctlModule.getNumberPeers() > 0 || sceNetAdhocctlModule.hleNetAdhocctlGetState() == sceNetAdhocctl.PSP_ADHOCCTL_STATE_DISCONNECTED;
}
private void openSocket() throws SocketException {
if (adhocctlSocket == null) {
adhocctlSocket = new DatagramSocket(sceNetAdhocModule.getRealPortFromServerPort(adhocctlBroadcastPort));
// For broadcast
adhocctlSocket.setBroadcast(true);
// Non-blocking (timeout = 0 would mean blocking)
adhocctlSocket.setSoTimeout(1);
}
}
private void broadcastPeers() {
if (sceNetAdhocctlModule.hleNetAdhocctlGetGroupName() == null) {
return;
}
try {
openSocket();
JpcspAdhocctlMessage adhocctlMessage = new JpcspAdhocctlMessage(sceUtility.getSystemParamNickname(), Wlan.getMacAddress(), sceNetAdhocctlModule.hleNetAdhocctlGetGroupName());
if (sceNetAdhocctlModule.hleNetAdhocctlGetMode() == PSP_ADHOCCTL_MODE_GAMEMODE && sceNetAdhocctlModule.hleNetAdhocctlGetRequiredGameModeMacs().size() > 0) {
boolean gameModeComplete = sceNetAdhocctlModule.isGameModeComplete();
adhocctlMessage.setGameModeComplete(gameModeComplete, sceNetAdhocctlModule.hleNetAdhocctlGetRequiredGameModeMacs());
}
SocketAddress[] socketAddress = sceNetAdhocModule.getMultiSocketAddress(sceNetAdhoc.ANY_MAC_ADDRESS, sceNetAdhocModule.getRealPortFromClientPort(sceNetAdhoc.ANY_MAC_ADDRESS, adhocctlBroadcastPort));
for (int i = 0; i < socketAddress.length; i++) {
DatagramPacket packet = new DatagramPacket(adhocctlMessage.getMessage(), JpcspAdhocctlMessage.getMessageLength(), socketAddress[i]);
adhocctlSocket.send(packet);
if (log.isDebugEnabled()) {
log.debug(String.format("broadcast sent to peer[%s]: %s", socketAddress[i], adhocctlMessage));
}
}
} catch (SocketException e) {
log.error("broadcastPeers", e);
} catch (IOException e) {
log.error("broadcastPeers", e);
}
}
private void pollPeers() {
try {
openSocket();
// Poll all the available messages.
// Exiting the loop only when no more messages are available (SocketTimeoutException)
while (true) {
byte[] bytes = new byte[JpcspAdhocctlMessage.getMessageLength()];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
adhocctlSocket.receive(packet);
JpcspAdhocctlMessage adhocctlMessage = new JpcspAdhocctlMessage(packet.getData(), packet.getLength());
if (log.isDebugEnabled()) {
log.debug(String.format("broadcast received from peer: %s", adhocctlMessage));
}
// Ignore messages coming from myself
if (!sceNetAdhoc.isSameMacAddress(Wlan.getMacAddress(), adhocctlMessage.macAddress)) {
if (adhocctlMessage.groupName.equals(sceNetAdhocctlModule.hleNetAdhocctlGetGroupName())) {
sceNetAdhocctlModule.hleNetAdhocctlAddPeer(adhocctlMessage.nickName, new pspNetMacAddress(adhocctlMessage.macAddress));
}
if (adhocctlMessage.ibss.equals(sceNetAdhocctlModule.hleNetAdhocctlGetIBSS())) {
sceNetAdhocctlModule.hleNetAdhocctlAddNetwork(adhocctlMessage.groupName, new pspNetMacAddress(adhocctlMessage.macAddress), adhocctlMessage.channel, adhocctlMessage.ibss, adhocctlMessage.mode);
if (adhocctlMessage.mode == PSP_ADHOCCTL_MODE_GAMEMODE) {
sceNetAdhocctlModule.hleNetAdhocctlAddGameModeMac(adhocctlMessage.macAddress);
if (sceNetAdhocctlModule.hleNetAdhocctlGetRequiredGameModeMacs().size() <= 0) {
sceNetAdhocctlModule.hleNetAdhocctlSetGameModeJoinComplete(adhocctlMessage.gameModeComplete);
if (adhocctlMessage.gameModeComplete) {
byte[][] macs = adhocctlMessage.gameModeMacs;
if (macs != null) {
sceNetAdhocctlModule.hleNetAdhocctlSetGameModeMacs(macs);
}
}
}
}
}
}
}
} catch (SocketException e) {
log.error("broadcastPeers", e);
} catch (SocketTimeoutException e) {
// Nothing available
} catch (IOException e) {
log.error("broadcastPeers", e);
}
}
@Override
public void updatePeers() {
broadcastPeers();
pollPeers();
}
}