From 010c33c5936bcbcd3347202ee81e4adf701e0128 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Mon, 10 Feb 2020 02:19:39 +0300 Subject: [PATCH] =?UTF-8?q?RCM=20(Fus=C3=A9e=20Gel=C3=A9e)=20support,=20nu?= =?UTF-8?q?merous=20UI=20updates=20and=20a=20lot=20of=20things=20for=20ver?= =?UTF-8?q?sion=202.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILD.md | 45 +++ JNI sources/linux/Makefile | 33 ++ .../linux/nsusbloader_Utilities_RcmSmash.h | 29 ++ JNI sources/linux/smashlib.c | 88 +++++ JNI sources/windows/Makefile | 34 ++ .../windows/nsusbloader_Utilities_RcmSmash.h | 29 ++ JNI sources/windows/smashlib.c | 168 +++++++++ README.md | 14 +- pom.xml | 2 +- src/main/java/nsusbloader/AppPreferences.java | 5 +- .../COM/USB/UsbCommunications.java | 4 +- .../java/nsusbloader/COM/USB/UsbConnect.java | 101 ++++-- .../nsusbloader/COM/USB/UsbErrorCodes.java | 4 +- .../Controllers/FrontController.java | 8 +- .../Controllers/NSLMainController.java | 5 + .../Controllers/RcmController.java | 257 ++++++++++++++ .../Controllers/SplitMergeController.java | 28 +- .../java/nsusbloader/MediatorControl.java | 1 + src/main/java/nsusbloader/NSLMain.java | 2 +- .../nsusbloader/Utilities/JNIRcmLoader.java | 105 ++++++ .../java/nsusbloader/Utilities/RcmSmash.java | 17 + .../java/nsusbloader/Utilities/RcmTask.java | 332 ++++++++++++++++++ src/main/resources/FrontTab.fxml | 3 +- src/main/resources/NSLMain.fxml | 8 + src/main/resources/RcmTab.fxml | 218 ++++++++++++ src/main/resources/SettingsTab.fxml | 2 +- src/main/resources/SplitMergeTab.fxml | 60 +++- src/main/resources/locale.properties | 2 + src/main/resources/locale_rus.properties | 2 + src/main/resources/locale_ukr.properties | 4 +- .../resources/native/linux/amd64/smashlib.so | Bin 0 -> 16048 bytes .../resources/native/linux/x86/smashlib.so | Bin 0 -> 15052 bytes .../native/windows/amd64/smashlib.dll | Bin 0 -> 52054 bytes .../resources/native/windows/x86/smashlib.dll | Bin 0 -> 32413 bytes src/main/resources/res/app_dark.css | 28 +- src/main/resources/res/app_light.css | 26 +- 36 files changed, 1572 insertions(+), 92 deletions(-) create mode 100644 BUILD.md create mode 100644 JNI sources/linux/Makefile create mode 100644 JNI sources/linux/nsusbloader_Utilities_RcmSmash.h create mode 100755 JNI sources/linux/smashlib.c create mode 100644 JNI sources/windows/Makefile create mode 100644 JNI sources/windows/nsusbloader_Utilities_RcmSmash.h create mode 100644 JNI sources/windows/smashlib.c create mode 100644 src/main/java/nsusbloader/Controllers/RcmController.java create mode 100644 src/main/java/nsusbloader/Utilities/JNIRcmLoader.java create mode 100644 src/main/java/nsusbloader/Utilities/RcmSmash.java create mode 100644 src/main/java/nsusbloader/Utilities/RcmTask.java create mode 100644 src/main/resources/RcmTab.fxml create mode 100755 src/main/resources/native/linux/amd64/smashlib.so create mode 100755 src/main/resources/native/linux/x86/smashlib.so create mode 100644 src/main/resources/native/windows/amd64/smashlib.dll create mode 100644 src/main/resources/native/windows/x86/smashlib.dll diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..8cf1aa7 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,45 @@ +### How to build this app + +Java application: +* Install Maven +* Execute +`# mvn -B -DskipTests clean package` + +**Building JNI libraries section** + +First of all install JDK, GCC, make, kernel headers and whatever else (or use Gentoo <3 ). + +Generate header (Optional! Already generated.): +``` +$ cp NS-USBloader/src/main/java/nsusbloader/Utilities/RcmSmash.java . +$ javac -h . RcmSmash.java +``` +**Build for Linux (amd64 Linux host):** +``` + $ cd 'NS-USBloader/JNI sources/linux' + $ make install clean +``` +**Build for Windows (on x86_64 host):** + +[ This part should be updated ] + +Install MinGW, msys (?) MinGW-w64 and JDK. Set JAVA_HOME, set PATH to match MinGW, MSYS, JDK/bin (and other?) environment variables. + +x86: Install MinGw to C:\MinGW\ + +Update sources: set (uncomment) line 'BUILD_FOR_X86' + +Set environment variables for MinGw: +* C:\MinGW\bin +* C:\MinGW\msys\1.0\bin +``` + $ cd 'NS-USBloader/JNI sources/windows' + $ make x86 +``` + +amd64: Install MinGw-w64 + +Update sources: remove line 'BUILD_FOR_X86' +``` + $ make amd64 +``` \ No newline at end of file diff --git a/JNI sources/linux/Makefile b/JNI sources/linux/Makefile new file mode 100644 index 0000000..f91bcf8 --- /dev/null +++ b/JNI sources/linux/Makefile @@ -0,0 +1,33 @@ +# Compiler +CC=gcc +# Flags +CFLAGS=-O2 +MKDIR_P = mkdir -p +APP_NAME = smashlib.so + +all: x86 amd64 + +x86: + $(MKDIR_P) ./x86 + $(CC) ${CFLAGS} -m32 -c -fPIC -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" smashlib.c -o smashlib_x86.o + $(CC) ${CFLAGS} -m32 -shared -fPIC -o ./x86/${APP_NAME} smashlib_x86.o -lc + +amd64: + $(MKDIR_P) ./amd64 + $(CC) ${CFLAGS} -m64 -c -fPIC -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" smashlib.c -o smashlib_amd64.o + $(CC) ${CFLAGS} -m64 -shared -fPIC -o ./amd64/${APP_NAME} smashlib_amd64.o -lc + +clean: + rm -rf \ + smashlib_amd64.o \ + smashlib_x86.o \ + ./x86 \ + ./amd64 + +install: x86 amd64 + install ./x86/${APP_NAME} ../../src/main/resources/native/linux/x86/ + install ./amd64/${APP_NAME} ../../src/main/resources/native/linux/amd64/ + +uninstall: + rm ../../src/main/resources/native/linux/x86/${APP_NAME} + rm ../../src/main/resources/native/linux/amd64/${APP_NAME} diff --git a/JNI sources/linux/nsusbloader_Utilities_RcmSmash.h b/JNI sources/linux/nsusbloader_Utilities_RcmSmash.h new file mode 100644 index 0000000..9ed04db --- /dev/null +++ b/JNI sources/linux/nsusbloader_Utilities_RcmSmash.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class nsusbloader_Utilities_RcmSmash */ + +#ifndef _Included_nsusbloader_Utilities_RcmSmash +#define _Included_nsusbloader_Utilities_RcmSmash +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: nsusbloader_Utilities_RcmSmash + * Method: smashLinux + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashLinux + (JNIEnv *, jclass, jint, jint); + +/* + * Class: nsusbloader_Utilities_RcmSmash + * Method: smashWindows + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashWindows + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/JNI sources/linux/smashlib.c b/JNI sources/linux/smashlib.c new file mode 100755 index 0000000..9cddc72 --- /dev/null +++ b/JNI sources/linux/smashlib.c @@ -0,0 +1,88 @@ +/* NS-USBloader - native libraries for 'special purposes' + * Copyright (C) 2020 Dmitry Isaenko + * + * This program 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. + * + * This program 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 this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nsusbloader_Utilities_RcmSmash.h" + +struct usbdevfs_urb urb; + +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashLinux + (JNIEnv * jni_env, jclass this_class, jint bus_id, jint device_addr){ + int ret_value; + + char *usb_path = (char*)malloc(24 * sizeof(char)); + + sprintf(usb_path, "/dev/bus/usb/%03d/%03d", bus_id, device_addr); + int fd = open(usb_path, O_RDWR); + if (fd == -1) + return -1; + + struct usb_ctrlrequest* ctrl_req; + + __u8* buf[0x7000+sizeof(ctrl_req)]; + + ctrl_req = (struct usb_ctrlrequest *) buf; + ctrl_req->bRequestType = 0x82; + ctrl_req->bRequest = USB_REQ_GET_STATUS; + ctrl_req->wValue = 0; + ctrl_req->wIndex = 0; + ctrl_req->wLength = 0x7000; + + memset(&urb, 0, sizeof(urb)); + urb.type = USBDEVFS_URB_TYPE_CONTROL; + urb.endpoint = USB_DIR_IN | 0; + urb.buffer = buf; + urb.buffer_length = sizeof(buf); + //Submit request + ret_value = ioctl(fd, USBDEVFS_SUBMITURB, &urb); + // If we failed on this step, it's a VERY bad sign. Nothing to do, let's report failure. + if (ret_value != 0) + return ret_value; + // Wait 1/4 sec + usleep(250000); + struct usbdevfs_urb urb1; + // Let's pick reply (everybody does it, right? In non-blocking manner.) + ret_value = ioctl(fd, USBDEVFS_REAPURBNDELAY, &urb1); + if (ret_value < 0){ + if (errno == EAGAIN){ // In case of resource temporarily unavailable + // Wired.. so much time left. Let's cancel it! + ret_value = ioctl(fd, USBDEVFS_DISCARDURB, &urb); + // And wait a bit more.. + usleep(40000); + // And try to pick reply. Yes, it's still possible. See /usr/src/linux/drivers/usb/core/devio.c + ret_value = ioctl(fd, USBDEVFS_REAPURBNDELAY, &urb1); + } + } + // Leftovers. + free(usb_path); + // Let's try to close device, but even if we fail with this, nvm. + close(fd); // So we won't even write returned value somewhere. + return 0; +} + +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashWindows + (JNIEnv * jni_env, jclass this_class){ + return -1; + } diff --git a/JNI sources/windows/Makefile b/JNI sources/windows/Makefile new file mode 100644 index 0000000..1547a13 --- /dev/null +++ b/JNI sources/windows/Makefile @@ -0,0 +1,34 @@ +# Compiler +CC32='C:/MinGW/bin/gcc' +CC64='C:/Program Files/mingw-w64/x86_64-8.1.0-win32-seh-rt_v6-rev0/mingw64/bin/gcc' +# Flags +CFLAGS=-O2 +MKDIR_P=mkdir +APP_NAME=smashlib.dll + +all: x86 amd64 + + #$(CC) ${CFLAGS} -m32 -c -fPIC -I "C:/MinGW/include/ddk" -I "${JAVA_HOME}/include" -I "${JAVA_HOME}/include/win32" smashlib.c -o ./x86/smashlib.o # MinGw-32 version +x86: + $(MKDIR_P) ./x86 + export PATH="C/MinGW/bin/:${PATH}" + $(CC32) ${CFLAGS} -m32 -c -fPIC -I "C:/MinGW/include/ddk" -I "${JAVA_HOME}/include" -I "${JAVA_HOME}/include/win32" smashlib.c -o ./smashlib_x86.o + $(CC32) ${CFLAGS} -shared -o ./x86/${APP_NAME} ./smashlib_x86.o -lsetupapi -lhid -Wl,--add-stdcall-alias + + #$(CC) ${CFLAGS} -m64 -c -fPIC -I "C:/MinGW/include/ddk" -I "${JAVA_HOME}/include" -I "${JAVA_HOME}/include/win32" smashlib.c -o ./amd64/smashlib.o # MinGw-32 version +amd64: + $(MKDIR_P) ./amd64 + export PATH="C/Program Files/mingw-w64/x86_64-8.1.0-win32-seh-rt_v6-rev0/mingw64/bin/:${PATH}" + $(CC64) ${CFLAGS} -m64 -c -fPIC -I "${JAVA_HOME}/include" -I "${JAVA_HOME}/include/win32" smashlib.c -o ./smashlib_amd64.o + $(CC64) ${CFLAGS} -shared -o ./amd64/${APP_NAME} ./smashlib_amd64.o -lsetupapi -lhid -Wl,--add-stdcall-alias + +clean: + rm -rf ./smashlib_x86.o ./smashlib_amd64.o ./x86 ./amd64 + +install: x86 amd64 + install ./x86/${APP_NAME} ../../src/main/resources/native/windows/x86/ + install ./amd64/${APP_NAME} ../../src/main/resources/native/windows/amd64/ + +uninstall: + rm ../../src/main/resources/native/windows/x86/${APP_NAME} + rm ../../src/main/resources/native/windows/amd64/${APP_NAME} diff --git a/JNI sources/windows/nsusbloader_Utilities_RcmSmash.h b/JNI sources/windows/nsusbloader_Utilities_RcmSmash.h new file mode 100644 index 0000000..9ed04db --- /dev/null +++ b/JNI sources/windows/nsusbloader_Utilities_RcmSmash.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class nsusbloader_Utilities_RcmSmash */ + +#ifndef _Included_nsusbloader_Utilities_RcmSmash +#define _Included_nsusbloader_Utilities_RcmSmash +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: nsusbloader_Utilities_RcmSmash + * Method: smashLinux + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashLinux + (JNIEnv *, jclass, jint, jint); + +/* + * Class: nsusbloader_Utilities_RcmSmash + * Method: smashWindows + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashWindows + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/JNI sources/windows/smashlib.c b/JNI sources/windows/smashlib.c new file mode 100644 index 0000000..834955a --- /dev/null +++ b/JNI sources/windows/smashlib.c @@ -0,0 +1,168 @@ +/* +* Derivative & modified code based on awesome example from here: https://www.velleman.eu/images/tmp/usbfind.c +* And MSDN documentation :) +* +* return +* -2 device not connected +* -1 Unable to open handler +* 0 maybe we're all set +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define _DEBUG +#define _BUILD_FOR_X86 + +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#ifdef BUILD_FOR_X86 +#include +#endif + +#include "nsusbloader_Utilities_RcmSmash.h" + +#define LIBUSB_IOCTL_GET_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS) + +static GUID GUID_DEVINTERFACE_USB_DEVICE = {0xA5DCBF10L, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}; +// NOTE: CHANGE TO NV DEVICE! +//static TCHAR VID_PID_PAIR[] = _T("vid_1a86&pid_7523"); // UNCOMMENT FOR TESTS +static TCHAR VID_PID_PAIR[] = _T("vid_0955&pid_7321"); // UNCOMMENT ON RELEASE + +typedef struct +{ + unsigned int timeout; + unsigned int recipient; + unsigned int index; + unsigned int status; + unsigned int ___zeroes_hold_0; // made it for better understanding. Literally useless. + unsigned int ___zeroes_hold_1; // Just consider each int as 4 bytes and calculate size =) +} simple_status_req; + +int win32_magic(LPCSTR lpFileName){ + unsigned char reqBuf[24] = {0}; + + simple_status_req* request; + request = (simple_status_req *) &reqBuf; + request->timeout = 1000; + request->recipient = 0x02; + request->index = 0; + request->status = 0; + +#ifdef DEBUG + printf("Device path: %s\nStatus: %x\nIn buffer size: %d\nIn buffer content: ", + lpFileName, + LIBUSB_IOCTL_GET_STATUS, + sizeof(reqBuf) ); // Path and what is our IOCTL request looks like + + for (int i = 0; i < sizeof(reqBuf); i++) + printf("%x ", reqBuf[i]); + printf("\n"); +#endif + unsigned char outBuffer[28672]; + + OVERLAPPED ovrlpd; + memset(&ovrlpd, 0, sizeof(ovrlpd)); + // Fucking finally let's open this + HANDLE handler = CreateFile( + lpFileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL + ); + + if ( handler == INVALID_HANDLE_VALUE ) + return -1; + + BOOL ret_val = DeviceIoControl( + handler, + LIBUSB_IOCTL_GET_STATUS, + (LPVOID) &reqBuf, + 24, + (LPVOID) &outBuffer, + 28672, + NULL, + &ovrlpd + ); + +#ifdef DEBUG + printf("\nDeviceIoControl reports: %d\nLast Error Code: %d\n", ret_val, GetLastError()); +#endif + Sleep(250); + DWORD bReceived = 0; + ret_val = GetOverlappedResult(handler, &ovrlpd, &bReceived, FALSE); +#ifdef DEBUG + if (! ret_val) { + // we won't report any issues since there is no workaround. + printf("\nLast Error Code: %d\n\n", GetLastError()); + } +#endif + CloseHandle(handler); + + return 0; +} + +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashWindows + (JNIEnv * jnie_enb, jclass this_class) { + int found = 0; + int ret_val = -2; + HDEVINFO hDevInfo; + SP_DEVICE_INTERFACE_DATA DevIntfData; + PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData; + SP_DEVINFO_DATA DevData; + + DWORD dwSize, dwType, dwMemberIdx; + HKEY hKey; + BYTE lpData[1024]; + + hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + + if (hDevInfo != INVALID_HANDLE_VALUE){ + DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + dwMemberIdx = 0; + + SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, dwMemberIdx, &DevIntfData); + + while(GetLastError() != ERROR_NO_MORE_ITEMS) { + DevData.cbSize = sizeof(DevData); + + SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL); + + DevIntfDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize); + DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, DevIntfDetailData, dwSize, &dwSize, &DevData)) { + if (NULL != _tcsstr((TCHAR*)DevIntfDetailData->DevicePath, VID_PID_PAIR)) { + found = 1; + ret_val = win32_magic(DevIntfDetailData->DevicePath); + } + } + + HeapFree(GetProcessHeap(), 0, DevIntfDetailData); + if (found) + break; + // Continue looping + SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData); + } + SetupDiDestroyDeviceInfoList(hDevInfo); + } + +#ifdef DEBUG + printf("Returning value: %d\n", ret_val); +#endif + return ret_val; +} + +JNIEXPORT jint JNICALL Java_nsusbloader_Utilities_RcmSmash_smashLinux + (JNIEnv * jnie_env, jclass this_class, jint bus_id, jint device_addr){ + return -1; +} diff --git a/README.md b/README.md index c95bd0d..e9c21d0 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,12 @@ [Support author](#support-this-app) -NS-USBloader is a PC-side installer for **[Adubbz/TinFoil (v0.2.1)](https://github.com/Adubbz/Tinfoil/)**, **[Huntereb/Awoo-Installer](https://github.com/Huntereb/Awoo-Installer)** (USB and Network supported) and **[XorTroll/GoldLeaf](https://github.com/XorTroll/Goldleaf)** (USB) NSP installer. +NS-USBloader is: +* A PC-side installer for **[Adubbz/TinFoil (v0.2.1)](https://github.com/Adubbz/Tinfoil/)**, **[Huntereb/Awoo-Installer](https://github.com/Huntereb/Awoo-Installer)** (USB and Network supported) and **[XorTroll/GoldLeaf](https://github.com/XorTroll/Goldleaf)** (USB) NSP installer. Replacement for default **usb_install_pc.py**, **remote_install_pc.py**, **GoldTree**/**Quark**. +* This application also could be used as RCM payload on Windows, MacOS and Linux (supported arch: x86, x86_64). +* And of course it's a tool for split files! +* And also for merging split-files into one :) [Click here for Android version ;)](https://github.com/developersu/ns-usbloader-mobile) @@ -23,6 +27,7 @@ Sometimes I add new posts about this project [on my home page](https://developer * [OpenJFX](https://wiki.openjdk.java.net/display/OpenJFX/Main) * [usb4java](https://mvnrepository.com/artifact/org.usb4java/usb4java) * Few icons taken from: [materialdesignicons.com](http://materialdesignicons.com/) +* Information, ideas and data from ['fusee-launcher'](https://github.com/reswitched/fusee-launcher) application #### List of awesome contributors! @@ -73,6 +78,12 @@ root # vim /etc/udev/rules.d/99-NS.rules SUBSYSTEM=="usb", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="3000", GROUP="plugdev" root # udevadm control --reload-rules && udevadm trigger ``` +4. For RCM part +``` +root # vim /etc/udev/rules.d/99-NS-RCM.rules +SUBSYSTEM=="usb", ATTRS{idVendor}=="0955", ATTRS{idProduct}=="7321", GROUP="plugdev" +root # udevadm control --reload-rules && udevadm trigger +``` Please note: you may have to change 'plugdev' group from example above to the different one. It's depends on you linux distro. @@ -154,7 +165,6 @@ If you want to see this app translated to your language, go grab [this file](htt Upload somewhere (create PR, use pastebin/google drive/whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add. To convert files of any locale to readable format (and vise-versa) you can use this site [https://itpro.cz/juniconv/](https://itpro.cz/juniconv/) - #### TODO (maybe): - [x] [Android support](https://github.com/developersu/ns-usbloader-mobile) diff --git a/pom.xml b/pom.xml index 00cd532..1fc6b99 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ NS-USBloader ns-usbloader - 1.1.0-SNAPSHOT + 2.0-SNAPSHOT https://github.com/developersu/ns-usbloader/ diff --git a/src/main/java/nsusbloader/AppPreferences.java b/src/main/java/nsusbloader/AppPreferences.java index 03c7ae2..b7d6262 100644 --- a/src/main/java/nsusbloader/AppPreferences.java +++ b/src/main/java/nsusbloader/AppPreferences.java @@ -123,7 +123,7 @@ public class AppPreferences { public double getSceneWidth(){ return preferences.getDouble("WIND_WIDTH", 850.0); } public void setSceneWidth(double value){ preferences.putDouble("WIND_WIDTH", value); } - public double getSceneHeight(){ return preferences.getDouble("WIND_HEIGHT", 475.0); } + public double getSceneHeight(){ return preferences.getDouble("WIND_HEIGHT", 525.0); } public void setSceneHeight(double value){ preferences.putDouble("WIND_HEIGHT", value); } // Split and Merge // public int getSplitMergeType(){ return preferences.getInt("SM_TYPE", 0); } @@ -131,4 +131,7 @@ public class AppPreferences { public String getSplitMergeRecent(){ return preferences.get("SM_RECENT", System.getProperty("user.home")); } public void setSplitMergeRecent(String value){ preferences.put("SM_RECENT", value); } + // RCM // + public String getRecentRcm(int num){ return preferences.get(String.format("RCM_%02d", num), ""); } + public void setRecentRcm(int num, String value){ preferences.put(String.format("RCM_%02d", num), value); } } diff --git a/src/main/java/nsusbloader/COM/USB/UsbCommunications.java b/src/main/java/nsusbloader/COM/USB/UsbCommunications.java index 014eb14..0d2685a 100644 --- a/src/main/java/nsusbloader/COM/USB/UsbCommunications.java +++ b/src/main/java/nsusbloader/COM/USB/UsbCommunications.java @@ -42,14 +42,14 @@ public class UsbCommunications extends Task { protected Void call() { logPrinter.print("\tStart chain", EMsgType.INFO); - UsbConnect usbConnect = new UsbConnect(logPrinter); + UsbConnect usbConnect = new UsbConnect(logPrinter, false); if (! usbConnect.isConnected()){ close(EFileStatus.FAILED); return null; } - DeviceHandle handler = usbConnect.getHandlerNS(); + DeviceHandle handler = usbConnect.getNsHandler(); TransferModule module; diff --git a/src/main/java/nsusbloader/COM/USB/UsbConnect.java b/src/main/java/nsusbloader/COM/USB/UsbConnect.java index 8477555..a1aa548 100644 --- a/src/main/java/nsusbloader/COM/USB/UsbConnect.java +++ b/src/main/java/nsusbloader/COM/USB/UsbConnect.java @@ -4,20 +4,41 @@ import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EMsgType; import org.usb4java.*; -class UsbConnect { - private final int DEFAULT_INTERFACE = 0; +public class UsbConnect { + private int DEFAULT_INTERFACE; private Context contextNS; private DeviceHandle handlerNS; + private Device deviceNS; private LogPrinter logPrinter; - private boolean connected; - - UsbConnect(LogPrinter logPrinter){ + private boolean connected; // TODO: replace to 'connectionFailure' and invert requests everywhere + + public UsbConnect(LogPrinter logPrinter, boolean initForRCM){ this.logPrinter = logPrinter; this.connected = false; + short VENDOR_ID; + short PRODUCT_ID; + + if (initForRCM){ + // CORRECT NV: + DEFAULT_INTERFACE = 1; + VENDOR_ID = 0x0955; + PRODUCT_ID = 0x7321; + /* // QA: + DEFAULT_INTERFACE = 0; + VENDOR_ID = 0x1a86; + PRODUCT_ID = 0x7523; + */ + } + else { + DEFAULT_INTERFACE = 0; + VENDOR_ID = 0x057E; + PRODUCT_ID = 0x3000; + } + int result; // Creating Context required by libusb. Optional. TODO: Consider removing. @@ -28,8 +49,7 @@ class UsbConnect { close(); return; } - else - logPrinter.print("libusb initialization", EMsgType.PASS); + logPrinter.print("libusb initialization", EMsgType.PASS); // Searching for NS in devices: obtain list of all devices DeviceList deviceList = new DeviceList(); @@ -39,11 +59,10 @@ class UsbConnect { close(); return; } - else - logPrinter.print("Get device list", EMsgType.PASS); + logPrinter.print("Get device list", EMsgType.PASS); // Searching for NS in devices: looking for NS DeviceDescriptor descriptor; - Device deviceNS = null; + deviceNS = null; for (Device device: deviceList){ descriptor = new DeviceDescriptor(); // mmm.. leave it as is. result = LibUsb.getDeviceDescriptor(device, descriptor); @@ -53,21 +72,20 @@ class UsbConnect { close(); return; } - if ((descriptor.idVendor() == 0x057E) && descriptor.idProduct() == 0x3000){ + if ((descriptor.idVendor() == VENDOR_ID) && descriptor.idProduct() == PRODUCT_ID){ deviceNS = device; logPrinter.print("Read file descriptors for USB devices", EMsgType.PASS); break; } } // Free device list. - if (deviceNS != null){ - logPrinter.print("NS in connected USB devices found", EMsgType.PASS); - } - else { + if (deviceNS == null){ logPrinter.print("NS in connected USB devices not found", EMsgType.FAIL); close(); return; } + logPrinter.print("NS in connected USB devices found", EMsgType.PASS); + // Handle NS device handlerNS = new DeviceHandle(); result = LibUsb.open(deviceNS, handlerNS); @@ -75,10 +93,11 @@ class UsbConnect { logPrinter.print("Open NS USB device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); if (result == LibUsb.ERROR_ACCESS) logPrinter.print("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.rules\n" + - "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"057e\", ATTRS{idProduct}==\"3000\", GROUP=\"plugdev\"\n" + - "root # udevadm control --reload-rules && udevadm trigger\n", EMsgType.INFO); + String.format("Steps to set 'udev' rules:\n" + + "root # vim /etc/udev/rules.d/99-NS.rules\n" + + "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", GROUP=\"plugdev\"\n" + + "root # udevadm control --reload-rules && udevadm trigger\n", VENDOR_ID, PRODUCT_ID) + , EMsgType.INFO); // Let's make a bit dirty workaround since such shit happened logPrinter.print("Requested context close", EMsgType.INFO); LibUsb.exit(contextNS); @@ -107,25 +126,24 @@ class UsbConnect { return; } */ - // Set configuration (soft reset if needed) - result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need - if (result != LibUsb.SUCCESS){ - logPrinter.print("Set active configuration to device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); - close(); - return; - } - else + if ( ! initForRCM){ + // Set configuration (soft reset if needed) + result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need + if (result != LibUsb.SUCCESS){ + logPrinter.print("Set active configuration to device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); + close(); + return; + } logPrinter.print("Set active configuration to device.", EMsgType.PASS); - + } // Claim interface result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE); if (result != LibUsb.SUCCESS) { - logPrinter.print("Claim interface\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); + logPrinter.print("Claim interface\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); close(); return; } - else - logPrinter.print("Claim interface", EMsgType.PASS); + logPrinter.print("Claim interface", EMsgType.PASS); this.connected = true; } @@ -134,23 +152,36 @@ class UsbConnect { * Get USB status * @return status of connection */ - boolean isConnected() { return connected; } + public boolean isConnected() { return connected; } /** * Getter for handler * @return DeviceHandle of NS */ - DeviceHandle getHandlerNS(){ return handlerNS; } + 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 * */ - void close(){ + public void close(){ // Close handler in the end if (handlerNS != null) { // Try to release interface int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE); if (result != LibUsb.SUCCESS) - logPrinter.print("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING); + logPrinter.print("Release interface" + + "\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING); else logPrinter.print("Release interface", EMsgType.PASS); diff --git a/src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java b/src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java index 8c8831b..f5c27a0 100644 --- a/src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java +++ b/src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java @@ -2,8 +2,8 @@ package nsusbloader.COM.USB; import org.usb4java.LibUsb; -class UsbErrorCodes { - static String getErrCode(int value){ +public class UsbErrorCodes { + public static String getErrCode(int value){ switch (value){ case LibUsb.ERROR_ACCESS: return "ERROR_ACCESS"; diff --git a/src/main/java/nsusbloader/Controllers/FrontController.java b/src/main/java/nsusbloader/Controllers/FrontController.java index 9f99177..d98727c 100644 --- a/src/main/java/nsusbloader/Controllers/FrontController.java +++ b/src/main/java/nsusbloader/Controllers/FrontController.java @@ -7,7 +7,6 @@ import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; @@ -25,8 +24,6 @@ import java.util.List; import java.util.ResourceBundle; public class FrontController implements Initializable { - @FXML - private Pane specialPane; @FXML private AnchorPane usbNetPane; @@ -52,7 +49,6 @@ public class FrontController implements Initializable { @Override public void initialize(URL url, ResourceBundle resourceBundle) { this.resourceBundle = resourceBundle; - specialPane.getStyleClass().add("special-pane-as-border"); // UI hacks ObservableList choiceProtocolList = FXCollections.observableArrayList("TinFoil", "GoldLeaf"); choiceProtocol.setItems(choiceProtocolList); @@ -195,7 +191,7 @@ public class FrontController implements Initializable { else fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NSP ROM", "*.nsp")); - filesList = fileChooser.showOpenMultipleDialog(specialPane.getScene().getWindow()); + filesList = fileChooser.showOpenMultipleDialog(usbNetPane.getScene().getWindow()); if (filesList != null && !filesList.isEmpty()) { tableFilesListController.setFiles(filesList); uploadStopBtn.setDisable(false); @@ -216,7 +212,7 @@ public class FrontController implements Initializable { else dirChooser.setInitialDirectory(new File(System.getProperty("user.home"))); - splitFile = dirChooser.showDialog(specialPane.getScene().getWindow()); + splitFile = dirChooser.showDialog(usbNetPane.getScene().getWindow()); if (splitFile != null && splitFile.getName().toLowerCase().endsWith(".nsp")) { tableFilesListController.setFile(splitFile); diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index c1f58a4..f621f5c 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -31,6 +31,8 @@ public class NSLMainController implements Initializable { private SettingsController SettingsTabController; @FXML private SplitMergeController SplitMergeTabController; + @FXML + private RcmController RcmTabController; @Override public void initialize(URL url, ResourceBundle rb) { @@ -123,6 +125,8 @@ public class NSLMainController implements Initializable { public SplitMergeController getSmCtrlr(){ return SplitMergeTabController; } + + public RcmController getRcmCtrlr(){ return RcmTabController; } /** * Save preferences before exit * */ @@ -147,5 +151,6 @@ public class NSLMainController implements Initializable { ); SplitMergeTabController.updatePreferencesOnExit(); // NOTE: This shit above should be re-written to similar pattern + RcmTabController.updatePreferencesOnExit(); } } diff --git a/src/main/java/nsusbloader/Controllers/RcmController.java b/src/main/java/nsusbloader/Controllers/RcmController.java new file mode 100644 index 0000000..7d92a91 --- /dev/null +++ b/src/main/java/nsusbloader/Controllers/RcmController.java @@ -0,0 +1,257 @@ +package nsusbloader.Controllers; + +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import nsusbloader.AppPreferences; +import nsusbloader.MediatorControl; +import nsusbloader.NSLDataTypes.EModule; +import nsusbloader.ServiceWindow; +import nsusbloader.Utilities.RcmTask; + +import java.io.File; +import java.net.URL; +import java.util.ResourceBundle; + +public class RcmController implements Initializable { + @FXML + private ToggleGroup rcmToggleGrp; + + @FXML + private VBox rcmToolPane; + + @FXML + private RadioButton pldrRadio1, + pldrRadio2, + pldrRadio3, + pldrRadio4, + pldrRadio5; + + @FXML + private Button injectPldBtn; + + @FXML + private Label payloadFNameLbl1, payloadFPathLbl1, + payloadFNameLbl2, payloadFPathLbl2, + payloadFNameLbl3, payloadFPathLbl3, + payloadFNameLbl4, payloadFPathLbl4, + payloadFNameLbl5, payloadFPathLbl5; + + @FXML + private Label statusLbl; + + private ResourceBundle rb; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + this.rb = resourceBundle; + rcmToggleGrp.selectToggle(pldrRadio1); + pldrRadio1.setOnAction(e -> statusLbl.setText("")); + pldrRadio2.setOnAction(e -> statusLbl.setText("")); + pldrRadio3.setOnAction(e -> statusLbl.setText("")); + pldrRadio4.setOnAction(e -> statusLbl.setText("")); + pldrRadio5.setOnAction(e -> statusLbl.setText("")); + + String recentRcm1 = AppPreferences.getInstance().getRecentRcm(1); + String recentRcm2 = AppPreferences.getInstance().getRecentRcm(2); + String recentRcm3 = AppPreferences.getInstance().getRecentRcm(3); + String recentRcm4 = AppPreferences.getInstance().getRecentRcm(4); + String recentRcm5 = AppPreferences.getInstance().getRecentRcm(5); + + String myRegexp; + if (File.separator.equals("/")) + myRegexp = "^.+/"; + else + myRegexp = "^.+\\\\"; + + if (! recentRcm1.isEmpty()) { + payloadFNameLbl1.setText(recentRcm1.replaceAll(myRegexp, "")); + payloadFPathLbl1.setText(recentRcm1); + } + if (! recentRcm2.isEmpty()) { + payloadFNameLbl2.setText(recentRcm2.replaceAll(myRegexp, "")); + payloadFPathLbl2.setText(recentRcm2); + } + if (! recentRcm3.isEmpty()) { + payloadFNameLbl3.setText(recentRcm3.replaceAll(myRegexp, "")); + payloadFPathLbl3.setText(recentRcm3); + } + if (! recentRcm4.isEmpty()) { + payloadFNameLbl4.setText(recentRcm4.replaceAll(myRegexp, "")); + payloadFPathLbl4.setText(recentRcm4); + } + if (! recentRcm5.isEmpty()) { + payloadFNameLbl5.setText(recentRcm5.replaceAll(myRegexp, "")); + payloadFPathLbl5.setText(recentRcm5); + } + + injectPldBtn.setDisable(false); // TODO: write logic ?? Like in case PAYLOADER exist, button active. If not: not active? + injectPldBtn.setOnAction(actionEvent -> smash()); + } + + private void smash(){ + statusLbl.setText(""); + if (MediatorControl.getInstance().getTransferActive()) { + ServiceWindow.getErrorNotification(rb.getString("windowTitleError"), rb.getString("windowBodyPleaseFinishTransfersFirst")); + return; + } + + Task RcmTask; + RadioButton selectedRadio = (RadioButton)rcmToggleGrp.getSelectedToggle(); + switch (selectedRadio.getId()){ + case "pldrRadio1": + RcmTask = new RcmTask(payloadFPathLbl1.getText()); + break; + case "pldrRadio2": + RcmTask = new RcmTask(payloadFPathLbl2.getText()); + break; + case "pldrRadio3": + RcmTask = new RcmTask(payloadFPathLbl3.getText()); + break; + case "pldrRadio4": + RcmTask = new RcmTask(payloadFPathLbl4.getText()); + break; + case "pldrRadio5": + RcmTask = new RcmTask(payloadFPathLbl5.getText()); + break; + default: + return; + } + + RcmTask.setOnSucceeded(event -> { + if (RcmTask.getValue()) + statusLbl.setText(rb.getString("done_txt")); + else + statusLbl.setText(rb.getString("failure_txt")); + }); + Thread RcmThread = new Thread(RcmTask); + RcmThread.setDaemon(true); + RcmThread.start(); + } + + @FXML + private void bntSelectPayloader(ActionEvent event){ + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle(rb.getString("btn_Select")); + + File validator = new File(payloadFPathLbl1.getText()).getParentFile(); + if (validator != null && validator.exists()) + fileChooser.setInitialDirectory(validator); + else + fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("bin", "*.bin")); + + File payloadFile = fileChooser.showOpenDialog(payloadFPathLbl1.getScene().getWindow()); + if (payloadFile != null) { + final Node btn = (Node)event.getSource(); + + switch (btn.getId()){ + case "selPldBtn1": + payloadFNameLbl1.setText(payloadFile.getName()); + payloadFPathLbl1.setText(payloadFile.getAbsolutePath()); + rcmToggleGrp.selectToggle(pldrRadio1); + break; + case "selPldBtn2": + payloadFNameLbl2.setText(payloadFile.getName()); + payloadFPathLbl2.setText(payloadFile.getAbsolutePath()); + rcmToggleGrp.selectToggle(pldrRadio2); + break; + case "selPldBtn3": + payloadFNameLbl3.setText(payloadFile.getName()); + payloadFPathLbl3.setText(payloadFile.getAbsolutePath()); + rcmToggleGrp.selectToggle(pldrRadio3); + break; + case "selPldBtn4": + payloadFNameLbl4.setText(payloadFile.getName()); + payloadFPathLbl4.setText(payloadFile.getAbsolutePath()); + rcmToggleGrp.selectToggle(pldrRadio4); + break; + case "selPldBtn5": + payloadFNameLbl5.setText(payloadFile.getName()); + payloadFPathLbl5.setText(payloadFile.getAbsolutePath()); + rcmToggleGrp.selectToggle(pldrRadio5); + } + } + } + @FXML + private void bntResetPayloader(ActionEvent event){ + final Node btn = (Node)event.getSource(); + + switch (btn.getId()){ + case "resPldBtn1": + payloadFNameLbl1.setText(""); + payloadFPathLbl1.setText(""); + statusLbl.setText(""); + break; + case "resPldBtn2": + payloadFNameLbl2.setText(""); + payloadFPathLbl2.setText(""); + statusLbl.setText(""); + break; + case "resPldBtn3": + payloadFNameLbl3.setText(""); + payloadFPathLbl3.setText(""); + statusLbl.setText(""); + break; + case "resPldBtn4": + payloadFNameLbl4.setText(""); + payloadFPathLbl4.setText(""); + statusLbl.setText(""); + break; + case "resPldBtn5": + payloadFNameLbl5.setText(""); + payloadFPathLbl5.setText(""); + statusLbl.setText(""); + } + } + + @FXML + public void selectPldrPane(MouseEvent mouseEvent) { + final Node selectedPane = (Node)mouseEvent.getSource(); + + switch (selectedPane.getId()){ + case "pldPane1": + pldrRadio1.fire(); + break; + case "pldPane2": + pldrRadio2.fire(); + break; + case "pldPane3": + pldrRadio3.fire(); + break; + case "pldPane4": + pldrRadio4.fire(); + break; + case "pldPane5": + pldrRadio5.fire(); + break; + } + } + + public void notifySmThreadStarted(boolean isStart, EModule type){ + rcmToolPane.setDisable(isStart); + if (type.equals(EModule.RCM) && isStart){ + MediatorControl.getInstance().getContoller().logArea.clear(); + } + } + /** + * Save application settings on exit + * */ + public void updatePreferencesOnExit(){ + AppPreferences.getInstance().setRecentRcm(1, payloadFPathLbl1.getText()); + AppPreferences.getInstance().setRecentRcm(2, payloadFPathLbl2.getText()); + AppPreferences.getInstance().setRecentRcm(3, payloadFPathLbl3.getText()); + AppPreferences.getInstance().setRecentRcm(4, payloadFPathLbl4.getText()); + AppPreferences.getInstance().setRecentRcm(5, payloadFPathLbl5.getText()); + } +} diff --git a/src/main/java/nsusbloader/Controllers/SplitMergeController.java b/src/main/java/nsusbloader/Controllers/SplitMergeController.java index de327c5..d0fa894 100644 --- a/src/main/java/nsusbloader/Controllers/SplitMergeController.java +++ b/src/main/java/nsusbloader/Controllers/SplitMergeController.java @@ -88,10 +88,15 @@ public class SplitMergeController implements Initializable { if (splitRad.isSelected()) { FileChooser fc = new FileChooser(); fc.setTitle(resourceBundle.getString("tabSplMrg_Btn_SelectFile")); - if (fileFolderActualPathLbl.getText().isEmpty()) - fc.setInitialDirectory(new File(System.getProperty("user.home"))); + if (! fileFolderActualPathLbl.getText().isEmpty()){ + File temporaryFile = new File(fileFolderActualPathLbl.getText()).getParentFile(); + if (temporaryFile != null && temporaryFile.exists()) + fc.setInitialDirectory(temporaryFile); + else + fc.setInitialDirectory(new File(System.getProperty("user.home"))); + } else - fc.setInitialDirectory(new File(fileFolderActualPathLbl.getText()).getParentFile()); + fc.setInitialDirectory(new File(System.getProperty("user.home"))); File fileFile = fc.showOpenDialog(changeSaveToBtn.getScene().getWindow()); if (fileFile == null) return; @@ -101,10 +106,16 @@ public class SplitMergeController implements Initializable { else{ DirectoryChooser dc = new DirectoryChooser(); dc.setTitle(resourceBundle.getString("tabSplMrg_Btn_SelectFolder")); - if (fileFolderActualPathLbl.getText().isEmpty()) - dc.setInitialDirectory(new File(System.getProperty("user.home"))); + if (! fileFolderActualPathLbl.getText().isEmpty()){ + File temporaryFile = new File(fileFolderActualPathLbl.getText()); + if (temporaryFile.exists()) + dc.setInitialDirectory(temporaryFile); + else + dc.setInitialDirectory(new File(System.getProperty("user.home"))); + } else - dc.setInitialDirectory(new File(fileFolderActualPathLbl.getText())); + dc.setInitialDirectory(new File(System.getProperty("user.home"))); + File folderFile = dc.showDialog(changeSaveToBtn.getScene().getWindow()); if (folderFile == null) return; @@ -132,6 +143,8 @@ public class SplitMergeController implements Initializable { convertBtn.setText(resourceBundle.getString("btn_Stop")); convertRegion.getStyleClass().clear(); convertRegion.getStyleClass().add("regionStop"); + convertBtn.getStyleClass().remove("buttonUp"); + convertBtn.getStyleClass().add("buttonStop"); return; } splitRad.setDisable(false); @@ -142,6 +155,8 @@ public class SplitMergeController implements Initializable { convertBtn.setOnAction(e -> setConvertBtnAction()); convertBtn.setText(resourceBundle.getString("tabSplMrg_Btn_Convert")); convertRegion.getStyleClass().clear(); + convertBtn.getStyleClass().remove("buttonStop"); + convertBtn.getStyleClass().add("buttonUp"); if (splitRad.isSelected()) convertRegion.getStyleClass().add("regionSplitToOne"); else @@ -159,6 +174,7 @@ public class SplitMergeController implements Initializable { * It's button listener when convert-process NOT in progress * */ private void setConvertBtnAction(){ + statusLbl.setText(""); if (MediatorControl.getInstance().getTransferActive()) { ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"), resourceBundle.getString("windowBodyPleaseFinishTransfersFirst")); return; diff --git a/src/main/java/nsusbloader/MediatorControl.java b/src/main/java/nsusbloader/MediatorControl.java index 12d9c38..4623ca4 100644 --- a/src/main/java/nsusbloader/MediatorControl.java +++ b/src/main/java/nsusbloader/MediatorControl.java @@ -25,6 +25,7 @@ public class MediatorControl { isTransferActive.set(isActive); mainCtrler.getFrontCtrlr().notifyTransmThreadStarted(isActive, appModuleType); mainCtrler.getSmCtrlr().notifySmThreadStarted(isActive, appModuleType); + mainCtrler.getRcmCtrlr().notifySmThreadStarted(isActive, appModuleType); } public synchronized boolean getTransferActive() { return this.isTransferActive.get(); } } diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java index 6dd4c20..7123189 100644 --- a/src/main/java/nsusbloader/NSLMain.java +++ b/src/main/java/nsusbloader/NSLMain.java @@ -13,7 +13,7 @@ import java.util.ResourceBundle; public class NSLMain extends Application { - public static final String appVersion = "v1.1"; + public static final String appVersion = "v2.0"; @Override public void start(Stage primaryStage) throws Exception{ diff --git a/src/main/java/nsusbloader/Utilities/JNIRcmLoader.java b/src/main/java/nsusbloader/Utilities/JNIRcmLoader.java new file mode 100644 index 0000000..77da6bb --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/JNIRcmLoader.java @@ -0,0 +1,105 @@ +package nsusbloader.Utilities; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; + +public class JNIRcmLoader { + private JNIRcmLoader(){} + public static boolean load(){ + String osName = System.getProperty("os.name").toLowerCase().replace(" ", ""); + String osArch = System.getProperty("os.arch").toLowerCase().replace(" ", ""); + String libPostfix; + + if (osName.equals("linux")){ + switch (osArch){ + case "i386": + case "i586": + case "i686": + osArch = "x86"; + break; + case "x86_64": + case "amd64": + osArch = "amd64"; + break; + default: + return false; + } + libPostfix = "so"; + } + else if (osName.contains("windows")){ + osName = "windows"; + libPostfix = "dll"; + switch (osArch){ + case "x86": + case "i386": + case "i586": + case "i686": + osArch = "x86"; + break; + case "x86_64": + case "amd64": + osArch = "amd64"; + break; + default: + return false; + } + } + else + return false; + final URL url_ = RcmSmash.class.getResource("/native/"+osName+"/"+osArch+"/smashlib."+libPostfix); + if (url_ == null) + return false; + + String proto = url_.getProtocol(); + + File libraryFile; + if (proto.equals("file")){ + // We can pick file from disk as is. + try { + libraryFile = new File(url_.toURI()); + } + catch (URISyntaxException e){ + e.printStackTrace(); + return false; + } + } + else if (proto.equals("jar")){ + // We have to export file to temp dir. + InputStream inStream = RcmSmash.class.getResourceAsStream("/native/"+osName+"/"+osArch+"/smashlib."+libPostfix); + if (inStream == null) + return false; + // Create temp folder + try{ + File tmpDirFile = File.createTempFile("jni", null); + if (! tmpDirFile.delete()) + return false; + if (! tmpDirFile.mkdirs()) + return false; + libraryFile = new File(tmpDirFile, "smashlib."+libPostfix); + byte[] ioBuffer = new byte[8192]; + FileOutputStream foStream = new FileOutputStream(libraryFile); + while (inStream.read(ioBuffer) != -1) + foStream.write(ioBuffer); + foStream.close(); + inStream.close(); + libraryFile.deleteOnExit(); + tmpDirFile.deleteOnExit(); + } + catch (IOException ioe){ + ioe.printStackTrace(); + return false; + } + } + else + return false; + + //System.out.println("LIB LOCATION: "+libraryFile); + System.load(libraryFile.getAbsolutePath()); + //System.out.println("LIB LOADED"); + return true; + } +} diff --git a/src/main/java/nsusbloader/Utilities/RcmSmash.java b/src/main/java/nsusbloader/Utilities/RcmSmash.java new file mode 100644 index 0000000..b236518 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/RcmSmash.java @@ -0,0 +1,17 @@ +package nsusbloader.Utilities; + +public class RcmSmash { + + private static final boolean supported; + + static { + supported = JNIRcmLoader.load(); + } + + private RcmSmash(){} + + public static native int smashLinux(final int bus_id, final int device_addr); + public static native int smashWindows(); + + public static boolean isSupported() { return supported; } +} diff --git a/src/main/java/nsusbloader/Utilities/RcmTask.java b/src/main/java/nsusbloader/Utilities/RcmTask.java new file mode 100644 index 0000000..8277478 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/RcmTask.java @@ -0,0 +1,332 @@ +package nsusbloader.Utilities; +/* + * Implementation of the 'Fusée Gelée' RCM payload that is inspired by 'fusee-launcher' application by ktemkin. + * Definitely uses ideas and even some code. + * Check original project: https://github.com/reswitched/fusee-launcher + * + * This code is not political. It could be used by anyone. + * Find details in LICENSE file in the root directory of this project. + **/ + +import javafx.concurrent.Task; +import nsusbloader.COM.USB.UsbConnect; +import nsusbloader.COM.USB.UsbErrorCodes; +import nsusbloader.ModelControllers.LogPrinter; +import nsusbloader.NSLDataTypes.EModule; +import nsusbloader.NSLDataTypes.EMsgType; +import org.usb4java.*; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.Arrays; + +public class RcmTask extends Task { + + private enum ECurrentOS { + win, lin, mac, unsupported + } + + private LogPrinter logPrinter; + private String filePath; + + private DeviceHandle handler; + + private byte[] fullPayload; + + private static final byte[] initSeq = { (byte) 0x98, (byte) 0x02, (byte) 0x03 }; + + private static final byte[] mezzo = { + (byte) 0x5c, (byte) 0x00, (byte) 0x9f, (byte) 0xe5, (byte) 0x5c, (byte) 0x10, (byte) 0x9f, (byte) 0xe5, (byte) 0x5c, (byte) 0x20, (byte) 0x9f, (byte) 0xe5, (byte) 0x01, (byte) 0x20, (byte) 0x42, (byte) 0xe0, + (byte) 0x0e, (byte) 0x00, (byte) 0x00, (byte) 0xeb, (byte) 0x48, (byte) 0x00, (byte) 0x9f, (byte) 0xe5, (byte) 0x10, (byte) 0xff, (byte) 0x2f, (byte) 0xe1, (byte) 0x00, (byte) 0x00, (byte) 0xa0, (byte) 0xe1, + (byte) 0x48, (byte) 0x00, (byte) 0x9f, (byte) 0xe5, (byte) 0x48, (byte) 0x10, (byte) 0x9f, (byte) 0xe5, (byte) 0x01, (byte) 0x29, (byte) 0xa0, (byte) 0xe3, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0xeb, + (byte) 0x38, (byte) 0x00, (byte) 0x9f, (byte) 0xe5, (byte) 0x01, (byte) 0x19, (byte) 0xa0, (byte) 0xe3, (byte) 0x01, (byte) 0x00, (byte) 0x80, (byte) 0xe0, (byte) 0x34, (byte) 0x10, (byte) 0x9f, (byte) 0xe5, + (byte) 0x03, (byte) 0x28, (byte) 0xa0, (byte) 0xe3, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0xeb, (byte) 0x20, (byte) 0x00, (byte) 0x9f, (byte) 0xe5, (byte) 0x10, (byte) 0xff, (byte) 0x2f, (byte) 0xe1, + (byte) 0x04, (byte) 0x30, (byte) 0x91, (byte) 0xe4, (byte) 0x04, (byte) 0x30, (byte) 0x80, (byte) 0xe4, (byte) 0x04, (byte) 0x20, (byte) 0x52, (byte) 0xe2, (byte) 0xfb, (byte) 0xff, (byte) 0xff, (byte) 0x1a, + (byte) 0x1e, (byte) 0xff, (byte) 0x2f, (byte) 0xe1, (byte) 0x00, (byte) 0xf0, (byte) 0x00, (byte) 0x40, (byte) 0x20, (byte) 0x00, (byte) 0x01, (byte) 0x40, (byte) 0x7c, (byte) 0x00, (byte) 0x01, (byte) 0x40, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x40, (byte) 0x40, (byte) 0x0e, (byte) 0x01, (byte) 0x40, (byte) 0x00, (byte) 0x70, (byte) 0x01, (byte) 0x40 + }; // 124 bytes + + private static final byte[] sprayPttrn = { 0x00, 0x00, 0x01, 0x40}; + + public RcmTask(String filePath){ + this.logPrinter = new LogPrinter(EModule.RCM); + this.filePath = filePath; + } + + @Override + protected Boolean call() { + logPrinter.print("Selected: "+filePath, EMsgType.INFO); + logPrinter.print("=============== RCM ===============", EMsgType.INFO); + + ECurrentOS ecurrentOS; + String realOsName = System.getProperty("os.name").toLowerCase().replace(" ", ""); + if (realOsName.equals("macos") || realOsName.equals("macosx") || realOsName.equals("freebsd")) + ecurrentOS = ECurrentOS.mac; + else if (realOsName.contains("windows")) + ecurrentOS = ECurrentOS.win; + else if (realOsName.equals("linux")) + ecurrentOS = ECurrentOS.lin; + else + ecurrentOS = ECurrentOS.unsupported; + logPrinter.print("Found your OS: "+System.getProperty("os.name"), EMsgType.PASS); + + if (! ecurrentOS.equals(ECurrentOS.mac)){ + if (! RcmSmash.isSupported()){ + logPrinter.print("Unfortunately your platform '"+System.getProperty("os.name")+ + "' of '"+System.getProperty("os.arch")+"' is not supported :("+ + "\n But you could file a bug with request."+ + "\n\n Nothing has been sent to NS. Execution stopped.", EMsgType.FAIL); + logPrinter.close(); + return false; + } + } + + if (preparePayload()){ + logPrinter.close(); + return false; + } + // === TEST THIS === + // writeTestFile(); + // ================= + // Bring up USB connection + + UsbConnect usbConnect = new UsbConnect(logPrinter, true); + + if (! usbConnect.isConnected()){ + logPrinter.close(); + return false; + } + this.handler = usbConnect.getNsHandler(); + + // Get device ID and show it. + if (readUsbDeviceID()){ + usbConnect.close(); + logPrinter.close(); + return false; + } + // Send payload + for (int i=0; i < fullPayload.length / 4096 ; i++){ + if (writeUsb(Arrays.copyOfRange(fullPayload, i*4096, (i+1)*4096))){ + logPrinter.print("Failed to sent payload ["+i+"]"+ + "\n\n Execution stopped.", EMsgType.FAIL); + usbConnect.close(); + logPrinter.close(); + return false; + } + } + logPrinter.print("Information sent to NS.", EMsgType.PASS); + + if (ecurrentOS.equals(ECurrentOS.mac)){ + if (smashMacOS()){ + usbConnect.close(); + logPrinter.close(); + return false; + } + } + else { + // JNI MAGIC HERE + int retval; + if (ecurrentOS.equals(ECurrentOS.lin)) + retval = RcmSmash.smashLinux(usbConnect.getNsBus(), usbConnect.getNsAddress()); + else if (ecurrentOS.equals(ECurrentOS.win)) + retval = RcmSmash.smashWindows(); + else { + // ( ?_?) + logPrinter.print("Failed to smash the stack since your OS is not supported. Please report this issue."+ + "\n\n Execution stopped and failed. And it's strange.", EMsgType.FAIL); + usbConnect.close(); + logPrinter.close(); + return false; + } + + if (retval != 0){ + logPrinter.print("Failed to smash the stack ("+retval+")"+ + "\n\n Execution stopped and failed.", EMsgType.FAIL); + usbConnect.close(); + logPrinter.close(); + return false; + } + logPrinter.print(".:: Payload complete ::.", EMsgType.PASS); + } + + usbConnect.close(); + logPrinter.close(); + return true; + } + /** + * Prepare the 'big' or full-size byte-buffer that is actually is a payload that we're about to use. + * @return false for issues + * true for good result + * */ + private boolean preparePayload(){ + File pldrFile = new File(filePath); + + // 126296 b <- biggest size per CTCaer; 16384 selected randomly as minimum threshold. It's probably wrong. + if (pldrFile.length() > 126296 || pldrFile.length() < 16384) { + logPrinter.print("File size of this payload looks wired. It's "+pldrFile.length()+" bytes."+ + "\n 1. Double-check that you're using the right payload." + + "\n 2. Please report this issue in case you're sure that you're doing everything right." + + "\n\n Nothing has been sent to NS. Execution stopped.", EMsgType.FAIL); + return true; + } + // Get payload file size + int pldFileSize = (int) pldrFile.length(); + // Get full payload array size + int totalSize = 4328 + pldFileSize + 8640; + totalSize += 4096 - (totalSize % 4096); + if ((totalSize / 4096 % 2) == 0) // Flip buffer story to get 0x40009000 (hi) buf to always smash with 0x7000 (dec: 28672) + totalSize += 4096; + // Double-check + if (totalSize > 0x30298){ + logPrinter.print("File size of the payload is too bit. Comparing to maximum size, it's greater to "+(totalSize - 0x30298)+" bytes!"+ + "\n 1. Double-check that you're using the right payload." + + "\n 2. Please report this issue in case you're sure that you're doing everything right." + + "\n\n Nothing has been sent to NS. Execution stopped.", EMsgType.FAIL); // Occurs: never. I'm too lazy to check. + return true; + } + // Define holder of 'everything payload' + fullPayload = new byte[totalSize]; + // Prepare array to store file payload. + byte[] dataPldFile = new byte[pldFileSize]; + + try{ + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pldrFile)); + int readSize; + if ((readSize = bis.read(dataPldFile)) != pldFileSize){ + logPrinter.print("Failed to retrieve data from payload file." + + "\n Got only "+readSize+" bytes while "+pldFileSize+" expected." + + "\n\n Nothing has been sent to NS. Execution stopped.", EMsgType.FAIL); + bis.close(); + return true; + } + bis.close(); + } + catch (Exception e){ + logPrinter.print("Failed to retrieve data from payload file: " +e.getMessage()+ + "\n\n Nothing has been sent to NS. Execution stopped.", EMsgType.FAIL); + return true; + } + // Trust me + System.arraycopy(initSeq, 0, fullPayload, 0, 3); + System.arraycopy(mezzo, 0, fullPayload, 680, 124); + System.arraycopy(dataPldFile, 0, fullPayload, 4328, 16384); + for (int i = 0; i < 2160; i++) + System.arraycopy(sprayPttrn, 0, fullPayload, 20712+i*4, 4); + System.arraycopy(dataPldFile, 16384, fullPayload, 29352, pldFileSize-16384); + return false; + } + /** + * Read device ID in the early beginning + * @return false if NO issues + * true if issues + * */ + private boolean readUsbDeviceID(){ + ByteBuffer readBuffer = ByteBuffer.allocateDirect(16); + IntBuffer readBufTransferred = IntBuffer.allocate(1); + int result = LibUsb.bulkTransfer(handler, (byte) 0x81, readBuffer, readBufTransferred, 1000); + if (result != LibUsb.SUCCESS) { + logPrinter.print("Unable to get device ID" + + "\n\n Nothing has been sent to NS. Execution stopped.", EMsgType.FAIL); + return true; + } + int trans = readBufTransferred.get(); + byte[] receivedBytes = new byte[trans]; + readBuffer.get(receivedBytes); + StringBuilder idStrBld = new StringBuilder("Found device with ID: "); + for (byte b: receivedBytes) + idStrBld.append(String.format("%02x ", b)); + logPrinter.print(idStrBld.toString(), EMsgType.PASS); + return false; + } + /** + * Sending byte array to USB device + * @return 'false' if no issues + * 'true' if errors happened + * */ + private boolean writeUsb(byte[] message){ + ByteBuffer writeBuffer = ByteBuffer.allocateDirect(4096); + writeBuffer.put(message); + IntBuffer writeBufTransferred = IntBuffer.allocate(1); + int result = LibUsb.bulkTransfer(handler, (byte) 0x01, writeBuffer, writeBufTransferred, 5050); + + if (result == LibUsb.SUCCESS) { + if (writeBufTransferred.get() == 4096) + return false; + + logPrinter.print("RCM Data transfer issue [write]" + + "\n Requested: " + message.length + + "\n Transferred: " + writeBufTransferred.get()+ + "\n\n Execution stopped.", EMsgType.FAIL); + return true; + } + logPrinter.print("RCM Data transfer issue [write]" + + "\n Returned: " + UsbErrorCodes.getErrCode(result) + + "\n\n Execution stopped.", EMsgType.FAIL); + return true; + } + /** + * MacOS version of RcmSmash class + * */ + boolean smashMacOS(){ + + // Release interface + int result = LibUsb.releaseInterface(handler, 1); + if (result != LibUsb.SUCCESS) { + logPrinter.print("Release interface failed" + + "\n Returned: " + result, EMsgType.FAIL); + return true; + } + logPrinter.print("Release interface 1.", EMsgType.PASS); + // Claim interface + result = LibUsb.claimInterface(handler, 0); + if (result != LibUsb.SUCCESS) { + logPrinter.print("Claim interface 0." + + "\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); + return true; + } + logPrinter.print("Claim interface", EMsgType.PASS); + + ByteBuffer writeBuffer = ByteBuffer.allocateDirect(28672); //writeBuffer.order() equals BIG_ENDIAN; 28672 + result = LibUsb.controlTransfer(handler, (byte) 0x82, LibUsb.REQUEST_GET_STATUS, (short) 0, (short) 0, writeBuffer, 1000); + if (result < 0){ + logPrinter.print("Failed to smash the stack ("+UsbErrorCodes.getErrCode(result)+")"+ + "\n\n Execution stopped and failed.", EMsgType.FAIL); + return true; + } + logPrinter.print("Payload complete!", EMsgType.PASS); + return false; + } + + //*****************************************************************************************************************/ + /* + private void writeTestFile(){ + try { + File testFile = new File("/tmp/dmTests.bin"); + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(testFile) + ); + bos.write(fullPayload); + bos.close(); + } + catch (Exception e){ e.printStackTrace(); } + // ------------------ TEST THIS p.2 ---------------------- + writeUsbTest(fullPayload); + } + + private boolean writeUsbTest(byte[] message){ + for (int i=0; i < message.length / 0x1000 ;i++){ + try { + File testFile = new File(String.format("/tmp/cnk_%02d.bin", i)); + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(testFile) + ); + bos.write(Arrays.copyOfRange(message, i*4096, (i+1)*4096)); + bos.close(); + } + catch (Exception e){ e.printStackTrace(); } + } + return false; + } + */ +} \ No newline at end of file diff --git a/src/main/resources/FrontTab.fxml b/src/main/resources/FrontTab.fxml index 6cf6935..287f202 100644 --- a/src/main/resources/FrontTab.fxml +++ b/src/main/resources/FrontTab.fxml @@ -4,6 +4,7 @@ + @@ -39,7 +40,7 @@ - + diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index 8d54217..8c5282f 100644 --- a/src/main/resources/NSLMain.fxml +++ b/src/main/resources/NSLMain.fxml @@ -23,6 +23,14 @@ + + + + + + + + diff --git a/src/main/resources/RcmTab.fxml b/src/main/resources/RcmTab.fxml new file mode 100644 index 0000000..b2ef607 --- /dev/null +++ b/src/main/resources/RcmTab.fxml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/SettingsTab.fxml b/src/main/resources/SettingsTab.fxml index 961b0e8..76c8c7b 100644 --- a/src/main/resources/SettingsTab.fxml +++ b/src/main/resources/SettingsTab.fxml @@ -127,7 +127,7 @@ - + diff --git a/src/main/resources/SplitMergeTab.fxml b/src/main/resources/SplitMergeTab.fxml index 9677b23..5792d84 100644 --- a/src/main/resources/SplitMergeTab.fxml +++ b/src/main/resources/SplitMergeTab.fxml @@ -4,22 +4,44 @@ + + + + - - - + + - + + + + + + + + + + + + + + + + + + + - + @@ -29,6 +51,9 @@ + + + @@ -40,6 +65,9 @@