FVNever.Oddities.WinHelp 1.0.0

dotnet add package FVNever.Oddities.WinHelp --version 1.0.0                
NuGet\Install-Package FVNever.Oddities.WinHelp -Version 1.0.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="FVNever.Oddities.WinHelp" Version="1.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add FVNever.Oddities.WinHelp --version 1.0.0                
#r "nuget: FVNever.Oddities.WinHelp, 1.0.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.
// Install FVNever.Oddities.WinHelp as a Cake Addin
#addin nuget:?package=FVNever.Oddities.WinHelp&version=1.0.0

// Install FVNever.Oddities.WinHelp as a Cake Tool
#tool nuget:?package=FVNever.Oddities.WinHelp&version=1.0.0                

Oddities Status Ventis

This repository groups several .NET libraries supporting old and obscure data formats.

Currently, the following data formats are supported:

If you encounter a case not handled by the library, don't hesitate to open an issue!

Read the corresponding sections below for each part of the library suite.

All the examples are collected in the Oddities.Samples project, feel free to take a look.

Documentation

Oddities.DIB NuGet

This library provides some helpful functions to work with the DIB (Device-Independent Bitmap) image format, often encountered as part of bigger bitmap formats or in resource sections of executable files, such as NE and PE (Portable Executable).

Here's an example of DIB data processing using the library:

byte[] DibSample(byte[] input)
{
    var dib = new Dib(input);
    var (w, h) = (dib.Width, dib.Height);
    var (r, g, b) = dib.GetPixel(0, 0);
    return dib.AsBmp(); // full bitmap data, may be saved to a BMP file
}

Known Limitations

  • Only 1, 4, and 8-bit palettes are supported.

Oddities.MRB NuGet

This library supports reading of MRB (Multi-Resolution Bitmap) and SHG (Segmented Hyper-Graphics) image formats. Most often, these files are encountered during reading of .HLP files using Oddities.WilHelp.

This library has been extracted into a separate package mainly because of different dependencies: it uses a third-party package to read WMF files (that may be part of SHG).

Here's a complex example of how to read an MRB file, extract SHG from it, then extract WMF from SHG, and finally extract a DIB from said SHG (that nesting did actually happen!):

Dib ExtractDibFromWmfInsideMrb(string mrbPath)
{
    using var stream = new FileStream(mrbPath, FileMode.Open);
    using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
    var file = MrbFile.Load(reader);
    if (file.ImageCount != 1) throw new Exception("Too many images in the file");
    var image = file.ReadImage(0);
    WmfDocument wmf = file.ReadWmfDocument(image);

    // Now, for example, let's extract the DIB record from the file, in case the file only stores a single bitmap:
    var record = wmf.Records.OfType<WmfStretchDIBRecord>().Single();
    using var dibStream = new MemoryStream();
    var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);
    record.DIB.Write(writer);
    return new Dib(dibStream.ToArray());
}

Known Limitations

  • Only WMF data is supported inside MRB, no BMP support.
  • Only RLE compression is supported.

Oddities.NE NuGet

Library supporting the NE (New Executable) file format (usually stored as .exe), the 16-bit executable file format used in Windows 1.0–3.x and some other operating systems.

The library is currently focused on reading the resource sections of NE files.

See Microsoft's documentation on the format.

Here's an example function that will read all the resources of type 32770u from a NE file and wrap every resource into a DIB.

Dib[] ReadImagesFromNeFile(string nePath)
{
    using var stream = new FileStream(nePath, FileMode.Open, FileAccess.Read);
    using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
    var neFile = NeFile.ReadFrom(reader);
    var resources = neFile.ReadResourceTable();
    var bitmapResourceType = resources.Single(x => x.TypeId == 32770u);
    return bitmapResourceType.Resources
        .Select(neFile.ReadResourceContent)
        .Select(x => new Dib(x))
        .ToArray();
}

Oddities.WinHelp NuGet

This library supports reading of Windows Help files, aka WinHelp, aka .hlp.

Since the Windows Help format is so complex, only certain features are supported.

Here's what's supported:

  • Reading the Windows Help file's HFS (Hierarchical File System) data and navigating between the files.
  • Reading each HFS entry's raw contents.
  • For |bm files, reading them using Oddities.MRB library.
  • For the |SYSTEM file, reading and partial validation of the header.
  • For the |FONT file, reading of the font descriptors.
  • For the |TOPIC file, iterating over the paragraphs and dumping the text records of text paragraphs.

