ns-usbloader-mobile/app/src/main/java/com/blogspot/developersu/ns_usbloader/service/GoldLeaf.java

250 lines
9.3 KiB
Java

package com.blogspot.developersu.ns_usbloader.service;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.ResultReceiver;
import com.blogspot.developersu.ns_usbloader.pfs.PFSProvider;
import com.blogspot.developersu.ns_usbloader.R;
import com.blogspot.developersu.ns_usbloader.view.NSPElement;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
class GoldLeaf extends UsbTransfer {
private ArrayList<NSPElement> nspElements;
private PFSProvider pfsElement;
// CMD G L U C
private final byte[] CMD_GLUC = new byte[]{0x47, 0x4c, 0x55, 0x43};
private final byte[] CMD_ConnectionRequest = new byte[]{0x00, 0x00, 0x00, 0x00}; // Write-only command
private final byte[] CMD_NSPName = new byte[]{0x02, 0x00, 0x00, 0x00}; // Write-only command
private final byte[] CMD_NSPData = new byte[]{0x04, 0x00, 0x00, 0x00}; // Write-only command
private final byte[] CMD_ConnectionResponse = new byte[]{0x01, 0x00, 0x00, 0x00};
private final byte[] CMD_Start = new byte[]{0x03, 0x00, 0x00, 0x00};
private final byte[] CMD_NSPContent = new byte[]{0x05, 0x00, 0x00, 0x00};
private final byte[] CMD_NSPTicket = new byte[]{0x06, 0x00, 0x00, 0x00};
private final byte[] CMD_Finish = new byte[]{0x07, 0x00, 0x00, 0x00};
GoldLeaf(ResultReceiver resultReceiver,
Context context,
UsbDevice usbDevice,
UsbManager usbManager,
ArrayList<NSPElement> nspElements) throws Exception {
super(resultReceiver, context, usbDevice, usbManager);
this.nspElements = nspElements;
String fileName;
InputStream fileInputStream;
fileInputStream = context.getContentResolver().openInputStream(nspElements.get(0).getUri());
fileName = nspElements.get(0).getFilename();
pfsElement = new PFSProvider(fileInputStream, fileName);
if (! pfsElement.init())
throw new Exception("GL File provided have incorrect structure and won't be uploaded.");
}
@Override
boolean run() {
if (initGoldLeafProtocol(pfsElement))
status = context.getResources().getString(R.string.status_uploaded); // else - no change status that is already set to FAILED
finish();
return false;
}
private boolean initGoldLeafProtocol(PFSProvider pfsElement){
// Go parse commands
byte[] readByte;
// Go connect to GoldLeaf
if (writeUsb(CMD_GLUC)){
issueDescription = "GL Initiating GoldLeaf connection: 1/2";
return false;
}
if (writeUsb(CMD_ConnectionRequest)){
issueDescription = "GL Initiating GoldLeaf connection: 2/2";
return false;
}
while (true) {
readByte = readUsb();
if (readByte == null)
return false;
if (Arrays.equals(readByte, CMD_GLUC)) {
readByte = readUsb();
if (readByte == null)
return false;
if (Arrays.equals(readByte, CMD_ConnectionResponse)) {
if (!handleConnectionResponse(pfsElement))
return false;
continue;
}
if (Arrays.equals(readByte, CMD_Start)) {
if (!handleStart(pfsElement))
return false;
continue;
}
if (Arrays.equals(readByte, CMD_NSPContent)) {
if (!handleNSPContent(pfsElement, true))
return false;
continue;
}
if (Arrays.equals(readByte, CMD_NSPTicket)) {
if (!handleNSPContent(pfsElement, false))
return false;
continue;
}
if (Arrays.equals(readByte, CMD_Finish)) { // All good
break;
}
}
}
return true;
}
/**
* ConnectionResponse command handler
* */
private boolean handleConnectionResponse(PFSProvider pfsElement){
if (writeUsb(CMD_GLUC)) {
issueDescription = "GL 'ConnectionResponse' command: INFO: [1/4]";
return false;
}
if (writeUsb(CMD_NSPName)) {
issueDescription = "GL 'ConnectionResponse' command: INFO: [2/4]";
return false;
}
if (writeUsb(pfsElement.getBytesNspFileNameLength())) {
issueDescription = "GL 'ConnectionResponse' command: INFO: [3/4]";
return false;
}
if (writeUsb(pfsElement.getBytesNspFileName())) {
issueDescription = "GL 'ConnectionResponse' command: INFO: [4/4]";
return false;
}
return true;
}
/**
* Start command handler
* */
private boolean handleStart(PFSProvider pfsElement){
if (writeUsb(CMD_GLUC)) {
issueDescription = "GL Handle 'Start' command: [Send command prepare]";
return false;
}
if (writeUsb(CMD_NSPData)) {
issueDescription = "GL Handle 'Start' command: [Send command]";
return false;
}
if (writeUsb(pfsElement.getBytesCountOfNca())) {
issueDescription = "GL Handle 'Start' command: [Send length]";
return false;
}
int ncaCount = pfsElement.getIntCountOfNca();
for (int i = 0; i < ncaCount; i++){
if (writeUsb(pfsElement.getNca(i).getNcaFileNameLength())) {
issueDescription = "GL Handle 'Start' command: File # "+i+"/"+ncaCount+" step: [1/4]";
return false;
}
if (writeUsb(pfsElement.getNca(i).getNcaFileName())) {
issueDescription = "GL Handle 'Start' command: File # "+i+"/"+ncaCount+" step: [2/4]";
return false;
}
if (writeUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getBodySize()+pfsElement.getNca(i).getNcaOffset()).array())) { // offset. real.
issueDescription = "GL Handle 'Start' command: File # "+i+"/"+ncaCount+" step: [3/4]";
return false;
}
if (writeUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getNca(i).getNcaSize()).array())) { // size
issueDescription = "GL Handle 'Start' command: File # "+i+"/"+ncaCount+" step: [4/4]";
return false;
}
}
return true;
}
/**
* NSPContent command handler
* isItRawRequest - if True, just ask NS what's needed
* - if False, send ticket
* */
private boolean handleNSPContent(PFSProvider pfsElement, boolean isItRawRequest){
int requestedNcaID;
if (isItRawRequest) {
byte[] readByte = readUsb();
if (readByte == null || readByte.length != 4) {
issueDescription = "GL Handle 'Content' command: [Read requested ID]";
return false;
}
requestedNcaID = ByteBuffer.wrap(readByte).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
else {
requestedNcaID = pfsElement.getNcaTicketID();
}
long realNcaOffset = pfsElement.getNca(requestedNcaID).getNcaOffset()+pfsElement.getBodySize();
long realNcaSize = pfsElement.getNca(requestedNcaID).getNcaSize();
long readFrom = 0;
int readPice = 16384; // 8mb NOTE: consider switching to 1mb 1048576
byte[] readBuf;
try{
BufferedInputStream bufferedInStream = new BufferedInputStream(context.getContentResolver().openInputStream(nspElements.get(0).getUri())); // TODO: refactor?
if (bufferedInStream.skip(realNcaOffset) != realNcaOffset) {
issueDescription = "GL Failed to skip NCA offset";
return false;
}
int updateProgressPeriods = 0;
while (readFrom < realNcaSize){
if (readPice > (realNcaSize - readFrom))
readPice = (int)(realNcaSize - readFrom); // TODO: Troubles could raise here
readBuf = new byte[readPice];
if (bufferedInStream.read(readBuf) != readPice) {
issueDescription = "GL Failed to read data from file.";
return false;
}
if (writeUsb(readBuf)) {
issueDescription = "GL Failed to write data into NS.";
return false;
}
readFrom += readPice;
if (updateProgressPeriods++ % 1024 == 0) // Update progress bar after every 16mb goes to NS
updateProgressBar((int) ((readFrom+1)/(realNcaSize/100+1)));
}
bufferedInStream.close();
resetProgressBar();
}
catch (java.io.IOException ioe){
issueDescription = "GL Failed to read NCA ID "+requestedNcaID+". IO Exception: "+ioe.getMessage();
return false;
}
return true;
}
}