/* 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.proonline; import static jpcsp.HLE.modules.sceNetAdhocctl.ADHOC_ID_LENGTH; import static jpcsp.HLE.modules.sceNetAdhocctl.GROUP_NAME_LENGTH; import static jpcsp.HLE.modules.sceNetAdhocctl.NICK_NAME_LENGTH; import static jpcsp.hardware.Wlan.MAC_ADDRESS_LENGTH; import static jpcsp.network.proonline.ProOnlineNetworkAdapter.convertIpToString; import org.apache.log4j.Logger; import jpcsp.HLE.Modules; import jpcsp.HLE.kernel.types.pspNetMacAddress; import jpcsp.HLE.modules.sceNetAdhocctl; import jpcsp.HLE.modules.sceUtility; import jpcsp.hardware.Wlan; /** * @author gid15 * */ public class PacketFactory { protected static Logger log = ProOnlineNetworkAdapter.log; protected static final int OPCODE_PING = 0; protected static final int OPCODE_LOGIN = 1; protected static final int OPCODE_CONNECT = 2; protected static final int OPCODE_DISCONNECT = 3; protected static final int OPCODE_SCAN = 4; protected static final int OPCODE_SCAN_COMPLETE = 5; protected static final int OPCODE_CONNECT_BSSID = 6; protected static final int OPCODE_CHAT = 7; private static final int CHAT_MESSAGE_LENGTH = 64; protected static abstract class SceNetAdhocctlPacketBase { protected final ProOnlineNetworkAdapter proOnline; protected int opcode; protected int offset; protected SceNetAdhocctlPacketBase(ProOnlineNetworkAdapter proOnline) { this.proOnline = proOnline; } public byte[] getBytes() { byte[] bytes = new byte[getLength()]; getBytes(bytes); return bytes; } protected void getBytes(byte[] bytes) { offset = 0; bytes[offset] = (byte) opcode; offset++; } protected void copyToBytes(byte[] bytes, String s, int length) { for (int i = 0; i < length; i++, offset++) { bytes[offset] = (byte) (i < s.length() ? s.charAt(i) : 0); } } protected String copyStringFromBytes(byte[] bytes, int length) { int stringLength = length; for (int i = 0; i < length; i++) { if (bytes[offset + i] == (byte) 0) { stringLength = i; break; } } String s = new String(bytes, offset, stringLength); offset += length; return s; } protected int copyInt8FromBytes(byte[] bytes) { return bytes[offset++] & 0xFF; } protected int copyInt32FromBytes(byte[] bytes) { return (copyInt8FromBytes(bytes) ) | (copyInt8FromBytes(bytes) << 8 ) | (copyInt8FromBytes(bytes) << 16) | (copyInt8FromBytes(bytes) << 24); } protected pspNetMacAddress copyMacFromBytes(byte[] bytes) { pspNetMacAddress mac = new pspNetMacAddress(); mac.setMacAddress(bytes, offset); offset += MAC_ADDRESS_LENGTH; return mac; } protected void copyToBytes(byte[] bytes, pspNetMacAddress mac) { System.arraycopy(mac.macAddress, 0, bytes, offset, MAC_ADDRESS_LENGTH); offset += MAC_ADDRESS_LENGTH; } protected void copyInt8ToBytes(byte[] bytes, int value) { bytes[offset++] = (byte) (value & 0xFF); } protected void copyInt32ToBytes(byte[] bytes, int value) { copyInt8ToBytes(bytes, value ); copyInt8ToBytes(bytes, value >> 8); copyInt8ToBytes(bytes, value >> 16); copyInt8ToBytes(bytes, value >> 24); } protected void init(byte[] bytes, int length) { offset = 0; if (length >= getLength()) { opcode = bytes[offset]; offset++; } } public int getLength() { return 1; } @Override public String toString() { return String.format("%s", getClass().getSimpleName()); } } protected static abstract class SceNetAdhocctlPacketBaseC2S extends SceNetAdhocctlPacketBase { protected ProOnlineServer proOnlineServer; protected SceNetAdhocctlPacketBaseC2S(ProOnlineNetworkAdapter proOnline) { super(proOnline); } protected SceNetAdhocctlPacketBaseC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer) { super(proOnline); this.proOnlineServer = proOnlineServer; } public abstract void process(); } protected static abstract class SceNetAdhocctlPacketBaseS2C extends SceNetAdhocctlPacketBase { protected SceNetAdhocctlPacketBaseS2C(ProOnlineNetworkAdapter proOnline) { super(proOnline); } public abstract void process(); } protected static class SceNetAdhocctlPingPacketC2S extends SceNetAdhocctlPacketBaseC2S { public SceNetAdhocctlPingPacketC2S(ProOnlineNetworkAdapter proOnline) { super(proOnline); opcode = OPCODE_PING; } public SceNetAdhocctlPingPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] bytes, int length) { super(proOnline, proOnlineServer); init(bytes, length); } @Override public void process() { // Nothing to do } } protected static class SceNetAdhocctlDisconnectPacketC2S extends SceNetAdhocctlPacketBaseC2S { public SceNetAdhocctlDisconnectPacketC2S(ProOnlineNetworkAdapter proOnline) { super(proOnline); opcode = OPCODE_DISCONNECT; } public SceNetAdhocctlDisconnectPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] bytes, int length) { super(proOnline, proOnlineServer); init(bytes, length); } @Override public void process() { proOnlineServer.processDisconnect(); } } protected static class SceNetAdhocctlScanPacketC2S extends SceNetAdhocctlPacketBaseC2S { public SceNetAdhocctlScanPacketC2S(ProOnlineNetworkAdapter proOnline) { super(proOnline); opcode = OPCODE_SCAN; } public SceNetAdhocctlScanPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] bytes, int length) { super(proOnline, proOnlineServer); init(bytes, length); } @Override public void process() { proOnlineServer.processScan(); } } protected static class SceNetAdhocctlLoginPacketC2S extends SceNetAdhocctlPacketBaseC2S { private pspNetMacAddress mac = new pspNetMacAddress(); private String nickName; private String game; public SceNetAdhocctlLoginPacketC2S(ProOnlineNetworkAdapter proOnline) { super(proOnline); opcode = OPCODE_LOGIN; mac.setMacAddress(Wlan.getMacAddress()); nickName = sceUtility.getSystemParamNickname(); game = Modules.sceNetAdhocctlModule.hleNetAdhocctlGetAdhocID(); } public SceNetAdhocctlLoginPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] bytes, int length) { super(proOnline, proOnlineServer); init(bytes, length); } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { mac = copyMacFromBytes(bytes); nickName = copyStringFromBytes(bytes, NICK_NAME_LENGTH); game = copyStringFromBytes(bytes, ADHOC_ID_LENGTH); } } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, mac); copyToBytes(bytes, nickName, NICK_NAME_LENGTH); copyToBytes(bytes, game, ADHOC_ID_LENGTH); } @Override public int getLength() { return super.getLength() + MAC_ADDRESS_LENGTH + NICK_NAME_LENGTH + ADHOC_ID_LENGTH; } @Override public void process() { proOnlineServer.processLogin(mac, nickName, game); } } protected static class SceNetAdhocctlConnectPacketC2S extends SceNetAdhocctlPacketBaseC2S { private String group; public SceNetAdhocctlConnectPacketC2S(ProOnlineNetworkAdapter proOnline) { super(proOnline); opcode = OPCODE_CONNECT; group = Modules.sceNetAdhocctlModule.hleNetAdhocctlGetGroupName(); } public SceNetAdhocctlConnectPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] bytes, int length) { super(proOnline, proOnlineServer); init(bytes, length); } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { group = copyStringFromBytes(bytes, GROUP_NAME_LENGTH); } } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, group, GROUP_NAME_LENGTH); } @Override public int getLength() { return super.getLength() + GROUP_NAME_LENGTH; } @Override public void process() { proOnlineServer.processConnect(group); } } protected static class SceNetAdhocctlChatPacketC2S extends SceNetAdhocctlPacketBaseC2S { private String message; public SceNetAdhocctlChatPacketC2S(ProOnlineNetworkAdapter proOnline, String message) { super(proOnline); opcode = OPCODE_CHAT; this.message = message; } public SceNetAdhocctlChatPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] bytes, int length) { super(proOnline, proOnlineServer); init(bytes, length); } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { message = copyStringFromBytes(bytes, CHAT_MESSAGE_LENGTH); } } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, message, CHAT_MESSAGE_LENGTH); } @Override public int getLength() { return super.getLength() + CHAT_MESSAGE_LENGTH; } @Override public void process() { proOnlineServer.processChat(message); } } private static class SceNetAdhocctlPingPacketS2C extends SceNetAdhocctlPacketBaseS2C { public SceNetAdhocctlPingPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } @Override public void process() { // Nothing to do } @Override public String toString() { return String.format("PingPacketS2C"); } } protected static class SceNetAdhocctlConnectPacketS2C extends SceNetAdhocctlPacketBaseS2C { private String nickName; private pspNetMacAddress mac; private int ip; public SceNetAdhocctlConnectPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } public SceNetAdhocctlConnectPacketS2C(String nickName, pspNetMacAddress mac, int ip) { super(null); opcode = OPCODE_CONNECT; this.nickName = nickName; this.mac = mac; this.ip = ip; } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { nickName = copyStringFromBytes(bytes, NICK_NAME_LENGTH); mac = copyMacFromBytes(bytes); ip = copyInt32FromBytes(bytes); } } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, nickName, NICK_NAME_LENGTH); copyToBytes(bytes, mac); copyInt32ToBytes(bytes, ip); } @Override public int getLength() { return super.getLength() + NICK_NAME_LENGTH + MAC_ADDRESS_LENGTH + 4; } @Override public String toString() { return String.format("ConnectPacketS2C[nickName='%s', mac=%s, ip=%s]", nickName, mac, convertIpToString(ip)); } @Override public void process() { proOnline.addFriend(nickName, mac, ip); } } protected static class SceNetAdhocctlConnectBSSIDPacketS2C extends SceNetAdhocctlPacketBaseS2C { private pspNetMacAddress mac; public SceNetAdhocctlConnectBSSIDPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } public SceNetAdhocctlConnectBSSIDPacketS2C(pspNetMacAddress mac) { super(null); opcode = OPCODE_CONNECT_BSSID; this.mac = mac; } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { mac = copyMacFromBytes(bytes); } } @Override public void process() { log.info(String.format("Received MAC address %s", mac)); proOnline.setConnectComplete(true); } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, mac); } @Override public int getLength() { return super.getLength() + MAC_ADDRESS_LENGTH; } @Override public String toString() { return String.format("ConnectBSSIDPacketS2C[mac=%s]", mac); } } protected static class SceNetAdhocctlScanPacketS2C extends SceNetAdhocctlPacketBaseS2C { private String group; private pspNetMacAddress mac; public SceNetAdhocctlScanPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } public SceNetAdhocctlScanPacketS2C(String group, pspNetMacAddress mac) { super(null); opcode = OPCODE_SCAN; this.group = group; this.mac = mac; } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { group = copyStringFromBytes(bytes, GROUP_NAME_LENGTH); mac = copyMacFromBytes(bytes); } } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, group, GROUP_NAME_LENGTH); copyToBytes(bytes, mac); } @Override public int getLength() { return super.getLength() + GROUP_NAME_LENGTH + MAC_ADDRESS_LENGTH; } @Override public void process() { Modules.sceNetAdhocctlModule.hleNetAdhocctlAddNetwork(group, mac, sceNetAdhocctl.PSP_ADHOCCTL_MODE_NORMAL); } @Override public String toString() { return String.format("ScanPacketS2C[group='%s', mac=%s]", group, mac); } } private static class SceNetAdhocctlScanCompletePacketS2C extends SceNetAdhocctlPacketBaseS2C { public SceNetAdhocctlScanCompletePacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } @Override public void process() { Modules.sceNetAdhocctlModule.hleNetAdhocctlScanComplete(); } @Override public String toString() { return String.format("ScanCompletePacketS2C"); } } protected static class SceNetAdhocctlDisconnectPacketS2C extends SceNetAdhocctlPacketBaseS2C { private int ip; public SceNetAdhocctlDisconnectPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } public SceNetAdhocctlDisconnectPacketS2C(int ip) { super(null); opcode = OPCODE_DISCONNECT; this.ip = ip; } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { ip = copyInt32FromBytes(bytes); } } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyInt32ToBytes(bytes, ip); } @Override public void process() { proOnline.deleteFriend(ip); } @Override public int getLength() { return super.getLength() + 4; } @Override public String toString() { return String.format("DisconnectPacketS2C ip=%s", convertIpToString(ip)); } } protected static class SceNetAdhocctlChatPacketS2C extends SceNetAdhocctlPacketBaseS2C { private String message; private String nickName; public SceNetAdhocctlChatPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] bytes, int length) { super(proOnline); init(bytes, length); } public SceNetAdhocctlChatPacketS2C(String message, String nickName) { super(null); opcode = OPCODE_CHAT; this.message = message; this.nickName = nickName; } @Override protected void init(byte[] bytes, int length) { super.init(bytes, length); if (length >= getLength()) { message = copyStringFromBytes(bytes, CHAT_MESSAGE_LENGTH); nickName = copyStringFromBytes(bytes, NICK_NAME_LENGTH); } } @Override public void process() { proOnline.displayChatMessage(nickName, message); } @Override protected void getBytes(byte[] bytes) { super.getBytes(bytes); copyToBytes(bytes, message, CHAT_MESSAGE_LENGTH); copyToBytes(bytes, nickName, NICK_NAME_LENGTH); } @Override public int getLength() { return super.getLength() + CHAT_MESSAGE_LENGTH + NICK_NAME_LENGTH; } @Override public String toString() { return String.format("ChatPacketS2C message='%s' from '%s'", message, nickName); } } public SceNetAdhocctlPacketBaseS2C createPacketS2C(ProOnlineNetworkAdapter proOnline, byte[] buffer, int length) { if (length > 0) { switch (buffer[0]) { case OPCODE_PING: return new SceNetAdhocctlPingPacketS2C(proOnline, buffer, length); case OPCODE_CONNECT_BSSID: return new SceNetAdhocctlConnectBSSIDPacketS2C(proOnline, buffer, length); case OPCODE_CONNECT: return new SceNetAdhocctlConnectPacketS2C(proOnline, buffer, length); case OPCODE_SCAN: return new SceNetAdhocctlScanPacketS2C(proOnline, buffer, length); case OPCODE_SCAN_COMPLETE: return new SceNetAdhocctlScanCompletePacketS2C(proOnline, buffer, length); case OPCODE_DISCONNECT: return new SceNetAdhocctlDisconnectPacketS2C(proOnline, buffer, length); case OPCODE_CHAT: return new SceNetAdhocctlChatPacketS2C(proOnline, buffer, length); default: ProOnlineNetworkAdapter.log.error(String.format("Received unknown S2C opcode %d", buffer[0])); break; } } return null; } public SceNetAdhocctlPacketBaseC2S createPacketC2S(ProOnlineNetworkAdapter proOnline, ProOnlineServer proOnlineServer, byte[] buffer, int length) { if (length > 0) { switch (buffer[0]) { case OPCODE_LOGIN: return new SceNetAdhocctlLoginPacketC2S(proOnline, proOnlineServer, buffer, length); case OPCODE_PING: return new SceNetAdhocctlPingPacketC2S(proOnline, proOnlineServer, buffer, length); case OPCODE_CONNECT: return new SceNetAdhocctlConnectPacketC2S(proOnline, proOnlineServer, buffer, length); case OPCODE_DISCONNECT: return new SceNetAdhocctlDisconnectPacketC2S(proOnline, proOnlineServer, buffer, length); case OPCODE_SCAN: return new SceNetAdhocctlScanPacketC2S(proOnline, proOnlineServer, buffer, length); case OPCODE_CHAT: return new SceNetAdhocctlChatPacketC2S(proOnline, proOnlineServer, buffer, length); default: ProOnlineNetworkAdapter.log.error(String.format("Received unknown C2S opcode %d", buffer[0])); break; } } return null; } }