A rusty, spiky, heat-seeking quake map parser.

Related tags

Parsing shalrath
Overview

Shalrath   License Latest Version Documentation

A rusty, spiky, heat-seeking quake map parser

shalrath is a rust representation, [nom] parser and string serializer for Quake map files.

It's written in pure Rust, and enforces the use of safe code crate-wide via #![forbid(unsafe_code)].

Rust Representation

The Rust representation lives in the [repr] module, and is a set of structs that represent the contents of a map file.

The overall class structure - with some of the more specific innermost types omitted for simplicity - looks something like this:

Map
└ Entity (1..*)
  ├ Properties (1..1)
  │ └ Property (0..*)
  └ Brushes (1..1)
    └ Brush (0..*)
      └ BrushPlane (4..*)

[Entity] is a game object that can contain [Property]s and [Brush]es.

[Property]s are key-value pairs stored as [String]s.

[Brush]es are convex shapes defined by the intersection of a set of [TexturePlane]s - 3D planes with associated texture mapping data.

At least one [Entity] - known as the worldspawn - must exist in any given map, and represents all of its structural [Brush]es. Structural [Brush]es are static geometry with no associated behavior.

In Quake terms, entities are given behavior by assigning them a classname property, which is used by the game code to assign an actor class that reads from other properties attached to the object.

These entities are separated into two categories:

  • Point Entities are [Entity]s that have a classname, but no [Brush]es.
    • These are used to represent actors like the player, enemies, or item pickups.
  • Brush Entities are [Entity]s that have both a classname and [Brush]es.
    • These are used to represent special world geometry like moving doors, elevators, and similar.

But, that's just for context, more of which can be found in the map file spec.

Ultimately, what you do with the Rust representation after parsing data into it is down to needs of your project. To that end, struct members are public in the case of named fields, and exposed via [Deref] for collection wrappers like [Properties] and [Brushes].

Parsing

The simplest way to parse a map file into AST is by way of the [FromStr] trait:

use shalrath::repr::*;

let map =
    "{\"classname\" \"worldspawn\"\n{\n( 0 1 2 ) ( 3 4 5 ) ( 6 7 8 ) TEXTURE 0 0 0 1 1\n}\n}"
        .parse::<Map>()
        .expect("Failed to parse map");

assert_eq!(
    map,
    Map::new(vec![Entity {
        properties: Properties::new(vec![Property {
            key: "classname".into(),
            value: "worldspawn".into()
        }]),
        brushes: Brushes::new(vec![Brush::new(vec![
            BrushPlane {
                plane: Triangle {
                    v0: Point {
                        x: 0.0,
                        y: 1.0,
                        z: 2.0
                    },
                    v1: Point {
                        x: 3.0,
                        y: 4.0,
                        z: 5.0
                    },
                    v2: Point {
                        x: 6.0,
                        y: 7.0,
                        z: 8.0
                    },
                },
                texture: "TEXTURE".into(),
                texture_offset: TextureOffset::Standard { u: 0.0, v: 0.0 },
                angle: 0.0,
                scale_x: 1.0,
                scale_y: 1.0,
                extension: Extension::Standard,
            }
        ])])
    }])
)

For a lower-level alternative, the [parser] module contains the [nom] functions used by the [FromStr] implementations, which can be used to parse plaintext data into individual AST structs.

Of these, [parse_map] is the primary entrypoint, and is equivalent to str::parse::<Map>():

use shalrath::parser::repr::parse_map;

let map_string = include_str!("../test_data/abstract-test.map");
let (_, map_ast) = parse_map(map_string).expect("Failed to parse map");
println!("{:#?}", map_ast);

String Serialization

The Rust representation can be serialized back into a text-based map representation via the [Display] or [ToString] traits:

use shalrath::repr::Map;

let map_string = include_str!("../test_data/abstract-test.map");
let map_ast = map_string.parse::<Map>().expect("Failed to parse map file");
let serialized_map_string = map_ast.to_string();
println!("{}", serialized_map_string);

In addition, round-trip parsing the resulting string back into the corresponding AST is a lossless operation, and is included as a standard part of shalrath's integration tests:

use shalrath::repr::Map;

let map_string = include_str!("../test_data/abstract-test.map");
let map_ast = map_string.parse::<Map>().expect("Failed to parse map file");
let serialized_map_string = map_ast.to_string();
let roundtrip_map_ast = serialized_map_string.parse::<Map>().expect("Failed to parse map file");
assert_eq!(map_ast, roundtrip_map_ast);

Format Support

Several variants of the base Quake 1 map format exist that retain the same core structure, but modify how brush planes are encoded.

shalrath supports these by categorizing them by UV format (represented by the [TextureOffset] enum):

UV Format Notes
Standard Faces project textures based on the closest world X/Y/Z plane.
Valve Faces project textures based on custom U/V axes, allowing for skewing and more accurate texturing of curved surfaces.

