Results 1 to 6 of 6
  1. #1
    Super-Moderator epigramx's Avatar
    Join Date

    HOWTO modify game assembly using patches.txt, for example forcing WWHD to 60FPS

    Disclaimer: this is meant as a mini-HOWTO to getting started on assembly patching. It isn't meant as a full guide to turning WWHD to 60FPS which can be an extremely complex process!

    Step 1: Have the game executable. This is an .rpx file in code/. If you have an update to the game, the .rpx you need is in code/ of the update and not the vanilla game.

    For example, for WWHD:

    Step 2: Create a "graphic" pack for Cemuhook that has a patches.txt. This is more accurately an "assembly code pack". You can find an example in "LWZX Crash workaround" that comes with current Cemuhook (as of this writing) and for rules.txt you can find an example at any WWHD (for example) graphic pack.

    For instance, here is the complete barebones rules.txt for a "WWHD mod" pack:

    titleIds = 0005000010143400,0005000010143600,0005000010143500
    name = "The Wind Waker HD - mod"
    version = 2
    For patches.txt you must use the correct "moduleMatches" checksum, as instructed by Cemuhook. The full text of the instructions of Cemuhook (from readme_patches.txt):

    Place patches.txt next to rules.txt of graphics pack
    Inside of it, you can have multiple groups (no variables outside of groups, also, give unique names to each of your groups, otherwise their contents will just overwrite each other)
    No patch groups from a given patches.txt will even be considered for activation if that graphics pack's rules.txt havent properly loaded in cemu, or if it's not enabled in the normal Graphic Packs GUI
    Otherwise, each group will be activated on module load, if that module's "Checksum" (visible from the newly added "View RPL Modules" window in Debug menu) is inside the "moduleMatches" list of that group
    Assembler follows "MacOsX" syntax, uses the Keystone library ( so make sure the keystone.dll is not missing if you want assembled lines to work
    All the offsets should be written for a module's defined virtual address, before relocation (as relocation will be applied to all symbols and addresses on the left side, as well as the right side of .ptr entries)
    All user defined symbols MUST start with _, and there are a few predefined symbols as well (PC equals the current relocated PC, but you can use builtin . for that as well, and there is a SECTION_XXXX for all the module's sections)
    Since, code cave support was added because otherwise there really is no place to put additional instructions in
    There can be one code cave per group, they aren't shared in any way, and it's size is specified with codeCaveSize integer variable inside the group
    If a code cave size on a group is set, any patches for addresses in the range of [0,codeCaveSize) will be placed (and properly relocated) into the cave area, which is dynamically allocated
    There is max 983040 bytes available for code caves in total, and the limit for each code cave (remember, max one per group) is 64K, an error will appear if you set it to anything higher (and given value will be clamped to 64K)
    CAVE_BEGIN and CAVE_END symbols correspond to the relocated addresses of the current group's cave start and end, respectively (they are undefined if there is no code cave in the group)
    To jump to code inside of your code cave, just define a symbol with value = the offset on the left side of the instruction you want to jump to, and then use "ba _symbolName" or "bla _symbolName"
    To get more verbose patches.txt loading/assembly messages in your log.txt, add the following lines to your cemuhook.ini:
    logPatchAssembly = true
    logPatchLoading = true
    As instructed above, to find the right checksum for "moduleMatches", while the game you're interested in is running, simply open the "View RPL Modules" window and find the Checksum:

    Long story short, a bare-bones patches.txt for WWHD EU would look like this:

    moduleMatches = 0xB7E748DE 
    ; assembly commands will be written here
    Read the rest of the Cemuhook instructions above by Rajkosto for more details about the format of the file (preferably from the latest version of Cemuhook's zip, if it has been updated after this post).

    Step 3: Let's replace an assembly command in WWHD based on disassembled code:

    First, you will need to disassemble the .rpx file found on Step 1. The popular way currently is by using IDA Pro. This is a non-Free program.

    To allow Wii U disassembly, aerosoul has released a Wii U loader for IDA which you can find here:

    Instructions on how to use it are included, but mainly drop the file in loaders/ of IDA.

    Open the 32bit version of IDA (not the 64bit one, Wii U is technically a 32bit machine), and then load the .rpx on it, after having used the loader of aerosoul as just instructed:

    According to Rajkosto also change Instruction set support from AltiVec to PS in Processor options. This enables 64bit floating point instruction set support for the Espresso CPU named Paired Singles.

    Now, after the executable is disassembled, and IDA takes its time to build a database (it can take a few minutes on slow machines), you can start searching for code to change with patches.txt.

    Let's make WWHD run at 60FPS. That can be done by turning swap interval's parameter from "2" to "1". This is because Cemu (currently, on 1.9.1) has a global vsync restriction of 60 times per second (possibly able to be altered in the future with a profile option but let's work with that for now).

    Go to the "Imports" tab of IDA and search (ctrl-f) for "swapinterval", and here it is

    Let's alter the parameter entered to that function from 2 to 1.

    We first have to find out on what memory address GX2SetSwapInterval is used

    First double click on the above to go to the .extern reference

    Then press "X" or find the equivalent with right-click to go to the code that uses it (it only uses it once in this case):

    (if you only see a "graph" view, right click and go to text view)

    And by that we basically know that the command we are interested in is at address # 274C3B8.

    To alter swap interval's paremeter from 2 to 1 we can alter the assembly command above the call to GX2SetSwapInterval to "li r3, 1" which as we can see is at address 0x0274C3B4 (add the "0x" in front to denote it's a hexadecimal number for the use of patches.txt)

    And basically our patches.txt now becomes:

    moduleMatches = 0xB7E748DE 
    0x0274C3B4 = li r3, 1 ; set GX2SetSwapInterval's parameter from 2 to 1
    By putting that rules.txt and that patches.txt in a pack and loading it, our WWHD EU is now 60FPS if the system is fast enough:

    You can verify the patch works correctly by looking into log.txt of Cemu after enabling the debug options of patching as instructed by Cemuhook:
    Applying patch group WWHD from pack 'The Wind Waker HD - mod' to module mainModule (checksum 0xB7E748DE)
    Assembled 'li r3, 1' for address 0x0274c3b4 (relocated: 0x0174c3b4) to 0x38600001
    PS. Now the real work would begin. This particular game does not have a dynamic FPS feature so it will run at double speed this way. Hence one would have to find code in disassembly that alters the time-step of the game, they would possibly need to turn it into a dynamic FPS mod because even fast current systems can't sustain 60FPS at all times (the game is badly optimized at particle effects and ..grass rendering), and the cave method would possibly be needed to be employed as instructed by readme_patches.txt of Cemuhook.

    PPS. Furthermore patches of this particular sort would possibly introduce several bugs or quirks that would need to be addressed. One only had to look at the complexity of making the BotW mod behave correctly. Alternatively look at how complex similar mods for the GC/Wii can become.

    Relevant video by Xalphenos:

    Last edited by epigramx; 25.05.2018 at 08:38.
    Author of WiimoteHook; co-author of Static FPS mods, author of guides to game assembly patching , compiling mesa and high-res screenshots

  2. #2
    Super-Moderator epigramx's Avatar
    Join Date
    Relevant Resources:

    Interchanging addresses between PPC and Cheat Engine (x86_64):

    Assembler code information:
    A PPC Aseembly tutorial (originally for the Wii):
    A basic PPC tutorial:
    Commands reference:

    Turning regular code to PPC assembly (for use in patches.txt caves):
    devkitPro/devkitPPC (for use in IDA after a binary is compiled):
    Last edited by epigramx; 13.09.2017 at 17:13.

  3. #3

  4. #4
    Super-Moderator epigramx's Avatar
    Join Date
    Quote Originally Posted by Xalphenos View Post
    Thanks Xalphenos.

  5. #5
    Super-Moderator epigramx's Avatar
    Join Date

  6. #6
    Super-Moderator epigramx's Avatar
    Join Date
    Cemu 1.10+ has a new feature to alter frequency via graphics packs. A clarification:

    The global interval of Cemu used to be 60Hz. When games like TPHD or WWHD restricted themselves to 30FPS, they did it via setting the swap interval to 2 and hence they were 30FPS via their vsync setting.

    That means if one wants to set a game like that to 60FPS they could alternatively set the global frequency to 120Hz. 75FPS might be a good goal for mods since many monitors can be overclocked to that.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts