Fixing bug in game: 2 Variables in the Same Address?

Started by biolizard89, February 12, 2011, 08:13:47 PM

Previous topic - Next topic

biolizard89

So I've been playing around with the Smash Bros Melee 6-player mode, and noticed that there's a bug in the game for Player 6's pad data.  The analog values for each player are each stored as a float, but it seems that Player 6's analog values are stored in the same addresses in memory as some pointers.  This causes Player 6 to always be shielding and unable to move, and when I poke those addresses, the game crashes due to accessing an invalid pointer.  Player 5 does not have this problem.

So, my question.  Clearly this is a bug in the game, but there must be a way to fix it, right?  Is is feasible to patch the routines reading Player 6's pad data so that they read P6 pad data from some other address, e.g. an address in the code handler, while allowing the pointers to still reside there?  Any suggestions on what would be the best way to go about it?

Thanks!

dcx2

You might want to check the caller to the whatever function reads the pad data.  Somewhere, there should be a "player pad pointer" that gets passed as an input parameter into the call to read the pad data.  The pointer will probably be loaded into r3 or r4, maybe up to r10, just before the bl to read the pad data.  Or it may be an index, in which case you need to find where the index gets turned into an actual pointer.

Try to hook the ASM that's loading the pad pointer with a C2, then explicitly check for the p6 pad pointer, and if it matches then swap the input parameter with a pointer to some blank space.

You can reserve some space in the code handler using the bl trick in your C2 code.  Put a "bl SKIP_DATA" into your code; then, put as many .word 0,0,0,...,0,0 as you need to allocate space for the pad data object, each 0 takes up 4 bytes; then put the SKIP_DATA: label and continue your ASM code.  A pointer to your data area will be in the LR, so use mflr to replace the old p6 pad pointer.

I have no idea what the sizeof(pad_data) is; if it is too large to fit in the code handler's memory then this technique won't work.  At that point, you might want to try finding the address of malloc() and see if you can reserve yourself some heap for the p6 pad data.  You'd probably need to keep the pointer returned by malloc() somewhere...you could use that data area again, since the bl would automatically give you a pointer to the pointer, but you'd need to lwz after the mflr since this is pointer-in-pointer.  Note that technically you'll be creating a memory leak, since we never call free(), but this won't really be too bad unless you plan on trying to call malloc() a bunch of times.  Which reminds me...you will want to skip malloc() if the pointer in the data area is not null, so that way it only gets called once.

EDIT:

That might be a bit much to digest...the first key would probably be finding out how the pad read function gets a pointer to write the pad data (is it an index to an array, is it a linked list).  Follow pad read to the very end, and just after the blr takes you back to the caller, scroll up and look for whatever input parameter looks like it might have been your pointer.

Then we also would like to know how big a pad data object is for reserving space.  Is the difference between consecutive pad addresses constant?  If so, then it seems like they'd be an array, and that difference is the size of the pad object.