Register - Login
Views: 99792426
Main - Memberlist - Active users - Calendar - Wiki - IRC Chat - Online users
Ranks - Rules/FAQ - Stats - Latest Posts - Color Chart - Smilies
05-03-22 04:48:14 AM
Jul - General Game/ROM Hacking - Final Destination 64 New poll - New thread - New reply
Next newer thread | Next older thread
GuyPerfect
Catgirl
Level: 68


Posts: 1016/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-13-14 11:16:23 AM (last edited by GuyPerfect at 10-13-14 04:45:19 PM) Link | Quote
Ha! And they said it couldn't be done in Vs. mode with simple GameShark codes.



The usual level switch code for the North American release of the game is as follows:

800A4D09 00XX

Where XX is:
  • 00 = Peach's Castle
  • 01 = Sector Z
  • 02 = Kongo Jungle
  • 03 = Planet Zebes
  • 04 = Hyrule Castle
  • 05 = Yoshi's Island
  • 06 = Dream Land
  • 07 = Saffron City
  • 08 = Mushroom Kingdom
  • 0D = "Metal Cavern"
  • 0E = "Battlefield"
  • 10 = "Final Destination"
The problem is, trying to load level 0x10 in Vs. mode crashes the game. This has been the bane of the Smash community for eons now, and people have jumped through some serious hoops to get around this problem. Up until now, the best solution known was to hijack the 1P Master Hand encounter to inject additional players and remove the boss character. It's not elegant, and the match doesn't necessarily end when it's supposed to, but it works, and for many people, that's A-OK.

The short version of the story is that the value at 0x800A4D09 is read a number of times while loading the level, and one particular read is executed at address 0x80104C14. For whatever reason, if a 0x10 is read prior to the ensuing function call in Vs. mode, the game crashes. It likely has something to do with the Master Hand scenario's behavior, likely due to the graphics being tied to the boss's damage meter. In the event some bland, featureless level's index is loaded at this point instead (such as 0x0E, Battlefield's index), the level loads just fine in Vs. mode, but without the sparkly stars or changing background.

Fortunately, N64 games tend to load code into RAM and execute from there, so it's ripe for the GameShark picking. If 0x10 is in the level index variable, the instruction at 0x80104C14 can be replaced wholesale by something else that won't break. So instead of doing what it does--loads the value from RAM--how's about it just always loads 0x0E in the event the actual level index is 0x10? The CPU instruction can be overwritten by the GameShark, and the game program will reload it from ROM before the next match, so it won't break doing it this way.

There's also the matter of the music. By default, the level uses the "Master Hand is approaching" tune, which isn't the one you'd want to battle to. The tune has an index of 0x18, and the formal Master Hand battle music is 0x19. So, easily enough, a GameShark code can detect one and replace it with the other.

So for the main attraction... The following code is a fix-only code that enables Final Destination to be used through the usual stage switch code. It does not, in of itself, cause Final Destination to be loaded in Vs. mode:

D0099113 0018
80099113 0019
D00A4D09 0010
81104C14 2002
D00A4D09 0010
81104C16 000E

I've tested this in Project64, as seen above, and the technique was discovered using Nemu's wonderful debugger. This was mere minutes before leaving for work this morning, so I haven't tested it on the hardware yet, but that's why God invented lunch breaks.



EDIT:

Whooee, it works just swimmingly on the hardware. When I get home for the night I'll upload a video for the world to squee over.
GuyPerfect
Catgirl
Level: 68


Posts: 1017/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-13-14 10:04:34 PM Link | Quote
Sooooo, Jul won't let me use YouTube's <iframe> style embed feature, and I don't have Flash installed, sooo... I have no idea of the rectangle below actually contains a video:

<object width="420" height="315" style="margin-left: 16px;"><embed src="//www.youtube-nocookie.com/v/zgMTsdLqGH4?hl=en_US&version=3&rel=0" type="application/x-shockwave-flash" width="420" height="315" allowscriptaccess="always" allowfullscreen="true"></embed></object>

If it does, enjoy the dumb little video! If not, here's a link to the corresponding page on YouTube: http://youtu.be/zgMTsdLqGH4
Xenesis

Roy Koopa
Actually a Doctor
Level: 101


Posts: 2203/2732
EXP: 10486572
For next: 231103

Since: 07-28-07

Pronouns: She/Her
From: Orange Star's Retirement Villa

Since last post: 9 days
Last activity: 18 hours

Posted on 10-14-14 02:46:17 AM Link | Quote
Heh, I think those spawn points need work in 4P mode. Still, nicely done. Now all it needs is a proper level select screen hack to display those extra stages

(Also you need to use the 'old embed code' to get Youtube videos to show up normally in jul posts, but whatever you did worked fine.)
GuyPerfect
Catgirl
Level: 68


Posts: 1018/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-14-14 07:15:16 PM Link | Quote
Originally posted by Xenesis
Heh, I think those spawn points need work in 4P mode.

I thought about doing something with those, but decided it was in the "level mods" category and therefore out of the scope of a fixer patch code. The stage wouldn't hurt to have some item spawn points as well. But hey, I'm not highly motivated to get on that!

Originally posted by Xenesis
Now all it needs is a proper level select screen hack to display those extra stages

I'm not thinking this one will happen via GameShark, unfortunately.

Originally posted by Xenesis
(Also you need to use the 'old embed code' to get Youtube videos to show up normally in jul posts, but whatever you did worked fine.)

I did use the Old Embed Code. But I don't have Flash installed, so all I see is a grey rectangle telling me I need a plugin to view the content.

Now if Jul would just allow <iframe>s or implement a YouTube tag of its own, we'd be in business!
GuyPerfect
Catgirl
Level: 68


Posts: 1019/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-15-14 12:17:27 AM (last edited by GuyPerfect at 10-15-14 12:30:22 AM) Link | Quote
So on a whim, I decided to look into those spawn points. Since players 3 and 4 are right on top of players 1 and 2, I hypothesized that the level only had 2 spawn points and somehow it was reusing them for additional players, but that doesn't appear to be the case.

Looking into it, I dug too deep and consequently can't make a GameShark code using what I've found.

In the North American ROM file, at address 0x4127FE, there's the following bytes: 62 17 E1 F2. This is a bit-packed value, with two bytes of a 16-bit, signed integer stored at bits 11-18 (high byte) and 2-9 (low byte) (bit 0 is the lowest bit, and bits are indexed in decimal). For this particular value, those bytes come out to 0xFC7C, which is two's complement for -900. This value is then converted to a float and used as Player 1's starting X coordinate on Final Destination.

Changing those bytes to 62 10 01 F2 sets P1's starting X coordinate to 0.0, which while technically is in the dead center of the platform, actually refers to the left edge of the character's origin. Still, it does what you'd expect it to do.

For those itching to look into it a little more, the applicable ROM bytes are loaded to RAM at address 0x800437CC and, after parsing out the bits, the integer bytes are stored at 0x8021B930 (EDIT: This is on the stack, so it might move around. I did a timed battle with default settings, P1 Link and P2 Mario, level 3). The reason I can't hijack this with a GameShark is because it happens too abruptly, and on the stack, that the GameShark doesn't have time to replace the data before it's already gone and replaced with something else. And N64 GameShark doesn't have the ability to load effective addresses from RAM like later Action Replay devices can, so it can't trace the stack.

Anyhoo, changing P1's starting X to zero and loading up Final Destination with 4 players shows that P3 still starts in the same spot it does normally. What this tells me is that there are four spawn points, and it's just a matter of finding a way to move them using a GameShark to make the level a little more presentable.
GuyPerfect
Catgirl
Level: 68


Posts: 1020/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-15-14 01:23:29 AM Link | Quote
Player 3's X is initialized to -600 and Player 4's to 600. As P1 is -900 and P2 is 900, this is rather close, and not spaced very evenly. What we're looking for is a 600-unit difference between steps, meaning P3 should be -300 and P4 should be 300. The following ROM patches to the North American file will fix the Final Destination spawn points:
  • 0x412808 - P3 - Change 50 13 F5 51 to 50 13 F9 A9
  • 0x41280C - P4 - Change 2B 10 04 58 to 2B 10 02 2C
