2
2
3
3
import static org .mcphackers .mcp .MCPPaths .*;
4
4
5
+ import java .io .File ;
6
+ import java .io .IOException ;
7
+ import java .net .URI ;
8
+ import java .nio .file .FileSystem ;
9
+ import java .nio .file .FileSystems ;
5
10
import java .nio .file .Files ;
6
11
import java .nio .file .Path ;
12
+ import java .util .ArrayList ;
13
+ import java .util .HashMap ;
7
14
import java .util .List ;
15
+ import java .util .Map ;
16
+ import java .util .regex .Pattern ;
17
+ import java .util .stream .Stream ;
8
18
9
19
import org .mcphackers .mcp .MCP ;
10
20
import org .mcphackers .mcp .MCPPaths ;
11
21
import org .mcphackers .mcp .tasks .mode .TaskParameter ;
12
22
import org .mcphackers .mcp .tools .FileUtil ;
23
+ import org .mcphackers .mcp .tools .mappings .MappingUtil ;
24
+ import org .mcphackers .rdi .injector .data .ClassStorage ;
25
+ import org .mcphackers .rdi .injector .data .Mappings ;
26
+ import org .mcphackers .rdi .nio .ClassStorageWriter ;
27
+ import org .mcphackers .rdi .nio .MappingsIO ;
28
+ import org .mcphackers .rdi .nio .RDInjector ;
29
+ import org .objectweb .asm .ClassWriter ;
13
30
14
31
public class TaskBuild extends TaskStaged {
15
32
/*
@@ -26,40 +43,7 @@ public TaskBuild(Side side, MCP instance) {
26
43
@ Override
27
44
protected Stage [] setStages () {
28
45
Path bin = MCPPaths .get (mcp , BIN , side );
29
- return new Stage []{
30
- stage (getLocalizedStage ("recompile" ),
31
- () -> new TaskRecompile (side , mcp , this ).doTask ()),
32
- stage (getLocalizedStage ("reobf" ), 50 ,
33
- () -> new TaskReobfuscate (side , mcp , this ).doTask ()),
34
- stage (getLocalizedStage ("build" ), 70 ,
35
- () -> {
36
- Side [] sides = side == Side .MERGED ? new Side []{Side .CLIENT , Side .SERVER } : new Side []{side };
37
- for (Side localSide : sides ) {
38
- Path originalJar = MCPPaths .get (mcp , JAR_ORIGINAL , localSide );
39
- Path reobfDir = MCPPaths .get (mcp , REOBF_SIDE , localSide );
40
- Path buildJar = MCPPaths .get (mcp , BUILD_JAR , localSide );
41
- Path buildZip = MCPPaths .get (mcp , BUILD_ZIP , localSide );
42
- FileUtil .createDirectories (MCPPaths .get (mcp , BUILD ));
43
- if (mcp .getOptions ().getBooleanParameter (TaskParameter .FULL_BUILD )) {
44
- Files .deleteIfExists (buildJar );
45
- Files .copy (originalJar , buildJar );
46
- List <Path > reobfClasses = FileUtil .walkDirectory (reobfDir , path -> !Files .isDirectory (path ));
47
- FileUtil .packFilesToZip (buildJar , reobfClasses , reobfDir );
48
- List <Path > assets = FileUtil .walkDirectory (bin , path -> !Files .isDirectory (path ) && !path .getFileName ().toString ().endsWith (".class" ));
49
- FileUtil .packFilesToZip (buildJar , assets , bin );
50
- FileUtil .deleteFileInAZip (buildJar , "/META-INF/MOJANG_C.DSA" );
51
- FileUtil .deleteFileInAZip (buildJar , "/META-INF/MOJANG_C.SF" );
52
- FileUtil .deleteFileInAZip (buildJar , "/META-INF/CODESIGN.DSA" );
53
- FileUtil .deleteFileInAZip (buildJar , "/META-INF/CODESIGN.SF" );
54
- } else {
55
- Files .deleteIfExists (buildZip );
56
- FileUtil .compress (reobfDir , buildZip );
57
- List <Path > assets = FileUtil .walkDirectory (bin , path -> !Files .isDirectory (path ) && !path .getFileName ().toString ().endsWith (".class" ));
58
- FileUtil .packFilesToZip (buildZip , assets , bin );
59
- }
60
- }
61
- })
62
- };
46
+ return new Stage []{stage (getLocalizedStage ("recompile" , 0 ), () -> new TaskRecompile (side , mcp , this ).doTask ()), stage (getLocalizedStage ("gathermd5" , 25 ), () -> new TaskUpdateMD5 (side , mcp , this ).updateMD5 (true )), stage (getLocalizedStage ("reobf" ), 50 , this ::reobfuscate )};
63
47
}
64
48
65
49
@ Override
@@ -80,4 +64,187 @@ public void setProgress(int progress) {
80
64
break ;
81
65
}
82
66
}
67
+
68
+ // Reobfuscation utilities
69
+ private Mappings getMappings (ClassStorage storage , Side side ) throws IOException {
70
+ Path mappingsPath = MCPPaths .get (mcp , MAPPINGS );
71
+ if (!Files .exists (mappingsPath )) {
72
+ return new Mappings ();
73
+ }
74
+ final boolean enableObfuscation = mcp .getOptions ().getBooleanParameter (TaskParameter .OBFUSCATION );
75
+ boolean joined = MappingUtil .readNamespaces (mappingsPath ).contains ("official" );
76
+ Mappings mappings = MappingsIO .read (mappingsPath , "named" , joined ? "official" : side .name );
77
+ modifyClassMappings (mappings , storage .getAllClasses (), enableObfuscation );
78
+ return mappings ;
79
+ }
80
+
81
+ private void modifyClassMappings (Mappings mappings , List <String > classNames , boolean obf ) {
82
+ Map <String , Integer > obfIndexes = new HashMap <>();
83
+ Map <String , String > packageMappings = getPackageMappings (mappings .classes );
84
+ for (String className : classNames ) {
85
+ String reobfName = mappings .classes .get (className );
86
+ if (reobfName == null /*&& !hashes.containsKey(className)*/ ) {
87
+ int i1 = className .lastIndexOf ('/' );
88
+ String packageName = i1 == -1 ? "" : className .substring (0 , i1 + 1 );
89
+ String obfPackage = packageMappings .get (packageName );
90
+ String clsName = i1 == -1 ? className : className .substring (i1 + 1 );
91
+ if (obf ) {
92
+ int obfIndex = obfIndexes .getOrDefault (obfPackage , 0 );
93
+ String obfName = MappingUtil .getObfuscatedName (obfIndex );
94
+ List <String > obfNames = new ArrayList <>();
95
+ for (Map .Entry <String , String > entry : mappings .classes .entrySet ()) {
96
+ obfNames .add (entry .getValue ());
97
+ }
98
+ while (obfNames .contains (obfPackage + obfName )) {
99
+ obfIndex ++;
100
+ obfName = MappingUtil .getObfuscatedName (obfIndex );
101
+ }
102
+ if (obfIndex > obfIndexes .getOrDefault (obfPackage , 0 )) {
103
+ obfIndexes .put (obfPackage , obfIndex );
104
+ }
105
+ clsName = obfName ;
106
+ }
107
+ if (obf || obfPackage != null ) {
108
+ String className2 = (obfPackage == null ? packageName : obfPackage ) + clsName ;
109
+ mappings .classes .put (className , className2 );
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ private Map <String , String > gatherMD5Hashes (boolean reobf ) throws IOException {
116
+ final Path md5 = MCPPaths .get (mcp , reobf ? MCPPaths .MD5_RO : MCPPaths .MD5 , side );
117
+ Map <String , String > hashes = new HashMap <>();
118
+
119
+ try (Stream <String > lines = Files .lines (md5 )) {
120
+ lines .forEach ((line ) -> {
121
+ String [] tokens = line .split (" " );
122
+ hashes .put (tokens [0 ], tokens [1 ]);
123
+ });
124
+ }
125
+ return hashes ;
126
+ }
127
+
128
+ private static Map <String , String > getPackageMappings (Map <String , String > classMappings ) {
129
+ Map <String , String > packageMappings = new HashMap <>();
130
+ for (Map .Entry <String , String > entry : classMappings .entrySet ()) {
131
+ int i1 = entry .getKey ().lastIndexOf ('/' );
132
+ int i2 = entry .getValue ().lastIndexOf ('/' );
133
+ String name1 = i1 == -1 ? "" : entry .getKey ().substring (0 , i1 + 1 );
134
+ String name2 = i2 == -1 ? "" : entry .getKey ().substring (0 , i2 + 1 );
135
+ packageMappings .put (name1 , name2 );
136
+ }
137
+ return packageMappings ;
138
+ }
139
+
140
+ private void reobfuscate () throws IOException {
141
+ final Path reobfBin = MCPPaths .get (mcp , BIN , side );
142
+
143
+ Side [] sides = side == Side .MERGED ? new Side []{Side .CLIENT , Side .SERVER } : new Side []{side };
144
+
145
+ Map <String , String > originalHashes = gatherMD5Hashes (false );
146
+ Map <String , String > recompHashes = gatherMD5Hashes (true );
147
+
148
+ for (Side localSide : sides ) {
149
+ final Path reobfJar = MCPPaths .get (mcp , REOBF_JAR , localSide );
150
+ Files .deleteIfExists (reobfJar );
151
+ RDInjector injector = new RDInjector (reobfBin );
152
+ Mappings mappings = getMappings (injector .getStorage (), localSide );
153
+ if (mappings != null ) {
154
+ injector .applyMappings (mappings );
155
+ }
156
+ injector .transform ();
157
+ new ClassStorageWriter (injector .getStorage (), ClassWriter .COMPUTE_MAXS ).write (Files .newOutputStream (reobfJar ));
158
+
159
+ Map <String , String > reversedNames = new HashMap <>();
160
+ if (mappings != null ) {
161
+ for (Map .Entry <String , String > entry : mappings .classes .entrySet ()) {
162
+ reversedNames .put (entry .getValue (), entry .getKey ());
163
+ }
164
+ }
165
+ Pattern regexPattern = Pattern .compile (mcp .getOptions ().getStringParameter (TaskParameter .EXCLUDED_CLASSES ));
166
+ List <Path > changedFiles = new ArrayList <>();
167
+ List <Path > assets = new ArrayList <>(FileUtil .walkDirectory (reobfBin , p -> !Files .isDirectory (p ) && !p .toString ().endsWith (".class" )));
168
+
169
+ // Identify differences
170
+ try (FileSystem fs = FileSystems .newFileSystem (reobfJar , null )) {
171
+ Iterable <Path > rootDirectories = fs .getRootDirectories ();
172
+ for (Path root : rootDirectories ) {
173
+ try (Stream <Path > stream = Files .walk (root )) {
174
+ stream .forEach (path -> {
175
+ if (!Files .isDirectory (path )) {
176
+ String obfClassName = root .relativize (path ).toString ().replace (".class" , "" );
177
+ // Force inner classes to compare outer class hash
178
+ String className = obfClassName ;
179
+ int index = className .indexOf ('$' );
180
+ if (index != -1 ) {
181
+ className = className .substring (0 , index );
182
+ }
183
+ String deobfName = reversedNames .get (className );
184
+ if (deobfName == null ) {
185
+ deobfName = className ;
186
+ }
187
+ String hash = originalHashes .get (deobfName );
188
+ String hashModified = recompHashes .get (deobfName );
189
+ boolean extract = (hash == null ) || !hash .equals (hashModified ) && !regexPattern .matcher (deobfName ).matches ();
190
+ if (extract ) {
191
+ changedFiles .add (path );
192
+ System .out .println (reversedNames .get (obfClassName ) + " : " + obfClassName );
193
+ }
194
+ }
195
+ });
196
+ }
197
+ }
198
+ }
199
+
200
+ // Export differences to JAR/ZIP file
201
+ Path buildJar = MCPPaths .get (mcp , BUILD_JAR , localSide );
202
+ Path buildZip = MCPPaths .get (mcp , BUILD_ZIP , localSide );
203
+ try {
204
+ FileUtil .createDirectories (MCPPaths .get (mcp , BUILD ));
205
+ // Simply copy re-obfuscated JAR
206
+ if (mcp .getOptions ().getBooleanParameter (TaskParameter .FULL_BUILD )) {
207
+ Files .deleteIfExists (buildJar );
208
+ Files .copy (reobfJar , buildJar );
209
+ try (FileSystem fs = FileSystems .newFileSystem (buildJar , null )) {
210
+ // Copy assets
211
+ for (Path asset : assets ) {
212
+ Files .copy (Files .newInputStream (asset ), fs .getPath (reobfBin .relativize (asset ).toString ()));
213
+ }
214
+ }
215
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/MOJANG_C.DSA" );
216
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/MOJANG_C.SF" );
217
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/CODESIGN.DSA" );
218
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/CODESIGN.RSA" );
219
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/CODESIGN.SF" );
220
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/MOJANGCS.RSA" );
221
+ FileUtil .deleteFileInAZip (buildJar , "/META-INF/MOJANGCS.SF" );
222
+ } else {
223
+ Files .deleteIfExists (buildZip );
224
+
225
+ Map <String , String > env = new HashMap <>();
226
+ env .put ("create" , "true" );
227
+
228
+ URI buildZipURI = URI .create ("jar:file:" + buildZip .toUri ().getPath ());
229
+
230
+ try (FileSystem reobfFs = FileSystems .newFileSystem (reobfJar , null )) {
231
+ try (FileSystem buildFs = FileSystems .newFileSystem (buildZipURI , env , null )) {
232
+ // Copy different files
233
+ for (Path changedFile : changedFiles ) {
234
+ Path file = reobfFs .getPath (changedFile .toString ());
235
+ Files .copy (file , buildFs .getPath (changedFile .toString ()));
236
+ }
237
+
238
+ // Copy assets
239
+ for (Path asset : assets ) {
240
+ Files .copy (Files .newInputStream (asset ), buildFs .getPath (reobfBin .relativize (asset ).toString ()));
241
+ }
242
+ }
243
+ }
244
+ }
245
+ } catch (IOException ex ) {
246
+ ex .printStackTrace ();
247
+ }
248
+ }
249
+ }
83
250
}
0 commit comments