Skip to content

MowoGroup/mdf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MDF

A fast, header-only C++17 serialization library with human-readable text and compact binary formats.

FeaturesQuick StartFormatAPIBinarySpecificationLicense

C++17 Header-only No dependencies MIT License Platforms


Features

  • Header-only -- just #include <mdf/mdf.h>, no build step required
  • Zero dependencies -- only the C++17 standard library
  • Two formats -- human-readable .mdf text and compact .mdfb binary
  • 14 value types -- bool, int, float, string, vec2/3/4, quat, UUID, asset references, enums, arrays
  • Constructor syntax -- vec3(0, 1, 0), rgb(0.5, 0.2, 0.1), hex(#FF6600), uuid("...")
  • Comments -- // line comments and /* */ block comments
  • Binary format -- string deduplication, CRC32 integrity, optimal type sizing (f32/f64, i32/i64)
  • Error reporting -- line:column diagnostics for parse errors
  • Bidirectional conversion -- .mdf <-> .mdfb with a single function call

Quick Start

Installation

Option 1: Copy the headers

cp -r include/mdf /your/project/include/

Option 2: CMake subdirectory

add_subdirectory(mdf)
target_link_libraries(your_target PRIVATE mdf::mdf)

Option 3: CMake FetchContent

include(FetchContent)
FetchContent_Declare(mdf
    GIT_REPOSITORY https://github.com/MowoGames/mdf.git
    GIT_TAG        v1.0.0
)
FetchContent_MakeAvailable(mdf)
target_link_libraries(your_target PRIVATE mdf::mdf)

Usage

#include <mdf/mdf.h>
#include <iostream>

int main() {
    // ---- Read an MDF file ----
    MDF::MdfDocument doc;
    std::string error;
    if (!MDF::MdfDocument::ParseFromFile("scene.mdf", doc, &error)) {
        std::cerr << "Parse error: " << error << std::endl;
        return 1;
    }

    auto& root = doc.Root();
    std::cout << "Root type: " << root.GetType() << std::endl;
    std::cout << "Root name: " << root.GetName() << std::endl;

    // Navigate the tree
    if (auto* player = root.FindChild("GameObject", "Player")) {
        if (auto* transform = player->FindChild("Transform")) {
            MDF::MdfVec3 pos = transform->GetProperty("position").AsVec3();
            std::cout << "Player position: " << pos.x << ", " << pos.y << ", " << pos.z << std::endl;
        }
    }

    // ---- Build a document ----
    MDF::MdfDocument out;
    auto& scene = out.CreateRoot("Scene", "MyScene");
    scene.SetProperty("version", 1);
    scene.SetProperty("ambient", MDF::MdfVec4(0.1f, 0.1f, 0.15f, 1.0f));

    auto& obj = scene.AddChild("GameObject", "Player");
    obj.SetProperty("active", true);
    obj.SetProperty("tag", "Player");

    auto& transform = obj.AddChild("Transform");
    transform.SetProperty("position", MDF::MdfVec3(0, 1, 0));
    transform.SetProperty("rotation", MDF::MdfVec3(0, 90, 0));
    transform.SetProperty("scale", MDF::MdfVec3(1, 1, 1));

    // Write text
    out.WriteToFile("output.mdf");

    // Write binary
    MDF::MdfBinaryWriter::WriteToFile(out, "output.mdfb");

    // Convert binary back to text
    MDF::MdfConverter::BinaryToText("output.mdfb", "roundtrip.mdf");
}

Format

MDF uses a clean, hierarchical syntax inspired by configuration languages:

Scene "MainScene" {
    version: 1
    gravity: vec3(0, -9.81, 0)

    // Environment settings
    Environment {
        ambient_color: rgb(0.1, 0.1, 0.15)
        skybox: @"Assets/Skyboxes/BlueSky.hdr"
        fog_density: 0.002
        fog_color: hex(#B0C4DE)
    }

    GameObject "Player" {
        tag: "Player"
        active: true
        layer: 1

        Transform {
            position: vec3(0, 1, 0)
            rotation: vec3(0, 90, 0)
            scale: vec3(1, 1, 1)
        }

        MeshRenderer {
            mesh: @"Assets/Models/Character.fbx"
            material: @"Assets/Materials/PlayerMat.mdf"
            cast_shadows: true
        }

        RigidBody {
            type: Dynamic
            mass: 75.0
            use_gravity: true
        }
    }

    GameObject "MainCamera" {
        Transform {
            position: vec3(0, 5, -10)
            rotation: vec3(25, 0, 0)
        }

        Camera {
            projection: Perspective
            fov: 60.0
            near: 0.1
            far: 1000.0
        }
    }
}

Value Types

Type Syntax Example
Null null value: null
Bool true / false active: true
Integer number count: 42
Float decimal / scientific speed: 3.14, tiny: 1e-6
String "quoted" name: "Player"
Vec2 vec2(x, y) uv: vec2(0.5, 1.0)
Vec3 vec3(x, y, z) position: vec3(0, 1, 0)
Vec4 vec4(x, y, z, w) color: vec4(1, 0, 0, 1)
Quat quat(x, y, z, w) rotation: quat(0, 0, 0, 1)
Color rgb(r, g, b) tint: rgb(0.5, 0.2, 0.1)
Color+A rgba(r, g, b, a) overlay: rgba(0, 0, 0, 0.5)
Hex hex(#RRGGBB) accent: hex(#FF6600)
UUID uuid("...") id: uuid("550e8400-...")
Asset Ref @"path" mesh: @"Models/Cube.fbx"
Enum bare identifier mode: Dynamic
Array [a, b, c] layers: [1, 2, 3]

API

Core Classes

MdfDocument          Root container, parse/write entry point
  MdfNode            Named/typed block with properties and children
    MdfValue         Type-safe variant value (14 types)
      MdfVec2/3/4    POD math types
      MdfQuat        POD quaternion type

Reading

MDF::MdfDocument doc;
std::string error;

// From file
MDF::MdfDocument::ParseFromFile("file.mdf", doc, &error);

// From string
MDF::MdfDocument::ParseFromString(source, doc, &error);

// Navigate
auto& root = doc.Root();                                // first root node
auto* child = root.FindChild("Type");                   // by type
auto* child = root.FindChild("Type", "Name");           // by type + name
auto children = root.FindChildren("Type");              // all of type
bool exists = root.HasProperty("key");                  // property check
MDF::MdfValue val = root.GetProperty("key");            // get value

Writing

MDF::MdfDocument doc;
auto& root = doc.CreateRoot("Scene", "MyScene");

// Set properties
root.SetProperty("version", 1);
root.SetProperty("name", "Hello");
root.SetProperty("position", MDF::MdfVec3(1, 2, 3));
root.SetProperty("id", MDF::MdfValue::MakeUUID("550e8400-..."));
root.SetProperty("mesh", MDF::MdfValue::MakeAssetRef("Models/Cube.fbx"));
root.SetProperty("mode", MDF::MdfValue::MakeEnum("Dynamic"));
root["shorthand"] = MDF::MdfValue(42);                  // operator[]

// Add children
auto& child = root.AddChild("Transform");
child.SetProperty("position", MDF::MdfVec3(0, 0, 0));

// Serialize
std::string text = doc.WriteToString();                  // to string
doc.WriteToFile("output.mdf");                           // to file
doc.WriteToFile("output.mdf", 2);                        // custom indent

Binary Format

// Write binary
auto bytes = MDF::MdfBinaryWriter::Write(doc);           // to buffer
MDF::MdfBinaryWriter::WriteToFile(doc, "data.mdfb");     // to file

// Read binary
MDF::MdfDocument doc;
MDF::MdfBinaryReader::ReadFromFile("data.mdfb", doc);    // from file
MDF::MdfBinaryReader::Read(bytes, doc);                  // from buffer
MDF::MdfBinaryReader::Read(ptr, size, doc);              // from raw pointer

// Convert
MDF::MdfConverter::TextToBinary("in.mdf", "out.mdfb");   // .mdf -> .mdfb
MDF::MdfConverter::BinaryToText("in.mdfb", "out.mdf");   // .mdfb -> .mdf

Value Type Checking

MDF::MdfValue val = node.GetProperty("key");

val.Type();           // MdfValueType enum
val.IsNull();         // type checks
val.IsInt();
val.IsFloat();
val.IsNumber();       // int or float
val.IsString();
val.IsVec3();
// ... etc

val.AsBool();         // typed getters (throw on mismatch)
val.AsInt();          // int64_t (auto-converts from float)
val.AsFloat();        // double (auto-converts from int)
val.AsString();       // works for String, UUID, AssetRef, Enum
val.AsVec3();         // MdfVec3
val.AsArray();        // std::vector<MdfValue>

Binary Format

The .mdfb binary format provides:

  • ~3-5x smaller files compared to text format
  • ~10x faster parsing than text
  • String deduplication -- each unique string stored once
  • CRC32 integrity -- data corruption detection
  • Optimal sizing -- int32 vs int64, float32 vs float64 chosen automatically

See docs/binary-format.md for the full specification.

Header Structure

include/mdf/
  mdf.h              All-in-one include (recommended)
  mdf_types.h        Vec2, Vec3, Vec4, Quat
  mdf_value.h        MdfValue (typed variant)
  mdf_document.h     MdfNode, MdfDocument
  mdf_lexer.h        Tokenizer
  mdf_parser.h       Text format parser
  mdf_writer.h       Text format writer
  mdf_binary.h       Binary format reader/writer/converter

You can include individual headers if you only need a subset:

#include <mdf/mdf.h>            // everything (recommended)
#include <mdf/mdf_document.h>   // just Document + Node + Value (no parse/write)
#include <mdf/mdf_types.h>      // just Vec2/3/4, Quat

License

MIT License. See LICENSE for details.

About

A lightweight, human-readable serialization format for data persistence.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors