-
-
Notifications
You must be signed in to change notification settings - Fork 44
/
PrePatcher.java
130 lines (123 loc) · 4.81 KB
/
PrePatcher.java
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
119
120
121
122
123
124
125
126
127
128
129
130
package me.nallar.nmsprepatcher;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Splitter;
import me.nallar.tickthreading.Log;
// The prepatcher adds method declarations in superclasses,
// so javac can compile the patch classes if they need to use a method/field they
// add on an instance other than this
public class PrePatcher {
private static final Logger log = Logger.getLogger("PatchLogger");
private static final Pattern privatePattern = Pattern.compile("^(\\s+?)private", Pattern.MULTILINE);
private static final Pattern extendsPattern = Pattern.compile("\\s+?extends\\s+?([\\S]+)[^\\{]+?\\{", Pattern.DOTALL | Pattern.MULTILINE);
private static final Pattern declareMethodPattern = Pattern.compile("@Declare\\s+?(public\\s+?(\\S*?)?\\s+?(\\S*?)\\s*?\\S+?\\s*?\\([^\\{]*\\)\\s*?\\{)", Pattern.DOTALL | Pattern.MULTILINE);
private static final Pattern declareVariablePattern = Pattern.compile("@Declare\\s+?//(public [^;\r\n]+?;)", Pattern.DOTALL | Pattern.MULTILINE);
public static void patch(File patchDirectory, File sourceDirectory) {
if (!patchDirectory.isDirectory()) {
throw new IllegalArgumentException("Not a directory! " + patchDirectory + ", " + sourceDirectory);
}
for (File file : patchDirectory.listFiles()) {
String contents = readFile(file);
if (contents == null) {
log.log(Level.SEVERE, "Failed to read " + file);
continue;
}
Matcher extendsMatcher = extendsPattern.matcher(contents);
if (!extendsMatcher.find()) {
log.info(file + " does not extend an NMS class.");
continue;
}
String shortClassName = extendsMatcher.group(1);
String className = null;
for (String line : Splitter.on('\n').split(contents)) {
if (line.endsWith('.' + shortClassName + ';')) {
className = line.substring(7, line.length() - 1);
}
}
if (className == null) {
log.info("Unable to find class " + shortClassName);
continue;
}
File sourceFile = new File(sourceDirectory, className.replace('.', '/') + ".java");
if (!sourceFile.exists()) {
log.severe("Can't find " + sourceFile + ", not patching.");
continue;
}
String sourceString = readFile(sourceFile).trim();
int previousIndex = sourceString.indexOf("\n//PREPATCH\n");
int cutIndex = previousIndex == -1 ? sourceString.lastIndexOf('}') : previousIndex;
StringBuilder source = new StringBuilder(sourceString.substring(0, cutIndex)).append("\n//PREPATCH\n");
log.info("Prepatching declarations for " + className);
Matcher matcher = declareMethodPattern.matcher(contents);
while (matcher.find()) {
String type = matcher.group(2);
String ret = "null"; // TODO: Add more types.
if ("static".equals(type)) {
type = matcher.group(3);
}
if ("boolean".equals(type)) {
ret = "false";
} else if ("void".equals(type)) {
ret = "";
} else if ("int".equals(type)) {
ret = "0";
} else if ("float".equals(type)) {
ret = "0f";
} else if ("double".equals(type)) {
ret = "0.0";
}
String decl = matcher.group(1) + "return " + ret + ";}";
log.info("adding " + type + " -> " + decl);
if (source.indexOf(decl) == -1) {
source.append(decl).append('\n');
}
}
Matcher variableMatcher = declareVariablePattern.matcher(contents);
while (variableMatcher.find()) {
String var = variableMatcher.group(1);
log.info("adding " + var);
source.append(var).append('\n');
}
source.append("\n}");
sourceString = source.toString();
// TODO: Fix package -> public properly later.
sourceString = sourceString.replace(" boolean isOutputEncrypted;", " public boolean isOutputEncrypted;");
sourceString = sourceString.replace(" final ", " ");
Matcher privateMatcher = privatePattern.matcher(sourceString);
sourceString = privateMatcher.replaceAll("$1protected");
try {
writeFile(sourceFile, sourceString);
} catch (IOException e) {
log.log(Level.SEVERE, "Failed to save " + sourceFile, e);
}
}
}
private static String readFile(File file) {
Scanner fileReader = null;
try {
fileReader = new Scanner(file, "UTF-8").useDelimiter("\\A");
return fileReader.next().replace("\r\n", "\n");
} catch (FileNotFoundException ignored) {
} finally {
if (fileReader != null) {
fileReader.close();
}
}
return null;
}
private static void writeFile(File file, String contents) throws IOException {
FileWriter fileWriter = new FileWriter(file);
try {
fileWriter.write(contents);
} finally {
fileWriter.close();
}
}
}