Skip to content

Commit f95b696

Browse files
committed
feat: remap transformer string literals during reobfuscation
1 parent c094346 commit f95b696

6 files changed

Lines changed: 903 additions & 4 deletions

File tree

gui/src/main/java/org/mcphackers/mcp/gui/MenuBar.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,17 @@ public void reloadSide() {
186186

187187
public void reloadOptions() {
188188
for (Map.Entry<TaskParameter, JMenuItem> entry : optionItems.entrySet()) {
189-
entry.getValue().setSelected(mcp.options.getBooleanParameter(entry.getKey()));
189+
TaskParameter param = entry.getKey();
190+
JMenuItem item = entry.getValue();
191+
if (param.type == String[].class) {
192+
// "Active" for an array-valued option = at least one entry. Lets
193+
// us render it as a toggle in the menu while still keeping the
194+
// underlying value a comma-separated list.
195+
String[] arr = mcp.options.getStringArrayParameter(param);
196+
item.setSelected(arr != null && arr.length > 0);
197+
} else if (param.type == Boolean.class) {
198+
item.setSelected(mcp.options.getBooleanParameter(param));
199+
}
190200
}
191201
}
192202

@@ -222,6 +232,32 @@ private void initOptions() {
222232
mcp.options.setParameter(param, b.isSelected());
223233
mcp.options.save();
224234
});
235+
} else if (param.type == String[].class) {
236+
// Array-valued option rendered as a checkbox: enabling it
237+
// pops up the value editor; an empty/cancelled value reverts
238+
// to disabled. Disabling clears the array. Lets the user
239+
// toggle expensive opt-in features (like string-literal
240+
// remapping for ASM/transformer code) without leaving the
241+
// menu.
242+
b = new JRadioButtonMenuItem();
243+
translatableComponents.put(b, "task.param." + param.name);
244+
optionItems.put(param, b);
245+
final JRadioButtonMenuItem checkbox = (JRadioButtonMenuItem) b;
246+
b.addActionListener(e -> {
247+
if (checkbox.isSelected()) {
248+
mcp.inputOptionsValue(param);
249+
String[] result = mcp.options.getStringArrayParameter(param);
250+
if (result == null || result.length == 0) {
251+
// User cancelled or supplied an empty value —
252+
// revert the checkbox so its visible state stays
253+
// in sync with the underlying option.
254+
checkbox.setSelected(false);
255+
}
256+
} else {
257+
mcp.options.setParameter(param, new String[0]);
258+
mcp.options.save();
259+
}
260+
});
225261
} else {
226262
b = new JMenuItem(param.getDesc());
227263
b.addActionListener(u -> mcp.inputOptionsValue(param));

gui/src/main/java/org/mcphackers/mcp/main/MainGUI.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class MainGUI extends MCP {
4646
public static final String[] TABS = {"task.decompile", "task.recompile", "task.reobfuscate", "task.build", "options.running"};
4747
public static final TaskParameter[][] TAB_PARAMETERS = {
4848
{TaskParameter.PATCHES, TaskParameter.FERNFLOWER_OPTIONS, TaskParameter.IGNORED_PACKAGES, TaskParameter.OUTPUT_SRC, TaskParameter.DECOMPILE_RESOURCES, TaskParameter.GUESS_GENERICS, TaskParameter.STRIP_GENERICS},
49-
{TaskParameter.SOURCE_VERSION, TaskParameter.TARGET_VERSION, TaskParameter.JAVA_HOME, TaskParameter.JAVAC_ARGS}, {TaskParameter.OBFUSCATION, TaskParameter.SRG_OBFUSCATION, TaskParameter.EXCLUDED_CLASSES, TaskParameter.STRIP_SOURCE_FILE},
49+
{TaskParameter.SOURCE_VERSION, TaskParameter.TARGET_VERSION, TaskParameter.JAVA_HOME, TaskParameter.JAVAC_ARGS}, {TaskParameter.OBFUSCATION, TaskParameter.SRG_OBFUSCATION, TaskParameter.EXCLUDED_CLASSES, TaskParameter.STRIP_SOURCE_FILE, TaskParameter.STRING_REMAP_PACKAGES},
5050
{TaskParameter.FULL_BUILD}, {TaskParameter.RUN_BUILD, TaskParameter.RUN_ARGS, TaskParameter.GAME_ARGS}
5151
};
5252
public Theme theme = Theme.THEMES_MAP.get(UIManager.getCrossPlatformLookAndFeelClassName());
@@ -293,10 +293,26 @@ public void changeWorkingDirectory() {
293293

294294
public void inputOptionsValue(TaskParameter param) {
295295
String s = MCP.TRANSLATOR.translateKey("options.enterValue");
296+
Object initialValue;
296297
if (param.type == String[].class) {
297298
s = MCP.TRANSLATOR.translateKey("options.enterValues") + "\n" + MCP.TRANSLATOR.translateKey("options.enterValues.info");
299+
// Default JOptionPane initial value for an array uses Object.toString
300+
// which prints "[Ljava.lang.String;@<hash>" — meaningless to users
301+
// and worse than an empty box. Render as the same comma-separated
302+
// form the parser accepts so it round-trips cleanly.
303+
String[] current = options.getStringArrayParameter(param);
304+
StringBuilder b = new StringBuilder();
305+
if (current != null) {
306+
for (int i = 0; i < current.length; i++) {
307+
if (i > 0) b.append(",");
308+
b.append(Util.convertToEscapedString(current[i]));
309+
}
310+
}
311+
initialValue = b.toString();
312+
} else {
313+
initialValue = Util.convertToEscapedString(String.valueOf(options.getParameter(param)));
298314
}
299-
String value = (String) JOptionPane.showInputDialog(frame, s, param.getDesc(), JOptionPane.PLAIN_MESSAGE, null, null, Util.convertToEscapedString(String.valueOf(options.getParameter(param))));
315+
String value = (String) JOptionPane.showInputDialog(frame, s, param.getDesc(), JOptionPane.PLAIN_MESSAGE, null, null, initialValue);
300316
safeSetParameter(param, value);
301317
options.save();
302318
}

src/main/java/org/mcphackers/mcp/tasks/TaskReobfuscate.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.mcphackers.mcp.tasks.mode.TaskParameter;
1919
import org.mcphackers.mcp.tools.FileUtil;
2020
import org.mcphackers.mcp.tools.injector.SourceFileTransformer;
21+
import org.mcphackers.mcp.tools.injector.StringLiteralRemapper;
2122
import org.mcphackers.mcp.tools.mappings.MappingUtil;
2223
import org.mcphackers.rdi.injector.data.ClassStorage;
2324
import org.mcphackers.rdi.injector.data.Mappings;
@@ -80,6 +81,16 @@ private void reobfuscate() throws IOException {
8081
RDInjector injector = new RDInjector(reobfBin);
8182
Mappings mappings = getMappings(injector.getStorage(), localSide);
8283
if (mappings != null) {
84+
// Rewrite string literals naming vanilla MCP symbols inside the
85+
// configured user packages. Must run BEFORE applyMappings so we
86+
// can still read deobf class names from Type LDCs and look up
87+
// field/method descriptors from the (still-deobf) ClassStorage
88+
// for reflection-pattern context. No-op when the parameter is
89+
// empty, so standard MCP workflows are unaffected.
90+
String[] stringRemapPackages = mcp.getOptions().getStringArrayParameter(TaskParameter.STRING_REMAP_PACKAGES);
91+
if (stringRemapPackages != null && stringRemapPackages.length > 0) {
92+
new StringLiteralRemapper(mappings, stringRemapPackages).transform(injector.getStorage());
93+
}
8394
injector.applyMappings(mappings);
8495
}
8596
if (stripSourceFile) {

src/main/java/org/mcphackers/mcp/tasks/mode/TaskParameter.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,23 @@ public enum TaskParameter {
3636
GUESS_GENERICS("generics", Boolean.class, false),
3737
STRIP_GENERICS("stripgenerics", Boolean.class, false),
3838
OUTPUT_SRC("outputsrc", Boolean.class, true),
39-
STRIP_SOURCE_FILE("stripsourcefile", Boolean.class, true);
39+
STRIP_SOURCE_FILE("stripsourcefile", Boolean.class, true),
40+
/**
41+
* Internal package prefixes (slash form, e.g. {@code me/M41G/nclient/}) whose
42+
* compiled classes should have their string-literal references to vanilla
43+
* MCP names rewritten to the obfuscated equivalents during reobfuscation.
44+
* <p>
45+
* Required for any code that relies on string-keyed access to vanilla
46+
* symbols — ASM transformers (LDC owner/name/desc strings inside
47+
* {@code FieldInsnNode}/{@code MethodInsnNode} construction) and
48+
* reflection ({@code Class.getDeclaredField("name")},
49+
* {@code Class.getDeclaredMethod("name", ...)}). Without this rewrite
50+
* those strings still point at deobf names that no longer exist after
51+
* reobfuscation, and the code becomes a silent no-op at runtime.
52+
* <p>
53+
* Empty by default; standard MCP workflows are unaffected.
54+
*/
55+
STRING_REMAP_PACKAGES("stringremap", String[].class, new String[0]);
4056

4157
public static final TaskParameter[] VALUES = TaskParameter.values();
4258

0 commit comments

Comments
 (0)