Use universal packed resource resolution
This commit is contained in:
@@ -21,11 +21,9 @@ public interface IHost
|
||||
{
|
||||
/// <summary>Report a recoverable runtime discrepancy while allowing script execution to continue.</summary>
|
||||
void ReportWarning(string message) => System.Console.Error.WriteLine(message);
|
||||
// Script-local resource ids resolve against the currently executing frame's SYS4INI section.
|
||||
// Interactive hosts track this stack; headless hosts may keep the no-op/default identity behavior.
|
||||
// Script context is retained for diagnostics/page location; resource operands are universal packed ids.
|
||||
void EnterScriptContext(string scriptName) { }
|
||||
void ExitScriptContext() { }
|
||||
long ResolveTextureResourceId(long resourceId) => resourceId;
|
||||
void ShowText(int offset, string text);
|
||||
// Native ADV text subsystem: op 0x7a updates the selected layout's last 20-byte cursor record;
|
||||
// op 0x204 rasterizes a string into a numbered surface before 0x1fb binds that surface.
|
||||
@@ -125,8 +123,8 @@ public interface IHost
|
||||
/// immediately after 0x236 returns.</returns>
|
||||
long? PlayMovieToSurface(long resourceId, int surfaceSlot, long movieFlags, long syncMask) => null;
|
||||
bool IsMovieSurfaceActive(int surfaceSlot) => false;
|
||||
// Native op 0x20f uses a universal raw-catalog id and parks script execution until the movie
|
||||
// Native op 0x20f uses a universal packed id and parks script execution until the movie
|
||||
// reaches EOF or the player cancels it. The decoder remains asynchronous; the interactive host
|
||||
// owns the modal wait so its render loop can continue publishing frames.
|
||||
void PlayModalMovieToSurface(long rawResourceId, int surfaceSlot, long movieFlags) { }
|
||||
void PlayModalMovieToSurface(long resourceId, int surfaceSlot, long movieFlags) { }
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace Age.Engine.Sys4;
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility facade over the runtime SYS4 catalog. Scene-local graphics/voice/movie ids resolve
|
||||
/// through the executing script's manifest; BGM uses direct names; SFX/cursors use universal packed ids.
|
||||
/// Typed facade over the runtime SYS4 catalog. Graphics, voice, movie, SFX, and cursor operands use
|
||||
/// universal packed ids; BGM uses direct names.
|
||||
/// See docs/asset-resolution-re.md.
|
||||
/// </summary>
|
||||
public sealed class ResourceMap
|
||||
@@ -18,43 +18,26 @@ public sealed class ResourceMap
|
||||
|
||||
public static ResourceMap Load() => new(Sys4AssetCatalog.Load(Paths.Sys4Ini));
|
||||
|
||||
/// <summary>Resolve a scene-local resId to its asset, or null if out of range / unknown scene.</summary>
|
||||
public AssetEntry? Resolve(string scene, long resId)
|
||||
/// <summary>Resolve a universal packed SYS4INI/AAI id to an AGF texture record.</summary>
|
||||
public AssetEntry? ResolveTexture(long resourceId)
|
||||
{
|
||||
return _catalog.ResolveScene(scene, resId);
|
||||
}
|
||||
|
||||
/// <summary>Resolve graphics normally through the scene manifest, with the universal raw-id
|
||||
/// fallback used by SYSTEM4-owned assets such as SO001.</summary>
|
||||
public AssetEntry? ResolveTexture(string scene, long resId)
|
||||
{
|
||||
var entry = _catalog.ResolveScene(scene, resId) ?? _catalog.ResolveRaw(resId);
|
||||
var entry = _catalog.ResolvePacked(resourceId);
|
||||
return entry is { IsPlaceholder: false } &&
|
||||
entry.Name.EndsWith(".AGF", StringComparison.OrdinalIgnoreCase) ? entry : null;
|
||||
}
|
||||
|
||||
/// <summary>Resolve voice audio through the active SC section when one exists, then through the
|
||||
/// universal raw catalog used by non-SC frontend scripts such as ROOM.</summary>
|
||||
public AssetEntry? ResolveVoice(string scene, long resId)
|
||||
/// <summary>Resolve a universal packed SYS4INI/AAI id to a voice audio record.</summary>
|
||||
public AssetEntry? ResolveVoice(long resourceId)
|
||||
{
|
||||
var entry = _catalog.ResolveScene(scene, resId) ?? _catalog.ResolveRaw(resId);
|
||||
var entry = _catalog.ResolvePacked(resourceId);
|
||||
return entry is { IsPlaceholder: false } && IsAudio(entry) ? entry : null;
|
||||
}
|
||||
|
||||
/// <summary>Resolve an already-normalized packed catalog id without applying a scene section base.</summary>
|
||||
public AssetEntry? ResolveRawTexture(long rawId)
|
||||
/// <summary>Resolve a universal packed SYS4INI/AAI id to an AGF-named movie record. ReadMovie
|
||||
/// validates the MPEG signature because still images use the same extension.</summary>
|
||||
public AssetEntry? ResolveMovie(long resourceId)
|
||||
{
|
||||
var entry = _catalog.ResolvePacked(rawId);
|
||||
return entry is { IsPlaceholder: false } &&
|
||||
entry.Name.EndsWith(".AGF", StringComparison.OrdinalIgnoreCase) ? entry : null;
|
||||
}
|
||||
|
||||
/// <summary>Resolve op 0x20f's universal raw-catalog movie id without applying the executing
|
||||
/// script's manifest base. AGE stores these MPEG program streams under .AGF names; ReadMovie
|
||||
/// validates the payload signature before playback.</summary>
|
||||
public AssetEntry? ResolveRawMovie(long rawId)
|
||||
{
|
||||
var entry = _catalog.ResolveRaw(rawId);
|
||||
var entry = _catalog.ResolvePacked(resourceId);
|
||||
return entry is { IsPlaceholder: false } &&
|
||||
entry.Name.EndsWith(".AGF", StringComparison.OrdinalIgnoreCase) ? entry : null;
|
||||
}
|
||||
@@ -81,7 +64,7 @@ public sealed class ResourceMap
|
||||
|
||||
/// <summary>
|
||||
/// Resolve a BGM id to its catalog entry. BGM is addressed by DIRECT LITERAL NAME (BGM{id:D3}.OGG), NOT the
|
||||
/// per-scene section manifest that voices/textures use. Confirmed by ear (play-bgm 5->BGM005, 8->BGM008)
|
||||
/// universal packed resource table used by voices/textures. Confirmed by ear (play-bgm 5->BGM005, 8->BGM008)
|
||||
/// and by the play-bgm 0x23->BGM035 case: BGM035 is a real standalone track (the BGM set skips 030-034),
|
||||
/// which the manifest mis-resolved to a graphics entry. See docs/asset-resolution-re.md.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,11 @@ public sealed record AssetEntry(
|
||||
int ArchiveId = -1,
|
||||
int FileNumber = -1,
|
||||
bool IsPlaceholder = false,
|
||||
int PackId = 0);
|
||||
int PackId = 0)
|
||||
{
|
||||
/// <summary>The exact packed SYS4INI/AAI id AGE uses to address this record.</summary>
|
||||
public int PackedId => checked((PackId << 24) | RawIndex);
|
||||
}
|
||||
|
||||
/// <summary>A real catalog entry paired with the packed resource id AGE uses at runtime.</summary>
|
||||
public sealed record PackedAssetEntry(long PackedId, AssetEntry Asset);
|
||||
|
||||
@@ -1539,29 +1539,27 @@ public sealed class VirtualMachine
|
||||
_host.CreateTexture((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2])); return pc + 1;
|
||||
case "set-texture": // 0x1f9 (resId)(slot)(colorkey) — load a file into the slot's surface
|
||||
{
|
||||
long requestedResourceId = Read(a[0]);
|
||||
long resolvedResourceId = _host.ResolveTextureResourceId(requestedResourceId);
|
||||
long resourceId = Read(a[0]);
|
||||
if (_diagSetTexture) // AGE_DIAG_SETTEX: log the SLOT operand source (literal vs which global) — grey-BG slot dig
|
||||
System.Console.Error.WriteLine($"[settex] resId=0x{requestedResourceId:x}->0x{resolvedResourceId:x} slot={(int)Read(a[1])} " +
|
||||
System.Console.Error.WriteLine($"[settex] resId=0x{resourceId:x} slot={(int)Read(a[1])} " +
|
||||
$"slotOp=(type={a[1].Type} val=0x{a[1].Value:x}){(a[1].Type == 3 ? $" G[0x{a[1].Value:x}]" : "")}");
|
||||
_host.ReleaseSurface((int)Read(a[1]));
|
||||
long colorKey = a.Count > 2 ? Read(a[2]) : -1;
|
||||
Gfx.SetSurface((int)Read(a[1]), resolvedResourceId, colorKey);
|
||||
_host.SetTexture(resolvedResourceId, (int)Read(a[1]), colorKey);
|
||||
Gfx.SetSurface((int)Read(a[1]), resourceId, colorKey);
|
||||
_host.SetTexture(resourceId, (int)Read(a[1]), colorKey);
|
||||
return pc + 1; // host still tracks dims for get-texture-size
|
||||
}
|
||||
case "u00422EB0": // pre-reference compatibility
|
||||
case "load-raw-texture-surface": // 0x249 (raw catalog id)(slot)(colorkey)
|
||||
case "load-raw-texture-surface": // 0x249 (packed resource id)(slot)(colorkey)
|
||||
{
|
||||
// Native shares 0x1f9's release/load/colorkey path, but constructs its mode-1
|
||||
// surface subclass and receives an already-global SYS4INI catalog index. The
|
||||
// CPU compositor does not need the D3D subclass distinction; it does need the
|
||||
// resource id to bypass the executing script's scene-section normalization.
|
||||
long rawResourceId = Read(a[0]);
|
||||
// surface subclass. Both texture opcodes receive the same universal packed id;
|
||||
// the CPU compositor does not need the D3D subclass distinction.
|
||||
long resourceId = Read(a[0]);
|
||||
int surfaceSlot = (int)Read(a[1]);
|
||||
_host.ReleaseSurface(surfaceSlot);
|
||||
Gfx.SetSurface(surfaceSlot, rawResourceId, Read(a[2]));
|
||||
_host.SetTexture(rawResourceId, surfaceSlot, Read(a[2]));
|
||||
Gfx.SetSurface(surfaceSlot, resourceId, Read(a[2]));
|
||||
_host.SetTexture(resourceId, surfaceSlot, Read(a[2]));
|
||||
return pc + 1;
|
||||
}
|
||||
case "draw-texture": // 0x1fb (handle)(slot)(srcX)(srcY)(w)(h)(dstX)(dstY) — bind object -> surface + rect + pos
|
||||
@@ -1652,14 +1650,14 @@ public sealed class VirtualMachine
|
||||
case "get-initial-root-run": // 0x130 (out)
|
||||
Write(a[0], _initialRootRun ? 1 : 0);
|
||||
return pc + 1;
|
||||
case "play-modal-movie-to-surface": // 0x20f (raw resource)(surface)(movie flags)
|
||||
case "play-modal-movie-to-surface": // 0x20f (packed resource)(surface)(movie flags)
|
||||
{
|
||||
long rawResourceId = Read(a[0]);
|
||||
long resourceId = Read(a[0]);
|
||||
int surfaceSlot = (int)Read(a[1]);
|
||||
// The modal and scene-local paths share retained-surface composition. The host's
|
||||
// distinct entry point preserves 0x20f's raw-id resolver and blocking lifecycle.
|
||||
Gfx.SetSurface(surfaceSlot, rawResourceId, 0);
|
||||
_host.PlayModalMovieToSurface(rawResourceId, surfaceSlot, Read(a[2]));
|
||||
// Modal and non-modal paths share packed resolution and retained-surface composition.
|
||||
// The distinct host entry point owns only 0x20f's blocking lifecycle.
|
||||
Gfx.SetSurface(surfaceSlot, resourceId, 0);
|
||||
_host.PlayModalMovieToSurface(resourceId, surfaceSlot, Read(a[2]));
|
||||
return pc + 1;
|
||||
}
|
||||
case "u004221A0": // pre-reference compatibility
|
||||
|
||||
Reference in New Issue
Block a user