
net.minecraftforge.gradle.tasks.ProcessJarTask Maven / Gradle / Ivy
package net.minecraftforge.gradle.tasks;
import static net.minecraftforge.gradle.common.Constants.EXT_NAME_MC;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import net.md_5.specialsource.AccessMap;
import net.md_5.specialsource.Jar;
import net.md_5.specialsource.JarMapping;
import net.md_5.specialsource.JarRemapper;
import net.md_5.specialsource.RemapperProcessor;
import net.md_5.specialsource.provider.JarProvider;
import net.md_5.specialsource.provider.JointProvider;
import net.minecraftforge.gradle.common.BaseExtension;
import net.minecraftforge.gradle.delayed.DelayedFile;
import net.minecraftforge.gradle.json.JsonFactory;
import net.minecraftforge.gradle.json.MCInjectorStruct;
import net.minecraftforge.gradle.json.MCInjectorStruct.InnerClass;
import net.minecraftforge.gradle.tasks.abstractutil.CachedTask;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;
import de.oceanlabs.mcp.mcinjector.MCInjectorImpl;
public class ProcessJarTask extends CachedTask
{
@InputFile
@Optional
private DelayedFile fieldCsv;
@InputFile
@Optional
private DelayedFile methodCsv;
@InputFile
private DelayedFile inJar;
@InputFile
private DelayedFile srg;
@InputFile
private DelayedFile exceptorCfg;
@Optional
@Input
private boolean stripSynthetics = false;
@InputFile
private DelayedFile exceptorJson;
@Input
private boolean applyMarkers = false;
private DelayedFile outCleanJar; // clean = pure forge, or pure FML
private DelayedFile outDirtyJar = new DelayedFile(getProject(), "{BUILD_DIR}/processed.jar"); // dirty = has any other ATs
@InputFiles
private ArrayList ats = new ArrayList();
private DelayedFile log;
private boolean isClean = true;
public void addTransformerClean(DelayedFile... obj)
{
for (DelayedFile object : obj)
{
ats.add(object);
}
}
/**
* adds an access transformer to the deobfuscation of this
* @param obj
*/
public void addTransformer(Object... obj)
{
for (Object object : obj)
{
if (object instanceof File)
ats.add(new DelayedFile(getProject(), ((File) object).getAbsolutePath()));
else if (object instanceof String)
ats.add(new DelayedFile(getProject(), (String) object));
else
ats.add(new DelayedFile(getProject(), object.toString()));
isClean = false;
}
}
@TaskAction
public void doTask() throws IOException
{
// make stuff into files.
File tempObfJar = new File(getTemporaryDir(), "deobfed.jar"); // courtesy of gradle temp dir.
File out = isClean ? getOutCleanJar() : getOutDirtyJar();
File tempExcJar = stripSynthetics ? new File(getTemporaryDir(), "excpeted.jar") : out; // courtesy of gradle temp dir.
// make the ATs list.. its a Set to avoid duplication.
Set ats = new HashSet();
for (DelayedFile obj : this.ats)
{
ats.add(getProject().file(obj).getCanonicalFile());
}
// deobf
getLogger().lifecycle("Applying SpecialSource...");
deobfJar(getInJar(), tempObfJar, getSrg(), ats);
File log = getLog();
if (log == null)
log = new File(getTemporaryDir(), "exceptor.log");
// apply exceptor
getLogger().lifecycle("Applying Exceptor...");
applyExceptor(tempObfJar, tempExcJar, getExceptorCfg(), log, ats);
if (stripSynthetics)
{
// strip out synthetics that arnt from enums..
getLogger().lifecycle("Stripping synthetics...");
stripSynthetics(tempExcJar, out);
}
}
private void deobfJar(File inJar, File outJar, File srg, Collection ats) throws IOException
{
getLogger().debug("INPUT: " + inJar);
getLogger().debug("OUTPUT: " + outJar);
// load mapping
JarMapping mapping = new JarMapping();
mapping.loadMappings(srg);
final Map renames = Maps.newHashMap();
for (File f : new File[]{ getFieldCsv(), getMethodCsv() })
{
if (f == null) continue;
Files.readLines(f, Charsets.UTF_8, new LineProcessor()
{
@Override
public boolean processLine(String line) throws IOException
{
String[] pts = line.split(",");
if (!"searge".equals(pts[0]))
{
renames.put(pts[0], pts[1]);
}
return true;
}
@Override public String getResult() { return null; }
});
}
// load in ATs
AccessMap accessMap = new AccessMap() {
@Override
public void addAccessChange(String symbolString, String accessString)
{
String[] pts = symbolString.split(" ");
if (pts.length >= 2)
{
int idx = pts[1].indexOf('(');
String start = pts[1];
String end = "";
if (idx != -1)
{
start = pts[1].substring(0, idx);
end = pts[1].substring(idx);
}
String rename = renames.get(start);
if (rename != null)
{
pts[1] = rename + end;
}
}
String joinedString = Joiner.on('.').join(pts);
super.addAccessChange(joinedString, accessString);
}
};
getLogger().info("Using AccessTransformers...");
for (File at : ats)
{
getLogger().info("" + at);
accessMap.loadAccessTransformer(at);
}
// make a processor out of the ATS and mappings.
RemapperProcessor srgProcessor = new RemapperProcessor(null, mapping, null);
RemapperProcessor atProcessor = new RemapperProcessor(null, null, accessMap);
// make remapper
JarRemapper remapper = new JarRemapper(srgProcessor, mapping, atProcessor);
// load jar
Jar input = Jar.init(inJar);
// ensure that inheritance provider is used
JointProvider inheritanceProviders = new JointProvider();
inheritanceProviders.add(new JarProvider(input));
mapping.setFallbackInheritanceProvider(inheritanceProviders);
// remap jar
remapper.remapJar(input, outJar);
}
private int fixAccess(int access, String target)
{
int ret = access & ~7;
int t = 0;
if (target.startsWith("public")) t = ACC_PUBLIC;
else if (target.startsWith("private")) t = ACC_PRIVATE;
else if (target.startsWith("protected")) t = ACC_PROTECTED;
switch (access & 7)
{
case ACC_PRIVATE: ret |= t; break;
case 0: ret |= (t != ACC_PRIVATE ? t : 0); break;
case ACC_PROTECTED: ret |= (t != ACC_PRIVATE && t != 0 ? t : ACC_PROTECTED); break;
case ACC_PUBLIC: ret |= ACC_PUBLIC; break;
}
if (target.endsWith("-f")) ret &= ~ACC_FINAL;
else if (target.endsWith("+f")) ret |= ACC_FINAL;
return ret;
}
public void applyExceptor(File inJar, File outJar, File config, File log, Set ats) throws IOException
{
String json = null;
File getJson = getExceptorJson();
if (getJson != null)
{
final Map struct = JsonFactory.loadMCIJson(getJson);
for (File at : ats)
{
getLogger().info("loading AT: "+at.getCanonicalPath());
Files.readLines(at, Charset.defaultCharset(), new LineProcessor
© 2015 - 2025 Weber Informatics LLC | Privacy Policy