How to Change Game Speed Independently of FPS (Example with Pokémon ORAS)

Hi everyone! Today I’m sharing a little discovery that will let you speed up your 3DS games.
I’ll use Pokémon Alpha Sapphire v1.4 as the demo.
After reverse engineering, here’s roughly the function that is called each frame:

C:
// mode = 0 => 60 FPS (16,666 µs)
// mode = 1 => 30 FPS (33,333 µs)
void runEachFrame() { // function is located at address: 0x0010E34C

  // This is what seems to speed up the game when switching from 30 -> 60 FPS
  // using Reshiban's codes 
  // https://gbatemp.net/threads/60-fps-patches-cheat-codes-releases-and-discussion.550527/
  bool shouldUpdate = true;
  if(mode) { // If 30 FPS, skip every other frame
    count ^= true;
    shouldUpdate = count;
  }

  if(shouldUpdate) {
    // Update game state: weather, lighting, game events, etc.
    update();
  } else {
    // ...
  }

  if(!mode || !shouldUpdate) {
    // Helps avoid major lags
    // If CPU and/or GPU are too slow, skip rendering the next frame
    // Keep the previous framebuffer because the previous render is not finished
    if(!skip) { 
      u64 start = GetSystemTick(); // svc 0x28
      // Draw everything on screen, swap buffers, etc.
      draw();

      u64 delta = GetSystemTick() - start;

      bool shouldSkip = false;
      u32 limit = mode ? 33333 : 16666; // 33,333 µs => 30 FPS

      // Measure GPU time, skip if overloaded (limit ~10,300 µs => 97 FPS)
      if(isGpuOverloaded() && getGpuProcessingTime() > limit) {
        shouldSkip = true;
      }

      // Check CPU time, skip if frame took too long (limit 16,666 µs => 60 FPS)
      if(isCpuOverloaded() && delta > 16666) {
        shouldSkip = true;
      }

      // Skip the next frame if overloaded
      if(shouldSkip) {
        skip = true;
      }

    } else {
      skip = false;
    }
  }
}

The idea to speed up the game is simple: just call all the functions inside update() multiple times (with a loop).
To slow down, it’s the same idea: call the function only every X frames.
Here’s the code for Pokémon:

Code:
[60 FPS + GAME SPEED]
0010E36C E3A06001 // mov r6, #1
0010E370 E3A0C00X // mov r12, #X
0010E374 E58FC010 // str r12, [pc, #0x10]
0010E378 E59FC00C // ldr r12, [pc, #0xC]
0010E37C E25CC001 // subs r12, r12, #1
0010E380 0A000075 // beq 0x0010E55C : update the view (draw()) and exit function
0010E384 E58FC000 // str r12, [pc, #0]
0010E388 EA00000E // b 0x0010E3C8 : update the model (update())
0010E38C FFFFFFFF // counter: .word 0xFFFFFFFF
0010E528 EAFFFF92 // b 0x0010E378 : loop again

Simply replace X with the value you want:
  • X=2 gives normal speed,
  • X=3 gives double speed, etc.
Note: I ignored the conditions if(!mode || !shouldUpdate) and if(!skip) to have true 60 FPS.

If you have any questions, feel free to ask.
The logic is the same for all 3DS games.

 
Does that mean you could make any game run at 60 FPS ?

Would it be possible with either the E or U version of Majora's Mask ?
Yes, in theory, it should be possible. You would need to find the function that is called in the game’s main loop which uses the svc 0x28 instruction, and then modify its code.

However, developers most likely locked the game at 30 FPS to maintain stable performance and avoid frame drops. For example, in Pokémon ORAS, menus such as the Pokémon Team or the Bag run at 60 FPS because they require fewer GPU resources. But when you move into the 3D overworld, the game drops to 30 FPS due to the higher rendering load.
 
Would that be an issue if you try to play on an emulator like Azahar ?

Also would the sound always be chopped off ?

No, I don’t think so.

That weird sound effect is caused by the game running faster because the same “update()” function is being called repeatedly; it’s not related to 60 FPS - the two are independent, don’t worry x)
 
  • Like
Reactions: Fyrus
Well, as much as I would like to understand where to start for MM, I don't understand code enough to do it

Hopefully someone more knowledgeable than me could take a spin at it
 
Yes, I tested it on a physical old 2DS, and it works x)
thank you, can you tell me please how do i put it into the 3ds? (should i copy the "code" or the "C"?, what folder should i put them into?, how to i change the speed (x) in the 3ds itself and how to turn it on midgame)
 
thank you, can you tell me please how do i put it into the 3ds? (should i copy the "code" or the "C"?, what folder should i put them into?, how to i change the speed (x) in the 3ds itself and how to turn it on midgame)

You can use my plugin to do it, it’s easier.

Use the latest version of Luma3DS.
Press L + D-Pad Down + Select -> Plugin Loader: Enabled

Put the sango-plugin-release.3gx file into:
/luma/plugins/000400000011C500

And make sure you are using Pokémon Alpha Sapphire v1.4 specifically!
 

Attachments

  • Like
Reactions: Oran223
You can use my plugin to do it, it’s easier.

Use the latest version of Luma3DS.
Press L + D-Pad Down + Select -> Plugin Loader: Enabled

Put the sango-plugin-release.3gx file into:
/luma/plugins/000400000011C500

And make sure you are using Pokémon Alpha Sapphire v1.4 specifically!

