questStates done

This commit is contained in:
Boki 2026-03-05 16:44:30 -05:00
parent 445ae1387c
commit 568df4c7fe
17 changed files with 1012 additions and 40 deletions

View file

@ -20,6 +20,9 @@ public sealed class UIElements : RemoteObject
private readonly MsvcStringReader _strings;
/// <summary>Optional lookup for resolving quest state IDs to human-readable text.</summary>
public QuestStateLookup? QuestStateLookup { get; set; }
public UIElements(MemoryContext ctx, MsvcStringReader strings) : base(ctx)
{
_strings = strings;
@ -382,7 +385,11 @@ public sealed class UIElements : RemoteObject
displayName = _strings.ReadNullTermWString(namePtr);
}
var isTracked = trackedMap.TryGetValue(questPtr, out var objectiveText);
var isTracked = trackedMap.ContainsKey(questPtr);
string? stateText = null;
if (QuestStateLookup is not null && questPtr != 0 && stateId > 0)
QuestStateLookup.TryGetStateText(questPtr, stateId, out stateText);
result.Add(new QuestLinkedEntry
{
@ -390,8 +397,8 @@ public sealed class UIElements : RemoteObject
DisplayName = displayName,
Act = act,
StateId = stateId,
StateText = stateText,
IsTracked = isTracked,
ObjectiveText = objectiveText,
QuestDatPtr = questPtr,
});
@ -402,8 +409,9 @@ public sealed class UIElements : RemoteObject
}
/// <summary>
/// Walks the tracked-quests linked list. Builds a dict of QuestDatPtr → ObjectiveText.
/// Node+0x20 is a pointer to a runtime state object; text is at stateObj+QuestStateObjTextOffset (std::wstring).
/// Walks the tracked-quests linked list. Builds a dict of QuestDatPtr → null.
/// Tracked status is used; objective text is not available from runtime memory
/// (stateObj contains quest flag arrays, not display text).
/// </summary>
private void TraverseTrackedQuests(nint headPtr, Dictionary<nint, string?> trackedMap)
{
@ -434,35 +442,15 @@ public sealed class UIElements : RemoteObject
}
var questPtr = (nint)BitConverter.ToInt64(nodeData, offsets.QuestNodeQuestPtrOffset);
string? objectiveText = null;
// +0x20 in tracked list is a pointer to the quest state runtime object
var stateObjPtr = (nint)BitConverter.ToInt64(nodeData, 0x20);
if (stateObjPtr != 0 && ((ulong)stateObjPtr >> 32) is > 0 and < 0x7FFF)
{
// Read std::wstring at stateObj + QuestStateObjTextOffset
objectiveText = ParseWStringFromMemory(stateObjPtr + offsets.QuestStateObjTextOffset);
}
if (questPtr != 0)
trackedMap[questPtr] = objectiveText;
trackedMap[questPtr] = null;
count++;
walk = next;
}
}
/// <summary>
/// Reads an inline MSVC std::wstring from a process memory address.
/// Same layout as UIElement strings: Buffer(8) + Reserved(8) + Length(4) + pad(4) + Capacity(4) + pad(4).
/// </summary>
private string? ParseWStringFromMemory(nint addr)
{
var strData = Ctx.Memory.ReadBytes(addr, 32);
if (strData is null || strData.Length < 28) return null;
return ParseWStringFromBuffer(strData, 0);
}
private void EnqueueChildren(Queue<nint> queue, HashSet<nint> visited, nint parentAddr)
{
var mem = Ctx.Memory;