Skip to content

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

FormatPurposeOur parser
WADArchive wrapping every game assetOfficialLeagueToolkit/Core/Wad/WadFile.cs; wrapped by LeagueConvert/IO/WadFile/StringWad.cs
BIN (PROP / PTCH)Typed property container — dominant config formatOfficialLeagueToolkit/Core/Meta/BinTree.cs + LeagueConvert/IO/PropertyBin/
SKNSkinned meshOfficialLeagueToolkit/Core/Mesh/SkinnedMesh.csLeagueConvert/IO/Skin/Skin.cs
SKLSkeletonLeagueToolkit/IO/SkeletonFile/Skeleton.cs (our fork with the track-mapping logic)
ANMAnimationLeagueToolkit/IO/AnimationFile/Animation.cs (our fork)
TEXNative textureOfficialLeagueToolkit/Core/Renderer/Texture.cs
RitobinText form of BINOfficialLeagueToolkit/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.cs clears 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 mEventDataMap is a BIN map keyed by metaclass hash. Hash 3049371309 = JointSnapEventData — that's what drives the joint-snap bake (see skeletons & animations).
  • Shader params show up as hex keys in --dump-materials when the property name isn't in hashes.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, in Influences) and head (lowercase stub parented to root, bind translation canceling the root), the two hash to the same value. The two-pass match in MapAllTracksInOrder is the reason it works. Don't collapse it into one pass — see the Shaco case study if you're tempted.
  • SkeletonJoint.GlobalTransform and InverseBindTransform are coupled — setting one recomputes the other. Don't hand-roll either side.

ANM

  • Uncompressed r3d2anmd readers used to leave Duration = 0 (only FrameDuration was set). We compute Duration = frameCount × FrameDuration explicitly now. The joint-snap bake silently skips animations with Duration == 0.
  • One .anm can be referenced by multiple clip names (Garen's Recall and Recall_Base share a file). When rewriting tracks in a bake, dedupe by Animation instance or you'll mutate twice.
  • mStartFrame / mEndFrame in JointSnapEventData are in 30 Hz ticks. Our bake samples at 60 Hz. Keep SnapFrameRate = 30f / SnapSampleRate = 60f in sync with that.

TEX

  • The engine also accepts DDS. Don't assume every texture is .tex.

See also

Built for engineers and AI assistants working on Khada.