Hrm... Now that I type it out like that, I see that the two values are right next to each other in ROM. I betcha that's in the region with Final Destination's level data, but you'll have to accept a rain check if I'm wrong.

Either way, spacing them out at ± 300 makes all 4 players 600 units apart, which is even spacing. It's certainly better, but methinks it's a smidge too close for comfort at the start of a match:

GuyPerfect
Catgirl
Level: 68


Posts: 1021/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-17-14 12:04:41 AM (last edited by GuyPerfect at 10-17-14 12:14:51 AM) Link | Quote
Hehe, some dork over at Smashboards registered an account just to start a thread and post my code there. There's nothing wrong with that, but not knowing of that thread, I started one of my own and copy/pasted my OP from this thread over there, then the bozo chimes in and has the audacity to accuse me of stealing "his" code.

They say that imitation is the sincerest form of flattery, and misrepresenting oneself as the author of a work is surely a way to convey the value of that work. But if only he were a bit more sincere, he could imitate the whole hacking-the-game thing and produce more cool codes for the community.

EDIT:
Incidentally, what is the proper MIPS instruction for loading from immediate data? I wound up using ADDI with a source register of r0, but that feels like I'm taking the long way around. Then again, knowing that all MIPS instructions are 32 bits, I can't really think of anything an immediate move could do that an immediate add to r0 can't.
Joe
Common spammer
šŸ¬
Level: 111


Posts: 3167/3392
EXP: 14501034
For next: 367326

Since: 08-02-07

From: Pororoca

Since last post: 12 days
Last activity: 6 hours

Posted on 10-17-14 05:50:16 PM Link | Quote
Originally posted by GuyPerfect
EDIT:
Incidentally, what is the proper MIPS instruction for loading from immediate data? I wound up using ADDI with a source register of r0, but that feels like I'm taking the long way around. Then again, knowing that all MIPS instructions are 32 bits, I can't really think of anything an immediate move could do that an immediate add to r0 can't.
That's how you're supposed to do it.

If you use an assembler, you'd use the LI pseudoinstruction:

li      $a0, 0x12345678

li $a1, 0x0000abcd
li $a2, 0xffffabcd


If your assembler is any good, it'll have multiple translations:

lui     $a0, 0x1234

ori $a0, $a0, 0x5678
ori $a1, $zero, 0xabcd
addiu $a2, $zero, 0xabcd


And a good disassembler will recognize some of those to show you something like this:

lui     $a0, 0x1234

ori $a0, $a0, 0x5678
li $a1, 0x0000abcd
li $a2, 0xffffabcd



____________________
ćµć«ć‚ƒć‚ć€‚
GuyPerfect
Catgirl
Level: 68


Posts: 1022/1096
EXP: 2665649
For next: 63151

Since: 07-23-07


Since last post: 1.7 years
Last activity: 219 days

Posted on 10-19-14 08:37:11 PM Link | Quote
The thread on Smashboards has devolved into the aforementioned goon persisting that he was the original author and that subsequent discussion and GameShark codes are in fact falsified, and whether another member should or should not kill himself for wasting air. I wish I was joking

So here's the relevant stuff...
__________

Why the Crash Happens

Remember the thing that started this all, what with Final Destination crashing when selecting it for a multiplayer match? The culprit begins right here:
80104C34    2F 01 00 10    SLTIU  AT, T8, 0x0010      ; Set AT if level is < 0x10

80104C38 10 20 00 18 BEQ AT, R0, 0x80104C9C
Two lines of code tucked into the Vs. stage loader check if the level happens to be 0x10 (a check immediately prior branches for everything greater than or equal to 0x11).

Here's what the code does: if Final Destination was set, the function then jumps to 0x80104C84, which has two function calls: first to 0x80192764, then to 0x80104850. If Final Destination was NOT set, then instead the function jumps to 0x80104C9C, where there's another call to 0x80104850. The code paths then merge after that. Why they didn't merge before calling 0x80104850, who knows. It was probably in the source code that way.

