583 lines
25 KiB
Java
583 lines
25 KiB
Java
package com.blogspot.developersu.ns_usbloader.Service;
|
|
|
|
import android.app.IntentService;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.hardware.usb.UsbDevice;
|
|
import android.hardware.usb.UsbDeviceConnection;
|
|
import android.hardware.usb.UsbEndpoint;
|
|
import android.hardware.usb.UsbInterface;
|
|
import android.hardware.usb.UsbManager;
|
|
import android.os.Bundle;
|
|
import android.os.ResultReceiver;
|
|
import android.util.Log;
|
|
|
|
import com.blogspot.developersu.ns_usbloader.NsConstants;
|
|
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;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
public class CommunicationsService extends IntentService {
|
|
private static final String SERVICE_TAG = "com.blogspot.developersu.ns_usbloader.Service.CommunicationsService";
|
|
|
|
private static AtomicBoolean isActive = new AtomicBoolean(false);
|
|
private static AtomicBoolean interrupt = new AtomicBoolean(false);
|
|
|
|
private ResultReceiver resultReceiver;
|
|
|
|
public static boolean isServiceActive(){
|
|
return isActive.get();
|
|
}
|
|
public static void cancel(){
|
|
interrupt.set(true);
|
|
}
|
|
|
|
public CommunicationsService() {
|
|
super(SERVICE_TAG);
|
|
}
|
|
|
|
private ArrayList<NSPElement> nspElements;
|
|
private UsbDeviceConnection deviceConnection;
|
|
private UsbInterface usbInterface;
|
|
private UsbEndpoint epIn;
|
|
private UsbEndpoint epOut;
|
|
|
|
private String status;
|
|
private String issueDescription;
|
|
|
|
@Override
|
|
protected void onHandleIntent(Intent intent) {
|
|
isActive.set(true);
|
|
interrupt.set(false);
|
|
status = getResources().getString(R.string.status_failed_to_upload);
|
|
resultReceiver = intent.getParcelableExtra(NsConstants.NS_RESULT_RECEIVER);
|
|
nspElements = intent.getParcelableArrayListExtra(NsConstants.SERVICE_CONTENT_NSP_LIST);
|
|
final int protocol = intent.getIntExtra(NsConstants.SERVICE_CONTENT_PROTOCOL, -1); // -1 since it's impossible
|
|
UsbDevice usbDevice = intent.getParcelableExtra(NsConstants.SERVICE_CONTENT_NS_DEVICE);
|
|
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
|
if (nspElements == null || usbDevice == null || usbManager == null || protocol < 0) {
|
|
reportExecutionFinish();
|
|
return;
|
|
}
|
|
// Start process
|
|
usbInterface = usbDevice.getInterface(0);
|
|
epIn = usbInterface.getEndpoint(0); // For bulk read
|
|
epOut = usbInterface.getEndpoint(1); // For bulk write
|
|
|
|
deviceConnection = usbManager.openDevice(usbDevice);
|
|
if ( ! deviceConnection.claimInterface(usbInterface, false))
|
|
return;
|
|
|
|
if (protocol == NsConstants.PROTO_TF_USB){
|
|
new TinFoil();
|
|
}
|
|
else if (protocol == NsConstants.PROTO_GL_USB){
|
|
new GoldLeaf();
|
|
}
|
|
|
|
for (NSPElement e: nspElements)
|
|
e.setStatus(status);
|
|
/*
|
|
Log.i("LPR", "Status " +status);
|
|
Log.i("LPR", "issue " +issueDescription);
|
|
Log.i("LPR", "Interrupt " +interrupt.get());
|
|
Log.i("LPR", "Active " +isActive.get());
|
|
*/
|
|
reportExecutionFinish();
|
|
}
|
|
|
|
private void resetProgressBar(){
|
|
resultReceiver.send(NsConstants.NS_RESULT_PROGRESS_INDETERMINATE, Bundle.EMPTY);
|
|
}
|
|
|
|
private void updateProgressBar(int currentPosition){
|
|
Bundle bundle = new Bundle();
|
|
bundle.putInt("POSITION", currentPosition);
|
|
resultReceiver.send(NsConstants.NS_RESULT_PROGRESS_VALUE, bundle);
|
|
}
|
|
|
|
private void reportExecutionFinish(){
|
|
deviceConnection.releaseInterface(usbInterface);
|
|
deviceConnection.close();
|
|
isActive.set(false);
|
|
Intent executionFinishIntent = new Intent(NsConstants.SERVICE_TRANSFER_TASK_FINISHED_INTENT);
|
|
executionFinishIntent.putExtra(NsConstants.SERVICE_CONTENT_NSP_LIST, nspElements);
|
|
if (issueDescription != null) {
|
|
executionFinishIntent.putExtra("ISSUES", issueDescription);
|
|
}
|
|
this.sendBroadcast(executionFinishIntent);
|
|
interrupt.set(false);
|
|
}
|
|
/*============================================================================================*/
|
|
/**
|
|
* Sending any byte array to USB device
|
|
* @return 'false' if no issues
|
|
* 'true' if errors happened
|
|
* */
|
|
private boolean writeToUsb(byte[] message){
|
|
int result;
|
|
result = deviceConnection.bulkTransfer(epOut, message, message.length, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
|
|
//Log.i("LPR", "RES: "+result);
|
|
return (result != message.length);
|
|
}
|
|
/**
|
|
* Reading what USB device responded.
|
|
* @return byte array if data read successful
|
|
* 'null' if read failed
|
|
* */
|
|
private byte[] readFromUsb(){
|
|
byte[] readBuffer = new byte[512];
|
|
// We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
|
|
int result;
|
|
result = deviceConnection.bulkTransfer(epIn, readBuffer, 512, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
|
|
|
|
if (result > 0)
|
|
return Arrays.copyOf(readBuffer, result);
|
|
return null;
|
|
}
|
|
/*============================================================================================*/
|
|
private class TinFoil{
|
|
TinFoil(){
|
|
|
|
if (!sendListOfNSP())
|
|
return;
|
|
|
|
if (proceedCommands()) // REPORT SUCCESS
|
|
status = getResources().getString(R.string.status_uploaded); // Don't change status that is already set to FAILED TODO: FIX
|
|
}
|
|
// Send what NSP will be transferred
|
|
|
|
private boolean sendListOfNSP(){
|
|
// Send list of NSP files:
|
|
// Proceed "TUL0"
|
|
if (writeToUsb("TUL0".getBytes())) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30} //"US-ASCII"?
|
|
issueDescription = "TF Send list of files: handshake failure";
|
|
return false;
|
|
}
|
|
//Collect file names
|
|
StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder
|
|
for(NSPElement element: nspElements) {
|
|
nspListNamesBuilder.append(element.getFilename()); // And here we come with java string default encoding (UTF-16)
|
|
nspListNamesBuilder.append('\n');
|
|
}
|
|
|
|
byte[] nspListNames = nspListNamesBuilder.toString().getBytes(); // android's .getBytes() default == UTF8
|
|
ByteBuffer byteBuffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); // integer = 4 bytes; BTW Java is stored in big-endian format
|
|
byteBuffer.putInt(nspListNames.length); // This way we obtain length in int converted to byte array in correct Big-endian order. Trust me.
|
|
byte[] nspListSize = byteBuffer.array();
|
|
|
|
// Sending NSP list
|
|
if (writeToUsb(nspListSize)) { // size of the list we're going to transfer goes...
|
|
issueDescription = "TF Send list of files: [send list length]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(new byte[8])) { // 8 zero bytes goes...
|
|
issueDescription = "TF Send list of files: [send padding]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(nspListNames)) { // list of the names goes...
|
|
issueDescription = "TF Send list of files: [send list itself]";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// After we sent commands to NS, this chain starts
|
|
|
|
private boolean proceedCommands(){
|
|
final byte[] magic = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30}; // eq. 'TUC0' @ UTF-8 (actually ASCII lol, u know what I mean)
|
|
|
|
byte[] receivedArray;
|
|
|
|
while (true){
|
|
if (interrupt.get()) // Check if user interrupted process.
|
|
return false;
|
|
receivedArray = readFromUsb();
|
|
if (receivedArray == null)
|
|
return false; // catches exception
|
|
|
|
if (!Arrays.equals(Arrays.copyOfRange(receivedArray, 0,4), magic)) // Bytes from 0 to 3 should contain 'magic' TUC0, so must be verified like this
|
|
continue;
|
|
|
|
// 8th to 12th(explicits) bytes in returned data stands for command ID as unsigned integer (Little-endian). Actually, we have to compare arrays here, but in real world it can't be greater then 0/1/2, thus:
|
|
// BTW also protocol specifies 4th byte to be 0x00 kinda indicating that that this command is valid. But, as you may see, never happens other situation when it's not = 0.
|
|
if (receivedArray[8] == 0x00){ //0x00 - exit
|
|
return true; // All interaction with USB device should be ended (expected);
|
|
}
|
|
else if ((receivedArray[8] == 0x01) || (receivedArray[8] == 0x02)){ //0x01 - file range; 0x02 unknown bug on backend side (dirty hack).
|
|
if (!fileRangeCmd()) // issueDescription inside
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* This is what returns requested file (files)
|
|
* Executes multiple times
|
|
* @return 'true' if everything is ok
|
|
* 'false' is error/exception occurs
|
|
* */
|
|
|
|
private boolean fileRangeCmd(){
|
|
byte[] receivedArray;
|
|
// Here we take information of what other side wants
|
|
receivedArray = readFromUsb();
|
|
if (receivedArray == null) {
|
|
issueDescription = "TF Unable to get meta information @fileRangeCmd()";
|
|
return false;
|
|
}
|
|
|
|
// range_offset of the requested file. In the begining it will be 0x10.
|
|
long receivedRangeSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 0,8)).order(ByteOrder.LITTLE_ENDIAN).getLong();
|
|
byte[] receivedRangeSizeRAW = Arrays.copyOfRange(receivedArray, 0,8);
|
|
long receivedRangeOffset = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 8,16)).order(ByteOrder.LITTLE_ENDIAN).getLong();
|
|
|
|
// Requesting UTF-8 file name required:
|
|
receivedArray = readFromUsb();
|
|
if (receivedArray == null) {
|
|
issueDescription = "TF Unable to get file name @fileRangeCmd()";
|
|
return false;
|
|
}
|
|
String receivedRequestedNSP;
|
|
try {
|
|
receivedRequestedNSP = new String(receivedArray, "UTF-8"); //TODO:FIX
|
|
}
|
|
catch (java.io.UnsupportedEncodingException uee){
|
|
issueDescription = "TF UnsupportedEncodingException @fileRangeCmd()";
|
|
return false;
|
|
}
|
|
|
|
// Sending response header
|
|
if (!sendResponse(receivedRangeSizeRAW)) // Get receivedRangeSize in 'RAW' format exactly as it has been received. It's simply.
|
|
return false; // issueDescription handled by method
|
|
|
|
try {
|
|
BufferedInputStream bufferedInStream = null;
|
|
|
|
for (NSPElement e: nspElements){
|
|
if (e.getFilename().equals(receivedRequestedNSP)){
|
|
InputStream elementIS = getContentResolver().openInputStream(e.getUri());
|
|
if (elementIS == null) {
|
|
issueDescription = "TF Unable to obtain InputStream";
|
|
return false;
|
|
}
|
|
bufferedInStream = new BufferedInputStream(elementIS); // TODO: refactor?
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bufferedInStream == null) {
|
|
issueDescription = "TF Unable to create BufferedInputStream";
|
|
return false;
|
|
}
|
|
|
|
byte[] readBuf ;//= new byte[1048576]; // eq. Allocate 1mb
|
|
|
|
if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
|
|
issueDescription = "TF Requested skip is out of file size. Nothing to transmit.";
|
|
return false;
|
|
}
|
|
|
|
long readFrom = 0;
|
|
// 'End Offset' equal to receivedRangeSize.
|
|
int readPice = 16384; // = 8Mb
|
|
|
|
while (readFrom < receivedRangeSize){
|
|
if (interrupt.get()) // Check if user interrupted process.
|
|
return true;
|
|
if ((readFrom + readPice) >= receivedRangeSize )
|
|
readPice = (int)(receivedRangeSize - readFrom); // TODO: Troubles could raise here
|
|
|
|
readBuf = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
|
|
|
|
if (bufferedInStream.read(readBuf) != readPice) {
|
|
issueDescription = "TF Reading of stream suddenly ended";
|
|
return false;
|
|
}
|
|
//write to USB
|
|
if (writeToUsb(readBuf)) {
|
|
issueDescription = "TF Failure during NSP transmission.";
|
|
return false;
|
|
}
|
|
readFrom += readPice;
|
|
|
|
updateProgressBar((int) ((readFrom+1)/(receivedRangeSize/100+1)));
|
|
Log.i("LPR", "CO: "+readFrom+"RRS: "+receivedRangeSize+"RES: "+(readFrom+1/(receivedRangeSize/100+1)));
|
|
}
|
|
bufferedInStream.close();
|
|
|
|
resetProgressBar();
|
|
} catch (java.io.IOException ioe){
|
|
issueDescription = "TF IOException: "+ioe.getMessage();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/**
|
|
* Send response header.
|
|
* @return true if everything OK
|
|
* false if failed
|
|
* */
|
|
private boolean sendResponse(byte[] rangeSize){ // This method as separate function itself for application needed as a cookie in the middle of desert.
|
|
if (writeToUsb(new byte[] { (byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30, // 'TUC0'
|
|
(byte) 0x01, // CMD_TYPE_RESPONSE = 1
|
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, // kinda padding. Guys, didn't you want to use integer value for CMD semantic?
|
|
(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00} ) // Send integer value of '1' in Little-endian format.
|
|
){
|
|
issueDescription = "TF Sending response: [1/3]";
|
|
return false;
|
|
}
|
|
|
|
if(writeToUsb(rangeSize)) { // Send EXACTLY what has been received
|
|
issueDescription = "TF Sending response: [2/3]";
|
|
return false;
|
|
}
|
|
|
|
if(writeToUsb(new byte[12])) { // kinda another one padding
|
|
issueDescription = "TF Sending response: [3/3] FAIL";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
/**
|
|
* GoldLeaf processing
|
|
* */
|
|
private class GoldLeaf{
|
|
// 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(){
|
|
String fileName;
|
|
InputStream fileInputStream;
|
|
try {
|
|
fileInputStream = getContentResolver().openInputStream(nspElements.get(0).getUri());
|
|
fileName = nspElements.get(0).getFilename();
|
|
}
|
|
catch (java.io.FileNotFoundException fnfe){
|
|
issueDescription = "GL FileNotFoundException @GoldLeaf()";
|
|
return;
|
|
}
|
|
|
|
PFSProvider pfsElement = new PFSProvider(fileInputStream, fileName);
|
|
if (!pfsElement.init()) {
|
|
issueDescription = "GL File provided have incorrect structure and won't be uploaded.";
|
|
status = getResources().getString(R.string.status_wrong_file);
|
|
return;
|
|
}
|
|
|
|
if (initGoldLeafProtocol(pfsElement))
|
|
status = getResources().getString(R.string.status_uploaded); // else - no change status that is already set to FAILED
|
|
}
|
|
|
|
private boolean initGoldLeafProtocol(PFSProvider pfsElement){
|
|
// Go parse commands
|
|
byte[] readByte;
|
|
|
|
// Go connect to GoldLeaf
|
|
if (writeToUsb(CMD_GLUC)){
|
|
issueDescription = "GL Initiating GoldLeaf connection: 1/2";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(CMD_ConnectionRequest)){
|
|
issueDescription = "GL Initiating GoldLeaf connection: 2/2";
|
|
return false;
|
|
}
|
|
|
|
while (true) {
|
|
readByte = readFromUsb();
|
|
if (readByte == null)
|
|
return false;
|
|
if (Arrays.equals(readByte, CMD_GLUC)) {
|
|
readByte = readFromUsb();
|
|
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 (writeToUsb(CMD_GLUC)) {
|
|
issueDescription = "GL 'ConnectionResponse' command: INFO: [1/4]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(CMD_NSPName)) {
|
|
issueDescription = "GL 'ConnectionResponse' command: INFO: [2/4]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(pfsElement.getBytesNspFileNameLength())) {
|
|
issueDescription = "GL 'ConnectionResponse' command: INFO: [3/4]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(pfsElement.getBytesNspFileName())) {
|
|
issueDescription = "GL 'ConnectionResponse' command: INFO: [4/4]";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/**
|
|
* Start command handler
|
|
* */
|
|
private boolean handleStart(PFSProvider pfsElement){
|
|
|
|
if (writeToUsb(CMD_GLUC)) {
|
|
issueDescription = "GL Handle 'Start' command: [Send command prepare]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(CMD_NSPData)) {
|
|
issueDescription = "GL Handle 'Start' command: [Send command]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(pfsElement.getBytesCountOfNca())) {
|
|
issueDescription = "GL Handle 'Start' command: [Send length]";
|
|
return false;
|
|
}
|
|
|
|
int ncaCount = pfsElement.getIntCountOfNca();
|
|
|
|
for (int i = 0; i < ncaCount; i++){
|
|
if (writeToUsb(pfsElement.getNca(i).getNcaFileNameLength())) {
|
|
issueDescription = "GL Handle 'Start' command: File # "+i+"/"+ncaCount+" step: [1/4]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(pfsElement.getNca(i).getNcaFileName())) {
|
|
issueDescription = "GL Handle 'Start' command: File # "+i+"/"+ncaCount+" step: [2/4]";
|
|
return false;
|
|
}
|
|
|
|
if (writeToUsb(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 (writeToUsb(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 = readFromUsb();
|
|
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(getContentResolver().openInputStream(nspElements.get(0).getUri())); // TODO: refactor?
|
|
if (bufferedInStream.skip(realNcaOffset) != realNcaOffset) {
|
|
issueDescription = "GL Failed to skip NCA offset";
|
|
return false;
|
|
}
|
|
|
|
while (readFrom < realNcaSize){
|
|
if (interrupt.get()) // Check if user interrupted process.
|
|
return false;
|
|
|
|
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 (writeToUsb(readBuf)) {
|
|
issueDescription = "GL Failed to write data into NS.";
|
|
return false;
|
|
}
|
|
|
|
readFrom += readPice;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|