mirror of
https://github.com/developersu/ns-usbloader.git
synced 2025-05-22 03:05:24 -04:00
Fix POM, update package name a bit, update NXDT-related part
This commit is contained in:
parent
8771d551a4
commit
98822de559
30 changed files with 299 additions and 108 deletions
109
src/main/java/nsusbloader/com/helpers/NSSplitReader.java
Normal file
109
src/main/java/nsusbloader/com/helpers/NSSplitReader.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.helpers;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Handle Split files
|
||||
* */
|
||||
public class NSSplitReader implements Closeable {
|
||||
|
||||
private final String splitFileDir;
|
||||
private final long referenceSplitChunkSize;
|
||||
|
||||
private byte subFileNum;
|
||||
private long curPosition;
|
||||
private BufferedInputStream biStream;
|
||||
|
||||
public NSSplitReader(File file, long seekToPosition) throws IOException, NullPointerException {
|
||||
this.splitFileDir = file.getAbsolutePath()+File.separator;
|
||||
File subFile = new File(file.getAbsolutePath()+File.separator+"00");
|
||||
if (! file.exists())
|
||||
throw new FileNotFoundException("File not found on "+file.getAbsolutePath()+File.separator+"00");
|
||||
this.referenceSplitChunkSize = subFile.length();
|
||||
this.subFileNum = (byte) (seekToPosition / referenceSplitChunkSize);
|
||||
this.biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileNum)));
|
||||
this.curPosition = seekToPosition;
|
||||
|
||||
seekToPosition -= referenceSplitChunkSize * subFileNum;
|
||||
|
||||
if (seekToPosition != biStream.skip(seekToPosition))
|
||||
throw new IOException("Unable to seek to requested position of "+seekToPosition+" for file "+splitFileDir+String.format("%02d", subFileNum));
|
||||
}
|
||||
|
||||
public long seek(long position) throws IOException{
|
||||
|
||||
byte subFileRequested = (byte) (position / referenceSplitChunkSize);
|
||||
|
||||
if ((subFileRequested != this.subFileNum) || (curPosition > position)) {
|
||||
biStream.close();
|
||||
biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileRequested)));
|
||||
this.subFileNum = subFileRequested;
|
||||
this.curPosition = referenceSplitChunkSize * subFileRequested;
|
||||
}
|
||||
|
||||
long retVal = biStream.skip(position - curPosition);
|
||||
|
||||
retVal += curPosition;
|
||||
this.curPosition = position;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public int read(byte[] readBuffer) throws IOException, NullPointerException {
|
||||
final int requested = readBuffer.length;
|
||||
int readPrtOne;
|
||||
|
||||
if ( (curPosition + requested) <= (referenceSplitChunkSize * (subFileNum+1))) {
|
||||
if ((readPrtOne = biStream.read(readBuffer)) < 0 )
|
||||
return readPrtOne;
|
||||
curPosition += readPrtOne;
|
||||
return readPrtOne;
|
||||
}
|
||||
|
||||
int partOne = (int) (referenceSplitChunkSize * (subFileNum+1) - curPosition);
|
||||
int partTwo = requested - partOne;
|
||||
int readPrtTwo;
|
||||
|
||||
if ( (readPrtOne = biStream.read(readBuffer, 0, partOne)) < 0)
|
||||
return readPrtOne;
|
||||
|
||||
curPosition += readPrtOne;
|
||||
|
||||
if (readPrtOne != partOne)
|
||||
return readPrtOne;
|
||||
|
||||
biStream.close();
|
||||
subFileNum += 1;
|
||||
biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileNum)));
|
||||
|
||||
if ( (readPrtTwo = biStream.read(readBuffer, partOne, partTwo) ) < 0)
|
||||
return readPrtTwo;
|
||||
|
||||
curPosition += readPrtTwo;
|
||||
|
||||
return readPrtOne + readPrtTwo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (biStream != null)
|
||||
biStream.close();
|
||||
}
|
||||
}
|
398
src/main/java/nsusbloader/com/net/NETCommunications.java
Normal file
398
src/main/java/nsusbloader/com/net/NETCommunications.java
Normal file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.net;
|
||||
|
||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||
import nsusbloader.ModelControllers.Log;
|
||||
import nsusbloader.NSLDataTypes.EModule;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import nsusbloader.com.helpers.NSSplitReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class NETCommunications extends CancellableRunnable {
|
||||
|
||||
private final ILogPrinter logPrinter;
|
||||
|
||||
private final String switchIP;
|
||||
private final static int SWITCH_PORT = 2000;
|
||||
private final String hostIP;
|
||||
private final int hostPort;
|
||||
private final String extras;
|
||||
private final boolean doNotServe;
|
||||
|
||||
private final HashMap<String, UniFile> files;
|
||||
|
||||
private final ServerSocket serverSocket;
|
||||
private Socket clientSocket;
|
||||
|
||||
private final boolean isValid;
|
||||
|
||||
private OutputStream currSockOS;
|
||||
private PrintWriter currSockPW;
|
||||
|
||||
private boolean jobInProgress = true;
|
||||
/**
|
||||
* Simple constructor that everybody uses
|
||||
* */
|
||||
public NETCommunications(List<File> filesList,
|
||||
String switchIP,
|
||||
boolean doNotServe,
|
||||
String hostIP,
|
||||
String hostPortNum,
|
||||
String extras)
|
||||
{
|
||||
this.doNotServe = doNotServe;
|
||||
if (doNotServe)
|
||||
this.extras = extras;
|
||||
else
|
||||
this.extras = "";
|
||||
this.switchIP = switchIP;
|
||||
this.logPrinter = Log.getPrinter(EModule.USB_NET_TRANSFERS);
|
||||
|
||||
NetworkSetupValidator validator =
|
||||
new NetworkSetupValidator(filesList, doNotServe, hostIP, hostPortNum, logPrinter);
|
||||
|
||||
this.hostIP = validator.getHostIP();
|
||||
this.hostPort = validator.getHostPort();
|
||||
this.files = validator.getFiles();
|
||||
this.serverSocket = validator.getServerSocket();
|
||||
this.isValid = validator.isValid();
|
||||
|
||||
if (! isValid)
|
||||
close(EFileStatus.FAILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (! isValid || isCancelled() )
|
||||
return;
|
||||
|
||||
logPrinter.print("\tStart chain", EMsgType.INFO);
|
||||
|
||||
final String handshakeContent = buildHandshakeContent();
|
||||
|
||||
byte[] handshakeCommand = handshakeContent.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] handshakeCommandSize = ByteBuffer.allocate(Integer.BYTES).putInt(handshakeCommand.length).array();
|
||||
|
||||
if (sendHandshake(handshakeCommandSize, handshakeCommand))
|
||||
return;
|
||||
|
||||
// Check if we should serve requests
|
||||
if (this.doNotServe){
|
||||
logPrinter.print("List of files transferred. Replies won't be served.", EMsgType.PASS);
|
||||
close(EFileStatus.UNKNOWN);
|
||||
return;
|
||||
}
|
||||
logPrinter.print("Initiation files list has been sent to NS.", EMsgType.PASS);
|
||||
|
||||
// Go transfer
|
||||
serveRequestsLoop();
|
||||
}
|
||||
/**
|
||||
* Create string that we'll send to TF/AW and which initiates chain
|
||||
* */
|
||||
private String buildHandshakeContent(){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (String fileNameEncoded : files.keySet()) {
|
||||
builder.append(hostIP);
|
||||
builder.append(':');
|
||||
builder.append(hostPort);
|
||||
builder.append('/');
|
||||
builder.append(extras);
|
||||
builder.append(fileNameEncoded);
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private boolean sendHandshake(byte[] handshakeCommandSize, byte[] handshakeCommand){
|
||||
try {
|
||||
Socket handshakeSocket = new Socket(InetAddress.getByName(switchIP), SWITCH_PORT);
|
||||
OutputStream os = handshakeSocket.getOutputStream();
|
||||
|
||||
os.write(handshakeCommandSize);
|
||||
os.write(handshakeCommand);
|
||||
os.flush();
|
||||
|
||||
handshakeSocket.close();
|
||||
}
|
||||
catch (IOException uhe){
|
||||
logPrinter.print("Unable to connect to NS and send files list:\n "
|
||||
+ uhe.getMessage(), EMsgType.FAIL);
|
||||
close(EFileStatus.UNKNOWN);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private void serveRequestsLoop(){
|
||||
try {
|
||||
while (jobInProgress){
|
||||
clientSocket = serverSocket.accept();
|
||||
BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(clientSocket.getInputStream())
|
||||
);
|
||||
|
||||
currSockOS = clientSocket.getOutputStream();
|
||||
currSockPW = new PrintWriter(new OutputStreamWriter(currSockOS));
|
||||
|
||||
String line;
|
||||
LinkedList<String> tcpPacket = new LinkedList<>();
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.trim().isEmpty()) { // If TCP packet is ended
|
||||
handleRequest(tcpPacket); // Proceed required things
|
||||
tcpPacket.clear(); // Clear data and wait for next TCP packet
|
||||
}
|
||||
else
|
||||
tcpPacket.add(line); // Otherwise collect data
|
||||
}
|
||||
clientSocket.close();
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
if (isCancelled())
|
||||
logPrinter.print("Interrupted by user.", EMsgType.INFO);
|
||||
else
|
||||
logPrinter.print(e.getMessage(), EMsgType.INFO);
|
||||
close(EFileStatus.UNKNOWN);
|
||||
return;
|
||||
}
|
||||
logPrinter.print("All transfers complete", EMsgType.PASS);
|
||||
close(EFileStatus.UPLOADED);
|
||||
}
|
||||
/**
|
||||
* Handle requests
|
||||
* @return true if failed
|
||||
* */
|
||||
private void handleRequest(LinkedList<String> packet) throws Exception{
|
||||
if (packet.get(0).startsWith("DROP")){
|
||||
jobInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
File requestedFile;
|
||||
String reqFileName = packet.get(0).replaceAll("(^[A-z\\s]+/)|(\\s+?.*$)", "");
|
||||
|
||||
if (! files.containsKey(reqFileName)){
|
||||
writeToSocket(NETPacket.getCode404());
|
||||
logPrinter.print("File "+reqFileName+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
long reqFileSize = files.get(reqFileName).getSize();
|
||||
requestedFile = files.get(reqFileName).getFile();
|
||||
|
||||
if (! requestedFile.exists() || reqFileSize == 0){ // well.. tell 404 if file exists with 0 length is against standard, but saves time
|
||||
writeToSocket(NETPacket.getCode404());
|
||||
logPrinter.print("File "+requestedFile.getName()+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
|
||||
logPrinter.update(requestedFile, EFileStatus.FAILED);
|
||||
return;
|
||||
}
|
||||
if (packet.get(0).startsWith("HEAD")){
|
||||
writeToSocket(NETPacket.getCode200(reqFileSize));
|
||||
logPrinter.print("Replying for requested file: "+requestedFile.getName(), EMsgType.INFO);
|
||||
return;
|
||||
}
|
||||
if (packet.get(0).startsWith("GET")) {
|
||||
for (String line: packet) {
|
||||
if (line.toLowerCase().startsWith("range")){
|
||||
parseGETrange(requestedFile, reqFileName, reqFileSize, line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseGETrange(File file, String fileName, long fileSize, String rangeDirective) throws Exception{
|
||||
try {
|
||||
String[] rangeStr = rangeDirective.toLowerCase().replaceAll("^range:\\s+?bytes=", "").split("-", 2);
|
||||
|
||||
if (! rangeStr[0].isEmpty() && ! rangeStr[1].isEmpty()) { // If both ranges defined: Read requested
|
||||
long fromRange = Long.parseLong(rangeStr[0]);
|
||||
long toRange = Long.parseLong(rangeStr[1]);
|
||||
|
||||
if (fromRange > toRange){ // If start bytes greater then end bytes
|
||||
writeToSocket(NETPacket.getCode400());
|
||||
logPrinter.print("Requested range for "
|
||||
+ file.getName()
|
||||
+ " is incorrect. Returning 400", EMsgType.FAIL);
|
||||
logPrinter.update(file, EFileStatus.FAILED);
|
||||
return;
|
||||
}
|
||||
writeToSocket(fileName, fromRange, toRange);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! rangeStr[0].isEmpty()) { // If only START defined: Read all
|
||||
writeToSocket(fileName, Long.parseLong(rangeStr[0]), fileSize);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rangeStr[1].isEmpty()) { // If Range not defined: like "Range: bytes=-"
|
||||
writeToSocket(NETPacket.getCode400());
|
||||
logPrinter.print("Requested range for "
|
||||
+ file.getName()
|
||||
+ " is incorrect (empty start & end). Returning 400", EMsgType.FAIL);
|
||||
logPrinter.update(file, EFileStatus.FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileSize > 500){
|
||||
writeToSocket(fileName, fileSize - 500, fileSize);
|
||||
return;
|
||||
}
|
||||
// If file smaller than 500 bytes
|
||||
writeToSocket(NETPacket.getCode416());
|
||||
logPrinter.print("File size requested for "
|
||||
+ file.getName()
|
||||
+ " while actual size of it: "
|
||||
+ fileSize+". Returning 416", EMsgType.FAIL);
|
||||
logPrinter.update(file, EFileStatus.FAILED);
|
||||
}
|
||||
catch (NumberFormatException nfe){
|
||||
writeToSocket(NETPacket.getCode400());
|
||||
logPrinter.print("Requested range for "
|
||||
+ file.getName()
|
||||
+ " has incorrect format. Returning 400\n\t"
|
||||
+ nfe.getMessage(), EMsgType.FAIL);
|
||||
logPrinter.update(file, EFileStatus.FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeToSocket(String string) {
|
||||
currSockPW.write(string);
|
||||
currSockPW.flush();
|
||||
}
|
||||
/**
|
||||
* Send files.
|
||||
* */
|
||||
private void writeToSocket(String fileName, long start, long end) throws Exception{
|
||||
File file = files.get(fileName).getFile();
|
||||
|
||||
logPrinter.print("Reply to range: "+start+"-"+end, EMsgType.INFO);
|
||||
|
||||
writeToSocket(NETPacket.getCode206(files.get(fileName).getSize(), start, end));
|
||||
try{
|
||||
if (file.isDirectory())
|
||||
handleSplitFile(file, start, end);
|
||||
else
|
||||
handleRegularFile(file, start, end);
|
||||
|
||||
logPrinter.updateProgress(1.0);
|
||||
}
|
||||
catch (Exception e){
|
||||
logPrinter.update(file, EFileStatus.FAILED);
|
||||
throw new Exception("File transmission failed:\n "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSplitFile(File file, long start, long end) throws Exception{
|
||||
long count = end - start + 1;
|
||||
|
||||
int readPice = 1024;// NOTE: keep it small for better speed
|
||||
byte[] byteBuf;
|
||||
long currentOffset = 0;
|
||||
|
||||
NSSplitReader nsr = new NSSplitReader(file, start);
|
||||
|
||||
while (currentOffset < count){
|
||||
if ((currentOffset + readPice) >= count){
|
||||
readPice = Math.toIntExact(count - currentOffset);
|
||||
}
|
||||
byteBuf = new byte[readPice];
|
||||
|
||||
if (nsr.read(byteBuf) != readPice)
|
||||
throw new IOException("File stream suddenly ended.");
|
||||
|
||||
currSockOS.write(byteBuf);
|
||||
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
|
||||
|
||||
currentOffset += readPice;
|
||||
}
|
||||
currSockOS.flush(); // TODO: check if this really needed.
|
||||
nsr.close();
|
||||
}
|
||||
private void handleRegularFile(File file, long start, long end) throws Exception{
|
||||
long count = end - start + 1;
|
||||
|
||||
int readPice = 1024; // NOTE: keep it small for better speed
|
||||
byte[] byteBuf;
|
||||
long currentOffset = 0;
|
||||
|
||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
||||
|
||||
if (bis.skip(start) != start)
|
||||
throw new IOException("Unable to skip requested range.");
|
||||
|
||||
while (currentOffset < count){
|
||||
|
||||
if ((currentOffset + readPice) >= count){
|
||||
readPice = Math.toIntExact(count - currentOffset);
|
||||
}
|
||||
byteBuf = new byte[readPice];
|
||||
|
||||
if (bis.read(byteBuf) != readPice){
|
||||
throw new IOException("File stream suddenly ended.");
|
||||
}
|
||||
currSockOS.write(byteBuf);
|
||||
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
|
||||
currentOffset += readPice;
|
||||
}
|
||||
currSockOS.flush(); // TODO: check if this really needed.
|
||||
bis.close();
|
||||
}
|
||||
|
||||
public ServerSocket getServerSocket(){
|
||||
return serverSocket;
|
||||
}
|
||||
public Socket getClientSocket(){
|
||||
return clientSocket;
|
||||
}
|
||||
/**
|
||||
* Close when done
|
||||
* */
|
||||
private void close(EFileStatus status){
|
||||
try {
|
||||
if (serverSocket != null && ! serverSocket.isClosed()) {
|
||||
serverSocket.close();
|
||||
logPrinter.print("Closing server socket.", EMsgType.PASS);
|
||||
}
|
||||
}
|
||||
catch (IOException ioe){
|
||||
logPrinter.print("Closing server socket failed. Sometimes it's not an issue.", EMsgType.WARNING);
|
||||
}
|
||||
|
||||
HashMap<String, File> tempMap = new HashMap<>();
|
||||
for (UniFile sf : files.values())
|
||||
tempMap.put(sf.getFile().getName(), sf.getFile());
|
||||
|
||||
logPrinter.update(tempMap, status);
|
||||
|
||||
logPrinter.print("\tEnd chain", EMsgType.INFO);
|
||||
logPrinter.close();
|
||||
}
|
||||
}
|
82
src/main/java/nsusbloader/com/net/NETPacket.java
Normal file
82
src/main/java/nsusbloader/com/net/NETPacket.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.net;
|
||||
|
||||
import nsusbloader.NSLMain;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class NETPacket {
|
||||
private static final String CODE_200 =
|
||||
"HTTP/1.0 200 OK\r\n" +
|
||||
"Server: NS-USBloader-"+NSLMain.appVersion+"\r\n" +
|
||||
"Date: %s\r\n" +
|
||||
"Content-type: application/octet-stream\r\n" +
|
||||
"Accept-Ranges: bytes\r\n" +
|
||||
"Content-Range: bytes 0-%d/%d\r\n" +
|
||||
"Content-Length: %d\r\n" +
|
||||
"Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT\r\n\r\n";
|
||||
private static final String CODE_206 =
|
||||
"HTTP/1.0 206 Partial Content\r\n"+
|
||||
"Server: NS-USBloader-"+NSLMain.appVersion+"\r\n" +
|
||||
"Date: %s\r\n" +
|
||||
"Content-type: application/octet-stream\r\n"+
|
||||
"Accept-Ranges: bytes\r\n"+
|
||||
"Content-Range: bytes %d-%d/%d\r\n"+
|
||||
"Content-Length: %d\r\n"+
|
||||
"Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT\r\n\r\n";
|
||||
private static final String CODE_400 =
|
||||
"HTTP/1.0 400 invalid range\r\n"+
|
||||
"Server: NS-USBloader-"+NSLMain.appVersion+"\r\n" +
|
||||
"Date: %s\r\n" +
|
||||
"Connection: close\r\n"+
|
||||
"Content-Type: text/html;charset=utf-8\r\n"+
|
||||
"Content-Length: 0\r\n\r\n";
|
||||
private static final String CODE_404 =
|
||||
"HTTP/1.0 404 Not Found\r\n"+
|
||||
"Server: NS-USBloader-"+NSLMain.appVersion+"\r\n" +
|
||||
"Date: %s\r\n" +
|
||||
"Connection: close\r\n"+
|
||||
"Content-Type: text/html;charset=utf-8\r\n"+
|
||||
"Content-Length: 0\r\n\r\n";
|
||||
private static final String CODE_416 =
|
||||
"HTTP/1.0 416 Requested Range Not Satisfiable\r\n"+
|
||||
"Server: NS-USBloader-"+NSLMain.appVersion+"\r\n" +
|
||||
"Date: %s\r\n" +
|
||||
"Connection: close\r\n"+
|
||||
"Content-Type: text/html;charset=utf-8\r\n"+
|
||||
"Content-Length: 0\r\n\r\n";
|
||||
public static String getCode200(long nspFileSize){
|
||||
return String.format(CODE_200, ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME), nspFileSize-1, nspFileSize, nspFileSize);
|
||||
}
|
||||
public static String getCode206(long nspFileSize, long startPos, long endPos){
|
||||
return String.format(CODE_206, ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME), startPos, endPos, nspFileSize, endPos-startPos+1);
|
||||
}
|
||||
public static String getCode404(){
|
||||
return String.format(CODE_404, ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
public static String getCode416(){
|
||||
return String.format(CODE_416, ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
public static String getCode400(){
|
||||
return String.format(CODE_400, ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
}
|
218
src/main/java/nsusbloader/com/net/NetworkSetupValidator.java
Normal file
218
src/main/java/nsusbloader/com/net/NetworkSetupValidator.java
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.net;
|
||||
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
public class NetworkSetupValidator {
|
||||
|
||||
private String hostIP;
|
||||
private int hostPort;
|
||||
private final HashMap<String, UniFile> files;
|
||||
private ServerSocket serverSocket;
|
||||
private final boolean valid;
|
||||
private final ILogPrinter logPrinter;
|
||||
|
||||
private final boolean doNotServe;
|
||||
|
||||
NetworkSetupValidator(List<File> filesList,
|
||||
boolean doNotServe,
|
||||
String hostIP,
|
||||
String hostPortNum,
|
||||
ILogPrinter logPrinter) {
|
||||
this.files = new HashMap<>();
|
||||
this.logPrinter = logPrinter;
|
||||
this.doNotServe = doNotServe;
|
||||
|
||||
try {
|
||||
validateFiles(filesList);
|
||||
encodeAndAddFilesToMap(filesList);
|
||||
resolveIp(hostIP);
|
||||
resolvePort(hostPortNum);
|
||||
}
|
||||
catch (Exception e){
|
||||
logPrinter.print(e.getMessage(), EMsgType.FAIL);
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
|
||||
private void validateFiles(List<File> filesList) {
|
||||
filesList.removeIf(f -> {
|
||||
if (f.isFile())
|
||||
return false;
|
||||
|
||||
File[] subFiles = f.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||
|
||||
if (subFiles == null || subFiles.length == 0) {
|
||||
logPrinter.print("NET: Exclude folder: " + f.getName(), EMsgType.WARNING);
|
||||
return true;
|
||||
}
|
||||
|
||||
Arrays.sort(subFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));
|
||||
|
||||
for (int i = subFiles.length - 2; i > 0 ; i--){
|
||||
if (subFiles[i].length() != subFiles[i-1].length()) {
|
||||
logPrinter.print("NET: Exclude split file: "+f.getName()+
|
||||
"\n Chunk sizes of the split file are not the same, but has to be.", EMsgType.WARNING);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
long firstFileLength = subFiles[0].length();
|
||||
long lastFileLength = subFiles[subFiles.length-1].length();
|
||||
|
||||
if (lastFileLength > firstFileLength){
|
||||
logPrinter.print("NET: Exclude split file: "+f.getName()+
|
||||
"\n Chunk sizes of the split file are not the same, but has to be.", EMsgType.WARNING);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void encodeAndAddFilesToMap(List<File> filesList) throws UnsupportedEncodingException, FileNotFoundException {
|
||||
for (File file : filesList){
|
||||
String encodedName = URLEncoder.encode(file.getName(), "UTF-8").replaceAll("\\+", "%20"); // replace '+' to '%20'
|
||||
UniFile uniFile = new UniFile(file);
|
||||
files.put(encodedName, uniFile);
|
||||
}
|
||||
|
||||
if (files.size() == 0) {
|
||||
throw new FileNotFoundException("NET: No files to send.");
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveIp(String hostIPaddr) throws IOException{
|
||||
if (! hostIPaddr.isEmpty()){
|
||||
this.hostIP = hostIPaddr;
|
||||
logPrinter.print("NET: Host IP defined as: " + hostIP, EMsgType.PASS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (findIpUsingHost("google.com"))
|
||||
return;
|
||||
|
||||
if (findIpUsingHost("people.com.cn"))
|
||||
return;
|
||||
|
||||
throw new IOException("Try using 'Expert mode' and set IP manually. " + getAvaliableIpExamples());
|
||||
}
|
||||
|
||||
private boolean findIpUsingHost(String host) {
|
||||
try {
|
||||
Socket scoketK;
|
||||
scoketK = new Socket();
|
||||
scoketK.connect(new InetSocketAddress(host, 80));
|
||||
hostIP = scoketK.getLocalAddress().getHostAddress();
|
||||
scoketK.close();
|
||||
|
||||
logPrinter.print("NET: Host IP detected as: " + hostIP, EMsgType.PASS);
|
||||
return true;
|
||||
}
|
||||
catch (IOException e){
|
||||
logPrinter.print("NET: Can't get your computer IP using "
|
||||
+ host
|
||||
+ " server (InetSocketAddress). Returned:\n\t"+e.getMessage(), EMsgType.INFO);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getAvaliableIpExamples(){
|
||||
try {
|
||||
StringBuilder builder = new StringBuilder("Check for:\n");
|
||||
Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
NetworkInterface n = enumeration.nextElement();
|
||||
Enumeration<InetAddress> enumeration1 = n.getInetAddresses();
|
||||
while (enumeration1.hasMoreElements()){
|
||||
builder.append("- ");
|
||||
builder.append(enumeration1.nextElement().getHostAddress());
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
catch (SocketException socketException) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private void resolvePort(String hostPortNum) throws Exception{
|
||||
if (! hostPortNum.isEmpty()) {
|
||||
parsePort(hostPortNum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (doNotServe)
|
||||
throw new Exception("NET: Port must be defined if 'Don't serve requests' option selected!");
|
||||
|
||||
findPort();
|
||||
}
|
||||
|
||||
private void findPort() throws Exception{
|
||||
Random portRandomizer = new Random();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
try {
|
||||
this.hostPort = portRandomizer.nextInt(999) + 6000;
|
||||
serverSocket = new ServerSocket(hostPort); //System.out.println(serverSocket.getInetAddress()); 0.0.0.0
|
||||
logPrinter.print("NET: Your port detected as: " + hostPort, EMsgType.PASS);
|
||||
break;
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
if (i == 4) {
|
||||
throw new Exception("NET: Can't find good port\n"
|
||||
+ "Set port by in settings ('Expert mode').");
|
||||
}
|
||||
|
||||
logPrinter.print("NET: Can't use port " + hostPort + "\nLooking for another one.", EMsgType.WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parsePort(String hostPortNum) throws Exception{
|
||||
try {
|
||||
this.hostPort = Integer.parseInt(hostPortNum);
|
||||
|
||||
if (doNotServe)
|
||||
return;
|
||||
|
||||
serverSocket = new ServerSocket(hostPort);
|
||||
logPrinter.print("NET: Using defined port number: " + hostPort, EMsgType.PASS);
|
||||
}
|
||||
catch (IllegalArgumentException | IOException eee){
|
||||
throw new Exception("NET: Can't use port defined in settings: " + hostPortNum + "\n\t"+eee.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
String getHostIP() { return hostIP; }
|
||||
int getHostPort() { return hostPort; }
|
||||
HashMap<String, UniFile> getFiles() { return files; }
|
||||
ServerSocket getServerSocket() { return serverSocket; }
|
||||
boolean isValid() { return valid; }
|
||||
}
|
44
src/main/java/nsusbloader/com/net/UniFile.java
Normal file
44
src/main/java/nsusbloader/com/net/UniFile.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.net;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
class UniFile {
|
||||
private final long size;
|
||||
private final File file;
|
||||
|
||||
UniFile(File file) {
|
||||
this.file = file;
|
||||
|
||||
if (file.isFile()) {
|
||||
size = file.length();
|
||||
}
|
||||
else {
|
||||
long fSize = 0;
|
||||
File[] subFiles = file.listFiles((myFile, name) -> name.matches("[0-9]{2}"));
|
||||
for (File subFile : subFiles)
|
||||
fSize += subFile.length();
|
||||
size = fSize;
|
||||
}
|
||||
}
|
||||
|
||||
public long getSize() { return size; }
|
||||
public File getFile() { return file; }
|
||||
}
|
393
src/main/java/nsusbloader/com/usb/GoldLeaf_05.java
Normal file
393
src/main/java/nsusbloader/com/usb/GoldLeaf_05.java
Normal file
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb;
|
||||
|
||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import nsusbloader.com.helpers.NSSplitReader;
|
||||
import nsusbloader.com.usb.PFS.PFSProvider;
|
||||
import org.usb4java.DeviceHandle;
|
||||
import org.usb4java.LibUsb;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* GoldLeaf processing
|
||||
* */
|
||||
public class GoldLeaf_05 extends TransferModule {
|
||||
// CMD G L U C
|
||||
private static final byte[] CMD_GLUC = new byte[]{0x47, 0x4c, 0x55, 0x43};
|
||||
private static final byte[] CMD_ConnectionRequest = new byte[]{0x00, 0x00, 0x00, 0x00}; // Write-only command
|
||||
private static final byte[] CMD_NSPName = new byte[]{0x02, 0x00, 0x00, 0x00}; // Write-only command
|
||||
private static final byte[] CMD_NSPData = new byte[]{0x04, 0x00, 0x00, 0x00}; // Write-only command
|
||||
|
||||
private static final byte[] CMD_ConnectionResponse = new byte[]{0x01, 0x00, 0x00, 0x00};
|
||||
private static final byte[] CMD_Start = new byte[]{0x03, 0x00, 0x00, 0x00};
|
||||
private static final byte[] CMD_NSPContent = new byte[]{0x05, 0x00, 0x00, 0x00};
|
||||
private static final byte[] CMD_NSPTicket = new byte[]{0x06, 0x00, 0x00, 0x00};
|
||||
private static final byte[] CMD_Finish = new byte[]{0x07, 0x00, 0x00, 0x00};
|
||||
|
||||
private RandomAccessFile raf; // NSP File
|
||||
private NSSplitReader nsr; // It'a also NSP File
|
||||
|
||||
GoldLeaf_05(DeviceHandle handler, LinkedHashMap<String, File> nspMap, CancellableRunnable task, ILogPrinter logPrinter){
|
||||
super(handler, nspMap, task, logPrinter);
|
||||
|
||||
this.task = task;
|
||||
status = EFileStatus.FAILED;
|
||||
|
||||
logPrinter.print("============= GoldLeaf v0.5 =============\n" +
|
||||
" Only one file per time could be sent. In case you selected more the first one would be picked.", EMsgType.INFO);
|
||||
if (nspMap.isEmpty()){
|
||||
logPrinter.print("For using this GoldLeaf version you have to add file to the table and select it for upload", EMsgType.INFO);
|
||||
return;
|
||||
}
|
||||
File nspFile = (File) nspMap.values().toArray()[0];
|
||||
logPrinter.print("File for upload: "+nspFile.getAbsolutePath(), EMsgType.INFO);
|
||||
|
||||
if (!nspFile.getName().toLowerCase().endsWith(".nsp")) {
|
||||
logPrinter.print("GL This file doesn't look like NSP", EMsgType.FAIL);
|
||||
return;
|
||||
}
|
||||
PFSProvider pfsElement;
|
||||
try{
|
||||
pfsElement = new PFSProvider(nspFile, logPrinter);
|
||||
}
|
||||
catch (Exception e){
|
||||
logPrinter.print("GL File provided has incorrect structure and won't be uploaded\n\t"+e.getMessage(), EMsgType.FAIL);
|
||||
status = EFileStatus.INCORRECT_FILE_FAILED;
|
||||
return;
|
||||
}
|
||||
logPrinter.print("GL File structure validated and it will be uploaded", EMsgType.PASS);
|
||||
|
||||
try{
|
||||
if (nspFile.isDirectory())
|
||||
this.nsr = new NSSplitReader(nspFile, 0);
|
||||
else
|
||||
this.raf = new RandomAccessFile(nspFile, "r");
|
||||
}
|
||||
catch (IOException ioe){
|
||||
logPrinter.print("GL File not found\n\t"+ioe.getMessage(), EMsgType.FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Go parse commands
|
||||
byte[] readByte;
|
||||
|
||||
// Go connect to GoldLeaf
|
||||
if (writeUsb(CMD_GLUC)) {
|
||||
logPrinter.print("GL Initiating GoldLeaf connection [1/2]", EMsgType.FAIL);
|
||||
return;
|
||||
}
|
||||
logPrinter.print("GL Initiating GoldLeaf connection: [1/2]", EMsgType.PASS);
|
||||
if (writeUsb(CMD_ConnectionRequest)){
|
||||
logPrinter.print("GL Initiating GoldLeaf connection: [2/2]", EMsgType.FAIL);
|
||||
return;
|
||||
}
|
||||
logPrinter.print("GL Initiating GoldLeaf connection: [2/2]", EMsgType.PASS);
|
||||
|
||||
while (true) {
|
||||
readByte = readUsb();
|
||||
if (readByte == null)
|
||||
return;
|
||||
|
||||
if (Arrays.equals(readByte, CMD_GLUC)) {
|
||||
if ((readByte = readUsb()) == null)
|
||||
return;
|
||||
|
||||
if (Arrays.equals(readByte, CMD_ConnectionResponse)) {
|
||||
if (handleConnectionResponse(pfsElement))
|
||||
return;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (Arrays.equals(readByte, CMD_Start)) {
|
||||
if (handleStart(pfsElement))
|
||||
return;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (Arrays.equals(readByte, CMD_NSPContent)) {
|
||||
if (handleNSPContent(pfsElement, true))
|
||||
return;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (Arrays.equals(readByte, CMD_NSPTicket)) {
|
||||
if (handleNSPContent(pfsElement, false))
|
||||
return;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (Arrays.equals(readByte, CMD_Finish)) {
|
||||
logPrinter.print("GL Closing GoldLeaf connection: Transfer successful.", EMsgType.PASS);
|
||||
status = EFileStatus.UPLOADED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
raf.close();
|
||||
}
|
||||
catch (IOException | NullPointerException ignored){}
|
||||
try {
|
||||
nsr.close();
|
||||
}
|
||||
catch (IOException | NullPointerException ignored){}
|
||||
}
|
||||
/**
|
||||
* ConnectionResponse command handler
|
||||
* @return true if failed
|
||||
* false if no issues
|
||||
* */
|
||||
private boolean handleConnectionResponse(PFSProvider pfsElement){
|
||||
logPrinter.print("GL 'ConnectionResponse' command:", EMsgType.INFO);
|
||||
if (writeUsb(CMD_GLUC)) {
|
||||
logPrinter.print(" [1/4]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [1/4]", EMsgType.PASS);
|
||||
if (writeUsb(CMD_NSPName)) {
|
||||
logPrinter.print(" [2/4]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [2/4]", EMsgType.PASS);
|
||||
|
||||
if (writeUsb(pfsElement.getBytesNspFileNameLength())) {
|
||||
logPrinter.print(" [3/4]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [3/4]", EMsgType.PASS);
|
||||
|
||||
if (writeUsb(pfsElement.getBytesNspFileName())) {
|
||||
logPrinter.print(" [4/4]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [4/4]", EMsgType.PASS);
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Start command handler
|
||||
* @return true if failed
|
||||
* false if no issues
|
||||
* */
|
||||
private boolean handleStart(PFSProvider pfsElement){
|
||||
logPrinter.print("GL Handle 'Start' command:", EMsgType.INFO);
|
||||
if (writeUsb(CMD_GLUC)) {
|
||||
logPrinter.print(" [Prefix]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [Prefix]", EMsgType.PASS);
|
||||
|
||||
if (writeUsb(CMD_NSPData)) {
|
||||
logPrinter.print(" [Command]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [Command]", EMsgType.PASS);
|
||||
|
||||
if (writeUsb(pfsElement.getBytesCountOfNca())) {
|
||||
logPrinter.print(" [Sub-files count]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [Sub-files count]", EMsgType.PASS);
|
||||
|
||||
int ncaCount = pfsElement.getIntCountOfNca();
|
||||
logPrinter.print(" [Information for "+ncaCount+" sub-files]", EMsgType.INFO);
|
||||
for (int i = 0; i < ncaCount; i++){
|
||||
logPrinter.print("File #"+i, EMsgType.INFO);
|
||||
if (writeUsb(pfsElement.getNca(i).getNcaFileNameLength())) {
|
||||
logPrinter.print(" [1/4] Name length", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [1/4] Name length", EMsgType.PASS);
|
||||
|
||||
if (writeUsb(pfsElement.getNca(i).getNcaFileName())) {
|
||||
logPrinter.print(" [2/4] Name", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [2/4] Name", EMsgType.PASS);
|
||||
if (writeUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getBodySize()+pfsElement.getNca(i).getNcaOffset()).array())) { // offset. real.
|
||||
logPrinter.print(" [3/4] Offset", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [3/4] Offset", EMsgType.PASS);
|
||||
if (writeUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getNca(i).getNcaSize()).array())) { // size
|
||||
logPrinter.print(" [4/4] Size", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
logPrinter.print(" [4/4] Size", EMsgType.PASS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* NSPContent command handler
|
||||
* @param isItRawRequest true: just ask NS what's needed
|
||||
* false: send ticket
|
||||
* @return true if failed
|
||||
* false if no issues
|
||||
* */
|
||||
private boolean handleNSPContent(PFSProvider pfsElement, boolean isItRawRequest){
|
||||
int requestedNcaID;
|
||||
|
||||
if (isItRawRequest) {
|
||||
logPrinter.print("GL Handle 'Content' command", EMsgType.INFO);
|
||||
byte[] readByte = readUsb();
|
||||
if (readByte == null || readByte.length != 4) {
|
||||
logPrinter.print(" [Read requested ID]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
requestedNcaID = ByteBuffer.wrap(readByte).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
logPrinter.print(" [Read requested ID = "+requestedNcaID+" ]", EMsgType.PASS);
|
||||
}
|
||||
else {
|
||||
requestedNcaID = pfsElement.getNcaTicketID();
|
||||
logPrinter.print("GL Handle 'Ticket' command (ID = "+requestedNcaID+" )", EMsgType.INFO);
|
||||
}
|
||||
|
||||
long realNcaOffset = pfsElement.getNca(requestedNcaID).getNcaOffset()+pfsElement.getBodySize();
|
||||
long realNcaSize = pfsElement.getNca(requestedNcaID).getNcaSize();
|
||||
|
||||
long readFrom = 0;
|
||||
|
||||
int readPice = 8388608; // 8mb
|
||||
byte[] readBuf;
|
||||
|
||||
try{
|
||||
if (raf == null){
|
||||
nsr.seek(realNcaOffset);
|
||||
|
||||
while (readFrom < realNcaSize){
|
||||
if (realNcaSize - readFrom < readPice)
|
||||
readPice = Math.toIntExact(realNcaSize - readFrom); // it's safe, I guarantee
|
||||
readBuf = new byte[readPice];
|
||||
if (nsr.read(readBuf) != readPice)
|
||||
return true;
|
||||
//System.out.println("S: "+readFrom+" T: "+realNcaSize+" P: "+readPice); // DEBUG
|
||||
if (writeUsb(readBuf))
|
||||
return true;
|
||||
//-----------------------------------------/
|
||||
logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0);
|
||||
//-----------------------------------------/
|
||||
readFrom += readPice;
|
||||
}
|
||||
}
|
||||
else {
|
||||
raf.seek(realNcaOffset);
|
||||
|
||||
while (readFrom < realNcaSize){
|
||||
if (realNcaSize - readFrom < readPice)
|
||||
readPice = Math.toIntExact(realNcaSize - readFrom); // it's safe, I guarantee
|
||||
readBuf = new byte[readPice];
|
||||
if (raf.read(readBuf) != readPice)
|
||||
return true;
|
||||
//System.out.println("S: "+readFrom+" T: "+realNcaSize+" P: "+readPice); // DEBUG
|
||||
if (writeUsb(readBuf))
|
||||
return true;
|
||||
//-----------------------------------------/
|
||||
logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0);
|
||||
//-----------------------------------------/
|
||||
readFrom += readPice;
|
||||
}
|
||||
}
|
||||
//-----------------------------------------/
|
||||
logPrinter.updateProgress(1.0);
|
||||
//-----------------------------------------/
|
||||
}
|
||||
catch (IOException ioe){
|
||||
logPrinter.print("GL Failed to read NCA ID "+requestedNcaID+". IO Exception:\n "+ioe.getMessage(), EMsgType.FAIL);
|
||||
ioe.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sending any byte array to USB device
|
||||
* @return 'false' if no issues
|
||||
* 'true' if errors happened
|
||||
* */
|
||||
private boolean writeUsb(byte[] message){
|
||||
ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
|
||||
writeBuffer.put(message); // Don't do writeBuffer.rewind();
|
||||
IntBuffer writeBufTransferred = IntBuffer.allocate(1);
|
||||
int result;
|
||||
|
||||
while (! task.isCancelled()) {
|
||||
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
|
||||
|
||||
switch (result){
|
||||
case LibUsb.SUCCESS:
|
||||
if (writeBufTransferred.get() == message.length)
|
||||
return false;
|
||||
else {
|
||||
logPrinter.print("GL Data transfer issue [write]\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
case LibUsb.ERROR_TIMEOUT:
|
||||
continue;
|
||||
default:
|
||||
logPrinter.print("GL Data transfer issue [write]\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
|
||||
logPrinter.print("GL Execution stopped", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
logPrinter.print("GL Execution interrupted", EMsgType.INFO);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Reading what USB device responded.
|
||||
* @return byte array if data read successful
|
||||
* 'null' if read failed
|
||||
* */
|
||||
private byte[] readUsb(){
|
||||
ByteBuffer readBuffer = ByteBuffer.allocateDirect(512);
|
||||
// We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
|
||||
IntBuffer readBufTransferred = IntBuffer.allocate(1);
|
||||
|
||||
int result;
|
||||
while (! task.isCancelled()) {
|
||||
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
|
||||
|
||||
switch (result) {
|
||||
case LibUsb.SUCCESS:
|
||||
int trans = readBufTransferred.get();
|
||||
byte[] receivedBytes = new byte[trans];
|
||||
readBuffer.get(receivedBytes);
|
||||
return receivedBytes;
|
||||
case LibUsb.ERROR_TIMEOUT:
|
||||
continue;
|
||||
default:
|
||||
logPrinter.print("GL Data transfer issue [read]\n Returned: " + UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
|
||||
logPrinter.print("GL Execution stopped", EMsgType.FAIL);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
logPrinter.print("GL Execution interrupted", EMsgType.INFO);
|
||||
return null;
|
||||
}
|
||||
}
|
1204
src/main/java/nsusbloader/com/usb/GoldLeaf_07.java
Normal file
1204
src/main/java/nsusbloader/com/usb/GoldLeaf_07.java
Normal file
File diff suppressed because it is too large
Load diff
1134
src/main/java/nsusbloader/com/usb/GoldLeaf_08.java
Normal file
1134
src/main/java/nsusbloader/com/usb/GoldLeaf_08.java
Normal file
File diff suppressed because it is too large
Load diff
43
src/main/java/nsusbloader/com/usb/PFS/NCAFile.java
Normal file
43
src/main/java/nsusbloader/com/usb/PFS/NCAFile.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.PFS;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Data class to hold NCA, tik, xml etc. meta-information
|
||||
* */
|
||||
public class NCAFile {
|
||||
//private int ncaNumber;
|
||||
private byte[] ncaFileName;
|
||||
private long ncaOffset;
|
||||
private long ncaSize;
|
||||
|
||||
//public void setNcaNumber(int ncaNumber){ this.ncaNumber = ncaNumber; }
|
||||
void setNcaFileName(byte[] ncaFileName) { this.ncaFileName = ncaFileName; }
|
||||
void setNcaOffset(long ncaOffset) { this.ncaOffset = ncaOffset; }
|
||||
void setNcaSize(long ncaSize) { this.ncaSize = ncaSize; }
|
||||
|
||||
//public int getNcaNumber() {return this.ncaNumber; }
|
||||
public byte[] getNcaFileName() { return ncaFileName; }
|
||||
public byte[] getNcaFileNameLength() { return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ncaFileName.length).array(); }
|
||||
public long getNcaOffset() { return ncaOffset; }
|
||||
public long getNcaSize() { return ncaSize; }
|
||||
}
|
205
src/main/java/nsusbloader/com/usb/PFS/PFSProvider.java
Normal file
205
src/main/java/nsusbloader/com/usb/PFS/PFSProvider.java
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.PFS;
|
||||
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Used in GoldLeaf USB protocol
|
||||
* */
|
||||
public class PFSProvider {
|
||||
private static final byte[] PFS0 = new byte[]{0x50, 0x46, 0x53, 0x30}; // PFS0
|
||||
|
||||
private String nspFileName;
|
||||
private NCAFile[] ncaFiles;
|
||||
private long bodySize;
|
||||
private int ticketID = -1;
|
||||
|
||||
public PFSProvider(File nspFile, ILogPrinter logPrinter) throws Exception{
|
||||
if (nspFile.isDirectory()) {
|
||||
nspFileName = nspFile.getName();
|
||||
nspFile = new File(nspFile.getAbsolutePath() + File.separator + "00");
|
||||
}
|
||||
else
|
||||
nspFileName = nspFile.getName();
|
||||
|
||||
RandomAccessFile randAccessFile = new RandomAccessFile(nspFile, "r");
|
||||
|
||||
int filesCount;
|
||||
int header;
|
||||
|
||||
logPrinter.print("PFS Start NSP file analyze for ["+nspFileName+"]", EMsgType.INFO);
|
||||
|
||||
byte[] fileStartingBytes = new byte[12];
|
||||
// Read PFS0, files count, header, padding (4 zero bytes)
|
||||
if (randAccessFile.read(fileStartingBytes) == 12)
|
||||
logPrinter.print("PFS Read file starting bytes.", EMsgType.PASS);
|
||||
else {
|
||||
logPrinter.print("PFS Read file starting bytes.", EMsgType.FAIL);
|
||||
randAccessFile.close();
|
||||
throw new Exception("Unable to read file starting bytes");
|
||||
}
|
||||
// Check PFS0
|
||||
if (Arrays.equals(PFS0, Arrays.copyOfRange(fileStartingBytes, 0, 4)))
|
||||
logPrinter.print("PFS Read 'PFS0'.", EMsgType.PASS);
|
||||
else
|
||||
logPrinter.print("PFS Read 'PFS0': this file looks wired.", EMsgType.WARNING);
|
||||
// Get files count
|
||||
filesCount = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 4, 8)).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
if (filesCount > 0 ) {
|
||||
logPrinter.print("PFS Read files count [" + filesCount + "]", EMsgType.PASS);
|
||||
}
|
||||
else {
|
||||
logPrinter.print("PFS Read files count", EMsgType.FAIL);
|
||||
randAccessFile.close();
|
||||
throw new Exception("Unable to read file count");
|
||||
}
|
||||
// Get header
|
||||
header = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 8, 12)).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
if (header > 0 )
|
||||
logPrinter.print("PFS Read header ["+header+"]", EMsgType.PASS);
|
||||
else {
|
||||
logPrinter.print("PFS Read header ", EMsgType.FAIL);
|
||||
randAccessFile.close();
|
||||
throw new Exception("Unable to read header");
|
||||
}
|
||||
//*********************************************************************************************
|
||||
// Create NCA set
|
||||
this.ncaFiles = new NCAFile[filesCount];
|
||||
// Collect files from NSP
|
||||
byte[] ncaInfoArr = new byte[24]; // should be unsigned long, but.. java.. u know my pain man
|
||||
|
||||
HashMap<Integer, Long> ncaNameOffsets = new LinkedHashMap<>();
|
||||
|
||||
int offset;
|
||||
long nca_offset;
|
||||
long nca_size;
|
||||
long nca_name_offset;
|
||||
|
||||
for (int i=0; i<filesCount; i++){
|
||||
if (randAccessFile.read(ncaInfoArr) == 24) {
|
||||
logPrinter.print("PFS Read NCA inside NSP: " + i, EMsgType.PASS);
|
||||
}
|
||||
else {
|
||||
logPrinter.print("PFS Read NCA inside NSP: "+i, EMsgType.FAIL);
|
||||
randAccessFile.close();
|
||||
throw new Exception("Unable to read NCA inside NSP");
|
||||
}
|
||||
offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 0, 4)).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
nca_offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 4, 12)).order(ByteOrder.LITTLE_ENDIAN).getLong();
|
||||
nca_size = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 12, 20)).order(ByteOrder.LITTLE_ENDIAN).getLong();
|
||||
nca_name_offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 20, 24)).order(ByteOrder.LITTLE_ENDIAN).getInt(); // yes, cast from int to long.
|
||||
|
||||
logPrinter.print(" Padding check", offset == 0?EMsgType.PASS:EMsgType.WARNING);
|
||||
logPrinter.print(" NCA offset check: "+nca_offset, nca_offset >= 0?EMsgType.PASS:EMsgType.WARNING);
|
||||
logPrinter.print(" NCA size check: "+nca_size, nca_size >= 0?EMsgType.PASS: EMsgType.WARNING);
|
||||
logPrinter.print(" NCA name offset check: "+nca_name_offset, nca_name_offset >= 0?EMsgType.PASS:EMsgType.WARNING);
|
||||
|
||||
NCAFile ncaFile = new NCAFile();
|
||||
ncaFile.setNcaOffset(nca_offset);
|
||||
ncaFile.setNcaSize(nca_size);
|
||||
this.ncaFiles[i] = ncaFile;
|
||||
|
||||
ncaNameOffsets.put(i, nca_name_offset);
|
||||
}
|
||||
// Final offset
|
||||
byte[] bufForInt = new byte[4];
|
||||
if ((randAccessFile.read(bufForInt) == 4) && (Arrays.equals(bufForInt, new byte[4])))
|
||||
logPrinter.print("PFS Final padding check", EMsgType.PASS);
|
||||
else
|
||||
logPrinter.print("PFS Final padding check", EMsgType.WARNING);
|
||||
|
||||
// Calculate position including header for body size offset
|
||||
bodySize = randAccessFile.getFilePointer()+header;
|
||||
//*********************************************************************************************
|
||||
// Collect file names from NCAs
|
||||
logPrinter.print("PFS Collecting file names", EMsgType.INFO);
|
||||
List<Byte> ncaFN; // Temporary
|
||||
byte[] b = new byte[1]; // Temporary
|
||||
for (int i=0; i<filesCount; i++){
|
||||
ncaFN = new ArrayList<>();
|
||||
randAccessFile.seek(filesCount*24+16+ncaNameOffsets.get(i)); // Files cont * 24(bit for each meta-data) + 4 bytes goes after all of them + 12 bit what were in the beginning
|
||||
while ((randAccessFile.read(b)) != -1){
|
||||
if (b[0] == 0x00)
|
||||
break;
|
||||
else
|
||||
ncaFN.add(b[0]);
|
||||
}
|
||||
byte[] exchangeTempArray = new byte[ncaFN.size()];
|
||||
for (int j=0; j < ncaFN.size(); j++)
|
||||
exchangeTempArray[j] = ncaFN.get(j);
|
||||
// Find and store ticket (.tik)
|
||||
if (new String(exchangeTempArray, StandardCharsets.UTF_8).toLowerCase().endsWith(".tik"))
|
||||
this.ticketID = i;
|
||||
this.ncaFiles[i].setNcaFileName(Arrays.copyOf(exchangeTempArray, exchangeTempArray.length));
|
||||
}
|
||||
randAccessFile.close();
|
||||
logPrinter.print("PFS Finished NSP file analyze for ["+nspFileName+"]", EMsgType.PASS);
|
||||
}
|
||||
/**
|
||||
* Return file name as byte array
|
||||
* */
|
||||
public byte[] getBytesNspFileName(){
|
||||
return nspFileName.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
/**
|
||||
* Return file name length as byte array
|
||||
* */
|
||||
public byte[] getBytesNspFileNameLength(){
|
||||
return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(getBytesNspFileName().length).array();
|
||||
}
|
||||
/**
|
||||
* Return NCA count inside of file as byte array
|
||||
* */
|
||||
public byte[] getBytesCountOfNca(){
|
||||
return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ncaFiles.length).array();
|
||||
}
|
||||
/**
|
||||
* Return NCA count inside of file as int
|
||||
* */
|
||||
public int getIntCountOfNca(){
|
||||
return ncaFiles.length;
|
||||
}
|
||||
/**
|
||||
* Return requested-by-number NCA file inside of file
|
||||
* */
|
||||
public NCAFile getNca(int ncaNumber){
|
||||
return ncaFiles[ncaNumber];
|
||||
}
|
||||
/**
|
||||
* Return bodySize
|
||||
* */
|
||||
public long getBodySize(){
|
||||
return bodySize;
|
||||
}
|
||||
/**
|
||||
* Return special NCA file: ticket
|
||||
* (sugar)
|
||||
* */
|
||||
public int getNcaTicketID(){
|
||||
return ticketID;
|
||||
}
|
||||
}
|
367
src/main/java/nsusbloader/com/usb/TinFoil.java
Normal file
367
src/main/java/nsusbloader/com/usb/TinFoil.java
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb;
|
||||
|
||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import nsusbloader.com.helpers.NSSplitReader;
|
||||
import org.usb4java.DeviceHandle;
|
||||
import org.usb4java.LibUsb;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Tinfoil processing
|
||||
* */
|
||||
class TinFoil extends TransferModule {
|
||||
// "TUL0".getBytes(StandardCharsets.US_ASCII)
|
||||
private static final byte[] TUL0 = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x4c, (byte) 0x30};
|
||||
private static final byte[] MAGIC = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30}; // aka 'TUC0' ASCII
|
||||
|
||||
private static final byte CMD_EXIT = 0x00;
|
||||
private static final byte CMD_FILE_RANGE_DEFAULT = 0x01;
|
||||
private static final byte CMD_FILE_RANGE_ALTERNATIVE = 0x02;
|
||||
/* byte[] magic = new byte[4];
|
||||
ByteBuffer bb = StandardCharsets.UTF_8.encode("TUC0").rewind().get(magic); // Let's rephrase this 'string' */
|
||||
|
||||
TinFoil(DeviceHandle handler, LinkedHashMap<String, File> nspMap, CancellableRunnable task, ILogPrinter logPrinter){
|
||||
super(handler, nspMap, task, logPrinter);
|
||||
logPrinter.print("============= Tinfoil =============", EMsgType.INFO);
|
||||
|
||||
if (! sendListOfFiles())
|
||||
return;
|
||||
|
||||
if (proceedCommands()) // REPORT SUCCESS
|
||||
status = EFileStatus.UPLOADED; // Don't change status that is already set to FAILED
|
||||
}
|
||||
/**
|
||||
* Send what NSP will be transferred
|
||||
* */
|
||||
private boolean sendListOfFiles(){
|
||||
final String fileNamesListToSend = getFileNamesToSend();
|
||||
|
||||
byte[] nspListNames = getFileNamesToSendAsBytes(fileNamesListToSend);
|
||||
byte[] nspListNamesSize = getFileNamesLengthToSendAsBytes(nspListNames);
|
||||
byte[] padding = new byte[8];
|
||||
|
||||
if (writeUsb(TUL0)) {
|
||||
logPrinter.print("TF Send list of files: handshake [1/4]", EMsgType.FAIL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeUsb(nspListNamesSize)) { // size of the list we can transfer
|
||||
logPrinter.print("TF Send list of files: list length [2/4]", EMsgType.FAIL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeUsb(padding)) {
|
||||
logPrinter.print("TF Send list of files: padding [3/4]", EMsgType.FAIL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeUsb(nspListNames)) {
|
||||
logPrinter.print("TF Send list of files: list itself [4/4]", EMsgType.FAIL);
|
||||
return false;
|
||||
}
|
||||
logPrinter.print("TF Send list of files complete.", EMsgType.PASS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getFileNamesToSend(){
|
||||
StringBuilder fileNamesListBuilder = new StringBuilder();
|
||||
for(String nspFileName: nspMap.keySet()) {
|
||||
fileNamesListBuilder.append(nspFileName); // And here we come with java string default encoding (UTF-16)
|
||||
fileNamesListBuilder.append('\n');
|
||||
}
|
||||
return fileNamesListBuilder.toString();
|
||||
}
|
||||
|
||||
private byte[] getFileNamesToSendAsBytes(String fileNamesListToSend){
|
||||
return fileNamesListToSend.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private byte[] getFileNamesLengthToSendAsBytes(byte[] fileNamesListToSendAsBytes){
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN); // integer = 4 bytes; BTW Java is stored in big-endian format
|
||||
byteBuffer.putInt(fileNamesListToSendAsBytes.length); // This way we obtain length in int converted to byte array in correct Big-endian order. Trust me.
|
||||
return byteBuffer.array();
|
||||
}
|
||||
/**
|
||||
* After we sent commands to NS, this chain starts
|
||||
* */
|
||||
private boolean proceedCommands(){
|
||||
logPrinter.print("TF Awaiting for NS commands.", EMsgType.INFO);
|
||||
try{
|
||||
byte[] deviceReply;
|
||||
byte command;
|
||||
|
||||
while (true){
|
||||
deviceReply = readUsb();
|
||||
if (! isReplyValid(deviceReply))
|
||||
continue;
|
||||
command = getCommandFromReply(deviceReply);
|
||||
|
||||
switch (command){
|
||||
case CMD_EXIT:
|
||||
logPrinter.print("TF Transfer complete.", EMsgType.PASS);
|
||||
return true;
|
||||
case CMD_FILE_RANGE_DEFAULT:
|
||||
case CMD_FILE_RANGE_ALTERNATIVE:
|
||||
//logPrinter.print("TF Received 'FILE RANGE' command [0x0"+command+"].", EMsgType.PASS);
|
||||
if (fileRangeCmd())
|
||||
return false; // catches exception
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
logPrinter.print(e.getMessage(), EMsgType.INFO);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReplyValid(byte[] reply){
|
||||
return Arrays.equals(Arrays.copyOfRange(reply, 0,4), MAGIC);
|
||||
}
|
||||
|
||||
private byte getCommandFromReply(byte[] reply){
|
||||
return reply[8];
|
||||
}
|
||||
/**
|
||||
* This is what returns requested file (files)
|
||||
* Executes multiple times
|
||||
* @return 'false' if everything is ok
|
||||
* 'true' is error/exception occurs
|
||||
* */
|
||||
private boolean fileRangeCmd(){
|
||||
try {
|
||||
byte[] receivedArray = readUsb();
|
||||
|
||||
byte[] sizeAsBytes = Arrays.copyOfRange(receivedArray, 0,8);
|
||||
long size = ByteBuffer.wrap(sizeAsBytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); // could be unsigned long. This app won't support files greater then 8796093022208 Gb
|
||||
long offset = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 8,16)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // could be unsigned long. This app doesn't support files greater then 8796093022208 Gb
|
||||
|
||||
// Requesting UTF-8 file name required:
|
||||
receivedArray = readUsb();
|
||||
|
||||
String nspFileName = new String(receivedArray, StandardCharsets.UTF_8);
|
||||
|
||||
logPrinter.print(String.format("TF Reply to: %s" +
|
||||
"\n Offset: %-20d 0x%x" +
|
||||
"\n Size: %-20d 0x%x",
|
||||
nspFileName,
|
||||
offset, offset,
|
||||
size, size), EMsgType.INFO);
|
||||
|
||||
File nspFile = nspMap.get(nspFileName);
|
||||
boolean isSplitFile = nspFile.isDirectory();
|
||||
|
||||
// Sending response 'header'
|
||||
if (sendMetaInfoForFile(sizeAsBytes)) // Get size in 'RAW' format exactly as it has been received to simplify the process.
|
||||
return true;
|
||||
|
||||
if (isSplitFile)
|
||||
sendSplitFile(nspFile, size, offset);
|
||||
else
|
||||
sendNormalFile(nspFile, size, offset);
|
||||
} catch (IOException ioe){
|
||||
logPrinter.print("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL);
|
||||
ioe.printStackTrace();
|
||||
return true;
|
||||
} catch (ArithmeticException ae){
|
||||
logPrinter.print("TF ArithmeticException (can't cast 'offset end' - 'offsets current' to 'integer'):" +
|
||||
"\n "+ae.getMessage(), EMsgType.FAIL);
|
||||
ae.printStackTrace();
|
||||
return true;
|
||||
} catch (NullPointerException npe){
|
||||
logPrinter.print("TF NullPointerException (in some moment application didn't find something. Something important.):" +
|
||||
"\n "+npe.getMessage(), EMsgType.FAIL);
|
||||
npe.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
catch (Exception defe){
|
||||
logPrinter.print(defe.getMessage(), EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void sendSplitFile(File nspFile, long size, long offset) throws IOException, NullPointerException, ArithmeticException {
|
||||
byte[] readBuffer;
|
||||
long currentOffset = 0;
|
||||
int chunk = 8388608; // = 8Mb;
|
||||
|
||||
NSSplitReader nsSplitReader = new NSSplitReader(nspFile, size);
|
||||
if (nsSplitReader.seek(offset) != offset)
|
||||
throw new IOException("TF Requested offset is out of file size. Nothing to transmit.");
|
||||
|
||||
while (currentOffset < size){
|
||||
if ((currentOffset + chunk) >= size )
|
||||
chunk = Math.toIntExact(size - currentOffset);
|
||||
//System.out.println("CO: "+currentOffset+"\t\tEO: "+size+"\t\tRP: "+chunk); // NOTE: DEBUG
|
||||
logPrinter.updateProgress((currentOffset + chunk) / (size / 100.0) / 100.0);
|
||||
|
||||
readBuffer = new byte[chunk]; // TODO: not perfect moment, consider refactoring.
|
||||
|
||||
if (nsSplitReader.read(readBuffer) != chunk)
|
||||
throw new IOException("TF Reading from stream suddenly ended.");
|
||||
|
||||
if (writeUsb(readBuffer))
|
||||
throw new IOException("TF Failure during file transfer.");
|
||||
currentOffset += chunk;
|
||||
}
|
||||
nsSplitReader.close();
|
||||
logPrinter.updateProgress(1.0);
|
||||
}
|
||||
|
||||
void sendNormalFile(File nspFile, long size, long offset) throws IOException, NullPointerException, ArithmeticException {
|
||||
byte[] readBuffer;
|
||||
long currentOffset = 0;
|
||||
int chunk = 8388608;
|
||||
|
||||
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspFile));
|
||||
|
||||
if (bufferedInStream.skip(offset) != offset)
|
||||
throw new IOException("TF Requested offset is out of file size. Nothing to transmit.");
|
||||
|
||||
while (currentOffset < size) {
|
||||
if ((currentOffset + chunk) >= size)
|
||||
chunk = Math.toIntExact(size - currentOffset);
|
||||
//System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+chunk); // NOTE: DEBUG
|
||||
logPrinter.updateProgress((currentOffset + chunk) / (size / 100.0) / 100.0);
|
||||
|
||||
readBuffer = new byte[chunk];
|
||||
|
||||
if (bufferedInStream.read(readBuffer) != chunk)
|
||||
throw new IOException("TF Reading from stream suddenly ended.");
|
||||
|
||||
if (writeUsb(readBuffer))
|
||||
throw new IOException("TF Failure during file transfer.");
|
||||
currentOffset += chunk;
|
||||
}
|
||||
bufferedInStream.close();
|
||||
logPrinter.updateProgress(1.0);
|
||||
}
|
||||
/**
|
||||
* Send response header.
|
||||
* @return false if everything OK
|
||||
* true if failed
|
||||
* */
|
||||
private boolean sendMetaInfoForFile(byte[] sizeAsBytes){
|
||||
final byte[] standardReplyBytes = new byte[] { 0x54, 0x55, 0x43, 0x30, // 'TUC0'
|
||||
0x01, 0x00, 0x00, 0x00, // CMD_TYPE_RESPONSE = 1
|
||||
0x01, 0x00, 0x00, 0x00 };
|
||||
|
||||
final byte[] twelveZeroBytes = new byte[12];
|
||||
|
||||
if (writeUsb(standardReplyBytes)){ // Send integer value of '1' in Little-endian format.
|
||||
logPrinter.print("TF Sending response failed [1/3]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(writeUsb(sizeAsBytes)) { // Send EXACTLY what has been received
|
||||
logPrinter.print("TF Sending response failed [2/3]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(writeUsb(twelveZeroBytes)) { // kinda another one padding
|
||||
logPrinter.print("TF Sending response failed [3/3]", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sending any byte array to USB device
|
||||
* @return 'false' if no issues
|
||||
* 'true' if errors happened
|
||||
* */
|
||||
private boolean writeUsb(byte[] message) {
|
||||
ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
|
||||
writeBuffer.put(message); // Don't do writeBuffer.rewind();
|
||||
IntBuffer writeBufTransferred = IntBuffer.allocate(1);
|
||||
int result;
|
||||
//int varVar = 0; //todo:remove
|
||||
while (! task.isCancelled() ) {
|
||||
/*
|
||||
if (varVar != 0)
|
||||
logPrinter.print("writeUsb() retry cnt: "+varVar, EMsgType.INFO); //NOTE: DEBUG
|
||||
varVar++;
|
||||
*/
|
||||
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 5050); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
|
||||
|
||||
switch (result){
|
||||
case LibUsb.SUCCESS:
|
||||
if (writeBufTransferred.get() == message.length)
|
||||
return false;
|
||||
logPrinter.print("TF Data transfer issue [write]" +
|
||||
"\n Requested: "+message.length+
|
||||
"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
|
||||
return true;
|
||||
case LibUsb.ERROR_TIMEOUT:
|
||||
//System.out.println("writeBuffer position: "+writeBuffer.position()+" "+writeBufTransferred.get());
|
||||
//writeBufTransferred.clear(); // MUST BE HERE IF WE 'GET()' IT
|
||||
continue;
|
||||
default:
|
||||
logPrinter.print("TF Data transfer issue [write]" +
|
||||
"\n Returned: "+ UsbErrorCodes.getErrCode(result) +
|
||||
"\n (execution stopped)", EMsgType.FAIL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
logPrinter.print("TF Execution interrupted", EMsgType.INFO);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Reading what USB device responded.
|
||||
* @return byte array if data read successful
|
||||
* 'null' if read failed
|
||||
* */
|
||||
private byte[] readUsb() throws Exception{
|
||||
ByteBuffer readBuffer = ByteBuffer.allocateDirect(512);
|
||||
// We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
|
||||
IntBuffer readBufTransferred = IntBuffer.allocate(1);
|
||||
int result;
|
||||
while (! task.isCancelled()) {
|
||||
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
|
||||
|
||||
switch (result) {
|
||||
case LibUsb.SUCCESS:
|
||||
int trans = readBufTransferred.get();
|
||||
byte[] receivedBytes = new byte[trans];
|
||||
readBuffer.get(receivedBytes);
|
||||
return receivedBytes;
|
||||
case LibUsb.ERROR_TIMEOUT:
|
||||
continue;
|
||||
default:
|
||||
throw new Exception("TF Data transfer issue [read]" +
|
||||
"\n Returned: " + UsbErrorCodes.getErrCode(result)+
|
||||
"\n (execution stopped)");
|
||||
}
|
||||
}
|
||||
throw new InterruptedException("TF Execution interrupted");
|
||||
}
|
||||
}
|
80
src/main/java/nsusbloader/com/usb/TransferModule.java
Normal file
80
src/main/java/nsusbloader/com/usb/TransferModule.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb;
|
||||
|
||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import org.usb4java.DeviceHandle;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class TransferModule {
|
||||
EFileStatus status = EFileStatus.UNKNOWN;
|
||||
|
||||
LinkedHashMap<String, File> nspMap;
|
||||
ILogPrinter logPrinter;
|
||||
DeviceHandle handlerNS;
|
||||
CancellableRunnable task;
|
||||
|
||||
TransferModule(DeviceHandle handler, LinkedHashMap<String, File> nspMap, CancellableRunnable task, ILogPrinter printer){
|
||||
this.handlerNS = handler;
|
||||
this.nspMap = nspMap;
|
||||
this.task = task;
|
||||
this.logPrinter = printer;
|
||||
|
||||
filterFiles();
|
||||
}
|
||||
void filterFiles(){
|
||||
nspMap.values().removeIf(f -> {
|
||||
if (f.isFile())
|
||||
return false;
|
||||
|
||||
File[] subFiles = f.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||
|
||||
if (subFiles == null || subFiles.length == 0) {
|
||||
logPrinter.print("TransferModule: Exclude folder: " + f.getName(), EMsgType.WARNING);
|
||||
return true;
|
||||
}
|
||||
|
||||
Arrays.sort(subFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));
|
||||
|
||||
for (int i = subFiles.length - 2; i > 0 ; i--){
|
||||
if (subFiles[i].length() != subFiles[i-1].length()) {
|
||||
logPrinter.print("TransferModule: Exclude split file: "+f.getName()+
|
||||
"\n Chunk sizes of the split file are not the same, but has to be.", EMsgType.WARNING);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
long firstFileLength = subFiles[0].length();
|
||||
long lastFileLength = subFiles[subFiles.length-1].length();
|
||||
|
||||
if (lastFileLength > firstFileLength){
|
||||
logPrinter.print("TransferModule: Exclude split file: "+f.getName()+
|
||||
"\n Chunk sizes of the split file are not the same, but has to be.", EMsgType.WARNING);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
public EFileStatus getStatus(){ return status; }
|
||||
}
|
93
src/main/java/nsusbloader/com/usb/UsbCommunications.java
Normal file
93
src/main/java/nsusbloader/com/usb/UsbCommunications.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb;
|
||||
|
||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.ModelControllers.Log;
|
||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||
import nsusbloader.NSLDataTypes.EModule;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import org.usb4java.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
// TODO: add filter option to show only NSP files
|
||||
public class UsbCommunications extends CancellableRunnable {
|
||||
|
||||
private final ILogPrinter logPrinter;
|
||||
private final LinkedHashMap<String, File> nspMap;
|
||||
private final String protocol;
|
||||
private final boolean nspFilterForGl;
|
||||
|
||||
public UsbCommunications(List<File> nspList, String protocol, boolean filterNspFilesOnlyForGl){
|
||||
this.protocol = protocol;
|
||||
this.nspFilterForGl = filterNspFilesOnlyForGl;
|
||||
this.nspMap = new LinkedHashMap<>();
|
||||
for (File f: nspList)
|
||||
nspMap.put(f.getName(), f);
|
||||
this.logPrinter = Log.getPrinter(EModule.USB_NET_TRANSFERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logPrinter.print("\tStart", EMsgType.INFO);
|
||||
|
||||
UsbConnect usbConnect = UsbConnect.connectHomebrewMode(logPrinter);
|
||||
|
||||
if (! usbConnect.isConnected()){
|
||||
close(EFileStatus.FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
DeviceHandle handler = usbConnect.getNsHandler();
|
||||
|
||||
TransferModule module;
|
||||
|
||||
switch (protocol) {
|
||||
case "TinFoil":
|
||||
module = new TinFoil(handler, nspMap, this, logPrinter);
|
||||
break;
|
||||
case "GoldLeafv0.8":
|
||||
module = new GoldLeaf_08(handler, nspMap, this, logPrinter, nspFilterForGl);
|
||||
break;
|
||||
case "GoldLeafv0.7.x":
|
||||
module = new GoldLeaf_07(handler, nspMap, this, logPrinter, nspFilterForGl);
|
||||
break;
|
||||
default:
|
||||
module = new GoldLeaf_05(handler, nspMap, this, logPrinter);
|
||||
break;
|
||||
}
|
||||
|
||||
usbConnect.close();
|
||||
|
||||
close(module.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Report status and close
|
||||
*/
|
||||
private void close(EFileStatus status){
|
||||
logPrinter.update(nspMap, status);
|
||||
logPrinter.print("\tEnd", EMsgType.INFO);
|
||||
logPrinter.close();
|
||||
}
|
||||
}
|
244
src/main/java/nsusbloader/com/usb/UsbConnect.java
Normal file
244
src/main/java/nsusbloader/com/usb/UsbConnect.java
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb;
|
||||
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import org.usb4java.*;
|
||||
|
||||
public class UsbConnect {
|
||||
private static final int DEFAULT_INTERFACE = 0;
|
||||
private static final int DEFAULT_HOMEBREW_CONFIGURATION = 1;
|
||||
|
||||
private static final short RCM_VID = 0x0955;
|
||||
private static final short RCM_PID = 0x7321;
|
||||
|
||||
private static final short HOMEBREW_VID = 0x057E;
|
||||
private static final short HOMEBREW_PID = 0x3000;
|
||||
|
||||
// private static final short TEST_VID = 0x1a86;
|
||||
// private static final short TEST_PID = 0x7523;
|
||||
|
||||
private Context contextNS;
|
||||
private DeviceHandle handlerNS;
|
||||
private Device deviceNS;
|
||||
|
||||
private ILogPrinter logPrinter;
|
||||
|
||||
private boolean connected; // TODO: replace to 'connectionFailure' and invert requests everywhere
|
||||
|
||||
private short VENDOR_ID;
|
||||
private short PRODUCT_ID;
|
||||
|
||||
private int returningValue;
|
||||
private DeviceList deviceList;
|
||||
|
||||
public static UsbConnect connectRcmMode(ILogPrinter logPrinter){
|
||||
UsbConnect usbConnect = new UsbConnect(logPrinter);
|
||||
usbConnect.VENDOR_ID = RCM_VID;
|
||||
usbConnect.PRODUCT_ID = RCM_PID;
|
||||
try{
|
||||
usbConnect.createContextAndInitLibUSB();
|
||||
usbConnect.getDeviceList();
|
||||
usbConnect.findDevice();
|
||||
usbConnect.openDevice();
|
||||
usbConnect.freeDeviceList();
|
||||
usbConnect.setAutoDetachKernelDriver();
|
||||
//this.resetDevice();
|
||||
usbConnect.claimInterface();
|
||||
usbConnect.connected = true;
|
||||
}
|
||||
catch (Exception e){
|
||||
logPrinter.print(e.getMessage(), EMsgType.FAIL);
|
||||
usbConnect.close();
|
||||
}
|
||||
|
||||
return usbConnect;
|
||||
}
|
||||
public static UsbConnect connectHomebrewMode(ILogPrinter logPrinter){
|
||||
UsbConnect usbConnect = new UsbConnect(logPrinter);
|
||||
usbConnect.VENDOR_ID = HOMEBREW_VID;
|
||||
usbConnect.PRODUCT_ID = HOMEBREW_PID;
|
||||
try {
|
||||
usbConnect.createContextAndInitLibUSB();
|
||||
usbConnect.getDeviceList();
|
||||
usbConnect.findDevice();
|
||||
usbConnect.openDevice();
|
||||
usbConnect.freeDeviceList();
|
||||
usbConnect.setAutoDetachKernelDriver();
|
||||
//this.resetDevice();
|
||||
usbConnect.setConfiguration(DEFAULT_HOMEBREW_CONFIGURATION);
|
||||
usbConnect.claimInterface();
|
||||
usbConnect.connected = true;
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
logPrinter.print(e.getMessage(), EMsgType.FAIL);
|
||||
usbConnect.close();
|
||||
}
|
||||
return usbConnect;
|
||||
}
|
||||
|
||||
private UsbConnect(){}
|
||||
|
||||
private UsbConnect(ILogPrinter logPrinter){
|
||||
this.logPrinter = logPrinter;
|
||||
this.connected = false;
|
||||
};
|
||||
|
||||
private void createContextAndInitLibUSB() throws Exception{
|
||||
// Creating Context required by libusb. Optional? Consider removing.
|
||||
contextNS = new Context();
|
||||
|
||||
returningValue = LibUsb.init(contextNS);
|
||||
if (returningValue != LibUsb.SUCCESS)
|
||||
throw new Exception("LibUSB initialization failed: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
|
||||
private void getDeviceList() throws Exception{
|
||||
deviceList = new DeviceList();
|
||||
returningValue = LibUsb.getDeviceList(contextNS, deviceList);
|
||||
if (returningValue < 0)
|
||||
throw new Exception("Can't get device list: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
|
||||
private void findDevice() throws Exception{
|
||||
// Searching for NS in devices: looking for NS
|
||||
DeviceDescriptor descriptor;
|
||||
|
||||
for (Device device: deviceList){
|
||||
descriptor = getDeviceDescriptor(device);
|
||||
|
||||
if ((descriptor.idVendor() == VENDOR_ID) && descriptor.idProduct() == PRODUCT_ID){
|
||||
deviceNS = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (deviceNS == null) {
|
||||
this.freeDeviceList();
|
||||
throw new Exception("NS not found in connected USB devices");
|
||||
}
|
||||
}
|
||||
|
||||
private DeviceDescriptor getDeviceDescriptor(Device device) throws Exception{
|
||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
||||
|
||||
returningValue = LibUsb.getDeviceDescriptor(device, descriptor);
|
||||
|
||||
if (returningValue != LibUsb.SUCCESS){
|
||||
this.freeDeviceList();
|
||||
throw new Exception("Get USB device descriptor failure: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private void openDevice() throws Exception{
|
||||
// Handle NS device
|
||||
handlerNS = new DeviceHandle();
|
||||
returningValue = LibUsb.open(deviceNS, handlerNS);
|
||||
|
||||
if (returningValue == LibUsb.SUCCESS)
|
||||
return;
|
||||
|
||||
handlerNS = null; // Avoid issue on close();
|
||||
if (returningValue == LibUsb.ERROR_ACCESS) {
|
||||
throw new Exception(String.format(
|
||||
"Can't open NS USB device: %s\n" +
|
||||
"Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!\n\n" +
|
||||
"Steps to set 'udev' rules:\n" +
|
||||
"root # vim /etc/udev/rules.d/99-NS" + ((RCM_VID == VENDOR_ID) ? "RCM" : "") + ".rules\n" +
|
||||
"SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", GROUP=\"plugdev\"\n" +
|
||||
"root # udevadm control --reload-rules && udevadm trigger\n", UsbErrorCodes.getErrCode(returningValue), VENDOR_ID, PRODUCT_ID));
|
||||
}
|
||||
else
|
||||
throw new Exception("Can't open NS USB device: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
|
||||
private void freeDeviceList(){
|
||||
LibUsb.freeDeviceList(deviceList, true);
|
||||
}
|
||||
|
||||
private void setAutoDetachKernelDriver(){
|
||||
// Actually, there are no drivers in Linux kernel which uses this device.
|
||||
returningValue = LibUsb.setAutoDetachKernelDriver(handlerNS, true);
|
||||
if (returningValue != LibUsb.SUCCESS)
|
||||
logPrinter.print("Skip kernel driver attach & detach ("+UsbErrorCodes.getErrCode(returningValue)+")", EMsgType.INFO);
|
||||
}
|
||||
|
||||
/*
|
||||
private void resetDevice(){
|
||||
result = LibUsb.resetDevice(handlerNS);
|
||||
if (returningValue != LibUsb.SUCCESS)
|
||||
throw new Exception("Reset device\n Returned: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
*/
|
||||
private void setConfiguration(int configuration) throws Exception{
|
||||
returningValue = LibUsb.setConfiguration(handlerNS, configuration);
|
||||
if (returningValue != LibUsb.SUCCESS)
|
||||
throw new Exception("Unable to set active configuration on device: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
private void claimInterface() throws Exception{
|
||||
returningValue = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE);
|
||||
if (returningValue != LibUsb.SUCCESS)
|
||||
throw new Exception("Claim interface failure: "+UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
/**
|
||||
* Get USB status
|
||||
* @return status of connection
|
||||
*/
|
||||
public boolean isConnected() { return connected; }
|
||||
/**
|
||||
* Getter for handler
|
||||
* @return DeviceHandle of NS
|
||||
*/
|
||||
public DeviceHandle getNsHandler(){ return handlerNS; }
|
||||
/**
|
||||
* Getter for 'Bus ID' where NS located found
|
||||
*/
|
||||
public int getNsBus(){
|
||||
return LibUsb.getBusNumber(deviceNS);
|
||||
}
|
||||
/**
|
||||
* Getter for 'Device address' where NS located at
|
||||
*/
|
||||
public int getNsAddress(){
|
||||
return LibUsb.getDeviceAddress(deviceNS);
|
||||
}
|
||||
/**
|
||||
* Correct exit
|
||||
* */
|
||||
public void close(){
|
||||
// Close handler in the end
|
||||
if (handlerNS != null) {
|
||||
// Try to release interface
|
||||
returningValue = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE);
|
||||
|
||||
if (returningValue != LibUsb.SUCCESS) {
|
||||
logPrinter.print("Release interface failure: " +
|
||||
UsbErrorCodes.getErrCode(returningValue) +
|
||||
" (sometimes it's not an issue)", EMsgType.WARNING);
|
||||
}
|
||||
|
||||
LibUsb.close(handlerNS);
|
||||
}
|
||||
// Close context in the end
|
||||
if (contextNS != null)
|
||||
LibUsb.exit(contextNS);
|
||||
}
|
||||
}
|
56
src/main/java/nsusbloader/com/usb/UsbErrorCodes.java
Normal file
56
src/main/java/nsusbloader/com/usb/UsbErrorCodes.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb;
|
||||
|
||||
import org.usb4java.LibUsb;
|
||||
|
||||
public class UsbErrorCodes {
|
||||
public static String getErrCode(int value){
|
||||
switch (value){
|
||||
case LibUsb.ERROR_ACCESS:
|
||||
return "ERROR_ACCESS";
|
||||
case LibUsb.ERROR_BUSY:
|
||||
return "ERROR_BUSY";
|
||||
case LibUsb.ERROR_INTERRUPTED:
|
||||
return "ERROR_INTERRUPTED";
|
||||
case LibUsb.ERROR_INVALID_PARAM:
|
||||
return "ERROR_INVALID_PARAM";
|
||||
case LibUsb.ERROR_IO:
|
||||
return "ERROR_IO";
|
||||
case LibUsb.ERROR_NO_DEVICE:
|
||||
return "ERROR_NO_DEVICE";
|
||||
case LibUsb.ERROR_NO_MEM:
|
||||
return "ERROR_NO_MEM";
|
||||
case LibUsb.ERROR_NOT_FOUND:
|
||||
return "ERROR_NOT_FOUND";
|
||||
case LibUsb.ERROR_NOT_SUPPORTED:
|
||||
return "ERROR_NOT_SUPPORTED";
|
||||
case LibUsb.ERROR_OTHER:
|
||||
return "ERROR_OTHER";
|
||||
case LibUsb.ERROR_OVERFLOW:
|
||||
return "ERROR_OVERFLOW";
|
||||
case LibUsb.ERROR_PIPE:
|
||||
return "ERROR_PIPE";
|
||||
case LibUsb.ERROR_TIMEOUT:
|
||||
return "ERROR_TIMEOUT";
|
||||
default:
|
||||
return Integer.toString(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.common;
|
||||
|
||||
import nsusbloader.com.usb.UsbErrorCodes;
|
||||
import org.usb4java.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DeviceInformation {
|
||||
private static final byte DEFAULT_IN_EP_ADDRESS = -127; // 0x81
|
||||
private static final byte DEFAULT_OUT_EP_ADDRESS = 1;
|
||||
|
||||
private Device device;
|
||||
private ConfigDescriptor configDescriptor;
|
||||
private List<NsUsbInterface> interfacesInformation = new ArrayList<>();
|
||||
|
||||
private DeviceInformation(){}
|
||||
|
||||
public static DeviceInformation build(DeviceHandle handler) throws Exception{
|
||||
Device device = LibUsb.getDevice(handler);
|
||||
return DeviceInformation.build(device);
|
||||
}
|
||||
public static DeviceInformation build(Device device) throws Exception{
|
||||
DeviceInformation deviceInformation = new DeviceInformation();
|
||||
deviceInformation.device = device;
|
||||
deviceInformation.claimConfigurationDescriptor();
|
||||
deviceInformation.collectInterfaces();
|
||||
deviceInformation.freeConfigurationDescriptor();
|
||||
return deviceInformation;
|
||||
}
|
||||
|
||||
private void claimConfigurationDescriptor() throws Exception{
|
||||
configDescriptor = new ConfigDescriptor();
|
||||
int returningValue = LibUsb.getActiveConfigDescriptor(device, configDescriptor);
|
||||
|
||||
if (returningValue != LibUsb.SUCCESS)
|
||||
throw new Exception("Get Active config descriptor failed: "+ UsbErrorCodes.getErrCode(returningValue));
|
||||
}
|
||||
|
||||
private void collectInterfaces(){
|
||||
for (Interface intrface : configDescriptor.iface())
|
||||
interfacesInformation.add(new NsUsbInterface(intrface));
|
||||
}
|
||||
|
||||
private void freeConfigurationDescriptor(){
|
||||
LibUsb.freeConfigDescriptor(configDescriptor);
|
||||
}
|
||||
|
||||
/** Bulk transfer endpoint IN */
|
||||
public NsUsbEndpointDescriptor getSimplifiedDefaultEndpointDescriptorIn() throws Exception{
|
||||
return getSimplifiedDefaultEndpointDescriptor(true);
|
||||
}
|
||||
/** Bulk transfer endpoint OUT */
|
||||
public NsUsbEndpointDescriptor getSimplifiedDefaultEndpointDescriptorOut() throws Exception{
|
||||
return getSimplifiedDefaultEndpointDescriptor(false);
|
||||
}
|
||||
|
||||
private NsUsbEndpointDescriptor getSimplifiedDefaultEndpointDescriptor(boolean isDescriptorIN) throws Exception{
|
||||
byte endpointAddress;
|
||||
|
||||
if (isDescriptorIN)
|
||||
endpointAddress = DEFAULT_IN_EP_ADDRESS;
|
||||
else
|
||||
endpointAddress = DEFAULT_OUT_EP_ADDRESS;
|
||||
|
||||
NsUsbInterface nsUsbInterface = interfacesInformation.get(0);
|
||||
|
||||
NsUsbInterfaceDescriptor firstInterfaceDescriptor = nsUsbInterface.getInterfaceDescriptors()[0];
|
||||
NsUsbEndpointDescriptor[] endpointDescriptors = firstInterfaceDescriptor.getEndpointDescriptors();
|
||||
|
||||
for (NsUsbEndpointDescriptor epDescriptor : endpointDescriptors){
|
||||
if (epDescriptor.getbEndpointAddress() == endpointAddress)
|
||||
return epDescriptor;
|
||||
}
|
||||
throw new Exception("No "+(isDescriptorIN?"IN":"OUT")+" endpoint descriptors found on default interface");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.common;
|
||||
|
||||
import org.usb4java.EndpointDescriptor;
|
||||
|
||||
public class NsUsbEndpointDescriptor {
|
||||
private final byte bLength;
|
||||
private final byte bDescriptorType;
|
||||
private final byte bEndpointAddress;
|
||||
private final byte bmAttributes;
|
||||
//Ignoring: Transfer Type, Synch Type, Usage Type
|
||||
private final short wMaxPacketSize;
|
||||
private final byte bInterval;
|
||||
|
||||
NsUsbEndpointDescriptor(EndpointDescriptor endpointDescriptor){
|
||||
this.bLength = endpointDescriptor.bLength();
|
||||
this.bDescriptorType = endpointDescriptor.bDescriptorType();
|
||||
this.bEndpointAddress = endpointDescriptor.bEndpointAddress();
|
||||
this.bmAttributes = endpointDescriptor.bmAttributes();
|
||||
this.wMaxPacketSize = endpointDescriptor.wMaxPacketSize();
|
||||
this.bInterval = endpointDescriptor.bInterval();
|
||||
}
|
||||
|
||||
public byte getbLength() {
|
||||
return bLength;
|
||||
}
|
||||
|
||||
public byte getbDescriptorType() {
|
||||
return bDescriptorType;
|
||||
}
|
||||
|
||||
public byte getbEndpointAddress() {
|
||||
return bEndpointAddress;
|
||||
}
|
||||
|
||||
public byte getBmAttributes() {
|
||||
return bmAttributes;
|
||||
}
|
||||
|
||||
public short getwMaxPacketSize() {
|
||||
return wMaxPacketSize;
|
||||
}
|
||||
|
||||
public byte getbInterval() {
|
||||
return bInterval;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.common;
|
||||
|
||||
import org.usb4java.EndpointDescriptor;
|
||||
|
||||
public class NsUsbEndpointDescriptorUtils {
|
||||
static NsUsbEndpointDescriptor[] convertFromNatives(EndpointDescriptor[] nativeEpDescriptors){
|
||||
int descriptorsCount = nativeEpDescriptors.length;
|
||||
NsUsbEndpointDescriptor[] nsUsbEpDescriptors = new NsUsbEndpointDescriptor[descriptorsCount];
|
||||
for (int i = 0; i < descriptorsCount; i++) {
|
||||
nsUsbEpDescriptors[i] = new NsUsbEndpointDescriptor(nativeEpDescriptors[i]);
|
||||
}
|
||||
return nsUsbEpDescriptors;
|
||||
}
|
||||
}
|
50
src/main/java/nsusbloader/com/usb/common/NsUsbInterface.java
Normal file
50
src/main/java/nsusbloader/com/usb/common/NsUsbInterface.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.common;
|
||||
|
||||
import org.usb4java.Interface;
|
||||
import org.usb4java.InterfaceDescriptor;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Adapter for easier access to USB devices which has only one interface with one interface descriptor (BULK)
|
||||
*
|
||||
* After few JVM failed to core few 'holders' were added: such as NsUsbEndpoint descriptor and NsUsbInterfaceDescriptor
|
||||
* */
|
||||
|
||||
public class NsUsbInterface {
|
||||
private final Interface iface;
|
||||
private final LinkedList<NsUsbInterfaceDescriptor> interfaceDescriptors;
|
||||
|
||||
public NsUsbInterface(Interface iface){
|
||||
this.iface = iface;
|
||||
this.interfaceDescriptors = new LinkedList<>();
|
||||
collectDescriptors();
|
||||
}
|
||||
|
||||
private void collectDescriptors(){
|
||||
for (InterfaceDescriptor ifaceDescriptor : iface.altsetting()){
|
||||
interfaceDescriptors.add(new NsUsbInterfaceDescriptor(ifaceDescriptor));
|
||||
}
|
||||
}
|
||||
public NsUsbInterfaceDescriptor[] getInterfaceDescriptors(){
|
||||
return interfaceDescriptors.toArray(new NsUsbInterfaceDescriptor[0]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 2019-2020 Dmitry Isaenko
|
||||
|
||||
This file is part of NS-USBloader.
|
||||
|
||||
NS-USBloader 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.
|
||||
|
||||
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package nsusbloader.com.usb.common;
|
||||
|
||||
import org.usb4java.InterfaceDescriptor;
|
||||
|
||||
public class NsUsbInterfaceDescriptor {
|
||||
private final byte bLength;
|
||||
private final byte bDescriptorType;
|
||||
private final byte bInterfaceNumber;
|
||||
private final byte bAlternateSetting;
|
||||
private final byte bNumEndpoints;
|
||||
private final byte bInterfaceClass;
|
||||
private final byte bInterfaceSubClass;
|
||||
private final byte bInterfaceProtocol;
|
||||
private final byte iInterface;
|
||||
//private final int extralen;
|
||||
//private final ByteBuffer extra;
|
||||
private final NsUsbEndpointDescriptor[] endpointDescriptors;
|
||||
|
||||
NsUsbInterfaceDescriptor(InterfaceDescriptor interfaceDescriptor){
|
||||
this.bLength = interfaceDescriptor.bLength();
|
||||
this.bDescriptorType = interfaceDescriptor.bDescriptorType();
|
||||
this.bInterfaceNumber = interfaceDescriptor.bInterfaceNumber();
|
||||
this.bAlternateSetting = interfaceDescriptor.bAlternateSetting();
|
||||
this.bNumEndpoints = interfaceDescriptor.bNumEndpoints();
|
||||
this.bInterfaceClass = interfaceDescriptor.bInterfaceClass();
|
||||
this.bInterfaceSubClass = interfaceDescriptor.bInterfaceSubClass();
|
||||
this.bInterfaceProtocol = interfaceDescriptor.bInterfaceProtocol();
|
||||
this.iInterface = interfaceDescriptor.iInterface();
|
||||
|
||||
this.endpointDescriptors = NsUsbEndpointDescriptorUtils.convertFromNatives(interfaceDescriptor.endpoint());
|
||||
}
|
||||
|
||||
public byte getbLength() {
|
||||
return bLength;
|
||||
}
|
||||
|
||||
public byte getbDescriptorType() {
|
||||
return bDescriptorType;
|
||||
}
|
||||
|
||||
public byte getbInterfaceNumber() {
|
||||
return bInterfaceNumber;
|
||||
}
|
||||
|
||||
public byte getbAlternateSetting() {
|
||||
return bAlternateSetting;
|
||||
}
|
||||
|
||||
public byte getbNumEndpoints() {
|
||||
return bNumEndpoints;
|
||||
}
|
||||
|
||||
public byte getbInterfaceClass() {
|
||||
return bInterfaceClass;
|
||||
}
|
||||
|
||||
public byte getbInterfaceSubClass() {
|
||||
return bInterfaceSubClass;
|
||||
}
|
||||
|
||||
public byte getbInterfaceProtocol() {
|
||||
return bInterfaceProtocol;
|
||||
}
|
||||
|
||||
public byte getiInterface() {
|
||||
return iInterface;
|
||||
}
|
||||
|
||||
public NsUsbEndpointDescriptor[] getEndpointDescriptors() {
|
||||
return endpointDescriptors;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue