Skip to content

Commit

Permalink
2.4 release
Browse files Browse the repository at this point in the history
Properties now use public static fields
Remove deobfuscate(Path), decompile(DecompilerType,Path,Path) and decompileCustomized(String,Path,Path) in MinecraftDecompiler.java, use Options API instead
Remove --ourDir, --outDeobfName --outDecomName options, use --output and --outputDecomp
Now the --targetNamespace is correctly passed to MinecraftDecompiler
Add --dontIncludeOthers options to drop the resource files in output jar
  • Loading branch information
XiaoPangxie732 committed Jul 16, 2021
1 parent 283a19c commit bc41991
Show file tree
Hide file tree
Showing 20 changed files with 536,443 additions and 387,668 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>cn.maxpixel</groupId>
<artifactId>minecraft-decompiler</artifactId>
<name>MinecraftDecompiler</name>
<version>2.4-SNAPSHOT</version>
<version>2.4</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<depend.asm.ver>9.2</depend.asm.ver>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/cn/maxpixel/mcdecompiler/Deobfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public Deobfuscator deobfuscate(Path source, Path target, String targetNamespace
LOGGER.error("Error when remapping classes or coping files", e);
}
});
if(options.rvn()) ClassProcessor.endRecord(Properties.get(Properties.Key.TEMP_DIR).resolve(FERNFLOWER_ABSTRACT_PARAMETER_NAMES));
if(options.rvn()) ClassProcessor.endRecord(Properties.TEMP_DIR.resolve(FERNFLOWER_ABSTRACT_PARAMETER_NAMES));
} catch (IOException e) {
LOGGER.error("Error when deobfuscating", e);
}
Expand Down
86 changes: 46 additions & 40 deletions src/main/java/cn/maxpixel/mcdecompiler/MinecraftDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@
import cn.maxpixel.mcdecompiler.decompiler.IDecompiler;
import cn.maxpixel.mcdecompiler.decompiler.IExternalResourcesDecompiler;
import cn.maxpixel.mcdecompiler.decompiler.ILibRecommendedDecompiler;
import cn.maxpixel.mcdecompiler.util.FileUtil;
import cn.maxpixel.mcdecompiler.util.JarUtil;
import cn.maxpixel.mcdecompiler.util.Utils;
import cn.maxpixel.mcdecompiler.util.VersionManifest;
import cn.maxpixel.mcdecompiler.util.*;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -60,14 +57,13 @@ public class MinecraftDecompiler {
private final Deobfuscator deobfuscator;

static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> FileUtil.deleteDirectoryIfExists(Properties.get(Properties.Key.TEMP_DIR))));
Runtime.getRuntime().addShutdownHook(new Thread(() -> FileUtil.deleteIfExists(Properties.TEMP_DIR)));
}

