blob: c7db68b770033801ec5abf7ab544393fbaee681e (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
package me.djtheredstoner.perspectivemod.asm;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import me.djtheredstoner.perspectivemod.asm.transformers.ActiveRenderInfoTransformer;
import me.djtheredstoner.perspectivemod.asm.transformers.EntityRendererTransformer;
import me.djtheredstoner.perspectivemod.asm.transformers.MinecraftTransformer;
import me.djtheredstoner.perspectivemod.asm.transformers.RenderManagerTransformer;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
public class ClassTransformer implements IClassTransformer {
// create a logger to distinguish our errors from a normal error
private static final Logger LOGGER = LogManager.getLogger("Perspective Mod v4 Transformer");
// create a map of transformers
private final Multimap<String, ITransformer> transformerMap = ArrayListMultimap.create();
// make a jvm flag that could be used to dump transformed classes
// usable by adding -DdebugBytecode=true to the jvm arguments
public static final boolean outputBytecode = Boolean.parseBoolean(System.getProperty("debugBytecode", "false"));
public ClassTransformer() {
// any transformer will be registered here
registerTransformer(new EntityRendererTransformer());
registerTransformer(new RenderManagerTransformer());
registerTransformer(new MinecraftTransformer());
registerTransformer(new ActiveRenderInfoTransformer());
}
private void registerTransformer(ITransformer transformer) {
// loop through names of classes
for (String cls : transformer.getClassName()) {
// put the classes into the transformer map
transformerMap.put(cls, transformer);
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (bytes == null) return null;
// get the list of transformers
Collection<ITransformer> transformers = transformerMap.get(transformedName);
// if empty, don't bother trying to run through transformation
if (transformers.isEmpty()) return bytes;
// wjat
ClassReader reader = new ClassReader(bytes);
ClassNode node = new ClassNode();
reader.accept(node, ClassReader.EXPAND_FRAMES);
// for every transformer, perform the transformations
for (ITransformer transformer : transformers) {
transformer.transform(node, transformedName);
}
// what?????
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
try {
// write™
node.accept(writer);
} catch (Throwable t) {
LOGGER.error("Exception when transforming " + transformedName + " : " + t.getClass().getSimpleName());
t.printStackTrace();
}
if (outputBytecode) {
File bytecodeDirectory = new File("bytecode");
String transformedClassName;
// anonymous classes
if (transformedName.contains("$")) {
transformedClassName = transformedName.replace('$', '.') + ".class";
} else {
transformedClassName = transformedName + ".class";
}
if (!bytecodeDirectory.exists()) {
bytecodeDirectory.mkdirs();
}
File bytecodeOutput = new File(bytecodeDirectory, transformedClassName);
try {
if (!bytecodeOutput.exists()) {
bytecodeOutput.createNewFile();
}
} catch (Exception e) {
e.printStackTrace();
}
try (FileOutputStream os = new FileOutputStream(bytecodeOutput)) {
// write to the generated class to /run/bytecode/classfile.class
// with the class bytes from transforming
os.write(writer.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
// return the written bytes and finalize transform
return writer.toByteArray();
}
}
|