/******************************************************************************* * 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; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.serde.MappingFormat; import cuchaz.enigma.translation.mapping.tree.EntryTree; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Locale; import java.util.jar.JarFile; public class CommandMain { public static void main(String[] args) throws Exception { try { // process the command String command = getArg(args, 0, "command", true); if (command.equalsIgnoreCase("deobfuscate")) { deobfuscate(args); } else if (command.equalsIgnoreCase("decompile")) { decompile(args); } else if (command.equalsIgnoreCase("convertmappings")) { convertMappings(args); } else { throw new IllegalArgumentException("Command not recognized: " + command); } } catch (IllegalArgumentException ex) { System.out.println(ex.getMessage()); printHelp(); } } private static void printHelp() { System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); System.out.println("Usage:"); System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain "); System.out.println("\twhere is one of:"); System.out.println("\t\tdeobfuscate []"); System.out.println("\t\tdecompile []"); System.out.println("\t\tconvertmappings "); } private static void decompile(String[] args) throws Exception { File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeSources(fileJarOut.toPath(), new ConsoleProgressListener()); } private static void deobfuscate(String[] args) throws Exception { File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeTransformedJar(fileJarOut, new ConsoleProgressListener()); } private static Deobfuscator getDeobfuscator(Path fileMappings, JarFile jar) throws Exception { System.out.println("Reading jar..."); Deobfuscator deobfuscator = new Deobfuscator(jar); if (fileMappings != null) { System.out.println("Reading mappings..."); EntryTree mappings = chooseEnigmaFormat(fileMappings).read(fileMappings); deobfuscator.setMappings(mappings); } return deobfuscator; } private static void convertMappings(String[] args) throws Exception { Path fileMappings = getReadablePath(getArg(args, 1, "enigma mapping", true)); File result = getWritableFile(getArg(args, 2, "enigma mapping", true)); String name = getArg(args, 3, "format desc", true); MappingFormat saveFormat; try { saveFormat = MappingFormat.valueOf(name.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(name + "is not a valid mapping format!"); } System.out.println("Reading mappings..."); MappingFormat readFormat = chooseEnigmaFormat(fileMappings); EntryTree mappings = readFormat.read(fileMappings); System.out.println("Saving new mappings..."); saveFormat.write(mappings, result.toPath(), new ConsoleProgressListener()); } private static MappingFormat chooseEnigmaFormat(Path path) { if (Files.isDirectory(path)) { return MappingFormat.ENIGMA_DIRECTORY; } else { return MappingFormat.ENIGMA_FILE; } } private static String getArg(String[] args, int i, String name, boolean required) { if (i >= args.length) { if (required) { throw new IllegalArgumentException(name + " is required"); } else { return null; } } return args[i]; } private static File getWritableFile(String path) { if (path == null) { return null; } File file = new File(path).getAbsoluteFile(); File dir = file.getParentFile(); if (dir == null) { throw new IllegalArgumentException("Cannot write file: " + path); } // quick fix to avoid stupid stuff in Gradle code if (!dir.isDirectory()) { dir.mkdirs(); } return file; } private static File getWritableFolder(String path) { if (path == null) { return null; } File dir = new File(path).getAbsoluteFile(); if (!dir.exists()) { throw new IllegalArgumentException("Cannot write to folder: " + dir); } return dir; } private static File getReadableFile(String path) { if (path == null) { return null; } File file = new File(path).getAbsoluteFile(); if (!file.exists()) { throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); } return file; } private static Path getReadablePath(String path) { if (path == null) { return null; } Path file = Paths.get(path).toAbsolutePath(); if (!Files.exists(file)) { throw new IllegalArgumentException("Cannot find file: " + file.toString()); } return file; } public static class ConsoleProgressListener implements ProgressListener { private static final int ReportTime = 5000; // 5s private int totalWork; private long startTime; private long lastReportTime; @Override public void init(int totalWork, String title) { this.totalWork = totalWork; this.startTime = System.currentTimeMillis(); this.lastReportTime = this.startTime; System.out.println(title); } @Override public void step(int numDone, String message) { long now = System.currentTimeMillis(); boolean isLastUpdate = numDone == this.totalWork; boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime; if (shouldReport) { int percent = numDone * 100 / this.totalWork; System.out.println(String.format("\tProgress: %3d%%", percent)); this.lastReportTime = now; } if (isLastUpdate) { double elapsedSeconds = (now - this.startTime) / 1000.0; System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); } } } }