The following example demonstrates most of the library's capabilities:

void DumpWinHelpFile(string hlpPath, string outDir)
{
    using var input = new FileStream(hlpPath, FileMode.Open, FileAccess.Read);
    using var reader = new BinaryReader(input, Encoding.UTF8, leaveOpen: true);
    var file = WinHelpFile.Load(reader);
    var dibs = new List<Dib>();
    foreach (var entry in file.GetFiles(Encoding.UTF8))
    {
        Console.WriteLine(entry.FileName);
        var fileName = entry.FileName.Replace("|", "_");
        var outputName = Path.Combine(outDir, fileName);
        var bytes = file.ReadFile(entry);
        File.WriteAllBytes(outputName, bytes);

        if (entry.FileName.StartsWith("|bm"))
        {
            // Here, you could extract DIB from WMF images, but I'm too lazy to update the signature of
            // ExtractDibFromWmfInsideMrb to make it work with bytes. Just imagine it works.

            // var dib = ExtractDibFromWmfInsideMrb(bytes);
            // dibs.Add(dib);
        }
        else if (entry.FileName == "|SYSTEM")
        {
            using var stream = new MemoryStream(bytes);
            using var headerReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
            var header = SystemHeader.Load(headerReader);
            Console.WriteLine(" - SystemHeader ok.");
        }
        else if (entry.FileName == "|FONT")
        {
            using var stream = new MemoryStream(bytes);
            using var fontReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
            var fontFile = FontFile.Load(fontReader);
            Console.WriteLine(" - Font ok.");

            foreach (var descriptor in fontFile.ReadDescriptors())
                Console.WriteLine($" - - Font descriptor: {descriptor.Attributes}");
        }
        else if (entry.FileName == "|TOPIC")
        {
            using var stream = new MemoryStream(bytes);
            using var topicReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
            var topic = TopicFile.Load(topicReader);
            Console.WriteLine(" - Topic ok.");

            var i = 0;
            foreach (var p in topic.ReadParagraphs())
            {
                Console.WriteLine($" - Paragraph {p} ({p.DataLen1}, {p.DataLen2}) ok.");

                var out1 = outputName + $"{i}.1";
                Console.WriteLine($" - - Paragraph data: {out1}");
                File.WriteAllBytes(out1, p.ReadData1());

                var out2 = outputName + $"{i}.2";
                Console.WriteLine($" - - Paragraph data: {out2}");
                File.WriteAllBytes(out2, p.ReadData2());

                if (p.RecordType == ParagraphRecordType.TextRecord)
                {
                    var items = p.ReadItems(Encoding.GetEncoding(1251));
                    Console.WriteLine($"- - Items: {items.Settings}");
                    foreach (var item in items.Items)
                    {
                        Console.WriteLine($"- - - {item}");
                    }
                }

                ++i;
            }
        }
    }

    // Also, you may dump dibs here.
}

Known Limitations

  • Not supporting any image types other than 0x22 in paragraphs.
  • Not supporting embedded bitmaps (only the external ones that are stored in their own HFS entries).

Project History

This project started as part of the game reimplementation O21. The original game is an old Windows game (from the 3.1 era), and so it was necessary to implement several old Windows data formats to load the game data properly.

Since then, the data format support was extracted into a separate library suite that lives in this repository.

Acknowledgments

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net7.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 549 5/31/2023

[Added]

The initial release (using the code extracted from the O21 project (https://github.com/ForNeVeR/O21)) including the following packages:

- Oddities.DIB for DIB (Device-Independent Bitmap) (https://learn.microsoft.com/en-us/windows/win32/gdi/device-independent-bitmaps) format processing.
- Oddities.MRB for MRB (Multi-Resolution Bitmap) (https://fileinfo.com/extension/mrb) and SHG (Segmented Hyper-Graphics) (https://fileinfo.com/extension/shg) formats processing.
- Oddities.NE for NE (New Executable) (https://en.wikipedia.org/wiki/New_Executable) format processing.
- Oddities.WinHelp for Windows Help (http://www.oocities.org/mwinterhoff/helpfile.htm) format processing.