diff --git a/src/main/java/chokistream/NTRClient.java b/src/main/java/chokistream/NTRClient.java index 5733940..03fc3eb 100644 --- a/src/main/java/chokistream/NTRClient.java +++ b/src/main/java/chokistream/NTRClient.java @@ -121,85 +121,59 @@ public int framesSinceLast(DSScreenBoth screens) { } public static void sendNFCPatch(String host, int chooseAddr) { - int seq = 24000; // 0x5DC0 - int type = 1; - int cmd = 10; // 0x0a + Packet pak = new Packet(); + pak.seq = 24000; + pak.type = 1; + pak.cmd = 10; - int[] args = new int[3]; - - args[0] = 26; // pid; 0x1A - args[1] = switch(chooseAddr) { // addr + pak.args[0] = 26; // pid; 0x1A + pak.args[1] = switch(chooseAddr) { case 0: yield 0x00105AE4; // Sys ver. < 11.4 default: yield 0x00105B00; // Sys ver. >= 11.4 }; - byte[] exdata = {0x70,0x47}; - - args[2] = exdata.length; + pak.exdata = new byte[] {0x70,0x47}; + pak.args[2] = pak.exdata.length; try { - sendPacket(host, type, cmd, args, exdata, seq); + sendPacket(host, pak); logger.log("NFC Patch sent!"); } catch(IOException e) { - e.printStackTrace(); + e.printStackTrace(); // TODO: change this? logger.log("NFC Patch failed to send"); } } public static void sendInitPacket(String host, int port, DSScreen screen, int priority, int quality, int qos) throws UnknownHostException, ConnectException, IOException { - int seq = 3000; // 0x0BB8 - int type = 0; - int cmd = 901; //0x0385 - - int[] args = new int[16]; + Packet pak = new Packet(); + pak.seq = 3000; + pak.type = 0; + pak.cmd = 901; - args[0] = ((screen == DSScreen.TOP)? 1 : 0) << 8 | (priority % 256); - args[1] = quality; - args[2] = (qos*2) << 16; // Convert to the format expected by NTR and NTR-HR + pak.args[0] = ((screen == DSScreen.TOP)? 1 : 0) << 8 | (priority % 256); + pak.args[1] = quality; + pak.args[2] = (qos*2) << 16; // Convert to the format expected by NTR and NTR-HR try { logger.log("Sending init packet", LogLevel.VERBOSE); - sendPacket(host, type, cmd, args, new byte[0], seq); + sendPacket(host, pak); } catch(IOException e) { logger.log("Init packet failed to send"); throw e; } } - public static void sendPacket(String host, int type, int cmd, int[] args, byte[] exdata, int seq) throws UnknownHostException, ConnectException, IOException { - int dataLen = exdata.length; - - byte[] pak = new byte[84+dataLen]; - - copyByteArray(intToBytes(0x12345678), pak, 0); - - copyByteArray(intToBytes(seq), pak, 4); - copyByteArray(intToBytes(type), pak, 8); - copyByteArray(intToBytes(cmd), pak, 12); - - // arguments - int argmax = args.length; - if(argmax > 16) - argmax = 16; - for(int i = 0; i < argmax; i++) { - copyByteArray(intToBytes(args[i]), pak, i*4+16); - } - - copyByteArray(intToBytes(dataLen), pak, 80); - copyByteArray(exdata, pak, 84); - + public static void sendPacket(String host, Packet packet) throws UnknownHostException, ConnectException, IOException { + byte[] pak = packet.getRaw(); logger.log("Sending packet to NTR...", LogLevel.EXTREME); logger.log(pak, LogLevel.EXTREME); - Socket mySoc = new Socket(host, 8000); - //mySoc.setTcpNoDelay(true); OutputStream myOut = mySoc.getOutputStream(); myOut.write(pak); myOut.close(); mySoc.close(); - return; } /** @@ -214,7 +188,7 @@ public static int bytesToInt(byte[] dat, int i) { return dat[i+3]<<24 | dat[i+2]>>8 & 0xff00 | dat[i+1]<<8 & 0xff0000 | dat[i]>>>24; } catch(ArrayIndexOutOfBoundsException e) { - e.printStackTrace(); + e.printStackTrace(); // TODO: change this } return 0; } @@ -261,4 +235,144 @@ public static void copyByteArray(byte[] src, byte[] dst, int i) { e.printStackTrace(); } } + + /** + * Represents a (TCP) packet received from NTR / NTR-HR + */ + private static class Packet { + + /* Header */ + + /* Sequence ID. More or less optional. */ + public int seq = 0; + + /* "Type." + * Traditionally set to 0 if the Extra Data section is empty, and 1 otherwise. + * This may or may not matter to NTR. Refer to docs. (TODO) + */ + public int type = -1; // placeholder; + + /* Command. Required. */ + public int cmd; + + /** + * Arguments. Context-dependent, based on the Command. + * Supports arbitrary array length between 0 and 16 (inclusive). + * Technically unsigned 32-bit integers. + */ + public int[] args = new int[16]; + + /** + * Extra Data (aka "Data") section. + * Supports arbitrary array length. + */ + // TODO: Implement a length limit? + public byte[] exdata = new byte[0]; + + Packet(){}; + + Packet(int seq, int type, int cmd, int[] args, byte[] exdata) { + this.seq = seq; + this.type = type; + this.cmd = cmd; + this.args = args; + this.exdata = exdata; + } + + Packet(int cmd, int[] args) { + this.cmd = cmd; + this.args = args; + } + + /** + * Convert raw data into a Packet. + * @param pak A packet, in the form of raw bytes. + */ + Packet(byte[] pak) { + if(pak.length < 84) { + // TODO: throw an exception? + logger.log("NTRClient Packet error: pak.length < 84"); + return; + } + + // verify magic number + if(pak[0] != 0x78 || pak[1] != 0x56 || pak[2] != 0x34 || pak[3] != 0x12) { + // TODO: throw an exception? + logger.log("Processed NTR packet does not seem to match the expected format."); + return; + } + + seq = bytesToInt(pak, 4); + type = bytesToInt(pak, 8); + cmd = bytesToInt(pak, 12); + + for(int i = 0; i < 16; i++) { + args[i] = bytesToInt(pak, i*4+16); + } + + // Unsigned 32-bit integer + int exdataLen = bytesToInt(pak, 80); + + if(exdataLen > 0) { + int expectedExdataLen = pak.length-84; + if(expectedExdataLen != exdataLen) { // shouldn't ever happen; code logic error. + logger.log("NTRClient Packet error: pak.length - 84 != exdataLen. "+expectedExdataLen+" != "+exdataLen); + if(expectedExdataLen < exdataLen) { + exdataLen = expectedExdataLen; + } + } + exdata = new byte[exdataLen]; + System.arraycopy(pak, 84, exdata, 0, exdataLen); + } else if(exdataLen < 0) { // :( + logger.log("NTRClient Packet error: exdataLen < 0. exdataLen = "+exdataLen); + } + } + + /** + * Convert this Packet into raw data. + * @return byte[] result of the conversion. + */ + byte[] getRaw() { + int exdataLen = exdata.length; + byte[] pak = new byte[84+exdataLen]; + + // magic number + pak[0] = 0x78; + pak[1] = 0x56; + pak[2] = 0x34; + pak[3] = 0x12; + + System.arraycopy(intToBytes(seq), 0, pak, 4, 4); + + int myType = type; + if(myType == -1) { + if(exdataLen > 0) { + myType = 1; + } else { + myType = 0; + } + } + System.arraycopy(intToBytes(myType), 0, pak, 8, 4); + + System.arraycopy(intToBytes(cmd), 0, pak, 12, 4); + + int argsLen = args.length; + if(argsLen > 16) { // shouldn't ever happen; code logic error. + // TODO: throw an exception? + logger.log("NTRClient Packet error: args.length > 16"); + argsLen = 16; + } + for(int i = 0; i < argsLen; i++) { + System.arraycopy(intToBytes(args[i]), 0, pak, i*4+16, 4); + } + + System.arraycopy(intToBytes(exdataLen), 0, pak, 80, 4); + + if(exdataLen > 0) { + System.arraycopy(exdata, 0, pak, 84, exdataLen); + } + + return pak; + } + } }