League File Formats
Byte-level specs live on the LeagueToolkit wiki — that's the authoritative source and the one that keeps up with Riot's version bumps. This page is a local lookup: which file is parsed by what in our tree, and what bites us in practice.
If you want to decode a real skin's contents without running anything, fetch the CDragon bin.json instead of reading raw bytes.
Parser map
| Format | Purpose | Our parser |
|---|---|---|
| WAD | Archive wrapping every game asset | OfficialLeagueToolkit/Core/Wad/WadFile.cs; wrapped by LeagueConvert/IO/WadFile/StringWad.cs |
| BIN (PROP / PTCH) | Typed property container — dominant config format | OfficialLeagueToolkit/Core/Meta/BinTree.cs + LeagueConvert/IO/PropertyBin/ |
| SKN | Skinned mesh | OfficialLeagueToolkit/Core/Mesh/SkinnedMesh.cs → LeagueConvert/IO/Skin/Skin.cs |
| SKL | Skeleton | LeagueToolkit/IO/SkeletonFile/Skeleton.cs (our fork with the track-mapping logic) |
| ANM | Animation | LeagueToolkit/IO/AnimationFile/Animation.cs (our fork) |
| TEX | Native texture | OfficialLeagueToolkit/Core/Renderer/Texture.cs |
| Ritobin | Text form of BIN | OfficialLeagueToolkit/Toolkit/Ritobin/RitobinWriter.cs — used by --dump-materials |
We don't currently parse MapGeo, WGEO, SCB/SCO, or the various legacy formats (INIBIN, AiMesh, MOB, etc.). If you need to, the upstream library has them — see legacy formats.
What bites us
WAD
- PBE WADs change daily.
WadDownloader.csclears the PBE cache before each download — don't add blanket caching on top. - Our old docs listed only three compression types. The full set is
None / GZip / Satellite / Zstd / ZstdChunked. Satellite is rare but valid — data lives in a separate file, not inline. - Unresolved chunk paths render as hex (
0xDEADBEEFCAFEBABE). Log and skip; don't crash.
BIN
- Animation clip
mEventDataMapis a BIN map keyed by metaclass hash. Hash3049371309=JointSnapEventData— that's what drives the joint-snap bake (see skeletons & animations). - Shader params show up as hex keys in
--dump-materialswhen the property name isn't inhashes.binhashes.txt. Nothing's broken — the dictionary is just incomplete for that name.
SKN
- v0.1 skins carry a single implicit submesh called
"Base". Don't assume multi-submesh when walking ranges. - A vertex's bone index is not a joint index. It's an index into the skeleton's influence list, which in turn holds the joint index.
SKL
- ELF hash is case-insensitive. When a skeleton carries both
Head(real, inInfluences) andhead(lowercase stub parented to root, bind translation canceling the root), the two hash to the same value. The two-pass match inMapAllTracksInOrderis the reason it works. Don't collapse it into one pass — see the Shaco case study if you're tempted. SkeletonJoint.GlobalTransformandInverseBindTransformare coupled — setting one recomputes the other. Don't hand-roll either side.
ANM
- Uncompressed
r3d2anmdreaders used to leaveDuration = 0(onlyFrameDurationwas set). We computeDuration = frameCount × FrameDurationexplicitly now. The joint-snap bake silently skips animations withDuration == 0. - One
.anmcan be referenced by multiple clip names (Garen'sRecallandRecall_Baseshare a file). When rewriting tracks in a bake, dedupe byAnimationinstance or you'll mutate twice. mStartFrame/mEndFrameinJointSnapEventDataare in 30 Hz ticks. Our bake samples at 60 Hz. KeepSnapFrameRate = 30f/SnapSampleRate = 60fin sync with that.
TEX
- The engine also accepts DDS. Don't assume every texture is
.tex.
See also
- Hashing algorithms — which hash family applies where.
- CDragon bin.json — decoded per-skin data without running the extractor.
- Skeletons & animations — the heaviest format-handling code in the repo.
- LeagueToolkit wiki index — curated tour of the upstream docs.