diff --git a/Makefile b/Makefile index 7969d1dae..cdedffe90 100644 --- a/Makefile +++ b/Makefile @@ -59,16 +59,16 @@ E_SETUP_BIN_FILES := $(patsubst src/setup/%.c, $(E_DIR)/files/setup/U%.bin, $(SE B_SETUP_BINZ_FILES := $(patsubst src/setup/%.c, $(B_DIR)/files/U%Z, $(SETUP_C_FILES)) E_SETUP_BINZ_FILES := $(patsubst src/setup/%.c, $(E_DIR)/files/U%Z, $(SETUP_C_FILES)) -setup: $(B_SETUP_BINZ_FILES) +stagesetup: $(B_SETUP_BINZ_FILES) $(B_DIR)/files/setup/%.o: src/setup/%.c $(SETUP_H_FILES) mkdir -p $(B_DIR)/files/setup $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/bin/cc -c $(CFLAGS) -o $@ -O2 $< $(B_DIR)/files/setup/%.elf: $(B_DIR)/files/setup/%.o - cp $< build/setup.tmp.o - $(TOOLCHAIN)-ld -T setup.ld -o $@ - rm -f build/setup.tmp.o + cp $< build/zero.tmp.o + $(TOOLCHAIN)-ld -T ld/zero.ld -o $@ + rm -f build/zero.tmp.o $(B_DIR)/files/setup/U%.bin: $(B_DIR)/files/setup/%.elf $(TOOLCHAIN)-objcopy $< $@ -O binary @@ -95,9 +95,9 @@ $(B_DIR)/files/lang/%.o: src/lang/%.c $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/bin/cc -c $(CFLAGS) -o $@ -O2 $< $(B_DIR)/files/lang/%.elf: $(B_DIR)/files/lang/%.o - cp $< build/setup.tmp.o - $(TOOLCHAIN)-ld -T setup.ld -o $@ - rm -f build/setup.tmp.o + cp $< build/zero.tmp.o + $(TOOLCHAIN)-ld -T ld/zero.ld -o $@ + rm -f build/zero.tmp.o $(B_DIR)/files/lang/L%.bin: $(B_DIR)/files/lang/%.elf $(TOOLCHAIN)-objcopy $< $@ -O binary @@ -115,20 +115,21 @@ $(B_DIR)/files/L%Z: $(B_DIR)/files/lang/L%.bin tools/rarezip $< > $@ ################################################################################ -# Globals file +# Game setup file -globals: $(B_DIR)/Uglobals +setup: $(B_DIR)/ucode/setup.bin -$(B_DIR)/globals.o: src/globals.c $(SETUP_H_FILES) +$(B_DIR)/setup.o: src/setup.c $(SETUP_H_FILES) mkdir -p $(B_DIR) $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/bin/cc -c $(CFLAGS) -o $@ -O2 $< -$(B_DIR)/globals.elf: $(B_DIR)/globals.o - cp $< build/globals.tmp.o - $(TOOLCHAIN)-ld -e 0x80059fe0 -T globals.ld -o $@ - rm -f build/globals.tmp.o +$(B_DIR)/setup.elf: $(B_DIR)/setup.o + cp $< build/setup.tmp.o + $(TOOLCHAIN)-ld -e 0x80059fe0 -T ld/setup.ld -o $@ + rm -f build/setup.tmp.o -$(B_DIR)/Uglobals: $(B_DIR)/globals.elf +$(B_DIR)/ucode/setup.bin: $(B_DIR)/setup.elf + mkdir -p $(B_DIR)/ucode $(TOOLCHAIN)-objcopy $< $@ -O binary ################################################################################ @@ -165,7 +166,7 @@ test: $(B_SETUP_BINZ_FILES) $(B_LANG_BINZ_FILES) --exclude=bgdata \ --exclude=ob \ $(E_DIR)/files $(B_DIR)/files - @diff -q $(E_DIR)/Uglobals $(B_DIR)/Uglobals + @diff -q $(E_DIR)/ucode/setup.bin $(B_DIR)/ucode/setup.bin testall: REGION=ntsc RELEASE=final make test @@ -178,9 +179,9 @@ testall: ################################################################################ # Miscellaneous -all: setup lang globals +all: setup lang stagesetup -rom: $(B_SETUP_BINZ_FILES) $(B_DIR)/Uglobals +rom: $(B_SETUP_BINZ_FILES) $(B_DIR)/ucode/setup.bin tools/inject pd.$(ROMID).z64 clean: diff --git a/README.md b/README.md index dba573fa9..e06d32d39 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,27 @@ # Perfect Dark Decompilation (WIP) -This repository contains a work-in-progress decompilation of Perfect Dark for the Nintendo 64. So far only the stage setup files and lang files are decompiled. +This repository contains a work-in-progress decompilation of Perfect Dark for the Nintendo 64. -## I have no idea what you're talking about +## Roadmap -Go have a look at the level scripts in the src/setup directory. Here's a mapping table for your convenience: +| Section | Progress | +| --------------- | ----------------------------------------- | +| 0x40 rspboot | Won't disassemble as source is likely ASM | +| 0x1000 boot | Not started | +| 0x3050 library | Not started | +| 0x39850 setup | About 50% identified | +| 0x4e850 rarezip | 2/8 functions done | +| 0x5032e game | Not started | +| Lang files | Done | +| Setup files | Done | +| Prop files | Not started | +| Character files | Not started | +| Gun files | Not started | +| BG files | Not started | +| Pad files | Not started | +| Tile files | Not started | + +If you want to browse the stage setup files, use this table to find the filename: | Stage | File | | ---------------- | ------------------------------------ | @@ -30,17 +47,11 @@ Go have a look at the level scripts in the src/setup directory. Here's a mapping | WAR! | [setupstat.c](src/setup/setupstat.c) | | The Duel | [setupate.c](src/setup/setupate.c) | | CI Training | [setupdish.c](src/setup/setupdish.c) | -| Global Functions | [globals.c](src/globals.c) | +| Common Functions | [setup.c](src/setup.c) | 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 +## Compiling Install the following: @@ -51,43 +62,10 @@ Install the following: Then: 1. 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 be `0x80371240`). -2. Edit a setup file. Open up `src/setup/setupame.c` (Defection), find the symbol `func0422_intro` and add `explosions_around_chr(CHR_JOANNA)` as the first statement. +2. Run `make extract`. This will create an `extracted/ntsc-final` containing assets from your ROM. +3. Make edits to the C files in the `src` directory if desired. 3. Run `make rom`. This will create a ROM file at `build/ntsc-final/pd.z64`. -4. Play the ROM. -5. Start Defection, watch the intro and admire Joanna jumping from the dropship into a sea of explosions to her fiery death. - -## Where's the list of commands? Is there a reference? - -See `src/include/commands.h` and `src/include/constants.h` 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 src/globals.c. - -## 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? -1. With your ROM in place from earlier, run `make extract`. This will create an `extracted/ntsc-final` directory and populate it with the binaries from your ROM. -2. Without making any modifications to the setup files, run `make` to build them as usual. These will be compiled at `build/ntsc-final/files`. -3. Run `make test`. This will compare the setup binaries in `extracted` with the ones in `build`. If you get no output then they're matching. Try making a change to a file and repeat the `make` and `make 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. +Run `make` followed by `make test`. If `make test` produces no output then everything is matching. diff --git a/globals.ld b/ld/rarezip.ld similarity index 72% rename from globals.ld rename to ld/rarezip.ld index de0261a44..767743acc 100644 --- a/globals.ld +++ b/ld/rarezip.ld @@ -3,13 +3,13 @@ OUTPUT_ARCH (mips) SECTIONS { - .data 0x80059fe0 : AT(0x0000) { - build/globals.tmp.o (.data); + .text 0x70200000 : AT(0x0000) { + build/rarezip.tmp.o (.text); + build/rarezip.tmp.o (.data); } - /DISCARD/ : { * (.MIPS.abiflags); * (.options); diff --git a/ld/setup.ld b/ld/setup.ld new file mode 100644 index 000000000..fe1aea21e --- /dev/null +++ b/ld/setup.ld @@ -0,0 +1,20 @@ +/*OUTPUT_FORMAT ("elf32-bigmips")*/ +OUTPUT_ARCH (mips) + +SECTIONS +{ + .data 0x80059fe0 : AT(0x0000) { + build/setup.tmp.o (.data); + } + + /DISCARD/ : { + * (.MIPS.abiflags); + * (.options); + * (.gnu.attributes); + * (.pdr); + * (.mdebug); + * (.gptab.bss); + * (.gptab.data); + * (.reginfo); + } +} diff --git a/ld/zero.ld b/ld/zero.ld new file mode 100644 index 000000000..faddffe8f --- /dev/null +++ b/ld/zero.ld @@ -0,0 +1,24 @@ +/*OUTPUT_FORMAT ("elf32-bigmips")*/ +OUTPUT_ARCH (mips) + +SECTIONS +{ + .data 0x00000000 : AT(0x0000) { + build/zero.tmp.o (.data); + } + + .rodata : AT(SIZEOF(.data)) { + build/zero.tmp.o (.rodata); + } + + /DISCARD/ : { + * (.MIPS.abiflags); + * (.options); + * (.gnu.attributes); + * (.pdr); + * (.mdebug); + * (.gptab.bss); + * (.gptab.data); + * (.reginfo); + } +} diff --git a/setup.ld b/setup.ld deleted file mode 100644 index 94b3462f0..000000000 --- a/setup.ld +++ /dev/null @@ -1,35 +0,0 @@ -/*OUTPUT_FORMAT ("elf32-bigmips")*/ -OUTPUT_ARCH (mips) - -SECTIONS -{ - _LnameXDataStart = ADDR(.data); - _LnameXDataRomStart = 0x0000; - .data 0x00000000 : AT(0x0000) { - build/setup.tmp.o (.data); - } - _LnameXDataEnd = ADDR(.data) + SIZEOF(.data); - _LnameXDataRomEnd = 0x0000 + SIZEOF(.data); - - - - _LnameXRODataStart = ADDR(.rodata); - _LnameXRODataRomStart = _LnameXDataRomEnd; - .rodata : AT(_LnameXDataRomEnd) { - build/setup.tmp.o (.rodata); - } - _LnameXRODataEnd = ADDR( .rodata) + SIZEOF( .rodata); - _LnameXRODataRomEnd = _LnameXDataRomEnd + SIZEOF( .rodata); - - /DISCARD/ : { - * (.MIPS.abiflags); - * (.options); - * (.gnu.attributes); - * (.pdr); - * (.mdebug); - * (.gptab.bss); - * (.gptab.data); - * (.reginfo); -} -} - diff --git a/src/globals.c b/src/setup.c similarity index 100% rename from src/globals.c rename to src/setup.c diff --git a/tools/extract b/tools/extract index 7e0eaea5c..7cd30618e 100755 --- a/tools/extract +++ b/tools/extract @@ -26,7 +26,7 @@ class Extractor: self.rom = fd.read() fd.close() - self.globals = self.decompress(self.rom[self.val('globals'):]) + self.setup = self.decompress(self.rom[self.val('setup'):]) self.extract_all() def extract_all(self): @@ -110,7 +110,7 @@ class Extractor: i = self.val('files') offsets = [] while True: - offset = int.from_bytes(self.globals[i:i+4], 'big') + offset = int.from_bytes(self.setup[i:i+4], 'big') if offset == 0 and len(offsets): return offsets offsets.append(offset) @@ -143,7 +143,7 @@ class Extractor: # def extract_globals(self): - self.write('Uglobals', self.globals) + self.write('ucode/setup.bin', self.setup) # # Textures @@ -208,37 +208,37 @@ class Extractor: 'ntsc-final': { 'game': 0x4fc40, 'files': 0x28080, - 'globals': 0x39850, + 'setup': 0x39850, 'sfxctl': 0x80a250, }, 'ntsc-1.0': { 'game': 0x4fc40, 'files': 0x28080, - 'globals': 0x39850, + 'setup': 0x39850, 'sfxctl': 0x80a250, }, 'ntsc-beta': { 'game': 0x43c40, 'files': 0x29160, - 'globals': 0x30850, + 'setup': 0x30850, 'sfxctl': 0x7be940, }, 'pal-final': { 'game': 0x4fc40, 'files': 0x28910, - 'globals': 0x39850, + 'setup': 0x39850, 'sfxctl': 0x7f87e0, }, 'pal-beta': { 'game': 0x4fc40, 'files': 0x29b90, - 'globals': 0x39850, + 'setup': 0x39850, 'sfxctl': 0x7f87e0, }, 'jap-final': { 'game': 0x4fc40, 'files': 0x28800, - 'globals': 0x39850, + 'setup': 0x39850, 'sfxctl': 0x7fc670, }, } diff --git a/tools/inject b/tools/inject index 46d78dd8f..06522a66e 100755 --- a/tools/inject +++ b/tools/inject @@ -34,19 +34,19 @@ class Injector: self.rompart3 = rombuffer[0x001a15c0:0x00ed83a0] self.rompart4 = rombuffer[0x01d5ca00:] - fp = open('build/ntsc-final/Uglobals', 'rb') - globals = fp.read() + fp = open('build/ntsc-final/ucode/setup.bin', 'rb') + setup = fp.read() fp.close() - self.globaltop = globals[0:0x28080] - self.globalbot = globals[0x2a000:] + self.globaltop = setup[0:0x28080] + self.globalbot = setup[0x2a000:] self.files = [] i = 0 while i <= 0x7dd: romnameaddr = int.from_bytes(rombuffer[0x01d5ca00 + i * 4:0x01d5ca00 + i * 4 + 4], 'big') + 0x01d5ca00 - romdataaddr = int.from_bytes(globals[0x28080 + i * 4:0x28080 + i * 4 + 4], 'big') + romdataaddr = int.from_bytes(setup[0x28080 + i * 4:0x28080 + i * 4 + 4], 'big') name = '' while rombuffer[romnameaddr] != 0: