libbgcode.NET
0.2.0
dotnet add package libbgcode.NET --version 0.2.0
NuGet\Install-Package libbgcode.NET -Version 0.2.0
<PackageReference Include="libbgcode.NET" Version="0.2.0" />
<PackageVersion Include="libbgcode.NET" Version="0.2.0" />
<PackageReference Include="libbgcode.NET" />
paket add libbgcode.NET --version 0.2.0
#r "nuget: libbgcode.NET, 0.2.0"
#:package libbgcode.NET@0.2.0
#addin nuget:?package=libbgcode.NET&version=0.2.0
#tool nuget:?package=libbgcode.NET&version=0.2.0
libbgcode.NET
A .NET reader and writer for Prusa's binary G-code (bgcode) container format: the file
header, lazy block enumeration, per-block compression (deflate, heatshrink), MeatPack G-code
encoding and decoding, and CRC-32 checksums.
Implemented from the format's published specification. The facts the specification does not state — that deflate payloads are zlib-wrapped, that the CRC-32 covers each block from its header through its data, the reconstruction rules MeatPack's lossy packing demands of a decoder, and the JSON metadata encoding PrusaSlicer 3 adds (it writes its slicer metadata twice, a legacy INI block and a JSON block) — are established from real PrusaSlicer output and pinned by interop tests against pybgcode, Prusa's own binding of the reference implementation, at the exact commit PrusaSlicer 3.0.0-alpha11 pins.
The library targets net10.0 and depends on
HeatshrinkDotNet for the heatshrink
blocks.
Untrusted input
The reader is written for files anybody may have uploaded. Every size on the wire is treated as
attacker-influenced: nothing is allocated from a declared size without a caller-configurable
bound, a payload must decompress to exactly its declared size, and the block walk refuses a file
whose offsets cannot be trusted. A malformed file yields null from whichever call discovered it,
never an exception — a contract held in place by a seeded mutation test and a coverage-guided
fuzzing harness (libbgcode.NET.Fuzz).
Usage
using libbgcode.NET;
using FileStream file = File.OpenRead("model.bgcode");
BgcodeReader? reader = BgcodeReader.Open(file);
if (reader is null)
{
// Not a readable binary G-code file. Note that the name decides nothing:
// PrusaSlicer routinely writes binary G-code to files called .gcode, so
// dispatch on BgcodeReader.Magic, never on the extension.
return;
}
while (reader.NextBlock() is { } block)
{
switch (block.Type)
{
case BgcodeBlockType.PrinterMetadata:
// "printer_model=COREONE\nnozzle_diameter=0.4\n..." - INI, one pair per line.
string? ini = reader.ReadText(block);
break;
case BgcodeBlockType.Thumbnail:
// block.Thumbnail carries format and pixel size; ReadData returns the image bytes.
byte[]? image = reader.ReadData(block);
break;
case BgcodeBlockType.GCode:
// Decompressed and MeatPack-decoded to plain G-code text.
string? gcode = reader.ReadText(block);
break;
}
}
Blocks are descriptors: NextBlock() reads headers only and seeks past payloads, so walking to
the one block you want costs a few small reads regardless of file size. The specification orders
blocks (file metadata, printer metadata, thumbnails, print metadata, slicer metadata, G-code), so
a reader after early metadata can stop at the first later type.
BgcodeReaderOptions bounds what a payload may cost (MaxDataBytes, default 64 MiB) and turns on
per-block CRC-32 verification (VerifyChecksum, off by default).
Writing
using FileStream file = File.Create("model.bgcode");
using BgcodeWriter writer = new(file); // CRC-32 trailers by default
writer.WritePrinterMetadata("printer_model=COREONE\nnozzle_diameter=0.4\n");
writer.WriteThumbnail(new BgcodeThumbnailParameters(BgcodeThumbnailFormat.Png, 16, 16), pngBytes);
writer.WritePrintMetadata("estimated printing time (normal mode)=34s\n");
writer.WriteSlicerMetadata("layer_height=0.2\n");
writer.WriteGCode(gcodeText); // MeatPack with comments, heatshrink 12/4 - the slicers' defaults
The writer enforces the specification's block order and the reference reader's mandatory chain (printer, print and slicer metadata before any G-code), so it cannot produce a file the reference implementation refuses; a call out of order throws before it writes. G-code is cut into blocks at 64 KiB of source on line boundaries, each with fresh MeatPack state, exactly as the reference binarizer cuts it. Every compression and encoding the format allows is available per block; the defaults are what PrusaSlicer writes.
MeatPack lives in its own package, MeatPack.NET, developed in this
repository — MeatPackDecoder.Unpack and MeatPackEncoder.Pack work on payloads from anywhere,
serial hosts included; libbgcode.NET depends on it for the G-code blocks.
Converting whole files
BgcodeConverter.ToAscii and BgcodeConverter.ToBinary convert between the container and the
ASCII G-code PrusaSlicer writes: the producer line, the head of ; key = value comments a
printer reads from the first kilobytes, thumbnail sections, print statistics, and the config
section (plus PrusaSlicer 3's JSON one). Which comment keys the printer and print metadata blocks
gather was learned by observing the reference implementation's behaviour, not by reading its
code; both directions are cross-checked against pybgcode.
Two small apps wrap them: bin2gcode <in.bgcode> [out.gcode] and
gcode2bin <in.gcode> [out.bgcode] [--drop-comments] [--plain] [--no-compression] [--no-checksum].
What this is not
It does not parse the G-code itself — it hands you the text.
Licenses
- libbgcode.NET and MeatPack.NET are licensed under the Mozilla Public License 2.0.
- The MeatPack scheme is Scott Mudge's (BSD-3-Clause, LICENSE.meatpack); the decoder here is an independent implementation of the documented scheme, including the reconstruction behaviour the bgcode variant expects.
- The binary G-code format and its specification are Prusa's;
this library is an independent implementation of that published format and shares no code with
libbgcode.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- HeatshrinkDotNet (>= 1.0.0)
- MeatPack.NET (>= 0.2.0)
- System.IO.Hashing (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.