shk_parser

Crates.ioshk_parser
lib.rsshk_parser
version0.1.1
created_at2025-07-28 18:00:16.012333+00
updated_at2025-07-28 21:18:05.09449+00
descriptionA parser for Stronghold Kingdoms attack formation files (.cas)
homepage
repositoryhttps://github.com/yourusername/shk_parser
max_upload_size
id1771438
size51,798
Tom Voet (tomvoet)

documentation

https://docs.rs/shk_parser

README

SHK Parser

A Rust library for parsing Stronghold Kingdoms attack formation files (.cas files).

Features

  • ๐Ÿš€ Fast & Safe: Written in Rust with zero-copy parsing where possible
  • ๐ŸŽฏ Type-Safe: Strong typing for all unit types and abilities
  • ๐Ÿ“– Well-Documented: Comprehensive API documentation and examples
  • ๐Ÿงช Well-Tested: Extensive test coverage with real game files
  • ๐Ÿ”ง Easy to Use: Simple, clean API with helpful error messages
  • ๐ŸŒ Serialization: Optional JSON support via serde
  • ๐Ÿ’ป CLI Tool: Command-line interface with multiple output formats
  • โšก Better Errors: Detailed error messages with thiserror

Optional Features

Enable additional functionality with cargo features:

[dependencies]
shk_parser = { version = "0.1.0", features = [
  "serde",
  "tsify",
  "cli"
] }
  • serde: Enables JSON serialization/deserialization
  • tsify: Enables TypeScript type generation for WASM bindings (requires serde)
  • cli: Enables the command-line interface

Quick Start

Add this to your Cargo.toml:

[dependencies]
shk_parser = "0.1.0"

Parse a formation file:

use shk_parser::parse_formation_file;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let units = parse_formation_file("my_formation.cas")?;

    println!("Formation contains {} units:", units.len());
    for (i, unit) in units.iter().enumerate() {
        println!("  {}: {}", i + 1, unit);
    }

    Ok(())
}

Supported Units

The parser supports all Stronghold Kingdoms unit types:

Basic Units

  • Archer: Basic ranged units
  • Pikeman: Basic melee units

Advanced Units

  • Catapult: Siege weapons with target coordinates
  • Captain: Special command units with various abilities

Captain Abilities

  • Delay: Wait for a specified time before acting
  • Rallying Cry: Rally nearby troops to boost morale
  • Arrow Volley: Coordinate arrow attack at target location
  • Battle Cry: Boost unit morale and effectiveness
  • Catapults Volley: Coordinate catapult bombardment at target

API Reference

Core Functions

// Parse from raw bytes
fn parse_formation(data: &[u8]) -> Result<Vec<UnitRecord>, ParseError>

// Parse directly from file path
fn parse_formation_file<P: AsRef<Path>>(path: P) -> Result<Vec<UnitRecord>, ParseError>

Types

pub struct UnitRecord {
    pub position: Position,
    pub unit_type: UnitType,
}

pub struct Position {
    pub x: u8,
    pub y: u8,
}

pub enum UnitType {
    Archer,
    Pikeman,
    Catapult { target: Position },
    Captain { ability: CaptainAbility, wait_time: u8 },
    Unknown(u8),
}

pub enum CaptainAbility {
    Delay,
    RallyingCry,
    ArrowVolley { target: Position },
    BattleCry,
    CatapultsVolley { target: Position },
    Unknown(u8),
}

Command Line Usage

With the cli feature enabled, you can use the binary tool:

# Parse all test files (default behavior)
cargo run --features cli --bin parse_formations

# Parse specific files
cargo run --features cli --bin parse_formations -- file1.cas file2.cas

# Verbose output with detailed parsing info
cargo run --features cli --bin parse_formations -- --verbose file.cas

# JSON output (requires serde feature)
cargo run --features cli,serde --bin parse_formations -- --format json file.cas

CLI Options

A parser for Stronghold Kingdoms formation files (.cas)

Usage: parse_formations [OPTIONS] [FILES]...

Arguments:
  [FILES]...  Formation file(s) to parse

Options:
  -f, --format <FORMAT>  Output format [default: human]
  -v, --verbose          Show detailed unit information
  -h, --help             Print help

File Format

.cas files use a simple binary format:

  1. Header (4 bytes): Number of units as little-endian u32
  2. Unit Records (variable length): One record per unit

Record Formats

Unit Type Size Format
Archer/Pikeman 3 bytes x, y, unit_type
Catapult 5 bytes x, y, unit_type, target_x, target_y
Captain (simple) 4 bytes x, y, unit_type, wait_time
Captain (with target) 6 bytes x, y, unit_type, wait_time, target_x, target_y

Examples

Parse and Display All Units

use shk_parser::parse_formation_file;

let units = parse_formation_file("formation.cas")?;
for unit in &units {
    match &unit.unit_type {
        UnitType::Archer => println!("Archer at ({}, {})", unit.position.x, unit.position.y),
        UnitType::Captain { ability, wait_time } => {
            println!("Captain using {:?} for {}s at ({}, {})",
                ability, wait_time, unit.position.x, unit.position.y);
        }
        _ => println!("{}", unit),
    }
}

Filter by Unit Type

use shk_parser::{parse_formation_file, UnitType};

let units = parse_formation_file("formation.cas")?;

// Find all captains
let captains: Vec<_> = units.iter()
    .filter(|unit| matches!(unit.unit_type, UnitType::Captain { .. }))
    .collect();

println!("Found {} captains", captains.len());

Access Captain Abilities

use shk_parser::{parse_formation_file, UnitType, CaptainAbility};

let units = parse_formation_file("formation.cas")?;

for unit in &units {
    if let UnitType::Captain { ability, wait_time } = &unit.unit_type {
        match ability {
            CaptainAbility::ArrowVolley { target } => {
                println!("Arrow volley targeting ({}, {}) after {}s",
                    target.x, target.y, wait_time);
            }
            CaptainAbility::Delay => {
                println!("Waiting for {}s", wait_time);
            }
            _ => println!("Captain ability: {:?}", ability),
        }
    }
}

Error Handling

The library provides detailed error information:

use shk_parser::{parse_formation_file, ParseError};

match parse_formation_file("formation.cas") {
    Ok(units) => println!("Parsed {} units", units.len()),
    Err(ParseError::FileNotFound(path)) => eprintln!("File not found: {}", path),
    Err(ParseError::InvalidFormat(msg)) => eprintln!("Invalid file format: {}", msg),
    Err(ParseError::IoError(err)) => eprintln!("IO error: {}", err),
}

Testing

Run the test suite:

cargo test

The library includes tests with real .cas files to ensure compatibility.

License

Licensed under either of

at your option.

Commit count: 0

cargo fmt