### Combined Ghost Extraction Example Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-ghosts-from-replays A consolidated example demonstrating how to parse a replay, extract all ghosts using the `GetGhosts()` method, and define the `ExtractGhost` helper function within the same scope. ```cs var replay = Gbx.ParseNode("Path/To/My/Replay.Replay.Gbx"); foreach (CGameCtnGhost ghost in replay.GetGhosts()) { ExtractGhost(ghost); } void ExtractGhost(CGameCtnGhost ghost) { ghost.Save($"{ghost.GhostUid}.Ghost.Gbx"); } ``` -------------------------------- ### Include GBX.NET LZO Library Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Include the GBX.NET.LZO library once at the start of your program. Ensure you are using GBX.NET version 2 or higher. ```cs #:package GBX.NET@2.* #:package GBX.NET.LZO@2.* using GBX.NET; using GBX.NET.LZO; Gbx.LZO = new Lzo(); ``` -------------------------------- ### Explicit vs. Implicit Parse Example Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Illustrates the difference between explicit and implicit parsing of Gbx files. Implicit parsing is generally recommended for simplicity. ```csharp // Implicit parse (recommended) var gbxImplicit = await Gbx.Stream.ParseAsync(File.OpenRead("path/to/file.Gbx")); // Explicit parse var gbxExplicit = await Gbx.Stream.ParseAsync(File.OpenRead("path/to/file.Gbx")); ``` -------------------------------- ### Only Header Parse Example Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Demonstrates how to parse only the header of a Gbx file, which is faster if the full content is not needed. This is useful for quick file inspection. ```csharp var gbxHeader = await Gbx.Stream.ParseHeaderAsync(File.OpenRead("path/to/file.Gbx")); Console.WriteLine($"File version: {gbxHeader.Version}"); ``` -------------------------------- ### Initialize CRC32 Hashing Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET.Hashing/README.md Prepare the CRC32 hashing by initializing the Gbx.CRC32 instance. This should be performed only once at the beginning of your program execution. The LZO package is also required for this example. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.LZO; using GBX.NET.Hashing; Gbx.LZO = new Lzo(); Gbx.CRC32 = new CRC32(); // You need to add this var map = Gbx.ParseNode("Path/To/My.Map.Gbx"); map.RemovePassword(); map.Save("MapWithNoPassword.Map.Gbx"); ``` -------------------------------- ### Iterating and Pattern Matching Ghost Inputs in C# Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-input-data-from-ghosts Demonstrates how to parse a replay file, access ghost inputs, and use pattern matching (switch statements and if conditions) to identify and process specific input types like Accelerate and Steer. It also shows how to get raw and normalized steer values. ```csharp var replay = Gbx.ParseNode("Path/To/My/Replay.Replay.Gbx"); // Taking just the first ghost makes thing cleanerfor this example var ghost = replay.GetGhosts().FirstOrDefault(); if (ghost is null) { throw new Exception("No ghost found."); } if (ghost.Inputs is not null) { // Loop through all inputs foreach (IInput input in ghost.Inputs) { // Do something with specific inputs // Pattern matching via switch switch (input) { case Accelerate accel: var pressed = accel.Pressed; break; case IInputSteer steer: var pureSteer = steer.Value; // represents -65536 to 65536 steer range var normalizedSteer = steer.GetValue(); // represents -1 to 1 steer range break; } // Pattern matching via if statement if (input is Respawn respawn) { // special Respawn logic } // Advanced pattern matching if (input is IInputSteer { Value < 10000 } steerUnder10000) { // something } // Print out the input string representation Console.WriteLine(input.ToString()); } } ``` -------------------------------- ### Get All Ghosts Using Helper Method Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-ghosts-from-replays Iterates through all ghosts in a replay, including those within MediaTracker clips, using the `GetGhosts()` helper method. This is the recommended approach for modern GBX.NET versions. ```cs foreach (CGameCtnGhost ghost in replay.GetGhosts()) { ExtractGhost(ghost); } ``` -------------------------------- ### Load and Display Block Count per Block Name in Map Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Loads a map file and displays the count of each block type. Requires GBX.NET to be installed. ```csharp await foreach (var block in Gbx.Stream.ParseAsync(File.OpenRead("path/to/your/map.Map.Gbx"))) { Console.WriteLine($"Block: {block.Node.GetType().Name}"); } ``` -------------------------------- ### Implicit Parse with Gbx.ParseNode() and Pattern Matching Source: https://github.com/bigbang1112/gbx-net/wiki/Essentials Use Gbx.ParseNode() to directly get the CMwNod object for unknown Gbx types and pattern matching to determine its specific type. Ensure LZO is initialized if needed. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.Engines.MwFoundations; Gbx.LZO = new GBX.NET.LZO.MiniLZO(); // Returns CMwNod - possible to cast or pattern match with GBX.NET.Engines types CMwNod node = Gbx.ParseNode("RandomMap.Map.Gbx"); if (node is CGameCtnReplayRecord replay) { } else if (node is CGameCtnChallenge map) { // We should reach here } switch (node) { case CGameCtnReplayRecord replay: break; case CGameCtnChallenge map: // We should reach here break; } string name = node switch { CGameCtnReplayRecord replay => "Replay", CGameCtnChallenge map => "Map", // We should reach here _ => "Unknown" }; ``` -------------------------------- ### Initialize MiniLZO Compression for GBX.NET Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET.LZO/README.md Use the MiniLZO class if you prefer its implementation. This uses a different compression level than the default LZO. ```csharp using GBX.NET.LZO; Gbx.LZO = new MiniLZO(); ``` -------------------------------- ### Build Solution with .NET CLI Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Use this command to build the entire solution from the command line. Ensure you are in the solution directory. ```bash dotnet build ``` -------------------------------- ### Get Map UID from Header Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Retrieve the unique identifier (UID) of the map using the MapUid property from the parsed header. ```cs string mapUid = map.MapUid; Console.WriteLine(mapUid); ``` -------------------------------- ### Create a New GBX.NET Project (Lightweight) Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Creates a new project using GBX.NET with minimal dependencies. This is suitable for projects that only need core GBX.NET functionality. ```bash dotnet new console -o MyGbxProject cd MyGbxProject dotnet add package GBX.NET ``` -------------------------------- ### Get Map Environment - C# Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Retrieve the environment name of a map. The GetEnvironment() method provides a convenient way to access this information. ```csharp string environment = map.GetEnvironment(); Console.WriteLine(environment); ``` -------------------------------- ### Initialize LZO Compression for GBX.NET Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET.LZO/README.md Add this line once at the beginning of your program to prepare LZO compression. Ensure you have the necessary using directive. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.LZO; // Add this Gbx.LZO = new Lzo(); // Add this ONLY ONCE and before you start using Parse methods var map = Gbx.ParseNode("Path/To/My.Map.Gbx"); Console.WriteLine($"Block count: {map.GetBlocks().Count()}"); ``` -------------------------------- ### Get Map Name from Header Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Access the MapName property directly from the parsed header node to retrieve the map's name. ```cs string mapName = map.MapName; Console.WriteLine(mapName); ``` -------------------------------- ### Initialize ZLib Compression for GBX.NET Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET.ZLib/README.md Add this code once at the beginning of your program execution to prepare ZLib compression. Ensure GBX.NET.LZO is also included if LZO compression is used. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.LZO; using GBX.NET.ZLib; // Add this Gbx.LZO = new Lzo(); Gbx.ZLib = new ZLib(); // Add this ONLY ONCE and before you start using Parse methods var ghost = Gbx.ParseNode("Path/To/My.Ghost.Gbx"); // SampleData will (likely) use ZLib decompression foreach (var sample in ghost.SampleData.Samples) { Console.WriteLine(sample.Position); } ``` -------------------------------- ### Parse Map Header Node Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Use ParseHeaderNode for CGameCtnChallenge types to efficiently get basic map information from the header chunk. ```cs var map = GameBox.ParseHeaderNode("Path/To/My/Track.Map.Gbx"); ``` -------------------------------- ### Create a New GBX.NET Project (Visual Studio) Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Instructions for creating a new GBX.NET project within Visual Studio. This typically involves creating a new .NET project and adding the GBX.NET NuGet package. ```csharp // In Visual Studio: // 1. Create a new Console Application project. // 2. Right-click on the project in Solution Explorer and select "Manage NuGet Packages...". // 3. Browse for and install the "GBX.NET" package. ``` -------------------------------- ### Declare Script Metadata in ManiaScript Source: https://github.com/bigbang1112/gbx-net/wiki/Working-with-script-metadata This is an example of how script metadata is declared directly within ManiaScript. Note that inline value assignment is not supported. ```php declare metadata Integer TimeLimit for Map; TimeLimit = 60000; ``` -------------------------------- ### Enable Zlib Compression Handling Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md To access zlib-compressed data within Gbx files, such as ghost samples or record data, you must explicitly provide a zlib implementation. This involves referencing the GBX.NET.ZLib package and setting Gbx.ZLib to a new ZLib instance. ```cs #:package GBX.NET@2.* #:package GBX.NET.ZLib@2.* // IMPORTANT to include this package! using GBX.NET; using GBX.NET.ZLib; Gbx.ZLib = new ZLib(); ``` -------------------------------- ### Get Challenge Time Limit - C# Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Extract the time limit for a challenge. This value is stored within the ChallengeParameters property of the parsed map object. ```csharp TimeInt32 timeLimit = map.ChallengeParameters.TimeLimit; Console.WriteLine(timeLimit); ``` -------------------------------- ### Get Environment Block Size - C# Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Obtain the block size of the map's environment. This requires accessing the Collection property and then calling GetBlockSize(). ```csharp Int3 blockSize = map.Collection.GetBlockSize(); Console.WriteLine(blockSize); ``` -------------------------------- ### Iterate Through Struct Members Source: https://github.com/bigbang1112/gbx-net/wiki/Working-with-script-metadata Use TryGetStruct to get a struct trait and then iterate through its members, printing each member's name and its value obtained via GetValue(). ```cs if (metadata.TryGetStruct("Complex", out var structTrait)) { foreach (var (name, trait) in structTrait.Value) { Console.WriteLine($"{name}: {trait.GetValue()}"); } } ``` -------------------------------- ### Enable Trimming in .csproj Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Configure your .csproj file to enable trimming (tree shaking) for your application during publish. This is fully supported by GBX.NET and can significantly reduce the compiled size of your application. ```xml true ``` -------------------------------- ### Use Game Version Interfaces for CGameCtnChallenge Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Demonstrates how to use game version interfaces to scope Gbx classes for specific game versions, like TMF. This helps hide unrelated properties and avoid null checks. Ensure you have the necessary using directives. ```cs using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.Interfaces.Game; var map = Gbx.ParseNode(); foreach (var block in map.GetBlocks()) { Console.WriteLine(block.Name); // Console.WriteLine(block.Color); -- block color is not available in TMF } ``` -------------------------------- ### Blazor Start with Brotli Decode Source: https://github.com/bigbang1112/gbx-net/blob/master/Templates/MyGbxBlazorWasmApp/wwwroot/index.html Customizes Blazor's resource loading to decompress Brotli-compressed assets on the client-side when not on localhost. This optimizes asset delivery for production environments. ```javascript import { BrotliDecode } from './decode.min.js'; Blazor.start({ loadBootResource: function (type, name, defaultUri, integrity) { if (type !== 'dotnetjs' && location.hostname !== 'localhost' && type !== 'configuration') { return (async function () { const response = await fetch(defaultUri + '.br', { cache: 'no-cache' }); if (!response.ok) { throw new Error(response.statusText); } const originalResponseBuffer = await response.arrayBuffer(); const originalResponseArray = new Int8Array(originalResponseBuffer); const decompressedResponseArray = BrotliDecode(originalResponseArray); const contentType = type === 'dotnetwasm' ? 'application/wasm' : 'application/octet-stream'; return new Response(decompressedResponseArray, { headers: { 'content-type': contentType } }); })(); } } }); ``` -------------------------------- ### Enable NativeAOT Compilation in .csproj Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Configure your .csproj file to enable NativeAOT compilation for your application. This provides further optimizations, including reduced binary size and faster startup times, and automatically enables trimming. ```xml true ``` -------------------------------- ### Enable WasmBuildNative for Blazor Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md In Blazor WebAssembly projects, enable WasmBuildNative in your .csproj file for the LZO implementation to work correctly. This requires installing WebAssembly Build Tools. ```xml true ``` -------------------------------- ### Place Custom Block with GBX.NET Source: https://github.com/bigbang1112/gbx-net/wiki/Embedding-custom-items-to-a-map Use `PlaceBlock` with the `string blockModel` overload to embed custom blocks. The block reference path must exclude the 'Blocks' folder and append '_CustomBlock'. Backslashes are required. ```cs map.PlaceBlock("MyFolder\MyBlock.Block.Gbx_CustomBlock", coord: (5, 6, 7), Direction.West); ``` -------------------------------- ### Get Mod Pack Download URL - C# Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Retrieve the download URL for a map's mod pack. This information is stored in the ModPackDesc property, specifically in the LocatorUrl field. ```csharp PackDesc mod = map.ModPackDesc; Console.WriteLine(mod.LocatorUrl); ``` -------------------------------- ### Get Medal Times from Header Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Extract bronze, silver, gold, and author medal times from the map header using dedicated properties. Note that these are nullable TimeInt32 types. ```cs TimeInt32? bronzeMedal = map.BronzeTime; TimeInt32? silverMedal = map.SilverTime; TimeInt32? goldMedal = map.GoldTime; TimeInt32? authorMedal = map.AuthorTime; Console.WriteLine($"Bronze medal: {bronzeMedal.ToTmString()}"); Console.WriteLine($"Silver medal: {silverMeda.ToTmString()}"); Console.WriteLine($"Gold medal: {goldMedal.ToTmString()}"); Console.WriteLine($"Author medal: {authorMedal.ToTmString()}"); ``` -------------------------------- ### Save Gbx File with CMwNod Object Source: https://github.com/bigbang1112/gbx-net/wiki/Essentials This snippet demonstrates saving a CMwNod object directly to a Gbx file. The node is automatically wrapped into a Gbx object for saving. Ensure LZO compression is initialized if needed. ```cs using GBX.NET; using GBX.NET.Engines.Game; Gbx.LZO = new GBX.NET.LZO.MiniLZO(); var node = Gbx.ParseNode("RandomMap.Map.Gbx"); // Do some stuff... node.Save("ModifiedMap.Map.Gbx"); ``` -------------------------------- ### Export Thumbnail from Map (GBX.NET.Imaging) Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET.Imaging/README.md Exports a correctly oriented thumbnail from a map file. Ensure the GBX.NET.Imaging namespace is included. The format parameter can be used to adjust quality. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.Imaging; // You need to add this var map = Gbx.ParseHeaderNode("Path/To/My.Map.Gbx"); map.ExportThumbnail("MyThumbnail.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); ``` -------------------------------- ### Get Medal Times from Challenge Parameters Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data When a map is fully parsed, medal times are accessed via the ChallengeParameters property. This approach is also valid for header-parsed nodes if the ChallengeParameters are available. ```cs TimeInt32? bronzeMedal = map.ChallengeParameters.BronzeTime; TimeInt32? silverMedal = map.ChallengeParameters.SilverTime; TimeInt32? goldMedal = map.ChallengeParameters.GoldTime; TimeInt32? authorMedal = map.ChallengeParameters.AuthorTime; ``` -------------------------------- ### Place Custom Item with GBX.NET Source: https://github.com/bigbang1112/gbx-net/wiki/Embedding-custom-items-to-a-map Use `PlaceAnchoredObject` to embed custom items into a map. Ensure the item reference path correctly excludes the 'Items' folder and includes the '.Item.Gbx' extension. Backslashes are required. ```cs map.PlaceAnchoredObject(new("MyFolder\MyItem.Item.Gbx", 26, "my_login"), absolutePosition: (32, 24, 16), pitchYawRoll: (0, 1, 2)); ``` -------------------------------- ### Parse Replay and Extract Ghosts Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-ghosts-from-replays Parses a replay file and iterates through all ghosts, including those embedded in clip media. Requires the Gbx library. Note the C# 9+ null check syntax. ```csharp var replay = Gbx.ParseNode("Path/To/My/Replay.Replay.Gbx"); foreach (CGameCtnGhost ghost in replay.Ghosts!) { ExtractGhost(ghost); } if (replay.Clip is not null) // C# 9+, otherwise use "!= null" { foreach (CGameCtnMediaTrack track in replay.Clip.Tracks) { foreach (CGameCtnMediaBlock block in track.Blocks) { if (block is CGameCtnMediaBlockGhost mediaBlockGhost) { ExtractGhost(mediaBlockGhost.GhostModel); } } } } void ExtractGhost(CGameCtnGhost ghost) { ghost.Save($"{ghost.GhostUid}.Ghost.Gbx"); } ``` -------------------------------- ### Read Replay Metadata Efficiently Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET/README.md Scans a directory for replay files and displays basic information by parsing only the header of each file for performance benefits. ```csharp using GBX.NET; using GBX.NET.Engines.Game; foreach (var filePath in Directory.EnumerateFiles("Path/To/My/Directory", "*.Replay.Gbx", SearchOption.AllDirectories)) { try { DisplayBasicReplayInfo(filePath); } catch (Exception ex) { Console.WriteLine($"Gbx exception occurred {Path.GetFileName(filePath)}: {ex}"); } } void DisplayBasicReplayInfo(string filePath) { var nodeHeader = Gbx.ParseHeaderNode(filePath); if (nodeHeader is CGameCtnReplayRecord replay) { Console.WriteLine($"{replay.MapInfo}: {replay.Time}"); } } ``` -------------------------------- ### Parse Replay File Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-ghosts-from-replays Use this to parse a replay file into a `CGameCtnReplayRecord` object. Ensure the file path is correct. ```cs var replay = Gbx.ParseNode("Path/To/My/Replay.Replay.Gbx"); ``` -------------------------------- ### Nullable Reference Files Interface and Implementation Source: https://github.com/bigbang1112/gbx-net/wiki/Design:-Reference-table-file-for-any-node-member Defines an interface for nullable reference files and provides a default implementation within `CMwNod`. This allows accessing node members that might have missing external files without throwing an exception, returning `default` instead. ```cs interface INullableReferenceFiles where TNode : CMwNod { TResult? AsNullable(Func memberFunc); } class CMwNod : INullableReferenceFiles { public TResult? AsNullable(Func memberFunc) { try { return memberFunc(this); } catch (ReferenceFileNotFoundException) { return default; } } } ``` -------------------------------- ### Process Multiple Gbx Types Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Demonstrates how to process different Gbx file types within a single operation. This is useful for batch processing various Gbx data. ```csharp await foreach (var gbx in Gbx.Stream.ParseAsync(File.OpenRead("path/to/your/file.Gbx"))) { // Process each Gbx object based on its type if (gbx.Node is Map map) { // Handle Map objects } else if (gbx.Node is Challenge challenge) { // Handle Challenge objects } // ... and so on for other Gbx types } ``` -------------------------------- ### Configure Nightly Build NuGet Source Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Add this XML configuration to your NuGet.Config file to include the nightly build package source. This allows fetching the latest development builds. ```xml ``` -------------------------------- ### Extracting Inputs from TM2020 Replays Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-input-data-from-ghosts This method is for extracting inputs from TM2020 replays, available since GBX.NET 1.1.1. It uses the CGameCtnGhost.PlayerInputs property and supports tick-based data storage. ```csharp var replay = Gbx.ParseNode("Path/To/My/Replay.Replay.Gbx"); // Taking just the first ghost makes thing cleaner var ghost = replay.GetGhosts().FirstOrDefault(); if (ghost is null) { throw new Exception("No ghost found."); } // There could be multiple sets of inputs, but I haven't seen more than one just yet var playerInputs = ghost.PlayerInputs.FirstOrDefault(); if (playerInputs is null) { throw new Exception("No player inputs found."); } var startOffset = playerInputs.StartOffset ?? TimeInt32.Zero; var tempGas = default(bool?); var tempBrake = default(bool?); var tempHorn = default(bool?); foreach (IInput input in playerInputs.Inputs) { // Do something with specific inputs // NOTE: StartOffset is not currently included in IInput structs and needs to be adjusted // This is no longer needed since 1.2.0 var time = input.Time + startOffset; // Pattern matching via switch switch (input) { case Accelerate accel: var pressed = accel.Pressed; break; case IInputSteer steer: var pureSteer = steer.Value; // represents -127 to 127 steer range var normalizedSteer = steer.GetValue(); // represents -1 to 1 steer range break; } // Pattern matching via if statement if (input is RespawnTM2020 respawn) { // special Respawn logic } // Advanced pattern matching if (input is SteerTM2020 { Value < 10 } steerUnder10) { // something } // Print out the input string representation, using a time relative to 0 Console.WriteLine(input.ToString()); } ``` -------------------------------- ### Process Multiple Gbx Types for Ghosts Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET/README.md Retrieves ghost objects from different Gbx file types using pattern matching. Requires the GBX.NET.LZO package and initialization of LZO compression. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.LZO; Gbx.LZO = new Lzo(); var node = Gbx.ParseNode("Path/To/My.Gbx"); var ghost = node switch { CGameCtnReplayRecord replay => replay.GetGhosts().FirstOrDefault(), CGameCtnMediaClip clip => clip.GetGhosts().FirstOrDefault(), CGameCtnGhost g => g, _ => null }; if (ghost is null) { Console.WriteLine("This Gbx file does not have any ghost."); } else { Console.WriteLine("Time: {0}", ghost.RaceTime); } ``` -------------------------------- ### Class Summary XML Comment Source: https://github.com/bigbang1112/gbx-net/wiki/Class-documentation Use this XML comment to provide a brief explanation of a class's purpose. This is optional but recommended for clarity. ```cs /// /// [explanation] /// ``` -------------------------------- ### Implicit Parse Methods in GBX.NET Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Implicit parsing is suitable for general use, returning a generic Gbx or CMwNod. For unknown Gbx files, Gbx.Parse returns a general Gbx class, and Gbx.ParseNode returns null. ```cs Gbx gbxMap = Gbx.Parse("Path/To/My.Map.Gbx"); CMwNod map = Gbx.ParseNode("Path/To/My.Map.Gbx"); Gbx gbxMap = Gbx.ParseHeader("Path/To/My.Map.Gbx"); CMwNod gbxMap = Gbx.ParseHeaderNode("Path/To/My.Map.Gbx"); ``` -------------------------------- ### Mermaid Graph for Gbx Explorer 2 Structure Source: https://github.com/bigbang1112/gbx-net/wiki/Gbx-Explorer-2-Design-(Nov-2023-‐-Dec-2023) Visual representation of the Gbx Explorer 2 project structure and dependencies. ```mermaid graph TD A(Shared) --> B(Server) A ---> C(Component) B -.-|HTTP| C C --> D(Client) C --> E(Photino) ``` -------------------------------- ### Implicit Parse with Gbx.Parse() and Pattern Matching Source: https://github.com/bigbang1112/gbx-net/wiki/Essentials Use Gbx.Parse() for unknown Gbx types and pattern matching to determine the node type at runtime. Ensure LZO is initialized if needed. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.Engines.MwFoundations; Gbx.LZO = new GBX.NET.LZO.MiniLZO(); // Returns Gbx - possible to cast or pattern match with generic Gbx Gbx gbx = Gbx.Parse("RandomMap.Map.Gbx"); CMwNod node = gbx.Node; // Not specified at compile time, but should store object of type CGameCtnChallenge if (gbx is Gbx gbxReplay) { CGameCtnReplayRecord replay = gbxReplay.Node; } else if (gbx is Gbx gbxMap) { // We should reach here CGameCtnChallenge map = gbxMap.Node; } switch (gbx) { case Gbx gbxReplay: CGameCtnReplayRecord replay = gbxReplay.Node; break; case Gbx gbxMap: // We should reach here CGameCtnChallenge map = gbxMap.Node; break; } string name = gbx switch { Gbx gbxReplay => "Replay", Gbx gbxMap => "Map", // We should reach here _ => "Unknown" }; ``` -------------------------------- ### Convert Gbx and CMwNod to JSON Source: https://github.com/bigbang1112/gbx-net/blob/master/Src/GBX.NET.NewtonsoftJson/README.md Use the `ToJson()` extension method to serialize a GBX object or its nodes into a JSON string. Requires GBX.NET.LZO for parsing Gbx files. ```csharp using GBX.NET; using GBX.NET.Engines.Game; using GBX.NET.LZO; using GBX.NET.NewtonsoftJson; // Add this Gbx.LZO = new Lzo(); var gbx = Gbx.Parse("Path/To/My.Map.Gbx"); string jsonGbx = gbx.ToJson(); string jsonNode = gbx.Node.ToJson(); ``` -------------------------------- ### Read Replay Metadata Quickly Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Efficiently reads metadata from a large number of replay files. This method prioritizes speed for metadata extraction. ```csharp await foreach (var replay in Gbx.Stream.ParseAsync(File.OpenRead("path/to/your/replay.Replay.Gbx"))) { Console.WriteLine($"Replay: {replay.Name} by {replay.Author}"); } ``` -------------------------------- ### Save Gbx File with Gbx Object Source: https://github.com/bigbang1112/gbx-net/wiki/Essentials Use this snippet to save modifications made to a Gbx object to a new Gbx file. Ensure LZO compression is initialized if needed. ```cs using GBX.NET; using GBX.NET.Engines.Game; Gbx.LZO = new GBX.NET.LZO.MiniLZO(); var gbx = Gbx.Parse("RandomMap.Map.Gbx"); // Do some stuff... gbx.Save("ModifiedMap.Map.Gbx"); ``` -------------------------------- ### Embed Custom Data with Smallest Size Compression Source: https://github.com/bigbang1112/gbx-net/wiki/Embedding-custom-items-to-a-map Embed custom data into the ZIP archive using `UpdateEmbeddedZipData` and specify `CompressionLevel.SmallestSize` for potentially better compression. It's recommended to decompress the Gbx before packing for more efficient compression. ```cs map.UpdateEmbeddedZipData(zip => { var entry = zip.CreateEntry("Blocks/MyFolder/MyBlock.Item.Gbx", CompressionLevel.SmallestSize); using var entryStream = entry.Open(); using var fileStream = File.OpenRead("Any/Other/Path/MyBlock.Block.Gbx"); Gbx.Decompress(input: fileStream, output: entryStream); }); ``` -------------------------------- ### Parse Map and Extract Basic Data Source: https://github.com/bigbang1112/gbx-net/wiki/Extracting-basic-map-data Use Gbx.ParseNode to load a map file and access its properties like MapName, MapUid, medal times, thumbnail, environment, block size, time limit, and mod pack details. ```cs var map = Gbx.ParseNode("Path/To/My/Track.Map.Gbx"); string mapName = map.MapName; Console.WriteLine(mapName); string mapUid = map.MapUid; Console.WriteLine(mapUid); TimeInt32? bronzeMedal = map.BronzeTime; TimeInt32? silverMedal = map.SilverTime; TimeInt32? goldMedal = map.GoldTime; TimeInt32? authorMedal = map.AuthorTime; Console.WriteLine($"Bronze medal: {bronzeMedal.ToTmString()}"); Console.WriteLine($"Silver medal: {silverMedal.ToTmString()}"); Console.WriteLine($"Gold medal: {goldMedal.ToTmString()}"); Console.WriteLine($"Author medal: {authorMedal.ToTmString()}"); byte[]? thumbnail = map.Thumbnail; if (thumbnail is null) { Console.WriteLine("Map has no thumbnail."); } string environment = map.GetEnvironment(); Console.WriteLine(environment); Int3 blockSize = map.Collection.GetBlockSize(); Console.WriteLine(blockSize); TimeInt32 timeLimit = map.ChallengeParameters.TimeLimit; Console.WriteLine(timeLimit); FileRef mod = map.ModPackDesc; Console.WriteLine(mod.LocatorUrl); ``` -------------------------------- ### Read Gbx Replay Headers Quickly Source: https://github.com/bigbang1112/gbx-net/blob/master/README.md Use Gbx.ParseHeaderNode to read only the header of Gbx files for performance benefits when scanning many files. This method avoids reading the full file content and is useful for extracting basic information like map name and time from replays. ```cs #:package GBX.NET@2.* using GBX.NET; using GBX.NET.Engines.Game; foreach (var filePath in Directory.EnumerateFiles("Path/To/My/Directory", "*.Replay.Gbx", SearchOption.AllDirectories)) { try { DisplayBasicReplayInfo(filePath); } catch (Exception ex) { Console.WriteLine($"Gbx exception occurred {Path.GetFileName(filePath)}: {ex}"); } } void DisplayBasicReplayInfo(string filePath) { var nodeHeader = Gbx.ParseHeaderNode(filePath); if (nodeHeader is CGameCtnReplayRecord replay) { Console.WriteLine($"{replay.MapInfo}: {replay.Time}"); } } ```