Hacking Persona 3, Part 2

In part one, I started a journey to add more music to Persona 3, and to randomly select between new and old tracks.

I left off with a very specific problem when replacing a file: the game is reading the file’s location inside BGM.CVM from something other than the CVM file. The only place I can think of that would have the answer is the source code. A little Googling reveals a program named PS2Dis, a PS2 disassembler. Let’s open the SLUS_216.21 file. ps2dis_1

We’re clearly going to need a little background on assembly. The PS2 runs a 64-bit MIPS R5900 processor. I don’t really know what that means, but now I know what instruction set to Google. This overview got me mostly up to speed pretty fast.

Alright, now we have a small chance of understanding what we come across. After exploring the menus of PS2Dis, it seems we can open a label list by pressing Ctrl-G. ps2dis_search

Neat! Well, we know the music files are all named “#.ADX”, so let’s try entering “01.ADX” (this is the one time you actually type the quotes).

Hmm, there’s a list of song names, but there’s nothing that looks like a file table with offsets and sizes. What if we Find (Ctrl-F) “01.ADX” instead? ps2dis_filetable_1

Bingo. (Side note: you can change the data type PS2Dis displays an address with “C” for instruction, “B” for byte, and “W” for word. It greatly enhances readability, as it’ll automatically display ASCII values for bytes and preview the value of words containing addresses.)

Let’s see if we can find the start of this “file table.” ps2dis_filetable_start

Aha. After some analysis (the output of cvmtool helps a lot), here’s the format:

uint32 file_count
uint32 file_count  //yes, twice
uint32 0x00000014  //magic number!
"#DirLst#"
uint32 0x00000000

for each file:
    uint32    file_size
    uint32    extAttributeLen  //still not 100% sure if this is right, but it's always zero, so...
    uint32    file_offset
    uint8      flags
    uint8      unknown  //I have no idea what this is.  It's nonzero and fluctuates, but just writing 0x00 here works fine.
    char[34] filename   //always 34 bytes, with the unused bytes set to 0x00

Simple enough. Let’s keep investigating – where does the game decide which music file to load? Let’s go back to that first list of song names. ps2dis_load

Cool. If we press Space and then F3, we can use PS2Dis’s analysis tool to find code that refers to this address. ps2dis_songname_referrer

Alright, so – wait a second, that “li” does not refer to the address we were just at. Let’s follow it (press the right arrow) and see what it’s loading… ps2dis_tracks

Ah, it’s a list of the addresses of the filenames, evenly spaced by 12 bytes (0x0C). We can test our theory by changing 0x0010920c’s “load immediate” address to be 0x0C bytes further ahead. Try it and, indeed, just about every song in the game will change, because we’ve offset the array index by one.

We’ve now covered how to modify the BGM.CVM file to add or modify music files, and the necessary source code changes that must be made to accomodate the new files. In Part 3, I’ll talk about the tool I wrote to actually change things, as well as the assembly routine for picking songs.