Implement native layout-3 save restoration
This commit is contained in:
@@ -71,8 +71,8 @@ public sealed record AdvTextHistoryRenderBatch(
|
||||
AdvTextStyle Style);
|
||||
|
||||
/// <summary>
|
||||
/// Engine-owned retained ADV backlog. It deliberately has no persistence behavior: native numbered-save
|
||||
/// restoration belongs to the future unified save architecture, while live HISTORY.BIN reads this model.
|
||||
/// Engine-owned retained ADV backlog. Native numbered saves serialize this same model; live HISTORY.BIN
|
||||
/// reads it directly, so restored history is immediately available to the script UI.
|
||||
/// </summary>
|
||||
public sealed class AdvTextHistory
|
||||
{
|
||||
@@ -185,6 +185,35 @@ public sealed class AdvTextHistory
|
||||
_navigationAnchorIndex = -1;
|
||||
}
|
||||
|
||||
/// <summary>Replace retained history from AGE's numbered-save history tail.</summary>
|
||||
public void RestorePersistenceSnapshot(
|
||||
IReadOnlyList<AdvTextHistoryEntry> entries,
|
||||
IReadOnlyList<AdvTextHistoryRecord> records)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entries);
|
||||
ArgumentNullException.ThrowIfNull(records);
|
||||
_records.Clear();
|
||||
_records.AddRange(records);
|
||||
_entries.Clear();
|
||||
_entries.AddRange(entries);
|
||||
_pendingGroupStarts.Clear();
|
||||
_layouts.Clear();
|
||||
foreach (AdvTextHistoryRecord record in records)
|
||||
{
|
||||
var layout = GetOrCreateLayout(record.Layout.Slot);
|
||||
layout.Width = record.Layout.Width;
|
||||
layout.Height = record.Layout.Height;
|
||||
layout.OriginX = record.Layout.OriginX;
|
||||
layout.OriginY = record.Layout.OriginY;
|
||||
layout.CursorX = record.Layout.CursorX;
|
||||
layout.CursorY = record.Layout.CursorY;
|
||||
layout.Right = record.Layout.Right;
|
||||
layout.Bottom = record.Layout.Bottom;
|
||||
}
|
||||
CurrentLayoutSlot = entries.Count > 0 ? entries[^1].LayoutSlot : 0;
|
||||
_navigationAnchorIndex = entries.Count - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve a logical entry relative to AGE's latest-boundary navigation anchor. Repeated calls do not
|
||||
/// mutate the anchor; HISTORY.BIN supplies cumulative deltas while counting and paging backward.
|
||||
|
||||
@@ -56,6 +56,13 @@ public sealed record GfxDiagnosticSnapshot(
|
||||
BlockingGfxObjectDiagnostic? BlockingRangeTransform,
|
||||
IReadOnlyList<BlockingGfxObjectDiagnostic> BlockingObjects);
|
||||
|
||||
public sealed record GfxPersistenceSnapshot(
|
||||
IReadOnlyList<(int Slot, long ResourceId, long ColorKey, bool Created)> Surfaces,
|
||||
IReadOnlyList<(long Handle, GfxState.GfxObject Object)> Objects,
|
||||
long RangeFirst,
|
||||
long RangeCount,
|
||||
GfxState.GfxObject RangeTransform);
|
||||
|
||||
/// <summary>A renderable view of one visible gfx object — the host composites these in ascending-handle order
|
||||
/// (= the engine's z-order) each frame. Built by <see cref="GfxState.SnapshotVisibleObjects"/>; the surface
|
||||
/// resId/colorkey are resolved from the object's live source slot at snapshot time (see docs/engine-re.md,
|
||||
@@ -293,31 +300,7 @@ public sealed class GfxState
|
||||
{
|
||||
if (!_objects.TryGetValue(sourceHandle, out var s)) return false;
|
||||
bool destinationIsNew = !_objects.ContainsKey(destinationHandle);
|
||||
_objects[destinationHandle] = new GfxObject
|
||||
{
|
||||
V18 = s.V18, V24 = s.V24, V16c = s.V16c,
|
||||
Field64 = s.Field64, Field68 = s.Field68, Field6c = s.Field6c,
|
||||
Color = s.Color, HasColor = s.HasColor, StaticColorMode = s.StaticColorMode,
|
||||
OneShotColorTarget = s.OneShotColorTarget, ColorDelayMs = s.ColorDelayMs,
|
||||
ColorDurationMs = s.ColorDurationMs, OneShotColorEnabled = s.OneShotColorEnabled,
|
||||
OneShotColorBlend = s.OneShotColorBlend,
|
||||
SrcFrameCount = s.SrcFrameCount, SrcColumns = s.SrcColumns, SrcCell = s.SrcCell,
|
||||
SrcPeriod = s.SrcPeriod, SrcStart = s.SrcStart, SrcAnim = s.SrcAnim,
|
||||
ColorPeriod = s.ColorPeriod, ColorStart = s.ColorStart, ColorTarget = s.ColorTarget,
|
||||
ColorAnim = s.ColorAnim, SourceSlot = s.SourceSlot, SrcRect = s.SrcRect, Visible = s.Visible,
|
||||
ScaleCurrent = s.ScaleCurrent, ScaleTarget = s.ScaleTarget,
|
||||
ScaleDelayMs = s.ScaleDelayMs, ScaleDurationMs = s.ScaleDurationMs, ScaleEnabled = s.ScaleEnabled,
|
||||
TranslationCurrent = s.TranslationCurrent, TranslationTarget = s.TranslationTarget,
|
||||
TranslationDelayMs = s.TranslationDelayMs, TranslationDurationMs = s.TranslationDurationMs,
|
||||
TranslationEnabled = s.TranslationEnabled,
|
||||
RotationCurrent = s.RotationCurrent, RotationTarget = s.RotationTarget,
|
||||
RotationDelayMs = s.RotationDelayMs, RotationDurationMs = s.RotationDurationMs,
|
||||
RotationChannelEnabled = s.RotationChannelEnabled,
|
||||
OneShotAnimationControlFlags = s.OneShotAnimationControlFlags,
|
||||
OneShotStartMs = s.OneShotStartMs,
|
||||
RotationPeriodMs = s.RotationPeriodMs, RotationAxis = s.RotationAxis,
|
||||
RotationEnabled = s.RotationEnabled, RotationStartMs = s.RotationStartMs,
|
||||
};
|
||||
_objects[destinationHandle] = CloneState(s);
|
||||
if (destinationIsNew) InsertOrderedHandle(destinationHandle);
|
||||
CurrentObject = destinationHandle;
|
||||
MarkRetainedMutation();
|
||||
@@ -418,6 +401,79 @@ public sealed class GfxState
|
||||
}
|
||||
}
|
||||
|
||||
public GfxPersistenceSnapshot CapturePersistenceSnapshot()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var surfaces = _surfaces
|
||||
.Select(pair => (
|
||||
pair.Key, pair.Value.ResId, pair.Value.ColorKey,
|
||||
_createdSurfaces.Contains(pair.Key)))
|
||||
.OrderBy(item => item.Key)
|
||||
.ToArray();
|
||||
var objects = _orderedObjectHandles
|
||||
.Select(handle => (handle, CloneState(_objects[handle])))
|
||||
.ToArray();
|
||||
return new GfxPersistenceSnapshot(
|
||||
surfaces, objects, _rangeTransformFirst, _rangeTransformCount,
|
||||
CloneState(_rangeTransform));
|
||||
}
|
||||
}
|
||||
|
||||
public void RestorePersistenceSnapshot(GfxPersistenceSnapshot snapshot)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(snapshot);
|
||||
lock (_lock)
|
||||
{
|
||||
_surfaces.Clear();
|
||||
_createdSurfaces.Clear();
|
||||
foreach (var (slot, resourceId, colorKey, created) in snapshot.Surfaces)
|
||||
{
|
||||
_surfaces[slot] = (resourceId, colorKey);
|
||||
if (created) _createdSurfaces.Add(slot);
|
||||
}
|
||||
_objects.Clear();
|
||||
_orderedObjectHandles.Clear();
|
||||
foreach (var (handle, state) in snapshot.Objects)
|
||||
{
|
||||
_objects[handle] = CloneState(state);
|
||||
_orderedObjectHandles.Add(handle);
|
||||
}
|
||||
_orderedObjectHandles.Sort();
|
||||
_rangeTransformFirst = snapshot.RangeFirst;
|
||||
_rangeTransformCount = snapshot.RangeCount;
|
||||
_rangeTransform = CloneState(snapshot.RangeTransform);
|
||||
MarkRetainedMutation();
|
||||
}
|
||||
}
|
||||
|
||||
private static GfxObject CloneState(GfxObject s)
|
||||
=> new()
|
||||
{
|
||||
V18 = s.V18, V24 = s.V24, V16c = s.V16c,
|
||||
Field64 = s.Field64, Field68 = s.Field68, Field6c = s.Field6c,
|
||||
Color = s.Color, HasColor = s.HasColor, StaticColorMode = s.StaticColorMode,
|
||||
OneShotColorTarget = s.OneShotColorTarget, ColorDelayMs = s.ColorDelayMs,
|
||||
ColorDurationMs = s.ColorDurationMs, OneShotColorEnabled = s.OneShotColorEnabled,
|
||||
OneShotColorBlend = s.OneShotColorBlend,
|
||||
SrcFrameCount = s.SrcFrameCount, SrcColumns = s.SrcColumns, SrcCell = s.SrcCell,
|
||||
SrcPeriod = s.SrcPeriod, SrcStart = s.SrcStart, SrcAnim = s.SrcAnim,
|
||||
ColorPeriod = s.ColorPeriod, ColorStart = s.ColorStart, ColorTarget = s.ColorTarget,
|
||||
ColorAnim = s.ColorAnim, SourceSlot = s.SourceSlot, SrcRect = s.SrcRect, Visible = s.Visible,
|
||||
ScaleCurrent = s.ScaleCurrent, ScaleTarget = s.ScaleTarget,
|
||||
ScaleDelayMs = s.ScaleDelayMs, ScaleDurationMs = s.ScaleDurationMs, ScaleEnabled = s.ScaleEnabled,
|
||||
TranslationCurrent = s.TranslationCurrent, TranslationTarget = s.TranslationTarget,
|
||||
TranslationDelayMs = s.TranslationDelayMs, TranslationDurationMs = s.TranslationDurationMs,
|
||||
TranslationEnabled = s.TranslationEnabled,
|
||||
RotationCurrent = s.RotationCurrent, RotationTarget = s.RotationTarget,
|
||||
RotationDelayMs = s.RotationDelayMs, RotationDurationMs = s.RotationDurationMs,
|
||||
RotationChannelEnabled = s.RotationChannelEnabled,
|
||||
OneShotAnimationControlFlags = s.OneShotAnimationControlFlags,
|
||||
OneShotStartMs = s.OneShotStartMs,
|
||||
RotationPeriodMs = s.RotationPeriodMs, RotationAxis = s.RotationAxis,
|
||||
RotationEnabled = s.RotationEnabled, RotationStartMs = s.RotationStartMs,
|
||||
};
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
// ---- surfaces (image buffers per slot): ctx+0x52bd4[slot], from create/set-texture ----
|
||||
|
||||
@@ -12,5 +12,9 @@ public sealed class Script
|
||||
public IReadOnlyList<uint> BodyDwords { get; init; } = Array.Empty<uint>();
|
||||
/// <summary>T1 entries: code DWORD offsets of op-0x71 message boundaries.</summary>
|
||||
public IReadOnlyList<int> ReadMessageOffsets { get; init; } = Array.Empty<int>();
|
||||
/// <summary>T2 entries: code DWORD offsets of resumable call-script sites.</summary>
|
||||
public IReadOnlyList<int> ScriptCallOffsets { get; init; } = Array.Empty<int>();
|
||||
/// <summary>T3 entries: code DWORD offsets used to reconstruct the intra-script call stack.</summary>
|
||||
public IReadOnlyList<int> LocalCallOffsets { get; init; } = Array.Empty<int>();
|
||||
public string GetString(int offset) => Strings.TryGetValue(offset, out var s) ? s : "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user