diff --git a/asm/src/main/java/dev/openrs2/asm/MemberRef.kt b/asm/src/main/java/dev/openrs2/asm/MemberRef.kt index f6f34ecd..53dca95c 100644 --- a/asm/src/main/java/dev/openrs2/asm/MemberRef.kt +++ b/asm/src/main/java/dev/openrs2/asm/MemberRef.kt @@ -6,13 +6,27 @@ import org.objectweb.asm.tree.FieldNode import org.objectweb.asm.tree.MethodInsnNode import org.objectweb.asm.tree.MethodNode -data class MemberRef(val owner: String, val name: String, val desc: String) { +data class MemberRef(val owner: String, val name: String, val desc: String) : Comparable { constructor(clazz: ClassNode, field: FieldNode) : this(clazz.name, field.name, field.desc) constructor(clazz: ClassNode, method: MethodNode) : this(clazz.name, method.name, method.desc) constructor(fieldInsn: FieldInsnNode) : this(fieldInsn.owner, fieldInsn.name, fieldInsn.desc) constructor(methodInsn: MethodInsnNode) : this(methodInsn.owner, methodInsn.name, methodInsn.desc) constructor(owner: String, desc: MemberDesc) : this(owner, desc.name, desc.desc) + override fun compareTo(other: MemberRef): Int { + var result = owner.compareTo(other.owner) + if (result != 0) { + return result + } + + result = name.compareTo(other.name) + if (result != 0) { + return result + } + + return desc.compareTo(other.desc) + } + override fun toString(): String { return "$owner.$name $desc" } diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 7fdc0a14..9b084769 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -9,6 +9,7 @@ object Versions { const val guava = "28.2-jre" const val guice = "4.2.3" const val inlineLogger = "1.0.2" + const val jackson = "2.10.3" const val javaParser = "3.15.18" const val jdom = "2.0.6" const val jgrapht = "1.4.0" diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 00000000..74d6c1f8 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1 @@ +/deob-map diff --git a/deob-map/build.gradle.kts b/deob-map/build.gradle.kts new file mode 100644 index 00000000..d531cb4a --- /dev/null +++ b/deob-map/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + `maven-publish` + kotlin("jvm") +} + +dependencies { + api(project(":asm")) +} + +publishing { + publications.create("maven") { + from(components["java"]) + + pom { + packaging = "jar" + name.set("OpenRS2 Deobfuscator Map") + description.set( + """ + Data structures for representing a map of obfuscated to + refactored names. + """.trimIndent() + ) + } + } +} diff --git a/deob-map/src/main/java/dev/openrs2/deob/map/Field.kt b/deob-map/src/main/java/dev/openrs2/deob/map/Field.kt new file mode 100644 index 00000000..b0d88939 --- /dev/null +++ b/deob-map/src/main/java/dev/openrs2/deob/map/Field.kt @@ -0,0 +1,3 @@ +package dev.openrs2.deob.map + +data class Field(val owner: String, val name: String) diff --git a/deob-map/src/main/java/dev/openrs2/deob/map/Method.kt b/deob-map/src/main/java/dev/openrs2/deob/map/Method.kt new file mode 100644 index 00000000..9a1a86c4 --- /dev/null +++ b/deob-map/src/main/java/dev/openrs2/deob/map/Method.kt @@ -0,0 +1,14 @@ +package dev.openrs2.deob.map + +import java.util.SortedMap + +data class Method( + val owner: String, + val name: String, + /* + * Uses concrete type as there is no interface for a map sorted by + * insertion order. + */ + val arguments: LinkedHashMap, + val locals: SortedMap +) diff --git a/deob-map/src/main/java/dev/openrs2/deob/map/NameMap.kt b/deob-map/src/main/java/dev/openrs2/deob/map/NameMap.kt new file mode 100644 index 00000000..815942ad --- /dev/null +++ b/deob-map/src/main/java/dev/openrs2/deob/map/NameMap.kt @@ -0,0 +1,10 @@ +package dev.openrs2.deob.map + +import dev.openrs2.asm.MemberRef +import java.util.SortedMap + +data class NameMap( + val classes: SortedMap, + val fields: SortedMap, + val methods: SortedMap +) diff --git a/deob-processor/build.gradle.kts b/deob-processor/build.gradle.kts new file mode 100644 index 00000000..6e5650de --- /dev/null +++ b/deob-processor/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `maven-publish` + kotlin("jvm") +} + +dependencies { + implementation(project(":deob-annotations")) + implementation(project(":deob-map")) + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${Versions.jackson}") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:${Versions.jackson}") +} + +publishing { + publications.create("maven") { + from(components["java"]) + + pom { + packaging = "jar" + name.set("OpenRS2 Deobfuscator Annotation Processor") + description.set( + """ + Processes deobfuscator annotations to create a mapping file of + obfuscated to refactored names. + """.trimIndent() + ) + } + } +} diff --git a/deob-processor/src/main/java/dev/openrs2/deob/processor/LocalVariableScanner.kt b/deob-processor/src/main/java/dev/openrs2/deob/processor/LocalVariableScanner.kt new file mode 100644 index 00000000..16f018f0 --- /dev/null +++ b/deob-processor/src/main/java/dev/openrs2/deob/processor/LocalVariableScanner.kt @@ -0,0 +1,19 @@ +package dev.openrs2.deob.processor + +import com.sun.source.tree.VariableTree +import com.sun.source.util.TreePathScanner +import com.sun.source.util.Trees +import dev.openrs2.deob.annotation.Pc + +class LocalVariableScanner(private val trees: Trees) : TreePathScanner>() { + override fun visitVariable(node: VariableTree, p: MutableMap): Void? { + val element = trees.getElement(currentPath) + + val pc = element.getAnnotation(Pc::class.java) + if (pc != null) { + p[pc.value] = element.simpleName.toString() + } + + return super.visitVariable(node, p) + } +} diff --git a/deob-processor/src/main/java/dev/openrs2/deob/processor/NameMapProcessor.kt b/deob-processor/src/main/java/dev/openrs2/deob/processor/NameMapProcessor.kt new file mode 100644 index 00000000..b2be0dc8 --- /dev/null +++ b/deob-processor/src/main/java/dev/openrs2/deob/processor/NameMapProcessor.kt @@ -0,0 +1,102 @@ +package dev.openrs2.deob.processor + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import com.sun.source.util.Trees +import dev.openrs2.asm.MemberRef +import dev.openrs2.asm.toInternalClassName +import dev.openrs2.deob.annotation.OriginalArg +import dev.openrs2.deob.annotation.OriginalClass +import dev.openrs2.deob.annotation.OriginalMember +import dev.openrs2.deob.map.Field +import dev.openrs2.deob.map.Method +import dev.openrs2.deob.map.NameMap +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.TreeMap +import javax.annotation.processing.AbstractProcessor +import javax.annotation.processing.ProcessingEnvironment +import javax.annotation.processing.RoundEnvironment +import javax.annotation.processing.SupportedAnnotationTypes +import javax.annotation.processing.SupportedOptions +import javax.annotation.processing.SupportedSourceVersion +import javax.lang.model.SourceVersion +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.TypeElement +import javax.lang.model.element.VariableElement + +@SupportedAnnotationTypes( + "dev.openrs2.deob.annotation.OriginalArg", + "dev.openrs2.deob.annotation.OriginalClass", + "dev.openrs2.deob.annotation.OriginalMember", + "dev.openrs2.deob.annotation.Pc" +) +@SupportedSourceVersion(SourceVersion.RELEASE_11) +@SupportedOptions( + "map" +) +class NameMapProcessor : AbstractProcessor() { + private val classes = TreeMap() + private val fields = TreeMap() + private val methods = TreeMap() + private lateinit var trees: Trees + private lateinit var localScanner: LocalVariableScanner + + override fun init(env: ProcessingEnvironment) { + super.init(env) + trees = Trees.instance(env) + localScanner = LocalVariableScanner(trees) + } + + private fun getPath(): Path? { + val map = processingEnv.options["map"] ?: return null + return Paths.get(map) + } + + override fun process(annotations: Set, env: RoundEnvironment): Boolean { + val mapPath = getPath() ?: return true + + for (element in env.getElementsAnnotatedWith(OriginalClass::class.java)) { + check(element is TypeElement) + + val originalClass = element.getAnnotation(OriginalClass::class.java)!! + classes[originalClass.value] = element.qualifiedName.toString().toInternalClassName() + } + + for (element in env.getElementsAnnotatedWith(OriginalMember::class.java)) { + val path = trees.getPath(element) + val owner = trees.getScope(path).enclosingClass.qualifiedName.toString().toInternalClassName() + val name = element.simpleName.toString() + + val originalMember = element.getAnnotation(OriginalMember::class.java)!! + val ref = MemberRef(originalMember.owner, originalMember.name, originalMember.descriptor) + + when (element) { + is VariableElement -> fields[ref] = Field(owner, name) + is ExecutableElement -> { + val arguments = element.parameters.map { parameter -> + val originalArg = parameter.getAnnotation(OriginalArg::class.java)!! + Pair(originalArg.value, parameter.simpleName.toString()) + }.toMap(LinkedHashMap()) + + val locals = TreeMap() + localScanner.scan(path, locals) + + methods[ref] = Method(owner, name, arguments, locals) + } + else -> error("Unexpected element type") + } + } + + if (env.processingOver()) { + Files.newBufferedWriter(mapPath).use { writer -> + val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule() + mapper.writeValue(writer, NameMap(classes, fields, methods)) + } + } + + return true + } +} diff --git a/deob-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/deob-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000..f89b051b --- /dev/null +++ b/deob-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +dev.openrs2.deob.processor.NameMapProcessor diff --git a/settings.gradle.kts b/settings.gradle.kts index 368773da..9fbb4411 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,6 +15,8 @@ include( "deob", "deob-annotations", "deob-ast", + "deob-map", + "deob-processor", "game", "util" )