diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java index 175c9a7..86d6dcd 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java @@ -1,6 +1,7 @@ package dev.skidfuscator.obfuscator; import com.formdev.flatlaf.intellijthemes.FlatDarkPurpleIJTheme; +import com.formdev.flatlaf.intellijthemes.FlatGradiantoMidnightBlueIJTheme; import dev.skidfuscator.obfuscator.command.HelpCommand; import dev.skidfuscator.obfuscator.command.MappingsCommand; import dev.skidfuscator.obfuscator.command.ObfuscateCommand; diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ActionPanel.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ActionPanel.java index 42783fe..af8af16 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ActionPanel.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ActionPanel.java @@ -6,7 +6,7 @@ import java.awt.*; import java.io.File; -public class ActionPanel extends JPanel { +public class ActionPanel extends JPanel implements SkidPanel{ private final MainFrame mainFrame; private final JTextArea logArea; private final JButton startButton; @@ -40,6 +40,8 @@ public void startObfuscation() { return; } + + // Create session SkidfuscatorSession session = SkidfuscatorSession.builder() .input(new File(config.getInputPath())) @@ -48,10 +50,7 @@ public void startObfuscation() { ? new File[0] : new File(config.getLibsPath()).listFiles() ) - .runtime(config.getRuntimePath().isEmpty() - ? null - : new File(config.getRuntimePath()) - ) + .runtime(null) .phantom(false) .debug(config.isDebugEnabled()) .build(); diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConfigPanel.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConfigPanel.java index 6e8e9c7..4e789da 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConfigPanel.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConfigPanel.java @@ -9,26 +9,22 @@ import dev.skidfuscator.obfuscator.gui.autosave.AutoSaveDocumentListener; import dev.skidfuscator.obfuscator.gui.config.SkidfuscatorConfig; import dev.skidfuscator.obfuscator.util.JdkDownloader; +import dev.skidfuscator.obfuscator.util.Observable; -import javax.swing.*; -import java.awt.*; -import java.io.File; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import javax.swing.border.BevelBorder; import javax.swing.border.EtchedBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.DefaultHighlighter; -public class ConfigPanel extends JPanel { +public class ConfigPanel extends JPanel implements SkidPanel{ private final JTextField inputField; private final JTextField outputField; private final JTextField libsField; private final JTextField runtimeField; private final JCheckBox debugBox; private final SkidfuscatorConfig config; + private Observable runtimeInstalled = new Observable.SimpleObservable<>( + JdkDownloader.isJdkDownloaded() + ); public ConfigPanel() { setLayout(new GridBagLayout()); @@ -116,7 +112,9 @@ private void updateCheck() { boolean valid = new File(inputField.getText()).exists(); inputCheck.setText(valid ? "✓" : "✗"); inputCheck.setForeground(valid ? new Color(46, 204, 64) : new Color(255, 65, 54)); - + System.out.println("Input valid: " + valid); + config.getValidInput().set(valid); + if (!valid) { StringBuilder tooltip = new StringBuilder(""); tooltip.append("
⚠ Warning: Invalid Input Configuration
"); @@ -171,7 +169,10 @@ private void updateCheck() { boolean valid = parent != null && parent.exists() && validEnd && validInput; outputCheck.setText(valid ? "✓" : "✗"); - outputCheck.setForeground(valid ? new Color(46, 204, 64) : new Color(255, 65, 54)); + outputCheck.setForeground(valid + ? new Color(46, 204, 64) + : new Color(255, 65, 54) + ); // Set tooltip explaining validation failure StringBuilder tooltip = new StringBuilder(""); tooltip.append("
⚠ Warning: Invalid Output Configuration
"); @@ -186,6 +187,7 @@ private void updateCheck() { tooltip.append("
Output file cannot be the same as input file
"); } tooltip.append(""); + config.getValidOutput().set(validInput); if (!valid) { ToolTipManager.sharedInstance().setInitialDelay(0); @@ -193,7 +195,7 @@ private void updateCheck() { outputField.setToolTipText(tooltip.toString()); } else { - outputCheck.setToolTipText(null); + outputField.setToolTipText(null); } } public void insertUpdate(DocumentEvent e) { updateCheck(); } @@ -204,9 +206,15 @@ private void updateCheck() { if (config.getLastOutputPath() != null) { outputField.setText(config.getLastOutputPath()); outputListener.insertUpdate(null); - } else if (config.getLastInputPath() != null) { - outputField.setText(config.getLastInputPath().replace(".jar", "-obf.jar")); + } else if (config.getLastInputPath() != null || inputField.getText() != null) { + final String input = config.getLastInputPath() != null + ? config.getLastInputPath() + : inputField.getText(); + + outputField.setText(input.replace(".jar", "-obf.jar")); outputListener.insertUpdate(null); + } else { + config.getValidOutput().set(false); } add(outputField, gbc); gbc.gridx = 2; @@ -271,12 +279,14 @@ private void updateCheck() { String jmodPath = JdkDownloader.getCachedJmodPath(); runtimeField.setText(jmodPath); runtimeField.setEnabled(!JdkDownloader.isJdkDownloaded()); + runtimeInstalled.set(JdkDownloader.isJdkDownloaded()); } catch (IOException e) { // Fallback to config if (config.getLastRuntimePath() != null) { if (config.getLastRuntimePath().isEmpty()) { runtimeField.setText(Jvm.getLibsPath()); runtimeField.setEnabled(false); + runtimeInstalled.set(true); } else { runtimeField.setText(config.getLastRuntimePath()); } @@ -288,12 +298,19 @@ private void updateCheck() { // Add download button next to browse button gbc.gridx = 2; JPanel runtimeButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); - JButton downloadButton = new JButton("Download JDK"); - + JLabel downloadCheck = new JLabel("✗"); + downloadCheck.setForeground(new Color(255, 65, 54)); + + JButton downloadButton = new JButton("Install"); + runtimeButtonPanel.setPreferredSize(new Dimension(150, 30)); + // Set initial button state based on JDK download status if (JdkDownloader.isJdkDownloaded()) { - downloadButton.setText("Downloaded"); + downloadButton.setText("Installed"); downloadButton.setEnabled(false); + downloadCheck.setText("✓"); + downloadCheck.setForeground(new Color(46, 204, 64)); + runtimeInstalled.set(true); } downloadButton.addActionListener(e -> { @@ -312,8 +329,12 @@ protected void done() { String path = get(); runtimeField.setText(path); runtimeField.setEnabled(false); - downloadButton.setText("Downloaded"); + downloadButton.setText("Installed"); downloadButton.setEnabled(false); + downloadCheck.setText("✓"); + downloadCheck.setForeground(new Color(46, 204, 64)); + runtimeInstalled.set(true); + } catch (Exception ex) { JOptionPane.showMessageDialog( ConfigPanel.this, @@ -321,14 +342,19 @@ protected void done() { "Download Error", JOptionPane.ERROR_MESSAGE ); - downloadButton.setText("Download JDK"); + downloadButton.setText("Install"); downloadButton.setEnabled(true); + downloadCheck.setText("✗"); + downloadCheck.setForeground(new Color(255, 65, 54)); + runtimeInstalled.set(false); } } }; worker.execute(); }); runtimeButtonPanel.add(downloadButton); + runtimeButtonPanel.add(Box.createHorizontalGlue()); + runtimeButtonPanel.add(downloadCheck); // removing for now //runtimeButtonPanel.add(createBrowseButton(runtimeField, false)); @@ -413,5 +439,13 @@ public String getLibraryPath() { // TODO: Add a library path field to the config panel return null; } + + public Observable getRuntimeInstalled() { + return runtimeInstalled; + } + + public SkidfuscatorConfig getConfig() { + return config; + } } diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConsolePanel.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConsolePanel.java index 35e8e4d..c26154b 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConsolePanel.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/ConsolePanel.java @@ -24,7 +24,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ConsolePanel extends JPanel { +public class ConsolePanel extends JPanel implements SkidPanel { private final JTextPane consoleOutput; private final SimpleDateFormat timeFormat; private final StyledDocument doc; diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/LibrariesPanel.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/LibrariesPanel.java index fa175d1..6814b24 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/LibrariesPanel.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/LibrariesPanel.java @@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.swing.Timer; -public class LibrariesPanel extends JPanel { +public class LibrariesPanel extends JPanel implements SkidPanel { private final JList libraryList; private final DefaultListModel libraryModel; private final JList missingClassesList; @@ -179,6 +179,9 @@ public LibrariesPanel(ConfigPanel configPanel, SkidApplicationClassSource classS add(bottomPanel, BorderLayout.SOUTH); // Analyze the input jar if specified in config + } + + public void open() { SwingUtilities.invokeLater(this::analyzeConfigJar); } diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/MainFrame.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/MainFrame.java index 8306b99..9af54e5 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/MainFrame.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/MainFrame.java @@ -4,6 +4,8 @@ import com.formdev.flatlaf.ui.FlatTabbedPaneUI; import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.SkidfuscatorSession; +import dev.skidfuscator.obfuscator.util.JdkDownloader; +import dev.skidfuscator.obfuscator.util.Observable; import lombok.Getter; import javax.imageio.ImageIO; @@ -96,7 +98,7 @@ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { g.drawString("Skidfuscator Community", 20, 175); g.setColor(new Color(130, 130, 130)); g.setFont(new Font("Segoe UI", Font.ITALIC, 11)); - g.drawString("Build: 2023.1", 20, 190); + g.drawString("Build: " + Skidfuscator.VERSION, 20, 190); // Draw second separator g.setColor(Color.DARK_GRAY); @@ -228,15 +230,23 @@ public void mouseClicked(MouseEvent e) { contentPanel.removeAll(); switch (tabbedPane.getSelectedIndex()) { case 0: + configPanel.open(); contentPanel.add(configPanel, BorderLayout.CENTER); break; case 1: + if (!JdkDownloader.isJdkDownloaded()) { + JOptionPane.showMessageDialog(this, "Please download the JDK first", "Error", JOptionPane.ERROR_MESSAGE); + tabbedPane.setSelectedIndex(0); + return; + } + librariesPanel.open(); contentPanel.add(librariesPanel, BorderLayout.CENTER); break; case 2: contentPanel.add(transformerPanel, BorderLayout.CENTER); break; case 3: + consolePanel.open(); contentPanel.add(consolePanel, BorderLayout.CENTER); break; } @@ -244,6 +254,13 @@ public void mouseClicked(MouseEvent e) { contentPanel.repaint(); }); + + // Observe input/output and adapt button to it + configPanel.getConfig().getValidInput().addObserver(value -> refreshStartButton()); + configPanel.getConfig().getValidOutput().addObserver(value -> refreshStartButton()); + configPanel.getRuntimeInstalled().addObserver(value -> refreshStartButton()); + refreshStartButton(); + // Final setup pack(); setLocationRelativeTo(null); @@ -309,6 +326,23 @@ public void updateTabStates(boolean configEnabled, boolean transformersEnabled) tabbedPane.setEnabledAt(1, transformersEnabled); } + private void refreshStartButton() { + System.out.println("Refreshing start button"); + + if (!configPanel.getConfig().isValid() || !configPanel.getRuntimeInstalled().get()) { + // Set warning icon + final Image warningIcon = new ImageIcon(getClass().getResource("/images/warning.png")).getImage(); + final Image warningImage = warningIcon.getScaledInstance(16, 16, Image.SCALE_SMOOTH); + final ImageIcon warningIconScaled = new ImageIcon(warningImage); + startButton.setIcon(warningIconScaled); + startButton.setEnabled(false); + } else { + startButton.setToolTipText(null); + startButton.setIcon(null); + startButton.setEnabled(true); + } + } + public void startObfuscation() { ConfigPanel config = this.getConfigPanel(); TransformerPanel transformers = this.getTransformerPanel(); @@ -347,15 +381,17 @@ public void startObfuscation() { ? libraryFolder.toFile().listFiles() : new File(config.getLibsPath()).listFiles() ) - .runtime(config.getRuntimePath().isEmpty() - ? null - : new File(config.getRuntimePath()) - ) + //.runtime(config.getRuntimePath().isEmpty() + // ? null + // : new File(config.getRuntimePath()) + //) .jmod(config.getRuntimePath().contains("jmods")) .debug(config.isDebugEnabled()) .build(); // Start obfuscation in background startButton.setEnabled(false); + + SwingWorker worker = new SwingWorker() { @Override protected Void doInBackground() { diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/SkidPanel.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/SkidPanel.java new file mode 100644 index 0000000..61efbba --- /dev/null +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/SkidPanel.java @@ -0,0 +1,5 @@ +package dev.skidfuscator.obfuscator.gui; + +public interface SkidPanel { + default void open() {} +} diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/config/SkidfuscatorConfig.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/config/SkidfuscatorConfig.java index 874ee33..c44fc34 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/config/SkidfuscatorConfig.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/gui/config/SkidfuscatorConfig.java @@ -2,11 +2,10 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import dev.skidfuscator.obfuscator.util.Observable; import lombok.Data; import java.io.*; -import java.nio.file.*; -import java.util.Properties; @Data public class SkidfuscatorConfig { @@ -22,6 +21,14 @@ public class SkidfuscatorConfig { private boolean phantomEnabled; private String lastDirectory; + private transient Observable + validInput = new Observable.SimpleObservable<>(false), + validOutput = new Observable.SimpleObservable<>(false); + + public boolean isValid() { + return validInput.get() && validOutput.get(); + } + public static class Builder { private final SkidfuscatorConfig config; diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java index 49d033b..f597aad 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java @@ -1,5 +1,6 @@ package dev.skidfuscator.obfuscator.util; +import dev.skidfuscator.obfuscator.Skidfuscator; import lombok.experimental.UtilityClass; import java.text.DateFormat; @@ -61,7 +62,7 @@ public static void printLogo() { " │ " + topMemory + " │", " └───────────────────────────────────────────┘", "", - " Author: Ghast Version: 2.0.11 Today: " + " Author: Ghast Version: " + Skidfuscator.VERSION + " Today: " + DateFormat.getDateTimeInstance().format(new Date(Instant.now().toEpochMilli())), "" }; diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/Observable.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/Observable.java new file mode 100644 index 0000000..a7b4ee5 --- /dev/null +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/Observable.java @@ -0,0 +1,76 @@ +package dev.skidfuscator.obfuscator.util; + +import java.util.HashSet; +import java.util.Set; + +public interface Observable { + T get(); + + void set(T value); + + void addObserver(Observer observer); + + void removeObserver(Observer observer); + + class SimpleObservable implements Observable { + private final Set> observers = new HashSet<>(); + private T value; + + public SimpleObservable(T value) { + this.value = value; + } + + public T get() { + return value; + } + + public void set(T value) { + this.value = value; + observers.forEach(observer -> observer.onChanged(value)); + } + + @Override + public void addObserver(Observer observer) { + observers.add(observer); + } + + @Override + public void removeObserver(Observer observer) { + observers.remove(observer); + } + } + + class MergedBooleanObservable implements Observable { + private final Set> observables = new HashSet<>(); + private final Set> observers = new HashSet<>(); + + public MergedBooleanObservable(Observable... observables) { + for (Observable observable : observables) { + this.observables.add(observable); + observable.addObserver(value -> observers.forEach(observer -> observer.onChanged(get()))); + } + } + + public Boolean get() { + return observables.stream().allMatch(Observable::get); + } + + public void set(Boolean value) { + observables.forEach(observable -> observable.set(value)); + } + + @Override + public void addObserver(Observer observer) { + observers.add(observer); + } + + @Override + public void removeObserver(Observer observer) { + observers.remove(observer); + } + } + + interface Observer { + void onChanged(T value); + } +} diff --git a/dev.skidfuscator.client.standalone/src/main/resources/images/warning.png b/dev.skidfuscator.client.standalone/src/main/resources/images/warning.png new file mode 100644 index 0000000..4d4990a Binary files /dev/null and b/dev.skidfuscator.client.standalone/src/main/resources/images/warning.png differ diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java index e57843d..d6fa0e9 100644 --- a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java @@ -109,6 +109,8 @@ public class Skidfuscator { public static final boolean FLATTENING = false; public static boolean CLOUD = false; + public static final String VERSION = "2.1.0"; + private final SkidfuscatorSession session; protected Set installedDependencies = new HashSet<>(); @@ -524,7 +526,7 @@ public Set _importJvm() { */ if (libFiles == null) { LOGGER.warn("FATAL! Library files for JDK are null"); - System.exit(2); + throw new IllegalStateException("Null JDK files! Exiting..."); } try (final ProgressWrapper wrapper = ProgressUtil.progressCheck( @@ -787,9 +789,7 @@ private void _verify() { + "-----------------------------------------------------\n" ); - if (!CLOUD) - System.exit(1); - return; + throw new IllegalStateException("Failed to compute some libraries!"); } // [failsafe] some people cancel mid download, corrupting the library diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/JdkDownloader.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/JdkDownloader.java index e884978..5941615 100644 --- a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/JdkDownloader.java +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/JdkDownloader.java @@ -128,16 +128,11 @@ public static Path getJdkHome() throws IOException { wrapper.tick(); wrapper.succeed(); } catch (IOException e) { - Files.deleteIfExists(jdkPath); + Files.deleteIfExists(getCachedJdk()); throw e; } } - - switch (OS) { - case "mac os x": - case "mac": - return jdkPath.resolve("Contents/Home"); - } + jdkPath = getCachedJdk(); return jdkPath; }