Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions AquaMai.Mods/GameSystem/SkipBoardNoCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Linq;
using AMDaemon;
using AquaMai.Config.Attributes;
using Comio.BD15070_4;
using Mecha;
using HarmonyLib;
using System.Reflection.Emit;
using Manager;
using UnityEngine;

namespace AquaMai.Mods.GameSystem;

[ConfigSection(
en: "Skip BoardNo check to use the old cab light board 837-15070-02",
zh: "跳过 BoardNo 检查以使用 837-15070-02(旧框灯板)")]
public class SkipBoardNoCheck
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(BoardCtrl15070_4), "_md_initBoard_GetBoardInfo")]
static IEnumerable<CodeInstruction> Transpiler1(IEnumerable<CodeInstruction> instructions)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): 考虑重构 Transpiler 方法以使用共享的 IL 修补助手来消除重复代码。

以下是将两个 Transpiler 合并为一个 IL 修补器并删除所有重复的循环/列表逻辑而无需更改任何行为的一种方法:

// 1) 执行 “Pop, Pop, Ldc_I4_1” 替换 + 日志记录的通用助手
static IEnumerable<CodeInstruction> ReplaceIsEqual(
    IEnumerable<CodeInstruction> instructions,
    string contextName)
{
    var codes = instructions.ToList();
    bool patched = false;

    for (int i = 0; i < codes.Count; i++)
    {
        var instr = codes[i];
        if (instr.opcode == OpCodes.Callvirt
            && instr.operand != null
            && instr.operand.ToString().Contains("IsEqual"))
        {
            codes[i]     = new CodeInstruction(OpCodes.Pop);
            codes.Insert(i+1, new CodeInstruction(OpCodes.Pop));
            codes.Insert(i+2, new CodeInstruction(OpCodes.Ldc_I4_1));
            MelonLoader.MelonLogger.Msg(
                $"[SkipBoardNoCheck] 修补 {contextName} 方法成功");
            patched = true;
            break;
        }
    }

    if (!patched)
        MelonLoader.MelonLogger.Warning(
            $"[SkipBoardNoCheck] 未找到需要修补的代码位置:{contextName}");

    return codes;
}
// 2) 单个 Transpiler 方法,修补到两个目标,
//    将其自己的上下文字符串传递给助手
[HarmonyPatch(typeof(BoardCtrl15070_4), "_md_initBoard_GetBoardInfo")]
[HarmonyPatch(typeof(Bd15070_4Control), "IsNeedFirmUpdate")]
public static class SkipBoardNoCheck
{
    [HarmonyTranspiler]
    static IEnumerable<CodeInstruction> Transpiler1(
        IEnumerable<CodeInstruction> instrs)
        => ReplaceIsEqual(
            instrs,
            "BoardCtrl15070_4._md_initBoard_GetBoardInfo");

    [HarmonyTranspiler]
    static IEnumerable<CodeInstruction> Transpiler2(
        IEnumerable<CodeInstruction> instrs)
        => ReplaceIsEqual(
            instrs,
            "Bd15070_4Control.IsNeedFirmUpdate");
}

这消除了所有手动 IL 重复,同时保持了两个补丁和日志的完整性。

Original comment in English

issue (complexity): Consider refactoring the transpiler methods to use a shared helper for IL patching to eliminate duplicated code.

Here’s one way to collapse both transpilers into a single IL‐patcher and remove all of the duplicated loop/list logic without changing any behavior:

// 1) Common helper that does the “Pop, Pop, Ldc_I4_1” replace + logging
static IEnumerable<CodeInstruction> ReplaceIsEqual(
    IEnumerable<CodeInstruction> instructions,
    string contextName)
{
    var codes = instructions.ToList();
    bool patched = false;

    for (int i = 0; i < codes.Count; i++)
    {
        var instr = codes[i];
        if (instr.opcode == OpCodes.Callvirt
            && instr.operand != null
            && instr.operand.ToString().Contains("IsEqual"))
        {
            codes[i]     = new CodeInstruction(OpCodes.Pop);
            codes.Insert(i+1, new CodeInstruction(OpCodes.Pop));
            codes.Insert(i+2, new CodeInstruction(OpCodes.Ldc_I4_1));
            MelonLoader.MelonLogger.Msg(
                $"[SkipBoardNoCheck] 修补 {contextName} 方法成功");
            patched = true;
            break;
        }
    }

    if (!patched)
        MelonLoader.MelonLogger.Warning(
            $"[SkipBoardNoCheck] 未找到需要修补的代码位置:{contextName}");

    return codes;
}
// 2) Single transpiler method, patched to both targets,
//    passing its own context string into the helper
[HarmonyPatch(typeof(BoardCtrl15070_4), "_md_initBoard_GetBoardInfo")]
[HarmonyPatch(typeof(Bd15070_4Control), "IsNeedFirmUpdate")]
public static class SkipBoardNoCheck
{
    [HarmonyTranspiler]
    static IEnumerable<CodeInstruction> Transpiler1(
        IEnumerable<CodeInstruction> instrs)
        => ReplaceIsEqual(
            instrs,
            "BoardCtrl15070_4._md_initBoard_GetBoardInfo");

