net.minecraftforge.gradle.userdev.util.Deobfuscator Maven / Gradle / Ivy
Go to download
Minecraft mod development framework used by Forge and FML for the gradle build system adapted for mohist api.
The newest version!
/*
* ForgeGradle
* Copyright (C) 2018 Forge Development LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package net.minecraftforge.gradle.userdev.util;
import net.minecraftforge.gradle.common.util.HashFunction;
import net.minecraftforge.gradle.common.util.HashStore;
import net.minecraftforge.gradle.common.util.MavenArtifactDownloader;
import net.minecraftforge.gradle.common.util.McpNames;
import net.minecraftforge.gradle.common.util.Utils;
import net.minecraftforge.gradle.mcp.MCPRepo;
import net.minecraftforge.gradle.userdev.tasks.RenameJarSrg2Mcp;
import org.apache.commons.io.IOUtils;
import org.gradle.api.Project;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.annotation.Nullable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
public class Deobfuscator {
private final Project project;
private final File cacheRoot;
private final DocumentBuilder xmlParser;
private final XPath xPath;
private final Transformer xmlTransformer;
public Deobfuscator(Project project, File cacheRoot) {
this.project = project;
this.cacheRoot = cacheRoot;
try {
xPath = XPathFactory.newInstance().newXPath();
xmlParser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
xmlTransformer = TransformerFactory.newInstance().newTransformer();
} catch (ParserConfigurationException | TransformerConfigurationException e) {
throw new RuntimeException("Error configuring XML parsers", e);
}
}
public File deobfPom(File original, String mappings, String... cachePath) throws IOException {
project.getLogger().debug("Updating POM file {} with mappings {}", original.getName(), mappings);
File output = getCacheFile(cachePath);
File input = new File(output.getParent(), output.getName() + ".input");
HashStore cache = new HashStore()
.load(input)
.add("mappings", mappings)
.add("orig", original);
if (!cache.isSame() || !output.exists()) {
try {
Document pom = xmlParser.parse(original);
NodeList versionNodes = (NodeList) xPath.compile("/*[local-name()=\"project\"]/*[local-name()=\"version\"]").evaluate(pom, XPathConstants.NODESET);
if (versionNodes.getLength() > 0) {
versionNodes.item(0).setTextContent(versionNodes.item(0).getTextContent() + "_mapped_" + mappings);
}
xmlTransformer.transform(new DOMSource(pom), new StreamResult(output));
} catch (IOException | SAXException | XPathExpressionException | TransformerException e) {
project.getLogger().error("Error attempting to modify pom file " + original.getName(), e);
return original;
}
Utils.updateHash(output, HashFunction.SHA1);
cache.save();
}
return output;
}
@Nullable
public File deobfBinary(File original, @Nullable String mappings, String... cachePath) throws IOException {
project.getLogger().debug("Deobfuscating binary file {} with mappings {}", original.getName(), mappings);
File names = findMapping(mappings);
if (names == null || !names.exists()) {
return null;
}
File output = getCacheFile(cachePath);
File input = new File(output.getParent(), output.getName() + ".input");
HashStore cache = new HashStore()
.load(input)
.add("names", names)
.add("orig", original);
if (!cache.isSame() || !output.exists()) {
RenameJarSrg2Mcp rename = project.getTasks().create("_RenameSrg2Mcp_" + new Random().nextInt(), RenameJarSrg2Mcp.class);
rename.getInput().set(original);
rename.getOutput().set(output);
rename.getMappings().set(names);
rename.setSignatureRemoval(true);
rename.apply();
rename.setEnabled(false);
Utils.updateHash(output, HashFunction.SHA1);
cache.save();
}
return output;
}
@Nullable
public File deobfSources(File original, @Nullable String mappings, String... cachePath) throws IOException {
project.getLogger().debug("Deobfuscating sources file {} with mappings {}", original.getName(), mappings);
File names = findMapping(mappings);
if (names == null || !names.exists()) {
return null;
}
File output = getCacheFile(cachePath);
File input = new File(output.getParent(), output.getName() + ".input");
HashStore cache = new HashStore()
.load(input)
.add("names", names)
.add("orig", original);
if (!cache.isSame() || !output.exists()) {
McpNames map = McpNames.load(names);
try (ZipInputStream zin = new ZipInputStream(new FileInputStream(original));
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(output))) {
ZipEntry _old;
while ((_old = zin.getNextEntry()) != null) {
zout.putNextEntry(Utils.getStableEntry(_old.getName()));
if (_old.getName().endsWith(".java")) {
String mapped = map.rename(zin, false);
IOUtils.write(mapped, zout, StandardCharsets.UTF_8);
} else {
IOUtils.copy(zin, zout);
}
}
}
Utils.updateHash(output, HashFunction.SHA1);
cache.save();
}
return output;
}
private File getCacheFile(String... cachePath) {
File cacheFile = new File(cacheRoot, String.join(File.separator, cachePath));
cacheFile.getParentFile().mkdirs();
return cacheFile;
}
@Nullable
private File findMapping(@Nullable String mapping) {
if (mapping == null)
return null;
int idx = mapping.lastIndexOf('_');
String channel = mapping.substring(0, idx);
String version = mapping.substring(idx + 1);
String desc = MCPRepo.getMappingDep(channel, version);
return MavenArtifactDownloader.generate(project, desc, false);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy