diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java index 9e5fd9b..fcd33a3 100644 --- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java +++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java @@ -22,6 +22,7 @@ import nsusbloader.COM.USB.UsbErrorCodes; import nsusbloader.ModelControllers.ILogPrinter; import nsusbloader.NSLDataTypes.EMsgType; import org.usb4java.DeviceHandle; +import org.usb4java.DeviceDescriptor; import org.usb4java.LibUsb; import java.io.*; @@ -40,9 +41,21 @@ class NxdtUsbAbi1 { private final boolean isWindows; private boolean isWindows10; + private short endpointMaxPacketSize = 0; + private static final int NXDT_MAX_DIRECTIVE_SIZE = 0x1000; private static final int NXDT_FILE_CHUNK_SIZE = 0x800000; private static final int NXDT_FILE_PROPERTIES_MAX_NAME_LENGTH = 0x300; + private static final int NXDT_USB_TIMEOUT = 5000; + + private static final short NXDT_USB_FS_BCD_REVISION = 0x0110; + private static final short NXDT_USB_FS_EP_MAX_PACKET_SIZE = 0x40; + + private static final short NXDT_USB_HS_BCD_REVISION = 0x0200; + private static final short NXDT_USB_HS_EP_MAX_PACKET_SIZE = 0x200; + + private static final short NXDT_USB_SS_BCD_REVISION = 0x0300; + private static final short NXDT_USB_SS_EP_MAX_PACKET_SIZE = 0x400; private static final byte ABI_VERSION = 1; private static final byte[] MAGIC_NXDT = { 0x4e, 0x58, 0x44, 0x54 }; @@ -77,8 +90,6 @@ class NxdtUsbAbi1 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - private static final long W_MAX_PACKET_SIZE = 0x200; - public NxdtUsbAbi1(DeviceHandle handler, ILogPrinter logPrinter, String saveToPath, @@ -174,7 +185,58 @@ class NxdtUsbAbi1 { writeUsb(USBSTATUS_UNSUPPORTED_ABI); throw new Exception("ABI v"+versionABI+" is not supported in current version."); } - writeUsb(USBSTATUS_SUCCESS); + + // Get endpoint max packet size + getEndpointMaxPacketSize(); + + // Send status response + endpoint max packet size + byte[] response = new byte[USBSTATUS_SUCCESS.length + 2]; + System.arraycopy(USBSTATUS_SUCCESS, 0, response, 0, USBSTATUS_SUCCESS.length); + response[USBSTATUS_SUCCESS.length] = (byte)(endpointMaxPacketSize & 0xFF); + response[USBSTATUS_SUCCESS.length + 1] = (byte)((endpointMaxPacketSize >> 8) & 0xFF); + + writeUsb(response); + } + + private void getEndpointMaxPacketSize() throws Exception{ + // Get device descriptor to determine the max packet size in use. We'll send it to the console. + // We'll get the device descriptor instead of an endpoint descriptor because it's a non-blocking call (the device descriptor is cached in memory by usb4java). + DeviceDescriptor deviceDescriptor = getDeviceDescriptor(); + + short bcdUsbRevision = deviceDescriptor.bcdUSB(); + byte usbRevisionUpper = (byte)((bcdUsbRevision >> 8) & 0xFF); + byte usbRevisionLower = (byte)(bcdUsbRevision & 0xFF); + + switch(bcdUsbRevision){ + case NXDT_USB_FS_BCD_REVISION: // USB 1.1 (Full Speed). + endpointMaxPacketSize = NXDT_USB_FS_EP_MAX_PACKET_SIZE; + break; + case NXDT_USB_HS_BCD_REVISION: // USB 2.0 (High Speed). + endpointMaxPacketSize = NXDT_USB_HS_EP_MAX_PACKET_SIZE; + break; + case NXDT_USB_SS_BCD_REVISION: // USB 3.0 (Super Speed). + endpointMaxPacketSize = NXDT_USB_SS_EP_MAX_PACKET_SIZE; + break; + default: + writeUsb(USBSTATUS_HOSTIOERROR); + throw new Exception("Invalid BCD USB release number \""+usbRevisionUpper+"."+usbRevisionLower+"\" in device descriptor!"); + } + + //logPrinter.print("USB revision: "+usbRevisionUpper+"."+usbRevisionLower+".", EMsgType.INFO); + //logPrinter.print("Max packet size: "+endpointMaxPacketSize+" b.", EMsgType.INFO); + } + + private DeviceDescriptor getDeviceDescriptor() throws Exception{ + int result; + DeviceDescriptor descriptor = new DeviceDescriptor(); + + result = LibUsb.getDeviceDescriptor(LibUsb.getDevice(handlerNS), descriptor); + if (result != LibUsb.SUCCESS){ + writeUsb(USBSTATUS_HOSTIOERROR); + throw new Exception("Failed to get device descriptor! Returned "+result+" ("+UsbErrorCodes.getErrCode(result)+")."); + } + + return descriptor; } private void handleSendFileProperties(byte[] message) throws Exception{ @@ -188,6 +250,8 @@ class NxdtUsbAbi1 { return; } + logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO); + // If RomFs related if (isRomFs(filename)) { if (isWindows) @@ -198,7 +262,6 @@ class NxdtUsbAbi1 { createPath(filename); } else { - logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO); filename = saveToPath + filename; } @@ -222,13 +285,11 @@ class NxdtUsbAbi1 { if (fileSize == 0) return; - if (isWindows10) - dumpFileOnWindowsTen(fileToDump, fileSize); - else - dumpFile(fileToDump, fileSize); + dumpFile(fileToDump, fileSize); writeUsb(USBSTATUS_SUCCESS); + //logPrinter.print("File transfer successfully finished!", EMsgType.INFO); } private int getLEint(byte[] bytes, int fromOffset){ @@ -258,63 +319,46 @@ class NxdtUsbAbi1 { } private void dumpFile(File file, long size) throws Exception{ - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, false))) { - byte[] readBuffer; - long received = 0; - int bufferSize; - - boolean zlt_expected = isAligned(size); - - while (received < size) { - readBuffer = readUsbFile(); - bos.write(readBuffer); - bufferSize = readBuffer.length; - received += bufferSize; - logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0); - } + byte[] readBuffer; + long received = 0; + int chunkSize = NXDT_FILE_CHUNK_SIZE; - if (zlt_expected) { - logPrinter.print("Finishing with ZLT packet request", EMsgType.INFO); - readUsbFile(); - } - } finally { - logPrinter.updateProgress(1.0); - } - } + boolean zltExpected = isAligned(size, endpointMaxPacketSize); + //logPrinter.print("ZLT packet expected: "+zlt_expected, EMsgType.INFO); - // @see https://bugs.openjdk.java.net/browse/JDK-8146538 - private void dumpFileOnWindowsTen(File file, long size) throws Exception{ - FileOutputStream fos = new FileOutputStream(file, true); + FileOutputStream fos = new FileOutputStream(file, false); + FileDescriptor fd = fos.getFD(); try (BufferedOutputStream bos = new BufferedOutputStream(fos)) { - FileDescriptor fd = fos.getFD(); - byte[] readBuffer; - long received = 0; - int bufferSize; - - boolean zlt_expected = isAligned(size); + while(true){ + // Check if we aren't expecting a ZLT packet and the whole file has already been received. + if (!zltExpected && received >= size) break; + + // Update chunk size if needed. + if (received < size && (long)chunkSize > (size - received)) chunkSize = (int)(size - received); + + // Read current chunk. Don't attempt to read more than we're supposed to by allocating a buffer with a size equal to the current chunk size. + // Immediately check if we're dealing with a ZLT packet afterwards. + readBuffer = readUsbFile(chunkSize); + if (readBuffer == null || readBuffer.length == 0) + { + //logPrinter.print("ZLT packet received.", EMsgType.INFO); + if (zltExpected && received >= size) break; + continue; + } - while (received < size) { - readBuffer = readUsbFile(); bos.write(readBuffer); - fd.sync(); // Fixes flushing under Windows (unharmful for other OS) - bufferSize = readBuffer.length; - received += bufferSize; + if (isWindows10) fd.sync(); // Fixes flushing under Windows 10. See https://bugs.openjdk.java.net/browse/JDK-8146538 + received += readBuffer.length; - logPrinter.updateProgress((received + bufferSize) / (size / 100.0) / 100.0); + //logPrinter.print("Received "+readBuffer.length+" b. Got thus far: "+received+" b.", EMsgType.INFO); + logPrinter.updateProgress((double)received / (double)size); } - - if (zlt_expected) { - logPrinter.print("Finishing with ZLT packet request", EMsgType.INFO); - readUsbFile(); - } - } finally { - logPrinter.updateProgress(1.0); } } /** Handle Zero-length terminator **/ - private boolean isAligned(long size){ - return ((size & (W_MAX_PACKET_SIZE - 1)) == 0); + private boolean isAligned(long size, long alignment){ + return ((size & (alignment - 1)) == 0); } /** Sending any byte array to USB device **/ @@ -326,7 +370,7 @@ class NxdtUsbAbi1 { if ( parent.isCancelled() ) throw new InterruptedException("Execution interrupted"); - int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 5050); + int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, NXDT_USB_TIMEOUT); if (result == LibUsb.SUCCESS) { if (writeBufTransferred.get() == message.length) @@ -338,7 +382,6 @@ class NxdtUsbAbi1 { throw new Exception("Data transfer issue [write]" + "\n Returned: " + UsbErrorCodes.getErrCode(result) + "\n (execution stopped)"); - } /** * Reading what USB device responded (command). @@ -374,29 +417,25 @@ class NxdtUsbAbi1 { * @return byte array if data read successful * 'null' if read failed * */ - private byte[] readUsbFile() throws Exception{ - ByteBuffer readBuffer = ByteBuffer.allocateDirect(NXDT_FILE_CHUNK_SIZE); + private byte[] readUsbFile(int chunkSize) throws Exception{ + ByteBuffer readBuffer = ByteBuffer.allocateDirect(chunkSize); IntBuffer readBufTransferred = IntBuffer.allocate(1); - int result; - int countDown = 0; - while (! parent.isCancelled() && countDown < 5) { - result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); - switch (result) { - case LibUsb.SUCCESS: - int trans = readBufTransferred.get(); - byte[] receivedBytes = new byte[trans]; - readBuffer.get(receivedBytes); - return receivedBytes; - case LibUsb.ERROR_TIMEOUT: - countDown++; - break; - default: - throw new Exception("Data transfer issue [read file]" + - "\n Returned: " + UsbErrorCodes.getErrCode(result)+ - "\n (execution stopped)"); - } + if ( parent.isCancelled() ) + throw new InterruptedException(); + + int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, NXDT_USB_TIMEOUT); + + switch (result) { + case LibUsb.SUCCESS: + int trans = readBufTransferred.get(); + byte[] receivedBytes = new byte[trans]; + readBuffer.get(receivedBytes); + return receivedBytes; + default: + throw new Exception("Data transfer issue [read file]" + + "\n Returned: " + UsbErrorCodes.getErrCode(result)+ + "\n (execution stopped)"); } - throw new InterruptedException(); } } diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index a2d42d6..9114c3d 100644 --- a/src/main/resources/NSLMain.fxml +++ b/src/main/resources/NSLMain.fxml @@ -71,12 +71,12 @@ Steps to roll NXDT functionality back: - + - +