Add initial Fernflower driver

pull/48/head
Graham 5 years ago
parent c9803a9a5c
commit ed0da80e30
  1. 32
      decompiler/pom.xml
  2. 73
      decompiler/src/main/java/dev/openrs2/decompiler/Decompiler.java
  3. 97
      decompiler/src/main/java/dev/openrs2/decompiler/DecompilerIo.java
  4. 52
      decompiler/src/main/java/dev/openrs2/decompiler/Slf4jFernflowerLogger.java
  5. 1
      pom.xml

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.openrs2</groupId>
<artifactId>openrs2</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>openrs2-decompiler</artifactId>
<packaging>jar</packaging>
<name>OpenRS2 Decompiler</name>
<dependencies>
<dependency>
<groupId>dev.openrs2</groupId>
<artifactId>openrs2-fernflower</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.openrs2</groupId>
<artifactId>openrs2-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,73 @@
package dev.openrs2.decompiler;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
public final class Decompiler implements Closeable {
private static final Map<String, Object> OPTIONS = Map.of(
IFernflowerPreferences.ASCII_STRING_CHARACTERS, "1",
IFernflowerPreferences.INDENT_STRING, "\t",
IFernflowerPreferences.SYNTHETIC_NOT_SET, "1"
);
private static Path getDestination(String archive) {
archive = archive.replaceAll("_gl[.]jar$", "");
if (archive.equals("runescape")) {
archive = "client";
}
return Paths.get("nonfree").resolve(archive).resolve("src/main/java");
}
public static void main(String[] args) throws IOException {
var libraries = List.of(Paths.get("nonfree/code/jogl.jar"));
var deobOutput = Paths.get("nonfree/code/deob");
var sources = List.of(
deobOutput.resolve("runescape_gl.jar"),
deobOutput.resolve("loader_gl.jar"),
deobOutput.resolve("signlink_gl.jar"),
deobOutput.resolve("unpack_gl.jar"),
deobOutput.resolve("unpacker_gl.jar")
);
try (var decompiler = new Decompiler(libraries, sources, Decompiler::getDestination)) {
decompiler.run();
}
}
private final DecompilerIo io;
private final Fernflower fernflower;
private final List<Path> libraries, sources;
public Decompiler(List<Path> libraries, List<Path> sources, Function<String, Path> destination) {
this.io = new DecompilerIo(destination);
this.fernflower = new Fernflower(io, io, OPTIONS, Slf4jFernflowerLogger.INSTANCE);
this.libraries = libraries;
this.sources = sources;
}
public void run() {
for (var library : libraries) {
fernflower.addLibrary(library.toFile());
}
for (var source : sources) {
fernflower.addSource(source.toFile());
}
fernflower.decompileContext();
}
@Override
public void close() throws IOException {
io.close();
}
}

@ -0,0 +1,97 @@
package dev.openrs2.decompiler;
import java.io.Closeable;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import com.google.common.io.ByteStreams;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
public final class DecompilerIo implements IBytecodeProvider, IResultSaver, Closeable {
private final Map<String, JarFile> inputJars = new HashMap<>();
private final Function<String, Path> destination;
public DecompilerIo(Function<String, Path> destination) {
this.destination = destination;
}
@Override
public byte[] getBytecode(String externalPath, String internalPath) throws IOException {
if (internalPath == null) {
throw new UnsupportedOperationException();
}
var jar = inputJars.get(externalPath);
if (jar == null) {
jar = new JarFile(externalPath);
inputJars.put(externalPath, jar);
}
try (var in = jar.getInputStream(jar.getJarEntry(internalPath))) {
return ByteStreams.toByteArray(in);
}
}
@Override
public void saveFolder(String path) {
/* ignore */
}
@Override
public void copyFile(String source, String path, String entryName) {
throw new UnsupportedOperationException();
}
@Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
throw new UnsupportedOperationException();
}
@Override
public void createArchive(String path, String archiveName, Manifest manifest) {
/* ignore */
}
@Override
public void saveDirEntry(String path, String archiveName, String entryName) {
/* ignore */
}
@Override
public void copyEntry(String source, String path, String archiveName, String entry) {
/* ignore */
}
@Override
public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) {
var p = destination.apply(archiveName).resolve(entryName);
try {
Files.createDirectories(p.getParent());
try (var writer = Files.newBufferedWriter(p)) {
writer.write(content);
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public void closeArchive(String path, String archiveName) {
/* ignore */
}
@Override
public void close() throws IOException {
for (var jar : inputJars.values()) {
jar.close();
}
}
}

@ -0,0 +1,52 @@
package dev.openrs2.decompiler;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class Slf4jFernflowerLogger extends IFernflowerLogger {
private static final Logger logger = LoggerFactory.getLogger(Slf4jFernflowerLogger.class);
public static final IFernflowerLogger INSTANCE = new Slf4jFernflowerLogger();
private Slf4jFernflowerLogger() {
/* empty */
}
@Override
public void startClass(String className) {
logger.info("Decompiling {}", className);
}
@Override
public void writeMessage(String message, Severity severity) {
switch (severity) {
case TRACE:
logger.trace(message);
case INFO:
logger.info(message);
case WARN:
logger.warn(message);
case ERROR:
logger.error(message);
default:
throw new IllegalArgumentException();
}
}
@Override
public void writeMessage(String message, Severity severity, Throwable t) {
switch (severity) {
case TRACE:
logger.trace(message, t);
case INFO:
logger.info(message, t);
case WARN:
logger.warn(message, t);
case ERROR:
logger.error(message, t);
default:
throw new IllegalArgumentException();
}
}
}

@ -11,6 +11,7 @@
<modules>
<module>asm</module>
<module>decompiler</module>
<module>deob</module>
<module>deob-annotations</module>
<module>fernflower</module>

Loading…
Cancel
Save