    [HarmonyTranspiler]
    static IEnumerable<CodeInstruction> Transpiler2(
        IEnumerable<CodeInstruction> instrs)
        => ReplaceIsEqual(
            instrs,
            "Bd15070_4Control.IsNeedFirmUpdate");
}

This removes all the manual‐IL duplication while keeping both patches and logs intact.

{
var codes = new List<CodeInstruction>(instructions);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 考虑使用更健壮的模式来匹配目标指令。

通过字符串值匹配操作数容易出错,并且如果方法名称或签名更改,可能会中断。为了可靠性,请直接比较 MethodInfo。

建议的实现:

        var codes = new List<CodeInstruction>(instructions);
        bool patched = false;

        // 获取目标 IsEqual 方法的 MethodInfo
        var isEqualMethod = typeof(BoardCtrl15070_4).GetMethod("IsEqual", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        for (int i = 0; i < codes.Count; i++)
        {
            if (codes[i].opcode == OpCodes.Callvirt &&
                codes[i].operand != null &&
                codes[i].operand is MethodInfo method &&
                method == isEqualMethod)
            {
                codes[i] = new CodeInstruction(OpCodes.Pop);
                codes.Insert(i + 1, new CodeInstruction(OpCodes.Pop));
                codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_I4_1));

                patched = true;

如果 IsEqual 方法被重载,你可能需要在 GetMethod 中指定参数类型。例如:

var isEqualMethod = typeof(BoardCtrl15070_4).GetMethod("IsEqual", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(/* param types here */) }, null);

此外,请确保你的文件顶部有 using System.Reflection;(如果尚未存在)。

Original comment in English

suggestion: Consider using a more robust pattern for matching the target instruction.

Matching the operand by its string value is error-prone and may break if method names or signatures change. Prefer comparing MethodInfo directly for reliability.

Suggested implementation:

        var codes = new List<CodeInstruction>(instructions);
        bool patched = false;

        // Get MethodInfo for the target IsEqual method
        var isEqualMethod = typeof(BoardCtrl15070_4).GetMethod("IsEqual", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        for (int i = 0; i < codes.Count; i++)
        {
            if (codes[i].opcode == OpCodes.Callvirt &&
                codes[i].operand != null &&
                codes[i].operand is MethodInfo method &&
                method == isEqualMethod)
            {
                codes[i] = new CodeInstruction(OpCodes.Pop);
                codes.Insert(i + 1, new CodeInstruction(OpCodes.Pop));
                codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_I4_1));

                patched = true;

If the IsEqual method is overloaded, you may need to specify the parameter types in GetMethod. For example:

var isEqualMethod = typeof(BoardCtrl15070_4).GetMethod("IsEqual", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(/* param types here */) }, null);

Also, ensure you have using System.Reflection; at the top of your file if not already present.

bool patched = false;
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Callvirt &&
codes[i].operand != null &&
Comment on lines +19 to +28
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 重复的 Transpiler 逻辑可以重构以提高可维护性。

考虑将共享的修补逻辑移到一个辅助方法中,以最大程度地减少重复并简化未来的更新。

建议的实现:

    static IEnumerable<CodeInstruction> Transpiler1(IEnumerable<CodeInstruction> instructions)
    {
        return PatchIsEqualTranspiler(instructions);
    [HarmonyTranspiler]
    [HarmonyPatch(typeof(Bd15070_4Control), "IsNeedFirmUpdate")]
    static IEnumerable<CodeInstruction> Transpiler2(IEnumerable<CodeInstruction> instructions)
    {
        return PatchIsEqualTranspiler(instructions);
    // 用于重构共享 Transpiler 逻辑的辅助方法
    private static IEnumerable<CodeInstruction> PatchIsEqualTranspiler(IEnumerable<CodeInstruction> instructions)
    {
        var codes = new List<CodeInstruction>(instructions);
        for (int i = 0; i < codes.Count; i++)
        {
            if (codes[i].opcode == OpCodes.Callvirt &&
                codes[i].operand != null &&
                codes[i].operand.ToString().Contains("IsEqual"))
            {
                codes[i] = new CodeInstruction(OpCodes.Pop);
                codes.Insert(i + 1, new CodeInstruction(OpCodes.Pop));
                codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_I4_1));
                break;
            }
        }
        return codes;
    }
Original comment in English

suggestion: Duplicated transpiler logic could be refactored for maintainability.

Consider moving the shared patching logic into a helper method to minimize duplication and simplify future updates.

Suggested implementation:

    static IEnumerable<CodeInstruction> Transpiler1(IEnumerable<CodeInstruction> instructions)
    {
        return PatchIsEqualTranspiler(instructions);
    [HarmonyTranspiler]
    [HarmonyPatch(typeof(Bd15070_4Control), "IsNeedFirmUpdate")]
    static IEnumerable<CodeInstruction> Transpiler2(IEnumerable<CodeInstruction> instructions)
    {
        return PatchIsEqualTranspiler(instructions);
    // Helper method to refactor shared transpiler logic
    private static IEnumerable<CodeInstruction> PatchIsEqualTranspiler(IEnumerable<CodeInstruction> instructions)
    {
        var codes = new List<CodeInstruction>(instructions);
        for (int i = 0; i < codes.Count; i++)
        {
            if (codes[i].opcode == OpCodes.Callvirt &&
                codes[i].operand != null &&
                codes[i].operand.ToString().Contains("IsEqual"))
            {
                codes[i] = new CodeInstruction(OpCodes.Pop);
                codes.Insert(i + 1, new CodeInstruction(OpCodes.Pop));
                codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_I4_1));
                break;
            }
        }
        return codes;
    }

codes[i].operand.ToString().Contains("IsEqual"))
{
codes[i] = new CodeInstruction(OpCodes.Pop);
codes.Insert(i + 1, new CodeInstruction(OpCodes.Pop));
codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_I4_1));

