Skip to content
LBPTrackerPublic

About

Reading LittleBigPlanet's serialised resources, in TypeScript, with no dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

@lbptracker/cwlib

Reading LittleBigPlanet's serialised resources, in TypeScript, with no dependencies. Named after ennuo's cwlib, which is the reference this was checked against rather than ported from.

import { readThings, musicSequencers } from '@lbptracker/cwlib/things/level.ts';
import { nodeInflate } from '@lbptracker/cwlib/platform/node.ts';

const level = await readThings(bytes, nodeInflate);
console.log(level.things.length, musicSequencers(level.things).length);

What it reads

Every module is imported by its path under src/: @lbptracker/cwlib/<folder>/<module>.ts.

io/
resource.ts the LVLb / PLNb / … container: revision, branch, the zlib chunk table, the dependency table
stream.ts, serializer.ts the big-endian readers: the fixed-width header one, and the payload's with its revision gates and compressed integers
text-resource.ts the engine's text serialisation (GSBt, DLCt, MUSt, SSPt, … 19 magics): tab-indented fields, arrays of [i], read typed or as a tree
writer.ts, hex.ts the payload writer and the container around it; SHA-1s as hex
tape.ts a stream kept as read — bytes cut at every reference — so it can be written back and edited
json.ts, from-json.ts any reader's result as JSON text and back: typed arrays, maps, big integers, NaN, a Thing graph's cycles, and what a reader keeps out of sight
png.ts RGBA8 pixels as a PNG, for an export that carries images
archives/
game.ts one install opened: a GUID or SHA-1 to bytes, through the FileDB, the remap, the FARCs and the loose files; several stacked, an update over the game
gather.ts everything a resource needs: its dependency tables walked through a resolver the caller injects
install.ts resources added to an install: a FARC appended to, a FileDB's rows added, patched and removed — cwlib's ResourceSystem saving
hashinate.ts a resource's dependencies rewritten where its reader read them, and cwlib's hashinate: GUIDs turned to the SHA-1s of what they name
farc.ts, filedb.ts the game's FARC archives (SHA-1 → bytes, read lazily, and written), its FileDB (GUID → path, SHA-1; read and written) and its GUID remap
savearchive.ts PS3 FAR4 saves, XXTEA with a constant key; the same archive in the clear, read and written
legacy-mod.ts, big-save.ts the one-file MODb/MODe mod, and a save rooted at an RBigProfile listed by path — ported from Java, proven on no file
mod.ts, zip.ts, psf.ts Craftworld Toolkit .mod packages (with their icon and translation patches), a stored/deflated ZIP reader and writer, PARAM.SFO
imports/
model-importer.ts, animation-importer.ts, static-mesh-builder.ts, blend-packet-importer.ts cwlib's importers: a glTF to an RMesh or an RAnimation, Things baked into an RStaticMesh, a Blender packet to a mesh — Java's bytes, with JOML, HashSet order and NvTriStrip ported
things/
thing.ts, parts.ts, parts/ the Thing graph and its part readers, one file per part — partReaders().size is the number (53 as of 2026-10-24)
level.ts the walk: worlds, plans, streamed CHKb chunks and islands; readThings picks one by magic
write-parts.ts every part's writer, beside its reader: a part written from its value, byte for byte on the game's 3.3 million
encode-things.ts a level's or a plan's whole payload from its values: the world, its Things, the tail
write-level.ts a level, plan or streaming chunk written back byte for byte, with Things removed, added from another file or moved
resources/
mesh.ts RMesh (MSHb): positions, normals, tangents (and their handedness from the UVs, uvHandedness), the softbody weight, every UV channel, joints and weights, a triangle list and a material per primitive, the bones
texture-encode.ts, texture-encode-bc.ts an RGBA image to a GTF or TEX : scaled, mipmapped and compressed to DXT1/3/5, as cwlib's toGTF/toTEX
texture.ts GTF and TEX textures: every mip and face as BC blocks or unswizzled RGBA8, ready for WebGPU, and a pure-TypeScript BC decode
material.ts RGfxMaterial (GMTb): the whole box-and-wire graph, the texture slots, and materialSummary for a renderer to start from
staticmesh-gltf.ts, gltf-builder.ts an RStaticMesh written as glTF; the buffer, materials and container every glTF export shares
animation-gltf.ts an RAnimation written as a glTF animation, alone or on its mesh
mesh-obj.ts an RMesh written as Wavefront OBJ, and its softbody springs and clusters
mesh-gltf.ts, gltf-material.ts an RMesh written as a binary glTF: geometry, skin and morphs, its materials with their textures as PNG
bevel.ts, staticmesh.ts RBevel (BEVb), the profile a shape's edge is swept with; RStaticMesh (SMHb), a level's backdrop
physics-material.ts RMaterial (MATb): a shape's physics — density, friction, bounce, its sound family, what it collides with, whether it breaks
palette.ts, outfit-list.ts RPalette (PALb), a page of the popit; ROutfitList (OUTb), costumes as their pieces
slot-list.ts, packs.ts RSlotList (SLTb) and RPacks (PCKb): slots — a level's id, root, name, author, labels — and the DLC packs that hold them
joint-settings.ts, pins.ts, cached-costume-data.ts RJoint (JNTb), what a kind of joint does; RPins (PINb), the pin definitions; RCachedCostumeData (CCDb), the costume index
animation.ts RAnimation (ANMb): a skeletal animation — the bones, then per frame the rotations, positions, scales and morph weights of the bones that move, unpacked to floats
script.ts RScript (FSHb): a compiled Fish class — its type, field, property and function tables, the 64-bit instructions, the string and constant pools
script-instructions.ts an RScript's instructions decoded: each word's opcode and its class's fields
bone-set.ts RBoneSet (BSTb): the toolkit's own format, the playable characters' skeletons as its model importer keeps them
quest.ts RQuest (QSTb): a story quest — its texts, prize, objectives, the slots that activate and complete it, its key; a reading of our own
font-face.ts RFontFace (FNTb, loose in gamedata/fonts/): a bitmap font — the glyph table, its lookup chains, each glyph's bitmap
guid-subst.ts, dlc.ts RGuidSubst (GSBt) and RDLC (DLCt) in the game's text form: GUID swaps (a language's dialogue, a character's animations) and the DLC packs' GUIDs and files
synced-profile.ts RSyncedProfile (PRFb, in a save): time played, the Sackboy the player wears, the creature avatars, the colours, the PSN id
adventure-create-profile.ts RAdventureCreateProfile (ADCb): an adventure's level slots, its planet and areas as world Things, the links between levels
adventure-shared-data.ts the adventure shared data (ADSb): one AdventureData — the adventure's items, quests and start points
animated-texture.ts, guid-list.ts, voip-recording.ts ATXb (a flipbook: its atlas and frame size), GLTb (a list of GUIDs), VOPb (a recorded voice, as bytes)
music-settings.ts MUSt: interactive music settings, a track's stems and how they fade (the 01.28 update's)
big-profile.ts, local-profile.ts RBigProfile (BPRb) and RLocalProfile (IPRb): a save's inventory and slots, a player's settings and history — ported from Java, proven on no file
tutorials.ts RTutorials (TUTb): the tutorial videos by group, each with its title, description and thumbnail
thing-recording.ts RThingRecording (RECb): a Thing's recorded path, per frame a position, an angle and a flag, as the game's loader reads it — the newest packed into a bit stream, decoded
translation.ts RTranslationTable (.trans, no magic): the game's text by LAMS key, one table per language, and the tag-to-key hash; read and written
instrument.ts RInstrument (INSb): the sampler patch — slots, sample GUIDs, key splits, the 27 parameters, the arpeggiator
structs/
slot.ts, network.ts, inventory.ts the structs more than one resource or part reads: a slot, a PSN identity, an item's inventory details
sequencer/
project.ts, notes.ts, chips.ts the music sequencer as data — note records, chaining, automation, the chips' plans and colours
write-plan.ts a sequencer written back out as a .plan
backup.ts the pile: a folder, a zip of one, or a save, in and sequencers out
platform/ the inflate adapters and the FARC's file access, Node and browser

