diff --git a/.github/workflows/build-pack-manual.yml b/.github/workflows/build-pack-manual.yml index 41d25d7..80156b2 100644 --- a/.github/workflows/build-pack-manual.yml +++ b/.github/workflows/build-pack-manual.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: jobs: - build-and-create-draft: + build-and-setup: # The type of runner that the job will run on runs-on: ubuntu-latest @@ -45,7 +45,7 @@ jobs: path: version.txt ubuntu-pack: - needs: [ build-and-create-draft ] + needs: [ build-and-setup ] runs-on: ubuntu-latest steps: @@ -106,7 +106,7 @@ jobs: path: FutureRestore-GUI-Linux-Universal.zip macos-pack: - needs: [ build-and-create-draft ] + needs: [ build-and-setup ] runs-on: macos-latest steps: @@ -118,6 +118,9 @@ jobs: with: java-version: 15 + - name: Install create-dmg + run: brew install create-dmg + - name: Download final jar uses: actions/download-artifact@v2 with: @@ -141,19 +144,28 @@ jobs: run: | jpackage --input ./input --main-jar "FutureRestore GUI-1.0-all.jar" --main-class Main --type app-image --icon ./.github/workflows/mac/FutureRestoreGUIIcons.icns --copyright "© CoocooFroggy 2021" --verbose --mac-package-identifier com.coocoofroggy.futurerestoregui --name "FutureRestore GUI" --module-path ./.github/workflows/mac/javafx-jmods-11.0.2 --add-modules javafx.swing,java.logging,java.sql,java.base,jdk.crypto.ec,java.naming --app-version $VERSION codesign --remove-signature FutureRestore\ GUI.app + mkdir dmg-input + mv "FutureRestore GUI.app" dmg-input + +# - name: Debug with ssh +# uses: lhotari/action-upterm@v1 + + - name: Create DMG from .app + run: | + create-dmg --hdiutil-quiet --volname "FutureRestore GUI Installer" --volicon ".github/workflows/mac/DMG Images/FRGUI-Drive-Icon.icns" --background ".github/workflows/mac/DMG Images/DMG-Background.png" --window-pos 200 120 --window-size 600 342 --icon-size 100 --icon "FutureRestore GUI.app" 80 200 --hide-extension "FutureRestore GUI.app" --app-drop-link 450 200 "FutureRestore GUI Installer.dmg" "dmg-input/" - - name: Zip .app - id: zip_app - run: zip -r FutureRestore-GUI-Mac.zip "FutureRestore GUI.app" +# - name: Zip .app +# id: zip_app +# run: zip -r FutureRestore-GUI-Mac.zip "FutureRestore GUI.app" - name: Upload Mac release uses: actions/upload-artifact@v2 with: - name: FutureRestore-GUI-Mac-${{ steps.output-version.outputs.version }}.zip - path: FutureRestore-GUI-Mac.zip + name: FutureRestore-GUI-Mac-${{ steps.output-version.outputs.version }}.dmg + path: FutureRestore GUI Installer.dmg windows-pack: - needs: [ build-and-create-draft ] + needs: [ build-and-setup ] runs-on: windows-latest steps: diff --git a/.github/workflows/build-pack-release.yml b/.github/workflows/build-pack-release.yml index 7879802..b76a22d 100644 --- a/.github/workflows/build-pack-release.yml +++ b/.github/workflows/build-pack-release.yml @@ -174,6 +174,9 @@ jobs: with: java-version: 15 + - name: Install create-dmg + run: brew install create-dmg + - name: Download final jar uses: actions/download-artifact@v2 with: @@ -197,10 +200,16 @@ jobs: run: | jpackage --input ./input --main-jar "FutureRestore GUI-1.0-all.jar" --main-class Main --type app-image --icon ./.github/workflows/mac/FutureRestoreGUIIcons.icns --copyright "© CoocooFroggy 2021" --verbose --mac-package-identifier com.coocoofroggy.futurerestoregui --name "FutureRestore GUI" --module-path ./.github/workflows/mac/javafx-jmods-11.0.2 --add-modules javafx.swing,java.logging,java.sql,java.base,jdk.crypto.ec,java.naming --app-version $VERSION codesign --remove-signature FutureRestore\ GUI.app + mkdir dmg-input + mv "FutureRestore GUI.app" dmg-input + + - name: Create DMG from .app + run: | + create-dmg --hdiutil-quiet --volname "FutureRestore GUI Installer" --volicon "/.github/workflows/mac/DMG Images/FRGUI-Drive-Icon.icns" --background "/.github/workflows/mac/DMG Images/DMG-Background.png" --window-pos 200 120 --window-size 600 342 --icon-size 100 --icon "FutureRestore GUI.app" 80 200 --hide-extension "FutureRestore GUI.app" --app-drop-link 450 200 "FutureRestore GUI Installer.dmg" "dmg-input/" - - name: Zip .app - id: zip_app - run: zip -r FutureRestore-GUI-Mac.zip "FutureRestore GUI.app" +# - name: Zip .app +# id: zip_app +# run: zip -r FutureRestore-GUI-Mac.zip "FutureRestore GUI.app" - name: Download URL to upload to uses: actions/download-artifact@v2 @@ -220,9 +229,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.set_upload_url.outputs.upload-url }} - asset_path: FutureRestore-GUI-Mac.zip - asset_name: FutureRestore-GUI-Mac-${{ steps.output-version.outputs.version }}.zip - asset_content_type: application/zip + asset_path: FutureRestore GUI Installer.dmg + asset_name: FutureRestore-GUI-Mac-${{ steps.output-version.outputs.version }}.dmg + asset_content_type: application/octet-stream windows-pack: needs: [ build-and-create-draft ] diff --git a/.github/workflows/mac/DMG Images/DMG-Background.png b/.github/workflows/mac/DMG Images/DMG-Background.png new file mode 100644 index 0000000..7ed9829 Binary files /dev/null and b/.github/workflows/mac/DMG Images/DMG-Background.png differ diff --git a/.github/workflows/mac/DMG Images/FRGUI-Drive-Icon.icns b/.github/workflows/mac/DMG Images/FRGUI-Drive-Icon.icns new file mode 100644 index 0000000..c1053de Binary files /dev/null and b/.github/workflows/mac/DMG Images/FRGUI-Drive-Icon.icns differ diff --git a/build.gradle b/build.gradle index 87be374..49803a8 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ dependencies { implementation 'com.github.Dansoftowner:jSystemThemeDetector:3.6' implementation 'com.github.oshi:oshi-core:5.6.1' implementation group: 'commons-io', name: 'commons-io', version: '2.8.0' + implementation 'com.github.rjeschke:txtmark:0.13' } test { diff --git a/src/main/java/FRUtils.java b/src/main/java/FRUtils.java new file mode 100644 index 0000000..59a7d05 --- /dev/null +++ b/src/main/java/FRUtils.java @@ -0,0 +1,402 @@ +import com.google.gson.Gson; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FRUtils { + private static final ArrayList disabledComponents = new ArrayList<>(); + + public static boolean openWebpage(String uriString, MainMenu mainMenuInstance) { + URI uri = null; + try { + uri = new URI(uriString); + } catch (URISyntaxException e) { + System.out.println("Unable to create link for " + uriString); + e.printStackTrace(); + } + Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(uri); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + } + // Since it failed to open, copy it to clipboard + StringSelection stringSelection = new StringSelection(uriString); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + // If no MainMenu was passed, just return without logging anything. Let them log it themselves + if (mainMenuInstance != null) + mainMenuInstance.messageToLog("Unable to open URL \"" + uriString + "\""); + return false; + } + + public static boolean updateFRGUI(MainMenu mainMenuInstance) throws IOException, InterruptedException { + JPanel mainMenuView = mainMenuInstance.getMainMenuView(); + + // If FutureRestore process is running, cancel early + if (!(FutureRestoreWorker.futureRestoreProcess == null || !FutureRestoreWorker.futureRestoreProcess.isAlive())) { + failUpdate("Can't update when FutureRestore is running!", mainMenuInstance, false); + return false; + } + + // Disable the whole menu + setEnabled(mainMenuView, false, true); + + String osName = System.getProperty("os.name").toLowerCase(); + String frguiDownloadIdentifier = null; + if (osName.contains("mac")) { + frguiDownloadIdentifier = "Mac"; + } else if (osName.contains("win")) { + frguiDownloadIdentifier = "Windows"; + } else if (osName.contains("linux")) { + // Debs only work on debian + int exitCode = Runtime.getRuntime().exec("dpkg --version").waitFor(); + if (exitCode == 0) + frguiDownloadIdentifier = "Debian"; + } + + if (frguiDownloadIdentifier == null) { + Object[] choices = {"Open link", "Ok"}; + Object defaultChoice = choices[0]; + + int response = JOptionPane.showOptionDialog(mainMenuView, "Unable to automatically update for this operating system. Please update FutureRestore GUI manually.\n" + + "https://github.com/CoocooFroggy/FutureRestore-GUI/releases/latest", "Download FutureRestore GUI", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, defaultChoice); + if (response == JOptionPane.YES_OPTION) { + FRUtils.openWebpage("https://github.com/CoocooFroggy/FutureRestore-GUI/releases/latest", mainMenuInstance); + } + return false; + } else { + File downloadedFrgui = downloadFRGUI(mainMenuInstance, frguiDownloadIdentifier); + + if (downloadedFrgui == null) { + // We already notify the user of error in downloadFRGUI() + return false; + } + + if (installFrgui(downloadedFrgui, frguiDownloadIdentifier, mainMenuInstance)) { + System.out.println("All done updating FRGUI. Closing now..."); + System.exit(0); + } else { + System.err.println("Failed to update FutureRestore GUI. Try again, or try installing the update manually."); + setEnabled(mainMenuView, true, true); + } + return true; + } + } + + public static File downloadFRGUI(MainMenu mainMenuInstance, String frguiDownloadIdentifier) { + JProgressBar logProgressBar = mainMenuInstance.getLogProgressBar(); + JTextField currentTaskTextField = mainMenuInstance.getCurrentTaskTextField(); + + //Download synchronously + String frguiDownloadName = null; + String frguiDownloadUrl = null; + try { + System.out.println("Finding download..."); + URL releasesApiUrl = new URL("https://api.github.com/repos/CoocooFroggy/FutureRestore-GUI/releases/latest"); + String releasesApiResponse = IOUtils.toString(releasesApiUrl.openConnection().getInputStream(), StandardCharsets.UTF_8); + Gson gson = new Gson(); + Map latestReleaseApi = gson.fromJson(releasesApiResponse, Map.class); + + ArrayList> assetsApi = (ArrayList>) latestReleaseApi.get("assets"); + + for (Map asset : assetsApi) { + frguiDownloadName = (String) asset.get("name"); + if (frguiDownloadName.contains(frguiDownloadIdentifier)) { + // Found a download for our OS + frguiDownloadUrl = (String) asset.get("browser_download_url"); + break; + } + } + + } catch (IOException e) { + e.printStackTrace(); + failUpdate("Unable to update FutureRestore GUI.", mainMenuInstance, true); + return null; + } + + if (frguiDownloadName == null || frguiDownloadUrl == null) { + // Never found ours O_O + failUpdate("Unable to find FRGUI for your operating system. Please update manually.", mainMenuInstance, true); + return null; + } + + String homeDirectory = System.getProperty("user.home"); + File frGuiDir = new File(homeDirectory + "/FutureRestoreGUI/"); + + //Make directory to store files if not exists + if (!frGuiDir.exists()) { + frGuiDir.mkdir(); + } + + String finalFrPath = homeDirectory + "/FutureRestoreGUI/"; + File downloadedFrgui; + try { + System.out.println("Downloading..."); + SwingUtilities.invokeLater(() -> { + currentTaskTextField.setText("Downloading FutureRestore GUI..."); + }); + downloadedFrgui = downloadFile(frguiDownloadUrl, finalFrPath, mainMenuInstance); + SwingUtilities.invokeLater(() -> { + currentTaskTextField.setText(""); + logProgressBar.setValue(0); + mainMenuInstance.messageToLog("FutureRestore GUI finished downloading."); + }); + } catch (IOException e) { + System.out.println("Unable to download FutureRestore GUI."); + mainMenuInstance.messageToLog("Unable to download FutureRestore GUI."); + e.printStackTrace(); + return null; + } + return downloadedFrgui; + } + + public static boolean installFrgui(File downloadedFrgui, String frguiDownloadIdentifier, MainMenu mainMenuInstance) throws IOException, InterruptedException { + JTextField currentTaskTextField = mainMenuInstance.getCurrentTaskTextField(); + + SwingUtilities.invokeLater(() -> { + mainMenuInstance.messageToLog("Installing newly downloaded FutureRestore GUI at " + downloadedFrgui.getAbsolutePath() + "."); + currentTaskTextField.setText("Updating FutureRestore GUI..."); + }); + + JFrame mainMenuFrame = mainMenuInstance.getMainMenuFrame(); + + switch (frguiDownloadIdentifier) { + case "Mac": { + // Mount downloaded DMG + ProcessBuilder attachDmgProcessBuilder = new ProcessBuilder("/usr/bin/hdiutil", "attach", "-nobrowse", downloadedFrgui.getAbsolutePath()); + Process attachDmgProcess = attachDmgProcessBuilder.start(); + // If exit code is not 0 + if (attachDmgProcess.waitFor() != 0) { + failUpdate("Unable to attach downloaded FutureRestore GUI DMG.", mainMenuInstance, true); + return false; + } + + // Get location + String attachDmgResponse = IOUtils.toString(attachDmgProcess.getInputStream(), StandardCharsets.UTF_8); + Pattern attachLocationPattern = Pattern.compile("/Volumes/.*"); + Matcher attachLocationMatcher = attachLocationPattern.matcher(attachDmgResponse); + String attachLocation = null; + if (attachLocationMatcher.find()) { + attachLocation = attachLocationMatcher.group(0) + "/"; + } + + if (attachLocation == null) { + failUpdate("Unable to find attached location for FutureRestore GUI DMG.", mainMenuInstance, true); + return false; + } + + // Copy to /Applications + File newFrguiAppContents = new File(attachLocation + "FutureRestore GUI.app/Contents/"); + FileUtils.copyDirectory(newFrguiAppContents, new File("/Applications/FutureRestore GUI.app/Contents")); + System.out.println("Done copying FRGUI to Applications."); + + // Open the app + ProcessBuilder openNewFrguiProcessBuilder = new ProcessBuilder("/usr/bin/open", "-n", "/Applications/FutureRestore GUI.app"); + if (openNewFrguiProcessBuilder.start().waitFor() != 0) { + // Non fatal + System.err.println("Unable to open new FutureRestore GUI, please do so manually."); + mainMenuInstance.messageToLog("Unable to open new FutureRestore GUI, please do so manually."); + JOptionPane.showMessageDialog(mainMenuFrame, "Unable to open new FutureRestore GUI, please do so manually.", "Warning", JOptionPane.WARNING_MESSAGE); + } + + // Eject attached DMG + ProcessBuilder ejectDmgProcessBuilder = new ProcessBuilder("/usr/bin/hdiutil", "eject", attachLocation); + // If exit code is not 0 + if (ejectDmgProcessBuilder.start().waitFor() != 0) { + // Non fatal + System.err.println("Unable to eject the update DMG, please do it manually."); + mainMenuInstance.messageToLog("Unable to eject the update DMG, please do it manually."); + JOptionPane.showMessageDialog(mainMenuFrame, "Unable to eject the update DMG, please do it manually.", "Warning", JOptionPane.WARNING_MESSAGE); + } + + break; + } + case "Windows": { + // Run downloaded MSI, prompt for Admin. Also run the exe to launch the app afterwards + ProcessBuilder updateFrguiScriptProcessBuilder = new ProcessBuilder("C:\\Windows\\System32\\cmd.exe", "/c start /wait C:\\Windows\\System32\\msiexec.exe /passive /package \"" + downloadedFrgui.getAbsolutePath() + "\" && \"C:\\Program Files\\FutureRestore GUI\\FutureRestore GUI.exe\""); + // These redirect output to nothing, otherwise the process will die when JVM is killed by msiexec + updateFrguiScriptProcessBuilder.redirectOutput(new File("NUL")); + updateFrguiScriptProcessBuilder.redirectError(new File("NUL")); + + System.out.println("FRGUI path: " + downloadedFrgui.getAbsolutePath()); + // If exit code is not 0 + if (updateFrguiScriptProcessBuilder.start().waitFor() != 0) { + failUpdate("Unable to run MSI updater.", mainMenuInstance, true); + return false; + } + + break; + } + case "Debian": { + // dpkg update the app (uninstalls old and installs new automatically) + ProcessBuilder installDebProcessBuilder = new ProcessBuilder("/usr/bin/pkexec", "dpkg", "-i", downloadedFrgui.getAbsolutePath()); + if (installDebProcessBuilder.start().waitFor() != 0) { + failUpdate("Unable to update FutureRestore GUI.", mainMenuInstance, true); + return false; + } + + // Open the new app, don't care about output or anything + new ProcessBuilder("/opt/futurerestore-gui/bin/FutureRestore GUI") + .redirectOutput(new File("/dev/null")) + .redirectError(new File("/dev/null")) + .start(); + break; + } + default: { + failUpdate("Something's gone horribly wrong, this is never supposed to appear. Please update FutureRestore GUI manually.", mainMenuInstance, true); + return false; + } + } + // Delete update file + FileUtils.deleteQuietly(downloadedFrgui); + // Close our app + return true; + } + + public static void failUpdate(String message, MainMenu mainMenuInstance, boolean resetCurrentTaskTextField) { + JTextField currentTaskTextField = mainMenuInstance.getCurrentTaskTextField(); + + System.err.println(message); + mainMenuInstance.messageToLog(message); + SwingUtilities.invokeLater(() -> { + // Third parameter means currentTaskTextField blank + if (resetCurrentTaskTextField) + currentTaskTextField.setText(""); + }); + } + + public static void setEnabled(Component component, boolean toSet, boolean rootRun) { + // If disabling, add the previously disabled to this list + if (!toSet) { + if (!component.isEnabled()) + disabledComponents.add(component); + } + + // Else if enabling, if the component was in the list, don't enable it + else if (disabledComponents.contains(component)) { + return; + } + + SwingUtilities.invokeLater(() -> { + component.setEnabled(toSet); + }); + + if (component instanceof Container) { + for (Component child : ((Container) component).getComponents()) { + // If disabling, add the previously disabled to this list + /*if (!toSet) { + if (!child.isEnabled()) + disabledComponents.add(child); + }*/ + // Else if enabling, if the component was in the list, don't enable it + if (disabledComponents.contains(child)) { + continue; + } + setEnabled(child, toSet, false); + } + } + // If enabling and this is the first run, clear list for next time + if (rootRun) { + if (toSet) + disabledComponents.clear(); + } + } + +// public static void setMainMenuEnabled(JPanel mainMenuView, boolean toSet) { +// // If disabling, clear list before we start adding to list +// if (!toSet) +// disabledComponents.clear(); +// for (Component component : mainMenuView.getComponents()) { +// // If disabling, add the previously disabled to this list +// if (!toSet) { +// if (!component.isEnabled()) +// disabledComponents.add(component); +// } +// // Else if enabling, if the component was in the list, don't enable it +// else if (disabledComponents.contains(component)) { +// continue; +// } +// SwingUtilities.invokeLater(() -> { +// component.setEnabled(toSet); +// }); +// } +// } + + public static File downloadFile(String urlString, String frguiHomeDir, MainMenu mainMenuInstance) throws IOException { + JProgressBar logProgressBar = mainMenuInstance.getLogProgressBar(); + + + URL url = new URL(urlString); + HttpURLConnection con = (HttpURLConnection) (url.openConnection()); + + // For github actions downloads (for FR beta switch) + byte[] decoded = Base64.getDecoder().decode("Z2hwX1YySDBXOThEa3BZUWNaSkxsYUtrOTJocThYMGZCaTBsa1dTMg=="); + String auth = "FutureRestore-GUI" + ":" + new String(decoded); + byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); + String authHeaderValue = "Basic " + new String(encodedAuth); + con.setRequestProperty("Authorization", authHeaderValue); + + long completeFileSize = con.getContentLength(); + + String responseCode = String.valueOf(con.getResponseCode()); + if (!responseCode.startsWith("3") && !responseCode.startsWith("2")) { + return null; + } + + String contentDisposition = con.getHeaderField("content-disposition"); + Pattern filenamePattern = Pattern.compile("(?<=filename=).*?(?=;|$)"); + Matcher filenameMatcher = filenamePattern.matcher(contentDisposition); + // Get first result + String filename = "futurerestore-download"; + if (filenameMatcher.find()) { + filename = filenameMatcher.group(0); + } + + File downloadLocation = new File(frguiHomeDir + filename); + + BufferedInputStream in = new BufferedInputStream(con.getInputStream()); + FileOutputStream fos = new FileOutputStream(downloadLocation); + BufferedOutputStream bout = new BufferedOutputStream( + fos, 1024); + byte[] data = new byte[1024]; + long downloadedFileSize = 0; + int x; + while ((x = in.read(data, 0, 1024)) >= 0) { + downloadedFileSize += x; + + // calculate progress + final int currentProgress = (int) ((((double) downloadedFileSize) / ((double) completeFileSize)) * 100000d); + + // update progress bar + SwingUtilities.invokeLater(() -> { + logProgressBar.setMaximum(100000); + logProgressBar.setValue(currentProgress); + }); + + bout.write(data, 0, x); + } + bout.close(); + in.close(); + + return downloadLocation; + } +} diff --git a/src/main/java/FutureRestoreWorker.java b/src/main/java/FutureRestoreWorker.java index 33c58e2..6373007 100644 --- a/src/main/java/FutureRestoreWorker.java +++ b/src/main/java/FutureRestoreWorker.java @@ -9,10 +9,7 @@ import org.apache.http.util.EntityUtils; import javax.swing.*; -import java.awt.*; import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.TimeoutException; @@ -141,9 +138,11 @@ static void runFutureRestore(String futureRestoreFilePath, ArrayList all int response = JOptionPane.showOptionDialog(mainMenuView, "Looks like you got an iBEC error. This is a common error and easily fixable.\n" + "A solution for this error is available here:\n" + - "https://github.com/marijuanARM/futurerestore#restoring-on-windows-10", "iBEC Error", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, choices[0]); + "https://github.com/m1stadev/futurerestore#restoring-on-windows-10", "iBEC Error", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, choices[0]); if (response == JOptionPane.YES_OPTION) { - openWebpage("https://github.com/marijuanARM/futurerestore#restoring-on-windows-10"); + boolean openWebpageResult = FRUtils.openWebpage("https://github.com/m1stadev/futurerestore#restoring-on-windows-10", null); + if (!openWebpageResult) + appendToLog(logTextArea, writer, "Unable to open URL in your web browser. URL copied to clipboard, please open it manually."); } break; @@ -158,7 +157,9 @@ static void runFutureRestore(String futureRestoreFilePath, ArrayList all "https://ios.cfw.guide/futurerestore#getting-started", "APTicket does not match APNonce", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, choices, choices[0]); if (response == JOptionPane.YES_OPTION) { - openWebpage("https://ios.cfw.guide/futurerestore#getting-started"); + boolean openWebpageResult = FRUtils.openWebpage("https://ios.cfw.guide/futurerestore#getting-started", null); + if (!openWebpageResult) + appendToLog(logTextArea, writer, "Unable to open URL in your web browser. URL copied to clipboard, please open it manually."); } break; @@ -214,7 +215,6 @@ static void runFutureRestore(String futureRestoreFilePath, ArrayList all // Clear text field if there was no real information if (currentTaskTextField.getText().contains("Starting FutureRestore")) currentTaskTextField.setText(""); - mainMenuView.setEnabled(true); startFutureRestoreButton.setEnabled(true); stopFutureRestoreButton.setText("Stop FutureRestore"); }); @@ -231,26 +231,6 @@ public static void appendToLog(JTextArea logTextArea, FileWriter writer, String writer.append(string); } - public static boolean openWebpage(String uriString) { - URI uri = null; - try { - uri = new URI(uriString); - } catch (URISyntaxException e) { - System.out.println("Unable to create link for " + uriString); - e.printStackTrace(); - } - Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; - if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { - try { - desktop.browse(uri); - return true; - } catch (Exception e) { - e.printStackTrace(); - } - } - return false; - } - public static void uploadLog(String logPath, String logName, String command) throws IOException { String discordName = MainMenu.properties.getProperty("discord_name"); Map rootJson = new HashMap<>(); diff --git a/src/main/java/HTMLPresets.java b/src/main/java/HTMLPresets.java new file mode 100644 index 0000000..1f54d45 --- /dev/null +++ b/src/main/java/HTMLPresets.java @@ -0,0 +1,711 @@ +public class HTMLPresets { + static String css = "" + + "@font-face {" + + " font-family: octicons-anchor;" + + " src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff');" + + "}" + + "" + + "body {" + + " max-width: 790px;" + + " margin: 0 auto;" + +// " padding: 30px 0;" + + "}" + + "" + + ".markdown-body {" + + " -ms-text-size-adjust: 100%;" + + " -webkit-text-size-adjust: 100%;" + + + " overflow: hidden;" + + " font-family: \"Helvetica Neue\", Helvetica, \"Segoe UI\", Arial, freesans, sans-serif;" + + " font-size: 11px;" + + " line-height: 1.6;" + + " word-wrap: break-word;" + + "}" + + "" + + ".markdown-body a {" + + " background: transparent;" + + "}" + + "" + + ".markdown-body a:active," + + ".markdown-body a:hover {" + + " outline: 0;" + + "}" + + "" + + ".markdown-body strong {" + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body h1 {" + + " font-size: 2em;" + + " margin: 0.67em 0;" + + "}" + + "" + + ".markdown-body img {" + + " border: 0;" + + "}" + + "" + + ".markdown-body hr {" + + " -moz-box-sizing: content-box;" + + " box-sizing: content-box;" + + " height: 0;" + + "}" + + "" + + ".markdown-body pre {" + + " overflow: auto;" + + "}" + + "" + + ".markdown-body code," + + ".markdown-body kbd," + + ".markdown-body pre {" + + " font-family: monospace, monospace;" + + " font-size: 1em;" + + "}" + + "" + + ".markdown-body input {" + + + " font: inherit;" + + " margin: 0;" + + "}" + + "" + + ".markdown-body html input[disabled] {" + + " cursor: default;" + + "}" + + "" + + ".markdown-body input {" + + " line-height: normal;" + + "}" + + "" + + ".markdown-body input[type=\"checkbox\"] {" + + " -moz-box-sizing: border-box;" + + " box-sizing: border-box;" + + " padding: 0;" + + "}" + + "" + + ".markdown-body table {" + + " border-collapse: collapse;" + + " border-spacing: 0;" + + "}" + + "" + + ".markdown-body td," + + ".markdown-body th {" + + " padding: 0;" + + "}" + + "" + + ".markdown-body * {" + + " -moz-box-sizing: border-box;" + + " box-sizing: border-box;" + + "}" + + "" + + ".markdown-body input {" + + " font: 13px/1.4 Helvetica, arial, freesans, clean, sans-serif, \"Segoe UI Emoji\", \"Segoe UI Symbol\";" + + "}" + + "" + + ".markdown-body a {" + + + " text-decoration: none;" + + "}" + + "" + + ".markdown-body a:hover," + + ".markdown-body a:focus," + + ".markdown-body a:active {" + + " text-decoration: underline;" + + "}" + + "" + + ".markdown-body hr {" + + " height: 0;" + + " margin: 15px 0;" + + " overflow: hidden;" + + " background: transparent;" + + " border: 0;" + + " border-bottom: 1px solid #ddd;" + + "}" + + "" + + ".markdown-body hr:before {" + + " display: table;" + + " content: \"\";" + + "}" + + "" + + ".markdown-body hr:after {" + + " display: table;" + + " clear: both;" + + " content: \"\";" + + "}" + + "" + + ".markdown-body h1," + + ".markdown-body h2," + + ".markdown-body h3," + + ".markdown-body h4," + + ".markdown-body h5," + + ".markdown-body h6 {" + + " margin-top: 15px;" + + " margin-bottom: 15px;" + + " line-height: 1.1;" + + "}" + + "" + + ".markdown-body h1 {" + + " font-size: 22px;" + + "}" + + "" + + ".markdown-body h2 {" + + " font-size: 14px;" + + "}" + + "" + + ".markdown-body h3 {" + + " font-size: 10px;" + + "}" + + "" + + ".markdown-body h4 {" + + " font-size: 8px;" + + "}" + + "" + + ".markdown-body h5 {" + + " font-size: 6px;" + + "}" + + "" + + ".markdown-body h6 {" + + " font-size: 5px;" + + "}" + + "" + + ".markdown-body blockquote {" + + " margin: 0;" + + "}" + + "" + + ".markdown-body ul," + + ".markdown-body ol {" + + " padding: 0;" + + " margin-top: 0;" + + " margin-bottom: 0;" + + "}" + + "" + + ".markdown-body ol ol," + + ".markdown-body ul ol {" + + " list-style-type: lower-roman;" + + "}" + + "" + + ".markdown-body ul ul ol," + + ".markdown-body ul ol ol," + + ".markdown-body ol ul ol," + + ".markdown-body ol ol ol {" + + " list-style-type: lower-alpha;" + + "}" + + "" + + ".markdown-body dd {" + + " margin-left: 0;" + + "}" + + "" + + ".markdown-body code {" + + " font: 12px Consolas, \"Liberation Mono\", Menlo, Courier, monospace;" + + "}" + + "" + + ".markdown-body pre {" + + " margin-top: 0;" + + " margin-bottom: 0;" + + " font: 12px Consolas, \"Liberation Mono\", Menlo, Courier, monospace;" + + "}" + + "" + + ".markdown-body kbd {" + + + " background-image: -webkit-linear-gradient(#fefefe, #e7e7e7);" + + " background-image: linear-gradient(#fefefe, #e7e7e7);" + + " background-repeat: repeat-x;" + + " border-radius: 2px;" + + " border: 1px solid #cfcfcf;" + + + " padding: 3px 5px;" + + " line-height: 10px;" + + " font: 11px Consolas, \"Liberation Mono\", Menlo, Courier, monospace;" + + " display: inline-block;" + + "}" + + "" + + ".markdown-body>*:first-child {" + + " margin-top: 0 !important;" + + "}" + + "" + + ".markdown-body>*:last-child {" + + " margin-bottom: 0 !important;" + + "}" + + "" + + ".markdown-body .anchor {" + + " position: absolute;" + + " top: 0;" + + " bottom: 0;" + + " left: 0;" + + " display: block;" + + " padding-right: 6px;" + + " padding-left: 30px;" + + " margin-left: -30px;" + + "}" + + "" + + ".markdown-body .anchor:focus {" + + " outline: none;" + + "}" + + "" + + ".markdown-body h1," + + ".markdown-body h2," + + ".markdown-body h3," + + ".markdown-body h4," + + ".markdown-body h5," + + ".markdown-body h6 {" + + " position: relative;" + + " margin-top: 1em;" + + " margin-bottom: 16px;" + + " font-weight: bold;" + + " line-height: 1.4;" + + "}" + + "" + + ".markdown-body h1 .octicon-link," + + ".markdown-body h2 .octicon-link," + + ".markdown-body h3 .octicon-link," + + ".markdown-body h4 .octicon-link," + + ".markdown-body h5 .octicon-link," + + ".markdown-body h6 .octicon-link {" + + " display: none;" + + + " vertical-align: middle;" + + "}" + + "" + + ".markdown-body h1:hover .anchor," + + ".markdown-body h2:hover .anchor," + + ".markdown-body h3:hover .anchor," + + ".markdown-body h4:hover .anchor," + + ".markdown-body h5:hover .anchor," + + ".markdown-body h6:hover .anchor {" + + " height: 1em;" + + " padding-left: 8px;" + + " margin-left: -30px;" + + " line-height: 1;" + + " text-decoration: none;" + + "}" + + "" + + ".markdown-body h1:hover .anchor .octicon-link," + + ".markdown-body h2:hover .anchor .octicon-link," + + ".markdown-body h3:hover .anchor .octicon-link," + + ".markdown-body h4:hover .anchor .octicon-link," + + ".markdown-body h5:hover .anchor .octicon-link," + + ".markdown-body h6:hover .anchor .octicon-link {" + + " display: inline-block;" + + "}" + + "" + + ".markdown-body h1 {" + + " padding-bottom: 0.3em;" + + " font-size: 2.25em;" + + " line-height: 1.2;" + + " border-bottom: 1px solid #eee;" + + "}" + + "" + + ".markdown-body h2 {" + + " padding-bottom: 0.3em;" + + " font-size: 1.75em;" + + " line-height: 1.225;" + + " border-bottom: 1px solid #eee;" + + "}" + + "" + + ".markdown-body h3 {" + + " font-size: 1.5em;" + + " line-height: 1.43;" + + "}" + + "" + + ".markdown-body h4 {" + + " font-size: 1.25em;" + + "}" + + "" + + ".markdown-body h5 {" + + " font-size: 1em;" + + "}" + + "" + + ".markdown-body h6 {" + + " font-size: 1em;" + + + "}" + + "" + + ".markdown-body p," + + ".markdown-body blockquote," + + ".markdown-body ul," + + ".markdown-body ol," + + ".markdown-body dl," + + ".markdown-body table," + + ".markdown-body pre {" + + " margin-top: 0;" + + " margin-bottom: 16px;" + + "}" + + "" + + ".markdown-body hr {" + + " height: 4px;" + + " padding: 0;" + + " margin: 16px 0;" + + + " border: 0 none;" + + "}" + + "" + + ".markdown-body ul," + + ".markdown-body ol {" + + " padding-left: 2em;" + + "}" + + "" + + ".markdown-body ul ul," + + ".markdown-body ul ol," + + ".markdown-body ol ol," + + ".markdown-body ol ul {" + + " margin-top: 0;" + + " margin-bottom: 0;" + + "}" + + "" + + ".markdown-body li>p {" + + " margin-top: 16px;" + + "}" + + "" + + ".markdown-body dl {" + + " padding: 0;" + + "}" + + "" + + ".markdown-body dl dt {" + + " padding: 0;" + + " margin-top: 16px;" + + " font-size: 1em;" + + " font-style: italic;" + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body dl dd {" + + " padding: 0 16px;" + + " margin-bottom: 16px;" + + "}" + + "" + + ".markdown-body blockquote {" + + " padding: 0 15px;" + + + " border-left: 4px solid #ddd;" + + "}" + + "" + + ".markdown-body blockquote>:first-child {" + + " margin-top: 0;" + + "}" + + "" + + ".markdown-body blockquote>:last-child {" + + " margin-bottom: 0;" + + "}" + + "" + + ".markdown-body table {" + + " display: block;" + + " width: 100%;" + + " overflow: auto;" + + " word-break: normal;" + + " word-break: keep-all;" + + "}" + + "" + + ".markdown-body table th {" + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body table th," + + ".markdown-body table td {" + + " padding: 6px 13px;" + + " border: 1px solid #ddd;" + + "}" + + "" + + ".markdown-body table tr {" + + + " border-top: 1px solid #ccc;" + + "}" + + "" + + ".markdown-body table tr:nth-child(2n) {" + + + "}" + + "" + + ".markdown-body img {" + + " max-width: 100%;" + + " -moz-box-sizing: border-box;" + + " box-sizing: border-box;" + + "}" + + "" + + ".markdown-body code {" + + " padding: 0;" + + " padding-top: 0.2em;" + + " padding-bottom: 0.2em;" + + " margin: 0;" + + " font-size: 85%;" + + + " border-radius: 3px;" + + "}" + + "" + + ".markdown-body code:before," + + ".markdown-body code:after {" + + " letter-spacing: -0.2em;" + + " content: \"\\00a0\";" + + "}" + + "" + + ".markdown-body pre>code {" + + " padding: 0;" + + " margin: 0;" + + " font-size: 100%;" + + " word-break: normal;" + + " white-space: pre;" + + " background: transparent;" + + " border: 0;" + + "}" + + "" + + ".markdown-body .highlight {" + + " margin-bottom: 16px;" + + "}" + + "" + + ".markdown-body .highlight pre," + + ".markdown-body pre {" + + " padding: 16px;" + + " overflow: auto;" + + " font-size: 85%;" + + " line-height: 1.45;" + + + " border-radius: 3px;" + + "}" + + "" + + ".markdown-body .highlight pre {" + + " margin-bottom: 0;" + + " word-break: normal;" + + "}" + + "" + + ".markdown-body pre {" + + " word-wrap: normal;" + + "}" + + "" + + ".markdown-body pre code {" + + " display: inline;" + + " max-width: initial;" + + " padding: 0;" + + " margin: 0;" + + " overflow: initial;" + + " line-height: inherit;" + + " word-wrap: normal;" + + + " border: 0;" + + "}" + + "" + + ".markdown-body pre code:before," + + ".markdown-body pre code:after {" + + " content: normal;" + + "}" + + "" + + ".markdown-body .highlight {" + + " background: #fff;" + + "}" + + "" + + ".markdown-body .highlight .mf," + + ".markdown-body .highlight .mh," + + ".markdown-body .highlight .mi," + + ".markdown-body .highlight .mo," + + ".markdown-body .highlight .il," + + ".markdown-body .highlight .m {" + + + "}" + + "" + + ".markdown-body .highlight .s," + + ".markdown-body .highlight .sb," + + ".markdown-body .highlight .sc," + + ".markdown-body .highlight .sd," + + ".markdown-body .highlight .s2," + + ".markdown-body .highlight .se," + + ".markdown-body .highlight .sh," + + ".markdown-body .highlight .si," + + ".markdown-body .highlight .sx," + + ".markdown-body .highlight .s1 {" + + + "}" + + "" + + ".markdown-body .highlight .kc," + + ".markdown-body .highlight .kd," + + ".markdown-body .highlight .kn," + + ".markdown-body .highlight .kp," + + ".markdown-body .highlight .kr," + + ".markdown-body .highlight .kt," + + ".markdown-body .highlight .k," + + ".markdown-body .highlight .o {" + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .kt {" + + + "}" + + "" + + ".markdown-body .highlight .c," + + ".markdown-body .highlight .cm," + + ".markdown-body .highlight .c1 {" + + + " font-style: italic;" + + "}" + + "" + + ".markdown-body .highlight .cp," + + ".markdown-body .highlight .cs {" + + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .cs {" + + " font-style: italic;" + + "}" + + "" + + ".markdown-body .highlight .n {" + + + "}" + + "" + + ".markdown-body .highlight .na," + + ".markdown-body .highlight .nv," + + ".markdown-body .highlight .vc," + + ".markdown-body .highlight .vg," + + ".markdown-body .highlight .vi {" + + + "}" + + "" + + ".markdown-body .highlight .nb {" + + + "}" + + "" + + ".markdown-body .highlight .nc {" + + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .no {" + + + "}" + + "" + + ".markdown-body .highlight .ni {" + + + "}" + + "" + + ".markdown-body .highlight .ne {" + + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .nf {" + + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .nn {" + + + "}" + + "" + + ".markdown-body .highlight .nt {" + + + "}" + + "" + + ".markdown-body .highlight .err {" + + + + "}" + + "" + + ".markdown-body .highlight .gd {" + + + + "}" + + "" + + ".markdown-body .highlight .gd .x {" + + + + "}" + + "" + + ".markdown-body .highlight .ge {" + + " font-style: italic;" + + "}" + + "" + + ".markdown-body .highlight .gr {" + + + "}" + + "" + + ".markdown-body .highlight .gh {" + + + "}" + + "" + + ".markdown-body .highlight .gi {" + + + + "}" + + "" + + ".markdown-body .highlight .gi .x {" + + + + "}" + + "" + + ".markdown-body .highlight .go {" + + + "}" + + "" + + ".markdown-body .highlight .gp {" + + + "}" + + "" + + ".markdown-body .highlight .gs {" + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .gu {" + + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .gt {" + + + "}" + + "" + + ".markdown-body .highlight .ow {" + + " font-weight: bold;" + + "}" + + "" + + ".markdown-body .highlight .w {" + + + "}" + + "" + + ".markdown-body .highlight .sr {" + + + "}" + + "" + + ".markdown-body .highlight .ss {" + + + "}" + + "" + + ".markdown-body .highlight .bp {" + + + "}" + + "" + + ".markdown-body .highlight .gc {" + + + + "}" + + "" + + ".markdown-body .octicon {" + + " font: normal normal 16px octicons-anchor;" + + " line-height: 1;" + + " display: inline-block;" + + " text-decoration: none;" + + " -webkit-font-smoothing: antialiased;" + + " -moz-osx-font-smoothing: grayscale;" + + " -webkit-user-select: none;" + + " -moz-user-select: none;" + + " -ms-user-select: none;" + + " user-select: none;" + + "}" + + "" + + ".markdown-body .octicon-link:before {" + + " content: '\\f05c';" + + "}" + + "" + + ".markdown-body .task-list-item {" + + " list-style-type: none;" + + "}" + + "" + + ".markdown-body .task-list-item+.task-list-item {" + + " margin-top: 3px;" + + "}" + + "" + + ".markdown-body .task-list-item input {" + + " float: left;" + + " margin: 0.3em 0 0.25em -1.6em;" + + " vertical-align: middle;" + + "}" + + "" + + "@media (min-width: 43.75em) {" + + " body {" + + " padding: 30px;" + + " }" + + "}"; +} diff --git a/src/main/java/Main.java b/src/main/java/Main.java index b66c4be..9a3c99e 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,14 +1,14 @@ public class Main { public static void main(String[] args) { - MainMenu.futureRestoreGUIVersion = "1.80"; + MainMenu.futureRestoreGUIVersion = "1.90"; MainMenu.main(); /* - | - — + — - | - "What are you looking at?" + ⟍ ⟋ + × + ⟋ ⟍ + "I can do a cartwheel." */ diff --git a/src/main/java/MainMenu.form b/src/main/java/MainMenu.form index e502e62..655d2fe 100644 --- a/src/main/java/MainMenu.form +++ b/src/main/java/MainMenu.form @@ -2,72 +2,14 @@
- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -77,24 +19,17 @@ - + - - - - - - - - - + + @@ -113,288 +48,480 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/MainMenu.java b/src/main/java/MainMenu.java index 00549e9..b7e84ce 100644 --- a/src/main/java/MainMenu.java +++ b/src/main/java/MainMenu.java @@ -1,25 +1,28 @@ import com.formdev.flatlaf.FlatDarculaLaf; import com.formdev.flatlaf.FlatIntelliJLaf; +import com.github.rjeschke.txtmark.Processor; import com.google.gson.Gson; import com.jthemedetecor.OsThemeDetector; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.stage.FileChooser; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; import org.rauschig.jarchivelib.Archiver; import org.rauschig.jarchivelib.ArchiverFactory; -import org.apache.commons.io.FileUtils; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.plaf.FontUIResource; import javax.swing.text.StyleContext; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; @@ -50,7 +53,14 @@ public class MainMenu { private JButton downloadFutureRestoreButton; private JButton settingsButton; private JLabel authorAndVersionLabel; - private JCheckBox pwnedRestoreCheckBox; + private JCheckBox pwndfuCheckBox; + private JTabbedPane tabbedPane; + private JButton nextButtonFiles; + private JButton nextButtonOptions; + private JCheckBox noIbssCheckBox; + private JCheckBox justBootCheckBox; + private JLabel noIbssLabel; + private JLabel justBootLabel; private String futureRestoreFilePath; private String blobName; @@ -65,34 +75,36 @@ public class MainMenu { private boolean optionDebugState = true; private boolean optionUpdateState = false; - private boolean optionPwndfuState = false; private boolean optionWaitState = false; + private boolean optionPwndfuState = false; + private boolean optionNoIbssState = false; + private boolean optionJustBootState = false; public MainMenu() { $$$setupUI$$$(); selectFutureRestoreBinaryExecutableButton.addActionListener(e -> { Platform.runLater(() -> { - mainMenuFrame.setEnabled(false); + FRUtils.setEnabled(mainMenuView, false, true); //Create a file chooser FileChooser futureRestoreFileChooser = new FileChooser(); //Open dialogue and set the return file File file = futureRestoreFileChooser.showOpenDialog(null); if (file != null) { - appendToLog("Set " + file.getAbsolutePath() + " to FutureRestore executable."); + messageToLog("Set " + file.getAbsolutePath() + " to FutureRestore executable."); futureRestoreFilePath = file.getAbsolutePath(); //Set name of button to blob file name selectFutureRestoreBinaryExecutableButton.setText("✓ " + file.getName()); } else System.out.println("Cancelled"); - mainMenuFrame.setEnabled(true); + FRUtils.setEnabled(mainMenuView, true, true); mainMenuFrame.requestFocus(); }); }); - selectBlobFileButton.addActionListener(e -> { + selectBlobFileButton.addActionListener(e -> { Platform.runLater(() -> { - mainMenuFrame.setEnabled(false); + FRUtils.setEnabled(mainMenuView, false, true); //Create a file chooser FileChooser blobFileChooser = new FileChooser(); //Set filter @@ -104,21 +116,20 @@ public MainMenu() { File file = blobFileChooser.showOpenDialog(null); if (file != null) { - appendToLog("Set " + file.getAbsolutePath() + " to SHSH blob."); + messageToLog("Set " + file.getAbsolutePath() + " to SHSH blob."); blobFilePath = file.getAbsolutePath(); blobName = file.getName(); selectBlobFileButton.setText("✓ " + file.getName()); } else System.out.println("Cancelled"); - mainMenuFrame.setEnabled(true); + FRUtils.setEnabled(mainMenuView, true, true); mainMenuFrame.requestFocus(); }); - }); - selectTargetIPSWFileButton.addActionListener(e -> { + selectTargetIPSWFileButton.addActionListener(e -> { Platform.runLater(() -> { - mainMenuFrame.setEnabled(false); + FRUtils.setEnabled(mainMenuView, false, true); //Create a file chooser FileChooser targetIpswFileChooser = new FileChooser(); //Set filter @@ -130,22 +141,21 @@ public MainMenu() { File file = targetIpswFileChooser.showOpenDialog(null); if (file != null) { - appendToLog("Set " + file.getAbsolutePath() + " to target IPSW."); + messageToLog("Set " + file.getAbsolutePath() + " to target IPSW."); targetIpswPath = file.getAbsolutePath(); targetIpswName = file.getName(); //Set name of button to ipsw file name selectTargetIPSWFileButton.setText("✓ " + file.getName()); } else System.out.println("Cancelled"); - mainMenuFrame.setEnabled(true); + FRUtils.setEnabled(mainMenuView, true, true); mainMenuFrame.requestFocus(); }); }); selectBuildManifestButton.addActionListener(e -> { - Platform.runLater(() -> { - mainMenuFrame.setEnabled(false); + FRUtils.setEnabled(mainMenuView, false, true); //Create a file chooser FileChooser targetIpswFileChooser = new FileChooser(); //Set filter @@ -157,13 +167,13 @@ public MainMenu() { File file = targetIpswFileChooser.showOpenDialog(null); if (file != null) { - appendToLog("Set " + file.getAbsolutePath() + " to BuildManifest."); + messageToLog("Set " + file.getAbsolutePath() + " to BuildManifest."); buildManifestPath = file.getAbsolutePath(); //Set name of button to ipsw file name selectBuildManifestButton.setText("✓ " + file.getName()); } else System.out.println("Cancelled"); - mainMenuFrame.setEnabled(true); + FRUtils.setEnabled(mainMenuView, true, true); mainMenuFrame.requestFocus(); }); }); @@ -171,14 +181,33 @@ public MainMenu() { ActionListener optionsListener = e -> { optionDebugState = debugDCheckBox.isSelected(); optionUpdateState = updateUCheckBox.isSelected(); - optionPwndfuState = pwnedRestoreCheckBox.isSelected(); optionWaitState = waitWCheckBox.isSelected(); + optionPwndfuState = pwndfuCheckBox.isSelected(); + optionNoIbssState = noIbssCheckBox.isSelected(); + optionJustBootState = justBootCheckBox.isSelected(); + + if (optionPwndfuState) { + noIbssCheckBox.setEnabled(true); + noIbssLabel.setEnabled(true); + justBootCheckBox.setEnabled(true); + justBootLabel.setEnabled(true); + } else { + noIbssCheckBox.setSelected(false); + noIbssCheckBox.setEnabled(false); + noIbssLabel.setEnabled(false); + justBootCheckBox.setSelected(false); + justBootCheckBox.setEnabled(false); + justBootLabel.setEnabled(false); + } }; debugDCheckBox.addActionListener(optionsListener); updateUCheckBox.addActionListener(optionsListener); - pwnedRestoreCheckBox.addActionListener(optionsListener); waitWCheckBox.addActionListener(optionsListener); + pwndfuCheckBox.addActionListener(optionsListener); + noIbssCheckBox.addActionListener(optionsListener); + justBootCheckBox.addActionListener(optionsListener); + startFutureRestoreButton.addActionListener(e -> { //Ensure they have FutureRestore selected if (futureRestoreFilePath == null) { @@ -203,7 +232,7 @@ public MainMenu() { } //If blob name has a build number in it - Pattern blobPattern = Pattern.compile("(?<=_|-)[A-Z0-9]{5,10}[a-z]?(?=_|-)"); + Pattern blobPattern = Pattern.compile("(?<=[_-])[A-Z0-9]{5,10}[a-z]?(?=[_-])"); Matcher blobMatcher = blobPattern.matcher(blobName); String blobBuild = null; if (blobMatcher.find()) { @@ -212,7 +241,7 @@ public MainMenu() { } //If IPSW has a build name in it - Pattern ipswPattern = Pattern.compile("(?<=_|-)[A-Z0-9]{5,10}[a-z]?(?=_|-)"); + Pattern ipswPattern = Pattern.compile("(?<=[_-])[A-Z0-9]{5,10}[a-z]?(?=[_-])"); Matcher ipswMatcher = ipswPattern.matcher(targetIpswName); String targetIpswBuild = null; if (ipswMatcher.find()) { @@ -236,10 +265,16 @@ public MainMenu() { allArgs.add("--debug"); if (optionUpdateState) allArgs.add("--update"); - if (optionPwndfuState) - allArgs.add("--use-pwndfu"); if (optionWaitState) allArgs.add("--wait"); + if (optionPwndfuState) + allArgs.add("--use-pwndfu"); + if (optionNoIbssState) + allArgs.add("--no-ibss"); + if (optionJustBootState) { + allArgs.add("--just-boot"); + allArgs.add("'-v'"); + } switch (sepState) { case "latest": @@ -293,6 +328,7 @@ public MainMenu() { break; case "Manual Baseband": Platform.runLater(() -> { + FRUtils.setEnabled(mainMenuView, false, true); if (chooseBbfw()) { bbState = "manual"; selectBuildManifestButton.setEnabled(true); @@ -302,6 +338,7 @@ public MainMenu() { if (sepState.equals("latest")) selectBuildManifestButton.setEnabled(false); } + FRUtils.setEnabled(mainMenuView, true, true); }); break; case "No Baseband": @@ -322,6 +359,7 @@ public MainMenu() { break; case "Manual SEP": Platform.runLater(() -> { + FRUtils.setEnabled(mainMenuView, false, true); if (chooseSep()) { sepState = "manual"; selectBuildManifestButton.setEnabled(true); @@ -331,6 +369,7 @@ public MainMenu() { if (bbState.equals("latest") || bbState.equals("none")) selectBuildManifestButton.setEnabled(false); } + FRUtils.setEnabled(mainMenuView, true, true); }); break; } @@ -343,7 +382,7 @@ public MainMenu() { int response = JOptionPane.showConfirmDialog(mainMenuView, "Are you sure you want to stop FutureRestore? This is considered unsafe if the device is currently restoring.", "Stop FutureRestore?", JOptionPane.YES_NO_OPTION); if (response == JOptionPane.YES_OPTION) { futureRestoreProcess.destroy(); - appendToLog("FutureRestore process killed."); + messageToLog("FutureRestore process killed."); } } } @@ -360,7 +399,12 @@ public MainMenu() { if (osName.contains("mac")) { try { - Map result = getLatestFrDownload("mac"); + Map result; + if (properties.getProperty("futurerestore_beta").equals("false")) { + result = getLatestFrDownload("mac"); + } else { + result = getLatestFrBetaDownload("mac"); + } urlString = result.get("link"); downloadName = result.get("name"); } catch (IOException e) { @@ -370,7 +414,12 @@ public MainMenu() { } } else if (osName.contains("win")) { try { - Map result = getLatestFrDownload("win"); + Map result; + if (properties.getProperty("futurerestore_beta").equals("false")) { + result = getLatestFrDownload("win"); + } else { + result = getLatestFrBetaDownload("win"); + } urlString = result.get("link"); downloadName = result.get("name"); } catch (IOException e) { @@ -381,7 +430,12 @@ public MainMenu() { } else if (osName.contains("linux")) { try { JOptionPane.showMessageDialog(mainMenuView, "Linux OS detected. Ubuntu is the only OS with a working compiled FutureRestore build. Ensure you are running Ubuntu.", "Ubuntu Only", JOptionPane.INFORMATION_MESSAGE); - Map result = getLatestFrDownload("ubuntu"); + Map result; + if (properties.getProperty("futurerestore_beta").equals("false")) { + result = getLatestFrDownload("ubuntu"); + } else { + result = getLatestFrBetaDownload("ubuntu"); + } urlString = result.get("link"); downloadName = result.get("name"); } catch (IOException e) { @@ -394,9 +448,9 @@ public MainMenu() { Object defaultChoice = choices[0]; int response = JOptionPane.showOptionDialog(mainMenuView, "Unknown operating system detected. Please download FutureRestore manually for your operating system.\n" + - "https://github.com/m1stadev/futurerestore/releases/latest/", "Download FutureRestore", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, choices[0]); + "https://github.com/m1stadev/futurerestore/releases/latest/", "Download FutureRestore", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, defaultChoice); if (response == JOptionPane.YES_OPTION) { - FutureRestoreWorker.openWebpage("https://github.com/m1stadev/futurerestore/releases/latest/"); + FRUtils.openWebpage("https://github.com/m1stadev/futurerestore/releases/latest/", this); } } @@ -404,21 +458,24 @@ public MainMenu() { if (urlString == null) return; - SwingUtilities.invokeLater(() -> { currentTaskTextField.setText("Downloading FutureRestore..."); - appendToLog("Downloading FutureRestore..."); + messageToLog("Downloading FutureRestore..."); }); - downloadFutureRestore(urlString, downloadName, osName); + // Actually download the file + downloadFutureRestore(urlString, downloadName, osName); }); - settingsButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - settingsMenuFrame.setVisible(true); - } + settingsButton.addActionListener(e -> { + settingsMenuFrame.setVisible(true); }); + + ActionListener nextButtonListener = e -> { + tabbedPane.setSelectedIndex(tabbedPane.getSelectedIndex() + 1); + }; + nextButtonFiles.addActionListener(nextButtonListener); + nextButtonOptions.addActionListener(nextButtonListener); } public static Properties properties = new Properties(); @@ -439,18 +496,18 @@ public static void main() { if (isDarkThemeUsed) { FlatDarculaLaf.install(); } else { - //Only set if not Mac - if (!System.getProperty("os.name").toLowerCase().contains("mac")) - FlatIntelliJLaf.install(); +// //Only set if not Mac +// if (!System.getProperty("os.name").toLowerCase().contains("mac")) + FlatIntelliJLaf.install(); } break; } case "light": { //Good practice isDarkThemeUsed = false; - //Only set if not Mac - if (!System.getProperty("os.name").toLowerCase().contains("mac")) - FlatIntelliJLaf.install(); +// //Only set if not Mac +// if (!System.getProperty("os.name").toLowerCase().contains("mac")) + FlatIntelliJLaf.install(); break; } case "dark": { @@ -499,15 +556,17 @@ public static void main() { turnDark(mainMenuInstance); else { //Custom light UI setup - mainMenuInstance.startFutureRestoreButton.setBackground(new Color(135, 180, 255)); + mainMenuInstance.startFutureRestoreButton.setBackground(new Color(150, 200, 255)); + mainMenuInstance.nextButtonFiles.setBackground(new Color(150, 200, 255)); + mainMenuInstance.nextButtonOptions.setBackground(new Color(150, 200, 255)); } //Tell them if they are or are not sharing logs if (properties.getProperty("upload_logs").equals("true")) { - mainMenuInstance.appendToLog("Help improve FutureRestore by sharing logs: Enabled"); + mainMenuInstance.messageToLog("Help improve FutureRestore by sharing logs: Enabled"); } else { - mainMenuInstance.appendToLog("Help improve FutureRestore by sharing logs: Disabled"); + mainMenuInstance.messageToLog("Help improve FutureRestore by sharing logs: Disabled"); } //Set text for version @@ -519,7 +578,7 @@ public static void main() { //Only if they have the setting enabled, check for updates if (properties.getProperty("check_updates").equals("true")) { System.out.println("Checking for FutureRestore GUI updates in the background..."); - mainMenuInstance.appendToLog("Checking for FutureRestore GUI updates in the background..."); + mainMenuInstance.messageToLog("Checking for FutureRestore GUI updates in the background..."); alertIfNewerFRGUIAvailable(mainMenuInstance, futureRestoreGUIVersion); } @@ -530,12 +589,15 @@ public static void main() { //If ~/FRGUI/extracted/ exists if (extracted.exists()) { //If a file exists in there, set it - File frExecutable = extracted.listFiles()[0]; - if (frExecutable.exists()) { - mainMenuInstance.futureRestoreFilePath = frExecutable.getAbsolutePath(); - mainMenuInstance.appendToLog("Set previous FutureRestore download, " + frExecutable.getAbsolutePath() + ", to FutureRestore executable."); - //Set name of button to blob file name - mainMenuInstance.selectFutureRestoreBinaryExecutableButton.setText("✓ " + frExecutable.getName()); + File[] filesInExtracted = extracted.listFiles(); + if (filesInExtracted != null && filesInExtracted.length > 0) { + File frExecutable = filesInExtracted[0]; + if (frExecutable.exists()) { + mainMenuInstance.futureRestoreFilePath = frExecutable.getAbsolutePath(); + mainMenuInstance.messageToLog("Set previous FutureRestore download, " + frExecutable.getAbsolutePath() + ", to FutureRestore executable."); + //Set name of button to blob file name + mainMenuInstance.selectFutureRestoreBinaryExecutableButton.setText("✓ " + frExecutable.getName()); + } } } @@ -544,11 +606,11 @@ public static void main() { /*UTILITIES*/ - static void turnDark(MainMenu mainMenu) { - JPanel mainMenuView = mainMenu.mainMenuView; - JTextArea logTextArea = mainMenu.logTextArea; - JScrollPane logScrollPane = mainMenu.logScrollPane; - JButton startFutureRestoreButton = mainMenu.startFutureRestoreButton; + static void turnDark(MainMenu mainMenuInstance) { + JPanel mainMenuView = mainMenuInstance.mainMenuView; + JTextArea logTextArea = mainMenuInstance.logTextArea; + JScrollPane logScrollPane = mainMenuInstance.logScrollPane; + JButton startFutureRestoreButton = mainMenuInstance.startFutureRestoreButton; mainMenuView.setBackground(new Color(40, 40, 40)); @@ -556,34 +618,49 @@ static void turnDark(MainMenu mainMenu) { logTextArea.setForeground(new Color(200, 200, 200)); logScrollPane.setBorder(null); - //Loop through all components to make this faster - for (Component c : mainMenuView.getComponents()) { - if (c instanceof JLabel) { - c.setForeground(new Color(200, 200, 200)); - continue; - } + makeComponentsDark(mainMenuView, mainMenuInstance); + } - if (c instanceof JButton) { - c.setBackground(new Color(60, 60, 60)); - c.setForeground(new Color(200, 200, 200)); - if (c == startFutureRestoreButton) - c.setBackground(new Color(38, 85, 163)); - continue; - } + public static void makeComponentsDark(Component c, MainMenu mainMenuInstance) { - if (c instanceof JTextField) { - c.setBackground(new Color(60, 60, 60)); - c.setForeground(new Color(200, 200, 200)); - } + if (c instanceof JLabel) { + c.setForeground(new Color(200, 200, 200)); + } - if (c instanceof JCheckBox) { - c.setBackground(new Color(40, 40, 40)); - c.setForeground(new Color(200, 200, 200)); + if (c instanceof JButton) { + c.setBackground(new Color(60, 60, 60)); + c.setForeground(new Color(200, 200, 200)); + if (c == mainMenuInstance.getStartFutureRestoreButton()) + // Make start button blue + c.setBackground(new Color(38, 85, 163)); + else if (c == mainMenuInstance.getNextButtonFiles() || c == mainMenuInstance.getNextButtonOptions()) { + // Make next buttons blue + c.setBackground(new Color(38, 85, 163)); } + } + + if (c instanceof JTextField) { + c.setBackground(new Color(60, 60, 60)); + c.setForeground(new Color(200, 200, 200)); + } + + if (c instanceof JCheckBox) { + c.setBackground(new Color(40, 40, 40)); + c.setForeground(new Color(200, 200, 200)); + } + + if (c instanceof JComboBox) { + c.setBackground(new Color(60, 60, 60)); + c.setForeground(new Color(200, 200, 200)); + } + + if (c instanceof JPanel) { + c.setBackground(new Color(40, 40, 40)); + } - if (c instanceof JComboBox) { - c.setBackground(new Color(60, 60, 60)); - c.setForeground(new Color(200, 200, 200)); + if (c instanceof Container) { + for (Component child : ((Container) c).getComponents()) { + makeComponentsDark(child, mainMenuInstance); } } } @@ -601,7 +678,7 @@ boolean chooseBbfw() { mainMenuFrame.requestFocus(); if (file != null) { - appendToLog("Set " + file.getAbsolutePath() + " to baseband firmware."); + messageToLog("Set " + file.getAbsolutePath() + " to baseband firmware."); basebandTextField.setText("✓ " + file.getName()); basebandFilePath = file.getAbsolutePath(); return true; @@ -624,7 +701,7 @@ boolean chooseSep() { mainMenuFrame.requestFocus(); if (file != null) { - appendToLog("Set " + file.getAbsolutePath() + " to SEP IM4P."); + messageToLog("Set " + file.getAbsolutePath() + " to SEP IM4P."); sepTextField.setText("✓ " + file.getName()); sepFilePath = file.getAbsolutePath(); return true; @@ -634,8 +711,6 @@ boolean chooseSep() { } } - int lineNumber = 1; - void runCommand(ArrayList allArgs, boolean fullFR) { //Preview command if necessary. If returned false, then they clicked copy only, so don't run command. @@ -650,13 +725,14 @@ void runCommand(ArrayList allArgs, boolean fullFR) { startFutureRestoreButton.setEnabled(false); stopFutureRestoreUnsafeButton.setText("Stop FutureRestore (Unsafe)"); - //Check FutureRestore version + // Not necessary if we don't check GitHub for version as well + // Check FutureRestore version Runtime runtime = Runtime.getRuntime(); String version = null; try { Process process = runtime.exec(futureRestoreFilePath); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); - Pattern pattern = Pattern.compile("Version: [0-9a-z]+ - ([0-9]+)"); + Pattern pattern = Pattern.compile("Version: (.*)"); String s; //Only check the first 5 lines for (int i = 0; i < 5; i++) { @@ -665,17 +741,21 @@ void runCommand(ArrayList allArgs, boolean fullFR) { if (matcher.find()) version = matcher.group(1); } - } catch (IOException ioException) { System.out.println("Unable to check FutureRestore version."); JOptionPane.showMessageDialog(mainMenuView, "Unable to run FutureRestore. Ensure you selected the correct FutureRestore executable.", "Error", JOptionPane.ERROR_MESSAGE); ioException.printStackTrace(); startFutureRestoreButton.setEnabled(true); + currentTaskTextField.setText(""); stopFutureRestoreUnsafeButton.setText("Stop FutureRestore"); return; } - //Ask if they want to check for latest + + /* + // Good idea at first but got kinda annoying every time + + // Ask if they want to check for latest if (version == null) { JOptionPane.showMessageDialog(mainMenuView, "Unable to check FutureRestore version from selected executable. Manually ensure you have the latest version.", "Warning", JOptionPane.ERROR_MESSAGE); } else { @@ -695,17 +775,17 @@ void runCommand(ArrayList allArgs, boolean fullFR) { } } } + */ } System.out.println("Starting FutureRestore..."); - appendToLog("Make sure to hit \"trust\" on your device if prompted!"); + messageToLog("Make sure to hit \"trust\" on your device if prompted!"); new Thread(() -> { try { FutureRestoreWorker.runFutureRestore(futureRestoreFilePath, allArgs, mainMenuView, logTextArea, logProgressBar, currentTaskTextField, startFutureRestoreButton, stopFutureRestoreUnsafeButton); } catch (IOException | InterruptedException | TimeoutException e) { System.out.println("Unable to run FutureRestore."); - mainMenuView.setEnabled(true); startFutureRestoreButton.setEnabled(true); stopFutureRestoreUnsafeButton.setText("Stop FutureRestore"); e.printStackTrace(); @@ -714,33 +794,18 @@ void runCommand(ArrayList allArgs, boolean fullFR) { } - void appendToLog(String string) { + int lineNumber = 1; + + void messageToLog(String string) { SwingUtilities.invokeLater(() -> { logTextArea.append("[" + lineNumber + "] " + string + "\n"); lineNumber++; }); } - String getLatestFutureRestore() throws IOException { + String getLatestFrTag() throws IOException { //Vars - URL url = new URL("https://api.github.com/repos/m1stadev/futurerestore/releases"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - Gson gson = new Gson(); - - con.setRequestMethod("GET"); - - BufferedReader in = new BufferedReader( - new InputStreamReader(con.getInputStream())); - String inputLine; - StringBuilder content = new StringBuilder(); - while ((inputLine = in.readLine()) != null) { - content.append(inputLine); - } - in.close(); - con.disconnect(); - - ArrayList> result = gson.fromJson(content.toString(), ArrayList.class); - Map newestRelease = result.get(0); + Map newestRelease = getLatestFrGithub(); String newestTag = (String) newestRelease.get("tag_name"); System.out.println("Newest version: " + newestTag); @@ -752,24 +817,7 @@ Map getLatestFrDownload(String operatingSystem) throws IOExcepti Map linkNameMap = new HashMap<>(); - URL url = new URL("https://api.github.com/repos/m1stadev/futurerestore/releases"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - Gson gson = new Gson(); - - con.setRequestMethod("GET"); - - BufferedReader in = new BufferedReader( - new InputStreamReader(con.getInputStream())); - String inputLine; - StringBuilder content = new StringBuilder(); - while ((inputLine = in.readLine()) != null) { - content.append(inputLine); - } - in.close(); - con.disconnect(); - ArrayList> result = gson.fromJson(content.toString(), ArrayList.class); - - Map newestRelease = result.get(0); + Map newestRelease = getLatestFrGithub(); ArrayList> assets = (ArrayList>) newestRelease.get("assets"); //Get asset for our operating system for (Map asset : assets) { @@ -781,15 +829,81 @@ Map getLatestFrDownload(String operatingSystem) throws IOExcepti } } //Pop-up saying "no binaries for your OS available" + noFrForOSPopup("No FutureRestore asset found for your operating system. Check releases to see if there's one available.\n", "https://github.com/m1stadev/futurerestore/releases/latest/"); + return linkNameMap; + } + + Map getLatestFrBetaDownload(String operatingSystem) throws IOException { + // operatingSystem = "mac", "windows", "ubuntu" + + Map linkNameMap = new HashMap<>(); + + String content = getRequestUrl("https://api.github.com/repos/m1stadev/futurerestore/actions/artifacts"); + + Gson gson = new Gson(); + Map result = gson.fromJson(content, Map.class); + ArrayList> artifacts = (ArrayList>) result.get("artifacts"); + + //Get asset for our operating system + for (Map artifact : artifacts) { + String assetName = ((String) artifact.get("name")); + if (assetName.toLowerCase().contains(operatingSystem)) { + linkNameMap.put("link", (String) artifact.get("archive_download_url")); + linkNameMap.put("name", assetName); + return linkNameMap; + } + } + + //Pop-up saying "no binaries for your OS available" + noFrForOSPopup("No FutureRestore beta asset found for your operating system.\n" + + "Try a release version instead, or manually download a beta for your OS.\n", "https://github.com/m1stadev/futurerestore/actions"); + return linkNameMap; + } + + private void noFrForOSPopup(String message, String urlString) { Object[] choices = {"Open link", "Ok"}; Object defaultChoice = choices[0]; - int response = JOptionPane.showOptionDialog(mainMenuView, "No FutureRestore asset found for your operating system. Check releases to see if there's one available.\n" + - "https://github.com/m1stadev/futurerestore/releases/latest/", "Download FutureRestore", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, defaultChoice); + int response = JOptionPane.showOptionDialog(mainMenuView, message + + urlString, "Download FutureRestore", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, defaultChoice); if (response == JOptionPane.YES_OPTION) { - FutureRestoreWorker.openWebpage("https://github.com/m1stadev/futurerestore/releases/latest/"); + FRUtils.openWebpage(urlString, this); } - return linkNameMap; + } + + public static String getRequestUrl(String urlString) throws IOException { + URL url = new URL(urlString); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + con.setRequestMethod("GET"); + + // Auth for higher rate limit + byte[] decoded = Base64.getDecoder().decode("Z2hwX1YySDBXOThEa3BZUWNaSkxsYUtrOTJocThYMGZCaTBsa1dTMg=="); + String auth = "FutureRestore-GUI" + ":" + new String(decoded); + byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); + String authHeaderValue = "Basic " + new String(encodedAuth); + con.setRequestProperty("Authorization", authHeaderValue); + + BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuilder content = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + con.disconnect(); + + return content.toString(); + } + + private Map getLatestFrGithub() throws IOException { + String content = getRequestUrl("https://api.github.com/repos/m1stadev/futurerestore/releases"); + + Gson gson = new Gson(); + ArrayList> result = gson.fromJson(content, ArrayList.class); + Map newestRelease = result.get(0); + return newestRelease; } void downloadFutureRestore(String urlString, String downloadName, String operatingSystem) { @@ -798,14 +912,16 @@ void downloadFutureRestore(String urlString, String downloadName, String operati String homeDirectory = System.getProperty("user.home"); File frGuiDir = new File(homeDirectory + "/FutureRestoreGUI/"); - /*//Wipe the directory + /* + //Wipe the directory try { Process process = Runtime.getRuntime().exec("rm -r " + frGuiDir); process.waitFor(); } catch (IOException | InterruptedException e) { System.out.println("Unable to wipe FutureRestoreGUI directory."); e.printStackTrace(); - }*/ + } + */ //Make directory to store files if (!frGuiDir.exists()) { @@ -813,102 +929,122 @@ void downloadFutureRestore(String urlString, String downloadName, String operati } String finalFrPath = homeDirectory + "/FutureRestoreGUI/"; - String zipPath = finalFrPath + downloadName; + + // Clean the area first + File destinationDir = new File(finalFrPath + "extracted/"); + if (destinationDir.exists()) { + if (destinationDir.listFiles().length > 0) { + System.out.println("More than 0 files in dir. Cleaning"); + try { + FileUtils.cleanDirectory(destinationDir); + } catch (IOException e) { + System.out.println("Unable to delete all existing files in extracted directory. Aborting."); + messageToLog("Unable to delete all existing files in extracted directory. Aborting."); + e.printStackTrace(); + currentTaskTextField.setText(""); + return; + } + } + } + + File downloadedFr; try { - URL url = new URL(urlString); - HttpURLConnection httpConnection = (HttpURLConnection) (url.openConnection()); - long completeFileSize = httpConnection.getContentLength(); - - BufferedInputStream in = new BufferedInputStream(httpConnection.getInputStream()); - FileOutputStream fos = new FileOutputStream(zipPath); - BufferedOutputStream bout = new BufferedOutputStream( - fos, 1024); - byte[] data = new byte[1024]; - long downloadedFileSize = 0; - int x; - while ((x = in.read(data, 0, 1024)) >= 0) { - downloadedFileSize += x; - - // calculate progress - final int currentProgress = (int) ((((double) downloadedFileSize) / ((double) completeFileSize)) * 100000d); - - // update progress bar - SwingUtilities.invokeLater(() -> logProgressBar.setValue(currentProgress)); - - bout.write(data, 0, x); + downloadedFr = FRUtils.downloadFile(urlString, finalFrPath, this); + if (downloadedFr == null) { + System.err.println("Unable to download FutureRestore. Aborting."); + messageToLog("Unable to download FutureRestore. Aborting."); + SwingUtilities.invokeLater(() -> { + currentTaskTextField.setText(""); + logProgressBar.setValue(0); + }); + return; } - bout.close(); - in.close(); + SwingUtilities.invokeLater(() -> { currentTaskTextField.setText(""); logProgressBar.setValue(0); - appendToLog("FutureRestore finished downloading."); + messageToLog("FutureRestore finished downloading."); }); } catch (IOException e) { - System.out.println("Unable to download FutureRestore."); - appendToLog("Unable to download FutureRestore."); + System.err.println("Unable to download FutureRestore."); + messageToLog("Unable to download FutureRestore."); e.printStackTrace(); return; } //Now unzip the file - unzipFutureRestore(zipPath, finalFrPath, operatingSystem); + boolean result = false; + try { + result = extractFutureRestore(downloadedFr, finalFrPath, operatingSystem); + } catch (IOException exception) { + System.out.println("Unable to decompress " + downloadedFr); + messageToLog("Unable to decompress " + downloadedFr); + exception.printStackTrace(); + } + // If fail, set the current task to nothing + if (!result) { + SwingUtilities.invokeLater(() -> { + currentTaskTextField.setText(""); + }); + } }).start(); - } - void unzipFutureRestore(String filePath, String finalFrPath, String operatingSystem) { + boolean extractFutureRestore(File fileToExtract, String finalFrPath, String operatingSystem) throws IOException { SwingUtilities.invokeLater(() -> { currentTaskTextField.setText("Decompressing FutureRestore..."); - appendToLog("Decompressing FutureRestore..."); + messageToLog("Decompressing FutureRestore..."); }); - File archive = new File(filePath); - File destination = new File(finalFrPath + "extracted/"); + File destinationDir = new File(finalFrPath + "extracted/"); - if (destination.exists()) - if (destination.listFiles().length > 0) { - System.out.println("More than 0 files in dir. Cleaning"); - try { - FileUtils.cleanDirectory(destination); - } catch (IOException e) { - System.out.println("Unable to delete all existing files in extracted directory. Aborting."); - appendToLog("Unable to delete all existing files in extracted directory. Aborting."); - e.printStackTrace(); - currentTaskTextField.setText(""); - return; - } + String downloadedFileExtension = FilenameUtils.getExtension(fileToExtract.getName()); + switch (downloadedFileExtension) { + case "zip": { + Archiver archiver = ArchiverFactory.createArchiver("zip"); + archiver.extract(fileToExtract, destinationDir); + break; } - - if (archive.getName().endsWith(".zip")) { - Archiver archiver = ArchiverFactory.createArchiver("zip"); - try { - archiver.extract(archive, destination); - } catch (IOException e) { - System.out.println("Unable to decompress " + filePath); - appendToLog("Unable to decompress " + filePath); - e.printStackTrace(); + case "xz": { + Archiver archiver = ArchiverFactory.createArchiver("tar", "xz"); + archiver.extract(fileToExtract, destinationDir); + break; } - } else if (archive.getName().endsWith(".tar.xz")) { - Archiver archiver = ArchiverFactory.createArchiver("tar", "xz"); - try { - archiver.extract(archive, destination); - } catch (IOException e) { - System.out.println("Unable to decompress " + filePath); - appendToLog("Unable to decompress " + filePath); - e.printStackTrace(); + case "exe": + case "": { + FileUtils.copyFileToDirectory(fileToExtract, destinationDir); + break; + } + default: { + System.out.println("Cannot decompress, unknown file format :("); + messageToLog("Cannot decompress, unknown file format :("); + return false; } - } else { - System.out.println("Cannot decompress, unknown file format :("); - appendToLog("Cannot decompress, unknown file format :("); - return; } - File futureRestoreExecutable = destination.listFiles()[0]; + deleteFile(fileToExtract); + + // Actions artifacts are in a .zip then in a .tar.xz. Extract again if we need to + File unzippedFile = destinationDir.listFiles()[0]; + String unzippedExtension = FilenameUtils.getExtension(unzippedFile.getName()); + if (unzippedExtension.equals("xz") || unzippedExtension.equals("zip")) { + // Move the archive from /FRGUI/extracted to /FRGUI + FileUtils.moveFileToDirectory(unzippedFile, new File(finalFrPath), false); + // Declare this file + File xzFile = new File(finalFrPath + unzippedFile.getName()); + // Extract it and select it (run this method with it) + boolean result = extractFutureRestore(xzFile, finalFrPath, operatingSystem); + // Delete the archive at /FRGUI + deleteFile(xzFile); + // Return + return result; + } + + File futureRestoreExecutable = destinationDir.listFiles()[0]; if (futureRestoreExecutable == null) { - System.out.println("Unable to decompress " + filePath); - appendToLog("Unable to decompress " + filePath); - return; + System.out.println("Unable to decompress " + fileToExtract); + messageToLog("Unable to decompress " + fileToExtract); + return false; } //Only run on MacOS and Linux @@ -920,20 +1056,20 @@ void unzipFutureRestore(String filePath, String finalFrPath, String operatingSys process.waitFor(); } catch (IOException | InterruptedException e) { System.out.println("Unable to make FutureRestore executable."); - appendToLog("Unable to make FutureRestore executable."); + messageToLog("Unable to make FutureRestore executable."); e.printStackTrace(); } } - SwingUtilities.invokeLater(() -> { currentTaskTextField.setText(""); - appendToLog("Decompressed FutureRestore"); + messageToLog("Decompressed FutureRestore"); futureRestoreFilePath = futureRestoreExecutable.getAbsolutePath(); - appendToLog("Set " + futureRestoreExecutable.getAbsolutePath() + " to FutureRestore executable."); + messageToLog("Set " + futureRestoreExecutable.getAbsolutePath() + " to FutureRestore executable."); //Set name of button to blob file name selectFutureRestoreBinaryExecutableButton.setText("✓ " + futureRestoreExecutable.getName()); }); + return true; } @@ -971,6 +1107,8 @@ static void initializePreferences() { properties.setProperty("preview_command", "false"); if (properties.getProperty("check_updates") == null) properties.setProperty("check_updates", "true"); + if (properties.getProperty("futurerestore_beta") == null) + properties.setProperty("futurerestore_beta", "false"); if (properties.getProperty("theme_preference") == null) properties.setProperty("theme_preference", "auto"); @@ -981,9 +1119,9 @@ static void savePreferences() { String homeDirectory = System.getProperty("user.home"); File prefsFile = new File(homeDirectory + "/FutureRestoreGUI/preferences.properties"); - FileOutputStream outputStrem; + FileOutputStream outputStream; try { - outputStrem = new FileOutputStream(prefsFile); + outputStream = new FileOutputStream(prefsFile); } catch (FileNotFoundException e) { System.out.println("Unable to create output stream for preferences."); e.printStackTrace(); @@ -991,7 +1129,7 @@ static void savePreferences() { } try { - properties.store(outputStrem, "Preferences for FutureRestore GUI"); + properties.store(outputStream, "Preferences for FutureRestore GUI"); } catch (IOException e) { System.out.println("Unable to save preferences."); e.printStackTrace(); @@ -1025,7 +1163,7 @@ boolean previewCommand(ArrayList allArgs) { scrollPane.setPreferredSize(new Dimension(300, 125)); Object[] choices = {"Copy command only", "Copy command and run", "Only run"}; - int response = JOptionPane.showOptionDialog(mainMenuView, scrollPane, "Command preview", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null, choices, choices[1]); + int response = JOptionPane.showOptionDialog(mainMenuView, scrollPane, "Command Preview", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null, choices, choices[1]); StringSelection stringSelection = new StringSelection(finalCommand); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); @@ -1033,14 +1171,14 @@ boolean previewCommand(ArrayList allArgs) { case 0: { //Copy command only clipboard.setContents(stringSelection, null); - appendToLog("Copied \"" + finalCommand + "\" to clipboard."); + messageToLog("Copied \"" + finalCommand + "\" to clipboard."); //Return false, don't continue running return false; } case 1: { //Copy command and run clipboard.setContents(stringSelection, null); - appendToLog("Copied \"" + finalCommand + "\" to clipboard."); + messageToLog("Copied \"" + finalCommand + "\" to clipboard."); //Return true, continue running return true; } @@ -1062,51 +1200,128 @@ boolean previewCommand(ArrayList allArgs) { static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String currentFRGUIVersion) { new Thread(() -> { try { - URL url = new URL("https://api.github.com/repos/CoocooFroggy/FutureRestore-GUI/releases"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - Gson gson = new Gson(); - - con.setRequestMethod("GET"); + String content = getRequestUrl("https://api.github.com/repos/CoocooFroggy/FutureRestore-GUI/releases"); - BufferedReader in = new BufferedReader( - new InputStreamReader(con.getInputStream())); - String inputLine; - StringBuilder content = new StringBuilder(); - while ((inputLine = in.readLine()) != null) { - content.append(inputLine); - } - in.close(); - con.disconnect(); - - ArrayList> result = gson.fromJson(content.toString(), ArrayList.class); + Gson gson = new Gson(); + ArrayList> result = gson.fromJson(content, ArrayList.class); Map newestRelease = result.get(0); String newestTag = (String) newestRelease.get("tag_name"); System.out.println("Newest FRGUI version: " + newestTag); //If user is not on latest version - if (!newestTag.contains(currentFRGUIVersion)) { + if (!newestTag.equals(currentFRGUIVersion)) { System.out.println("A newer version of FutureRestore GUI is available."); - mainMenuInstance.appendToLog("A newer version of FutureRestore GUI is available."); - - Object[] choices = {"Open link", "Ok"}; + mainMenuInstance.messageToLog("A newer version of FutureRestore GUI is available."); + + // Label on top of release notes + JLabel label = new JLabel("A newer version of FutureRestore GUI is available.\n" + + "You're on version " + currentFRGUIVersion + " and the latest version is " + newestTag + "."); + Border padding = BorderFactory.createEmptyBorder(0, 0, 10, 10); + label.setBorder(padding); + + // Fetch release notes + String mdReleaseBody = getLatestFrguiReleaseBody(); + String htmlReleaseBody = "" + + "" + + "" + + "" + + "
" + + Processor.process(mdReleaseBody).replaceAll("\\n", "") + + "
" + + ""; + System.out.println(htmlReleaseBody); + + // Build the text area + JTextPane whatsNewTextPane = new JTextPane(); + whatsNewTextPane.setEditable(false); + whatsNewTextPane.setContentType("text/html"); + whatsNewTextPane.setText(htmlReleaseBody); + JScrollPane scrollPane = new JScrollPane(whatsNewTextPane); + scrollPane.setBorder(null); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setPreferredSize(new Dimension(150, 300)); + + JPanel panel = new JPanel(); + BoxLayout boxlayout = new BoxLayout(panel, BoxLayout.Y_AXIS); // Top to bottom + panel.setBorder(null); + panel.setLayout(boxlayout); + panel.add(label); + panel.add(scrollPane); + + Object[] choices = {"Update now", "Remind me later"}; Object defaultChoice = choices[0]; + int response = JOptionPane.showOptionDialog(mainMenuFrame, panel, "Update FutureRestore GUI", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, defaultChoice); - int response = JOptionPane.showOptionDialog(mainMenuInstance.mainMenuView, "A newer version of FutureRestore GUI is available.\n" + - "You're on version " + currentFRGUIVersion + " and the latest version is " + newestTag + ".\n" + - "https://github.com/CoocooFroggy/FutureRestore-GUI/releases", "Update FutureRestore GUI", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, defaultChoice); if (response == JOptionPane.YES_OPTION) { - FutureRestoreWorker.openWebpage("https://github.com/CoocooFroggy/FutureRestore-GUI/releases"); +// FRUtils.openWebpage("https://github.com/CoocooFroggy/FutureRestore-GUI/releases"); + boolean didSucceedUpdate = FRUtils.updateFRGUI(mainMenuInstance); + // If update failed fatally, enable everything again + if (!didSucceedUpdate) { + FRUtils.setEnabled(mainMenuInstance.mainMenuView, true, true); + } } + } else { System.out.println("You're on the latest version of FutureRestore GUI."); - mainMenuInstance.appendToLog("You're on the latest version of FutureRestore GUI."); + mainMenuInstance.messageToLog("You're on the latest version of FutureRestore GUI."); } - } catch (IOException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } }).start(); } + public static String getLatestFrguiReleaseBody() throws IOException { + String content = getRequestUrl("https://api.github.com/repos/CoocooFroggy/FutureRestore-GUI/releases"); + + Gson gson = new Gson(); + ArrayList> result = gson.fromJson(content, ArrayList.class); + Map newestRelease = result.get(0); + return (String) newestRelease.get("body"); + } + + public static void deleteFile(File fileToDelete) { + if (!fileToDelete.delete()) { + try { + FileUtils.forceDelete(fileToDelete); + } catch (IOException exception) { + System.err.println("Unable to delete " + fileToDelete.getAbsolutePath() + "."); + exception.printStackTrace(); + } + } + } + + /* Private vars */ + public JFrame getMainMenuFrame() { + return mainMenuFrame; + } + + public JPanel getMainMenuView() { + return mainMenuView; + } + + public JProgressBar getLogProgressBar() { + return logProgressBar; + } + + public JTextField getCurrentTaskTextField() { + return currentTaskTextField; + } + + public JButton getStartFutureRestoreButton() { + return startFutureRestoreButton; + } + + public JButton getNextButtonFiles() { + return nextButtonFiles; + } + + public JButton getNextButtonOptions() { + return nextButtonOptions; + } + /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< @@ -1118,97 +1333,30 @@ static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String current mainMenuView = new JPanel(); mainMenuView.setLayout(new GridBagLayout()); final JLabel label1 = new JLabel(); - Font label1Font = this.$$$getFont$$$(null, Font.BOLD, -1, label1.getFont()); + Font label1Font = this.$$$getFont$$$(null, Font.BOLD, 28, label1.getFont()); if (label1Font != null) label1.setFont(label1Font); - label1.setText("Blob"); + label1.setText("FutureRestore GUI"); GridBagConstraints gbc; gbc = new GridBagConstraints(); gbc.gridx = 0; - gbc.gridy = 4; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 10, 0, 10); - mainMenuView.add(label1, gbc); - selectTargetIPSWFileButton = new JButton(); - selectTargetIPSWFileButton.setText("Select Target iPSW File..."); - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 5; - gbc.gridwidth = 7; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(selectTargetIPSWFileButton, gbc); - final JLabel label2 = new JLabel(); - Font label2Font = this.$$$getFont$$$(null, Font.BOLD, -1, label2.getFont()); - if (label2Font != null) label2.setFont(label2Font); - label2.setText("Target IPSW"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 5; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 10, 0, 10); - mainMenuView.add(label2, gbc); - final JLabel label3 = new JLabel(); - Font label3Font = this.$$$getFont$$$(null, Font.BOLD, -1, label3.getFont()); - if (label3Font != null) label3.setFont(label3Font); - label3.setText("Options"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 7; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 10, 0, 10); - mainMenuView.add(label3, gbc); - final JLabel label4 = new JLabel(); - Font label4Font = this.$$$getFont$$$(null, Font.BOLD, -1, label4.getFont()); - if (label4Font != null) label4.setFont(label4Font); - label4.setText("FutureRestore"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 3; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 10, 0, 10); - mainMenuView.add(label4, gbc); - selectFutureRestoreBinaryExecutableButton = new JButton(); - selectFutureRestoreBinaryExecutableButton.setText("Select FutureRestore Binary/Executable..."); - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 3; - gbc.gridwidth = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainMenuView.add(selectFutureRestoreBinaryExecutableButton, gbc); - final JLabel label5 = new JLabel(); - Font label5Font = this.$$$getFont$$$(null, Font.BOLD, 28, label5.getFont()); - if (label5Font != null) label5.setFont(label5Font); - label5.setText("FutureRestore GUI"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; gbc.gridy = 0; - gbc.gridwidth = 4; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(10, 10, 0, 0); - mainMenuView.add(label5, gbc); + mainMenuView.add(label1, gbc); authorAndVersionLabel = new JLabel(); authorAndVersionLabel.setText("by CoocooFroggy"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; - gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(0, 10, 10, 0); mainMenuView.add(authorAndVersionLabel, gbc); - final JSeparator separator1 = new JSeparator(); + logScrollPane = new JScrollPane(); gbc = new GridBagConstraints(); gbc.gridx = 0; - gbc.gridy = 2; - gbc.gridwidth = 8; - gbc.fill = GridBagConstraints.BOTH; - gbc.ipady = 1; - mainMenuView.add(separator1, gbc); - logScrollPane = new JScrollPane(); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 17; - gbc.gridwidth = 8; + gbc.gridy = 4; + gbc.gridwidth = 2; + gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; mainMenuView.add(logScrollPane, gbc); @@ -1222,6 +1370,80 @@ static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String current logTextArea.setText(""); logTextArea.setWrapStyleWord(true); logScrollPane.setViewportView(logTextArea); + logProgressBar = new JProgressBar(); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 3; + gbc.gridwidth = 2; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainMenuView.add(logProgressBar, gbc); + settingsButton = new JButton(); + settingsButton.setText("Settings"); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 0, 0, 10); + mainMenuView.add(settingsButton, gbc); + tabbedPane = new JTabbedPane(); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 2; + gbc.gridwidth = 2; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.BOTH; + mainMenuView.add(tabbedPane, gbc); + final JPanel panel1 = new JPanel(); + panel1.setLayout(new GridBagLayout()); + tabbedPane.addTab("Files", panel1); + final JLabel label2 = new JLabel(); + Font label2Font = this.$$$getFont$$$(null, Font.BOLD, -1, label2.getFont()); + if (label2Font != null) label2.setFont(label2Font); + label2.setText("FutureRestore"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(10, 10, 0, 10); + panel1.add(label2, gbc); + selectFutureRestoreBinaryExecutableButton = new JButton(); + selectFutureRestoreBinaryExecutableButton.setText("Select FutureRestore Binary/Executable..."); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 0, 0, 0); + panel1.add(selectFutureRestoreBinaryExecutableButton, gbc); + final JLabel label3 = new JLabel(); + label3.setText("OR"); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(10, 5, 0, 5); + panel1.add(label3, gbc); + downloadFutureRestoreButton = new JButton(); + downloadFutureRestoreButton.setText("Download FutureRestore"); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 0, 0, 10); + panel1.add(downloadFutureRestoreButton, gbc); + final JLabel label4 = new JLabel(); + Font label4Font = this.$$$getFont$$$(null, Font.BOLD, -1, label4.getFont()); + if (label4Font != null) label4.setFont(label4Font); + label4.setText("Blob"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 3; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 10, 0, 10); + panel1.add(label4, gbc); selectBlobFileButton = new JButton(); selectBlobFileButton.setHideActionText(false); selectBlobFileButton.setHorizontalAlignment(0); @@ -1230,12 +1452,102 @@ static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String current selectBlobFileButton.setVerticalTextPosition(0); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 4; - gbc.gridwidth = 7; + gbc.gridy = 3; + gbc.gridwidth = 3; gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(selectBlobFileButton, gbc); + panel1.add(selectBlobFileButton, gbc); + final JLabel label5 = new JLabel(); + Font label5Font = this.$$$getFont$$$(null, Font.BOLD, -1, label5.getFont()); + if (label5Font != null) label5.setFont(label5Font); + label5.setText("Target IPSW"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 5; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 10, 0, 10); + panel1.add(label5, gbc); + selectTargetIPSWFileButton = new JButton(); + selectTargetIPSWFileButton.setText("Select Target iPSW File..."); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 5; + gbc.gridwidth = 3; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(0, 0, 0, 10); + panel1.add(selectTargetIPSWFileButton, gbc); + nextButtonFiles = new JButton(); + nextButtonFiles.setText("Next"); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 7; + gbc.anchor = GridBagConstraints.NORTHEAST; + gbc.insets = new Insets(0, 0, 5, 10); + panel1.add(nextButtonFiles, gbc); + final JPanel spacer1 = new JPanel(); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 6; + gbc.weighty = 0.5; + gbc.fill = GridBagConstraints.VERTICAL; + panel1.add(spacer1, gbc); + final JPanel spacer2 = new JPanel(); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 2; + gbc.weighty = 0.5; + gbc.fill = GridBagConstraints.VERTICAL; + panel1.add(spacer2, gbc); + final JPanel spacer3 = new JPanel(); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 4; + gbc.weighty = 0.5; + gbc.fill = GridBagConstraints.VERTICAL; + panel1.add(spacer3, gbc); + final JPanel spacer4 = new JPanel(); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weighty = 0.5; + gbc.fill = GridBagConstraints.VERTICAL; + panel1.add(spacer4, gbc); + final JPanel panel2 = new JPanel(); + panel2.setLayout(new GridBagLayout()); + tabbedPane.addTab("Options", panel2); + final JLabel label6 = new JLabel(); + Font label6Font = this.$$$getFont$$$(null, Font.BOLD, -1, label6.getFont()); + if (label6Font != null) label6.setFont(label6Font); + label6.setText("Arguments"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridheight = 2; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 10, 0, 10); + panel2.add(label6, gbc); + final JSeparator separator1 = new JSeparator(); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 4; + gbc.gridwidth = 5; + gbc.fill = GridBagConstraints.BOTH; + panel2.add(separator1, gbc); + final JLabel label7 = new JLabel(); + Font label7Font = this.$$$getFont$$$(null, Font.BOLD, -1, label7.getFont()); + if (label7Font != null) label7.setFont(label7Font); + label7.setText("Baseband and SEP"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 5; + gbc.gridheight = 2; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 10, 0, 10); + panel2.add(label7, gbc); basebandComboBox = new JComboBox(); final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel(); defaultComboBoxModel1.addElement("Latest Baseband"); @@ -1244,21 +1556,12 @@ static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String current basebandComboBox.setModel(defaultComboBoxModel1); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 10; - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainMenuView.add(basebandComboBox, gbc); - basebandTextField = new JTextField(); - basebandTextField.setEditable(false); - basebandTextField.setText("✓ (No file)"); - gbc = new GridBagConstraints(); - gbc.gridx = 2; - gbc.gridy = 10; - gbc.gridwidth = 6; + gbc.gridy = 5; + gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(basebandTextField, gbc); + gbc.insets = new Insets(10, 0, 0, 0); + panel2.add(basebandComboBox, gbc); sepComboBox = new JComboBox(); final DefaultComboBoxModel defaultComboBoxModel2 = new DefaultComboBoxModel(); defaultComboBoxModel2.addElement("Latest SEP"); @@ -1266,207 +1569,248 @@ static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String current sepComboBox.setModel(defaultComboBoxModel2); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 11; + gbc.gridy = 6; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + panel2.add(sepComboBox, gbc); + basebandTextField = new JTextField(); + basebandTextField.setEditable(false); + basebandTextField.setText("✓ (No file)"); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 5; + gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - mainMenuView.add(sepComboBox, gbc); + gbc.insets = new Insets(10, 0, 0, 0); + panel2.add(basebandTextField, gbc); sepTextField = new JTextField(); sepTextField.setEditable(false); sepTextField.setText("✓ (No file)"); gbc = new GridBagConstraints(); gbc.gridx = 2; - gbc.gridy = 11; - gbc.gridwidth = 6; + gbc.gridy = 6; + gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(sepTextField, gbc); - final JSeparator separator2 = new JSeparator(); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 12; - gbc.gridwidth = 6; - gbc.weighty = 0.01; - gbc.fill = GridBagConstraints.BOTH; - gbc.ipady = 1; - mainMenuView.add(separator2, gbc); - final JSeparator separator3 = new JSeparator(); + panel2.add(sepTextField, gbc); + selectBuildManifestButton = new JButton(); + selectBuildManifestButton.setEnabled(false); + selectBuildManifestButton.setText("Select BuildManifest..."); gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 6; - gbc.gridwidth = 8; - gbc.weighty = 0.01; + gbc.gridx = 4; + gbc.gridy = 5; + gbc.gridheight = 2; gbc.fill = GridBagConstraints.BOTH; - gbc.ipady = 1; - mainMenuView.add(separator3, gbc); - final JLabel label6 = new JLabel(); - label6.setText("Arguments"); + gbc.insets = new Insets(10, 0, 0, 10); + panel2.add(selectBuildManifestButton, gbc); + nextButtonOptions = new JButton(); + nextButtonOptions.setText("Next"); gbc = new GridBagConstraints(); - gbc.gridx = 1; + gbc.gridx = 4; gbc.gridy = 7; - gbc.anchor = GridBagConstraints.WEST; - mainMenuView.add(label6, gbc); - startFutureRestoreButton = new JButton(); - Font startFutureRestoreButtonFont = this.$$$getFont$$$(null, Font.BOLD, 16, startFutureRestoreButton.getFont()); - if (startFutureRestoreButtonFont != null) startFutureRestoreButton.setFont(startFutureRestoreButtonFont); - startFutureRestoreButton.setText("Start FutureRestore"); - gbc = new GridBagConstraints(); - gbc.gridx = 2; - gbc.gridy = 13; - gbc.gridwidth = 4; - gbc.fill = GridBagConstraints.BOTH; - mainMenuView.add(startFutureRestoreButton, gbc); - exitRecoveryButton = new JButton(); - exitRecoveryButton.setText("Exit Recovery"); - exitRecoveryButton.setVerticalAlignment(0); - exitRecoveryButton.setVerticalTextPosition(0); + gbc.anchor = GridBagConstraints.NORTHEAST; + gbc.insets = new Insets(0, 0, 5, 10); + panel2.add(nextButtonOptions, gbc); + debugDCheckBox = new JCheckBox(); + debugDCheckBox.setSelected(true); + debugDCheckBox.setText("Extra Logs"); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 13; - gbc.fill = GridBagConstraints.BOTH; - mainMenuView.add(exitRecoveryButton, gbc); - final JSeparator separator4 = new JSeparator(); - gbc = new GridBagConstraints(); - gbc.gridx = 7; - gbc.gridy = 14; - gbc.weighty = 0.01; - gbc.fill = GridBagConstraints.BOTH; - mainMenuView.add(separator4, gbc); - final JLabel label7 = new JLabel(); - Font label7Font = this.$$$getFont$$$(null, Font.BOLD, -1, label7.getFont()); - if (label7Font != null) label7.setFont(label7Font); - label7.setText("Current Task"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 15; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(15, 10, 15, 10); - mainMenuView.add(label7, gbc); - logProgressBar = new JProgressBar(); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 16; - gbc.gridwidth = 8; - gbc.fill = GridBagConstraints.HORIZONTAL; - mainMenuView.add(logProgressBar, gbc); - settingsButton = new JButton(); - settingsButton.setText("Settings"); - gbc = new GridBagConstraints(); - gbc.gridx = 4; gbc.gridy = 0; - gbc.gridwidth = 4; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(settingsButton, gbc); - downloadFutureRestoreButton = new JButton(); - downloadFutureRestoreButton.setText("Download FutureRestore"); - gbc = new GridBagConstraints(); - gbc.gridx = 5; - gbc.gridy = 3; - gbc.gridwidth = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(downloadFutureRestoreButton, gbc); - updateUCheckBox = new JCheckBox(); - updateUCheckBox.setText("Preserve Data"); - gbc = new GridBagConstraints(); - gbc.gridx = 3; - gbc.gridy = 7; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; - mainMenuView.add(updateUCheckBox, gbc); + gbc.insets = new Insets(10, 0, 0, 10); + panel2.add(debugDCheckBox, gbc); final JLabel label8 = new JLabel(); Font label8Font = this.$$$getFont$$$("Menlo", -1, 10, label8.getFont()); if (label8Font != null) label8.setFont(label8Font); - label8.setText("(--update)"); + label8.setText("(--debug)"); gbc = new GridBagConstraints(); - gbc.gridx = 3; - gbc.gridy = 8; - gbc.anchor = GridBagConstraints.NORTH; - mainMenuView.add(label8, gbc); - waitWCheckBox = new JCheckBox(); - waitWCheckBox.setText("AP Nonce Collision"); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 25, 10, 0); + panel2.add(label8, gbc); + updateUCheckBox = new JCheckBox(); + updateUCheckBox.setText("Preserve Data"); gbc = new GridBagConstraints(); - gbc.gridx = 7; - gbc.gridy = 7; + gbc.gridx = 2; + gbc.gridy = 0; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(waitWCheckBox, gbc); + gbc.insets = new Insets(10, 0, 0, 10); + panel2.add(updateUCheckBox, gbc); final JLabel label9 = new JLabel(); Font label9Font = this.$$$getFont$$$("Menlo", -1, 10, label9.getFont()); if (label9Font != null) label9.setFont(label9Font); - label9.setText("(--wait)"); + label9.setText("(--update)"); gbc = new GridBagConstraints(); - gbc.gridx = 7; - gbc.gridy = 8; - gbc.anchor = GridBagConstraints.NORTH; - mainMenuView.add(label9, gbc); - final JSeparator separator5 = new JSeparator(); + gbc.gridx = 2; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 25, 10, 0); + panel2.add(label9, gbc); + pwndfuCheckBox = new JCheckBox(); + pwndfuCheckBox.setText("Pwned Restore"); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 9; - gbc.gridwidth = 7; - gbc.weighty = 0.01; - gbc.fill = GridBagConstraints.BOTH; - gbc.ipady = 1; - gbc.insets = new Insets(7, 0, 0, 0); - mainMenuView.add(separator5, gbc); + gbc.gridy = 2; + gbc.weightx = 1.0; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(10, 0, 0, 10); + panel2.add(pwndfuCheckBox, gbc); final JLabel label10 = new JLabel(); Font label10Font = this.$$$getFont$$$("Menlo", -1, 10, label10.getFont()); if (label10Font != null) label10.setFont(label10Font); - label10.setText("(--debug)"); + label10.setText("(--use-pwndfu)"); gbc = new GridBagConstraints(); - gbc.gridx = 2; - gbc.gridy = 8; - gbc.anchor = GridBagConstraints.NORTH; - mainMenuView.add(label10, gbc); - debugDCheckBox = new JCheckBox(); - debugDCheckBox.setSelected(true); - debugDCheckBox.setText("Extra Logs"); + gbc.gridx = 1; + gbc.gridy = 3; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 25, 10, 0); + panel2.add(label10, gbc); + waitWCheckBox = new JCheckBox(); + waitWCheckBox.setText("AP Nonce Collision"); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(10, 0, 0, 10); + panel2.add(waitWCheckBox, gbc); + final JLabel label11 = new JLabel(); + Font label11Font = this.$$$getFont$$$("Menlo", -1, 10, label11.getFont()); + if (label11Font != null) label11.setFont(label11Font); + label11.setText("(--wait)"); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 25, 10, 0); + panel2.add(label11, gbc); + noIbssCheckBox = new JCheckBox(); + noIbssCheckBox.setEnabled(false); + noIbssCheckBox.setSelected(false); + noIbssCheckBox.setText("64 Bit Checkm8"); gbc = new GridBagConstraints(); gbc.gridx = 2; - gbc.gridy = 7; + gbc.gridy = 2; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; - mainMenuView.add(debugDCheckBox, gbc); - pwnedRestoreCheckBox = new JCheckBox(); - pwnedRestoreCheckBox.setText("Pwned Restore"); + gbc.insets = new Insets(10, 0, 0, 10); + panel2.add(noIbssCheckBox, gbc); + noIbssLabel = new JLabel(); + noIbssLabel.setEnabled(false); + Font noIbssLabelFont = this.$$$getFont$$$("Menlo", -1, 10, noIbssLabel.getFont()); + if (noIbssLabelFont != null) noIbssLabel.setFont(noIbssLabelFont); + noIbssLabel.setText("(--no-ibss)"); gbc = new GridBagConstraints(); - gbc.gridx = 4; - gbc.gridy = 7; - gbc.gridwidth = 2; + gbc.gridx = 2; + gbc.gridy = 3; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 25, 10, 0); + panel2.add(noIbssLabel, gbc); + justBootCheckBox = new JCheckBox(); + justBootCheckBox.setEnabled(false); + justBootCheckBox.setSelected(false); + justBootCheckBox.setText("Boot from Pwned DFU"); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 2; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; - mainMenuView.add(pwnedRestoreCheckBox, gbc); - final JLabel label11 = new JLabel(); - Font label11Font = this.$$$getFont$$$("Menlo", -1, 10, label11.getFont()); - if (label11Font != null) label11.setFont(label11Font); - label11.setText("(--use-pwndfu)"); + gbc.insets = new Insets(10, 0, 0, 0); + panel2.add(justBootCheckBox, gbc); + justBootLabel = new JLabel(); + justBootLabel.setEnabled(false); + Font justBootLabelFont = this.$$$getFont$$$("Menlo", -1, 10, justBootLabel.getFont()); + if (justBootLabelFont != null) justBootLabel.setFont(justBootLabelFont); + justBootLabel.setText("(--just-boot \"-v\")"); gbc = new GridBagConstraints(); - gbc.gridx = 4; - gbc.gridy = 8; - gbc.gridwidth = 2; - gbc.anchor = GridBagConstraints.NORTH; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(label11, gbc); + gbc.gridx = 3; + gbc.gridy = 3; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(0, 25, 10, 0); + panel2.add(justBootLabel, gbc); + final JLabel label12 = new JLabel(); + Font label12Font = this.$$$getFont$$$(null, -1, -1, label12.getFont()); + if (label12Font != null) label12.setFont(label12Font); + label12.setText("Pwned Args"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 2; + gbc.gridheight = 2; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 10, 0, 10); + panel2.add(label12, gbc); + final JPanel panel3 = new JPanel(); + panel3.setLayout(new GridBagLayout()); + tabbedPane.addTab("Controls", panel3); + final JLabel label13 = new JLabel(); + Font label13Font = this.$$$getFont$$$(null, Font.BOLD, -1, label13.getFont()); + if (label13Font != null) label13.setFont(label13Font); + label13.setText("Controls"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(10, 10, 0, 10); + panel3.add(label13, gbc); + exitRecoveryButton = new JButton(); + exitRecoveryButton.setText("Exit Recovery"); + exitRecoveryButton.setVerticalAlignment(0); + exitRecoveryButton.setVerticalTextPosition(0); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 0, 0, 0); + panel3.add(exitRecoveryButton, gbc); + startFutureRestoreButton = new JButton(); + Font startFutureRestoreButtonFont = this.$$$getFont$$$(null, Font.BOLD, 16, startFutureRestoreButton.getFont()); + if (startFutureRestoreButtonFont != null) startFutureRestoreButton.setFont(startFutureRestoreButtonFont); + startFutureRestoreButton.setText("Start FutureRestore"); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 0, 0, 0); + panel3.add(startFutureRestoreButton, gbc); stopFutureRestoreUnsafeButton = new JButton(); stopFutureRestoreUnsafeButton.setText("Stop FutureRestore"); gbc = new GridBagConstraints(); - gbc.gridx = 7; - gbc.gridy = 15; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(stopFutureRestoreUnsafeButton, gbc); - selectBuildManifestButton = new JButton(); - selectBuildManifestButton.setEnabled(false); - selectBuildManifestButton.setText("Select BuildManifest..."); - gbc = new GridBagConstraints(); - gbc.gridx = 7; - gbc.gridy = 13; + gbc.gridx = 3; + gbc.gridy = 1; gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(0, 0, 0, 10); - mainMenuView.add(selectBuildManifestButton, gbc); + gbc.insets = new Insets(10, 0, 0, 10); + panel3.add(stopFutureRestoreUnsafeButton, gbc); + final JPanel spacer5 = new JPanel(); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 2; + gbc.weighty = 2.0; + gbc.fill = GridBagConstraints.VERTICAL; + panel3.add(spacer5, gbc); + final JPanel spacer6 = new JPanel(); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 0; + gbc.weighty = 2.0; + gbc.fill = GridBagConstraints.VERTICAL; + panel3.add(spacer6, gbc); + final JLabel label14 = new JLabel(); + Font label14Font = this.$$$getFont$$$(null, Font.BOLD, -1, label14.getFont()); + if (label14Font != null) label14.setFont(label14Font); + label14.setText("Current Task"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 3; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(15, 10, 25, 10); + panel3.add(label14, gbc); currentTaskTextField = new JTextField(); currentTaskTextField.setEditable(false); Font currentTaskTextFieldFont = this.$$$getFont$$$(null, -1, 18, currentTaskTextField.getFont()); @@ -1474,28 +1818,21 @@ static void alertIfNewerFRGUIAvailable(MainMenu mainMenuInstance, String current currentTaskTextField.setHorizontalAlignment(0); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 15; - gbc.gridwidth = 5; + gbc.gridy = 3; + gbc.gridwidth = 3; + gbc.weightx = 1.0; + gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.BOTH; - mainMenuView.add(currentTaskTextField, gbc); - final JSeparator separator6 = new JSeparator(); - separator6.setOrientation(1); - gbc = new GridBagConstraints(); - gbc.gridx = 6; - gbc.gridy = 13; - gbc.weightx = 0.01; - gbc.fill = GridBagConstraints.BOTH; - gbc.ipadx = 1; - mainMenuView.add(separator6, gbc); - final JLabel label12 = new JLabel(); - label12.setText("OR"); + gbc.insets = new Insets(0, 0, 10, 10); + panel3.add(currentTaskTextField, gbc); + final JPanel spacer7 = new JPanel(); gbc = new GridBagConstraints(); - gbc.gridx = 4; - gbc.gridy = 3; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 5, 0, 5); - mainMenuView.add(label12, gbc); + gbc.gridx = 2; + gbc.gridy = 4; + gbc.weighty = 2.0; + gbc.fill = GridBagConstraints.VERTICAL; + panel3.add(spacer7, gbc); } /** diff --git a/src/main/java/SettingsMenu.form b/src/main/java/SettingsMenu.form index d65fff6..3146bf4 100644 --- a/src/main/java/SettingsMenu.form +++ b/src/main/java/SettingsMenu.form @@ -2,7 +2,7 @@ - + @@ -34,15 +34,15 @@ - + - + - + @@ -58,10 +58,10 @@ - + - + @@ -80,10 +80,10 @@ - + - + @@ -133,10 +133,10 @@ - + - + @@ -150,7 +150,7 @@ - + @@ -159,7 +159,7 @@ - + @@ -168,7 +168,7 @@ - + @@ -177,14 +177,14 @@ - + - + @@ -193,13 +193,47 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/SettingsMenu.java b/src/main/java/SettingsMenu.java index cec2ace..9fccb4e 100644 --- a/src/main/java/SettingsMenu.java +++ b/src/main/java/SettingsMenu.java @@ -18,23 +18,21 @@ public class SettingsMenu { private JRadioButton autoRadioButton; private JRadioButton lightRadioButton; private JRadioButton darkRadioButton; + private JCheckBox futureRestoreBetaCheckBox; public SettingsMenu() { - shareLogsCheckBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // Set prefs to true or false depending on what they check / uncheck - if (shareLogsCheckBox.isSelected()) { - MainMenu.properties.setProperty("upload_logs", "true"); - discordTextArea.setEnabled(true); - } else { - MainMenu.properties.setProperty("upload_logs", "false"); - discordTextArea.setEnabled(false); - } + shareLogsCheckBox.addActionListener(e -> { + // Set prefs to true or false depending on what they check / uncheck + if (shareLogsCheckBox.isSelected()) { + MainMenu.properties.setProperty("upload_logs", "true"); + discordTextArea.setEnabled(true); + } else { + MainMenu.properties.setProperty("upload_logs", "false"); + discordTextArea.setEnabled(false); + } - MainMenu.savePreferences(); + MainMenu.savePreferences(); - } }); discordTextArea.getDocument().addDocumentListener(new DocumentListener() { @@ -56,29 +54,33 @@ public void changedUpdate(DocumentEvent arg0) { } }); - previewCommandCheckBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (previewCommandCheckBox.isSelected()) { - MainMenu.properties.setProperty("preview_command", "true"); - MainMenu.savePreferences(); - } else { - MainMenu.properties.setProperty("preview_command", "false"); - MainMenu.savePreferences(); - } + previewCommandCheckBox.addActionListener(e -> { + if (previewCommandCheckBox.isSelected()) { + MainMenu.properties.setProperty("preview_command", "true"); + MainMenu.savePreferences(); + } else { + MainMenu.properties.setProperty("preview_command", "false"); + MainMenu.savePreferences(); } }); - GUIUpdatesCheckBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (GUIUpdatesCheckBox.isSelected()) { - MainMenu.properties.setProperty("check_updates", "true"); - MainMenu.savePreferences(); - } else { - MainMenu.properties.setProperty("check_updates", "false"); - MainMenu.savePreferences(); - } + GUIUpdatesCheckBox.addActionListener(e -> { + if (GUIUpdatesCheckBox.isSelected()) { + MainMenu.properties.setProperty("check_updates", "true"); + MainMenu.savePreferences(); + } else { + MainMenu.properties.setProperty("check_updates", "false"); + MainMenu.savePreferences(); + } + }); + + futureRestoreBetaCheckBox.addActionListener(e -> { + if (futureRestoreBetaCheckBox.isSelected()) { + MainMenu.properties.setProperty("futurerestore_beta", "true"); + MainMenu.savePreferences(); + } else { + MainMenu.properties.setProperty("futurerestore_beta", "false"); + MainMenu.savePreferences(); } }); @@ -132,11 +134,23 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { else settingsMenuInstance.GUIUpdatesCheckBox.setSelected(false); + // FR beta update check box + if (MainMenu.properties.getProperty("futurerestore_beta").equals("true")) + settingsMenuInstance.futureRestoreBetaCheckBox.setSelected(true); + else + settingsMenuInstance.futureRestoreBetaCheckBox.setSelected(false); + //Theme prefs radio buttons switch (MainMenu.properties.getProperty("theme_preference")) { - case "auto": settingsMenuInstance.autoRadioButton.setSelected(true); break; - case "light": settingsMenuInstance.lightRadioButton.setSelected(true); break; - case "dark": settingsMenuInstance.darkRadioButton.setSelected(true); break; + case "auto": + settingsMenuInstance.autoRadioButton.setSelected(true); + break; + case "light": + settingsMenuInstance.lightRadioButton.setSelected(true); + break; + case "dark": + settingsMenuInstance.darkRadioButton.setSelected(true); + break; } } @@ -183,17 +197,17 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { gbc.fill = GridBagConstraints.VERTICAL; settingsMenuView.add(spacer1, gbc); final JLabel label1 = new JLabel(); - label1.setText("(Optional) Let us contact you about your logs by providing your Discord"); + label1.setText("(Optional) Let us contact you about your logs by providing your Discord."); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 3; gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); + gbc.insets = new Insets(0, 5, 0, 10); settingsMenuView.add(label1, gbc); final JPanel spacer2 = new JPanel(); gbc = new GridBagConstraints(); gbc.gridx = 0; - gbc.gridy = 10; + gbc.gridy = 12; gbc.gridwidth = 3; gbc.fill = GridBagConstraints.VERTICAL; settingsMenuView.add(spacer2, gbc); @@ -207,12 +221,12 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { gbc.insets = new Insets(0, 10, 0, 0); settingsMenuView.add(previewCommandCheckBox, gbc); final JLabel label2 = new JLabel(); - label2.setText("See a preview of the final command before it runs"); + label2.setText("See a preview of the final command before it runs."); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 5; gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); + gbc.insets = new Insets(0, 5, 0, 10); settingsMenuView.add(label2, gbc); discordTextArea = new JTextArea(); discordTextArea.setLineWrap(true); @@ -226,12 +240,12 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { gbc.insets = new Insets(0, 10, 0, 0); settingsMenuView.add(discordTextArea, gbc); final JLabel label3 = new JLabel(); - label3.setText("Automatically share logs to help make FutureRestore better"); + label3.setText("Automatically share logs to help make FutureRestore better."); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); + gbc.insets = new Insets(0, 5, 0, 10); settingsMenuView.add(label3, gbc); final JLabel label4 = new JLabel(); Font label4Font = this.$$$getFont$$$(null, -1, 20, label4.getFont()); @@ -277,12 +291,12 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { gbc.insets = new Insets(0, 10, 0, 0); settingsMenuView.add(GUIUpdatesCheckBox, gbc); final JLabel label5 = new JLabel(); - label5.setText("Automatically check for updates for FutureRestore GUI"); + label5.setText("Automatically check for updates for FutureRestore GUI."); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 7; gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); + gbc.insets = new Insets(0, 5, 0, 10); settingsMenuView.add(label5, gbc); final JSeparator separator5 = new JSeparator(); separator5.setOrientation(1); @@ -295,7 +309,7 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { autoRadioButton.setText("Auto"); gbc = new GridBagConstraints(); gbc.gridx = 0; - gbc.gridy = 9; + gbc.gridy = 11; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(0, 10, 0, 0); settingsMenuView.add(autoRadioButton, gbc); @@ -303,20 +317,20 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { lightRadioButton.setText("Light"); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 9; + gbc.gridy = 11; gbc.anchor = GridBagConstraints.WEST; settingsMenuView.add(lightRadioButton, gbc); darkRadioButton = new JRadioButton(); darkRadioButton.setText("Dark"); gbc = new GridBagConstraints(); gbc.gridx = 2; - gbc.gridy = 9; + gbc.gridy = 11; gbc.anchor = GridBagConstraints.WEST; settingsMenuView.add(darkRadioButton, gbc); final JSeparator separator6 = new JSeparator(); gbc = new GridBagConstraints(); gbc.gridx = 0; - gbc.gridy = 8; + gbc.gridy = 10; gbc.gridwidth = 5; gbc.fill = GridBagConstraints.BOTH; settingsMenuView.add(separator6, gbc); @@ -324,17 +338,48 @@ static void initializeSettingsMenu(SettingsMenu settingsMenuInstance) { separator7.setOrientation(1); gbc = new GridBagConstraints(); gbc.gridx = 3; - gbc.gridy = 9; + gbc.gridy = 11; gbc.fill = GridBagConstraints.BOTH; settingsMenuView.add(separator7, gbc); final JLabel label6 = new JLabel(); label6.setText("Set the theme of the GUI. Requires a restart to take effect."); gbc = new GridBagConstraints(); gbc.gridx = 4; - gbc.gridy = 9; + gbc.gridy = 11; gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); + gbc.insets = new Insets(0, 5, 0, 10); settingsMenuView.add(label6, gbc); + futureRestoreBetaCheckBox = new JCheckBox(); + futureRestoreBetaCheckBox.setText("FutureRestore Beta"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 9; + gbc.gridwidth = 3; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 10, 0, 0); + settingsMenuView.add(futureRestoreBetaCheckBox, gbc); + final JSeparator separator8 = new JSeparator(); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 8; + gbc.gridwidth = 5; + gbc.fill = GridBagConstraints.BOTH; + settingsMenuView.add(separator8, gbc); + final JLabel label7 = new JLabel(); + label7.setText("\"Download FutureRestore\" will use the latest beta of FutureRestore."); + gbc = new GridBagConstraints(); + gbc.gridx = 4; + gbc.gridy = 9; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 5, 0, 10); + settingsMenuView.add(label7, gbc); + final JSeparator separator9 = new JSeparator(); + separator9.setOrientation(1); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 9; + gbc.fill = GridBagConstraints.BOTH; + settingsMenuView.add(separator9, gbc); ButtonGroup buttonGroup; buttonGroup = new ButtonGroup(); buttonGroup.add(autoRadioButton);