Skip to content

Unpack !AvaloniaResources into per-file resource tree nodes - #3847

Merged
siegfriedpammer merged 1 commit into
masterfrom
avalonia-resources-node
Jul 2, 2026
Merged

Unpack !AvaloniaResources into per-file resource tree nodes#3847
siegfriedpammer merged 1 commit into
masterfrom
avalonia-resources-node

Conversation

@siegfriedpammer

@siegfriedpammer siegfriedpammer commented Jul 1, 2026

Copy link
Copy Markdown
Member

Adds a reader for the !AvaloniaResources manifest resource that Avalonia embeds in compiled assemblies, and a tree node that unpacks it into a per-file ResourceEntryNode for each packed resource (compiled XAML, images, ...), each viewable and savable individually.

Contents

  • AvaloniaResourcesFile (in ICSharpCode.Decompiler) parses the v2 binary index (length-prefixed UTF-8 paths + offset/size), rooting paths the way Avalonia's AssetLoader does.
  • AvaloniaResourcesFileTreeNode lazily unpacks the entries into the resource tree.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 AvaloniaResourcesFile to parse the v2 !AvaloniaResources binary index and expose per-path payloads.
  • Adds AvaloniaResourcesFileTreeNode + MEF factory to unpack !AvaloniaResources into 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.

Comment on lines +132 to +134
string path = GetPathRooted(reader.ReadString());
int offset = reader.ReadInt32();
int size = reader.ReadInt32();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Comment on lines +59 to +63
var s = Resource.TryOpenStream();
if (s == null)
return;
s.Position = 0;
try

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@siegfriedpammer
siegfriedpammer merged commit 9b99352 into master Jul 2, 2026
13 checks passed
@christophwille
christophwille deleted the avalonia-resources-node branch July 2, 2026 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants