From f44f7641b15039f495e399aef789a6c36dc42397 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 21 Aug 2026 10:41:54 +0200 Subject: [PATCH] tools(LayoutDump): --props and --media, for questions the decomp cannot answer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two dump modes, both added because a chat bug turned on authored data rather than code. --props prints each state's property ids WITH VALUES. It is what settled the unseen-text indicator regression: element 0x1000048C authors dat property 0x3B ("Invisible") per state — state 13 Ghosted 0x3B = True -> hidden state 1 Normal 0x3B = False -> shown — which is what actually puts that element on screen. No amount of reading the decomp would have produced those two booleans. --media prints the RAW media sequence per state off the LayoutDesc, because ElementInfo keeps only the FIRST image of a state (LayoutImporter.cs, the `!imageRead` guard) and an animation is therefore invisible above that level. It reports what it CANNOT do rather than implying an element has no media: raw descriptors only carry what an element overrides, and states usually come from a base element — for the indicator, base 0x10000527, which lives in a different layout entirely. Following that chain means reimplementing LayoutImporter's Resolve, so the mode says so instead of printing a misleading empty list. What the two modes established between them, for whoever picks up the blink: the media vocabulary includes MediaDescImage, MediaDescPause and MediaDescJump, so an authored animation is a SEQUENCE with waits and a loop — a small program, not a frame array. Supporting it means keeping the sequence through import and running a media player, not just widening StateMedia to a list. Co-Authored-By: Claude Opus 5 --- tools/LayoutDump/Program.cs | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tools/LayoutDump/Program.cs b/tools/LayoutDump/Program.cs index 255917ff..38b8a2b0 100644 --- a/tools/LayoutDump/Program.cs +++ b/tools/LayoutDump/Program.cs @@ -46,6 +46,55 @@ if (root is null) Console.WriteLine($"layout 0x{ids[0]:X8}"); Print(root, 0); +int mediaAt = Array.IndexOf(args, "--media"); +if (mediaAt >= 0) +{ + // The RAW media sequence per state, straight off the LayoutDesc. ElementInfo + // keeps only the first image, so an animation is invisible above that level. + uint wanted = mediaAt + 1 < args.Length + ? Convert.ToUInt32(args[mediaAt + 1], 16) + : 0u; + // Layouts are not necessarily in Portal — go through the adapter, the same + // way LayoutImporter does. + var ld = adapter.Get(ids[0]); + if (ld is null) + { + Console.WriteLine($"layout 0x{ids[0]:X8} not found"); + return 2; + } + + foreach (var top in ld.Elements) + Walk(top.Value); + return 0; + + void Walk(DatReaderWriter.Types.ElementDesc d) + { + if (wanted == 0 || d.ElementId == wanted) + { + Console.WriteLine($"element 0x{d.ElementId:X8}"); + if (d.States.Count == 0) + { + // Raw descriptors only carry what THIS element overrides; states + // and their media usually come from the base element, and + // LayoutDesc::InqFullDesc @0x0069A520 resolves that chain. + // Following it here would mean reimplementing LayoutImporter's + // Resolve, so say so rather than imply the element has none. + Console.WriteLine( + $" (no states of its own — inherited from base 0x{d.BaseElement:X8};" + + " raw media not resolved here)"); + } + foreach (var st in d.States) + { + Console.WriteLine($" state {st.Key}: {st.Value.Media.Count} media"); + foreach (var m in st.Value.Media) + Console.WriteLine($" {m.GetType().Name}"); + } + } + foreach (var child in d.Children) + Walk(child.Value); + } +} + int resizeAt = Array.IndexOf(args, "--resize"); if (resizeAt >= 0 && resizeAt + 2 < args.Length) {