diff --git a/deob-ast/pom.xml b/deob-ast/pom.xml
new file mode 100644
index 0000000000..83dd3f6467
--- /dev/null
+++ b/deob-ast/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+
+
+ dev.openrs2
+ openrs2
+ 1.0.0-SNAPSHOT
+
+
+ openrs2-deob-ast
+ jar
+
+ OpenRS2 AST Deobfuscator
+
+
+ ${project.parent.basedir}
+
+
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ com.github.javaparser
+ javaparser-symbol-solver-core
+
+
+
diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/AstDeobfuscator.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/AstDeobfuscator.java
new file mode 100644
index 0000000000..70a934fc6b
--- /dev/null
+++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/AstDeobfuscator.java
@@ -0,0 +1,80 @@
+package dev.openrs2.deob.ast;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import com.github.javaparser.ParserConfiguration;
+import com.github.javaparser.printer.PrettyPrinter;
+import com.github.javaparser.printer.PrettyPrinterConfiguration;
+import com.github.javaparser.symbolsolver.JavaSymbolSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+import com.github.javaparser.utils.SourceRoot;
+import com.google.common.collect.ImmutableList;
+import dev.openrs2.deob.ast.transform.AddSubTransformer;
+import dev.openrs2.deob.ast.transform.ComplementTransformer;
+import dev.openrs2.deob.ast.transform.Transformer;
+
+public final class AstDeobfuscator {
+ private static final ImmutableList TRANSFORMERS = ImmutableList.of(
+ new AddSubTransformer(),
+ new ComplementTransformer()
+ );
+
+ public static void main(String[] args) {
+ var deobfuscator = new AstDeobfuscator(ImmutableList.of(
+ Paths.get("nonfree/client/src/main/java"),
+ Paths.get("nonfree/gl/src/main/java"),
+ Paths.get("nonfree/gl-dri/src/main/java"),
+ Paths.get("nonfree/loader/src/main/java"),
+ Paths.get("nonfree/signlink/src/main/java"),
+ Paths.get("nonfree/unpack/src/main/java"),
+ Paths.get("nonfree/unpacker/src/main/java")
+ ));
+ deobfuscator.run();
+ }
+
+ private final ImmutableList modules;
+
+ public AstDeobfuscator(ImmutableList modules) {
+ this.modules = modules;
+ }
+
+ public void run() {
+ var solver = new CombinedTypeSolver(new ReflectionTypeSolver(true));
+ for (var module : modules) {
+ solver.add(new JavaParserTypeSolver(module));
+ }
+
+ var config = new ParserConfiguration()
+ .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8)
+ .setSymbolResolver(new JavaSymbolSolver(solver));
+
+ var printerConfig = new PrettyPrinterConfiguration()
+ .setIndentType(PrettyPrinterConfiguration.IndentType.TABS_WITH_SPACE_ALIGN)
+ .setIndentSize(1);
+
+ var printer = new PrettyPrinter(printerConfig);
+
+ for (var module : modules) {
+ var root = new SourceRoot(module, config);
+
+ var results = root.tryToParseParallelized();
+ for (var result : results) {
+ if (!result.isSuccessful()) {
+ throw new IllegalArgumentException(result.toString());
+ }
+ }
+
+ root.getCompilationUnits().forEach(unit -> {
+ TRANSFORMERS.forEach(transformer -> {
+ transformer.transform(unit);
+ });
+ });
+
+ root.setPrinter(printer::print);
+ root.saveAll();
+ }
+ }
+}
diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/AddSubTransformer.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/AddSubTransformer.java
new file mode 100644
index 0000000000..09c80429be
--- /dev/null
+++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/AddSubTransformer.java
@@ -0,0 +1,36 @@
+package dev.openrs2.deob.ast.transform;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.IntegerLiteralExpr;
+import com.github.javaparser.ast.expr.UnaryExpr;
+import dev.openrs2.deob.ast.util.ExprUtils;
+
+public final class AddSubTransformer extends Transformer {
+ private static Expression negate(Expression expr) {
+ if (expr.isIntegerLiteralExpr()) {
+ return new IntegerLiteralExpr(-expr.asIntegerLiteralExpr().asInt());
+ } else if (expr.isLongLiteralExpr()) {
+ return ExprUtils.createLong(-expr.asLongLiteralExpr().asLong());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public void transform(CompilationUnit unit) {
+ unit.findAll(UnaryExpr.class).forEach(expr -> {
+ var operand = expr.getExpression();
+ if (!ExprUtils.isIntegerOrLongLiteral(operand)) {
+ return;
+ }
+
+ var op = expr.getOperator();
+ if (op == UnaryExpr.Operator.PLUS) {
+ expr.replace(operand);
+ } else if (op == UnaryExpr.Operator.MINUS) {
+ expr.replace(negate(operand));
+ }
+ });
+ }
+}
diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/ComplementTransformer.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/ComplementTransformer.java
new file mode 100644
index 0000000000..24c17aef32
--- /dev/null
+++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/ComplementTransformer.java
@@ -0,0 +1,66 @@
+package dev.openrs2.deob.ast.transform;
+
+import java.util.Optional;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.expr.BinaryExpr;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.IntegerLiteralExpr;
+import com.github.javaparser.ast.expr.UnaryExpr;
+import dev.openrs2.deob.ast.util.ExprUtils;
+
+public final class ComplementTransformer extends Transformer {
+ private static boolean isComplement(Expression expr) {
+ return expr.isUnaryExpr() && expr.asUnaryExpr().getOperator() == UnaryExpr.Operator.BITWISE_COMPLEMENT;
+ }
+
+ private static boolean isComplementOrLiteral(Expression expr) {
+ return isComplement(expr) || ExprUtils.isIntegerOrLongLiteral(expr);
+ }
+
+ private static Optional complement(BinaryExpr.Operator op) {
+ switch (op) {
+ case EQUALS:
+ case NOT_EQUALS:
+ return Optional.of(op);
+ case GREATER:
+ return Optional.of(BinaryExpr.Operator.LESS);
+ case GREATER_EQUALS:
+ return Optional.of(BinaryExpr.Operator.LESS_EQUALS);
+ case LESS:
+ return Optional.of(BinaryExpr.Operator.GREATER);
+ case LESS_EQUALS:
+ return Optional.of(BinaryExpr.Operator.GREATER_EQUALS);
+ default:
+ return Optional.empty();
+ }
+ }
+
+ private static Expression complement(Expression expr) {
+ if (expr.isUnaryExpr()) {
+ return expr.asUnaryExpr().getExpression();
+ } else if (expr.isIntegerLiteralExpr()) {
+ return new IntegerLiteralExpr(~expr.asIntegerLiteralExpr().asInt());
+ } else if (expr.isLongLiteralExpr()) {
+ return ExprUtils.createLong(~expr.asLongLiteralExpr().asLong());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public void transform(CompilationUnit unit) {
+ unit.findAll(BinaryExpr.class).forEach(expr -> {
+ complement(expr.getOperator()).ifPresent(op -> {
+ var left = expr.getLeft();
+ var right = expr.getRight();
+
+ if (isComplementOrLiteral(left) && isComplementOrLiteral(right) && !(ExprUtils.isIntegerOrLongLiteral(left) && ExprUtils.isIntegerOrLongLiteral(right))) {
+ expr.setOperator(op);
+ expr.setLeft(complement(left));
+ expr.setRight(complement(right));
+ }
+ });
+ });
+ }
+}
diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/Transformer.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/Transformer.java
new file mode 100644
index 0000000000..afcaac67b9
--- /dev/null
+++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/Transformer.java
@@ -0,0 +1,7 @@
+package dev.openrs2.deob.ast.transform;
+
+import com.github.javaparser.ast.CompilationUnit;
+
+public abstract class Transformer {
+ public abstract void transform(CompilationUnit unit);
+}
diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/util/ExprUtils.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/util/ExprUtils.java
new file mode 100644
index 0000000000..a4409d1686
--- /dev/null
+++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/util/ExprUtils.java
@@ -0,0 +1,18 @@
+package dev.openrs2.deob.ast.util;
+
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.LongLiteralExpr;
+
+public final class ExprUtils {
+ public static boolean isIntegerOrLongLiteral(Expression expr) {
+ return expr.isIntegerLiteralExpr() || expr.isLongLiteralExpr();
+ }
+
+ public static LongLiteralExpr createLong(long value) {
+ return new LongLiteralExpr(Long.toString(value).concat("L"));
+ }
+
+ private ExprUtils() {
+ /* empty */
+ }
+}
diff --git a/pom.xml b/pom.xml
index 6e37f07d04..5ef0872d14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,6 +21,7 @@
decompiler
deob
deob-annotations
+ deob-ast
game
util
@@ -52,6 +53,11 @@
guava
28.0-jre
+
+ com.github.javaparser
+ javaparser-symbol-solver-core
+ 3.14.9
+
dev.openrs2
fernflower