package com.spineview; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.IdentityHashMap; import java.util.List; import java.util.Random; import java.util.Set; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * “New 15 Bone+Slot+Att” 按钮(正式版,重启后仍在)。 * * 流程:点一下 → 弹编辑器自带的名称输入面板 → 用输入的名字匹配骨架里的附件 * → 名字不对/找不到就结束 → 否则在**当前选中骨骼**下建 15 根骨骼, * 每根 1 个插槽 + 1 个附件(该名字的附件副本)。 * * 参数要求(全部按已确认的字段语义写,见 混淆名对照表.md): * 骨骼:x/y 各 0~500 随机;rotation=0;scaleX=scaleY=1;shearX=shearY=0 * 插槽:颜色 alpha 0.5~1 随机;blend = normal / additive 随机 * 附件(挂在插槽上):x=y=rotation=0;scaleX=scaleY=1 * * 关键 API(都在 tools/deobf/anchors.tsv 里,附字节码依据): * UH.D(GU,Gp,Z,fI)GU addSlot ⚠️ 会连模板插槽的附件一起复制 * UH.D(Gn,GU,Z,Z,fI)Gn addAttachmentCopy(内部 Gn.D(GP) 复制模板附件) * GP.D(Gp)V addBone * OG.D(String,fI,GU)String uniqueName * VZ.D(GP,Z,Z)V refresh * un.D()/un.B() undoBegin/undoCommit * Gn.L()V delete * dt.D(dl)V Stage.addActor * xl.D(dt) / xl.L(Object) / xl.L(String) 编辑器名称面板:显示 / OK-Cancel 回调 / 拿到名字(抽象) */ final class NewFifteen { private static final int COUNT = 15; private static final String BUTTON_TEXT = "New 15 Bone+Slot+Att"; /** 生成的对话框子类(ASM 现生成:xl 是抽象类,且必须在载荷类加载器那边实现)。 */ private static Class dialogClass; private static ClassLoader dialogLoader; private static boolean installed; private NewFifteen() { } /** 由 ObjectPanel.build 调用:往按钮表格里加一行按钮并接上点击。 */ static void install(final ClassLoader loader, Object skin, Object buttonsTable, Class tableClass, Class cellClass, Class actorClass) { if (installed) return; try { Class textButton = Class.forName("com.badlogic.gdx.scenes.scene2d.ui.TextButton", true, loader); Class skinClass = Class.forName("com.badlogic.gdx.scenes.scene2d.ui.Skin", true, loader); final Object button = textButton.getConstructor(String.class, skinClass) .newInstance(BUTTON_TEXT, skin); Object cell = call(buttonsTable, "D", new Class[] {actorClass}, cellClass, button); call(cell, "C", new Class[0], cellClass); // Cell.left() call(buttonsTable, "I", new Class[0], cellClass); // Table.row() ObjectPanel.wire(loader, button, new Runnable() { @Override public void run() { // 面板路由线程里:只负责开线程走流程,别把它堵住(要等用户输入) Thread thread = new Thread(new Runnable() { @Override public void run() { try { promptThenCreate(loader); } catch (Throwable error) { System.err.println("[new15] 流程失败: " + error); error.printStackTrace(System.err); } } }, "spineview-new15"); thread.setDaemon(true); thread.start(); } }); installed = true; System.err.println("[new15] 按钮已加入: " + BUTTON_TEXT); } catch (Throwable error) { System.err.println("[new15] 加按钮失败: " + error); } } // ==================== 弹面板 → 等名字 → 建对象 ==================== private static void promptThenCreate(final ClassLoader loader) throws Exception { final Object[] holder = new Object[1]; SceneActions.onEditorThread(loader, new Runnable() { @Override public void run() { try { holder[0] = showPrompt(loader); } catch (Throwable error) { System.err.println("[new15] 弹面板失败: " + error); error.printStackTrace(System.err); } } }); final Object dialog = holder[0]; if (dialog == null) { System.err.println("[new15] 面板没建出来"); return; } String name = waitForName(loader, dialog, 300000L); if (name == null || name.trim().isEmpty()) { System.err.println("[new15] 没拿到名字(取消/超时)→ 结束"); removeQuietly(dialog); return; } final String finalName = name.trim(); System.err.println("[new15] 输入的名字 = " + finalName); removeQuietly(dialog); SceneActions.onEditorThread(loader, new Runnable() { @Override public void run() { try { create(loader, finalName); } catch (Throwable error) { System.err.println("[new15] 建对象失败: " + error); error.printStackTrace(System.err); } } }); } /** 在渲染线程里:建对话框 → 挂进 Stage → 按 pref 撑开 → 居中(都在同一帧里完成,不会先闪别处)。 */ private static Object showPrompt(ClassLoader loader) throws Exception { Class xl = Class.forName("xl", true, loader); Class stageClass = Class.forName("dt", true, loader); Object stage = global(loader, stageClass); if (stage == null) throw new IllegalStateException("拿不到 Stage"); Class dialog = dialogClass(loader); Object dialogObject = dialog.getConstructor(String.class, String.class) .newInstance("New Bones + Slots + Attachment", "Enter an existing attachment name in this skeleton:"); // ① 挂进 Stage(实测 xl.D(dt) 不会挂;Stage.addActor 用"扫描 getMethods"那个才生效) addActor(stage, dialogObject); // ② 走编辑器自己的显示流程(把键盘焦点给名字框) Method show = null; for (Method method : xl.getMethods()) { Class[] types = method.getParameterTypes(); if (types.length == 1 && types[0] == stageClass) show = method; } if (show != null) { try { show.setAccessible(true); show.invoke(dialogObject, stage); } catch (Throwable error) { System.err.println("[new15] xl.D(dt) 失败: " + error); } } // ③ 尺寸:默认 150x150 会把正文/按钮裁掉 → 用 pref(下限 360x170) float prefW = floatOf(dialogObject, "StackMapTable"); // getPrefWidth float prefH = floatOf(dialogObject, "init"); // getPrefHeight float minW = floatOf(dialogObject, "LineNumberTable"); // getMinWidth float minH = floatOf(dialogObject, "String"); // getMinHeight float width = Math.max(Math.max(prefW, minW), 360f); float height = Math.max(Math.max(prefH, minH), 170f); setFloatField(loader, dialogObject, "L", width); // Actor.width setFloatField(loader, dialogObject, "I", height); // Actor.height invalidate(loader, dialogObject); // ④ 居中:setPosition(x,y,align) 在 align=0 时是"以 (x,y) 为中心" center(stage, dialogObject); return dialogObject; } /** 轮询用户操作:OK 时 NAME 有值;Cancel 时 DONE=true 且 NAME 为空;窗口被移除时 stage 字段变 null。 */ private static String waitForName(ClassLoader loader, Object dialog, long timeoutMs) throws Exception { Field nameField = dialog.getClass().getField("NAME"); Field doneField = dialog.getClass().getField("DONE"); Field stageField = fieldOfType(Class.forName("dl", true, loader), Class.forName("dt", true, loader)); long deadline = System.currentTimeMillis() + timeoutMs; while (System.currentTimeMillis() < deadline) { Object name = nameField.get(dialog); if (name != null) return (String) name; if (Boolean.TRUE.equals(doneField.get(dialog))) return null; if (stageField != null && stageField.get(dialog) == null) return null; Thread.sleep(120L); } return null; } // ==================== 建 15 组对象 ==================== private static void create(ClassLoader loader, String name) throws Exception { Class boneClass = Class.forName("Gp", true, loader); Class slotClass = Class.forName("GU", true, loader); Class attClass = Class.forName("Gn", true, loader); Class hiClass = Class.forName("Hi", true, loader); Class skeletonClass = Class.forName("GP", true, loader); Class arrayClass = Class.forName("fI", true, loader); Class colorClass = Class.forName("com.badlogic.gdx.graphics.Color", true, loader); Class blendClass = Class.forName("iz", true, loader); Class uhClass = Class.forName("UH", true, loader); Class og = Class.forName("OG", true, loader); Class undoClass = Class.forName("un", true, loader); Class treeClass = Class.forName("VZ", true, loader); Object selected = SceneObjects.selected(loader); if (selected == null || !boneClass.isInstance(selected)) { System.err.println("[new15] 当前选中的不是骨骼 → 结束"); return; } Object skeleton = firstFieldOfType(boneClass, selected, skeletonClass); if (skeleton == null) { System.err.println("[new15] 拿不到骨架 → 结束"); return; } Object bones = arrayOfType(skeletonClass, skeleton, arrayClass, boneClass); Object slots = arrayOfType(skeletonClass, skeleton, arrayClass, slotClass); Object attachments = arrayOfType(skeletonClass, skeleton, arrayClass, attClass); if (bones == null || slots == null || attachments == null) { System.err.println("[new15] 骨架的骨骼/插槽/附件表没找齐 → 结束"); return; } // 按名字找附件(优先非 Hi 占位) Object template = null; for (int i = 0; i < size(arrayClass, attachments); i++) { Object candidate = get(arrayClass, attachments, i); if (candidate == null || !attClass.isInstance(candidate)) continue; if (!name.equals(SceneObjects.nameOf(candidate))) continue; if (!hiClass.isInstance(candidate)) { template = candidate; break; } if (template == null) template = candidate; } if (template == null) { System.err.println("[new15] 骨架里没有名为 \"" + name + "\" 的附件 → 结束"); return; } Object templateSlot = size(arrayClass, slots) > 0 ? get(arrayClass, slots, 0) : null; Object uh = staticField(og, uhClass); if (uh == null) { System.err.println("[new15] 拿不到编辑器模型助手 UH → 结束"); return; } Method addBone = find(skeletonClass, "D", new Class[] {boneClass}, void.class); Method addSlot = find(uhClass, "D", new Class[] {slotClass, boneClass, boolean.class, arrayClass}, slotClass); Method addAttachment = find(uhClass, "D", new Class[] {attClass, slotClass, boolean.class, boolean.class, arrayClass}, attClass); Method nameGen = find(og, "D", new Class[] {String.class, arrayClass, slotClass}, String.class); Method refresh = find(treeClass, "D", new Class[] {skeletonClass, boolean.class, boolean.class}, void.class); Method deleteAttachment = find(attClass, "L", new Class[0], void.class); Method undoBegin = noArgVoid(undoClass, "D"); Method undoCommit = noArgVoid(undoClass, "B"); if (addBone == null || addSlot == null || addAttachment == null || nameGen == null || undoBegin == null || undoCommit == null) { System.err.println("[new15] 编辑器 API 没找齐,停止"); return; } Object blendNormal = blendClass.getField("D").get(null); Object blendAdditive = blendClass.getField("B").get(null); Constructor boneCtor = boneClass.getConstructor(boneClass, skeletonClass, boneClass); Object empty = arrayClass.getConstructor().newInstance(); Random random = new Random(); int made = 0; undoBegin.setAccessible(true); undoBegin.invoke(null); for (int i = 0; i < COUNT; i++) { // ---- 骨骼:x/y 0~500 随机,其余按 0/1 ---- Object bone = boneCtor.newInstance(selected, skeleton, selected); set(bone, "D", String.class, nameGen.invoke(null, "bone", bones, null)); setFloatField(loader, bone, "Z", random.nextFloat() * 500f); // x setFloatField(loader, bone, "V", random.nextFloat() * 500f); // y setFloatField(loader, bone, "F", 0f); // rotation setFloatField(loader, bone, "Code", 1f); // scaleX setFloatField(loader, bone, "SourceFile", 1f); // scaleY setFloatField(loader, bone, "StackMapTable", 0f); // shearX setFloatField(loader, bone, "LineNumberTable", 0f); // shearY addBone.setAccessible(true); addBone.invoke(skeleton, bone); // ---- 插槽:alpha 0.5~1 随机;blend normal/additive 随机 ---- Object out = arrayClass.getConstructor().newInstance(); addSlot.setAccessible(true); Object slot = addSlot.invoke(uh, templateSlot, bone, Boolean.FALSE, out); if (slot == null) continue; // ADD_SLOT 会把模板插槽整个复制,连它挂着的附件一起复制 → 记下来待会删 Object inherited = readSlotAttachment(slotClass, attClass, slot); float alpha = 0.5f + random.nextFloat() * 0.5f; Object color = colorClass.getConstructor(float.class, float.class, float.class, float.class) .newInstance(1f, 1f, 1f, alpha); set(slot, "B", colorClass, color); // setup 颜色 set(slot, "D", colorClass, color); // animate 颜色 set(slot, "D", blendClass, random.nextBoolean() ? blendNormal : blendAdditive); // ---- 附件:复制"输入名字"的附件,变换 0/0/0/1/1 ---- addAttachment.setAccessible(true); Object attachment = addAttachment.invoke(uh, template, slot, Boolean.FALSE, Boolean.FALSE, empty); if (attachment == null) continue; setFloatField(loader, attachment, "D", 0f); // x setFloatField(loader, attachment, "B", 0f); // y setFloatField(loader, attachment, "S", 0f); // rotation setFloatField(loader, attachment, "L", 1f); // scaleX setFloatField(loader, attachment, "I", 1f); // scaleY if (!name.equals(SceneObjects.nameOf(attachment))) { set(attachment, "D", String.class, name); // 编辑器若自动改名,强制回输入的名字 } // 插槽的当前附件 = 我们这份(两个字段都写,setup/animate 都覆盖) set(slot, "B", attClass, attachment); set(slot, "D", attClass, attachment); // 删掉模板插槽带来的那份(保证"一根骨骼 = 1 插槽 + 1 附件") if (inherited != null && inherited != attachment && deleteAttachment != null) { deleteAttachment.setAccessible(true); deleteAttachment.invoke(inherited); } made++; } undoCommit.setAccessible(true); undoCommit.invoke(null); if (refresh != null) { refresh.setAccessible(true); refresh.invoke(staticField(og, treeClass), skeleton, Boolean.TRUE, Boolean.TRUE); } System.err.println("[new15] 完成:在 " + SceneObjects.nameOf(selected) + " 下建了 " + made + " 根骨骼(每根 1 插槽 1 附件 \"" + name + "\")"); } // ==================== 对话框子类(ASM) ==================== private static synchronized Class dialogClass(final ClassLoader loader) throws Exception { if (dialogClass != null) return dialogClass; final String internal = "com/spineview/NewFifteenPrompt"; ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); writer.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, internal, null, "xl", null); writer.visitField(Opcodes.ACC_PUBLIC, "NAME", "Ljava/lang/String;", null, null).visitEnd(); writer.visitField(Opcodes.ACC_PUBLIC, "DONE", "Z", null, null).visitEnd(); // 构造:super(title, body, (Ge) null) MethodVisitor ctor = writer.visitMethod(Opcodes.ACC_PUBLIC, "", "(Ljava/lang/String;Ljava/lang/String;)V", null, null); ctor.visitCode(); ctor.visitVarInsn(Opcodes.ALOAD, 0); ctor.visitVarInsn(Opcodes.ALOAD, 1); ctor.visitVarInsn(Opcodes.ALOAD, 2); ctor.visitInsn(Opcodes.ACONST_NULL); ctor.visitMethodInsn(Opcodes.INVOKESPECIAL, "xl", "", "(Ljava/lang/String;Ljava/lang/String;LGe;)V", false); ctor.visitInsn(Opcodes.RETURN); ctor.visitMaxs(0, 0); ctor.visitEnd(); // 抽象回调:OK 时拿到名字 → 记 NAME,并关窗 MethodVisitor name = writer.visitMethod(Opcodes.ACC_PUBLIC, "L", "(Ljava/lang/String;)V", null, null); name.visitCode(); name.visitVarInsn(Opcodes.ALOAD, 0); name.visitVarInsn(Opcodes.ALOAD, 1); name.visitFieldInsn(Opcodes.PUTFIELD, internal, "NAME", "Ljava/lang/String;"); name.visitVarInsn(Opcodes.ALOAD, 0); name.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "dl", "b_", "()Z", false); // remove() name.visitInsn(Opcodes.POP); name.visitInsn(Opcodes.RETURN); name.visitMaxs(0, 0); name.visitEnd(); // OK/Cancel 总回调:不管哪个都置 DONE MethodVisitor done = writer.visitMethod(Opcodes.ACC_PUBLIC, "L", "(Ljava/lang/Object;)V", null, null); done.visitCode(); done.visitVarInsn(Opcodes.ALOAD, 0); done.visitVarInsn(Opcodes.ALOAD, 1); done.visitMethodInsn(Opcodes.INVOKESPECIAL, "xl", "L", "(Ljava/lang/Object;)V", false); done.visitVarInsn(Opcodes.ALOAD, 0); done.visitInsn(Opcodes.ICONST_1); done.visitFieldInsn(Opcodes.PUTFIELD, internal, "DONE", "Z"); done.visitInsn(Opcodes.RETURN); done.visitMaxs(0, 0); done.visitEnd(); writer.visitEnd(); final byte[] bytes = writer.toByteArray(); dialogLoader = new ClassLoader(loader) { @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.equals("com.spineview.NewFifteenPrompt")) { Class found = findLoadedClass(name); if (found == null) found = defineClass(name, bytes, 0, bytes.length); if (resolve) resolveClass(found); return found; } return super.loadClass(name, resolve); } }; dialogClass = Class.forName("com.spineview.NewFifteenPrompt", true, dialogLoader); return dialogClass; } // ==================== 小工具 ==================== private static Object global(ClassLoader loader, Class type) { try { Class og = Class.forName("OG", true, loader); for (Field field : og.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers()) || field.getType() != type) continue; field.setAccessible(true); Object value = field.get(null); if (value != null) return value; } } catch (Throwable ignored) { // 读不到就返回 null } return null; } /** 挂进 Stage:用"扫描 getMethods 里 1 个 Actor 参数 + void"的那个(getMethod 那个实测不生效)。 */ private static void addActor(Object stage, Object actor) throws Exception { ClassLoader loader = stage.getClass().getClassLoader(); Class actorClass = Class.forName("dl", true, loader); Method add = null; for (Method method : stage.getClass().getMethods()) { Class[] types = method.getParameterTypes(); if (types.length != 1) continue; if (!actorClass.isAssignableFrom(types[0])) continue; if (method.getReturnType() != void.class) continue; add = method; } if (add == null) throw new NoSuchMethodException("Stage.addActor"); add.setAccessible(true); add.invoke(stage, actor); } private static void center(Object stage, Object dialog) { try { float width = floatOf(dialog, "L"); // getWidth float height = floatOf(dialog, "I"); // getHeight float cx = floatOf(stage, "D") / 2f; // Stage.getWidth / 2 float cy = floatOf(stage, "B") / 2f; // Stage.getHeight / 2 Class actorClass = Class.forName("dl", true, dialog.getClass().getClassLoader()); Method setPosition = null; for (Method method : actorClass.getMethods()) { Class[] types = method.getParameterTypes(); if (types.length == 3 && types[0] == float.class && types[1] == float.class && types[2] == int.class) setPosition = method; } if (setPosition != null) { setPosition.setAccessible(true); // align=0 → 以 (x,y) 为中心 setPosition.invoke(dialog, Float.valueOf(cx), Float.valueOf(cy), Integer.valueOf(0)); } System.err.println("[new15] 弹窗 " + width + "x" + height + " 居中于 (" + cx + "," + cy + ")"); } catch (Throwable error) { System.err.println("[new15] 居中失败: " + error); } } private static void invalidate(ClassLoader loader, Object dialog) { try { Class widgetGroup = Class.forName("eX", true, loader); for (Method method : widgetGroup.getMethods()) { if (method.getName().equals("b_") && method.getParameterTypes().length == 0 && method.getReturnType() == void.class) { method.setAccessible(true); method.invoke(dialog); return; } } } catch (Throwable ignored) { // 没 invalidate 也不致命 } } private static void removeQuietly(Object dialog) { try { Class actorClass = Class.forName("dl", true, dialog.getClass().getClassLoader()); Method remove = null; for (Method method : actorClass.getMethods()) { if (method.getName().equals("b_") && method.getParameterTypes().length == 0 && method.getReturnType() == boolean.class) remove = method; } if (remove != null) { remove.setAccessible(true); remove.invoke(dialog); } } catch (Throwable ignored) { // 已经关掉了 } } private static Object readSlotAttachment(Class slotClass, Class attClass, Object slot) throws Exception { for (Class type = slotClass; type != null && type != Object.class; type = type.getSuperclass()) { for (Field field : type.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) || field.getType() != attClass) continue; field.setAccessible(true); Object value = field.get(slot); if (value != null) return value; } } return null; } private static Field fieldOfType(Class owner, Class type) { for (Class current = owner; current != null && current != Object.class; current = current.getSuperclass()) { for (Field field : current.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) || field.getType() != type) continue; field.setAccessible(true); return field; } } return null; } private static Object firstFieldOfType(Class owner, Object target, Class type) { for (Class current = owner; current != null && current != Object.class; current = current.getSuperclass()) { for (Field field : current.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) || field.getType() != type) continue; field.setAccessible(true); try { Object value = field.get(target); if (value != null) return value; } catch (Throwable ignored) { // 下一个 } } } return null; } private static Object arrayOfType(Class owner, Object target, Class arrayClass, Class elementType) { for (Class current = owner; current != null && current != Object.class; current = current.getSuperclass()) { for (Field field : current.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) || !arrayClass.isAssignableFrom(field.getType())) { continue; } field.setAccessible(true); Object array; try { array = field.get(target); } catch (Throwable ignored) { continue; } if (array == null) continue; for (int i = 0; i < size(arrayClass, array); i++) { Object element = get(arrayClass, array, i); if (element != null && elementType.isInstance(element)) return array; } } } return null; } private static void setFloatField(ClassLoader loader, Object target, String name, float value) throws Exception { for (Class type = target.getClass(); type != null && type != Object.class; type = type.getSuperclass()) { for (Field field : type.getDeclaredFields()) { if (!field.getName().equals(name) || field.getType() != float.class) continue; field.setAccessible(true); field.setFloat(target, value); return; } } } private static void set(Object target, String name, Class type, Object value) throws Exception { for (Class current = target.getClass(); current != null && current != Object.class; current = current.getSuperclass()) { for (Field field : current.getDeclaredFields()) { if (!field.getName().equals(name) || field.getType() != type) continue; field.setAccessible(true); field.set(target, value); return; } } } private static float floatOf(Object target, String name) { try { for (Method method : target.getClass().getMethods()) { if (!method.getName().equals(name)) continue; if (method.getParameterTypes().length != 0) continue; if (method.getReturnType() != float.class) continue; method.setAccessible(true); return ((Float) method.invoke(target)).floatValue(); } } catch (Throwable ignored) { // 读不到 } return -1f; } private static Object call(Object target, String name, Class[] parameterTypes, Class returnType, Object... args) throws Exception { for (Class current = target.getClass(); current != null && current != Object.class; current = current.getSuperclass()) { for (Method method : current.getDeclaredMethods()) { if (!method.getName().equals(name)) continue; if (!Arrays.equals(method.getParameterTypes(), parameterTypes)) continue; if (returnType != null && method.getReturnType() != returnType) continue; method.setAccessible(true); return method.invoke(target, args); } } throw new NoSuchMethodException(target.getClass().getName() + "." + name); } private static Object staticField(Class owner, Class type) { for (Field field : owner.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers()) || field.getType() != type) continue; field.setAccessible(true); try { Object value = field.get(null); if (value != null) return value; } catch (Throwable ignored) { // 下一个 } } return null; } private static Method find(Class owner, String name, Class[] parameterTypes, Class returnType) { for (Class current = owner; current != null && current != Object.class; current = current.getSuperclass()) { for (Method method : current.getDeclaredMethods()) { if (!method.getName().equals(name)) continue; if (!Arrays.equals(method.getParameterTypes(), parameterTypes)) continue; if (returnType != null && method.getReturnType() != returnType) continue; method.setAccessible(true); return method; } } return null; } private static Method noArgVoid(Class owner, String name) { for (Method method : owner.getDeclaredMethods()) { if (!method.getName().equals(name)) continue; if (method.getParameterTypes().length != 0) continue; if (method.getReturnType() != void.class) continue; method.setAccessible(true); return method; } return null; } private static int size(Class arrayClass, Object array) { if (array == null) return 0; for (Class current = arrayClass; current != null && current != Object.class; current = current.getSuperclass()) { for (Field field : current.getDeclaredFields()) { if (field.getType() != int.class) continue; field.setAccessible(true); try { return field.getInt(array); } catch (Throwable ignored) { // 下一个 } } } return 0; } private static Object get(Class arrayClass, Object array, int index) { for (Method method : arrayClass.getMethods()) { if (!method.getName().equals("D")) continue; Class[] types = method.getParameterTypes(); if (types.length != 1 || types[0] != int.class) continue; if (method.getReturnType() != Object.class) continue; try { method.setAccessible(true); return method.invoke(array, Integer.valueOf(index)); } catch (Throwable ignored) { // 下一个 } } return null; } }