Obviously, that call to 0x80192764 is what's causing the problem. Turning that into some form of non-operation will prevent the crash from occurring. So the GameShark code in the original post can be cut down a couple of lines:
D0099113 0018

80099113 0019
D1104C84 0C06
81104C84 2000​
Regrettably, this more efficient version of the code isn't the one that will go down in history, because I already made a splash with the original one and some moron at Smashboards caused a scene with it. |-:

The contents of memory at 0x80192764 (the site of that function) are... well, undefined. Really. It's garbage data. It doesn't make any sense to program in a function call to garbage data, so it's clear that there used to be something there. Keep in mind that this code path only applies to the multiplayer stage loader. It's not processing any Master Hand-related code, it's just trying to execute nothingness and crashing.

This may be a lowly hypothesis, but hear me out... Whatever that function used to do only happened when Final Destination was being loaded in Vs. mode, and was since removed. Battlefield and Metal Mario's stage both load and play just fine in Vs. mode. But Final Destination, well, it has that background that changes according to Master Hand's HP. I know that I personally can't think of an elegant way to hook into that background code in a Vs. match, and what if HAL didn't have a good way either? Think about it: all versions of Final Destination since then have had timed scenery changes and are available in Vs. mode.

So I think it went like this: Final Destination's graphics weren't cooperating, so they left the stage out of Vs. mode. But then since they left out one special stage, they had to leave out the others too: Battlefield and Metal Mario's level, lest the players start asking questions about it.

Whatever the case, one thing is clear: it doesn't have anything to do with Master Hand.

__________

Random Override

One user asked if it was possible to replace the behavior of the "Random" stage selection, and user tehz as well as myself looked into it. What we found was that the stage select menu's cursor is checked against the value 9 (which is the "Random" selection) and rolls a random number if it was selected. It also checks if Mushroom Kingdom was rolled yet not unlocked, in which case it rolls another random number. Lastly, it checks the previously-rolled level and rolls a new one if the next one matches it, preventing the same stage from being selected twice in a row.

The code looks like this at runtime:
80133BBC    3C 04 80 13    LUI    A0, 0x8013          # Load stage select cursor from 0x80134BD8

80133BC0 8C 84 4B D8 LW A0, 0x4BD8 (A0)
80133BC4 24 01 00 09 ADDIU AT, R0, 0x0009
80133BC8 3C 11 80 0A LUI S1, 0x800A
80133BCC 14 81 00 0D BNE A0, AT, 0x80133C04 # If cursor == 9 then {
80133BD0 26 31 4A D0 ADDIU S1, S1, 0x4AD0 # Occurs before branch--S1 = 0x800A4AD0
80133BD4 0C 00 62 8C JAL 0x80018A30 # Roll a random number...
80133BD8 24 04 00 09 ADDIU A0, R0, 0x0009 # ... modulo 9 (occurs before function call)
80133BDC 00 04 08 25 OR S0, V0, R0
80133BE0 0C 04 C6 EB JAL 0x80131BAC # Check if Mushroom Kingdom was rolled, but not unlocked
80133BE4 00 40 20 25 OR A0, V0, R0 # V0 is non-zero if the check fails
80133BE8 14 40 FF FA BNEZ V0, 0x80133BD4 # If yes and no, roll another number
80133BEC 00 00 00 00 NOP
80133BF0 92 29 00 0F LBU T1, 0x000F (S1) # Load previous stage roll from 0x800A4ADF
80133BF4 12 09 FF F7 BEQ S0, T1, 0x80133BD4 # If we rolled it again, roll another number
80133BF8 00 00 00 00 NOP
80133BFC 10 00 00 06 BEQ R0, R0, 0x80133C18
80133C00 A2 30 00 0F SB S0, 0x000F (S1) # Occurs before branch--Write new rolled stage roll
# } else {
80133C04 0C 04 C9 0C JAL 0x80132430 # Load level index by stage select cursor value
80133C08 00 00 00 00 NOP
80133C0C 3C 11 80 0A LUI S1, 0x800A # S1 wasn't modified by the function, but whatever
80133C10 26 31 4A D0 ADDIU S1, S1, 0x4AD0
80133C14 A2 22 00 0F SB V0, 0x000F (S1) # Write new rolled (and translated) stage roll
# }
See that NOP at 0x80133BF8? The compiler put that in there because the instruction following a branch always executes before the program counter is updated. tehz used the GameShark to overwrite that instruction (remember, this code is loaded into RAM for faster access) to load a level index of our choosing into S0, effectively ignoring the result of the random number generator:
D1133C00 A230

