X-Hax / sa2-mod-loader

Sonic Adventure 2 PC Mod Loader
47 stars 16 forks source link

"Don't Lose Rings When Hit" code #21

Closed BtEtta closed 3 years ago

BtEtta commented 3 years ago

Sonic Adventure DX's mod loader comes with a code to prevent the ring total going down after being hit. I made one for Sonic Adventure 2.

Code "Don't Lose Rings When Hit"
write16 006C1B36 0xD08B

For what it's worth since this is the first time I've made a code for any game that's actually patching executable code (and first time where I've analysed the code myself to follow what's happening) I thought I'd quickly run down my working to make sure I haven't missed anything obvious or non-obvious.

There's a function at 0x0044CE10 that could be called addRings(). It loads the current ring count into eax, then adds the value from edx to it. In the case of losing rings edx is set to the two's complement of the current ring count.

When hit the ring count is copied into edx at 0x006C1B0D. From there it's copied into ebp and one of two things happen: If ebp is greater than 20 ebp is set to 20 (presumably this is used to determine how many rings fall out) edx is then converted to its two's complement and addRings() is called. If ebp is less than or equal to 20 the code diverts to check if ebp is 0 and if it is it jumps to the "lose a life" code, otherwise it returns to where edx is negated and addRings() is called.

Ideally I'd have been able to prevent loading the ring count into edx with nops but that just causes you to always lose a life. In the end I figured out I was able to replace the neg edx instruction with mov edx,eax as eax is set to 0 at that point in the code anyway.

kellsnc commented 3 years ago

Hi!

I might be wrong but since eax is not set to 0 in this function, it might end up being something else than 0 if mods call the RingDrop function themselves? You could nop the AddRings function call and hack the condition (jle loc_6C1C7B) to directly jump to the sound stuff.

BtEtta commented 3 years ago

This is why I posted my working to see if there was obvious stuff I missed.

It's been a little while since I dug into this but I believe eax was potentially non-zero at the start of ringDrop() and it was some unrelated function calls (that I admittedly didn't dive all the way in to) in ringDrop() before the neg edx instruction that reset eax to 0. If that's the case then even a mod calling ringDrop() would be unaffected.

I feel like I might have tried noping out the addRings() call but that caused some other side effect, or maybe I just didn't consider patching the jump too.

I don't currently have the game installed but I'm due to be getting a new PC some time in the next few days so I should be able to take a second look once that's up-and-running.

Another thing I didn't consider was whether this is for player one only. (Although I'm not sure why you'd want to have a code like this on for the multiplayer mode.)

kellsnc commented 3 years ago

Another thing I didn't consider was whether this is for player one only. (Although I'm not sure why you'd want to have a code like this on for the multiplayer mode.)

No this is for both players, the player id of the player that was hurt is passed to the function.

In fact eax seems to be the playerID so my previous concern was wrong:

mov eax, ebx (ebx is the player ID) and eax, 1

Forcing eax to 0 with and eax, 0 would make it not work in 2p so yeah hacking the condition would probably be better, I guess.

Edit: this will work:

Patch "Don't Lose Rings When Hit" write8 006C1B3A 0x90 x5 write16 006C1B41 0xE990

I added it to the Mod Loader, please tell me if there's any issue with it.

BtEtta commented 3 years ago

Can't see any issue with it.

Clearly it didn't occur to me to patch out the call to addRings() as if I'd just done that (even without patching the jump too) I'd have ended up with the same result as my original patch. ¯\_(ツ)_/¯

I was also wondering about the difference between code and patch in codes.lst but I'm now realising it's probably just matter of apply constantly vs. apply once.

Thanks.

kellsnc commented 3 years ago

it's probably just matter of apply constantly vs. apply once.

Yes, it's exactly that.

Well here it is then, thanks for the suggestion & sorry for the delay ^^