Byrgenwerthnotes of Paleblood, rebuilding Bloodborne one verified function at a time

Last updated: October 7, 2026

Architecture

How Paleblood fits together: two tracks, decompilation and runtime, and how a function moves from original code to verified, readable C++.

At a glance

Your own pkgs base + 1.09 update prepare_dump.sh extract, merge, SELF to ELF, hash check Ghidra + GhidraOrbis eboot.elf: names and sizes into symbols/ Paleblood's loader map, relocate, bind every import Boot harness the entry on our runtime, to the first gap Verification harness original and replacement, same inputs Runtime libraries kernel, files, input, audio, video, GNM Readable C++ verified and independently reviewed The game runs on Paleblood once the runtime is far enough along

Paleblood is a decompilation with its own runtime. The decompilation rewrites the game's code, function by function, as readable C++ that is proved to behave like the original. The runtime is what that code, and the original executable while it is being replaced, runs on: Paleblood's own loader and its own versions of the PS4 system libraries. The game does not run on Paleblood yet; the Runtime page has the roadmap and how far the executable boots today.

Until October 2026 the project ran the original executable on a borrowed runtime, a fork of bbport, and recorded the inputs of its replacements there. That runtime is being removed; the recordings it produced stay in use for verification.

The decompilation track

A replacement lives in the code repository's game/<system>/ and is written to its style rules: the objects a function touches described as structures with named fields, the original's other functions and globals reached through named declarations instead of addresses, named constants, and no test plumbing inside the game's code. A function reaches the main branch only when it is both verified and readable, and an independent reviewer, not the author, has checked both.

Replacements are registered in a hook table. When the runtime runs the game, each registered function's entry will jump to its replacement, so every caller lands in the new code: a 14-byte jump, which is why a function shorter than that cannot be hooked this way.

The runtime track

Paleblood's loader maps the game's executable, together with the game's own C library and file system modules, applies every relocation and binds every system import, to one of those modules or to the runtime's own implementation. The boot harness then runs the executable's entry and stops at the first system function the runtime does not implement yet, naming it. Each step of the runtime's work implements what the boot stops at, written clean-room from public documentation and our own reverse engineering.

The frame pipeline

The first system decompiled. Solid boxes are verified C++; dashed boxes are still original code.

next frame frame_timing_frame_step 0x02418d20 · once per frame SprjWindow running? else the game ends frame_timing_flipper_init 0x02434520 · first frame: Game.FlipMode frame_timing_pace_frame 0x02434770 · the frame limiter wait out the frame gettimeofday spin (1/30 or 1/60 s) task update 0x024512a0 · the game's frame of work

Once per frame the frame step checks that the window is running, creates the SprjFlipper on the first frame (its constructor reads Game.FlipMode), lets the flipper's limiter wait out the frame, and runs the game's task update. The full walk-through is in How the Frame Limiter Works.

Verification

Earlier game runs inputs recorded while the game ran Edge-case generators what a recording does not reach Capture library captures//_NNNN.json harness.py our loader; original and replacement, same inputs verify.py compare returns, registers, bytes, calls Mutation test wrong variants must fail edge-verified, verified plus the independent review

A replacement is checked against the original outside the game. The harness maps the executable from your own dump with Paleblood's loader, scripts every library call the function makes, runs the original and the replacement on the same inputs, and compares return values, callee-saved registers, every byte written and every library call. The inputs are cases recorded in earlier game runs (the capture library) and edge cases generated for the corners. Deliberately wrong versions of the replacement must fail; if one passes, the cases are too weak and get fixed first. A function whose code no recording reaches yet is edge-verified; it becomes verified once recordings for it exist and pass, and the independent review has approved it.

Addresses

Every address in this wiki is a PS4 virtual address with the executable at 0x400000, the convention of the community patches. See Ghidra & PS4 Quirks.

What comes next

The queues live in the code repository's NEXT.md: one for the decompilation track, one for the runtime track.