• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Patching Files on the Fly (specifically characters, stages, and hats)

_glook

Got a Passion for Smashin'
Joined
Sep 30, 2005
Messages
802
Location
Not UC Berkeley anymore
So some of you may have seen my proof of concept of SD Remix characters loading off of the memory card exploit. You basically have to hack each of the types of files separately, but once you've done one, you'll at least know where to start with it.

Current known injection points
These are the places know where you can both alter the file data safely and where you have a pointer to the file's data loaded in memory.
Code:
PlCo.dat (offset by 0x9fe0): 80067AF0
Some character files (this loads the main character files AND other character files, like Kirby's Caps): 800180F4
Only Main character files (PlXx.dat), offset by a variable amount: 80068F24
Stage dats (Gr[X]Xx.dat): 80018130
How to find new injection points
If this isn't enough for you and you want to patch other files, such as music or whatnot, here is how I usually find new injection points:
  • Search for the file name in memory as a string. Every file I've attempted to find has been in the DOL somewhere.
  • If you found it in the DOL, convert that to the corresponding RAM address. Every file name string stored in the DOL that I've searched for has had a corresponding pointer to that string somewhere in memory.
  • Do a read breakpoint on that spot in memory, and play through the game (you'll probably want interpreter core on) at the spot where you think that the file is being loaded (for all gameplay files, this is usually sometime after you select a stage on the SSS).
  • You should have a list of instruction addresses now. In my experience, there's usually two times the file name is accessed, the first time is to tell the game "hey, here's the file, check that it's valid and start loading it if you haven't already". The second time is usually where it actually reads the data from the file and creates useful game data structures from it.
  • You should try and hit the second breakpoint again. Once you hit the breakpoint, take the first 0x20 bytes of the file you're searching for and do a Dolphin memory search for it. I find that searching for the first 0x20 bytes is enough to find one one instance of the file, and by the time you hit this second breakpoint, the file should have already loaded in Dolphin (or at least the first part anyway). If there are no hits, step through the code and research until you find it.
  • Now that you know that the file is stored somewhere in RAM, do read breakpoints on the first 0x20 bytes on memory. You should have a list of addresses where it has tried to access the file data. At these instructions, the game has somewhere in its registers the address to the file data in RAM. If you analyze the code, you'll probably be able to figure out which ones it has.
  • Hopefully somewhere here is an indication of which character/stage/cap/music/sound/menu file you're loading. There's usually at least a file ID or something that can help you identify what exactly you're looking at. I've found that the different file types typically have separate code paths, but within each file type, you could be running into any number distinct files, so you can't just patch haphazardly.
  • ????
  • Profit!

How files are stored and handled in RAM
There are a couple of things to keep in mind about character files in RAM that may trip you up if you're not aware of them.
First off, the file data in memory are not bit by bit the same as the file data in storage (DVD). A lot of words in the file actually are RAM offsets, at when the game reads the game data and puts them into RAM, it translates those offsets into actual RAM addresses. So something that looks like 00 00 AD 08 or something in the .dat file will look something like 81 12 86 A0. I usually search for the first 0x20 bytes of the file for this reason, as those bytes usually don't contain any RAM offsets.
Also, character files actually start loading as soon as you select them on the CSS, which presumably cuts down on loading times while selecting a stage. Stage files seem to only be loaded once you select the stage, though.

Remember
You only have about 55,000 extra bytes of space to work with in the memory card, assuming you clobber ALL of the name tags. This means you won't be able to patch everything you want, especially if those patches involve stage files.

SDR Lite's Patching Method
You should have enough to get you started with your own memory card character patching, but if you're curious about how I've patched stuff up in SDR Lite, read on.

General approach
So the first question is what form is the patch data going to be in? The SD Remix files take up megabytes of space, so storing them in full is completely out of the question, since I only have kilobytes to work with (about 55 kilobytes). This means I've got to create some sort of patch file to store the character data.
I'm not going to sit here and re-implement xdelta or anything crazy like that. Granted, I haven't taken a look at the xdelta code, but I imagine it's far more than just 100 lines of assembly code, and when you're working at such a low level of code, there's only so much you want to deal with. As a result, I've run with my own very simple diff algorithm, which is actually good because in Melee hacking, you're typically not dealing with chunks of data being moved around, but simply everything staying in place but changing values, which means we don't need most of xdelta or whatever anyway. I'm actually using a couple of different methods because I was learning while making SDR Lite, so I'll go over each thing individually.

PlCo.dat (address 80067AF0)
This was actually the third file I patched, but it's the easiest, so we'll just go over it first. Shout-outs to Achilles for giving me the original memory location of PlCo.dat to get me started.
At this point in memory, we know we're going to be reading data from PlCo.dat, and we have the address of it (offset by 0x9fe0), stored in r0. As a result, you should not use r0 except for reading. You should also not change the value of r3, because it is used for other things later.
From here you can make simple memory overrides. SDR doesn't change too many values, so I simply did this:
Code:
lis r12, 0x[first half-word]
ori r12, r12, 0x[second half-word]
stw r12, 0x[offset](r0)
Kirby Cap patches (address 800180F4)
This was actually the second thing I patched, after character patches. This particular instruction is actually run once per active character slot and loads at least two character files, depending your character. There's the main PlXx.dat file loaded, then there's another file that's loaded (I don't know which one, but it might be the character costume file). Certain characters will also load other files. This is where I decided to patch Kirby's Cap files (PlKbCpXx.dat).
r30 points to a struct containing information about the file we're currently trying to read. It is at least 0x18 bytes big. At 0x04, it contains the first byte saying which type of file it is (from what I can tell, 02 is a main character file, like PlFe.dat, while 01 means it's an auxillary file, like costume files or Kirby's Cap files). The half word that ends this word is a file id, which, as far as I can tell, is unique for every file in the game. Some examples of what this is for various files:
Code:
For LinkCap (PlKbCpLk.dat): 0100027e
For Ness (PlKbCpNs.dat): 01000282
For Samus (PlKbCpSs.dat): 01000289oo
For Child Link (PlKbCpCl.dat): 01000274
For Pichu (PlKbCpPc.dat): 01000283
What I do is I simply check that the first byte is a 1, then I have an array in memory card that holds a file id => patch data location, so it's a simply array look-up to get the patch data.
The actual patch data I'm using for Kirby Cap files is pretty easy because I can take advantage of the fact that none of the changes that SDR does to the Kirby cap files change any of the RAM offsets in the files. As a result, I can simply write over the bytes that are different. As a result, the patch file format I ended up using was of the following format:
Code:
half word of number of bytes that are the same
byte of number of bytes that are different
the bytes that are different
... repeat until done ...
00000000
The algorithm as a result is pretty simple. 0x10(r30) contains a pointer to a struct specifically about the file's data storage in memory. The word at 0x4 in that struct is the pointer to where the actual file data starts in memory. I basically take this address and run it through an algorithm, which is basically:
  • Add the bytes that are the same to the file data pointer
  • Loop for every byte that is different:
    • Write the current byte that is different to the current byte of the file data pointer
    • Increment both the patch data pointer and the file data pointer
    • Decrement the number of bytes that are different
    • If we're at 0 bytes different, go back to reading bytes that are the same
  • If you hit a combined value of bytes different and bytes same as 0 (USING AND, NOT ADD, otherwise FFFFFF01 + FF will read 0!), you know you're done patching.

Character patches (80068F24)
This is the first one I did, and if I could do it again, I'd probably just use the same method I used with the Kirby patches, because the memory location here is offset, much like PlCo.dat, but the offset is different for every character. You have to manually find what each offset is. Fortunately, this offset seems to be RIGHT after the hex sequence "FE000000 00000000 18000000" for every character, and this sequence only occurs once in every character file. I ended up manually adjusting all of my patch data, but it worked out okay in the end.
There's a couple of gotchas using this data, though. This is one of those times where the RAM offsets are going to be an issue, so I can't do simple byte overwrites like could with Kirby Caps. I won't know exactly what addresses the game is going to use, since the RAM offsets are based off of the file data's location in memory. You might be thinking well why don't you just add the offset yourself, since you know where the file is stored in memory? That would be great, but I don't know which words to apply that logic to, and it would increase the size of the patch files to put that data in to begin with.
The solution I came up with is instead of a basic overwrite, I created a patch for each character based off of ADDs on each WORD. What this mean is that I search for words that are different (instead of bytes) and if I find one, I take the difference between the two words and store them in the patch file. I can then take this difference, take the word in the character file data, and add the difference onto the word and store it back, making it effectively the patch. Here's the data format I'm using for the patch file:
Code:
half-word of number of words that are the same
half word of number of words different
a series of words taking the word from the modified file and subtracting the word from the original
... repeat ...
00000000
The algorithm is similar to Kirby file patching. It takes the two half words and sees if they're both 0; if they are we're done. Otherwise, multiply the number of words by 4 to get the number of bytes and increase the file data pointer by that amount. Take the next "number of words different" words of the patch file and add it to the next "number of words different" words from the original file data and store it back into the original file data. Repeat.
There is, however, a problem with this method and that is you can't apply the same patch file twice, and the problem with this particular instruction injection point is that it is called FOR EACH player slot, and a character's data is store only ONCE in memory, so if you willy-nilly apply this without any checks, you'll apply the patch twice (or thrice, or even.. fourice) on the same character's data file and you'll be in a world of hurt. IN ADDITION, when you go back to the character select screen, if you don't change the character, the data IS NOT RELOADED from the disk, so it's still in the patched state.
My solution to fix this is to have a sanity check. I manually figure out the first byte in the modified file which has a changed byte that is exactly the same in the file as it is in memory, so that it is always the same (meaning it's not part of a RAM offset value). Once you've figured that out, you do a "sanity check" which is to see if the character's data file in memory at that location is the same as your sanity check. If it is the same, that means it's already been patched and you should not patch it again. If it isn't the same, that means it is a freshly loaded character file, so you should patch it. This would run before the character patcher.

Stage patches (80018130)
So this location that I found is very similar to the character one I found that I use for Kirby Cap patches. However, I also use my Omega toggle to see if I even want to load the altered version of the file or not. The way I do this is at the point where the game checks the stage's ID, I run some code to set another variable somewhere to the location of the stage patch data or 00000000 if I shouldn't patch the file. At the stage file reading injection point, I then check to see if that variable has been set and if it has, I use the patch data at the pointed location.
The logic here is pretty similar to the Kirby Patches, but the stage patches in SDR Lite use the RAM offsets stuff to, so you'll need to use the same patch method as the character patches. Doing "lwz r4, 0x10(r30)" then doing "lwz r4, 0x4(r4)" will get you the location of the stage file data in memory. The nice thing about this location is that the game reloads every stage every time you select it on the stage select screen, even if you've already selected it before (presumably to make space for game data). This means you don't have to do any sanity checks to make sure the patch hasn't already been applied. Also, the pointer to the stage file data starts at the beginning of the file, so you don't have to manage any offsets like you did with the character patches.

And thats it!
That's the basic methodology that I used to get SD Remix characters and hacked stages loading off of a memory card. This has been on my mind almost since I both knew about SD Remix and the memory card exploit, and I'm glad it's finally a reality.
I want to point out that the patching method listed here is not totally optimal. It is good enough for my purposes, but you can get the patch data sizes smaller if you wanted to. It's just more complicated, and doing all of this while learning all this stuff in the process was a big enough task in itself. Maybe you'll come up with a better algorithm and maybe you can get some of the more prohibitively expensive stage patches on the memory card, like Skyrule and Improved Fourside.
Or you can hack snapshots to get yourself some more space to store those stage patches.
Good luck, and happy hacking!

(For completion's sake, I've included my raw notes from making SDR Lite in a zip file for your perusal.)
 

Attachments

Last edited:

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
Awesome, Glook. This is some pioneering stuff.

Have you tried force-loading memory card data at will? I've never tried, but I don't see why it wouldn't be possible. So then you could have multiple save files with data on them.

My initial thought is to just change the name of the memory card filename, and then branch into this function:

[from wParam]
8001CBBC DoLoadData (no params, handles all of the loading tasks)

Next, copy/use whatever data from that save file you need, and then reload the original, "master", save and continue like normal.
 

_glook

Got a Passion for Smashin'
Joined
Sep 30, 2005
Messages
802
Location
Not UC Berkeley anymore
That would be very interesting! I know Dan has been able to create alternate save files for 20XXTE, so this seems like a good way to get the bigger stage hacks in without hacking snapshots. The thought hadn't occurred to me, but it'll definitely be on my mind going forward.
 

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
That would be very interesting! I know Dan has been able to create alternate save files for 20XXTE, so this seems like a good way to get the bigger stage hacks in without hacking snapshots. The thought hadn't occurred to me, but it'll definitely be on my mind going forward.
Ok, so not sure if you know this or not.

803bac5c (RAM) contains ASCII:

SuperSmashBros0110290334

This is the name of the memory card file. If you change anything with it, then next memory card load/save event will be done to the newly specified memory card file. You’ll see what I mean if you change this ASCII and then back out to the Start menu and have it try to load data again. It will try to create new data because it can’t find an SSBM save on the memory card with that new name.

So like in the Crazy Mod, there are two save files on the GCI file. The default one with the name above, that initializes the exploit and contains a lot of code that gets copied to the free space at the beginning of the RAM. The second save file “DuperSmashBros…” that gets changed during the exploit and loaded afterward that contains the Crazy Mod data to be saved among sessions.

In reference to my last post, I see it like this.

You have the GCI with two (or more) save files that contain data. Then whenever necessary, you inject code that changes the memory card file name to “SuperSmashSDR2..” and then run through the load function, pull out/patch with the loaded data, and then change the name back to “SuperSmashSDR1…”, load data again, then end custom function.
 
Last edited:

_glook

Got a Passion for Smashin'
Joined
Sep 30, 2005
Messages
802
Location
Not UC Berkeley anymore
Ok, so not sure if you know this or not.

803bac5c (RAM) contains ASCII:

SuperSmashBros0110290334

This is the name of the memory card file. If you change anything with it, then next memory card load/save event will be done to the newly specified memory card file. You’ll see what I mean if you change this ASCII and then back out to the Start menu and have it try to load data again. It will try to create new data because it can’t find an SSBM save on the memory card with that new name.

So like in the Crazy Mod, there are two save files on the GCI file. The default one with the name above, that initializes the exploit and contains a lot of code that gets copied to the free space at the beginning of the RAM. The second save file “DuperSmashBros…” that gets changed during the exploit and loaded afterward that contains the Crazy Mod data to be saved among sessions.

In reference to my last post, I see it like this.

You have the GCI with two (or more) save files that contain data. Then whenever necessary, you inject code that changes the memory card file name to “SuperSmashSDR2..” and then run through the load function, pull out/patch with the loaded data, and then change the name back to “SuperSmashSDR1…”, load data again, then end custom function.
This is awesome, I'll do some research on this; I'm sure I just have to do a read breakpoint on 803bac5c to get the function that loads/saves memory card data.
 

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
Yeah, I think it would be very beneficial to look into and could open so many doors for the memory card exploit.

wParam might have already documented everything you need.

Code:
8001BE30   SaveGameData ()
        r3  : 00000000  
        r4  : 803BAC5C  "SuperSmashBros01..."
        r5  : 803BAB74  directs the thing on what to save.
        r6  : 80433334  [the strings that go in the file] appears to be all
        r7  : 80BE7CE0  points to the picture data
        r8  : 80BEACE0  i believe this is the icon
        r9  : 8043331C  points to just before strings in r6
        r10 : 8001CC30  if (r3==0)return; *(80433318+8) = 2; (OnComplete() function)
        r11 : 00000000 

8001A4CC   Related to finding files on memcard (char *filename, int zero)
8001BD34   Load from memcard (int unknown, char *name, void *sameasr5fromsave)
8001BE28   mtlr from Load (executed AFTER data is loaded)
8001CBBC   DoLoadData (no params, handles all of the loading tasks)
 

_glook

Got a Passion for Smashin'
Joined
Sep 30, 2005
Messages
802
Location
Not UC Berkeley anymore
Yeah, I think it would be very beneficial to look into and could open so many doors for the memory card exploit.

wParam might have already documented everything you need.

Code:
8001BE30   SaveGameData ()
        r3  : 00000000 
        r4  : 803BAC5C  "SuperSmashBros01..."
        r5  : 803BAB74  directs the thing on what to save.
        r6  : 80433334  [the strings that go in the file] appears to be all
        r7  : 80BE7CE0  points to the picture data
        r8  : 80BEACE0  i believe this is the icon
        r9  : 8043331C  points to just before strings in r6
        r10 : 8001CC30  if (r3==0)return; *(80433318+8) = 2; (OnComplete() function)
        r11 : 00000000

8001A4CC   Related to finding files on memcard (char *filename, int zero)
8001BD34   Load from memcard (int unknown, char *name, void *sameasr5fromsave)
8001BE28   mtlr from Load (executed AFTER data is loaded)
8001CBBC   DoLoadData (no params, handles all of the loading tasks)
Oh wow, yeah everything's already here. Thanks a bunch!
 

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
Also, idk if you've noticed (or care), but 8025bb40 does not get executed when stage selection is set to RANDOM. Maybe other options too, but I haven't checked. I noticed this awhile back because I was using the same line for stage swapping for 20XX.
 

_glook

Got a Passion for Smashin'
Joined
Sep 30, 2005
Messages
802
Location
Not UC Berkeley anymore
Also, idk if you've noticed (or care), but 8025bb40 does not get executed when stage selection is set to RANDOM. Maybe other options too, but I haven't checked. I noticed this awhile back because I was using the same line for stage swapping for 20XX.
I haven't run into this issue for whatever reason, but maybe I'm doing things differently in SDRL. I'll try and keep an eye out for any issues involving that, though.
 

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
MnSlMap.usd
Injection Point: 8025A9DC
+0x40(r3) = pointer to start of MnSlMap.usd in RAM

MnSlMap.dat
Injection Point: 8025A9EC
+0x40(r3) = pointer to start of MnSlMap.usd in RAM



MnSlChr.usd
Injection Point: 80266974
+0x40(r3) = pointer to start of MnSlChr.usd

MnExtAll.usd
Injection Point: 80266980
+0x40(r3) = pointer to start of MnSlChr.usd

MnSlChr.dat
Injection Point: 80266958
+0x40(r3) = pointer to start of MnSlChr.dat

MnExtAll.dat
Injection Point: 80266964
+0x40(r3) = pointer to start of MnSlChr.dat
 

Achilles1515

Smash Master
Joined
Jun 18, 2007
Messages
3,211
Location
Cincinnati / Columbus OH
Code:
Funciton 1
lwz r4, 0x10(r30)
lwz r4, 0x4(r4)

Funciton 2
lwz r14, 0x14(r30)
Are these two not the same?
No. The first line loads a word, pointer A, relative to start location (address in r30). The second line loads a new pointer, pointer B, relative to pointer A's location.

What you wrote will load a pointer relative to start location (address in r30), and that's it.
 

Cyjorg

tiny.cc/19XXTE
Joined
Nov 18, 2013
Messages
686
Location
Purdue University
No. The first line loads a word, pointer A, relative to start location (address in r30). The second line loads a new pointer, pointer B, relative to pointer A's location.

What you wrote will load a pointer relative to start location (address in r30), and that's it.
Right. I definitely need sleep
 
Top Bottom