appendix:harmonyがある時

差分

このページの2つのバージョン間の差分を表示します。

この比較画面にリンクする

次のリビジョン
前のリビジョン
appendix:harmonyがある時 [2024/12/18 14:47]
fumble 作成
appendix:harmonyがある時 [2024/12/29 00:31] (現在)
fumble
行 54: 行 54:
 それぞれ渡すべきプロトタイプは それぞれ渡すべきプロトタイプは
  
 +prefixはパッチを当てるものと同じもの。\\
 +postfixはパッチを当てるものの戻り値を受け取る戻りに無しのもの。\\
 +finalizerはExceptionを貰ってExceptionを返すもの。\\
 +transpilerとilmanipulatorは以下の様なもの。\\
 <code csharp> <code csharp>
 static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
 +static void ILManipulator(ILContext il, MethodBase original, ILLabel retLabel)
 +static void SomeOtherILManipulator(ILContext ctx, MethodBase orig)
 </code> </code>
 +
 +渡すときはHarmonyMethod(MethodInfo method)の変換、つまりコンストラクターを呼んで変換させる。
 +<code csharp>
 +var transpilermethod = typeof(トランスパイラーのあるクラス).GetMethod(トランスパイラーのメソッド名);
 +var transpiler = Activator.CreateInstance(typeHarmonyMethod, transpiler);
 +</code>
 +
 +問題が起こりそうなのはTranspilerとILManipulatorのパターンでCodeInstructionがHarmonyの中で、ILLabelがMonoMod.Cliの中で宣言されているので参照出来ない状況だと面倒になる。\\
 +※ILContextはMono.CecilなのでSybaris2/BepInExでも参照してしまえば良い。
 +
 +特に面倒が起こりそうな Transpiler の実装方法を示す。
 +<code csharp>
 +static object Transpiler(IEnumerable instructions)
 +{
 +    var enumerator = instructions?.GetEnumerator();
 +    if (enumerator != null && enumerator.MoveNext())
 +    {
 +        var type = enumerator.Current.GetType();
 +        var fldOpecode = type.GetField("opcode");
 +        var fldOperand = type.GetField("operand");
 +        var newInstructions = Activator.CreateInstance(typeof(List<>).MakeGenericType(type)) as IList;
 +        foreach (var instruction in instructions)
 +        {
 +            var opecode = fldOpecode.GetValue(instruction);
 +            var operand = fldOperand.GetValue(instruction);
 +            if (opecode.Equals(変えたいところのOpCode) && operand != null && operand.ToString() == 変えたいところのOperand)
 +                newInstructions.Add(Activator.CreateInstance(type, 変えたいOpCode, 変えたいOperand));
 +            else
 +                newInstructions.Add(instruction);
 +        }
 +    }
 +    return newInstructions;
 +}
 +</code>
 +引数をIEnumerableで受けて戻り値をobjectとかで宣言してしまいます。\\
 +引数で貰った IEnumerable<CodeInstruction> を書き換えて返していいのかがわからない((Copilotは書き換えて構わないみたいなことを言いますが…))ので、リストを作り直しそこに詰め込んで返しています。\\
 +
 +<code csharp>
 +var newInstructions = Activator.CreateInstance(typeof(List<>).MakeGenericType(type)) as IList;
 +return newInstructions;
 +</code>
 +
 +こんなのでいいの?って思うかもしれませんが多分これで良いのです。\\
 +あとは変えたいところだけ、全部変えたいなら全部詰め直して返せば良いです。\\
 +OpCodeはSystem.Reflection.Emit.OpCodeっぽいのでこれを参照すればHarmonyLibの参照はなしでいけます。\\
 +ILManipulatorの方もILLabel retLabelをobjectで受けてリフレクションを使えばいけると思います、必要が無い限りILLabelのない方を使えばいいと思います。\\
  
  • appendix/harmonyがある時.1734500822.txt.gz
  • 最終更新: 2024/12/18 14:47
  • by fumble