From 226f2379244b9bad8d794ae7adb34a0d5fc4d21e Mon Sep 17 00:00:00 2001 From: asie Date: Fri, 2 Nov 2018 00:40:51 +0100 Subject: [PATCH] mixin refmap remapping support --- .../loom/mixin/MixinMappingProviderTiny.java | 8 +- .../net/fabricmc/loom/task/MapJarsTask.java | 5 + .../net/fabricmc/loom/task/MapJarsTiny.java | 43 +++--- .../net/fabricmc/loom/task/SetupTask.java | 4 +- .../net/fabricmc/loom/util/Constants.java | 1 + .../{ModProccessor.java => ModProcessor.java} | 128 ++++++++++++++++-- .../net/fabricmc/loom/util/ModRemapper.java | 4 +- 7 files changed, 159 insertions(+), 34 deletions(-) rename src/main/java/net/fabricmc/loom/util/{ModProccessor.java => ModProcessor.java} (55%) diff --git a/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java b/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java index e20ff44..5660fa8 100644 --- a/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java +++ b/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.mixin; +import net.fabricmc.stitch.representation.JarEntry; +import net.fabricmc.stitch.representation.JarReader; import net.fabricmc.tinyremapper.TinyUtils; import org.spongepowered.asm.obfuscation.mapping.common.MappingField; import org.spongepowered.asm.obfuscation.mapping.common.MappingMethod; @@ -35,6 +37,8 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; public class MixinMappingProviderTiny extends MappingProvider { private final String from, to; @@ -125,9 +129,7 @@ public class MixinMappingProviderTiny extends MappingProvider { public void read(File input) throws IOException { BufferedReader reader = Files.newBufferedReader(input.toPath()); - TinyUtils.read(reader, from, to, (classFrom, classTo) -> { - classMap.put(classFrom, classTo); - }, (fieldFrom, fieldTo) -> { + TinyUtils.read(reader, from, to, classMap::put, (fieldFrom, fieldTo) -> { fieldMap.put( new MappingField(fieldFrom.owner, fieldFrom.name, fieldFrom.desc), new MappingField(fieldTo.owner, fieldTo.name, fieldTo.desc) diff --git a/src/main/java/net/fabricmc/loom/task/MapJarsTask.java b/src/main/java/net/fabricmc/loom/task/MapJarsTask.java index 8c782e5..7996ec1 100644 --- a/src/main/java/net/fabricmc/loom/task/MapJarsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MapJarsTask.java @@ -50,6 +50,11 @@ public class MapJarsTask extends LoomBaseTask { return getFile(Constants.MINECRAFT_MERGED_JAR); } + @OutputFile + public File getIntermediaryJar() { + return getFile(Constants.MINECRAFT_INTERMEDIARY_JAR); + } + @OutputFile public File getMappedJar() { return getFile(Constants.MINECRAFT_MAPPED_JAR); diff --git a/src/main/java/net/fabricmc/loom/task/MapJarsTiny.java b/src/main/java/net/fabricmc/loom/task/MapJarsTiny.java index 3e20624..d5dd622 100644 --- a/src/main/java/net/fabricmc/loom/task/MapJarsTiny.java +++ b/src/main/java/net/fabricmc/loom/task/MapJarsTiny.java @@ -35,38 +35,43 @@ import net.fabricmc.tinyremapper.TinyUtils; import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.Arrays; public class MapJarsTiny { public void mapJars(MapJarsTask task) throws IOException { String fromM = "mojang"; - String toM = "pomf"; Path mappings = task.getMappingFile().toPath(); Path[] classpath = task.getMapperPaths().stream() .map(File::toPath) .toArray(Path[]::new); - task.getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); - - TinyRemapper remapper = TinyRemapper.newRemapper() - .withMappings(TinyUtils.createTinyMappingProvider(mappings, fromM, toM)) - .build(); - Path input = task.getInputJar().toPath(); - Path output = task.getMappedJar().toPath(); + Path outputMapped = task.getMappedJar().toPath(); + Path outputIntermediary = task.getIntermediaryJar().toPath(); - try { - OutputConsumerPath outputConsumer = new OutputConsumerPath(output); - outputConsumer.addNonClassFiles(input); - remapper.read(input); - remapper.read(classpath); - remapper.apply(input, outputConsumer); - outputConsumer.finish(); - remapper.finish(); - } catch (Exception e){ - remapper.finish(); - throw new RuntimeException("Failed to remap minecraft to " + toM, e); + for (String toM : Arrays.asList("pomf", "intermediary")) { + Path output = "pomf".equals(toM) ? outputMapped : outputIntermediary; + + task.getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); + + TinyRemapper remapper = TinyRemapper.newRemapper() + .withMappings(TinyUtils.createTinyMappingProvider(mappings, fromM, toM)) + .build(); + + try { + OutputConsumerPath outputConsumer = new OutputConsumerPath(output); + outputConsumer.addNonClassFiles(input); + remapper.read(input); + remapper.read(classpath); + remapper.apply(input, outputConsumer); + outputConsumer.finish(); + remapper.finish(); + } catch (Exception e) { + remapper.finish(); + throw new RuntimeException("Failed to remap minecraft to " + toM, e); + } } } } diff --git a/src/main/java/net/fabricmc/loom/task/SetupTask.java b/src/main/java/net/fabricmc/loom/task/SetupTask.java index 86cf8ae..300b231 100644 --- a/src/main/java/net/fabricmc/loom/task/SetupTask.java +++ b/src/main/java/net/fabricmc/loom/task/SetupTask.java @@ -26,7 +26,7 @@ package net.fabricmc.loom.task; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.ModProccessor; +import net.fabricmc.loom.util.ModProcessor; import org.gradle.api.DefaultTask; import org.gradle.api.artifacts.Configuration; import org.gradle.api.tasks.TaskAction; @@ -51,7 +51,7 @@ public class SetupTask extends DefaultTask { if(!output.getParentFile().exists()){ output.mkdirs(); } - ModProccessor.handleMod(input, output, getProject()); + ModProcessor.handleMod(input, output, getProject()); if (!output.exists()) { throw new RuntimeException("Output does not exist!"); } diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 655bbe6..f090409 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -44,6 +44,7 @@ public class Constants { public static final IDelayed MINECRAFT_SERVER_JAR = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-server.jar")); public static final IDelayed MINECRAFT_MERGED_JAR = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-merged.jar")); public static final IDelayed MINECRAFT_MAPPED_JAR = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-mapped-" + extension.pomfVersion + ".jar")); + public static final IDelayed MINECRAFT_INTERMEDIARY_JAR = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-intermediary.jar")); // public static final IDelayed MINECRAFT_PARTIAL_ENIGMA_JAR = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-partial-enigma-" + extension.pomfVersion + ".jar")); // public static final IDelayed MINECRAFT_FINAL_JAR = new DelayedFile(extension -> new File(CACHE_FILES, extension.version + "-final-" + extension.pomfVersion + ".jar")); public static final IDelayed MINECRAFT_FINAL_JAR = MINECRAFT_MAPPED_JAR; diff --git a/src/main/java/net/fabricmc/loom/util/ModProccessor.java b/src/main/java/net/fabricmc/loom/util/ModProcessor.java similarity index 55% rename from src/main/java/net/fabricmc/loom/util/ModProccessor.java rename to src/main/java/net/fabricmc/loom/util/ModProcessor.java index 56a2303..3d1ad85 100644 --- a/src/main/java/net/fabricmc/loom/util/ModProccessor.java +++ b/src/main/java/net/fabricmc/loom/util/ModProcessor.java @@ -24,9 +24,14 @@ package net.fabricmc.loom.util; +import com.google.common.base.Charsets; +import com.google.common.io.ByteStreams; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.mixin.MixinMappingProviderTiny; import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import net.fabricmc.tinyremapper.TinyUtils; @@ -35,17 +40,28 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; +import org.objectweb.asm.commons.Remapper; +import org.spongepowered.asm.mixin.injection.struct.MemberInfo; +import org.spongepowered.asm.obfuscation.mapping.common.MappingField; +import org.spongepowered.asm.obfuscation.mapping.common.MappingMethod; +import org.zeroturnaround.zip.ZipUtil; +import org.zeroturnaround.zip.transform.StringZipEntryTransformer; +import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; +import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.jar.JarFile; import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; -public class ModProccessor { +public class ModProcessor { public static void handleMod(File input, File output, Project project){ if(output.exists()){ @@ -64,7 +80,9 @@ public class ModProccessor { String fromM = "intermediary"; String toM = "pomf"; - Path mappings = Constants.MAPPINGS_TINY.get(extension).toPath(); + File mappingsFile = Constants.MAPPINGS_TINY.get(extension); + Path mappings = mappingsFile.toPath(); + Path mc = Constants.MINECRAFT_INTERMEDIARY_JAR.get(extension).toPath(); Path[] classpath = project.getConfigurations().getByName(Constants.CONFIG_MC_DEPENDENCIES).getFiles().stream() .map(File::toPath) .toArray(Path[]::new); @@ -79,16 +97,110 @@ public class ModProccessor { OutputConsumerPath outputConsumer = new OutputConsumerPath(Paths.get(output.getAbsolutePath())); outputConsumer.addNonClassFiles(input.toPath()); remapper.read(input.toPath()); + remapper.read(mc); remapper.read(classpath); remapper.apply(input.toPath(), outputConsumer); outputConsumer.finish(); - remapper.finish(); } catch (Exception e){ remapper.finish(); - throw new RuntimeException("Failed to remap jar to " + toM, e); + throw new RuntimeException("Failed to remap JAR to " + toM, e); } if(!output.exists()){ - throw new RuntimeException("Failed to remap jar to " + toM + " file not found: " + output.getAbsolutePath()); + throw new RuntimeException("Failed to remap JAR to " + toM + " file not found: " + output.getAbsolutePath()); + } + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + project.getLogger().lifecycle(":remapping " + input.getName() + " (Mixin reference map)"); + // first, identify all of the mixin refmaps + Set mixinRefmapFilenames = new HashSet<>(); + // TODO: this is a lovely hack + ZipUtil.iterate(output, (stream, entry) -> { + if (!entry.isDirectory() && entry.getName().endsWith(".json") && !entry.getName().contains("/") && !entry.getName().contains("\\")) { + // JSON file in root directory + try { + JsonObject json = gson.fromJson(new InputStreamReader(stream), JsonObject.class); + if (json != null && json.has("refmap")) { + mixinRefmapFilenames.add(json.get("refmap").getAsString()); + } + } catch (Exception e) { + // ... + } + } + }); + + if (mixinRefmapFilenames.size() > 0) { + Remapper asmRemapper; + // TODO: Expose in tiny-remapper + try { + Field f = TinyRemapper.class.getDeclaredField("remapper");; + f.setAccessible(true); + asmRemapper = (Remapper) f.get(remapper); + } catch (Exception e) { + throw new RuntimeException(e); + } + + ZipUtil.transformEntries( + output, + mixinRefmapFilenames.stream() + .map((f) -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") { + @Override + protected String transform(ZipEntry zipEntry, String input) throws IOException { + return transformRefmap(asmRemapper, gson, input); + } + })).toArray(ZipEntryTransformerEntry[]::new) + ); + + remapper.finish(); + } + } + + public static String transformRefmap(Remapper remapper, Gson gson, String input) throws IOException { + try { + JsonObject refMap = gson.fromJson(input, JsonObject.class); + JsonObject mappings = refMap.getAsJsonObject("mappings"); + + for (Map.Entry elementEntry : mappings.entrySet()) { + JsonObject value = elementEntry.getValue().getAsJsonObject(); + for (String k : new HashSet<>(value.keySet())) { + try { + String v = value.get(k).getAsString(); + String v2; + + if (v.charAt(0) == 'L') { + // field or member + MemberInfo info = MemberInfo.parse(v); + String owner = remapper.map(info.owner); + if (info.isField()) { + v2 = new MemberInfo( + remapper.mapFieldName(info.owner, info.name, info.desc), + owner, + remapper.mapDesc(info.desc) + ).toString(); + } else { + v2 = new MemberInfo( + remapper.mapMethodName(info.owner, info.name, info.desc), + owner, + remapper.mapMethodDesc(info.desc) + ).toString(); + } + } else { + // class + v2 = remapper.map(v); + } + + if (v2 != null) { + value.addProperty(k, v2); + } + } catch (Exception ee) { + ee.printStackTrace(); + } + } + } + + return gson.toJson(refMap); + } catch (Exception e) { + e.printStackTrace(); + return input; } } diff --git a/src/main/java/net/fabricmc/loom/util/ModRemapper.java b/src/main/java/net/fabricmc/loom/util/ModRemapper.java index cbe6100..abcd8fd 100644 --- a/src/main/java/net/fabricmc/loom/util/ModRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/ModRemapper.java @@ -94,11 +94,11 @@ public class ModRemapper { remapper.finish(); } catch (Exception e){ remapper.finish(); - throw new RuntimeException("Failed to remap jar", e); + throw new RuntimeException("Failed to remap JAR", e); } if(!deobfJar.exists() || !modJar.exists()){ - throw new RuntimeException("Failed to rebof jar"); + throw new RuntimeException("Failed to reobfuscate JAR"); } deobfJar.delete();