Skip to content

πŸ› οΈ League Toolkit

Rust library for parsing, editing, and writing League of Legends file formats

CI Crates.io Docs License

Documentation β€’ Crates.io β€’ Changelog


✨ Features

  • πŸ“¦ WAD Archives β€” Read and write .wad.client asset containers
  • 🎨 Textures β€” Decode/encode .tex and .dds formats
  • 🧍 Meshes β€” Parse skinned (.skn) and static (.scb/.sco) meshes
  • 🦴 Animation β€” Load skeletons (.skl) and animations (.anm)
  • πŸ“‹ Property Bins β€” Read/write .bin configuration files
  • πŸ—ΊοΈ Map Geometry β€” Parse .mapgeo environment assets
  • πŸ”§ Modular β€” Use individual crates or the umbrella crate

πŸ“¦ Installation

Add the umbrella crate to your project:

[dependencies]
league-toolkit = { version = "0.2", features = ["wad", "mesh", "texture"] }

Or use individual crates for a smaller dependency footprint:

[dependencies]
ltk_wad = "0.2"
ltk_texture = "0.5"
ltk_mesh = "0.3"

πŸš€ Quick Start

Reading a WAD Archive

use std::fs::File;
use ltk_wad::Wad;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = File::open("assets.wad.client")?;
    let mut wad = Wad::mount(file)?;
    
    println!("Archive contains {} files", wad.chunks().len());
    
    // Decode a specific chunk
    let (mut decoder, chunks) = wad.decode();
    for chunk in chunks.values().take(5) {
        let data = decoder.load_chunk_decompressed(chunk)?;
        println!("Chunk {:016x}: {} bytes", chunk.path_hash(), data.len());
    }
    
    Ok(())
}

Decoding a Texture

use ltk_texture::Tex;
use std::fs::File;

let tex = Tex::from_reader(&mut File::open("texture.tex")?)?;
let surface = tex.decode_mipmap(0)?;
surface.into_rgba_image()?.save("output.png")?;

See the ltk_texture README for supported formats, raw pixel data access, and encoding.

Parsing a Skinned Mesh

use ltk_mesh::SkinnedMesh;
use std::fs::File;

let mesh = SkinnedMesh::from_reader(&mut File::open("champion.skn")?)?;
println!("Vertices: {}", mesh.vertex_buffer().vertex_count());
println!("Submeshes: {}", mesh.ranges().len());

Working with Property Bins

use ltk_meta::concrete::{values, Bin, BinObject};
use std::fs::File;

// Read
let bin = Bin::from_reader(&mut File::open("data.bin")?)?;
for (path_hash, object) in &bin.objects {
    println!("Object {path_hash:08x}");
}

// Create
let bin = Bin::builder()
    .dependency("shared/data.bin")
    .object(
        BinObject::builder(0x12345678u32, 0xABCDEF00u32)
            .property(0x1111, values::I32::new(42))
            .build()
    )
    .build();

See the ltk_meta README for the full surface: property paths, override bins (PTCH), typed values, and round-trip writing.


πŸ“š Crates

Crate Description Formats
league-toolkit Umbrella crate (feature-gated re-exports) β€”
ltk_wad WAD archive reading/writing .wad.client
ltk_texture Texture decoding/encoding .tex, .dds
ltk_mesh Skinned & static mesh parsing .skn, .scb, .sco
ltk_anim Skeleton & animation formats .skl, .anm
ltk_meta Property bin files .bin
ltk_ritobin Human-readable bin format ritobin text
ltk_mapgeo Map environment geometry .mapgeo
ltk_file File type detection β€”
ltk_hash Hash functions (FNV-1a, ELF) β€”
ltk_shader Shader path utilities β€”
ltk_primitives Geometric primitives β€”
ltk_io_ext I/O extensions (internal) β€”

Each crate lives under crates/<name>.


βš™οΈ Feature Flags

The league-toolkit umbrella crate uses feature flags to control which subsystems are included:

Feature Enables Default
anim ltk_anim βœ…
file ltk_file βœ…
mesh ltk_mesh βœ…
meta ltk_meta βœ…
primitives ltk_primitives βœ…
texture ltk_texture βœ…
wad ltk_wad βœ…
hash ltk_hash βœ…
serde Serde support (where available) ❌

For a minimal build, disable defaults and opt-in selectively:

[dependencies]
league-toolkit = { version = "0.2", default-features = false, features = ["wad"] }

Some crates expose their own feature flags β€” e.g. texture encoding requires intel-tex on ltk_texture (see the ltk_texture README).


πŸ“– Documentation


πŸ› οΈ Development

Prerequisites: Rust stable toolchain

# Build all crates
cargo build

# Run tests
cargo test

# Build documentation
cargo doc --open

AI-Assisted Development

AI agents can produce large, hard-to-review changesets. This repository answers that with a document trail rather than a tool pipeline: work is specified, decided and sliced in the repo before it is written, and each artifact is reviewable on its own.

Document Holds Where
PRD Why a feature exists, who asks for it, numbered requirements (FR-N) docs/prd/NNN-slug.md
ADR One architectural decision: what forced it, the options it beat, what it costs docs/adr/NNNN-slug.md
Design doc The API surface and the wire format docs/design/<feature>.md
Ticket One slice of implementable work, rendered to a GitHub issue .scratch/<project>/issues/*.md

The rule that keeps them readable: each cites the others rather than restating them. A design doc cites requirements as FR-N and decisions as ADR-NNNN; two copies of one argument drift.

GitHub issues are rendered from the ticket files β€” the repo is the source of truth, and an issue that disagrees with its ticket is fixed by re-rendering, not by editing it on GitHub.

Claude Code users get four skills in .claude/skills/ that write and maintain all of this: write-prd, write-adr, write-ticket and sync-issues. Worked example: PRD-001 with ADR-0001 to ADR-0006 and docs/design/ptch-property-patches.md.

Contributors using AI agents SHOULD follow this workflow. A PR that arrives with no written reasoning behind it may need extra review cycles. Day-to-day rules for agents live in CLAUDE.md.

Project Structure

league-toolkit/
|-- crates/
|   |-- league-toolkit/    # Umbrella crate
|   |-- ltk_wad/           # WAD archives
|   |-- ltk_texture/       # Textures
|   |-- ltk_mesh/          # Meshes
|   |-- ltk_anim/          # Animation
|   |-- ltk_meta/          # Property bins
|   |-- ltk_ritobin/       # Ritobin text format
|   |-- ltk_mapgeo/        # Map geometry
|   |-- ltk_file/          # File detection
|   |-- ltk_hash/          # Hashing
|   |-- ltk_shader/        # Shader utilities
|   |-- ltk_primitives/    # Primitives
|   |-- ltk_io_ext/        # I/O extensions
|-- docs/
    |-- LTK_GUIDE.md       # Usage guide

πŸ“‹ Releasing

This repository uses Release-plz for automated versioning and publishing:

  1. Pushes to main trigger Release-plz to open a release PR
  2. Merging the release PR publishes updated crates to crates.io

πŸ“„ License

Licensed under either of:

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


Made with ❀️ by the LeagueToolkit community

About

The home of tooling for League of Legends

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

13 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages