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 resolutionProgram.cs- Main entry point withconvert-wadandconvert-allcommandsWadDownloader.cs- Downloads WAD files from Riot CDN using CDTB (CommunityDragon Toolbox)LeaguePathResolver.cs- Auto-detects League installation or downloads WADs
LeagueConvert/- Main conversion logicIO/Skin/- Skin data parsing and conversionStaticMaterial.cs- Shader parameter resolution and profilesSkin.cs- Skin data managementExtensions/SkinExtensions.cs- GLTF export logic
IO/WadFile/StringWad.cs- WAD file parsing with hash table lookupIO/HashTables/HashTables.cs- Hash table management (Game and BinHashes)
SimpleGltf/- GLTF format implementationJson/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 byxxHash64of 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 toNone; other file types default toZstd. - Hash Tables: Required for converting file hashes to readable paths:
hashes.game.txt— maps xxHash64 of WAD chunk paths (lowercased,/-normalized) → path string. Used byStringWadand the WAD toolchain.hashes.binhashes.txt— maps FNV-1a 32-bit hashes (of lowercased names) → BIN property/class/object names. Used when resolving.binproperty 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 handlinghandleFlipbook()- Animation-synced flipbook handlingupdateAutoFlipbooks()- Time-based flipbook animationsinitializeModel()- 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
MeshStandardMaterialtoMeshPhysicalMaterialwith 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
↓
RenderExtractor Workflow Details
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-downloadnot set → Download via CDTB - Cache location:
~/.khada-extractor/cache/Game/
- Check local League installation (
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
WAD Parsing:
- Uses
OfficialLeagueToolkitto read WAD structure - Resolves hashed file paths using
HashTables.Game - Extracts skin entries matching pattern:
data/characters/{champion}/skins/{skinName}/
- Uses
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
- Parse Phase: All shader parameters stored in
RawParamsdictionary - 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
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
flipbookAccessoryHashkeyframes - Time-based: Driven by
flipbookSpeed(FPS)
- Animation-synced: Driven by
Bloom/Emissive Shaders
- Have
BloomStrength> 0 - Color params are for emissive, NOT base tint
- Example:
Shaders/SkinnedMesh/PanBloom
Critical Design Decisions
No Generic Color Tinting: Only explicit shader profiles can apply color tints. Prevents VFX colors from being misapplied as base tints.
Texture Cloning: All texture modifications (repeat, offset) must clone the texture first to avoid cascading changes.
Procedural Shader Placeholders: Procedural shaders get 1x1 placeholder textures generated from color parameters.
Glass Material Conversion: Glass shaders are converted to
MeshPhysicalMaterialon the frontend with transmission, IOR, and thickness.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 ourOfficialLeagueToolkit/vendors fromleague-toolkit(Rust) — workspace ofltk_wad,ltk_meta,ltk_mesh,ltk_anim,ltk_texture,ltk_mapgeowadtools— 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.