patched = true;
MelonLoader.MelonLogger.Msg("[SkipBoardNoCheck] 修补 BoardCtrl15070_4._md_initBoard_GetBoardInfo 方法成功");
break;
}
}
if (!patched)
{
MelonLoader.MelonLogger.Warning("[SkipBoardNoCheck] 未找到需要修补的代码位置:BoardCtrl15070_4._md_initBoard_GetBoardInfo");
}
return codes;
}

[HarmonyTranspiler]
[HarmonyPatch(typeof(Bd15070_4Control), "IsNeedFirmUpdate")]
static IEnumerable<CodeInstruction> Transpiler2(IEnumerable<CodeInstruction> instructions)
{
var codes = new List<CodeInstruction>(instructions);
bool patched = false;
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Callvirt &&
codes[i].operand != null &&
codes[i].operand.ToString().Contains("IsEqual"))
{
codes[i] = new CodeInstruction(OpCodes.Pop);
codes.Insert(i + 1, new CodeInstruction(OpCodes.Pop));
codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_I4_1));

patched = true;
MelonLoader.MelonLogger.Msg("[SkipBoardNoCheck] 修补 Bd15070_4Control.IsNeedFirmUpdate 方法成功");
break;
}
}
if (!patched)
{
MelonLoader.MelonLogger.Warning("[SkipBoardNoCheck] 未找到需要修补的代码位置:Bd15070_4Control.IsNeedFirmUpdate");
}
return codes;
}
}
1 change: 1 addition & 0 deletions AquaMai/configSort.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
- GameSystem.RemoveEncryption
- GameSystem.TouchPanelBaudRate
- GameSystem.OptionLoadFix
- GameSystem.SkipBoardNoCheck
- UX.ServerAnnouncement
- Tweaks.SkipUserVersionCheck
- Fancy.Triggers
Expand Down
Loading