Files
Ananta-ps/tools/AnantaSpoonProbe/SpoonProbeBehaviour.cs
T

171 lines
7.3 KiB
C#

using System;
using System.Reflection;
using HarmonyLib;
using UnityEngine;
namespace AnantaSpoonProbe;
// Hotkeys (main thread):
// F8 = SAFE probe: SpoonTaskMgr.Instance != null? path constants, workAction count for 60004938.
// Ctrl+F8 = LoadSingleTask(60004938) with empty md5. Empty = safe-ignore per R211b.
public class SpoonProbeBehaviour : MonoBehaviour
{
public SpoonProbeBehaviour(IntPtr ptr) : base(ptr) { }
public SpoonProbeBehaviour() { }
private void Update()
{
try
{
if (Input.GetKeyDown(KeyCode.F8))
{
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
TryLoadSingleTask();
else
SafeProbe();
}
}
catch (Exception ex)
{
SpoonProbePlugin.PLog($"hotkey error: {ex}");
}
}
private static void SafeProbe()
{
try
{
var mgrType = AccessTools.TypeByName("L50.Spoon.SpoonRunTime.SpoonTaskMgr");
if (mgrType is null) { SpoonProbePlugin.PLog("SAFE: SpoonTaskMgr type NOT FOUND"); return; }
var instProp = AccessTools.Property(mgrType, "Instance");
var inst = instProp?.GetValue(null);
SpoonProbePlugin.PLog($"SAFE: SpoonTaskMgr.Instance {(inst is null ? "NULL" : "OK")}");
foreach (var n in new[] { "TASK_SPOON_FILE_PATH", "TASK_RAID_FILE_PATH", "EVENT_FILE_PATH",
"TASK_SPOON_FILE_NAME", "TASK_RAID_FILE_NAME", "EVENT_FILE_NAME" })
{
try
{
var f = AccessTools.Field(mgrType, n);
var v = f?.GetValue(null);
SpoonProbePlugin.PLog($"SAFE: const {n} = {SpoonProbePlugin.Summarize(v)} (field {(f is null ? "MISSING" : f.FieldType.Name)})");
}
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE: const {n} read FAIL: {ex.Message}"); }
}
// v2: enumerate ALL static string fields (interop may rename/hide privates).
try
{
int n = 0;
foreach (var f in mgrType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
{
if (f.FieldType != typeof(string)) continue;
if (n >= 20) break;
n++;
object? v = null;
try { v = f.GetValue(null); } catch (Exception ex) { v = $"<readfail {ex.Message}>"; }
SpoonProbePlugin.PLog($"SAFE: staticstr {f.Name} = {SpoonProbePlugin.Summarize(v)}");
}
if (n == 0) SpoonProbePlugin.PLog("SAFE: no static string fields visible via reflection");
}
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE: staticstr enum FAIL: {ex.Message}"); }
// Read-only: workAction count for the Prologue start task.
try
{
var m = AccessTools.Method(mgrType, "GetTaskWorkActionByTask");
if (m is not null && inst is not null)
{
var r = m.Invoke(inst, new object[] { 60004938u });
SpoonProbePlugin.PLog($"SAFE: GetTaskWorkActionByTask(60004938) -> {SpoonProbePlugin.Summarize(r)}");
}
else SpoonProbePlugin.PLog("SAFE: GetTaskWorkActionByTask method/instance missing");
}
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE: workAction read FAIL: {ex.Message}"); }
SpoonProbePlugin.PLog("SAFE probe done. (Ctrl+F8 = LoadSingleTask attempt)");
}
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE probe FAIL: {ex}"); }
}
private static void TryLoadSingleTask()
{
try
{
const uint taskId = 60004938;
var mgrType = AccessTools.TypeByName("L50.Spoon.SpoonRunTime.SpoonTaskMgr");
var spoonType = AccessTools.TypeByName("UX.Game.TaskSpoonViewInfo");
var stateType = AccessTools.TypeByName("UX.Game.TaskState");
if (mgrType is null || spoonType is null)
{
SpoonProbePlugin.PLog($"LOAD: type missing mgr={mgrType is not null} spoon={spoonType is not null}");
return;
}
var inst = AccessTools.Property(mgrType, "Instance")?.GetValue(null);
if (inst is null) { SpoonProbePlugin.PLog("LOAD: Instance NULL"); return; }
var spoon = Activator.CreateInstance(spoonType);
Set(spoon!, "SpoonMd5", ""); // MUST stay empty (fake md5 hard-crashes, R211b)
Set(spoon!, "SpRaidId", 23301180u);
Set(spoon!, "StartTaskId", taskId);
Set(spoon!, "EndTaskId", 60004955u);
Set(spoon!, "Alias", "");
Set(spoon!, "EventId", 1441u);
Set(spoon!, "EventStartTaskId", taskId);
SpoonProbePlugin.PLog($"LOAD: calling LoadSingleTask task={taskId} {SpoonProbePlugin.Summarize(spoon)}");
object stateVal = 1; // TaskState.Accepted = 1
try
{
if (stateType is not null && stateType.IsEnum)
stateVal = Enum.ToObject(stateType, 1);
}
catch { }
// counterValues: match the declared parameter type (Il2Cpp List<int> or managed List<int>).
object? counters = null;
try
{
var loadMi = FindLoadSingleTask(mgrType);
var ps = loadMi?.GetParameters();
if (ps is not null && ps.Length >= 5)
{
var listType = ps[2].ParameterType;
counters = Activator.CreateInstance(listType);
var add = listType.GetMethod("Add");
add?.Invoke(counters, new object[] { 0 });
}
}
catch (Exception ex) { SpoonProbePlugin.PLog($"LOAD: counter list build FAIL: {ex.Message}"); }
var mi2 = FindLoadSingleTask(mgrType);
if (mi2 is null) { SpoonProbePlugin.PLog("LOAD: LoadSingleTask method NOT FOUND"); return; }
mi2.Invoke(inst, new object?[] { taskId, stateVal, counters, spoon, false });
SpoonProbePlugin.PLog("LOAD: LoadSingleTask invoked (watch ENTER/LEAVE lines + dumps/spoon/)");
}
catch (Exception ex) { SpoonProbePlugin.PLog($"LOAD FAIL: {ex}"); }
}
private static MethodInfo? FindLoadSingleTask(Type mgrType)
{
foreach (var mi in mgrType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
if (mi.Name == "LoadSingleTask" && mi.GetParameters().Length == 5)
return mi;
return AccessTools.Method(mgrType, "LoadSingleTask");
}
private static void Set(object o, string name, object? v)
{
var t = o.GetType();
var f = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (f is not null)
{
if (v is uint u && f.FieldType.IsEnum) f.SetValue(o, Enum.ToObject(f.FieldType, u));
else f.SetValue(o, v);
return;
}
var p = t.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
p?.SetValue(o, v);
}
}