Climax Silent Hill Engine Toolkit — a real-time 3D level viewer, asset decoder, and archive extraction toolkit for game levels and locations built on Climax Engine: Silent Hill Origins (PS2 / PSP), Silent Hill: Shattered Memories (Wii / PS2 / PSP), and Ghost Rider (PS2 / PSP).
Opens proprietary Climax Engine container files (no file extension — named like MO_1_Room102), decodes native PS2 and Wii GPU textures, and renders full level geometry, baked lighting, collision meshes, and placed game objects interactively. Decodes the game audio as well: the per-level sound banks, the music streams and the cutscene dialogue. Includes full archive unpackers for SH.ARC, data.arc, and igc.arc.
Toolkit 0.6 · Game pre-alpha 0.0.1.2 — two version numbers, because there are now two things here. The toolkit inspects the games. The game plays one: Travis walks the hospital under the series' own fixed cameras, opens doors between levels, and none of it is scripted by hand — every camera, trigger, spawn point and animation comes out of the retail data.
License: GPL-3.0 — free to use, study, change and share; derived work stays under the same license.
There are two ways in, because there are two programs.
sho-game is the port. Run it with no arguments and it boots the way the disc
does — the splash, the language and aspect screens, the logo movie, the main
menu, the intro clip, and then the first room:
sho-game [path/to/SH.ARC]
sho-game --level HO_1_Lobby # skip the front end, open one room
None of that sequence is invented. The order of the boot records, which screen
is skippable, the five flags and their order, the button mask the pad is read
through — all of it is transcribed out of SLES_551.47; docs/TODO.md §6c–6f
records where each piece came from.
ClimaxGameEngineToolkit is the viewer. Open SH.ARC, load a hospital level,
tick Walk (collision).
W A S D |
move — relative to the active camera, as the original is |
Shift |
run |
E |
use a door when the prompt appears |
M |
release the mouse |
F1 |
hide the interface |
[ ] |
step through the player's animations |
The camera reverses your direction when it cuts to a new angle. That is not a bug — it is what the game did, and it is preserved deliberately.
Nothing in this list is hand-authored; each is read out of the shipped data.
-
Spawning.
CPlayerSpawner— and the one you arrive on is named after the room you came from, so you step out of the right door facing the right way. -
Cameras. Position, field of view and aim from the placement matrix.
CStaticCameraholds its authored shot;CConstraintCamerafollows Travis, because 400 of its 417 instances carry no pitch at all and compute it at runtime. -
Camera cuts.
PlaneTriggernames one camera per crossing direction. -
Doors.
ZoneTriggernames the destination container, the event and the button. 418 of the 464 doorways in the game resolve to a matching spawn point on the other side. -
Collision. The level's own
CBSPmesh, with sliding and ground snapping. -
Travis. Model, textures, face, and his own animation set — idle, walk and run picked out by their authored filenames (
PC_TG_Walk.anm). -
The boot sequence and the menu. Splash, language, aspect, logo movie, main menu, intro clip — read out of the executable rather than guessed at, down to which screen can be skipped and which cannot.
Saves, inventory, combat, enemies, facial animation (rwID_DMORPHANIMATION — a
morph system the toolkit does not implement), and localisation beyond the string
table. Some trigger volumes are the wrong size, which needs the executable read
rather than guessed at.
The gameplay systems above the level — Travis's state machine, the monsters,
items — exist in SHO-port/ as working stand-ins rather than transcriptions,
and SHO-port/docs/ says for each one exactly what was recovered from the
retail build and what is a placeholder. That is the current front line of the
work.
- Mount
SH.ARCdirectly — browse and load levels by their real internal names (MO_1_Room102,TH_E_Lobby, …), with matching texture dictionaries pulled in automatically - Placed game objects — reads the
0x0704instance chunks, so spawners, cameras, pickups, lights and triggers appear at their real world coordinates instead of the origin - Correct model placement — models stored in
rwID_RWS/rwID_CLUMPsections are instanced wherever the game objects that reference them say, instead of piling up at 0,0,0 - Level cameras — jump to any of the fixed cameras the game cuts between
- glTF 2.0 export — one mesh per texture name, with embedded textures and level lights
- Shattered Memories (Wii) — mounts
data.arc/igc.arc, recurses into their nested archives, labels entries from their contents (the archive stores no names), and renders them: GX display-list geometry for levels, props and characters, plus the GameCube/Wii textures — CMPR, RGBA8, RGB5A3, I4/I8/IA4/IA8, RGB565 - Playback panel — sound, skeletal animation and UV animation in one window:
the level's own sound bank (footsteps, doors, room tone), the 75 music streams from
MUSIC/and the 35 cutscene tracks fromIGC.ARC, with seeking, looping and WAV export - Material blend modes from the asset — the
0x0A01material extension carries the mode the engine reads throughClimaxT1MaterialGetFrameBlendMode: standard alpha, additive, subtractive, andNONE, which writes the surface straight out and ignores alpha entirely. That last one is what character skin relies on - Animated fire — the
0x2Bsections hold RenderWare UV animations, and the flame materials name one. Two tiled sheets scroll past each other at different rates; without it a flame is a still rectangle - Parse and render SHO container files with embedded geometry, materials, and PS2 textures
- Decode native PS2 PSMT4 / PSMT8 / PSMCT32 texture formats via software unswizzle
- Eight render modes: Textured, Vertex Color, Flat Shaded, Normals, Depth, Checkerboard, Unlit
- Skeletal animation clips decoded — 3029 across the archive, with the bone table read from the HAnim plugin; evaluation and skinning are not wired up yet
- Collision mesh overlay with optional semi-transparent fill
- Clump object markers (octahedron wireframe + projected labels)
- ImGuizmo translate gizmo for interactive pivot repositioning
- Orbit sphere gizmo for camera rotation with SDL cursor capture
- Structured hierarchy browser with section tree and material inspection
- Texture atlas browser with per-texture metadata
- File browser for loading levels at runtime
An "A2.0" archive: a 20-byte header, a 16-byte record per file, and a name table
that occupies the tail of the file. Payloads are raw zlib streams, except where
uncompressedSize is zero — those are stored uncompressed, which is how all 35
cutscene entries of IGC.ARC are written.
Header (20 bytes)
0x00 char magic[4] "A2.0"
0x04 u32 entryCount
0x08 u32 firstDataOffset
0x0C u32 nameTableOffset (== end of the payloads)
0x10 u32 nameTableSize (nameTableOffset + nameTableSize == file size)
Entry (16 bytes, entryCount of them, from 0x14)
0x00 u32 nameOffset byte offset into the name table
0x04 u32 offset absolute, 0x20-aligned
0x08 u32 compressedSize zlib stream length
0x0C u32 uncompressedSize
Verified against the retail PS2 SH.ARC: all 1487 entries inflate to exactly the
declared size, offsets are monotonic and aligned, and the name table ends precisely
at EOF. Reader: src/Arc.cpp.
A chunked binary format. A top-level 0x071C directory block lists every game-object
type and how many instances exist, followed by a flat chunk list:
| Chunk | Description |
|---|---|
0x0716 |
Named resource section (WORLD, CBSP, WAVEDICT, AINAVMESH…) |
0x0704 |
A placed game-object instance |
A 0x0716 section header is
[count][tagLen][tag][guid(16)][nameLen][name] followed by two build-path strings.
A 0x0704 body is a flat list of tagged records — [u32 size][u32 id][payload] —
where the top byte of id selects the kind (0x20 class name, 0x40 GUID,
0x80 instance name, 0x00 indexed property). Property 1 is a 64-byte
column-major 4×4 world matrix — the object's placement. Names are 0xBF-padded.
Property indices restart at 0 for each component of an object, so a group boundary
is simply "the index stopped increasing".
A 16-byte property is the GUID of a 0x0716 section the object owns. That is how
geometry gets placed: rwID_WORLD sections are already in level space, but
rwID_RWS and rwID_CLUMP sections are re-usable models, and the object holding
the reference supplies the transform. The same model is often referenced several
times — in IntroRoad one RWS model is instanced at four different spots.
CColorLight carries an RGBA colour in group 0 property 0, and
[type][cone angle°][range][enabled] in group 1.
Cross-checked over the whole retail archive: 254 of 255 containers parse to exactly
the object count their own type directory declares (11 834 objects, 7 065 of them
placed). The single outlier declares a CWiiStndController that the PS2 build does
not ship.
Sections that may appear inside a 0x0716:
| Block type | Description |
|---|---|
| CLUMP | RenderWare-style geometry tree |
| CBSP | Collision BSP mesh |
| TEXDICTION | Texture dictionary (TXD) |
| BINMESH | Pre-indexed triangle lists |
| WAVEDICT | The level's sound bank |
Four containers, two codecs. rwaID_WAVEDICT sections hold the level's own sound
bank — 2980 named samples across the 255 retail containers, all mono Sony 4-bit
ADPCM at 6–32 kHz. MUSIC/*.RWS are RenderWare Audio streams, also mono ADPCM,
with the rate and channel count in the 0x080E header and the data at a fixed
offset of 2048. IGC.ARC/*.IGCStream multiplexes 1024-byte pieces of a 48 kHz
16-bit stereo ADS into a record stream, tagged 0xA000; concatenating them
reassembles the ADS exactly. Decoder: src/Core/AudioParser.cpp.
See docs/ANIMATION_SPEC.md for the animation plan: skeleton, skinning, compressed keyframes and the playback UI.
See docs/EXECUTABLES.md for what the game binaries themselves give up: section layout, the engine's full tag list, and the recovered class registry.
See docs/TODO.md for the current work queue: what is broken, what is unimplemented, and what is already known about each.
See docs/SHSM_ARC_FORMAT.md for the Wii Shattered
Memories archives and containers: the 0x0000FA10 archive, the big-endian
section and game-object records, and the GameCube/Wii texture formats.
See docs/SH_FORMAT.md for the full format reference: archive layout, container chunks, the game-object record encoding, the PS2 display-list geometry, collision and texture formats, with the verification figures behind each claim.
The container parser lives in src/Loader.cpp; the PS2 texture decoder is in src/PS2Texture.cpp.
Builds on Linux, macOS and Windows; every push is built on all three by GitHub Actions, which also uploads a ready binary per platform as a workflow artifact.
| Dependency | Notes |
|---|---|
| CMake 3.16 | 3.21+ on Windows to auto-copy the DLLs |
| GCC / Clang / MSVC (C++17) | |
| OpenGL 3.3 | core profile |
| GLEW | system package |
| SDL2 | system package |
| GLM | header-only; fetched automatically if missing |
ImGui and ImGuizmo are always downloaded during the CMake configure step.
# Debian / Ubuntu
sudo apt install build-essential cmake pkg-config libsdl2-dev libglew-dev libglm-dev# macOS (Homebrew)
brew install cmake sdl2 glew glm# Windows (vcpkg — reads the dependency list from vcpkg.json)
vcpkg install --triplet x64-windowscmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release --parallelOn Windows add the vcpkg toolchain to the configure step:
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmakeThen run it — the TXD list is optional, textures may be embedded in the container:
./build/ClimaxGameEngineToolkit path/to/MO_1_Room102 [texture.txd ...]make -j$(nproc)
./ClimaxGameEngineToolkit path/to/MO_1_Room102 [texture.txd ...]Preferred — point it at the game archive and pick levels by name:
ClimaxGameEngineToolkit path/to/SH.ARC [LevelName]
Without a level name the viewer just mounts the archive; use the Archive panel
to browse the 269 level containers by name. Selecting one loads it together with
every .txd the archive names after it (the game names shared dictionaries after
the rooms they bridge, e.g. MO_1_Room102-MO_1_PoolArea.txd).
Loose, already-extracted files still work:
ClimaxGameEngineToolkit path/to/MO_1_Room102 [txd1 txd2 ...]
Export a level to glTF without opening the window:
ClimaxGameEngineToolkit path/to/SH.ARC MO_1_Room102 --export room.glb
Container files have no extension — MO_1_Room102, MO_1_Courtyard, etc.
Textures can be embedded in the container or supplied as separate TXD archives.
Press F2 in the application for the full manual.
| Action | Input |
|---|---|
| Orbit camera | Right-click drag / orbit sphere (top-right) |
| Zoom | Scroll wheel (proportional to distance) |
| Move pivot | Gizmo arrows / plane handles — follows the cursor 1:1 |
| Snap pivot while moving | Hold Ctrl (or tick Snap) |
| Jump to a level camera | Jump to camera dropdown |
| Reset camera | 1 |
| Hide / show the UI | F1 |
| Manual | F2 |
| Hide / show the gizmo | G |
| Play a sound | Click it in the Audio panel |
| Show / hide the Audio panel | Panels → Audio |
The gizmo, the orbit sphere and the camera never fight over the mouse: a hovered gizmo does not block wheel-zoom or right-drag orbit, only one of them can claim a left-button drag at a time, and the gizmo only appears once a level is loaded.
| Mode | Description |
|---|---|
| Textured | Albedo texture multiplied by vertex color |
| Vert. Color | Vertex color only, no texture |
| Flat | Per-face normal shading via dFdx/dFdy |
| Normals | Face normals visualised as RGB |
| Depth | Linear depth in greyscale |
| Checker | UV checkerboard (8x8 tiles) |
| Unlit | Texture sampled without any color modulation |
The build produces two executables and three libraries. The boundary is
enforced by the linker: climax-core and climax-game have no GL/SDL/ImGui
symbols, so any accidental include causes a link error instead of a silent
violation.
climax-core (static library — no GL, no SDL, no ImGui)
src/Core/RWS/FileSystem/CArchive.cpp — SH.ARC / IGC.ARC reader
src/Core/UI/StringTable.cpp — UI string hash table
src/Core/UI/Font.cpp — PS2 KFont rasteriser → texture atlas
src/Core/UI/ScreenDef.cpp — XML screen parser (mainmenu, newgame…)
src/Platform/PS2/PS2Texture.cpp — PS2 VRAM / GS format decoder (CPU)
src/Platform/PS2/AudioParser.cpp — VAG / ADS / RWS / IGC audio decoders
src/Platform/Wii/WiiGeometry.cpp — GameCube/Wii display-list geometry
climax-game (static library — no GL, no SDL, no ImGui; links climax-core)
src/Game/FrontEnd.cpp — boot sequence state machine
src/Game/CameraLinks.cpp — fixed-camera selection & cutting
src/Game/CharacterController.cpp — player movement, collision, doors
src/Game/ZoneLinks.cpp — level-to-level transition graph
ClimaxGameEngineToolkit (executable — SDL + GL + ImGui; links climax-game)
src/main.cpp — render loop, GL shaders, input
src/UI/UI.cpp — inspector panels, audio player, file browser
src/Loader/Loader.cpp — container parser, geometry upload
src/Loader/GeometryDecoder.cpp — RW geometry chunk decoder
src/Loader/ResourceLoader.cpp — texture / model resource cache
src/Loader/Export.cpp — glTF 2.0 / GLB writer
src/SG/SceneObject.cpp — scene-graph node
src/Core/Common.cpp — ViewerState globals (toolkit only)
src/Platform/PS2/RwsAudio.cpp — SDL audio device, voice mixer
src/Platform/Wii/WiiTexture.cpp — GX texture decoder (GPU upload)
src/Rendering/… — GL/Metal backend, GPU mesh, player model
sho-port (static library — no GL, no SDL, no ImGui; links climax-game)
SHO-port/src/Actor/ — Travis, the monsters
SHO-port/src/Combat/, Inventory/ — weapons, items
SHO-port/src/Triggers/, Puzzle/ — level logic and the ten puzzles
SHO-port/src/World/, Camera/, Audio/ — world, cameras, sound
SHO-port/include/SHO/Core/EventIds.h — the 206 message names, generated
sho-game (executable — the game; SDL + GL; links sho-port)
src/play_main.cpp — the front end: boot, menu, intro
SHO-port/src/Main.cpp — the level, from the handover onward
src/Rendering/VideoPlayer.cpp — FFmpeg-based FMV player (optional)
vendor/ (Makefile build only — CMake fetches into build/_deps/)
imgui/
imguizmo/
GNU General Public License v3.0 — Copyright (c) 2025-2026 Blackline Interactive.
You may use, study, change and share this program. If you distribute it, or anything derived from it, that work must carry the same license and its source must be available.

















