In order to edit this wiki, you must register and verify your account.
Cemu patches
Starting with version 1.17.0, Cemu supports patching gamecode via graphic packs. In previous versions of Cemu, patching gamecode was done through Cemuhook.
There are currently two different formats for patches supported:
- Cemuhook's patches.txt
- Cemu's own patch format (.asm)
This article documents the new .asm format (referred to as Cemu patches). For details on Cemuhook's format see example_patches.zip that is bundled with Cemuhook releases.
File structure
When a graphic pack is applied according to rules.txt, Cemu will scan the graphic pack folder for any files matching the pattern patch_<anything>.asm. For every match that is found, the file is parsed according to the rules laid out below.
Patch groups
Patches are divided into groups. Each group can apply to one or multiple modules based on a CRC match. The header of a group should always look like this:
[group_name] moduleMatches = 0x11223344, 0xCFF30E4E
The group name is generally only used for debugging. Any generated error message will refer to the group name and line number.
moduleMatches
is a list of CRCs that identify one or multiple RPX/RPL files. The patch group will be applied to any module that has a matching CRC. The CRC's of modules can be looked up in log.txt
Multiple patch groups can be in a single file. The instructions within a patch group can also reference labels or constants defined in any other patch group as long as they are activated for the same module (same CRC). Patch groups however cannot access anything outside of the current graphic pack.
Patching individual instructions
The most basic use case for patches is to replace individual instructions within a target module.
To do this, first prepend a line with address =
Cemu will then put the directive that follows at the specified location.
0x0200E3A4 = li r3, 0 0x0200E400 = nop
The write cursor
When replacing multiple instructions or when writing a larger function it can become tedious to manually keep track of the address for every line. For this Cemu offers a write cursor that automatically increments with output.
The current value of this pointer can be set with the .origin =
directive. It is followed by a single parameter that specifies the unrelocated address. It also supports the keyword codecave
, which will set the cursor to a location with unused space within the codecave region.
Assemble method in codecave
.origin = codecave someLabel: li r3, 0 blr
Replacing instructions in the game's text section
.origin = 0x0200E3A4 bla someLabel # written to 0x0200E3A4 blr # written to 0x0200E3A8
Note that the origin always has a lower priority than the per-line address. Using the address =
prefix will also not increment the current write cursor.
Labels
Labels simplify branching and addressing of variables. To define a label write the name of the label followed by :
OurLabel:
This will create a label at the current position of the write pointer. The label will automatically be relocated so that it always points to where the code is in memory. You can even put labels inside the code or data section of the module.
0x0202034C = ExternalLabel:
This is useful if you want to reference an external variable or branch to a game function from your own code.
Constants and expressions
The format also has support for compile-time constants and expressions.
myConst = $presetVariable + 5 li r3, myConst
Here myConst is assigned a value from a preset variable. Expressions are supported almost everywhere, you could also write:
li r3, $presetVariable + 5
Keep in mind that while constants act similar to variables, they don't allocate any space and cannot be used as a target for memory load/store instructions. For addressable variables see section #Data directives.
Data directives
In addition to PowerPC instructions the assembler also supports emitting several data types:
.byte | 8bit integer (a byte) |
.short | 16bit integer |
.int | 32bit integer |
.ptr | Pointer; Alias for .int |
.float | 32bit float |
.double | 64bit double |
.string | String of arbitrary length |
SomeFloat: .float 123.45 # writes to the current write cursor position # load float from memory: lis r12, SomeFloat@ha lfs f0, SomeFloat@l(r12)
Porting Cemuhook patches to Cemu's format
Be careful when porting patches to the new format. Both formats support the name = value
syntax but the meaning is different.
For example:
name = 0x12345
Cemuhook treats this as a pointer to the address 0x12345, always applying relocation to it. Whereas Cemu treats it as a variable with the value 0x12345 with no further processing.
To get the same result in Cemu .asm patches write:
name = reloc(0x12345)
or
0x12345 = name:
A sample patch in both formats
Cemuhook
[PatchName] moduleMatches = 0x12345678 # code Cave codeCaveSize = 0x24 # variable to hold preset parameter _ourVariable = 0x0000000 0x0000000 = .int $gfxPackPresetVariable # assemble function in code cave _codeCaveFunction = 0x0000004 0x0000004 = lis r11, _ourVariable@ha 0x0000008 = lwz r11, _ourVariable@l(r11) 0x000000C = cmpwi r11, 1 0x0000010 = bne .+0x0C 0x0000014 = li r3, 0 0x0000018 = blr 0x000001C = addi r3, r3, 1 0x0000020 = blr # patch game code to jump to our code cave function 0x21EFAA8 = bla _codeCaveFunction
Cemu
[PatchName] moduleMatches = 0x12345678 .origin = codecave # all follow-up instructions and variables will be written into the code cave # variable to hold preset parameter _ourVariable: .int $gfxPackPresetVariable # assemble function in code cave _codeCaveFunction: lis r11, _ourVariable@ha lwz r11, _ourVariable@l(r11) cmpwi r11, 1 bne label_skip li r3, 0 blr label_skip: addi r3, r3, 1 blr # patch game code to jump to our code cave function 0x21EFAA8 = bla _codeCaveFunction