Home > Software > TDR: Soul Reaver
TDR: Soul Reaver
This is a basic walkthrough of decompiling a debug build of Soul Reaver using Ghidra and This Dust Remembers What It Once Was.
TDR was developed and tested with debug builds of Soul Reaver, so this walkthrough will assume you have one of those. I used the one with a build date of 1999-06-01, but any prototype version should work as long as it includes the .SYM file. Important: some of the European retail versions of Soul Reaver include .SYM files, but I don't think they actually match up with the game binary, as they're much older. Those probably won't work.
You'll probably want to copy all of the TDR binaries and DLLs to C:\Windows for convenience, or at least add their location to your PATH environment variable.
PlayStationELFConverter.exe --exe2elf KAIN2.EXE KAIN2.ELF > PlayStationELFConverter_Log.txt 2>&1
SymDumpTE.exe --rename-for-compatibility --json KAIN2.SYM KAIN2.json > SymDumpTE_Log.txt 2>&1
CreateSkeleton.exe --create-playstation-memory --assume-sn-gp-base --name KAIN2 --externs-to-labels --output Output KAIN2.json > CreateSkeleton_Log.txt 2>&1
PopulateSkeleton.exe --name KAIN2 --input-json KAIN2.json --input-source Output\KAIN2.C --output Output > PopulateSkeleton_Log.txt 2>&1
The one required change for the Soul Reaver files (mentioned above):
As discussed above, there are lots of changes that would be good to make, but one is absolutely required for Soul Reaver, because if you don't, Ghidra won't be able to parse the C header file.
Open KAIN2.json and search for "name": "_walbossAttributes". You should find a section that looks like this:
"UsedByFunctions": [],
"struct_member_signature": "struct _253fake attackDeltas[0]; // size=0, offset=24",
"class_type": "struct_member",
"c_type": "struct _253fake[0]",
"type_name": "struct _253fake[0]",
"size": 0,
"offset": 24,
"parent_name": "struct _walbossAttributes",
"parent_hashcode": 0,
"name": "attackDeltas",
"source_file": null,
"hashcode": 0
}
],
"name": "_walbossAttributes",
"source_file": null,
"hashcode": -1378913783
There should be three instances of the text struct _253fake, or similar depending on which build of Soul Reaver you're looking at (struct .255fake, etc.) Change all three instances of the struct name so that they read struct _wba253fake. There's nothing special about this replacement name, it just needs to be unique because there's another struct or union with the same name elsewhere in the debug symbols.
When you're done, the section should look something like this:
"UsedByFunctions": [],
"struct_member_signature": "struct _wba253fake attackDeltas[0]; // size=0, offset=24",
"class_type": "struct_member",
"c_type": "struct _wba253fake[0]",
"type_name": "struct _wba253fake[0]",
"size": 0,
"offset": 24,
"parent_name": "struct _walbossAttributes",
"parent_hashcode": 0,
"name": "attackDeltas",
"source_file": null,
"hashcode": 0
}
],
"name": "_walbossAttributes",
"source_file": null,
"hashcode": -1378913783
Scroll up just past the beginning of the _walbossAttributes struct definition, and you should find the definition of the _253fake struct that it references. It should look something like this:
{
"UsedByFunctions": [],
"members": [
{
"UsedByFunctions": [],
"struct_member_signature": "short plusDelta; // size=0, offset=0",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 0,
"parent_name": "struct _253fake",
"parent_hashcode": 0,
"name": "plusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short minusDelta; // size=0, offset=2",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 2,
"parent_name": "struct _253fake",
"parent_hashcode": 0,
"name": "minusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short validAtHitPoint; // size=0, offset=4",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 4,
"parent_name": "struct _253fake",
"parent_hashcode": 0,
"name": "validAtHitPoint",
"source_file": null,
"hashcode": 0
}
],
"name": "_253fake",
"source_file": null,
"hashcode": -1080400537
},
Replace all the occurrences of _253fake (or whatever it's called in the build you're looking at) with your replacement name, so that it looks like this:
{
"UsedByFunctions": [],
"members": [
{
"UsedByFunctions": [],
"struct_member_signature": "short plusDelta; // size=0, offset=0",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 0,
"parent_name": "struct _wba253fake",
"parent_hashcode": 0,
"name": "plusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short minusDelta; // size=0, offset=2",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 2,
"parent_name": "struct _wba253fake",
"parent_hashcode": 0,
"name": "minusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short validAtHitPoint; // size=0, offset=4",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 4,
"parent_name": "struct _wba253fake",
"parent_hashcode": 0,
"name": "validAtHitPoint",
"source_file": null,
"hashcode": 0
}
],
"name": "_wba253fake",
"source_file": null,
"hashcode": -1080400537
},
Again, after doing this, you want to go to the CreateSkeleton.exe step, above.