Two things to know before using it

❗ A level is told from everything else by its first four bytes — LVLb or PLNb — and never by its name. A backup is full of ICON0.PNG, PARAM.SFO, costumes and photographs.

⚠️ Platform APIs are injected, never imported. loadResource(bytes, inflate) takes its inflater as an argument, which is what lets the same parser run under node --test and in a browser tab against a file the user picked. Follow that pattern for anything else platform-shaped.

Scope

LBP3_MIN_VERSION in serializer.ts refuses anything older than 0x3b7. The readers themselves go much further down — all 19 LBP1 files in the archive sample parse Thing-for-Thing identically to cwlib with the bound lowered by hand — but the range between 0x272 and 0x3b7 has no test coverage at all, and the bound is what keeps "we do not support this" from becoming "we read it and produced something". See 28 in steering/open-questions.md.

Verified against 22 real levels and, for the Thing walk, against a cwlib dump of 129,696 sequencer rows.

Building, testing, depending on it

This was packages/cwlib-ts in lbptracker until 0.2.48. What is known about the formats, and how each fact was measured, is in steering/ — start with CLAUDE.md, which indexes it.

  • npm run check — typecheck, then node --test on the .ts sources directly.
  • npm run build — tsc -p tsconfig.build.json into dist/: .js, .d.ts and maps. prepare runs it, so a git dependency builds itself on install.

⚠️ Consumers get dist/, not src/. Node refuses to strip types from anything under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), and a git dependency is a real copy there, not a symlink. exports maps @lbptracker/cwlib/<name>.ts to dist/<name>.js, so the import specifiers are the same either way.

The corpus tests read lbptracker's fixtures/: LBP_FIXTURES points at it, and the default is an lbptracker checkout next to this one; LBP_PLANS overrides its gallery plans. LBP_LEVELS (a toolkit checkout's tools/sequencerdump) and LBP_GAME (an installed LBP3 PS4 build), LBP_PATCH (its update's folder) have no default: the tests that need them skip without them. steering/tools.md has every variable.

About

Reading LittleBigPlanet's serialised resources, in TypeScript, with no dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages