Skip to content

Architecture Documentation

System Overview

Khada is a League of Legends 3D model viewer that extracts, processes, and renders character skins from the game's WAD files.

Package Structure

packages/extractor/ (or khada-extractor/ repository)

C# .NET extractor that converts League WAD files to GLTF format. This is a standalone .NET application that can be used independently or integrated into the Khada pipeline.

Key Components:

  • LeagueConvert.CommandLine/ - CLI interface and WAD resolution
    • Program.cs - Main entry point with convert-wad and convert-all commands
    • WadDownloader.cs - Downloads WAD files from Riot CDN using CDTB (CommunityDragon Toolbox)
    • LeaguePathResolver.cs - Auto-detects League installation or downloads WADs
  • LeagueConvert/ - Main conversion logic
    • IO/Skin/ - Skin data parsing and conversion
      • StaticMaterial.cs - Shader parameter resolution and profiles
      • Skin.cs - Skin data management
      • Extensions/SkinExtensions.cs - GLTF export logic
    • IO/WadFile/StringWad.cs - WAD file parsing with hash table lookup
    • IO/HashTables/HashTables.cs - Hash table management (Game and BinHashes)
  • SimpleGltf/ - GLTF format implementation
    • Json/Material.cs - Material and extras definitions
  • OfficialLeagueToolkit/ - WAD file format parsing (WadFile, WadChunk, etc.)
  • LeagueToolkit/ - Additional League file format utilities

Key Concepts:

  • WAD Files: Riot's archive format (magic RW). Chunks are keyed by xxHash64 of the lowercased, forward-slash-normalized path. v3 adds a 256-byte ECDSA signature; v3.4+ packs compression type + sub-chunk fields into a single u32.
  • Compression per chunk: None (0) | GZip (1) | Satellite (2) | Zstd (3) | ZstdChunked (4). Wwise banks/packages default to None; other file types default to Zstd.
  • Hash Tables: Required for converting file hashes to readable paths:
    • hashes.game.txt — maps xxHash64 of WAD chunk paths (lowercased, /-normalized) → path string. Used by StringWad and the WAD toolchain.
    • hashes.binhashes.txt — maps FNV-1a 32-bit hashes (of lowercased names) → BIN property/class/object names. Used when resolving .bin property fields.
    • Do not confuse with SDBM (present in the codebase for a handful of legacy auxiliary hashes only).
  • WAD Downloading: Uses CDTB (Python library) to download WADs from Riot's CDN. Supports PBE (default) and live servers.
  • Shader Profiles: Maps shader paths/hashes to parameter name conventions
  • Procedural Shaders: Shaders that generate visuals without diffuse textures (e.g., Glass)
  • Material Overrides: Per-submesh material definitions that override defaults
  • Two-Pass System: Raw params stored first, then resolved based on shader profile

packages/web/

Frontend Three.js-based model viewer.

Key Components:

  • src/engine/index.ts - Three.js engine and material handling
    • handleFlipbook() - Animation-synced flipbook handling
    • updateAutoFlipbooks() - Time-based flipbook animations
    • initializeModel() - Material setup and glass material conversion

Key Concepts:

  • Animation-synced flipbooks: Driven by skeletal animation keyframes
  • Time-based flipbooks: Driven by elapsed time and flipbookSpeed
  • Glass materials: Converted from MeshStandardMaterial to MeshPhysicalMaterial with transmission

packages/processor/

Post-processing pipeline for GLTF optimization.

packages/settings/

Configuration files for model fixes, aliases, and metadata.

Data Flow

League WAD File (or Champion Name)

[LeaguePathResolver] Auto-detect installation or download via CDTB

[StringWad] Load WAD and resolve hashes using hash tables

[Skin] Parse skin data from WAD entries

[StaticMaterial] ResolveForShader() → Shader Profile

[SkinExtensions] ConvertToGltfAsset()

GLTF File (with MaterialExtras)

[Processor] Optimize (gltfpack, texture compression)

[Web] Load GLTF → Three.js Materials

Render

