ResourceForkReader 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package ResourceForkReader --version 0.1.0
                    
NuGet\Install-Package ResourceForkReader -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ResourceForkReader" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ResourceForkReader" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="ResourceForkReader" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ResourceForkReader --version 0.1.0
                    
#r "nuget: ResourceForkReader, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ResourceForkReader@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ResourceForkReader&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=ResourceForkReader&version=0.1.0
                    
Install as a Cake Tool

ResourceForkReader

A lightweight .NET library for parsing classic Macintosh resource fork files. It focuses on reading the resource header and map, enumerating resource types and entries, and extracting raw resource data so you can interpret specific record formats (strings, string lists, versions, code segments, etc.).

A resource fork is a structured collection of typed resources (e.g., icons, strings, version info) historically used in classic Mac OS / early macOS applications. This library lets you inspect those resources from .NET code.

Features

  • Read resource fork header (ResourceForkHeader) and map (ResourceForkMap).
  • Enumerate resource types and entries (Types dictionary).
  • Extract raw resource data for any entry (GetResourceData).
  • Helpers for common record structures:
    • StringRecord – Pascal-style length-prefixed string.
    • StringListRecord – List of Pascal-style strings (STR#).
    • VersionRecord – Simple 2+2 ASCII version components (VERS).
    • Code0Segment – Parses CODE 0 segment jump table.
    • CodeSegment – Access executable code segment.
  • Efficient big-endian parsing utilities (SpanUtilities).
  • Zero external dependencies; spans + stackalloc for minimal allocations.

Installation

Currently source-only. You can:

  1. Reference the project directly:
    <ProjectReference Include="../src/ResourceForkReader.csproj" />
    
  2. Or copy the src/ folder into your solution.

A NuGet package could be published later (see roadmap).

Quick Start

using var stream = File.OpenRead(Path.Combine("Samples", "Microsoft Excel.res"));
var fork = new ResourceFork(stream);

// Enumerate types
foreach (var (type, entries) in fork.Map.Types)
{
    Console.WriteLine($"Type {type} has {entries.Count} resources");
}

// Read a string list (STR#) resource
var strListEntry = fork.Map.Types["STR#"][0];
var data = fork.GetResourceData(strListEntry); // raw bytes
var listRecord = new StringListRecord(data);
foreach (var s in listRecord.Values)
{
    Console.WriteLine(s);
}

// Read version (VERS)
var versEntry = fork.Map.Types["VERS"][0];
var versData = fork.GetResourceData(versEntry);
var version = new VersionRecord(versData);
Console.WriteLine($"Version: {version.Major}.{version.Minor}");

API Overview

ResourceFork

  • ResourceFork(Stream stream) – Accepts a seekable, readable stream of a resource fork ("*.res" or raw fork data).
  • Properties:
    • HeaderResourceForkHeader with offsets/lengths.
    • MapResourceForkMap containing type dictionary.
  • Methods:
    • byte[] GetResourceData(ResourceListEntry entry) – Materialize resource bytes.
    • int GetResourceData(ResourceListEntry entry, Stream output) – Stream resource bytes to an output stream (avoids extra allocation).

ResourceForkMap

Parses the map and builds Dictionary<string, List<ResourceListEntry>> Types where keys are four-character type codes ("STR ", "STR#", "CODE", "VERS", etc.). Includes offsets and attributes for each resource.

Records / Helpers

Type Purpose
StringRecord Parses a Pascal-style string (STR ).
StringListRecord Parses a string list resource (STR#).
VersionRecord Parses major/minor version from VERS.
Code0Segment Reads CODE 0 segment sizes and jump table.
CodeSegment Exposes executable code after offset.

SpanUtilities

Internal helpers for reading big-endian numeric and ASCII values from spans.

Working With Resource Types

Classic Mac resource type codes are four ASCII characters. Examples:

  • CODE – Executable code segments.
  • STR – Single Pascal string (note trailing space).
  • STR# – List of strings.
  • VERS – Version info.

You can extend parsing by adding new record structs/classes for other types (e.g., MENU, ICN#, FREF). Use the raw bytes from GetResourceData.

Build & Test

# Restore
dotnet restore
# Build library and tests
dotnet build
# Run tests
dotnet test

Target framework: .NET 9.0

Tests demonstrate extracting string resources and version records from the sample Microsoft Excel.res file.

Performance Notes

  • Uses Span<T>, ReadOnlySpan<T>, and stackalloc for small, fixed-size reads.
  • Minimal allocations when streaming resource data (GetResourceData overload with Stream).
  • Big-endian parsing is manual to avoid BitConverter overhead.

Error Handling

  • Throws ArgumentException if stream isn't seekable/readable or if resource data/map offsets are invalid.
  • Struct constructors validate length constraints and throw if data is insufficient.

Roadmap / Ideas

  • Publish NuGet package (add CI + packaging metadata).
  • Parsing of resource names (currently NameOffset isn't resolved to actual names).
  • Async I/O methods (GetResourceDataAsync).
  • Writing/modifying resource forks.
  • Additional built-in record parsers (icons, menus, dialog definitions).
  • Optional memory-mapped file support for very large forks.

Limitations

  • Read-only; no mutation or creation of resource forks.
  • Assumes well-formed classic Mac resource fork layout.
  • Doesn't parse resource name table yet.

Contributing

Pull requests welcome. Please add tests for new record types or parsing changes.

License

(Choose a license: MIT recommended.) Example:

Copyright (c) YEAR AUTHOR

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction...

If you add LICENSE with MIT, update this section accordingly.

References

  • Apple Legacy Docs: Resource Manager
  • Inside Macintosh (Volumes I–VI)

Feel free to open issues for unsupported resource types or feature requests.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 86 1/11/2026
0.1.0 293 11/11/2025