...and brush plane extension data, represented by the [Extension] enum:

Brush Plane Extension Notes
Standard Brush planes contain no extra data.
Hexen 2 Brush planes contain an extra numerical value whose usage is unknown.
Quake 2 Brush planes contain content_flags and surface_flags bitmasks, and a floating point value.

Other formats like Quake 3 and Daikatana exist, but are effectively variants of the above, and will be handled transparently by the parser.

Serde Support

For cases where serializing and deserializing from non-map formats is required, shalrath includes [serde::Serialize] and [serde::Deserialize] derives for all types in the [repr] module.

These can be enabled by applying the serde feature flag to the shalrath dependency in Cargo.toml.

Streaming

Currently shalrath only implements complete parsers that expect a full set of input data.

streaming implementations are planned, but currently pending further research.

You might also like...
A typed parser generator embedded in Rust code for Parsing Expression Grammars

Oak Compiled on the nightly channel of Rust. Use rustup for managing compiler channels. You can download and set up the exact same version of the comp

Rust query string parser with nesting support

What is Queryst? This is a fork of the original, with serde and serde_json updated to 0.9 A query string parsing library for Rust inspired by https://

A fast, extensible, command-line arguments parser

parkour A fast, extensible, command-line arguments parser. Introduction 📚 The most popular argument parser, clap, allows you list all the possible ar

Soon to be AsciiDoc parser implemented in rust!

pagliascii "But ASCII Doc, I am Pagliascii" Soon to be AsciiDoc parser implemented in rust! This project is the current implementation of the requeste

An LR parser generator, implemented as a proc macro

parsegen parsegen is an LR parser generator, similar to happy, ocamlyacc, and lalrpop. It currently generates canonical LR(1) parsers, but LALR(1) and

A modern dialogue executor and tree parser using YAML.

A modern dialogue executor and tree parser using YAML. This crate is for building(ex), importing/exporting(ex), and walking(ex) dialogue trees. convo

A friendly parser combinator crate

Chumsky A friendly parser combinator crate that makes writing LL-1 parsers with error recovery easy. Example Here follows a Brainfuck parser. See exam

Minimalist pedantic command line parser

Lexopt Lexopt is an argument parser for Rust. It tries to have the simplest possible design that's still correct. It's so simple that it's a bit tedio

Universal configuration library parser

LIBUCL Table of Contents generated with DocToc Introduction Basic structure Improvements to the json notation General syntax sugar Automatic arrays cr

Owner
Qodot
Quake ,map support for Godot
Qodot
Yet Another Parser library for Rust. A lightweight, dependency free, parser combinator inspired set of utility methods to help with parsing strings and slices.

Yap: Yet another (rust) parsing library A lightweight, dependency free, parser combinator inspired set of utility methods to help with parsing input.

James Wilson 117 Dec 14, 2022
Website for Microformats Rust parser (using 'microformats-parser'/'mf2')

Website for Microformats Rust parser (using 'microformats-parser'/'mf2')

Microformats 5 Jul 19, 2022
A native Rust port of Google's robots.txt parser and matcher C++ library.

robotstxt A native Rust port of Google's robots.txt parser and matcher C++ library. Native Rust port, no third-part crate dependency Zero unsafe code

Folyd 72 Dec 11, 2022
Rust parser combinator framework

nom, eating data byte by byte nom is a parser combinators library written in Rust. Its goal is to provide tools to build safe parsers without compromi

Geoffroy Couprie 7.6k Jan 7, 2023
url parameter parser for rest filter inquiry

inquerest Inquerest can parse complex url query into a SQL abstract syntax tree. Example this url: /person?age=lt.42&(student=eq.true|gender=eq.'M')&

Jovansonlee Cesar 25 Nov 2, 2020
Parsing Expression Grammar (PEG) parser generator for Rust

Parsing Expression Grammars in Rust Documentation | Release Notes rust-peg is a simple yet flexible parser generator that makes it easy to write robus

Kevin Mehall 1.2k Dec 30, 2022
A fast monadic-style parser combinator designed to work on stable Rust.

Chomp Chomp is a fast monadic-style parser combinator library designed to work on stable Rust. It was written as the culmination of the experiments de

Martin Wernstål 228 Oct 31, 2022
A parser combinator library for Rust

combine An implementation of parser combinators for Rust, inspired by the Haskell library Parsec. As in Parsec the parsers are LL(1) by default but th

Markus Westerlind 1.1k Dec 28, 2022
LR(1) parser generator for Rust

LALRPOP LALRPOP is a Rust parser generator framework with usability as its primary goal. You should be able to write compact, DRY, readable grammars.

null 2.4k Jan 7, 2023
The Elegant Parser

pest. The Elegant Parser pest is a general purpose parser written in Rust with a focus on accessibility, correctness, and performance. It uses parsing

pest 3.5k Jan 8, 2023