Phase 0: scaffolding
Phase 0 is done on the code side. There is a Rust workspace in client/ with three crates. Nothing plays, and I have not yet run any of it against the real Clementine client. This post covers what exists and what I do next.
The three crates
psu-inspect is a command line tool that reads PSUC.exe and PSUEx.dll and writes a report. It prints the SHA-256 of each file and a summary of the executable (image base, entry point, sections). It counts imported functions per DLL, checks whether the file is packed with UPX, and looks for hints of thread-local storage. It writes scope-report.md and imports.json. It only reads files and never runs the game.
The packing check matters because a packed executable only shows the packer's own imports. If PSUC.exe is packed, I unpack a copy with upx -d and run the tool again. The thread-local storage answer is marked inconclusive until then.
To test without any game data, the tool can build a tiny synthetic install, a hand-made PE file with no game content in it. I use that in the tests.
psu-runtime is the start of the runtime library. It does three things so far.
- It reserves a 4 GiB window of address space with
mmap, set to no access, so guest 32-bit addresses map to host memory by adding a base. - It maps the PE headers and sections at their original addresses (image base plus RVA).
- It keeps an import registry. Every imported function gets a stub address. When the code calls a stub, the registry logs the call as
dll!name(args). Functions that get called but have no implementation form my work queue.
One thing surprised me. Apple Silicon uses 16 KiB memory pages, and PE sections are protected in 4 KiB units. I can't enforce the original per-section permissions, so the image is mapped read-write for now.
psu-launcher is a placeholder. It prints the planned flow and does no networking. The real version will replace the .NET online.exe: patch, check the hash, lift again if the executable changed, then launch.
Tests
cargo test runs 16 tests, and all of them pass. They cover the PE parsing, the synthetic installs (plain and UPX-style), the guest memory window and the import registry.
What's next
The next step is to run psu-inspect on the real client. I will register an account, download the client and unzip it. Then I copy it, so the original is never touched, and point the tool at the copy.
The report tells me how many imports I have to implement, whether the executable is packed, and whether TLS is a problem. After that I can start the lifter, which turns the x86 code into C, and let imports.json decide what the runtime builds first.
I'll write up the results here, including whatever goes wrong.