Extractor Workflow Details

  1. Input Resolution:

    • If input is a file path → Use directly
    • If input is a champion name (e.g., "Zoe") → Resolve to WAD path:
      • Check local League installation (DATA/FINAL/Champions/Zoe.wad.client)
      • If not found and --no-download not set → Download via CDTB
      • Cache location: ~/.khada-extractor/cache/Game/
  2. Hash Table Loading:

    • Downloads latest from CommunityDragon if not found locally
    • Falls back to cached versions in temp directory
    • Required for both WAD parsing and binary file property resolution
  3. WAD Parsing:

    • Uses OfficialLeagueToolkit to read WAD structure
    • Resolves hashed file paths using HashTables.Game
    • Extracts skin entries matching pattern: data/characters/{champion}/skins/{skinName}/
  4. Skin Conversion:

    • Loads skin binary files and dependencies recursively
    • Resolves shader parameters using shader profiles
    • Generates GLTF with materials, meshes, skeletons, and animations

Material System

Shader Profile Resolution

  1. Parse Phase: All shader parameters stored in RawParams dictionary
  2. Resolve Phase: ResolveForShader() interprets params based on shader profile:
    • Check shader hash (if numeric string)
    • Check exact path match
    • Check substring match
    • Fall back to DefaultProfile

Material Extras Structure

typescript
MaterialExtras {
  visible: boolean
  hash: uint
  renderOrder?: number
  windingToCull?: "Front" | "Back" | "None"
  blendMode?: "Additive" | "Normal"
  uvScrollSpeed?: [x, y]
  uvTile?: [x, y]
  flipbookSize?: [columns, rows]
  flipbookFrameIndex?: number
  flipbookAccessoryHash?: uint
  flipbookSpeed?: number  // FPS for auto-animated flipbooks
  isGlass?: boolean
  glassParams?: {
    color1?: [r, g, b, a]
    color2?: [r, g, b, a]
    roughness?: number
    fresnelInner?: number
    fresnelOuter?: number
    alphaBias?: number
    lightIntensity?: number
    gradientBias?: number
  }
}

Shader Types

Standard Shaders

  • Use diffuse textures
  • Standard PBR parameters
  • Example: Shaders/SkinnedMesh/Diffuse

Procedural Shaders

  • No diffuse texture (generates visuals from params)
  • Requires placeholder texture generation
  • Example: Shaders/SkinnedMesh/Glass

Flipbook Shaders

  • Sprite sheet animations
  • Two types:
    • Animation-synced: Driven by flipbookAccessoryHash keyframes
    • Time-based: Driven by flipbookSpeed (FPS)

Bloom/Emissive Shaders

  • Have BloomStrength > 0
  • Color params are for emissive, NOT base tint
  • Example: Shaders/SkinnedMesh/PanBloom

Critical Design Decisions

  1. No Generic Color Tinting: Only explicit shader profiles can apply color tints. Prevents VFX colors from being misapplied as base tints.

  2. Texture Cloning: All texture modifications (repeat, offset) must clone the texture first to avoid cascading changes.

  3. Procedural Shader Placeholders: Procedural shaders get 1x1 placeholder textures generated from color parameters.

  4. Glass Material Conversion: Glass shaders are converted to MeshPhysicalMaterial on the frontend with transmission, IOR, and thickness.

  5. Two-Pass Parameter Resolution: Raw params stored first, then resolved based on shader profile to handle parameter name variations.

External References

The Khada extractor is a downstream consumer of the LeagueToolkit format stack. For byte-level format specs, prefer the upstream wiki and cross-check against our vendored copies in packages/extractor/OfficialLeagueToolkit/ and packages/extractor/LeagueToolkit/:

  • LeagueToolkit wiki — canonical format reference (WAD, BIN, SKN, SKL, ANM, TEX, MapGeo, Ritobin, hashing, metaclasses)
  • LeagueToolkit (C#) — upstream library our OfficialLeagueToolkit/ vendors from
  • league-toolkit (Rust) — workspace of ltk_wad, ltk_meta, ltk_mesh, ltk_anim, ltk_texture, ltk_mapgeo
  • wadtools — Rust CLI for WAD extract/list/diff (useful when poking at a WAD without spinning up our extractor)
  • CommunityDragon — hashtable source (hashes.game.txt, hashes.binhashes.txt, hashes.lcu.txt) and CDTB toolchain

See file-formats.md for a per-format index with both wiki and local-source pointers.

Built for engineers and AI assistants working on Khada.