Last active
November 6, 2023 00:49
-
-
Save tildejustin/1f6d4c2a9977978fca1d2df616f76859 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import time | |
from typing import TextIO | |
class Element: | |
containing_class: str | |
signature: str | |
official_name: str | |
intermediary_name: str | |
def __init__(self, definition: list[str]): | |
self.containing_class = definition[1] | |
self.signature = definition[2] | |
self.official_name = definition[3] | |
self.intermediary_name = definition[4] | |
def __str__(self) -> str: | |
return f"{self.intermediary_name}, {self.official_name}: {self.signature} in {self.containing_class}" | |
def find_element(signature: str, official_name: str, candidates: list[Element]) -> Element: | |
results = list(filter(lambda x: x.signature == signature and x.official_name == official_name, candidates)) | |
if len(results) != 1: | |
print(signature, official_name, [str(element) for element in candidates]) | |
raise AssertionError() | |
return results[0] | |
def rename_class(clas: str) -> str: | |
candidate = classes.get(clas) | |
if candidate is None: | |
return clas | |
return candidate | |
# some documentation on signatures | |
# https://jenkins.liteloader.com/view/Other/job/Mixin/javadoc/index.html?org/spongepowered/asm/mixin/injection/InjectionPoint.Selector.html | |
def remap_signature(signature: str) -> str: | |
is_method = signature[0] == "(" | |
method_part = [] | |
field_part = signature | |
if is_method: | |
sections = signature[1:].split(")") | |
# method_part = sections[0].split(";") | |
started = False | |
method_part = list() | |
name = list() | |
for c in sections[0]: | |
if c == "L" and not started: | |
started = True | |
name.append(c) | |
elif c == ";" and started: | |
started = False | |
method_part.append("".join(name)) | |
name = list() | |
elif started: | |
name.append(c) | |
else: | |
method_part.append(c) | |
field_part = sections[1] | |
for i, method_name in enumerate(method_part): | |
if method_name != "" and method_name[0] == "L": | |
method_part[i] = "L" + rename_class(method_name[1:]) + ";" | |
if ";" in field_part: | |
field_part = "L" + rename_class(field_part[1:-1]) + ";" | |
result = "" | |
if is_method: | |
result += "(" + "".join(method_part) + ")" | |
return result + field_part | |
def remap_file(file: TextIO) -> str: | |
output = "" | |
# fifo queue for inner classes | |
current_class: list[str] = list() | |
indent = 0 | |
for line in file: | |
current_line = line.strip().split(" ") | |
# go back to outer class after finishing inner class | |
if indent >= 1 and current_line[0] != "ARG" and indent != len(line) - len(line.lstrip("\t")): | |
indent = len(line) - len(line.lstrip("\t")) | |
while len(current_class) > indent: | |
current_class.pop() | |
match (current_line[0]): | |
case "CLASS": | |
indent += 1 | |
current_class.append(current_line[1]) | |
# already mapped | |
if len(current_line) == 2: | |
output += line | |
continue | |
parts = line.split(" ") | |
parts[1] = classes.get(parts[1]) | |
output += " ".join(parts) | |
case "METHOD" | "FIELD": | |
if current_line[1] == "<init>": | |
parts = line.split(" ") | |
parts[2] = remap_signature(parts[2]) | |
output += " ".join(parts) | |
continue | |
if len(current_line) == 3: | |
output += line | |
continue | |
element = find_element( | |
current_line[3], | |
current_line[1], | |
methods_by_class[current_class[indent - 1]] if current_line[0] == "METHOD" else | |
fields_by_class[current_class[indent - 1]] | |
) | |
parts = line.split(" ") | |
parts[1] = element.intermediary_name | |
parts[3] = remap_signature(element.signature) | |
output += " ".join(parts) + "\n" | |
# args and comments | |
case _: | |
output += line | |
return output | |
classes: dict[str, str] = dict() | |
methods: set[Element] = set() | |
fields: set[Element] = set() | |
with open("intermediary.tiny") as intermediary: | |
for line in intermediary.readlines(): | |
parts = line.strip().split("\t") | |
match (parts[0]): | |
case "CLASS": | |
classes[parts[1]] = parts[2] | |
case "METHOD": | |
methods.add(Element(parts)) | |
case "FIELD": | |
fields.add(Element(parts)) | |
methods_by_class: dict[str, list[Element]] = {clazz: [] for clazz in classes} | |
fields_by_class: dict[str, list[Element]] = {clazz: [] for clazz in classes} | |
for method in methods: | |
methods_by_class[method.containing_class].append(method) | |
for field in fields: | |
fields_by_class[field.containing_class].append(field) | |
def main(): | |
start = time.perf_counter() | |
for subdir, dirs, files in os.walk("mappings"): | |
for file in files: | |
# if "net\\minecraft\\advancement" not in subdir: | |
# continue | |
with open(subdir + "\\" + file, "r") as input: | |
dir = f"remapped{subdir.replace("mappings", "")}\\" | |
try: | |
os.makedirs(dir) | |
except FileExistsError: | |
pass | |
with open(dir + file, "w") as output: | |
try: | |
output.write(remap_file(input)) | |
except AssertionError: | |
print(file) | |
exit() | |
end = time.perf_counter() | |
print(end - start) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment