![]() |
||
---|---|---|
asm | ||
tools | ||
.gitignore | ||
Makefile | ||
README.md | ||
stagetable.txt |
README.md
Perfect Dark Setup Decompilation
This repository contains a decompilation of the level setup files used in Perfect Dark for the Nintendo 64.
Come again?
It's known that Perfect Dark (and GoldenEye) use a custom made binary scripting language to set up the levels. See the GE/PD Function Explorer and the GoldenEye Setup Editor. However, the specifics of how it works has been difficult to explore. Until now.
I've realised that Rare likely did their level setup and scripting with assembly macros. The giveaway was the U prefix on the setup filename, which is also a common prefix for compiled machine code (ucode). So I made a tool that would read the binaries and generate some assembly files which can be compiled back into those exact same binaries. Once I got it matching, I started annotating each setup file, discovering new commands and renaming symbols.
I still have no idea what you're talking about
Go have a look at the level scripts in the asm/setup directory. Here's a mapping table for your convenience:
Stage | File |
---|---|
Defection | setupame.s |
Investigation | setupear.s |
Extraction | setupark.s |
Villa | setupeld.s |
Chicago | setuppete.s |
G5 Building | setupdepo.s |
Infiltration | setuplue.s |
Rescue | setuplip.s |
Escape | setuptra.s |
Air Base | setupcave.s |
Air Force One | setuprit.s |
Crash Site | setupazt.s |
Pelagic II | setupdam.s |
Deep Sea | setuppam.s |
Defense | setupimp.s |
Attack Ship | setuplee.s |
Skedar Ruins | setupsho.s |
MBR | setupwax.s |
Maian SOS | setupsev.s |
WAR! | setupstat.s |
The Duel | setupate.s |
CI Training | setupdish.s |
Global Functions | globals.s |
There is also a stagetable.txt in the repository root which includes multiplayer stages.
What can I do with this?
You can read and modify the level scripting easily, then recompile it into a playable ROM file. Assuming you have a ROM already of course (it's not included in the repo). The only supported version is the NTSC 8.7 final version. Other versions will be supported eventually.
You can use this to make mods without having to deal with the GE Setup Editor's interface. Or you can use it for practicing speedruns (eg. start a level at a particular location with particular objectives completed). Or you could make ROMs to help figure out how certain commands work.
Okay, how? Show me how to make an edit
Install the following:
- make
- aarch64 build tools (aarch64-linux-gnu-as, aarch64-linux-gnu-ld, aarch64-linux-gnu-objcopy)
- Python 3
Then:
- Save your existing ROM file into the root of the repository with the name
pd.ntsc-final.z64
. It should not be byteswapped (the first four bytes should be0x80371240
). - Edit a setup file. Open up
asm/setup/setupame.s
(Defection), find the functionfunc0422_intro
and addexplosions_around_chr CHR_JOANNA
as the first statement. - Run
make rom
. This will create a ROM file atbuild/ntsc-final/pd.z64
. - Play the ROM.
- Start Defection, watch the intro and admire Joanna jumping from the dropship into a sea of explosions to her fiery death.
Wait, aarch64? Isn't that ARM?
Yeah. I originally used mips64, but the binaries it created were padded and therefore incorrect. I couldn't figure out how to change that with modern versions of the mips build tools and I didn't want to waste too much time on that aspect of it. Because there's no actual opcodes being generated, the architecture isn't really important. I just needed something that worked and was big endian (so x64_64 was out of the question too).
Where's the list of commands? Is there a reference?
See asm/include/commands.inc
and asm/include/constants.inc
for a start.
Whats with all this beginloop and endloop stuff?
All scripting is a series of labels and gotos. To give some structure to the code, I made a macro called beginloop
that replaces a label
+ yield
, and a macro called endloop
that replaces a goto_first
when used in this context. Most loops use the label and yield consecutively which allows this to work.
I don't think Rare had constructs like this though. There's a few rare places where yields are done at the end of the loop, and some where there's a dprint (comment) between the label and yield. In these cases my macros can't and aren't used.
I also added a reloop
macro, which is selectively used to replace a goto_first
within a loop. It's basically a continue
statement for those familiar with programming.
Can I edit global functions?
Well, yes but no. They won't be injected into the ROM because I'm lazy and haven't written code to do that. The stage files will though.
The global functions are at asm/globals.s.
How much stuff can I add before I run out of space?
Lots. If all the usual file space is exhaused, there's about 300KB of unused ROM space from other locations which will also be used. Considering each command averages only a couple of bytes, you're looking at about 10,000 to 100,000 commands you can add before you run out.
How do I know the built files are matching?
- With your ROM in place from earlier, run
make extract
. This will create anextracted/ntsc-final
directory and populate it with the binaries from your ROM. - Without making any modifications to the setup files, run
make
to build them as usual. These will be compiled atbuild/ntsc-final/files
. - Run
make test
. This will compare the setup binaries inextracted
with the ones inbuild
. If you get no output then they're matching. Try making a change to a file and repeat themake
andmake test
steps again to see it identify the mismatching files.
I want to see support for PAL, JAP and other versions
It's next on my to do list.
Can you do the same for GoldenEye?
I might do, but my focus is on PD for now.