Decompilation of Castlevania: Symphony of the Night (PSX+Saturn)
Go to file
2023-01-22 18:34:27 +00:00
.github/workflows Fix progress reporting 2023-01-20 13:53:48 +00:00
.vscode Decouple DRA to the rest of the overlays 2023-01-22 15:42:03 +00:00
asm/main/psxsdk Upload disassembly 2022-01-02 13:35:44 +00:00
bin Undo broken commit 2023-01-20 23:44:24 +00:00
config func_800FE8B4 to HasEnoughMp 2023-01-22 16:02:11 +00:00
docs mostly renamings, one func (#33) 2023-01-02 08:57:53 +00:00
include func_8018F420 (thanks @mkst) 2023-01-22 18:24:43 +00:00
src Fix func_80132A04 signature 2023-01-22 18:34:12 +00:00
tools [Tool] sort by overlay first and function name (#65) 2023-01-21 13:16:45 +00:00
.clang-format Revert "Update clang-format" 2023-01-10 21:05:14 +00:00
.gitignore Do not require a password to install the pre-requirements 2022-11-05 14:15:41 +00:00
.gitmodules Decompile code with make 2022-10-15 11:45:20 +01:00
CNAME Create CNAME 2022-10-18 23:52:54 +01:00
diff_settings.py Force use of mipsel-linux-gnu-objdump in diff 2023-01-02 16:49:03 +00:00
go.work Add tool to extract the game image 2022-12-26 20:37:34 +00:00
Makefile Use compiler flags from PSY-Q CC1 2023-01-22 18:34:27 +00:00
README.md Decouple DRA to the rest of the overlays 2023-01-22 15:42:03 +00:00
slus00067.sha Add SEL overlay 2022-12-21 17:08:58 +00:00

Castlevania: Symphony of the Night Decompilation

A work-in-progress decompilation of Castlevania Symphony of the Night for PlayStation 1. It aims to recreate the source code from the existing binaries using static and/or dynamic analysis. The code compiles byte-for-byte to the same binaries of the game, effectively being a matching decompilation. Currently it only supports the US version of the game SLUS-00067.

This repo does not include any assets or assembly code necessary for compiling the binaries. A prior copy of the game is required to extract the required assets.

Bins decomp progress

SHA-1 checksum File name Progress
54828d4e44ea9575f2a0917ff63def42a304abff SLUS_000.67 N/A
2eac5f7162e77416166c2511c787995488f01c37 DRA.BIN progress DRA.BIN
d076912661e67a38afae0a1b5044ab5f10bcfb39 RIC.BIN progress RIC.BIN
42226b6d9ed24448eed61b3c6cd2949e96bebab6 ST/CEN.BIN progress CEN.BIN
e42976f45b47d1c4912a198ae486b77ee6d77c9c ST/DRE.BIN progress DRE.BIN
adb3303e1ea707c63dfa978511a88cab4f61970a ST/MAD.BIN progress MAD.BIN
5d06216b895ab5ff892c88b0d9eff67ff16e2bd1 ST/NO3.BIN progress NO3.BIN
7c78a2bec6a26acfb62456e7f517915fe0c0e3f5 ST/NP3.BIN progress NP3.BIN
b10b9c2be721cf9cbed3aa94be468ba9e23bc68b ST/NZ0.BIN progress NZ0.BIN
a919a53a760107972049bfdeadde33376428eace ST/SEL.BIN progress SEL.BIN
bc2fabbe5ef0d1288490b6f1ddbf11092a2c0c57 ST/ST0.BIN progress ST0.BIN
2ae313f4e394422e4c5f37a2d8e976e92f9e3cda ST/WRP.BIN progress WRP.BIN
3bbdd3b73f8f86cf5f6c88652e9e6452a7fb5992 ST/RWRP.BIN progress RWRP.BIN

Game internals

The game is divided into three modules:

  • SLUS_000.67 the main executable. It contains all the hardware API (eg. gamepad, CD, memory card, GPU renderer) of the PlayStation 1 console. It does not contain any game logic.
  • DRA the game engine. It contains the business logic (eg. gameloop, API to draw maps, entities, load levels, handle entities, animations and collisions) and some data such as Alucard's sprites or the loading/save rooms.
  • ST/ the overlays for each area. An area (eg. Castle's entrance, Alchemy Laboratory, etc.) contains all the unique logic to handle map specific events, cutscenes, enemy AI, collisions and more. It also contains the rooms and entities layout. Each overlay can be considered as its own mini-game. The title screen SEL.BIN is an example of how a stage overlay can act very differently.

Even if different overlays are loaded at the same time in memory, like DRA and stages, they never communicate each other directly. Instead they share the same memory area where SLUS_000.67 is located. Each overlay exposes their API as function pointers in the shared memory area, effectively allowing overlays to communicate without directly coupling them. One prime example is struct GameApi, which exposes DRA APIs to the stages and stage APIs to DRA. All the shared area is defined in game.h.

Setup the project

This assumes you have Ubuntu, Debian or WSL in Windows.

  1. Inside the folder of your choice git clone https://github.com/Xeeynamo/sotn-decomp.git
  2. Run sudo apt-get install -y $(cat tools/requirements-debian.txt)
  3. Run make update-dependencies
  4. Inside the newly created repo, create a new iso/ folder and put the game disc image in, both BIN and CUE files
  5. Rename the CUE file as sotn.cue
  6. Run make extract_sotn

Build

  1. Run make extract to generate the assembly files for the functions not yet decompiled.
  2. Run make all to compile the binaries in the build/ directory.
  3. Run make disk to create a new CUE/BIN pair based on the new compiled binaries.

In case there are any changes in the config/ folder, you might need to run make clean to reset the extraction.

Some non-matching functions are present in the source preprocessed by the macro NON_MATCHING. You can still compile the game binaries by running CPP_FLAGS=-DNON_MATCHING make. In theory they might be logically equivalent in-game, but I cannot promise that. Few of them could match by tuning or changing the compiler.

Start decompilation

  1. Run make clean extract all expected at least once
  2. After setup and build, choose an overlay (eg. ST/WRP)
  3. Look for one of those functions which hasn't successfully decompiled yet (eg. INCLUDE_ASM("asm/st/wrp/nonmatchings/6FD0", func_801873A0);)
  4. Run ./tools/decompile.py func_801873A0 to decompile the function in the C source code where the function is supposed to be located
  5. If the function does not compile, try addressing the compilation errors until make compiles
  6. If the function does not match, invoke python3 ./tools/asm-differ/diff.py -mwo --overlay st/wrp func_801873A0 and refactor the code until it matches
  7. If the function matches, try refactoring to keep the code clean while checking if the function still matches once in a while

There are a few tricks to make the process more streamlined:

  1. Use decomp.me with PSY-Q 4.0. Be aware that the repo is using GCC 2.6.x, so decomp.me will sometimes give a slightly different output.
  2. The “context” section of decomp.me, is provided by the cmd SOURCE=src/dra/42398.c make context.
  3. Use decomp-permuter to solve some mismatches.
  4. Use this and this guide to understand how some compiler patterns work.
  5. Use the #ifndef NON_MATCHING if your code is logically equivalent but you cannot yet fully match it.

Duplicate functions

Due to how the game is structured, a lot of duplicate code can be found across the different overlays. We track a live list of possible duplicate functions that is useful to avoid trying to decompile functions that have been already decompiled elsewhere.

Resources

To do

The project is very barebone at the moment and there is a massive room of improvement, mostly in the infrastructure:

  • Not all the zone overlays (ST/{ZONE}/{ZONE}.BIN) are disassembled
  • Integrate ASPSX instead of GNU AS
  • Split binary data (eg. map layout, graphics, other assets) into individual files

Notes

  • The debug room overlay ST/MAD.BIN was compiled earlier than the first retail release of the game. All the offsets that refers to DRA.BIN points to invalid portions of data or to the wrong API calls, effectively breaking the majority of its original functionalities. That is why the debug room does not contain any object. By compiling the debug room with make mad_fix you can restore it by redirecting the old pointers to the retail version of the game. Be aware that not all the offsets have been yet redirected, so it will still be not entirely functional until further update.
  • I suspect that GCC 2.6.x / PSY-Q 3.4 have been used to originally compile DRA.BIN
  • main.exe uses PS-X libraries that might have been created with a different compiler and with -O1 rather than -O2