2022-07-31 20:41:56 +02:00
|
|
|
/*
|
2024-04-17 17:23:43 +02:00
|
|
|
* This file is part of FalsePatternLib.
|
|
|
|
|
*
|
2025-05-24 19:29:12 +02:00
|
|
|
* Copyright (C) 2022-2025 FalsePattern
|
2022-07-30 15:21:11 +02:00
|
|
|
* All Rights Reserved
|
|
|
|
|
*
|
2024-04-17 17:23:43 +02:00
|
|
|
* The above copyright notice and this permission notice shall be included
|
|
|
|
|
* in all copies or substantial portions of the Software.
|
2022-07-30 15:21:11 +02:00
|
|
|
*
|
2024-04-17 17:23:43 +02:00
|
|
|
* FalsePatternLib is free software: you can redistribute it and/or modify
|
2022-07-30 15:21:11 +02:00
|
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
2025-05-24 19:29:12 +02:00
|
|
|
* the Free Software Foundation, only version 3 of the License.
|
2022-07-30 15:21:11 +02:00
|
|
|
*
|
2024-04-17 17:23:43 +02:00
|
|
|
* FalsePatternLib is distributed in the hope that it will be useful,
|
2022-07-30 15:21:11 +02:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2024-04-17 17:23:43 +02:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU Lesser General Public License for more details.
|
2022-07-30 15:21:11 +02:00
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
2024-04-17 17:23:43 +02:00
|
|
|
* along with FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
|
2022-07-30 15:21:11 +02:00
|
|
|
*/
|
2022-07-25 20:21:43 +02:00
|
|
|
package com.falsepattern.lib.asm;
|
|
|
|
|
|
2022-07-25 21:10:54 +02:00
|
|
|
import com.falsepattern.lib.asm.exceptions.AsmClassNotFoundException;
|
|
|
|
|
import com.falsepattern.lib.asm.exceptions.AsmFieldNotFoundException;
|
|
|
|
|
import com.falsepattern.lib.asm.exceptions.AsmMethodNotFoundException;
|
2022-08-20 20:08:26 +02:00
|
|
|
import com.falsepattern.lib.internal.asm.CoreLoadingPlugin;
|
2022-07-25 20:21:43 +02:00
|
|
|
import com.falsepattern.lib.mapping.MappingManager;
|
|
|
|
|
import com.falsepattern.lib.mapping.types.MappingType;
|
|
|
|
|
import com.falsepattern.lib.mapping.types.NameType;
|
|
|
|
|
import com.falsepattern.lib.mapping.types.UniversalClass;
|
|
|
|
|
import com.falsepattern.lib.mapping.types.UniversalField;
|
|
|
|
|
import com.falsepattern.lib.mapping.types.UniversalMethod;
|
|
|
|
|
import lombok.SneakyThrows;
|
|
|
|
|
import lombok.val;
|
2022-07-25 21:10:54 +02:00
|
|
|
import org.objectweb.asm.ClassReader;
|
|
|
|
|
import org.objectweb.asm.ClassWriter;
|
|
|
|
|
import org.objectweb.asm.Opcodes;
|
2022-07-25 20:21:43 +02:00
|
|
|
import org.objectweb.asm.tree.ClassNode;
|
|
|
|
|
import org.objectweb.asm.tree.FieldNode;
|
|
|
|
|
import org.objectweb.asm.tree.MethodNode;
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
|
|
public class ASMUtil {
|
|
|
|
|
public static FieldNode findFieldStandard(ClassNode cn, String name, boolean optional) {
|
|
|
|
|
for (final FieldNode ret : cn.fields) {
|
|
|
|
|
if (name.equals(ret.name)) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (optional) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
throw new AsmFieldNotFoundException(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SneakyThrows
|
|
|
|
|
public static FieldNode findFieldFromMCP(ClassNode cn, String fieldName, boolean optional) {
|
|
|
|
|
val classMapping = discoverClassMappingType(cn);
|
|
|
|
|
if (classMapping == null) {
|
2023-07-16 13:09:05 +02:00
|
|
|
throw new AsmClassNotFoundException("The class "
|
|
|
|
|
+ cn
|
|
|
|
|
+ " is not from Minecraft, or the mapping manager"
|
|
|
|
|
+ "doesn't have it, cannot use findFieldFromMCP! Use findFieldStandard instead!");
|
2022-07-25 20:21:43 +02:00
|
|
|
}
|
2023-07-16 13:09:05 +02:00
|
|
|
return findFieldStandard(cn,
|
|
|
|
|
MappingManager.classForName(NameType.Internal, classMapping, cn.name)
|
|
|
|
|
.getField(MappingType.MCP, fieldName)
|
|
|
|
|
.getName(classMapping),
|
|
|
|
|
optional);
|
2022-07-25 20:21:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FieldNode findFieldFromUniversal(ClassNode cn, UniversalField field, boolean optional) {
|
2022-07-31 20:41:56 +02:00
|
|
|
String[] possibilities = CoreLoadingPlugin.isObfuscated() ? new String[]{field.getName(MappingType.SRG),
|
|
|
|
|
field.getName(MappingType.Notch)}
|
|
|
|
|
: new String[]{field.getName(MappingType.MCP)};
|
2022-07-25 20:21:43 +02:00
|
|
|
for (final FieldNode ret : cn.fields) {
|
|
|
|
|
if (anyEquals(ret.name, possibilities)) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (optional) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-07-31 20:41:56 +02:00
|
|
|
throw new AsmFieldNotFoundException(
|
|
|
|
|
possibilities.length == 1 ? possibilities[0] : Arrays.toString(possibilities));
|
2022-07-25 20:21:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static MethodNode findMethodStandard(ClassNode cn, String name, String descriptor, boolean optional) {
|
|
|
|
|
for (final MethodNode ret : cn.methods) {
|
|
|
|
|
if (name.equals(ret.name) && descriptor.equals(ret.desc)) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (optional) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
throw new AsmMethodNotFoundException(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SneakyThrows
|
|
|
|
|
public static MethodNode findMethodFromMCP(ClassNode cn, String mcpName, String mcpDescriptor, boolean optional) {
|
|
|
|
|
val classMapping = discoverClassMappingType(cn);
|
|
|
|
|
if (classMapping == null) {
|
2023-07-16 13:09:05 +02:00
|
|
|
throw new AsmClassNotFoundException("The class "
|
|
|
|
|
+ cn
|
|
|
|
|
+ " is not from Minecraft, or the mapping manager"
|
|
|
|
|
+ "doesn't have it, cannot use findMethodFromMCP! Use findFieldStandard instead!");
|
2022-07-25 20:21:43 +02:00
|
|
|
}
|
|
|
|
|
val method = MappingManager.classForName(NameType.Internal, classMapping, cn.name)
|
|
|
|
|
.getMethod(MappingType.MCP, mcpName, mcpDescriptor);
|
|
|
|
|
return findMethodStandard(cn, method.getName(classMapping), method.getDescriptor(classMapping), optional);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static MethodNode findMethodFromUniversal(ClassNode cn, UniversalMethod method, boolean optional) {
|
2022-07-31 20:41:56 +02:00
|
|
|
String[] possibleNames = CoreLoadingPlugin.isObfuscated() ? new String[]{method.getName(MappingType.SRG),
|
|
|
|
|
method.getName(MappingType.Notch)}
|
|
|
|
|
: new String[]{method.getName(MappingType.MCP)};
|
|
|
|
|
String[] possibleDescriptors =
|
|
|
|
|
CoreLoadingPlugin.isObfuscated() ? new String[]{method.getDescriptor(MappingType.SRG),
|
|
|
|
|
method.getDescriptor(MappingType.Notch)}
|
|
|
|
|
: new String[]{method.getDescriptor(MappingType.MCP)};
|
2022-07-25 20:21:43 +02:00
|
|
|
for (final MethodNode ret : cn.methods) {
|
|
|
|
|
if (anyEquals(ret.name, possibleNames) && anyEquals(ret.desc, possibleDescriptors)) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (optional) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-07-31 20:41:56 +02:00
|
|
|
throw new AsmFieldNotFoundException(
|
|
|
|
|
possibleDescriptors.length == 1 ? possibleDescriptors[0] : Arrays.toString(possibleDescriptors));
|
2022-07-25 20:21:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static MappingType discoverClassMappingType(ClassNode cn) {
|
2023-11-26 18:06:10 +01:00
|
|
|
if (!CoreLoadingPlugin.isObfuscated()) {
|
2022-07-25 20:21:43 +02:00
|
|
|
if (MappingManager.containsClass(NameType.Internal, MappingType.MCP, cn.name)) {
|
|
|
|
|
return MappingType.MCP;
|
|
|
|
|
}
|
|
|
|
|
} else if (MappingManager.containsClass(NameType.Internal, MappingType.SRG, cn.name)) {
|
|
|
|
|
return MappingType.SRG;
|
|
|
|
|
} else if (MappingManager.containsClass(NameType.Internal, MappingType.Notch, cn.name)) {
|
|
|
|
|
return MappingType.Notch;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static UniversalClass toUniversalClass(ClassNode cn) {
|
2023-11-26 18:06:10 +01:00
|
|
|
if (!CoreLoadingPlugin.isObfuscated()) {
|
2022-07-25 20:21:43 +02:00
|
|
|
try {
|
|
|
|
|
return MappingManager.classForName(NameType.Internal, MappingType.MCP, cn.name);
|
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
|
throw new AsmClassNotFoundException(cn.name);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
return MappingManager.classForName(NameType.Internal, MappingType.SRG, cn.name);
|
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
|
try {
|
|
|
|
|
return MappingManager.classForName(NameType.Internal, MappingType.Notch, cn.name);
|
|
|
|
|
} catch (ClassNotFoundException ex) {
|
|
|
|
|
throw new AsmClassNotFoundException(cn.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-25 21:10:54 +02:00
|
|
|
public static ClassNode parseClass(byte[] bytes, int readerFlags) {
|
|
|
|
|
val cn = new ClassNode(Opcodes.ASM5);
|
|
|
|
|
val reader = new ClassReader(bytes);
|
|
|
|
|
reader.accept(cn, readerFlags);
|
|
|
|
|
return cn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] serializeClass(ClassNode cn, int writerFlags) {
|
|
|
|
|
val writer = new ClassWriter(writerFlags);
|
2022-08-21 11:09:33 +02:00
|
|
|
cn.accept(writer);
|
2022-07-25 21:10:54 +02:00
|
|
|
return writer.toByteArray();
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-25 20:21:43 +02:00
|
|
|
public static boolean anyEquals(String str, String... options) {
|
2022-07-31 20:41:56 +02:00
|
|
|
for (val option : options) {
|
2022-07-25 20:21:43 +02:00
|
|
|
if (Objects.equals(str, option)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|