If you've ever flirted with the idea of modifying the PSX FFVII executable, you've probably struggled to find much info on exactly how to do it. I know I did. That's why I'd like to post a brief how-to on the procedure for performing assembly edits on the PSX release.
Understanding the executableUnlike FFVII PC, the PSX does not have a singular 'executable'. It is made up of overlays, representing the different parts of the engine (field, battle, menu, minigame etc.) and a small kernel storing state shared between the overlays. Overlays are so-called because they are 'painted over' memory space as they are loaded. So you are not going to edit 'the executable', but rather the particular overlay relevant to the behaviour you'd like to hack.
Overlays sit in their own individual files, and may even be themselves separated further still. For example, whilst the battle engine is simply BATTLE\BATTLE.X, the menu module is split into multiple .MNU files defining behaviours for different aspects of party management logic (SHOPMENU.MNU for shops; EQIPMENU.MNU for equipment; etc.). I don't know all the files for all the logic; I've only ever really taken an interest in BATTLE.X
Extracting and inserting the files on discThe easiest way to do this is to use CD Mage beta 5. Hit the 'export file...' and 'import file...' dialogs to extract and insert your executable files.
Turning a .X file into readable assemblerFFVII executables are compressed, so you can't just run them straight through a disassembler. You have to unpack them first. You can do this by removing some header bytes, then treating the file as a gzip archive (.gz). You will need a hex editor (I like Frhed; it has quite a few features and it's freeware) and a utility for unpacking gzipped files (7-zip can do this for you, and it's open source)
Here's the process:
- Using the hex editor, remove the first eight bytes from the file (store them in a document somewhere; you'll be reinserting them later). 'Remove' means remove, not 'zero out'.
- Save your edited file. If you save with the extension .gz, 7-zip should be able to pick up that it's in gzip format.
- The output of the gzip is a plain file which contains your executable binary
Disassembling the fileA disassembler will let you view an executable binary as a sequence of assembly opcodes - so you can read the code. I like to use LemASM for this job. Remember that the files are little-endian in both bytes and words, so you will need to use LemASM's 'swap bytes' and 'swap word' functions to view the file properly. You will also need to hit 'MIPS disassembly' under the File menu.
Reversing the assemblerI can't really provide a guide to this. The best reference for the PSOne's opcodes and registers is Halkun's own "Everything you wanted to know about the PlayStation but were afraid to ask". Otherwise, take a look at
this reference. Remember that quite a few MIPS opcodes have a one-instruction delay, so when you're trying to read the logic around 'lw' (load word) or branch instructions, remember that they don't take effect until the next opcode has been executed.
Writing new assemblerI would recommend using ARMIPS for this job. You give it a file which contains its own insertion instructions (e.g. BATTLE.BIN 0x800AB00), and it'll encode your assembler for you (it'll handle the endianness for you, too). You can use labelled jumps, you can use macros, and you can make it spit exceptions if your assembler is larger than the area you want to replace. It's a very handy tool.
Re-packing your executable fileThis is basically the decompression step in reverse, but because you're usually trying to avoid file size increases (unless you fancy updating file lookups), it's a tad more complicated. You are going to need the Unix gzip (or some gzip utility with the same functionality). If you're on Linux or OS X you should be able to just call that from bash; if you're on Windows, you're probably going to want a Cygwin-compiled version like the one that comes with FF7dec.exe.
The procedure is as follows:
- Turn your updated assembly file into a .gz by calling, at the command line, gzip -n -9 . The -n switch stops gzip inserting the file's name and compression timestamp into the archive, and -9 forces gzip to compress at tightly as it can, even if it means performing the compression slowly. This helps stop the filesize from increasing
- Reinsert the eight bytes you originally removed from the .X file into your .gz file. 'Reinsert' means prepend, not 'overwrite'. Save with the ending .X, .MNU, or whatever the original was.
- Reinsert your file using CDMage Beta
And that should be enough to get playing.