/*******************************************************************************
* Copyright (c) 2015 Jeff Martin.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public
* License v3.0 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
*
Contributors:
* Jeff Martin - initial API and implementation
******************************************************************************/
package cuchaz.enigma.gui;
import java.awt.Toolkit;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import joptsimple.ValueConverter;
import cuchaz.enigma.Enigma;
import cuchaz.enigma.EnigmaProfile;
import cuchaz.enigma.gui.config.Themes;
import cuchaz.enigma.gui.config.UiConfig;
import cuchaz.enigma.gui.dialog.CrashDialog;
import cuchaz.enigma.translation.mapping.serde.MappingFormat;
import cuchaz.enigma.utils.I18n;
public class Main {
public static void main(String[] args) throws IOException {
OptionParser parser = new OptionParser();
OptionSpec jar = parser.accepts("jar", "Jar file to open at startup; if there are multiple jars, the order must be the same between all collab session members").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
OptionSpec library = parser.accepts("library", "The libraries for the input jar files").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
OptionSpec mappings = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
OptionSpec profile = parser.accepts("profile", "Profile json to apply at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
parser.acceptsAll(List.of("edit-all", "e"), "Enable editing everything");
parser.acceptsAll(List.of("no-edit-all", "E"), "Disable editing everything");
parser.acceptsAll(List.of("edit-classes", "c"), "Enable editing class names");
parser.acceptsAll(List.of("no-edit-classes", "C"), "Disable editing class names");
parser.acceptsAll(List.of("edit-methods", "m"), "Enable editing method names");
parser.acceptsAll(List.of("no-edit-methods", "M"), "Disable editing method names");
parser.acceptsAll(List.of("edit-fields", "f"), "Enable editing field names");
parser.acceptsAll(List.of("no-edit-fields", "F"), "Disable editing field names");
parser.acceptsAll(List.of("edit-parameters", "p"), "Enable editing parameter names");
parser.acceptsAll(List.of("no-edit-parameters", "P"), "Disable editing parameter names");
parser.acceptsAll(List.of("edit-locals"), "Enable editing local variable names");
parser.acceptsAll(List.of("no-edit-locals"), "Disable editing local variable names");
parser.acceptsAll(List.of("edit-javadocs", "d"), "Enable editing Javadocs");
parser.acceptsAll(List.of("no-edit-javadocs", "D"), "Disable editing Javadocs");
parser.accepts("single-class-tree", "Unify the deobfuscated and obfuscated class panels");
parser.accepts("help", "Displays help information");
try {
OptionSet options = parser.parse(args);
if (options.has("help")) {
parser.printHelpOn(System.out);
return;
}
Set editables = EnumSet.allOf(EditableType.class);
for (OptionSpec> spec : options.specs()) {
for (String s : spec.options()) {
switch (s) {
case "edit-all" -> editables.addAll(List.of(EditableType.values()));
case "no-edit-all" -> editables.clear();
case "edit-classes" -> editables.add(EditableType.CLASS);
case "no-edit-classes" -> editables.remove(EditableType.CLASS);
case "edit-methods" -> editables.add(EditableType.METHOD);
case "no-edit-methods" -> editables.remove(EditableType.METHOD);
case "edit-fields" -> editables.add(EditableType.FIELD);
case "no-edit-fields" -> editables.remove(EditableType.FIELD);
case "edit-parameters" -> editables.add(EditableType.PARAMETER);
case "no-edit-parameters" -> editables.remove(EditableType.PARAMETER);
case "edit-locals" -> {
editables.add(EditableType.LOCAL_VARIABLE);
System.err.println("warning: --edit-locals has no effect as local variables are currently not editable");
}
case "no-edit-locals" -> {
editables.remove(EditableType.LOCAL_VARIABLE);
System.err.println("warning: --no-edit-locals has no effect as local variables are currently not editable");
}
case "edit-javadocs" -> editables.add(EditableType.JAVADOC);
case "no-edit-javadocs" -> editables.remove(EditableType.JAVADOC);
}
}
}
EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile));
Enigma enigma = Enigma.builder().setProfile(parsedProfile).build();
I18n.setLanguage(UiConfig.getLanguage(), enigma.getServices());
// Provide fallback anti-aliasing for desktop environments the JRE doesn't recognize
if (Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints") == null) {
setDefaultSystemProperty("awt.useSystemAAFontSettings", "lcd");
}
// Not setting "swing.aatext" here because that property has been removed:
// https://bugs.openjdk.org/browse/JDK-6391267
// If on MacOS, use the system's menu bar, not the in-app one
setDefaultSystemProperty("apple.laf.useScreenMenuBar", "true");
Themes.setupTheme();
Gui gui = new Gui(enigma, editables);
GuiController controller = gui.getController();
if (options.has("single-class-tree")) {
gui.setSingleClassTree(true);
}
if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) {
// install a global exception handler to the event thread
CrashDialog.init(gui.getFrame());
Thread.setDefaultUncaughtExceptionHandler((thread, t) -> {
t.printStackTrace(System.err);
if (!ExceptionIgnorer.shouldIgnore(t)) {
CrashDialog.show(t);
}
});
}
if (options.has(jar)) {
List jarPaths = options.valuesOf(jar);
List libraryPaths = options.valuesOf(library);
controller.openJar(jarPaths, libraryPaths).whenComplete((v, t) -> {
if (options.has(mappings)) {
Path mappingsPath = options.valueOf(mappings);
if (Files.isDirectory(mappingsPath)) {
controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath);
} else {
controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath);
}
}
});
}
} catch (OptionException e) {
System.out.println("Invalid arguments: " + e.getMessage());
System.out.println();
parser.printHelpOn(System.out);
}
}
private static void setDefaultSystemProperty(String property, String value) {
System.setProperty(property, System.getProperty(property, value));
}
public static class PathConverter implements ValueConverter {
public static final ValueConverter INSTANCE = new PathConverter();
PathConverter() {
}
@Override
public Path convert(String path) {
// expand ~ to the home dir
if (path.startsWith("~")) {
// get the home dir
Path dirHome = Paths.get(System.getProperty("user.home"));
// is the path just ~/ or is it ~user/ ?
if (path.startsWith("~/")) {
return dirHome.resolve(path.substring(2));
} else {
return dirHome.getParent().resolve(path.substring(1));
}
}
return Paths.get(path);
}
@Override
public Class extends Path> valueType() {
return Path.class;
}
@Override
public String valuePattern() {
return "path";
}
}
}