Attempt to remove arch and add AWToAt.java
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class AWToAT {
|
||||
private static final Map<String, String> ACCESS_POINT_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
ACCESS_POINT_MAP.put("accessible", "public");
|
||||
ACCESS_POINT_MAP.put("extendable", "public-f");
|
||||
ACCESS_POINT_MAP.put("mutable", "public-f");
|
||||
}
|
||||
|
||||
public String minecraftVersion;
|
||||
|
||||
public File remap(File file, String minecraftVersion) {
|
||||
this.minecraftVersion = minecraftVersion.replace("_", ".");
|
||||
File atFile = createATFile(file);
|
||||
processFile(file, atFile);
|
||||
return atFile;
|
||||
}
|
||||
|
||||
private File createATFile(File file) {
|
||||
File metaInf = new File(file.getParentFile(), "META-INF");
|
||||
if (!metaInf.exists() && !metaInf.mkdir()) throw new RuntimeException("Error creating META-INF folder");
|
||||
File atFile = new File(metaInf, "accesstransformer.cfg");
|
||||
try {
|
||||
atFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error creating new file", e);
|
||||
}
|
||||
return atFile;
|
||||
}
|
||||
|
||||
private void processFile(File file, File atFile) {
|
||||
/* Validates if we need to recreate the Access Transformer file if it's out of date */
|
||||
// Get the hash of the file
|
||||
String fileHash = getFileHash(file);
|
||||
try (Scanner atScanner = new Scanner(atFile)) {
|
||||
// Check if the AT file is up-to-date by comparing the hash of the file with the hash stored in the AT file
|
||||
boolean hashFound = false;
|
||||
while (atScanner.hasNextLine()) {
|
||||
String line = atScanner.nextLine();
|
||||
if (hashCheck(line, fileHash)) {
|
||||
hashFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the AT file is up-to-date, print a message and return
|
||||
if (hashFound) {
|
||||
System.out.println("Access Transformer file is already up to date.");
|
||||
return;
|
||||
}
|
||||
} catch (FileNotFoundException ignored) {
|
||||
// If the AT file does not exist, continue
|
||||
}
|
||||
|
||||
/* Creates the Access Transformer file */
|
||||
// Opens a scanner for reading the Access Widener file and a writer for writing to the Access Transformer file
|
||||
try (Scanner scanner = new Scanner(file); FileWriter writer = new FileWriter(atFile)) {
|
||||
// Create an ExecutorService with a fixed thread pool size equal to the number of available processors
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
// List to hold Future objects representing results of computation
|
||||
List<Future<String>> futures = new ArrayList<>();
|
||||
|
||||
// Write the hash of the file to the AT file
|
||||
writer.write("#DH_MAPPING_HASH:" + fileHash + "\n");
|
||||
|
||||
// Read each line from the file
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
// Skip lines starting with "accessWidener", "#" or blank lines
|
||||
if (line.startsWith("accessWidener") || line.startsWith("#") || line.isBlank()) continue;
|
||||
|
||||
// Submit the line to the executor service for processing
|
||||
// The processing is done by the processLine method
|
||||
futures.add(executor.submit(() -> processLine(line.split(" "))));
|
||||
}
|
||||
|
||||
// Write the results to the output file
|
||||
// The results are obtained by calling the get method on each Future
|
||||
for (Future<String> future : futures) {
|
||||
writer.write(future.get());
|
||||
}
|
||||
|
||||
// Shutdown the executor service to free up resources
|
||||
executor.shutdown();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error reading or writing to file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String processLine(String[] fields) {
|
||||
// fields[0] = access point like "accessible", "extendable", "mutable"
|
||||
// fields[1] = type like "field", "method", "class"
|
||||
// fields[2] = class name
|
||||
// fields[3] = field/method name
|
||||
// fields[4] = field/method descriptor
|
||||
|
||||
try {
|
||||
// Store the original field/method name
|
||||
String originalName = "";
|
||||
|
||||
// If there is a class name, replace the slashes with dots in the package name
|
||||
if (fields.length > 2) fields[2] = fields[2].replace("/", ".");
|
||||
|
||||
// If there is a field/method name, store the original name and remap it to SRG
|
||||
if (fields.length > 3) {
|
||||
originalName = fields[3];
|
||||
fields[3] = remapToSRG(fields[2], fields[3]);
|
||||
}
|
||||
|
||||
StringBuilder line = new StringBuilder(ACCESS_POINT_MAP.getOrDefault(fields[0], "public")).append(" ");
|
||||
switch (fields[1]) {
|
||||
case "field":
|
||||
line.append(fields[2]).append(" ").append(fields[3]).append(" #").append(originalName);
|
||||
// It'll be like: access-point class-name field-name-SRG # field-name-Mojmap
|
||||
// Eg: public net.minecraft.client.Minecraft f_90981_ # instance
|
||||
break;
|
||||
case "method":
|
||||
line.append(fields[2]).append(" ").append(fields[3]).append(fields[4]).append(" #").append(originalName);
|
||||
// It'll be like: access-point class-name method-name-SRG method-descriptor # method-name-Mojmap
|
||||
// Eg: public net.minecraft.client.Minecraft m_172797_()Lnet/minecraft/client/Minecraft; # getInstance
|
||||
break;
|
||||
default:
|
||||
line.append(fields[2]);
|
||||
// It'll be like: access-point class-name
|
||||
// Eg: public net.minecraft.client.Minecraft
|
||||
break;
|
||||
}
|
||||
line.append("\n");
|
||||
return line.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error processing line", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hashCheck(String line, String fileHash) {
|
||||
if (line.startsWith("#DH_MAPPING_HASH:")) {
|
||||
String hash = line.substring(17);
|
||||
return hash.equals(fileHash);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getFileHash(File file) {
|
||||
try {
|
||||
MessageDigest shaDigest = MessageDigest.getInstance("SHA-256");
|
||||
try (InputStream fis = new FileInputStream(file)) {
|
||||
byte[] byteArray = new byte[1024];
|
||||
int bytesCount;
|
||||
|
||||
// Read file data and update in message digest
|
||||
while ((bytesCount = fis.read(byteArray)) != -1) {
|
||||
shaDigest.update(byteArray, 0, bytesCount);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = shaDigest.digest();
|
||||
|
||||
// Convert byte array into signum representation
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte aByte : bytes) {
|
||||
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
|
||||
// Return complete hash
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WARNING: BELOW LIES HIGHLY CURSED CODE AND MIGHT EVEN BE ILLEGAL
|
||||
|
||||
|
||||
|
||||
|
||||
// Flag to track if there was an error in the GET request
|
||||
boolean error = false;
|
||||
|
||||
/**
|
||||
* This method returns a field or method name from Mojang mappings as SRG mappings.
|
||||
* It makes a GET request to the Linkie API to fetch the SRG name.
|
||||
*
|
||||
* @param clazz The class name
|
||||
* @param name The field or method name
|
||||
* @return The SRG name
|
||||
* @throws Exception If there is an error in the GET request or the SRG name is not found in the response
|
||||
*/
|
||||
private String remapToSRG(String clazz, String name) throws Exception {
|
||||
// Encode the class and field/method name to be used in the URL
|
||||
String query = URLEncoder.encode(clazz + "." + name, StandardCharsets.UTF_8);
|
||||
// Construct the URL for the GET request
|
||||
String urlString = "https://linkieapi.shedaniel.me/api/search?namespace=mojang&query=" + query + "&version=" + this.minecraftVersion + "&limit=1&allowClasses=false&allowFields=true&allowMethods=true&translate=mojang_srg";
|
||||
URL url = new URI(urlString).toURL();
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuilder content = new StringBuilder();
|
||||
// Read the response line by line
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
conn.disconnect();
|
||||
// Regex to find the SRG name in the response
|
||||
Pattern pattern = Pattern.compile("\"l\"\\s*:\\s*\\{[^}]*\"i\"\\s*:\\s*\"([^\"]*)\"");
|
||||
Matcher matcher = pattern.matcher(content.toString());
|
||||
if (matcher.find()) return matcher.group(1);
|
||||
else throw new Exception("Couldn't find the SRG mapping for name: " + name + "\nCould not find 'i' in 'l' object in the response"); // `i` is the SRG name which is stored in the `l` JSON object
|
||||
} else {
|
||||
if (error) {
|
||||
// If there was an error in the GET request, and we already tried again, throw an exception
|
||||
throw new Exception("The GET request failed");
|
||||
}
|
||||
// If there was an error in the GET request, wait 2.5 seconds and try again as we probably got rate limited
|
||||
error = true;
|
||||
Thread.sleep(2500);
|
||||
return remapToSRG(clazz, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user