public MinecraftDecompiler(Options options) {
Path tempPath = Properties.get(Properties.Key.TEMP_DIR);
FileUtil.deleteDirectoryIfExists(tempPath);
FileUtil.deleteIfExists(Properties.TEMP_DIR);
try {
Files.createDirectories(tempPath);
Files.createDirectories(Properties.TEMP_DIR);
} catch (IOException e) {
LOGGER.fatal("Error creating temp directory");
throw Utils.wrapInRuntime(LOGGER.throwing(e));
Expand Down Expand Up @@ -100,57 +96,37 @@ private void downloadJar(String version, Info.SideType type) {
}

public void deobfuscate() {
if(options.shouldDownloadJar()) {
deobfuscate(Properties.getOutputDeobfuscatedJarPath(options.version(), options.type()));
} else {
deobfuscate(Properties.getOutputDeobfuscatedJarPath());
}
}

public void deobfuscate(Path output) {
try {
Path input = options.shouldDownloadJar() ? Properties.getDownloadedMcJarPath(options.version(), options.type()) :
options.inputJar();
deobfuscator.deobfuscate(input, output, options.targetNamespace(), options);
deobfuscator.deobfuscate(input, options.outputJar(), options.targetNamespace(), options);
} catch (IOException e) {
LOGGER.fatal("Error deobfuscating", e);
}
}

public void decompile(Info.DecompilerType decompilerType) {
Path decompileDir = Properties.getOutputDecompiledDirectory(options.version(), options.type()).toAbsolutePath().normalize();
Path outputDeobfuscatedJarPath = Properties.getOutputDeobfuscatedJarPath(options.version(), options.type()).toAbsolutePath().normalize();
if(Files.notExists(outputDeobfuscatedJarPath))
throw new IllegalArgumentException("Please deobfuscate first or run decompile(DecompilerType, Path, Path) method");
decompile(decompilerType, outputDeobfuscatedJarPath, decompileDir);
}

public void decompile(Info.DecompilerType decompilerType, Path inputJar, Path outputDir) {
if(Files.notExists(options.outputJar())) deobfuscate();
LOGGER.info("Decompiling using \"{}\"", decompilerType);
decompile0(Decompilers.get(decompilerType), inputJar, outputDir);
decompile0(Decompilers.get(decompilerType), options.outputJar().toAbsolutePath().normalize(),
options.outputDecompDir().toAbsolutePath().normalize());
}

public void decompileCustomized(String customizedDecompilerName) {
Path decompileDir = Properties.getOutputDecompiledDirectory(options.version(), options.type()).toAbsolutePath().normalize();
Path outputDeobfuscatedJarPath = Properties.getOutputDeobfuscatedJarPath(options.version(), options.type()).toAbsolutePath().normalize();
if(Files.notExists(outputDeobfuscatedJarPath))
throw new IllegalArgumentException("Please deobfuscate first or run decompile(DecompilerType, Path, Path) method");
decompileCustomized(customizedDecompilerName, outputDeobfuscatedJarPath, decompileDir);
}

public void decompileCustomized(String customizedDecompilerName, Path inputJar, Path outputDir) {
if(Files.notExists(options.outputJar())) deobfuscate();
LOGGER.info("Decompiling using customized decompiler \"{}\"", customizedDecompilerName);
decompile0(Decompilers.getCustom(customizedDecompilerName), inputJar, outputDir);
decompile0(Decompilers.getCustom(customizedDecompilerName), options.outputJar().toAbsolutePath().normalize(),
options.outputDecompDir().toAbsolutePath().normalize());
}

private void decompile0(IDecompiler decompiler, Path inputJar, Path outputDir) {
try(FileSystem jarFs = JarUtil.getJarFileSystemProvider().newFileSystem(inputJar, Object2ObjectMaps.emptyMap())) {
FileUtil.deleteDirectoryIfExists(outputDir);
FileUtil.deleteIfExists(outputDir);
Files.createDirectories(outputDir);
Path libDownloadPath = Properties.getDownloadedLibPath().toAbsolutePath().normalize();
FileUtil.ensureDirectoryExist(libDownloadPath);
if(decompiler instanceof IExternalResourcesDecompiler erd)
erd.extractTo(Properties.get(Properties.Key.TEMP_DIR).toAbsolutePath().normalize());
erd.extractTo(Properties.TEMP_DIR.toAbsolutePath().normalize());
if(decompiler instanceof ILibRecommendedDecompiler lrd && options.version() != null)
lrd.downloadLib(libDownloadPath, options.version());
switch (decompiler.getSourceType()) {
Expand Down Expand Up @@ -178,6 +154,8 @@ public static class OptionBuilder {
private boolean includeOthers = true;
private boolean rvn;
private BufferedReader inputMappings;
private Path outputJar;
private Path outputDecompDir;

private Path inputJar;
private boolean reverse;
Expand All @@ -187,6 +165,8 @@ public static class OptionBuilder {
public OptionBuilder(String version, Info.SideType type) {
this.version = Objects.requireNonNull(version, "version cannot be null!");
this.type = Objects.requireNonNull(type, "type cannot be null!");
this.outputJar = Path.of("output", version + "_" + type + "_deobfuscated.jar");
this.outputDecompDir = Path.of("output", version + "_" + type + "_decompiled");
}

public OptionBuilder(Path inputJar) {
Expand All @@ -196,6 +176,8 @@ public OptionBuilder(Path inputJar) {
public OptionBuilder(Path inputJar, boolean reverse) {
this.inputJar = inputJar;
this.reverse = reverse;
this.outputJar = Path.of("output", "deobfuscated.jar");
this.outputDecompDir = Path.of("output", "decompiled");
}

public OptionBuilder libsUsing(String version) {
Expand All @@ -218,16 +200,26 @@ public OptionBuilder withMapping(InputStream inputMappings) {
}

public OptionBuilder withMapping(Reader inputMappings) {
return withMapping(inputMappings instanceof BufferedReader buf ? buf : new BufferedReader(inputMappings));
return withMapping(IOUtil.asBufferedReader(inputMappings, "inputMappings"));
}

public OptionBuilder withMapping(BufferedReader inputMappings) {
this.inputMappings = inputMappings;
this.inputMappings = Objects.requireNonNull(inputMappings, "inputMappings cannot be null");
return this;
}

public OptionBuilder output(Path outputJar) {
this.outputJar = Objects.requireNonNull(outputJar, "outputJar cannot be null");
return this;
}

public OptionBuilder outputDecomp(Path outputDecompDir) {
this.outputDecompDir = Objects.requireNonNull(outputDecompDir, "outputDecompDir cannot be null");
return this;
}

public OptionBuilder targetNamespace(String targetNamespace) {
this.targetNamespace = targetNamespace;
this.targetNamespace = Objects.requireNonNull(targetNamespace, "targetNamespace cannot be null");
return this;
}

Expand Down Expand Up @@ -273,6 +265,16 @@ public Path inputJar() {
return inputJar;
}

@Override
public Path outputJar() {
return outputJar;
}

@Override
public Path outputDecompDir() {
return outputDecompDir;
}

@Override
public boolean reverse() {
return reverse;
Expand Down Expand Up @@ -309,6 +311,10 @@ private boolean shouldDownloadJar() {

Path inputJar();

Path outputJar();

Path outputDecompDir();

@Override
boolean reverse();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,18 @@ public static void main(String[] args) throws Throwable {
"names using JAD style if the input mapping doesn't provide one");
OptionSpecBuilder reverseO = parser.accepts("reverse", "Reverse the input mapping, then use the reversed mapping to " +
"deobfuscate.").availableUnless(sideTypeO);
OptionSpecBuilder dontIncludeOthersO = parser.accepts("dontIncludeOthers", "Drop the resource files in the output jar.");
ArgumentAcceptingOptionSpec<String> targetNamespaceO = parser.accepts("targetNamespace", "The target namespace to remap " +
"to if you are using namespaced mappings(Tiny, Tsrgv2)").availableUnless(sideTypeO).withRequiredArg();
ArgumentAcceptingOptionSpec<Path> inputO = parser.acceptsAll(asList("i", "input"), "The input file. With this option, you must " +
"specify --mappingPath option and musn't specify --version or --side option.").availableUnless(sideTypeO).requiredUnless(sideTypeO)
.withRequiredArg().withValuesConvertedBy(new PathConverter());
ArgumentAcceptingOptionSpec<String> mappingPathO = parser.acceptsAll(asList("m", "map", "mappingPath"), "Mapping file use to " +
"deobfuscate.").requiredUnless(sideTypeO).withRequiredArg();
ArgumentAcceptingOptionSpec<Path> outDirO = parser.acceptsAll(asList("o", "outDir"), "The output directory of deobfuscated jar " +
"and decompiled dir.").withRequiredArg().withValuesConvertedBy(new PathConverter());
ArgumentAcceptingOptionSpec<String> outDeobfNameO = parser.accepts("outDeobfName", "The name of deobfuscated jar in the " +
"outDir which specified by --outDir option. Do NOT add suffix.").withRequiredArg().defaultsTo("deobfuscated");
ArgumentAcceptingOptionSpec<String> outDecomNameO = parser.accepts("outDecomName", "The name of decompiled dir in the " +
"outDir which specified by --outDir option.").withRequiredArg().defaultsTo("decompiled");
ArgumentAcceptingOptionSpec<Path> outputO = parser.acceptsAll(asList("o", "output"), "The remapped file. Includes suffix.")
.withRequiredArg().withValuesConvertedBy(new PathConverter());
ArgumentAcceptingOptionSpec<Path> outputDecompO = parser.accepts("outputDecomp", "The output decompile directory")
.withRequiredArg().withValuesConvertedBy(new PathConverter());
ArgumentAcceptingOptionSpec<Info.DecompilerType> decompileO = parser.acceptsAll(asList("d", "decompile"), "Decompile the " +
"deobfuscated jar. Values are \"FERNFLOWER\", \"OFFICIAL_FERNFLOWER\", \"FORGEFLOWER\", \"CFR\" and \"USER_DEFINED\". Do NOT pass " +
"any arg to this option when \"customDecompilerName\" option is specified.").withOptionalArg().ofType(Info.DecompilerType.class)
Expand Down Expand Up @@ -111,27 +110,28 @@ public URL convert(String value) {
options.valuesOf(customDecompilerJarsO).forEach(LambdaUtil.handleThrowable(
ClassLoaderUtils::appendToClassPath, LambdaUtil::rethrowAsRuntime));

options.valueOfOptional(tempDirO).ifPresent(p -> Properties.put(Properties.Key.TEMP_DIR, p));
options.valueOfOptional(tempDirO).ifPresent(p -> Properties.TEMP_DIR = p);
if(!options.has(sideTypeO)) {
if(!options.has(inputO))
throw new IllegalArgumentException("--input is required when you doesn't specify --side option");
if(!options.has(mappingPathO))
throw new IllegalArgumentException("--mappingPath is required when you doesn't specify --side option");
}
options.valueOfOptional(outDirO).ifPresent(p -> Properties.put(Properties.Key.OUTPUT_DIR, p));
options.valueOfOptional(outDeobfNameO).ifPresent(s -> Properties.put(Properties.Key.OUTPUT_DEOBFUSCATED_NAME, s));
options.valueOfOptional(outDecomNameO).ifPresent(s -> Properties.put(Properties.Key.OUTPUT_DECOMPILED_NAME, s));

MinecraftDecompiler.OptionBuilder builder;
if(options.has(sideTypeO)) {
builder = new MinecraftDecompiler.OptionBuilder(options.valueOf(versionO), options.valueOf(sideTypeO));
if(options.has(mappingPathO)) builder.withMapping(options.valueOf(mappingPathO));
options.valueOfOptional(mappingPathO).ifPresent(builder::withMapping);
} else {
builder = new MinecraftDecompiler.OptionBuilder(options.valueOf(inputO), options.has(reverseO))
.withMapping(options.valueOf(mappingPathO));
if(options.has(versionO)) builder.libsUsing(options.valueOf(versionO));
options.valueOfOptional(versionO).ifPresent(builder::libsUsing);
}
if(options.has(regenVarNameO)) builder.regenerateVariableNames();
if(options.has(dontIncludeOthersO)) builder.doNotIncludeOthers();
options.valueOfOptional(targetNamespaceO).ifPresent(builder::targetNamespace);
options.valueOfOptional(outputO).ifPresent(builder::output);
options.valueOfOptional(outputDecompO).ifPresent(builder::outputDecomp);

MinecraftDecompiler md = new MinecraftDecompiler(builder.build());
md.deobfuscate();
Expand Down
84 changes: 6 additions & 78 deletions src/main/java/cn/maxpixel/mcdecompiler/Properties.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,97 +18,25 @@

package cn.maxpixel.mcdecompiler;

import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;

import java.nio.file.Path;
import java.util.Objects;

public class Properties {
public static class Key<V> {
private final String name;

private Key(String name) {
this.name = Objects.requireNonNull(name);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key<?> key)) return false;
return name.equals(key.name);
}

@Override
public int hashCode() {
return name.hashCode();
}

public static final Key<Path> TEMP_DIR = new Key<>("tempDir");
public static final Key<Path> DOWNLOAD_DIR = new Key<>("downloadDir");

public static final Key<Path> OUTPUT_DIR = new Key<>("outputDir");
public static final Key<String> OUTPUT_DEOBFUSCATED_NAME = new Key<>("outputDeobfuscatedName");
public static final Key<String> OUTPUT_DECOMPILED_NAME = new Key<>("outputDecompiledName");
}

private static final Object2ObjectOpenHashMap<Key<?>, Object> PROPERTIES_MAP = new Object2ObjectOpenHashMap<>();

@SuppressWarnings("unchecked")
public static <V> V put(Key<V> key, V value) {
return (V) PROPERTIES_MAP.put(key, value);
}

@SuppressWarnings("unchecked")
public static <V> V get(Key<V> key) {
return (V) PROPERTIES_MAP.get(key);
}
public static Path TEMP_DIR = Path.of("temp");
public static Path DOWNLOAD_DIR = Path.of("downloads");

static {
// Default values
put(Key.TEMP_DIR, Path.of("temp"));
put(Key.DOWNLOAD_DIR, Path.of("downloads"));
put(Key.OUTPUT_DIR, Path.of("output"));
put(Key.OUTPUT_DEOBFUSCATED_NAME, "deobfuscated");
put(Key.OUTPUT_DECOMPILED_NAME, "decompiled");
}

// Methods have to do with Key.TEMP_DIR
public static Path getTempDecompileClassesPath() {
return get(Key.TEMP_DIR).resolve("decompileClasses");
return TEMP_DIR.resolve("decompileClasses");
}

// Methods have to do with Key.DOWNLOAD_DIR
public static Path getDownloadedLibPath() {
return get(Key.DOWNLOAD_DIR).resolve("libs");
return DOWNLOAD_DIR.resolve("libs");
}

public static Path getDownloadedMcJarPath(String version, Info.SideType type) {
return get(Key.DOWNLOAD_DIR).resolve(version).resolve(type + ".jar");
}

// Methods have to do with Key.OUTPUT_*
public static Path getOutputDecompiledDirectory() {
return get(Key.OUTPUT_DIR).resolve(get(Key.OUTPUT_DECOMPILED_NAME));
}

public static Path getOutputDeobfuscatedJarPath() {
return get(Key.OUTPUT_DIR).resolve(get(Key.OUTPUT_DEOBFUSCATED_NAME) + ".jar");
return DOWNLOAD_DIR.resolve(version).resolve(type + ".jar");
}


// Proguard only -- start
public static Path getDownloadedProguardMappingPath(String version, Info.SideType type) {
return get(Key.DOWNLOAD_DIR).resolve(version).resolve(type + "_mappings.txt");
}

public static Path getOutputDecompiledDirectory(String version, Info.SideType type) {
if(version == null || type == null || !get(Key.OUTPUT_DECOMPILED_NAME).equals("decompiled")) return getOutputDecompiledDirectory();
return get(Key.OUTPUT_DIR).resolve(version + "_" + type + "_decompiled");
}

public static Path getOutputDeobfuscatedJarPath(String version, Info.SideType type) {
if(version == null || type == null || !get(Key.OUTPUT_DEOBFUSCATED_NAME).equals("deobfuscated")) return getOutputDeobfuscatedJarPath();
return get(Key.OUTPUT_DIR).resolve(version + "_" + type + "_deobfuscated.jar");
return DOWNLOAD_DIR.resolve(version).resolve(type + "_mappings.txt");
}
// Proguard only -- end
}
Loading

0 comments on commit bc41991

Please sign in to comment.