Skip to content

Commit 9fc76ba

Browse files
Add Paragraph Delimited Mappings Extended (#171)
* Update MappingGenerators.java * Update MappingProcessors.java * Update MappingFormats.java * Update DefaultDetectionUnit.java * Update MappingProcessorTest.java * Add files via upload * Update README.md * Update DefaultDetectionUnit.java * Update MappingProcessors.java * Update README.md * Fix formatting issues * Fix formatting issues * Improve class section * Improve Var section * Improve Def section * Remove unused argument * Improve Param section Some optimizations * Optimize * Merge loops * Do nothing * Adjust code style Full class name by default * Support processing/generating Include/Incluir and AccessFlag/BanderaDeAcceso * misc --------- Co-authored-by: XiaoPangxie732 <[email protected]>
1 parent 9c793b0 commit 9fc76ba

File tree

13 files changed

+107207
-10
lines changed

13 files changed

+107207
-10
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Progress: View [the project](https://github.com/orgs/MaxPixelStudios/projects/1)
1010
For usage and more information, visit [the wiki](https://github.com/MaxPixelStudios/MinecraftDecompiler/wiki)(Currently in progress)
1111

1212
## Features
13-
1. Deobfuscate Minecraft using Proguard(Official) mappings, SRG, TSRG, CSRG and Tiny mappings
13+
1. Deobfuscate Minecraft using Proguard(Official) mappings, SRG, TSRG, CSRG, Tiny, and ParagraphDelimitedMappingsExtended mappings
1414
2. Decompile Minecraft using CFR/Fernflower/ForgeFlower/[Any Decompiler You Like](https://github.com/MaxPixelStudios/MinecraftDecompiler/wiki#tutorials-about-decompiler)
1515
3. Regenerate local variable names in JAD style
1616
4. Mapping API

modules/common/src/main/java/cn/maxpixel/mcdecompiler/common/util/Utils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public static RuntimeException wrapInRuntime(Throwable e) {
2929

3030
public static <I, O, E extends Throwable> O[] mapArray(I[] input, IntFunction<O[]> outputGenerator,
3131
LambdaUtil.Function_WithThrowable<I, O, E> func) throws E {
32-
Objects.requireNonNull(input);
32+
Objects.requireNonNull(input);// TODO: Do we need these 2 checks?
3333
Objects.requireNonNull(outputGenerator);
3434
Objects.requireNonNull(func);
3535
O[] output = outputGenerator.apply(input.length);

modules/mapping-api/src/main/java/cn/maxpixel/mcdecompiler/mapping/component/Documented.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ public class Documented implements Component {
3737
*/
3838
public final ObjectArrayList<@NotNull String> contents = new ObjectArrayList<>();
3939

40+
/**
41+
* No-arg constructor
42+
*/
43+
public Documented() {}
44+
45+
/**
46+
* Constructor
47+
* @param contentString The content string which will be set by {@link #setContentString(String)}
48+
*/
49+
public Documented(String contentString) {
50+
setContentString(contentString);
51+
}
52+
4053
/**
4154
* Gets the contents
4255
* @return The contents
@@ -55,7 +68,7 @@ public class Documented implements Component {
5568
}
5669

5770
/**
58-
* Breaks the string into lines and sets them as contents
71+
* Breaks the string into lines and sets them as contents. An empty string will simply be ignored
5972
* @param content the string
6073
*/
6174
public void setContentString(@NotNull String content) {

modules/mapping-api/src/main/java/cn/maxpixel/mcdecompiler/mapping/format/MappingFormats.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,25 @@ public char getCommentChar() {
156156
}
157157
};
158158

159+
public static final MappingFormat.Classified<PairedMapping> PDME = new MappingFormat.Classified<>() {
160+
@Override
161+
public @NotNull String getName() {
162+
return "pdme";
163+
}
164+
165+
@Override
166+
public @NotNull MappingProcessor.Classified<PairedMapping> getProcessor() {
167+
return MappingProcessors.PDME;
168+
}
169+
170+
@Override
171+
public @NotNull MappingGenerator.Classified<PairedMapping> getGenerator() {
172+
return MappingGenerators.PDME;
173+
}
174+
};
175+
159176
private static final Object2ObjectOpenHashMap<String, MappingFormat<?, ?>> MAPPING_FORMATS = new Object2ObjectOpenHashMap<>();
177+
160178
static {
161179
registerMappingFormat(SRG);
162180
registerMappingFormat(CSRG);
@@ -165,6 +183,7 @@ public char getCommentChar() {
165183
registerMappingFormat(PROGUARD);
166184
registerMappingFormat(TINY_V1);
167185
registerMappingFormat(TINY_V2);
186+
registerMappingFormat(PDME);
168187
}
169188

170189
public static boolean registerMappingFormat(MappingFormat<?, ?> format) {

modules/mapping-api/src/main/java/cn/maxpixel/mcdecompiler/mapping/generator/MappingGenerators.java

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package cn.maxpixel.mcdecompiler.mapping.generator;
2020

2121
import cn.maxpixel.mcdecompiler.common.util.NamingUtil;
22+
import cn.maxpixel.mcdecompiler.common.util.Utils;
2223
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
2324
import cn.maxpixel.mcdecompiler.mapping.PairedMapping;
2425
import cn.maxpixel.mcdecompiler.mapping.collection.ClassMapping;
@@ -27,12 +28,17 @@
2728
import cn.maxpixel.mcdecompiler.mapping.format.MappingFormat;
2829
import cn.maxpixel.mcdecompiler.mapping.format.MappingFormats;
2930
import cn.maxpixel.mcdecompiler.mapping.remapper.ClassifiedMappingRemapper;
31+
import cn.maxpixel.mcdecompiler.mapping.trait.AccessTransformationTrait;
32+
import cn.maxpixel.mcdecompiler.mapping.trait.InheritanceTrait;
3033
import cn.maxpixel.mcdecompiler.mapping.trait.NamespacedTrait;
3134
import cn.maxpixel.mcdecompiler.mapping.util.MappingUtil;
3235
import cn.maxpixel.mcdecompiler.mapping.util.TinyUtil;
36+
import it.unimi.dsi.fastutil.ints.IntIterator;
3337
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
3438
import it.unimi.dsi.fastutil.objects.ObjectList;
3539

40+
import java.util.Locale;
41+
3642
public interface MappingGenerators {
3743
MappingGenerator.Classified<PairedMapping> SRG = new MappingGenerator.Classified<>() {
3844
@Override
@@ -377,4 +383,91 @@ public ObjectList<String> generate(ClassifiedMapping<NamespacedMapping> mappings
377383
return lines;
378384
}
379385
};
386+
387+
MappingGenerator.Classified<PairedMapping> PDME = new MappingGenerator.Classified<>() {
388+
private static final String PARA = "¶";
389+
private static final String NIL = "nil";
390+
391+
@Override
392+
public MappingFormat<PairedMapping, ClassifiedMapping<PairedMapping>> getFormat() {
393+
return MappingFormats.PDME;
394+
}
395+
396+
@Override
397+
public ObjectList<String> generate(ClassifiedMapping<PairedMapping> mappings, ClassifiedMappingRemapper remapper) {
398+
ObjectArrayList<String> lines = new ObjectArrayList<>();
399+
if (mappings.classes.isEmpty()) return lines;
400+
mappings.classes.parallelStream().forEach(classMapping -> {
401+
PairedMapping cm = classMapping.mapping;
402+
String unmapped = cm.getUnmappedName().replace('/', '.');
403+
String mapped = cm.getMappedName().replace('/', '.');
404+
String clsDoc = getDoc(cm);
405+
synchronized (lines) {
406+
lines.add(String.join(PARA, "Class", unmapped, mapped, NIL, NIL, clsDoc));
407+
}
408+
classMapping.getFields().parallelStream().forEach(field -> {
409+
String desc = field.getComponent(Descriptor.class).unmappedDescriptor;
410+
String unmappedName = unmapped + '.' + field.getUnmappedName() + ':' + desc;
411+
String doc = getDoc(field);
412+
synchronized (lines) {
413+
lines.add(String.join("Var", unmappedName, field.mappedName, NIL, NIL, doc));
414+
}
415+
});
416+
classMapping.getMethods().parallelStream().forEach(method -> {
417+
String desc = method.getComponent(Descriptor.class).unmappedDescriptor;
418+
String unmappedName = unmapped + '.' + method.getUnmappedName() + desc;
419+
String doc = getDoc(method);
420+
synchronized (lines) {
421+
lines.add(String.join(PARA, "Def", unmappedName, method.mappedName, NIL, NIL, doc));
422+
}
423+
if (method.hasComponent(LocalVariableTable.Paired.class)) {
424+
LocalVariableTable.Paired lvt = method.getComponent(LocalVariableTable.Paired.class);
425+
IntIterator it = lvt.getLocalVariableIndexes().iterator();
426+
while (it.hasNext()) {
427+
int index = it.nextInt();
428+
PairedMapping loc = lvt.getLocalVariable(index);
429+
String line = String.join(
430+
PARA,
431+
"Param",
432+
nilWhenBlank(loc.unmappedName),
433+
nilWhenBlank(loc.mappedName),
434+
unmappedName,
435+
String.valueOf(index),
436+
getDoc(loc)
437+
);
438+
synchronized (lines) {
439+
lines.add(line);
440+
}
441+
}
442+
}
443+
});
444+
});
445+
if (mappings.hasTrait(InheritanceTrait.class)) {
446+
mappings.getTrait(InheritanceTrait.class).getMap().forEach((k, v) -> {
447+
if (!v.isEmpty()) lines.add(String.join(PARA, "Include", k.replace('/', '.'),
448+
String.join(",", Utils.mapArray(v.toArray(), String[]::new,
449+
s -> ((String) s).replace('/', '.'))), NIL, NIL, ""));
450+
});
451+
}
452+
if (mappings.hasTrait(AccessTransformationTrait.class)) {
453+
var map = mappings.getTrait(AccessTransformationTrait.class).getMap();
454+
map.object2IntEntrySet().fastForEach(e -> lines.add(String.join(PARA, "AccessFlag",
455+
e.getKey().replace('/', '.'), formatHex(e.getIntValue()), NIL, NIL, "")));
456+
}
457+
return lines;
458+
}
459+
460+
private static String nilWhenBlank(String s) {
461+
return s == null || s.isBlank() ? NIL : s;
462+
}
463+
464+
private static String getDoc(PairedMapping m) {
465+
return m.getComponentOptional(Documented.class).map(Documented::getContentString).orElse("");
466+
}
467+
468+
private static String formatHex(int value) {
469+
String s = Integer.toHexString(value).toUpperCase(Locale.ENGLISH);
470+
return "0x" + "0".repeat(4 - s.length()) + s;
471+
}
472+
};
380473
}

modules/mapping-api/src/main/java/cn/maxpixel/mcdecompiler/mapping/processor/MappingProcessors.java

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package cn.maxpixel.mcdecompiler.mapping.processor;
2020

2121
import cn.maxpixel.mcdecompiler.common.util.NamingUtil;
22+
import cn.maxpixel.mcdecompiler.common.util.Utils;
2223
import cn.maxpixel.mcdecompiler.mapping.NamespacedMapping;
2324
import cn.maxpixel.mcdecompiler.mapping.PairedMapping;
2425
import cn.maxpixel.mcdecompiler.mapping.collection.ClassMapping;
@@ -28,6 +29,8 @@
2829
import cn.maxpixel.mcdecompiler.mapping.component.StaticIdentifiable;
2930
import cn.maxpixel.mcdecompiler.mapping.format.MappingFormat;
3031
import cn.maxpixel.mcdecompiler.mapping.format.MappingFormats;
32+
import cn.maxpixel.mcdecompiler.mapping.trait.AccessTransformationTrait;
33+
import cn.maxpixel.mcdecompiler.mapping.trait.InheritanceTrait;
3134
import cn.maxpixel.mcdecompiler.mapping.trait.NamespacedTrait;
3235
import cn.maxpixel.mcdecompiler.mapping.util.MappingUtil;
3336
import cn.maxpixel.mcdecompiler.mapping.util.TinyUtil;
@@ -98,9 +101,6 @@ private static String getName(String s) {
98101
};
99102

100103
MappingProcessor.Classified<PairedMapping> CSRG = new MappingProcessor.Classified<>() {
101-
private static final Function<String, ClassMapping<PairedMapping>> COMPUTE_FUNC = name ->
102-
new ClassMapping<>(new PairedMapping(name));
103-
104104
@Override
105105
public MappingFormat<PairedMapping, ClassifiedMapping<PairedMapping>> getFormat() {
106106
return MappingFormats.CSRG;
@@ -131,13 +131,13 @@ public ClassifiedMapping<PairedMapping> process(ObjectList<String> content) {
131131
case 3 -> { // Field
132132
PairedMapping fieldMapping = MappingUtil.Paired.o(sa[1], sa[2]);
133133
synchronized (classes) {
134-
classes.computeIfAbsent(sa[0], COMPUTE_FUNC).addField(fieldMapping);
134+
classes.computeIfAbsent(sa[0], MappingUtil.Paired.COMPUTE_DEFAULT_CLASS).addField(fieldMapping);
135135
}
136136
}
137137
case 4 -> { // Method
138138
PairedMapping methodMapping = MappingUtil.Paired.duo(sa[1], sa[3], sa[2]);
139139
synchronized (classes) {
140-
classes.computeIfAbsent(sa[0], COMPUTE_FUNC).addMethod(methodMapping);
140+
classes.computeIfAbsent(sa[0], MappingUtil.Paired.COMPUTE_DEFAULT_CLASS).addMethod(methodMapping);
141141
}
142142
}
143143
default -> throw new IllegalArgumentException("Is this a CSRG mapping file?");
@@ -480,4 +480,113 @@ private static void error() {
480480
throw new IllegalArgumentException("Is this a Tiny v2 mapping file?");
481481
}
482482
};
483+
484+
MappingProcessor.Classified<PairedMapping> PDME = new MappingProcessor.Classified<>() {
485+
private static final char PARA = '¶';
486+
487+
@Override
488+
public MappingFormat<PairedMapping, ClassifiedMapping<PairedMapping>> getFormat() {
489+
return MappingFormats.PDME;
490+
}
491+
492+
@Override
493+
public ClassifiedMapping<PairedMapping> process(ObjectList<String> content) {
494+
InheritanceTrait inheritanceMap = new InheritanceTrait();
495+
AccessTransformationTrait at = new AccessTransformationTrait();
496+
ClassifiedMapping<PairedMapping> mappings = new ClassifiedMapping<>(inheritanceMap, at);
497+
Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> classes = new Object2ObjectOpenHashMap<>(); // k: unmapped name
498+
Object2ObjectOpenHashMap<String, PairedMapping> methodMap = new Object2ObjectOpenHashMap<>();
499+
for (String line : content) {
500+
String[] parts = MappingUtil.split(line, PARA);
501+
switch (parts[0]) {
502+
case "Class" -> {
503+
String unmapped = parts[1].replace('.', '/');
504+
String mapped = parts[2].replace('.', '/');
505+
classes.merge(unmapped, new ClassMapping<>(new PairedMapping(unmapped, mapped, new Documented(parts[5]))), (o, n) -> {
506+
n.addFields(o.getFields());
507+
n.addMethods(o.getMethods());
508+
return n;
509+
});
510+
}
511+
case "Def" -> getMethod(parts[1], parts[2], parts[5], classes, methodMap);
512+
case "Var" -> {
513+
int lastDot = parts[1].lastIndexOf('.');
514+
String nameAndDesc = parts[1].substring(lastDot + 1);
515+
int colon = nameAndDesc.indexOf(':');
516+
PairedMapping field = MappingUtil.Paired.duo(nameAndDesc.substring(0, colon), parts[2],
517+
nameAndDesc.substring(colon + 1));
518+
field.addComponent(new Documented(parts[5]));
519+
ClassMapping<PairedMapping> cm = classes.computeIfAbsent(parts[1].substring(0, lastDot)
520+
.replace('.', '/'), MappingUtil.Paired.COMPUTE_DEFAULT_CLASS);
521+
cm.addField(field);
522+
}
523+
case "Param" -> {
524+
PairedMapping local = new PairedMapping(parts[1], parts[2]);
525+
local.addComponent(new Documented(parts[5]));
526+
PairedMapping method = getMethod(parts[3], null, null, classes, methodMap);
527+
LocalVariableTable.Paired lvt = method.getComponent(LocalVariableTable.Paired.class);
528+
if (lvt == null) {// TODO
529+
lvt = new LocalVariableTable.Paired();
530+
method.addComponent(lvt);
531+
}
532+
lvt.setLocalVariable(Integer.parseInt(parts[4]), local);
533+
}
534+
case "Include", "Incluir" -> inheritanceMap.put(parts[1].replace('.', '/'),
535+
Utils.mapArray(MappingUtil.split(parts[2], ','), String[]::new,
536+
s -> s.replace('.', '/')));
537+
case "AccessFlag", "BanderaDeAcceso" -> {
538+
if (parts[1].contains(":")) { // field
539+
} else if (parts[1].contains("(")) { // method
540+
} else { // class
541+
at.add(parts[1].replace('.', '/'), parts[2].startsWith("0x") ?
542+
Integer.parseInt(parts[2].substring(2), 16) : Integer.parseInt(parts[2]));
543+
}
544+
}
545+
}
546+
}
547+
mappings.classes.addAll(classes.values());
548+
for (var cm : mappings.classes) parseOuterClass(cm.mapping.unmappedName, classes);
549+
return mappings;
550+
}
551+
552+
private static String parseOuterClass(String unmapped, Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> classes) {
553+
ClassMapping<PairedMapping> cm = classes.get(unmapped);
554+
String mapped = cm == null ? unmapped : cm.mapping.mappedName;
555+
int lastDollar = unmapped.lastIndexOf('$');
556+
if (lastDollar < 0) return mapped;
557+
String outer = unmapped.substring(0, lastDollar);
558+
if (cm != null) {
559+
if (mapped.contains("$")) return mapped;
560+
String ret = parseOuterClass(outer, classes) + '$' + mapped;
561+
cm.mapping.mappedName = ret;
562+
return ret;
563+
}
564+
String ret = parseOuterClass(outer, classes) + '$' + unmapped.substring(lastDollar + 1);
565+
classes.put(unmapped, new ClassMapping<>(new PairedMapping(unmapped, ret)));
566+
return ret;
567+
}
568+
569+
private static PairedMapping getMethod(String original, String mapped, String docs,
570+
Object2ObjectOpenHashMap<String, ClassMapping<PairedMapping>> classes,
571+
Object2ObjectOpenHashMap<String, PairedMapping> methodMap) {
572+
return methodMap.compute(original, (s, old) -> {
573+
if (old != null) {
574+
if (docs != null) old.addComponent(new Documented(docs));
575+
if (mapped != null) old.mappedName = mapped;
576+
return old;
577+
}
578+
int lastDot = s.lastIndexOf('.');
579+
String nameAndDesc = s.substring(lastDot + 1);
580+
int bracket = nameAndDesc.indexOf('(');
581+
String name = nameAndDesc.substring(0, bracket);
582+
PairedMapping method = MappingUtil.Paired.duo(name, mapped == null ? name : mapped,
583+
nameAndDesc.substring(bracket));
584+
if (docs != null) method.addComponent(new Documented(docs));
585+
ClassMapping<PairedMapping> cm = classes.computeIfAbsent(s.substring(0, lastDot)
586+
.replace('.', '/'), MappingUtil.Paired.COMPUTE_DEFAULT_CLASS);
587+
cm.addMethod(method);
588+
return method;
589+
});
590+
}
591+
};
483592
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cn.maxpixel.mcdecompiler.mapping.trait;
2+
3+
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
4+
5+
/**
6+
* A trait that stores access flag transformation data
7+
*/
8+
public class AccessTransformationTrait implements MappingTrait {
9+
private final Object2IntOpenHashMap<String> map = new Object2IntOpenHashMap<>();
10+
11+
@Override
12+
public String getName() {
13+
return "access-transformation";
14+
}
15+
16+
public Object2IntOpenHashMap<String> getMap() {
17+
return map;
18+
}
19+
20+
public void add(String name, int flag) {
21+
map.mergeInt(name, flag, (a, b) -> a | b);
22+
}
23+
}

0 commit comments

Comments
 (0)