Skip to content

Commit

Permalink
[NTRClient.java] Added a Packet class...
Browse files Browse the repository at this point in the history
Refactored code to use said Packet class. Went out of my way to replace uses of copyByteArray with System.arraycopy. And a little bit of other housekeeping.
  • Loading branch information
ChainSwordCS committed Mar 6, 2024
1 parent 7d663cb commit 311e3b7
Showing 1 changed file with 161 additions and 47 deletions.
208 changes: 161 additions & 47 deletions src/main/java/chokistream/NTRClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
}
}

0 comments on commit 311e3b7

Please sign in to comment.