81133BF8 2010
D1133C00 A230
81133BFA 00??
... where "??" is the index of the level to load instead of a random one. In effect, this adds a tenth stage to the stage select menu.

tehz also brought up the idea of expanding the pool of random levels to include such courses as the Metal Mario stage, Battlefield and Final Destination. The problem with that is that, unlike the first nine, the index values are not sequential, so you can't just change the number to divide at the end of the random number generation. So I worked on that one.

Instead of checking for Mushroom Kingdom in that one function call up there, I decided to redirect the program to a function of my own design instead that would use the output of the RNG to pick a stage from a custom pool. I found what I believe to be unused bytes in static memory at 0x800A4D40. So the modulo number needs to be changed from 9 to 12 (to incorporate the 3 stages I want to add), and the destination of the function call needs to be updated:
D1133BD8 2404

81133BDA 000C​
D1133BE2 C6EB
81133BE0 0C02
D1133BE2 C6EB
81133BE2 9350​
Clear as mud? The JAL instruction on MIPS is absolute, not relative, and since all instructions are 32 bits, the actual bus address is that value multiplied by 4. So in other words, 0x29350 * 4 = 0x(80)0A4D40. Aaaanyway...

The function I designed to go at 0x800A4D40 is as follows:
800A4D40    3C 10 80 0A    LUI   S0, 0x800A

800A4D44 02 04 80 20 ADD S0, S0, A0
800A4D48 82 10 4D 54 LB S0, 0x4D54 (S0)
800A4D4C 03 E0 00 08 JR RA
800A4D50 20 02 00 00 ADDI V0, R0, 0x0000
This adds the random number (stored in register A0 prior to the function call) to 0x800A4D54 and loads the byte at that address as the level index to use. When it exits, it clears register V0 so the branch in the calling function (the one that prevents a locked Mushroom Kingdom from being selected) doesn't get taken.

Since this code doesn't get swapped in and out depending on what the program is doing, it can be written unconditionally and save a lot of lines of GameShark code:
810A4D40 3C10

810A4D42 800A
810A4D44 0204
810A4D46 8020
810A4D48 8210
810A4D4A 4D54
810A4D4C 03E0
810A4D4E 0008
810A4D50 2002
810A4D52 0000​
Still kind of a lot, but it works.

So what's at 0x800A4D54, you ask? Well, it's the bytes immediately following this function soooo... whatever we want. In this case, I want 12 level indexes: 00, 01, 02, 03, 04, 05, 06, 07, 08, 0D, 0E and 10, for the stages I want. GameShark does that for us too:
810A4D54 0001

810A4D56 0203
810A4D58 0405
810A4D5A 0607
810A4D5C 080D
810A4D5E 0E10​
If you take the time to punch that into a GameShark and run the game, the Random option on the stage select menu works normally, except that all nine stages have a 1/12 chance of occurring, as well as 1/12 for Metal Mario's stage, Battlefield and Final Destination. Neat!
Next newer thread | Next older thread
Jul - General Game/ROM Hacking - Final Destination 64 New poll - New thread - New reply


Rusted Logic

Acmlmboard - commit 47be4dc [2021-08-23]
©2000-2022 Acmlm, Xkeeper, Kaito Sinclaire, et al.

30 database queries, 6 query cache hits.
Query execution time: 0.098924 seconds
Script execution time: 0.023290 seconds
Total render time: 0.122214 seconds