diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 94a1296..987e268 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -6,6 +6,14 @@ name: Java CI with Maven on: push: branches: [ master ] + paths-ignore: + - README.md + - LICENSE + - .gitignore + - CODE_OF_CONDUCT.md + - src/main/resources + - src/test/resources + - decompiler jobs: build: diff --git a/src/main/java/cn/maxpixel/mcdecompiler/asm/ClassProcessor.java b/src/main/java/cn/maxpixel/mcdecompiler/asm/ClassProcessor.java index 4dc63d0..1753631 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/asm/ClassProcessor.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/asm/ClassProcessor.java @@ -20,6 +20,7 @@ import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedClassMapping; import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedMethodMapping; +import cn.maxpixel.mcdecompiler.mapping.tsrg.TsrgMethodMapping; import cn.maxpixel.mcdecompiler.reader.AbstractMappingReader; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.objects.*; @@ -98,7 +99,12 @@ private void renameLVT() { .filter(m -> m.getName(fromNamespace).equals(methodNode.name) && m.getUnmappedDescriptor().equals(methodNode.desc)).findAny(); IntArrayList regen = new IntArrayList(); - methodNode.localVariables.forEach(lvn -> methodMapping.map(mm -> mm.getLocalVariableName(lvn.index, targetNamespace)) + methodNode.localVariables.forEach(lvn -> + methodMapping.map(mm -> { + int index = lvn.index; + if(mm instanceof TsrgMethodMapping tmm && !tmm.isStatic) index++; + return mm.getLocalVariableName(index, targetNamespace); + }) .filter(name -> !name.isEmpty() && !name.equals("o"/* tsrg2 empty lvn placeholder */)) .ifPresentOrElse(name -> lvn.name = name, () -> regen.add(lvn.index))); if(rvn) regenerateVariableNames(methodNode, regen); diff --git a/src/main/java/cn/maxpixel/mcdecompiler/decompiler/CFRDecompiler.java b/src/main/java/cn/maxpixel/mcdecompiler/decompiler/CFRDecompiler.java index b476af9..b68e4d0 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/decompiler/CFRDecompiler.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/decompiler/CFRDecompiler.java @@ -55,6 +55,7 @@ public void decompile(Path source, Path target) { PrintStream sysErr = System.err; System.setErr(new PrintStream(new OutputStream() { private static final Logger LOGGER = LogManager.getLogger("CFR"); + @Override public void write(int b) { throw new UnsupportedOperationException(); diff --git a/src/main/java/cn/maxpixel/mcdecompiler/decompiler/FernFlowerDecompiler.java b/src/main/java/cn/maxpixel/mcdecompiler/decompiler/FernFlowerDecompiler.java index dd59916..5adcf35 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/decompiler/FernFlowerDecompiler.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/decompiler/FernFlowerDecompiler.java @@ -18,13 +18,16 @@ package cn.maxpixel.mcdecompiler.decompiler; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import java.io.File; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.nio.file.Path; import java.util.Map; @@ -41,18 +44,32 @@ public SourceType getSourceType() { @Override public void decompile(Path source, Path target) throws IOException { checkArgs(source, target); - Map options = new Object2ObjectOpenHashMap<>(); - options.put("log", "TRACE"); - options.put("dgs", "1"); - options.put("hdc", "0"); - options.put("asc", "1"); - options.put("udv", "0"); - options.put("rsy", "1"); + Map options = Map.of( + "log", "TRACE", + "dgs", "1", + "asc", "1", + "rsy", "1" + ); ConsoleDecompiler decompiler = new AccessibleConsoleDecompiler(target.toFile(), options, new PrintStreamLogger(System.out)); decompiler.addSource(source.toFile()); -// List libs = listLibs(); +// ObjectList libs = listLibs(); // for(int index = 0; index < libs.size(); index++) decompiler.addLibrary(new File(libs.get(index))); + PrintStream sysOut = System.out; + System.setOut(new PrintStream(new OutputStream() { + private static final Logger LOGGER = LogManager.getLogger("FernFlower"); + + @Override + public void write(int b) { + throw new UnsupportedOperationException(); + } + + @Override + public void write(byte[] b, int off, int len) { + LOGGER.debug(new String(b, off, len).stripTrailing()); + } + })); decompiler.decompileContext(); + System.setOut(sysOut); } private static class AccessibleConsoleDecompiler extends ConsoleDecompiler { diff --git a/src/main/java/cn/maxpixel/mcdecompiler/decompiler/SpigotFernFlowerDecompiler.java b/src/main/java/cn/maxpixel/mcdecompiler/decompiler/SpigotFernFlowerDecompiler.java index 90936d0..c219454 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/decompiler/SpigotFernFlowerDecompiler.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/decompiler/SpigotFernFlowerDecompiler.java @@ -20,6 +20,7 @@ import cn.maxpixel.mcdecompiler.util.Utils; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; import java.io.IOException; import java.nio.file.Files; @@ -27,9 +28,7 @@ import java.util.Arrays; import java.util.Objects; -// Do not extend AbstractLibRecommendedDecompiler because this decompiler cannot read some of the libraries successfully -// TODO: Make SpigotFernFlowerDecompiler read all libraries successfully -public class SpigotFernFlowerDecompiler/* extends AbstractLibRecommendedDecompiler */implements IExternalResourcesDecompiler { +public class SpigotFernFlowerDecompiler extends AbstractLibRecommendedDecompiler implements IExternalResourcesDecompiler { private Path decompilerJarPath; SpigotFernFlowerDecompiler() {} @@ -42,9 +41,9 @@ public SourceType getSourceType() { public void decompile(Path source, Path target) throws IOException { checkArgs(source, target); ObjectArrayList args = new ObjectArrayList<>(); - args.addAll(Arrays.asList("java", "-jar", decompilerJarPath.toString(), "-log=TRACE", "-dgs=1", "-hdc=0", "-asc=1", "-udv=0", "-rsy=1", "-aoa=1")); -// List libs = listLibs(); -// for(int i = 0; i < 26; i++) args.add("-e=\"" + libs.get(i) + "\""); + args.addAll(Arrays.asList("java", "-jar", decompilerJarPath.toString(), "-log=TRACE", "-dgs=1", "-asc=1", "-udv=0", "-ump=0", "-rsy=1", "-aoa=1")); + ObjectList libs = listLibs(); + for(int i = 0; i < 26; i++) args.add("-e=\"" + libs.get(i) + "\""); args.add(source.toString()); args.add(target.toString()); Process process = Runtime.getRuntime().exec(args.toArray(new String[0])); diff --git a/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMapping.java b/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMapping.java index eee4b84..ff574c5 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMapping.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMapping.java @@ -20,6 +20,8 @@ import cn.maxpixel.mcdecompiler.mapping.AbstractMapping; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectSets; import java.util.Map; @@ -58,6 +60,10 @@ public NamespacedMapping(String[] namespaces, String[] names, int nameStart) { } public NamespacedMapping() {} + public ObjectSet getNamespaces() { + return ObjectSets.unmodifiable(names.keySet()); + } + public void setName(String namespace, String name) { names.put(namespace, name); } diff --git a/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMethodMapping.java b/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMethodMapping.java index 3496a6f..c194a13 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMethodMapping.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/mapping/namespaced/NamespacedMethodMapping.java @@ -21,6 +21,8 @@ import cn.maxpixel.mcdecompiler.mapping.components.Descriptor; import cn.maxpixel.mcdecompiler.mapping.components.Owned; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSets; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -95,6 +97,14 @@ public void setLocalVariableName(int index, String[] namespaces, String[] names, } } + public IntSet getLocalVariableIndexes() { + return IntSets.unmodifiable(lvt.keySet()); + } + + public Object2ObjectMap getLocalVariableNames(int index) { + return Object2ObjectMaps.unmodifiable(lvt.get(index)); + } + @Override public String getUnmappedDescriptor() { return unmappedDescriptor; diff --git a/src/main/java/cn/maxpixel/mcdecompiler/reader/CsrgMappingReader.java b/src/main/java/cn/maxpixel/mcdecompiler/reader/CsrgMappingReader.java index f276bd5..94fba61 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/reader/CsrgMappingReader.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/reader/CsrgMappingReader.java @@ -47,6 +47,7 @@ public CsrgMappingReader(String path) throws FileNotFoundException { } private final CsrgMappingProcessor PROCESSOR = new CsrgMappingProcessor(); + @Override public CsrgMappingProcessor getProcessor() { return PROCESSOR; @@ -136,7 +137,7 @@ public PairedMapping processPackage(String line) { } private PairedMapping processPackage(String[] line) { - return new PairedMapping(line[0].substring(0, line[0].length() - 1), line[1]); + return new PairedMapping(line[0].substring(0, line[0].length() - 1), line[1].substring(0, line[1].length() - 1)); } } } \ No newline at end of file diff --git a/src/main/java/cn/maxpixel/mcdecompiler/reader/ProguardMappingReader.java b/src/main/java/cn/maxpixel/mcdecompiler/reader/ProguardMappingReader.java index e819eb3..958e3b4 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/reader/ProguardMappingReader.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/reader/ProguardMappingReader.java @@ -91,6 +91,7 @@ private static BufferedReader downloadMapping(String version, Info.SideType type } private final ProguardMappingProcessor PROCESSOR = new ProguardMappingProcessor(); + @Override public ProguardMappingProcessor getProcessor() { return PROCESSOR; diff --git a/src/main/java/cn/maxpixel/mcdecompiler/reader/SrgMappingReader.java b/src/main/java/cn/maxpixel/mcdecompiler/reader/SrgMappingReader.java index 3b39565..49fb28c 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/reader/SrgMappingReader.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/reader/SrgMappingReader.java @@ -47,6 +47,7 @@ public SrgMappingReader(String path) throws FileNotFoundException { } private final SrgMappingProcessor PROCESSOR = new SrgMappingProcessor(); + @Override public SrgMappingProcessor getProcessor() { return PROCESSOR; diff --git a/src/main/java/cn/maxpixel/mcdecompiler/reader/TinyMappingReader.java b/src/main/java/cn/maxpixel/mcdecompiler/reader/TinyMappingReader.java index aae5127..872966c 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/reader/TinyMappingReader.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/reader/TinyMappingReader.java @@ -62,11 +62,14 @@ public TinyMappingReader(String path) throws FileNotFoundException, NullPointerE private final TinyV1MappingProcessor V1_PROCESSOR = new TinyV1MappingProcessor(); private final TinyV2MappingProcessor V2_PROCESSOR = new TinyV2MappingProcessor(); + @Override public NamespacedMappingProcessor getProcessor() { - if(version == 2) return V2_PROCESSOR; - else if(version == 1) return V1_PROCESSOR; - else throw new IllegalArgumentException("Unknown tiny mapping version"); + return switch(version) { + case 1 -> V1_PROCESSOR; + case 2 -> V2_PROCESSOR; + default -> throw new UnsupportedOperationException("Unknown tiny mapping version"); + }; } private class TinyV1MappingProcessor implements NamespacedMappingProcessor { diff --git a/src/main/java/cn/maxpixel/mcdecompiler/reader/TsrgMappingReader.java b/src/main/java/cn/maxpixel/mcdecompiler/reader/TsrgMappingReader.java index 5cb4acd..5d4e9a6 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/reader/TsrgMappingReader.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/reader/TsrgMappingReader.java @@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicReference; public class TsrgMappingReader extends AbstractMappingReader { - public final int version = lines.get(0).startsWith("tsrg2") ? 2 : -1; + public final int version = lines.get(0).startsWith("tsrg2") ? 2 : 1; public TsrgMappingReader(BufferedReader reader) { super(reader); @@ -57,14 +57,19 @@ public TsrgMappingReader(String path) throws FileNotFoundException, NullPointerE super(path); } - private final TsrgMappingProcessor PROCESSOR = new TsrgMappingProcessor(); + private final TsrgV1MappingProcessor V1_PROCESSOR = new TsrgV1MappingProcessor(); private final TsrgV2MappingProcessor V2_PROCESSOR = new TsrgV2MappingProcessor(); + @Override public MappingProcessor getProcessor() { - return version == 2 ? V2_PROCESSOR : PROCESSOR; + return switch(version) { + case 1 -> V1_PROCESSOR; + case 2 -> V2_PROCESSOR; + default -> throw new UnsupportedOperationException("Unknown tsrg mapping version"); + }; } - private class TsrgMappingProcessor implements PairedMappingProcessor, PackageMappingProcessor { + private class TsrgV1MappingProcessor implements PairedMappingProcessor, PackageMappingProcessor { private final ObjectArrayList packages = new ObjectArrayList<>(); private final ObjectArrayList mappings = new ObjectArrayList<>(5000); @Override @@ -121,7 +126,7 @@ public ObjectList getPackages() { @Override public PairedMapping processPackage(String line) { String[] strings = line.split(" "); - return new PairedMapping(strings[0].substring(0, strings[0].length() - 1), strings[1]); + return new PairedMapping(strings[0].substring(0, strings[0].length() - 1), strings[1].substring(0, strings[1].length() - 1)); } } @@ -215,7 +220,9 @@ public ObjectList getPackages() { @Override public NamespacedMapping processPackage(String line) { String[] split = line.split(" "); - split[0] = split[0].substring(0, split[0].length() - 1); + for(int i = 0; i < split.length; i++) { + split[i] = split[i].substring(0, split[i].length() - 1); + } return new NamespacedMapping(namespaces, split); } } diff --git a/src/main/java/cn/maxpixel/mcdecompiler/writer/AbstractMappingWriter.java b/src/main/java/cn/maxpixel/mcdecompiler/writer/AbstractMappingWriter.java index 8d0183f..6daed21 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/writer/AbstractMappingWriter.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/writer/AbstractMappingWriter.java @@ -124,7 +124,7 @@ public final void writeNamespacedPackage(NamespacedMapping pkg) { synchronized(buf) { buf.add(((PackageMappingGenerator) getGenerator()).generatePackage(pkg)); } - } else throw new IllegalArgumentException("Use writePairedPackage(s)"); + } else throw new UnsupportedOperationException("Use writePairedPackage(s)"); } /** @@ -182,7 +182,7 @@ public final void writePairedMapping(PairedClassMapping pcm) { } finally { if(needLock) lock.unlock(); } - } else throw new IllegalArgumentException("Use writeNamespacedMapping(s)"); + } else throw new UnsupportedOperationException("Use writeNamespacedMapping(s)"); } public final void writePairedMapping(NamespacedClassMapping ncm, String unmapped, String mapped) { @@ -233,7 +233,7 @@ public final void writeNamespacedMapping(NamespacedClassMapping ncm) { } finally { if(needLock) lock.unlock(); } - } else throw new IllegalArgumentException("Use writePairedMapping(s)"); + } else throw new UnsupportedOperationException("Use writePairedMapping(s)"); } public final void writeNamespacedMapping(PairedClassMapping pcm, String unmapped, String mapped) { @@ -274,6 +274,8 @@ public final void writeNamespacedMappings(Collection mapping public final void writeTo(OutputStream os) throws IOException { synchronized(buf) { + String header = getHeader(); + if(!header.isEmpty()) os.write(header.getBytes(StandardCharsets.UTF_8)); os.write(String.join("\n", buf).getBytes(StandardCharsets.UTF_8)); buf.clear(); } @@ -281,6 +283,8 @@ public final void writeTo(OutputStream os) throws IOException { public final void writeTo(Writer writer) throws IOException { synchronized(buf) { + String header = getHeader(); + if(!header.isEmpty()) writer.write(header); writer.write(String.join("\n", buf)); buf.clear(); } @@ -288,6 +292,8 @@ public final void writeTo(Writer writer) throws IOException { public final void writeTo(WritableByteChannel os) throws IOException { synchronized(buf) { + String header = getHeader(); + if(!header.isEmpty()) os.write(ByteBuffer.wrap(header.concat("\n").getBytes(StandardCharsets.UTF_8))); os.write(ByteBuffer.wrap(String.join("\n", buf).getBytes(StandardCharsets.UTF_8))); buf.clear(); } @@ -297,6 +303,10 @@ public final void writeTo(WritableByteChannel os) throws IOException { protected abstract boolean needLock(); + protected String getHeader() { + return ""; + } + public interface MappingGenerator { default boolean isPaired() { return this instanceof PairedMappingGenerator; diff --git a/src/main/java/cn/maxpixel/mcdecompiler/writer/CsrgMappingWriter.java b/src/main/java/cn/maxpixel/mcdecompiler/writer/CsrgMappingWriter.java index 0297efe..7c863ab 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/writer/CsrgMappingWriter.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/writer/CsrgMappingWriter.java @@ -36,6 +36,7 @@ public CsrgMappingWriter(MappingRemapper remapper) { } private final CsrgMappingGenerator GENERATOR = new CsrgMappingGenerator(); + @Override protected CsrgMappingGenerator getGenerator() { return GENERATOR; @@ -71,7 +72,7 @@ public String generateField(PairedFieldMapping mapping) { @Override public String generatePackage(AbstractMapping mapping) { if(mapping instanceof PairedMapping paired && paired.getClass() == PairedMapping.class) { - return paired.getUnmappedName() + '/' + ' ' + paired.getMappedName(); + return paired.getUnmappedName() + '/' + ' ' + paired.getMappedName() + '/'; } else throw new UnsupportedOperationException(); } } diff --git a/src/main/java/cn/maxpixel/mcdecompiler/writer/ProguardMappingWriter.java b/src/main/java/cn/maxpixel/mcdecompiler/writer/ProguardMappingWriter.java index ffd203f..ffe04a6 100644 --- a/src/main/java/cn/maxpixel/mcdecompiler/writer/ProguardMappingWriter.java +++ b/src/main/java/cn/maxpixel/mcdecompiler/writer/ProguardMappingWriter.java @@ -40,6 +40,7 @@ public ProguardMappingWriter(MappingRemapper remapper) { } private final ProguardMappingGenerator GENERATOR = new ProguardMappingGenerator(); + @Override protected ProguardMappingGenerator getGenerator() { return GENERATOR; diff --git a/src/main/java/cn/maxpixel/mcdecompiler/writer/SrgMappingWriter.java b/src/main/java/cn/maxpixel/mcdecompiler/writer/SrgMappingWriter.java new file mode 100644 index 0000000..bba38b8 --- /dev/null +++ b/src/main/java/cn/maxpixel/mcdecompiler/writer/SrgMappingWriter.java @@ -0,0 +1,87 @@ +/* + * MinecraftDecompiler. A tool/library to deobfuscate and decompile Minecraft. + * Copyright (C) 2019-2021 MaxPixelStudios + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package cn.maxpixel.mcdecompiler.writer; + +import cn.maxpixel.mcdecompiler.asm.MappingRemapper; +import cn.maxpixel.mcdecompiler.mapping.AbstractMapping; +import cn.maxpixel.mcdecompiler.mapping.components.Descriptor; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedClassMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedFieldMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedMethodMapping; + +public class SrgMappingWriter extends AbstractMappingWriter { + public SrgMappingWriter() { + super(); + } + + public SrgMappingWriter(MappingRemapper remapper) { + super(remapper); + } + + private final SrgMappingGenerator GENERATOR = new SrgMappingGenerator(); + + @Override + protected SrgMappingGenerator getGenerator() { + return GENERATOR; + } + + @Override + protected boolean needLock() { + return false; + } + + private class SrgMappingGenerator implements PairedMappingGenerator, PackageMappingGenerator { + @Override + public String generateClass(PairedClassMapping mapping) { + return "CL: " + mapping.getUnmappedName() + ' ' + mapping.getMappedName(); + } + + @Override + public String generateMethod(PairedMethodMapping mapping) { + if(notDescImpl(mapping)) throw new UnsupportedOperationException(); + String unmappedDesc = null; + String mappedDesc = null; + if(mapping instanceof Descriptor desc) unmappedDesc = desc.getUnmappedDescriptor(); + if(mapping instanceof Descriptor.Mapped desc) mappedDesc = desc.getMappedDescriptor(); + if(unmappedDesc == null) { + if(remapper != null) unmappedDesc = remapper.getUnmappedDescByMappedDesc(mappedDesc); + else throw new UnsupportedOperationException(); + } else if(mappedDesc == null) { + if(remapper != null) mappedDesc = remapper.getMappedDescByUnmappedDesc(unmappedDesc); + else throw new UnsupportedOperationException(); + } + return "MD: " + mapping.getOwner().getUnmappedName() + '/' + mapping.getUnmappedName() + ' ' + unmappedDesc + + ' ' + mapping.getOwner().getMappedName() + '/' + mapping.getMappedName() + ' ' + mappedDesc; + } + + @Override + public String generateField(PairedFieldMapping mapping) { + return "FD: " + mapping.getOwner().getUnmappedName() + '/' + mapping.getUnmappedName() + ' ' + + mapping.getOwner().getMappedName() + '/' + mapping.getMappedName(); + } + + @Override + public String generatePackage(AbstractMapping mapping) { + if(mapping instanceof PairedMapping paired && paired.getClass() == PairedMapping.class) { + return "PK: " + paired.getUnmappedName() + ' ' + paired.getMappedName(); + } else throw new UnsupportedOperationException(); + } + } +} \ No newline at end of file diff --git a/src/main/java/cn/maxpixel/mcdecompiler/writer/TinyMappingWriter.java b/src/main/java/cn/maxpixel/mcdecompiler/writer/TinyMappingWriter.java new file mode 100644 index 0000000..0efdcc7 --- /dev/null +++ b/src/main/java/cn/maxpixel/mcdecompiler/writer/TinyMappingWriter.java @@ -0,0 +1,176 @@ +/* + * MinecraftDecompiler. A tool/library to deobfuscate and decompile Minecraft. + * Copyright (C) 2019-2021 MaxPixelStudios + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package cn.maxpixel.mcdecompiler.writer; + +import cn.maxpixel.mcdecompiler.asm.MappingRemapper; +import cn.maxpixel.mcdecompiler.mapping.components.Descriptor; +import cn.maxpixel.mcdecompiler.mapping.components.Documented; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedClassMapping; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedFieldMapping; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedMethodMapping; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; + +import java.util.Objects; +import java.util.StringJoiner; + +public class TinyMappingWriter extends AbstractMappingWriter { + public final int version; + private final ObjectImmutableList namespaces; + + public TinyMappingWriter(int version, String... namespaces) { + super(); + this.version = version; + this.namespaces = new ObjectImmutableList<>(namespaces); + } + + public TinyMappingWriter(MappingRemapper remapper, int version, String... namespaces) { + super(remapper); + this.version = version; + this.namespaces = new ObjectImmutableList<>(namespaces); + } + + private final TinyV1MappingGenerator V1_GENERATOR = new TinyV1MappingGenerator(); + private final TinyV2MappingGenerator V2_GENERATOR = new TinyV2MappingGenerator(); + + @Override + protected MappingGenerator getGenerator() { + return switch(version) { + case 1 -> V1_GENERATOR; + case 2 -> V2_GENERATOR; + default -> throw new UnsupportedOperationException("Unknown tiny mapping version"); + }; + } + + @Override + protected boolean needLock() { + return switch(version) { + case 1 -> false; + case 2 -> true; + default -> throw new UnsupportedOperationException("Unknown tiny mapping version"); + }; + } + + @Override + protected String getHeader() { + return switch(version) { + case 1 -> { + StringJoiner joiner = new StringJoiner("\t"); + joiner.add("v1"); + namespaces.forEach(joiner::add); + yield joiner.toString(); + } + case 2 -> { + StringJoiner joiner = new StringJoiner("\t"); + joiner.add("tiny"); + joiner.add("2"); + joiner.add("0"); + namespaces.forEach(joiner::add); + yield joiner.toString(); + } + default -> throw new UnsupportedOperationException("Unknown tiny mapping version"); + }; + } + + private class TinyV1MappingGenerator implements NamespacedMappingGenerator { + @Override + public String generateClass(NamespacedClassMapping mapping) { + StringJoiner joiner = new StringJoiner("\t"); + joiner.add("CLASS"); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + return joiner.toString(); + } + + @Override + public String generateMethod(NamespacedMethodMapping mapping) { + StringJoiner joiner = new StringJoiner("\t"); + joiner.add("METHOD"); + joiner.add(mapping.getOwner().getName(namespaces.get(0))); + joiner.add(mapping.getUnmappedDescriptor()); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + return joiner.toString(); + } + + @Override + public String generateField(NamespacedFieldMapping mapping) { + StringJoiner joiner = new StringJoiner("\t"); + joiner.add("FIELD"); + joiner.add(mapping.getOwner().getName(namespaces.get(0))); + if(!(mapping instanceof Descriptor desc)) throw new UnsupportedOperationException(); + joiner.add(desc.getUnmappedDescriptor()); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + return joiner.toString(); + } + } + + private class TinyV2MappingGenerator implements NamespacedMappingGenerator { + @Override + public String generateClass(NamespacedClassMapping mapping) { + StringJoiner joiner = new StringJoiner("\t"); + joiner.add("c"); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + if(mapping instanceof Documented doc) return joiner + "\n\tc\t" + doc.getDoc(); + return joiner.toString(); + } + + @Override + public String generateMethod(NamespacedMethodMapping mapping) { + StringJoiner joiner = new StringJoiner("\t", "\t", ""); + joiner.add("m"); + joiner.add(mapping.getUnmappedDescriptor()); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + StringBuilder builder = new StringBuilder(joiner.toString()); + if(mapping instanceof Documented doc) builder.append("\n\t\tc\t").append(doc.getDoc()); + IntSet indexes = mapping.getLocalVariableIndexes(); + if(!indexes.isEmpty()) { + indexes.forEach(index -> { + StringJoiner sj = new StringJoiner("\t", "\t\t", ""); + sj.add("p"); + sj.add(Integer.toString(index)); + Object2ObjectMap names = mapping.getLocalVariableNames(index); + namespaces.forEach(namespace -> sj.add(names.getOrDefault(namespace, ""))); + builder.append('\n').append(sj); + if(mapping instanceof Documented.LocalVariable dlv) { + String doc = dlv.getLocalVariableDoc(index); + if(doc != null && !doc.isEmpty()) builder.append("\n\t\t\tc\t").append(doc); + } + }); + } + return builder.toString(); + } + + @Override + public String generateField(NamespacedFieldMapping mapping) { + StringJoiner joiner = new StringJoiner("\t", "\t", ""); + joiner.add("f"); + if(!(mapping instanceof Descriptor desc)) throw new UnsupportedOperationException(); + joiner.add(desc.getUnmappedDescriptor()); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + if(mapping instanceof Documented doc) return joiner + "\n\tc\t" + doc.getDoc(); + return joiner.toString(); + } + } +} \ No newline at end of file diff --git a/src/main/java/cn/maxpixel/mcdecompiler/writer/TsrgMappingWriter.java b/src/main/java/cn/maxpixel/mcdecompiler/writer/TsrgMappingWriter.java new file mode 100644 index 0000000..9569d89 --- /dev/null +++ b/src/main/java/cn/maxpixel/mcdecompiler/writer/TsrgMappingWriter.java @@ -0,0 +1,177 @@ +/* + * MinecraftDecompiler. A tool/library to deobfuscate and decompile Minecraft. + * Copyright (C) 2019-2021 MaxPixelStudios + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package cn.maxpixel.mcdecompiler.writer; + +import cn.maxpixel.mcdecompiler.asm.MappingRemapper; +import cn.maxpixel.mcdecompiler.mapping.AbstractMapping; +import cn.maxpixel.mcdecompiler.mapping.components.Descriptor; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedClassMapping; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedFieldMapping; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedMapping; +import cn.maxpixel.mcdecompiler.mapping.namespaced.NamespacedMethodMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedClassMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedFieldMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedMapping; +import cn.maxpixel.mcdecompiler.mapping.paired.PairedMethodMapping; +import cn.maxpixel.mcdecompiler.mapping.tsrg.TsrgMethodMapping; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; + +import java.util.Objects; +import java.util.StringJoiner; + +public class TsrgMappingWriter extends AbstractMappingWriter { + public final int version; + private ObjectImmutableList namespaces; + + public TsrgMappingWriter() { + super(); + this.version = 1; + } + + public TsrgMappingWriter(String... namespaces) { + super(); + this.version = 2; + this.namespaces = new ObjectImmutableList<>(namespaces); + } + + public TsrgMappingWriter(MappingRemapper remapper) { + super(remapper); + this.version = 1; + } + + public TsrgMappingWriter(MappingRemapper remapper, String... namespaces) { + super(remapper); + this.version = 2; + this.namespaces = new ObjectImmutableList<>(namespaces); + } + + private final TsrgV1MappingGenerator V1_GENERATOR = new TsrgV1MappingGenerator(); + private final TsrgV2MappingGenerator V2_GENERATOR = new TsrgV2MappingGenerator(); + + @Override + protected MappingGenerator getGenerator() { + return switch(version) { + case 1 -> V1_GENERATOR; + case 2 -> V2_GENERATOR; + default -> throw new UnsupportedOperationException("Unknown tsrg mapping version"); + }; + } + + @Override + protected boolean needLock() { + return true; + } + + @Override + protected String getHeader() { + return switch(version) { + case 1 -> super.getHeader(); + case 2 -> { + StringJoiner joiner = new StringJoiner(" "); + joiner.add("tsrg2"); + namespaces.forEach(joiner::add); + yield joiner.toString(); + } + default -> throw new UnsupportedOperationException("Unknown tsrg mapping version"); + }; + } + + private class TsrgV1MappingGenerator implements PairedMappingGenerator, PackageMappingGenerator { + @Override + public String generateClass(PairedClassMapping mapping) { + return mapping.getUnmappedName() + ' ' + mapping.getMappedName(); + } + + @Override + public String generateMethod(PairedMethodMapping mapping) { + if(notDescImpl(mapping)) throw new UnsupportedOperationException(); + String unmappedDesc; + if(mapping instanceof Descriptor desc) unmappedDesc = desc.getUnmappedDescriptor(); + else if(remapper != null) unmappedDesc = remapper.getUnmappedDescByMappedDesc(mapping.asMappedDescriptor().getMappedDescriptor()); + else throw new UnsupportedOperationException(); + return '\t' + mapping.getUnmappedName() + ' ' + unmappedDesc + ' ' + mapping.getMappedName(); + } + + @Override + public String generateField(PairedFieldMapping mapping) { + return '\t' + mapping.getUnmappedName() + ' ' + mapping.getMappedName(); + } + + @Override + public String generatePackage(AbstractMapping mapping) { + if(mapping instanceof PairedMapping paired && paired.getClass() == PairedMapping.class) { + return paired.getUnmappedName() + '/' + ' ' + paired.getMappedName() + '/'; + } else throw new UnsupportedOperationException(); + } + } + + private class TsrgV2MappingGenerator implements NamespacedMappingGenerator, PackageMappingGenerator { + @Override + public String generateClass(NamespacedClassMapping mapping) { + StringJoiner joiner = new StringJoiner(" "); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + return joiner.toString(); + } + + @Override + public String generateMethod(NamespacedMethodMapping mapping) { + StringJoiner joiner = new StringJoiner(" ", "\t", ""); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + StringBuilder builder = new StringBuilder(joiner.toString()); + builder.insert(builder.indexOf(" "), ' ' + mapping.getUnmappedDescriptor()); + if(mapping instanceof TsrgMethodMapping tsrg && tsrg.isStatic) builder.append("\n\t\tstatic"); + IntSet indexes = mapping.getLocalVariableIndexes(); + if(!indexes.isEmpty()) { + indexes.forEach(index -> { + StringJoiner sj = new StringJoiner(" "); + sj.add(Integer.toString(index)); + mapping.getLocalVariableNames(index).values().forEach(sj::add); + builder.append('\n').append('\t').append('\t').append(sj); + }); + } + return builder.toString(); + } + + @Override + public String generateField(NamespacedFieldMapping mapping) { + StringJoiner joiner = new StringJoiner(" ", "\t", ""); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(mapping.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + String s = joiner.toString(); + if(mapping instanceof Descriptor desc) { + int insert = s.indexOf(' '); + s = s.substring(0, insert) + ' ' + desc.getUnmappedDescriptor() + s.substring(insert); + } + return s; + } + + @Override + public String generatePackage(AbstractMapping mapping) { + if(mapping instanceof NamespacedMapping namespaced && namespaced.getClass() == NamespacedMapping.class) { + StringJoiner joiner = new StringJoiner("/ ", "", "/"); + namespaces.forEach(namespace -> joiner.add(Objects.requireNonNull(namespaced.getName(namespace), + () -> "The provided mapping doesn't have expected namespace " + namespace))); + return joiner.toString(); + } else throw new UnsupportedOperationException(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/fernflower.jar b/src/main/resources/fernflower.jar index 237243c..e9ecac4 100644 Binary files a/src/main/resources/fernflower.jar and b/src/main/resources/fernflower.jar differ