171 lines
7.3 KiB
C#
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);
|
|
}
|
|
}
|