Skip to content
Paul Hirch edited this page May 13, 2024 · 7 revisions

BinaryView wiki

Grille.IO.BinaryView is a Libary to easily write and read binary data from streams and files.

Available as a NuGet Package.

In advance: this “documentation” is very sparse at the moment. I hope that most function and classes are self-explanatory enough. If you have any question or problems feel free to open an issue.

This library basically provides more advanced versions of the System.IO.BinaryWriter and System.IO.BinaryReader classes. And has grown over the years to accommodate my custom file format (and occasionally reverse engineering) needs.

Features

  • Asymmetrical and Symmetrical write/read functions.
  • Simple to use string functions with encoding options (including C-Strings).
  • Convenient generic functions to serialize whole lists and (unmanaged) structs.
  • Easy compresion/decompresion of sections or whole stream's with GZip, Deflate, Brotli or ZLib.
  • Smart length prefixes depending on size. (42 takes 1 byte, 3000 takes 2 etc.)
  • Support for byte endianness and bit order.

Example Write/Read (asymmetrical)

using Grille.IO;
using Grille.IO.Compression;

Write

// Open a file to write
using (var bw = new BinaryViewWriter("file.bin"))
{
    // Type used for LengthPrefix by Strings and Arrays
    bw.DefaultLengthPrefix = LengthPrefix.UInt32;

    // Write data in the file
    bw.WriteString(Name);
    bw.WriteInt32(Age);
    bw.Write<Vector2>(Pos);
    
    // Compress section
    bw.BeginCompressedSection(CompressionType.Deflate);
    
    bw.WriteArray<byte>(Data0);

    // Override default prefix to use byte instead
    bw.WriteArray<Vector2>(Data1, LengthPrefix.Byte);

    // Write length manuel
    bw.WriteInt32(Data2.Length);
    bw.WriteArray<float>(Data2, LengthPrefix.None);

    bw.EndCompressedSection();
}

Read

// Open a file to read
using (var br = new BinaryViewReader("file.bin"))
{
    br.DefaultLengthPrefix = LengthPrefix.UInt32;

    // Read the data in same order of how they were written
    Name = br.ReadString();
    Age = br.ReadInt32();
    Pos = br.Read<Vector2>()
    
    // Decompress section
    br.BeginCompressedSection(CompressionType.Deflate);
    
    Data0 = br.ReadArray<byte>();

    // Read prefix-type must match written one.
    Data1 = br.ReadArray<Vector2>(LengthPrefix.Byte);

    // Read length manuel
    int length = br.ReadInt32();
    Data2 = br.WriteArray<float>(length);

    br.EndCompressedSection();
}

Example View (symmetrical)

Uses same code for write and read operations.

using GGL.IO;
using GGL.IO.Compression;

View

// Open a file to read
using (var view = new BinaryView("file.bin", ViewMode.Read /*ViewMode.Write*/))
{
    view.DefaultLengthPrefix = LengthPrefix.UInt32;

    view.String(ref Name);
    view.Int32(ref Age);
    view.Struct<Vector2>(ref Pos);
    
    view.BeginCompressedSection(CompressionType.Deflate);
    
    view.Array<byte>(ref Data0);
    view.Array<Vector2>(ref Data1, LengthPrefix.Byte);

    // Switch when different operations are needed for read and write.
    if (view.Mode == ViewMode.Read){
        var br = view.Reader;
        int length = br.ReadInt32();
        Data2 = br.WriteArray<float>(length);
    }
    else {
        var bw = view.Writer;
        bw.WriteInt32(Data2.Length);
        bw.WriteArray<float>(Data2, LengthPrefix.None);
    }

    // Above can also be solved this way:
    /*
    int length = Data2.Length;
    view.Int32(ref length);
    view.Array<float>(ref Data2, length);
    */

    view.EndCompressedSection();
}