first of all thank you so much! also, can you play pokemon sun with it? (i want that for the endless dialogues) lastly how do you change the speed (x) midgame on the 3ds?
 
first of all thank you so much! also, can you play pokemon sun with it? (i want that for the endless dialogues) lastly how do you change the speed (x) midgame on the 3ds?
No, the plugin only works for the specific game and version mentioned; it has to be remade for each game. However, Pokémon Sun is based on the same engine as ORAS, so it’s easy to redo. Send me a memory dump using CTRPF if you want me to do it.
 
  • Like
Reactions: Oran223
No, the plugin only works for the specific game and version mentioned; it has to be remade for each game. However, Pokémon Sun is based on the same engine as ORAS, so it’s easy to redo. Send me a memory dump using CTRPF if you want me to do it.
i tried with gemini to figure out how do i send you the memory dump using CTRPF. basically i dont know how to do that so can you please tell me how to? also, like he told me to tell you: "Just so you know, I am playing a ROM hack called Pokémon Nova Sun (v1.9), layered over the official Pokémon Sun v1.2 update using Luma's LayeredFS" and my luma version is 13.4 and lastly its not nessecary for me to play the nova sun instead of the regular one if it makes it complucated.
 
i tried with gemini to figure out how do i send you the memory dump using CTRPF. basically i dont know how to do that so can you please tell me how to? also, like he told me to tell you: "Just so you know, I am playing a ROM hack called Pokémon Nova Sun (v1.9), layered over the official Pokémon Sun v1.2 update using Luma's LayeredFS" and my luma version is 13.4 and lastly its not nessecary for me to play the nova sun instead of the regular one if it makes it complucated.
Place this file: https://github.com/PabloMK7/CTRPluginFramework-BlankTemplate/releases/tag/v0.8.0
Into: luma/plugins/0004000000164800/

1. Launch the game.
2. Press [Select] to open the plugin menu.
3. Go to Options > Gateways RAM Dumper.
4. Press [Start], then [Select], and enter a name for the dump.
5. Send me the generated file located in: luma/plugins/0004000000164800/Dumps/
 
  • Like
Reactions: Oran223
Place niji-plugin.3gx in: /luma/plugins/0004000000164800/
Launch the game and enable the Game Speed cheat code, then press L or R in-game to change the game speed.
I cannot test the cheat code myself, so please let me know if it worked x)

Here is the plugin's source code:

C++:
#include <3ds.h>
#include <CTRPluginFramework.hpp>

namespace CTRPluginFramework
{
    static s32 s_game_speed = 1;

    bool OnGameUpdate(u32 manager) {
        bool result = true;
        for(s32 i = 0; i < s_game_speed; i++) {
            result = HookContext::GetCurrent().OriginalFunction<bool>(manager);
        }
        return result;
    }

    int main(void)
    {
        Hook hook;
        hook.InitializeForMitm(0x003F1AD0, (u32)OnGameUpdate);
        hook.Enable();

        PluginMenu *menu = new PluginMenu("Niji Plugin", 1, 0, 0, "Created By ZettaD");
        menu->SynchronizeWithFrame(true);
        menu->Append(new MenuEntry("Game Speed", [](MenuEntry *e){
            if(Controller::IsKeyPressed(Key::R)) {
                s_game_speed++;
                if (s_game_speed > 5) s_game_speed = 1;
                OSD::Notify(Utils::Format("Game Speed: %d", s_game_speed));
            }
            if(Controller::IsKeyPressed(Key::L)) {
                s_game_speed--;
                if (s_game_speed < 1) s_game_speed = 5;
                OSD::Notify(Utils::Format("Game Speed: %d", s_game_speed));
            }
        }));
        menu->Run();
        delete menu;
        return (0);
    }

}
 

Attachments

Place niji-plugin.3gx in: /luma/plugins/0004000000164800/
Launch the game and enable the Game Speed cheat code, then press L or R in-game to change the game speed.
I cannot test the cheat code myself, so please let me know if it worked x)

Here is the plugin's source code:

C++:
#include <3ds.h>
#include <CTRPluginFramework.hpp>

namespace CTRPluginFramework
{
    static s32 s_game_speed = 1;

    bool OnGameUpdate(u32 manager) {
        bool result = true;
        for(s32 i = 0; i < s_game_speed; i++) {
            result = HookContext::GetCurrent().OriginalFunction<bool>(manager);
        }
        return result;
    }

    int main(void)
    {
        Hook hook;
        hook.InitializeForMitm(0x003F1AD0, (u32)OnGameUpdate);
        hook.Enable();

        PluginMenu *menu = new PluginMenu("Niji Plugin", 1, 0, 0, "Created By ZettaD");
        menu->SynchronizeWithFrame(true);
        menu->Append(new MenuEntry("Game Speed", [](MenuEntry *e){
            if(Controller::IsKeyPressed(Key::R)) {
                s_game_speed++;
                if (s_game_speed > 5) s_game_speed = 1;
                OSD::Notify(Utils::Format("Game Speed: %d", s_game_speed));
            }
            if(Controller::IsKeyPressed(Key::L)) {
                s_game_speed--;
                if (s_game_speed < 1) s_game_speed = 5;
                OSD::Notify(Utils::Format("Game Speed: %d", s_game_speed));
            }
        }));
        menu->Run();
        delete menu;
        return (0);
    }

}
is there went thing i need to do with the plugin's source code? (i’m only able to test it in like 3 hours)
 

Site & Scene News

Popular threads in this forum