Unpack !AvaloniaResources into per-file resource tree nodes - #3847
Conversation
f9143dd to
ac49c74
Compare
There was a problem hiding this comment.
Pull request overview
Adds first-class support for Avalonia’s !AvaloniaResources manifest resource by parsing its packed index format and surfacing each packed asset as an individual resource-tree entry in ILSpy (with accompanying unit tests).
Changes:
- Introduces
AvaloniaResourcesFileto parse the v2!AvaloniaResourcesbinary index and expose per-path payloads. - Adds
AvaloniaResourcesFileTreeNode+ MEF factory to unpack!AvaloniaResourcesinto per-file child nodes in the resource tree. - Extends ILSpy and decompiler test suites to cover routing, unpacking, and malformed/crafted blob handling.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| ILSpy/TreeNodes/AvaloniaResourcesFileTreeNode.cs | Adds a resource tree node + factory to expand !AvaloniaResources into per-file children. |
| ILSpy.Tests/Resources/ResourceFactoryTests.cs | Adds ILSpy-side tests for factory routing and child node unpacking/sorting. |
| ICSharpCode.Decompiler/Util/AvaloniaResourcesFile.cs | Implements the !AvaloniaResources v2 blob reader with bounds checks and dedup behavior. |
| ICSharpCode.Decompiler.Tests/Util/AvaloniaResourcesFileTests.cs | Adds parser correctness + robustness tests (aliasing, overlap rejection, huge counts, OOB). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| string path = GetPathRooted(reader.ReadString()); | ||
| int offset = reader.ReadInt32(); | ||
| int size = reader.ReadInt32(); |
There was a problem hiding this comment.
The OOM claim does not hold: BinaryReader.ReadString reads in 128-byte chunks, so a lying length prefix cannot allocate beyond what the blob actually contains, and the resulting EndOfStreamException was already mapped to BadImageFormatException (now locked in by a test). The valid part — an entry spilling past the declared index end into the data section — is fixed in 81da369: the index boundary is now enforced after each entry.
(Written by an agent.)
| var s = Resource.TryOpenStream(); | ||
| if (s == null) | ||
| return; | ||
| s.Position = 0; | ||
| try |
There was a problem hiding this comment.
Fixed in 81da369 with using var — unlike ResourcesFile, AvaloniaResourcesFile copies everything out of the stream in its constructor, so disposing immediately is safe. The unconditional Position = 0 stays: every Resource.TryOpenStream() implementation returns a seekable stream, matching ResourcesFileTreeNode.
(Written by an agent.)
Avalonia apps pack every resource (compiled XAML, image/SVG assets, ...) behind a single !AvaloniaResources manifest blob, which until now fell through to the generic resource node and could not be browsed. Parse the blob's index and expose each packed file as its own entry, mirroring how .resources files are unpacked, so individual files can be viewed and saved. The reader is bounds-checked against crafted offsets/sizes in the same defensive spirit as the recent .rsrc parsing guards. Assisted-by: Claude:claude-opus-4-8:Claude Code
ac49c74 to
81da369
Compare
Adds a reader for the
!AvaloniaResourcesmanifest resource that Avalonia embeds in compiled assemblies, and a tree node that unpacks it into a per-fileResourceEntryNodefor each packed resource (compiled XAML, images, ...), each viewable and savable individually.Contents
AvaloniaResourcesFile(inICSharpCode.Decompiler) parses the v2 binary index (length-prefixed UTF-8 paths + offset/size), rooting paths the way Avalonia'sAssetLoaderdoes.AvaloniaResourcesFileTreeNodelazily unpacks the entries into the resource tree.🤖 Generated with Claude Code