Hey guys.
A few weeks a go I worked on a small patch that will allow Super Mario Sunshine to render double the amount of frames it normally outputs. By changing Dolphin's frame limit (either by use of the VBeam hack or the frame limiter set to 120), we can take advantage of the extra frames by forcing all 60 frames to display in 1 second, thus making the game run at 60fps.
Watch a video here:
http://www.youtube.com/watch?v=9uoBTZxC3Rs
To use in Dolphin, just follow these steps (make sure you have the NTSC-U versions of SMS, GMSE01):
1.) Right click on the game in the game list and go to Properties
2.) Click the "Patches" tab and click "Add..."
3.) Give the patch a name, tick "dword", and set the Offset to 804167B8 and Value to 3F800000
4.) Click "OK", and make sure the patch is ticked.
5.) Click "Config" at the top of the main window, and under the "General" tab set "Framelimit:" to 120. OR - enable the Vbeam hack by right clicking the game in the games list, go to Properties, and put a checkmark next to the Vbeam hack.
6.) Click "OK" and play!
The patch has some side effects though:
1.) By increasing Dolphin's frame limiter, we also speed up the output of the audio. The video cut scenes are unaffected by the value change and so will play at the normal rate normally. But by increasing the Dolphin's frame limiter, the videos now play at double speed. Remarkably, the animations seem to work quite well and don't appear to break anything.
2.) The biggest problem with this patch is that the game won't allow you to enter the worlds from the rainbow goop on walls for some reason. You'd have to hack yourself into the worlds in order to play them (the video was taken from the demo version, so I could warp to those areas from a map select).
3.) Since the developers had to cut the framerate in half during development to improve the water effects, performance can take a hit here and there when using the turbo nozzle in ocean water. You can probably fix this by using the CPU overclock branch (or Tino's Iishiruka custom builds).
4.) You can't seem to finish Shadow Mario off with one last squirt.
As far as I can tell, there are 3 functions that look at the value that I modified for the patch. "drawDVDErr", "SMSGetAnmFrameRate", and "SMSGetVSyncTimesPerSec". It seems that most of the game's other functions take advantage of SMSGetAnmFrameRate the most. The function itself is relatively simple:
(NOTE: These addresses/function names come from the NTSC-U demo version of the game. For some reason, the developers decided to include the debug symbols only in the demo versions. The final retail versions don't have this. You can download the symbol mappings here, but if you want to look at the function in the NTSC-U final the function can be found at 0x802A7BD8)
.text:802A690C # =============== S U B R O U T I N E =======================================
.text:802A690C
.text:802A690C
.text:802A690C # SMSGetAnmFrameRate(void)
.text:802A690C SMSGetAnmFrameRate__Fv: # CODE XREF: TAnimalBase::execWalk((bool))+38p
.text:802A690C # TAnimalBase::execWalk((bool))+40p ...
.text:802A690C
.text:802A690C .set var_8, -8
.text:802A690C .set arg_4, 4
.text:802A690C
.text:802A690C mfspr r0, LR # Move from sprg,
.text:802A6910 stw r0, arg_4(r1) # Store Word
.text:802A6914 stwu r1, -0x18(r1) # Store Word with Update
.text:802A6918 stfd f31, 0x18+var_8(r1) # Store Floating-Point Double-Precision
.text:802A691C lfs f31, -0x3E8(r2) # Load Floating-Point Single-Precision
.text:802A6920 bl VIGetTvFormat # Branch
.text:802A6924 cmpwi r3, 2 # Compare Word Immediate
.text:802A6928 beq is_ntsc # load default ntsc framerate (60)?
.text:802A692C bge loc_802A6940 # Branch if greater than or equal
.text:802A6930 cmpwi r3, 0 # Compare Word Immediate
.text:802A6934 beq is_ntsc # load default ntsc framerate (60)?
.text:802A6938 bge is_pal # load default pal framerate (50)?
.text:802A693C b calc_basefps # patch: frame multiplier value? .5 by default
.text:802A6940 # ---------------------------------------------------------------------------
.text:802A6940
.text:802A6940 loc_802A6940: # CODE XREF: SMSGetAnmFrameRate(void)+20j
.text:802A6940 cmpwi r3, 5 # Compare Word Immediate
.text:802A6944 beq is_ntsc # load default ntsc framerate (60)?
.text:802A6948 b calc_basefps # patch: frame multiplier value? .5 by default
.text:802A694C # ---------------------------------------------------------------------------
.text:802A694C
.text:802A694C is_ntsc: # CODE XREF: SMSGetAnmFrameRate(void)+1Cj
.text:802A694C # SMSGetAnmFrameRate(void)+28j ...
.text:802A694C lfs f31, -0x3E8(r2) # load default ntsc framerate (60)?
.text:802A6950 b calc_basefps # patch: frame multiplier value? .5 by default
.text:802A6954 # ---------------------------------------------------------------------------
.text:802A6954
.text:802A6954 is_pal: # CODE XREF: SMSGetAnmFrameRate(void)+2Cj
.text:802A6954 lfs f31, -0x3E4(r2) # load default pal framerate (50)?
.text:802A6958
.text:802A6958 calc_basefps: # CODE XREF: SMSGetAnmFrameRate(void)+30j
.text:802A6958 # SMSGetAnmFrameRate(void)+3Cj ...
.text:802A6958 lfs f0, -0x408(r2) # patch: frame multiplier value? .5 by default
.text:802A695C lfs f1, -0x3E8(r2) # load default ntsc framerate? (60)
.text:802A6960 fmuls f0, f31, f0 # 60 * .5 = 30
.text:802A6964 fdivs f1, f1, f0 # 60/30 = 2
.text:802A6968 lwz r0, 0x18+arg_4(r1) # Load Word and Zero
.text:802A696C lfd f31, 0x18+var_8(r1) # Load Floating-Point Double-Precision
.text:802A6970 addi r1, r1, 0x18 # Add Immediate
.text:802A6974 mtspr LR, r0 # Move to sprg,
.text:802A6978 blr # Branch unconditionally
.text:802A6978 # End of function SMSGetAnmFrameRate(void)
The function basically looks at your current TV Render Mode and branches off to load what I assume is either the refresh rate or base framerate (in single precision floating point) for that particular render mode. After loading the value, the game loads a second value that pretty much serves as some kind of frame multiplier value that halves the base framerate by default. The patch I made modifies the value that gets multiplied into the base framerate so that instead of multiplying 60 by .5, it multiplies by 1.
This patch could definitely be more refined to help fix the other issues. I believe this patch could work on hardware as well, but we would need to find a way to change the game's frame buffer to allow 60 frames to be displayed in 1 second.
Since I kind of cheated by using a symbol list, I imagine that finding the values that affect the frame rate can be easily found in other games as well. Unfortunately, I spent many days trying to find something equivalent in Wind Waker but couldn't find anything that actually affects the frame rate. The symbol list in that game references the word 'framerate' a few times but they all seem to be related to the player actor's animations, not the game itself. The game seems to set the frame buffer somewhere during the "phase" functions, but I'm not sure how it works. The function names are difficult to understand and tracing calls the disassembly manually in IDA Pro alone is almost impossible unless you use Dolphin's debugger. I looked for data that could be modified in a similar way to the SMS patch (like 1.0/0.5/30.0/25.0 in floating point), but nothing seems to be relevant to the frame rate and affects entirely different functions. The game even seems to have it's own version of the GX NTSC rendering mode (g_ntscZeldaIntDf/g_ntscZeldaProg), for some reason.
I'm less than an amateur when it comes to this stuff, so I would love if someone could figure out how to unlock the framerate in Wind Waker/Twilight Princess, or any other game for that matter now that Dolphin can allow overclocking.
|