Gitaroo Man Lives!/Cheat Codes/60 FPS
This code enables 60 frames per second in the USA version of Gitaroo Man Lives! instead of the usual 30 frames per second. It is intended for use with the PPSSPP emulator and is unlikely to work as expected on real PSP hardware.
The code
Code format: CWCheat (usable in PPSSPP)
_S ULUS-10207 _G Gitaroo Man Lives! [USA] _C0 60 FPS [beta v2] _L 0x20175900 0x24050001 _L 0x2017C47C 0x3C023EB8 _L 0x201504C4 0x340505DC _L 0x2012C79C 0x3C023DCC _L 0x2012C7EC 0x3C023DCC _L 0x2012C8D0 0x3C023E4C _L 0x2019FBB8 0x46002805 _L 0x201A4000 0x4600B005 _C0 Disable 60 FPS [beta v2] _L 0x20175900 0x24050002 _L 0x2017C47C 0x3C023F38 _L 0x201504C4 0x8C8501C8 _L 0x2012C79C 0x3C023E4C _L 0x2012C7EC 0x3C023E4C _L 0x2012C8D0 0x3C023ECC _L 0x2019FBB8 0x44800000 _L 0x201A4000 0x44800000 //If you enable/disable this code in a stage, quit and reload the stage to make it take effect. //Side effect of 60FPS: //-In stages, some 2D HUD stuff animates twice as fast //-Outside of stages, audio fades 2x slower
_S ULJM-05130 _G Gitaroo Man Live! [JP] _C0 60 FPS [beta v2] _L 0x2017545c 0x24050001 _L 0x2017bff8 0x3C023EB8 _L 0x20150090 0x340505DC _L 0x2012c500 0x3C023DCC _L 0x2012c550 0x3C023DCC _L 0x2012c634 0x3C023E4C _L 0x2019f1a4 0x46002805 _L 0x201a34b4 0x4600B005 _C0 Disable 60 FPS [beta v2] _L 0x2017545c 0x24050002 _L 0x2017bff8 0x3C023F38 _L 0x20150090 0x8C8501C8 _L 0x2012c500 0x3C023E4C _L 0x2012c550 0x3C023E4C _L 0x2012c634 0x3C023ECC _L 0x2019f1a4 0x44800000 _L 0x201a34b4 0x44800000 //If you enable/disable this code in a stage, quit and reload the stage to make it take effect. //Side effect of 60FPS: //-In stages, some 2D HUD stuff animates twice as fast //-Outside of stages, audio fades 2x slower
Explanation
Explanation is for the US version code. Other versions should be same except addresses.
Format:
[Address of this instruction in PSP memory]: [original instruction] change to: [new instruction] disable:[hex code of original instruction] enable: [hex code of new instruction] cwcheat address: [address of instruction in PSP memory - 0x08800000. used in the CWCheat code]
Some terms:
- alpha: transparency. Higher is more visible, lower is less visible.
- delta/delta value: Value that is added to or subtracted from something to get a new value. For example, if you decrease volume by 3000 every frame, 3000 is the delta value.
- immediate: A type of hardcoded value. (Specifically, it's a hardcoded value stored right inside of the MIPS assembly instruction that uses it.)
- You see these with instructions like li (load immediate) and lui (load upper immediate).
Set frameskip in stages (line 1)
08975900: li a1, 0x2 change to: li a1, 0x1 disable:24050002 enable: 24050001 cwcheat address: 00175900
Result: Stages run at 60 fps with fewer side effects than the previous attempt at this code. (Some things happen 2x too fast as a result of hardcoded values, many of which are fixed by other codes below.)
What the code does:
- Code disabled: This instruction executes during stage load, setting a frameskip value of 2.
- Code enabled: Same, but sets a value of 1 instead.
(For those curious, the value loaded here gets stored in memory at 0x08A1FC08 (PSP address space). It may be useful to change this value in memory when experimenting, e.g. to see which animations are affected by the frameskip value.)
How I found it: The previous attempt at a 60 FPS code (by GamerzHell9137 on the PPSSPP forums) had various issues. I tried to solve only the problem of game-over scenes proceeding too quickly, but I accidentally solved a bunch of other stuff as well.
- First I hacked in weird values for start and end frames of the Lost Pre scene in the .CHC file
- Then searched for those values in memory, found where the end Lost Pre frame is stored in memory.
- Using PPSSPP's debugger, I set a read breakpoint on that memory to find which instructions read from memory containing the Lost Pre end frame.
- Stopping at one of these instructions and stepping forward one instruction at a time, I found where the Lost Pre end frame value is used: the game compares the currently-playing Lost Pre frame to the Lost Pre end frame, presumably to decide whether it should play the Lose Post scene yet.
- Starting from where the currently-playing Lost Pre frame is compared, I made my way backwards through the instructions to find where its value comes from: (currently-playing Lost Pre frame) = (previous currently-playing Lost Pre frame) + (some delta value)
- My thought process: "If I can halve that delta value, lose scenes will proceed half as quickly."
- So I went backwards from there to find where it gets that delta value: delta value = something * 2
That 2 is loaded from somewhere memory; finding the source of this value and changing it made me realize it was the frameskip value, and it affected tons of stuff, not just lose scenes. Setting it to 1 fixed not just the problem I was trying to solve, but a number of other problems as well (such as non-tempo animations playing too fast).
Fix game over animations too fast (line 2)
0897C47C: lui v0,0x3f38 change to: lui v0,0x3eb8 disable:3C023F38 enable: 3C023EB8 cwcheat addr: 0017C47C
Result: During game-over scenes, tempo animations play at the correct speed (instead of 2x too fast)
What the code does:
- Code disabled: During gameplay, tempo animations have a speed determined by the current SongSection's tempo. Right as you lose, the instruction above executes, replacing every tempo animation's speed with a hardcoded value of 0.72.
- Code enabled: Same, but the hardcoded value is 0.36.
How I found it:
- Found an affected animation (Stage 4's Ben-K animation brk_x2), changed its length to some weird value by hacking the .GMO file, then searched for that value in memory.
- Using breakpoints in PPSSPP's debugger, found a number of instructions that read back that anim length
- Found where current anim frame is compared to anim length, traced back current anim frame to find where it's determined, found the anim frame delta
- Found out how anim frame delta is determined, traced back the values that determine that, eventually found a hardcoded 0.72 that would affect those anims if I changed it
Fix Gitaroo audio fading too fast (line 3)
089504C4: lw a1,0x1c8(a0) change to: li a1,0x5DC disable:8C8501C8 enable: 340505DC cwc addr: 001504C4
Result: When you release or hit the end a note, Gitaroo audio fades normally instead of 2x too fast.
What the code does:
- Code disabled: This instruction executes when Gitaroo audio fades, whether from releasing or hitting the end of a note. (Unfortunately it also executes for audio fades in the main menu and stuff, leading to a limitation described below.) It loads a value from memory, which in practice is always 0xBB8 (3000).
- Code enabled: Same, but instead of loading a value from memory, it sets hardcoded value of 0x5DC (1500).
(I could instead change the instruction that determines the value in memory in the first place, but it's only executed at boot, and I'd rather have something I can enable and disable without rebooting the game.)
Limitation of this code: Audio fades happen 2x too slow outside of stages. (To fix this, I'd have write some code that checks whether you're in a stage or not.)
How I found it:
- Finally got around to using Cheat Engine. I hit a note, advanced frames one at a time and released the note, then searched for decreasing values in memory via Cheat Engine. I found two promising values and tested them to be sure (turned out to be left and right channel volume for Gitaroo audio).
- Then I set a breakpoint in PPSSPP's debugger on these values, finding the instruction that wrote to them, then worked backwards to find how the new values were calculated, and looked for a delta value subtracted each frame.
- That delta value was loaded from memory rather than created from an immediate. By replacing this value, I could change how quickly or slowly audio fades out.
Fix radar fading in/out and rotating too fast (lines 4-6)
By "radar," I mean the blue fan-shaped zone you control when playing the game. (The graphic for it is named RADER.GIM.) Also, when I talk about radar fading in and out, I don't just mean how transparent it is, I also mean the little animation where the arrows open and close.
0892C79C: lui v0,0x3E4C change to: lui v0,0x3DCC disable:3C023E4C enable: 3C023DCC cwcheat addr: 12C79C 0x2012C79C 0892C7EC: lui v0,0x3E4C change to: lui v0,0x3DCC disable:3C023E4C enable: 3C023DCC cwcheat addr: 12C7EC 0x2012C7EC
Result: The radar fades in and out at the correct speed instead of 2x too fast.
What the code does:
- Code disabled: The first instruction above executes as the radar fades in, the second one executes as it fades out. Both instructions load a hardcoded value of 0.2, which is then added to or subtracted from the current radar fade value. (Radar fade value: 0.0 is invisible, 1.0 is fully visible.)
- Code enabled: Same, but the hardcoded value is 0.1.
How I found it:
- I found the radar fade value in memory accident while using Cheat Engine to find what makes phrase bars fade out when you miss them. (I was letting go of the analog stick at the same time, which makes the radar fade out.)
- From there, I set a write breakpoint to find which instruction writes to that memory, then worked backwards from that instruction to find when the radar fade value is added to or subtracted from, then further backwards to where the value to add/subtract exists in hardcoded form.
0892C8D0: lui v0,0x3ECC change to: lui v0,0x3E4C disable:3C023ECC enable: 3C023E4C cwc addr: 12C8D0 0x2012C8D0
Result: The radar rotates at the correct speed instead of 2x too fast.
What the code does:
- Code disabled: This instruction loads a hardcoded value of 0.4. Later on (not shown here), this value helps determines the radar rotation delta value (amount to add to/subtract from current radar rotation).
- Code enabled: Same, but the hardcoded value is 0.2.
How I found it:
- I set a breakpoint somewhere near the stuff above, advanced a frame at a time while rotating the analog stick to different positions, and paid attention to the contents of the floating-point registers.
- There were several registers that contained the current angle of the analog stick (in radians, not degrees), but it took several breakpoints to find a register containing the current angle of the radar itself, which gradually increases from the previous analog stick rotation value to the current one.
- A note on finding stuff in radians: Try rotating your analog stick in 90-degree increments, like straight up or right. When you do this, approximate float values to look for (since they won't be exact) are 0, half of pi (1.5707...), pi (3.1415...), 3/2 of pi (4.712...), and maybe
tau2*pi (6.283...).- These values are equivalent to 0/90/180/270/360 degrees (counterclockwise starting from 3 o'clock, though Gitaroo Man might start from 9 o'clock for some reason...)
- Be aware, rotation values of higher than 6.283 and less than 0 are also valid, just as 720 degrees is. The angle of the radar was like this, making it hard to find it at first.
- A note on finding stuff in radians: Try rotating your analog stick in 90-degree increments, like straight up or right. When you do this, approximate float values to look for (since they won't be exact) are 0, half of pi (1.5707...), pi (3.1415...), 3/2 of pi (4.712...), and maybe
Fix letter ghost on results screens (line 7-8)
By "letter ghost" I mean that on the results screens, when the letter grade pops up, it has a "ghost" graphic behind it that quickly grows as it fades away to nothing.
0899FBB8: mtc1 zero,f0 change to: abs.s f0,f5 disable:44800000 enable: 46002805 cwc addr: 0019FBB8
Result: Makes the letter ghost fade normally on the single-player results screen. Without this code, at 60 FPS the letter reappears when it's done fading.
089A4000: mtc1 zero,f0 change to: abs.s f0,f22 disable:44800000 enable: 4600B005 cwc addr: 001A4000
Result: Same as above, but fixes it on the multiplayer results screens (VS and Duet).
What the codes do:
- Codes disabled: The instruction executes during the letter ghost's animation. It sets a hardcoded value of 0.0, and later instructions (not shown here) compare the letter ghosts's transparency value to that 0.0 to decide when to end the animation.
- Codes enabled: Same, but instead of 0.0 it uses 0.01. (Specifically, it takes the absolute value of another floating-point register, which is always -0.01 at this point.)
but why:
- At 60 FPS, the game already knows to fade the ghost by half as much per frame. This is the correct speed and should be fine, except...
- If you start with 1.0 and repeatedly subtract 0.02 from it, you will end up with exactly 0.0 and everything is fine. However, if you repeatedly subtract 0.01 (which the game does at 60 FPS), you will end up with very slightly above 0.0 due to floating-point inaccuracy.
- So since the ghost transparency isn't 0.0 yet, the game fades it one more time, giving it a negative transparency. Which makes it visible again.
- So ideally, instead of checking if ghost transparency is 0.0 or less, we'd check if it's
very slightly above 0.0
or less. - As it so happens, at the time this instruction executes, one of the floating-point registers contains a value of -0.01. The absolute value of that, 0.01, would be perfect for our
very slightly above 0.0
value.- Working backwards, I found that in both cases, the value was the result of other hardcoded values, meaning it would always be this -0.01 at this instruction.
- So instead letting the game load 0.0, I make it load the absolute value of that other register, i.e. 0.01. As a result, the ghost stops fading when its transparency reaches less than 0.01, leading to correct behavior.
- By the way, this means the letter ghost does not fade out completely—it ends with a transparency of very slightly above 0.0. But this results in no actual difference in the pixels shown onscreen, so the nitpicking part of my personality can go suck it!!!
How I found it:
- Used Cheat Engine, waited for the the letter ghost to appear and start fading, then advanced frame by frame while searching memory for decreasing values
- Once I found that value (ghost alpha) in memory, I set a breakpoint on it to find the instructions that used it
- found a compare instruction that would make the ghost stop fading after a certain point (in this case, when it reaches 0)
- set a breakpoint at the compare instruction, played the ghost fade animation again frame by frame while paying attention to the value of ghost alpha
- (the rest of the process from here on out was already described above, i.e. realizing the ghost alpha became negative and finding a way to prevent that)
Current issues
- Audio fades 2x too slow outside of stages (e.g. when you enter or exit menus)
- Current code has no effect on 30 FPS animations outside of stages (i.e. spinning faces in main menu, growing/shrinking backgrounds in submenus)
- Note: menus already animate at 60 FPS, something else is making those anims 30 FPS
Things that animate 2x too fast:
- health bars (flashing and gain/lose)
- phase indicators (CHARGE/BATTLE/etc)
- the words GREAT/GOOD/OK/MISS
- fade-out of the phrase bar when you drop a note partway through
- the big green fading circle when you hit a note
- fade-in/fade-out of tutorial stuff like word bubbles
Things I haven't tested but probably should:
- any effect on health gained/lost when you release a note early?