GBX.NET
1.2.2
Prefix Reserved
See the version list below for details.
dotnet add package GBX.NET --version 1.2.2
NuGet\Install-Package GBX.NET -Version 1.2.2
<PackageReference Include="GBX.NET" Version="1.2.2" />
paket add GBX.NET --version 1.2.2
#r "nuget: GBX.NET, 1.2.2"
// Install GBX.NET as a Cake Addin #addin nuget:?package=GBX.NET&version=1.2.2 // Install GBX.NET as a Cake Tool #tool nuget:?package=GBX.NET&version=1.2.2
GBX.NET is a GameBox (.Gbx) file parser library written in C# for .NET software framework. This file type can be seen in many of the Nadeo games like TrackMania, ShootMania or Virtual Skipper.
For any questions, open an issue, join the GameBox Sandbox Discord server or message me via DM: BigBang1112#9489.
- GBX.NET can recognize entire Gbx files, however cannot read all of the existing files. Gbx file is basically a serialized class from the GameBox engine, and all of these classes must be known to read. Exploring new chunks can be done by reverse engineering the games.
- GBX.NET can write most of the Gbx file types (those that can be read by the parser).
- All versions of Gbx are supported: ranging from TM1.0 to TM®, except the Gbx versions below 3 (which haven't been seen so far, even in the oldest game).
- GBX.NET 0.10.0+ is separated into MIT and GPL3.0, see License.
- Reading text-formatted Gbx is not currently supported.
- Reading compressed reference tables is not currently supported.
- Reading Pak files is partially supported with the GBX.NET.PAK sublibrary, but it applies only to Paks from TMU/F.
Here are some of the useful classes/types to start with:
Latest extension | Class | Can read | Can write | Other extension/s |
---|---|---|---|---|
Map.Gbx | CGameCtnChallenge | Yes | Yes | Challenge.Gbx |
Replay.Gbx | CGameCtnReplayRecord | Yes | No<sup>1</sup> | |
Ghost.Gbx | CGameCtnGhost | Yes | Yes | |
Clip.Gbx | CGameCtnMediaClip | Yes | Yes | |
Item.Gbx | CGameItemModel | Yes | No | |
Block.Gbx | CGameItemModel | Yes | No | |
Mat.Gbx | CPlugMaterialUserInst | Yes | Yes | |
Mesh.Gbx | CPlugSolid2Model | Yes<sup>2</sup> | Yes<sup>2</sup> | |
Shape.Gbx | CPlugSurface | Yes | Yes | |
Macroblock.Gbx | CGameCtnMacroBlockInfo | Yes | Yes | |
LightMapCache.Gbx | CHmsLightMapCache | Yes | Yes | |
SystemConfig.Gbx | CSystemConfig | Yes | Yes | |
FidCache.Gbx | CMwRefBuffer | Yes | Yes | |
Scores.Gbx | CGamePlayerScore | Yes | No |
- <sup>1</sup>Safety reasons. Consider extracting
CGameCtnGhost
fromCGameCtnReplayRecord
, transfer it over toCGameCtnMediaBlockGhost
, add it toCGameCtnMediaClip
, and save it as.Clip.Gbx
, which you can then import in MediaTracker. - <sup>2</sup>May not have perfect support for the
CPlugVisual3D
objects inside and TM2020 could be generally unstable as well.
Full list of supported file types is available in the SUPPORTED GBX FILE TYPES.
Compatibility and build
- GBX.NET is compatible down to .NET Standard 2.0 and .NET Framework 4.6.2.
- Current C# language version is 11.
To build the solution:
- Installing Visual Studio 2022 with default .NET tools, .NET WebAssembly Build Tools, .NET Core 3.1 Runtime, and .NET Framework 4.6.2 Targeting Pack is the easiest option.
- JetBrains Rider also works as it should. Visual Studio Code may work with a bit more setup.
- Make sure you have all the needed targetting packs installed (currently .NET 7.0, .NET 6.0, .NET Standard 2.1, .NET Standard 2.0, and .NET Framework 4.6.2).
(reminder: you can just use the NuGet packages in any IDE or text editor that supports them)
Techniques
The Gbx format compatibility is extensible through the GBX.NET.Engines
namespace.
Through the Gbx format, internal game objects (called nodes) are being serialized and deserialized through chunks. In GBX.NET, chunks are presented as node's nested classes and they are named using the pattern Chunk[chunkId]
(where chunkId
is formatted on 8 digits).
Nodes inherit CMwNod
or other class that inherits CMwNod
and each node class has a NodeAttribute
with its latest ID and a protected constructor. Chunks inherit the Chunk<T>
/SkippableChunk<T>
/HeaderChunk<T>
class based on the behaviour and each chunk class has a ChunkAttribute
with its latest ID.
To keep the code clean while also performant, the library includes a source generated layer on top for effective reading and writing with minimal cold start.
- Better performance than reflection (mainly due to minimal cold start)
Parse...()
methods are thread safe.- Con: Library is larger due to generated code (not a short one).
The library also speeds up parse time by ignoring unused skippable chunks with discover feature:
- Discover basically means "read a skippable chunk".
- Skippable chunks are read in-depth only if methods or properties request for them.
- Calling certain properties will discover all needed chunks synchronously before returning the value.
- You can pre-discover certain chunks on different threads to increase your code's performance.
Benchmarks
Coming soon!
Dependencies
GBX.NET
- 0.0.1 - 0.4.1: SharpZipLib.NETStandard
- 0.1.0 - 0.4.1: Microsoft.CSharp
- 0.0.1 - 0.9.0: System.Drawing.Common
- 0.13.0+: TmEssentials
- 0.15.0+: Microsoft.Extensions.Logging.Abstractions
GBX.NET.Imaging
- System.Drawing.Common
GBX.NET.Json
- Newtonsoft.Json
Usage
To parse a Gbx with a known type:
using GBX.NET;
using GBX.NET.Engines.Game;
var map = GameBox.ParseNode<CGameCtnChallenge>("MyMap.Map.Gbx");
To parse a Gbx with an unknown type:
using GBX.NET;
using GBX.NET.Engines.Game;
var node = GameBox.ParseNode("MyMap.Map.Gbx");
if (node is CGameCtnChallenge map)
{
// Node data is available in map
}
else if (node is CGameCtnReplayRecord replay)
{
// Node data is available in replay
}
// C# 7+
switch (node)
{
case CGameCtnChallenge map:
// Node data is available in map
break;
case CGameCtnReplayRecord replay:
// Node data is available in replay
break;
}
To get the Gbx metadata, use the node.GetGbx()
property (only possible on the main node).
To save changes of the parsed Gbx file:
using GBX.NET;
using GBX.NET.Engines.Game;
var node = GameBox.ParseNode("MyMap.Map.Gbx");
if (node is CGameCtnChallenge map)
{
// Do changes with CGameCtnChallenge
map.Save("MyMap.Map.Gbx");
}
else if (node is CGameCtnGhost ghost)
{
// Do changes with CGameCtnGhost
ghost.Save("MyGhost.Ghost.Gbx");
}
To save any supported Node
to a Gbx file:
using GBX.NET;
using GBX.NET.Engines.Game;
var replay = GameBox.ParseNode<CGameCtnReplayRecord>("MyReplay.Replay.Gbx");
// Ghosts property can be null if you would use the ParseNodeHeader method.
if (replay.Ghosts is not null)
{
foreach (CGameCtnGhost ghost in replay.Ghosts)
{
ghost.Save($"{ghost.GhostNickname}.Ghost.Gbx");
}
}
Compressed Gbx files require to include the GBX.NET.LZO library (or any similar implementation, but there are no other compatible at the moment). In most of the cases, the LZO compression is automatically detected after just referencing the library in the project. You don't require to have a using GBX.NET.LZO;
anywhere.
On specific platforms like Blazor WebAssembly though, the dependency system works differently and (currently) GBX.NET struggles to automatically detect the LZO library.
In these cases (if just following the MissingLzoException
message didn't solve the problem), add this line above the first attempt of parsing the Gbx. It should be called just once.
GBX.NET.Lzo.SetLzo(typeof(GBX.NET.LZO.MiniLZO));
Conventions
This convention is no longer relevant in GBX.NET 0.11.0+ when using the ParseNode method.
Make the code cleaner by aliasing the Node
from the parsed GameBox
:
var gbx = GameBox.Parse<CGameCtnChallenge>("MyMap.Map.Gbx");
var map = gbx.Node; // Like this
var bronzeTime = gbx.Node.BronzeTime; // WRONG !!!
var silverTime = map.SilverTime; // Correct
Trimming
Since 1.2.1, GBX.NET officially supports full trimming, but you have to be careful using it. You're not forced to use trimming in case of complications.
To make sure it works:
- Explicitly setting LZO is required:
GBX.NET.Lzo.SetLzo(typeof(GBX.NET.LZO.MiniLZO));
- After that, currently, you can just suppress warnings on the
GameBox.Parse...
and possibly other methods. Sadly, it's not possible to suppress warnings only for a specific library reference, so you have to do it this way for now. This will improve in GBX.NET 2. Please, do not suppress the warning globally.
#pragma warning disable IL2026 // add these lines
var gbx = GameBox.ParseNode<CGameCtnReplayRecord>(args[0], logger: logger);
#pragma warning restore IL2026 // add these lines
- In case you wanna use reflection on GBX.NET, it is strongly recommended to simply turn off trimming of this library. In case of Blazor WebAssembly specifically, it's worth noting that the release build trims automatically, so in case you're using reflection, modify your library reference:
<PackageReference Include="GBX.NET">
<IsTrimmable>false</IsTrimmable>
</PackageReference>
License
- The sub-library GBX.NET.LZO is licensed with GNU General Public License v3.0. If you're going to use this library, please license your work under GPL-3.0-or-later.
- GbxExplorer, and everything in the Tools and Samples folder is also licensed with GNU General Public License v3.0.
- The libraries GBX.NET, GBX.NET.Imaging, GBX.NET.Json, GBX.NET.Localization and the project DocGenerator are licensed with MIT and you can use them much more permissively.
- Information gathered from the project (chunk structure, parse examples, data structure, wiki information, markdown) is usable with The Unlicense.
- Font of the logo is called Raleway licensed under SIL Open Font License. You can use the logo as a part of your media.
Conclusion
Your work doesn't have to fall under the GNU GPL license if you're interested in either reading the header data only, or reading certain uncompressed Gbx files (usually the internal ones inside PAK files). If you're looking to read the content of a compressed Gbx body (applies to maps, replays and other user generated content), you have to license your work with GNU GPL v3.0 or later.
Special thanks
Without these people, this project wouldn't be what it is today:
- Stefan Baumann (Solux)
- Melissa (Miss)
- florenzius
- Kim
- tilman
- James Romeril
- frolad (Juice)
- Mika Kuijpers (TheMrMiku)
- donadigo
And many thanks to every bug reporter!
Alternative Gbx parsers
- gbx.js by ThaumicTom (Gbx header parser for clientside JavaScript)
- ManiaPlanetSharp by Solux (C# toolkit for accessing ManiaPlanet data, including Gbx parser used by ManiaExchange)
- pygbx by Donadigo (Gbx parser for Python)
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. 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. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.2
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- System.IO.Compression (>= 4.3.0)
- System.ValueTuple (>= 4.5.0)
- TmEssentials (>= 2.3.2)
-
.NETStandard 2.0
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- System.Reflection.Emit.Lightweight (>= 4.7.0)
- TmEssentials (>= 2.3.2)
-
.NETStandard 2.1
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- TmEssentials (>= 2.3.2)
-
net6.0
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- TmEssentials (>= 2.3.2)
-
net7.0
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- TmEssentials (>= 2.3.2)
NuGet packages (13)
Showing the top 5 NuGet packages that depend on GBX.NET:
Package | Downloads |
---|---|
GBX.NET.LZO
An LZO compression plugin for GBX.NET to allow de/serialization of compressed Gbx bodies. This official implementation uses lzo 2.10 from NativeSharpLzo and minilzo 2.06 port by zzattack. |
|
GBX.NET.Imaging
Provides extensions for image handling in GBX.NET (GDI+, Windows only). |
|
GBX.NET.Json
A wrapper for better JSON serialization of GBX.NET nodes, useful for comparing data. |
|
GBX.NET.PAK
A PAK file parser using the GBX.NET features. |
|
GBX.NET.Imaging.SkiaSharp
Provides extensions for image handling in GBX.NET (Google's Skia with SkiaSharp). |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
2.0.9 | 252 | 10/1/2024 | |
2.0.8 | 151 | 9/29/2024 | |
2.0.7 | 536 | 8/16/2024 | |
2.0.6 | 338 | 7/23/2024 | |
2.0.6-beta3 | 130 | 7/20/2024 | |
2.0.6-beta2 | 174 | 7/16/2024 | |
2.0.6-beta1 | 159 | 7/16/2024 | |
2.0.5 | 285 | 7/1/2024 | |
2.0.4 | 290 | 6/12/2024 | |
2.0.3 | 175 | 5/31/2024 | |
2.0.3-beta1 | 161 | 5/30/2024 | |
2.0.2 | 325 | 5/4/2024 | |
2.0.1 | 205 | 4/29/2024 | |
2.0.0 | 353 | 4/26/2024 | |
2.0.0-rc1 | 281 | 4/23/2024 | |
2.0.0-beta2 | 114 | 4/21/2024 | |
2.0.0-beta1 | 272 | 4/20/2024 | |
2.0.0-alpha3 | 99 | 4/18/2024 | |
2.0.0-alpha2 | 185 | 4/14/2024 | |
2.0.0-alpha1 | 197 | 4/12/2024 | |
1.2.9 | 60 | 11/7/2024 | |
1.2.8 | 34 | 11/7/2024 | |
1.2.7 | 216 | 8/12/2024 | |
1.2.6 | 910 | 2/19/2024 | |
1.2.5 | 457 | 12/31/2023 | |
1.2.4 | 537 | 11/26/2023 | |
1.2.3 | 349 | 11/24/2023 | |
1.2.2 | 592 | 7/8/2023 | |
1.2.1 | 677 | 6/7/2023 | |
1.2.0 | 864 | 5/4/2023 | |
1.2.0-beta2 | 130 | 4/30/2023 | |
1.2.0-beta1 | 118 | 4/25/2023 | |
1.1.3 | 542 | 2/1/2023 | |
1.1.2 | 328 | 1/21/2023 | |
1.1.1 | 527 | 12/30/2022 | |
1.1.0 | 1,304 | 12/11/2022 | |
1.1.0-alpha2 | 211 | 11/11/2022 | |
1.1.0-alpha1 | 133 | 10/25/2022 | |
1.0.1 | 786 | 9/10/2022 | |
1.0.0 | 445 | 9/9/2022 | |
0.16.6 | 480 | 8/24/2022 | |
0.16.5 | 493 | 8/21/2022 | |
0.16.4 | 487 | 8/19/2022 | |
0.16.3 | 497 | 8/18/2022 | |
0.16.2 | 773 | 8/11/2022 | |
0.16.1 | 509 | 7/21/2022 | |
0.16.0 | 1,316 | 7/16/2022 | |
0.15.1 | 564 | 4/4/2022 | |
0.15.0 | 1,010 | 2/23/2022 | |
0.15.0-rc2 | 156 | 2/19/2022 | |
0.15.0-rc | 197 | 2/1/2022 | |
0.14.3 | 376 | 12/19/2021 | |
0.11.0 | 601 | 6/25/2021 | |
0.8.0 | 454 | 1/29/2021 |