In order to edit this wiki, you must register and verify your account.

Cemu patches

From Cemu Wiki
Jump to:navigation Jump to:search

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:

ASM patch_<anything>.asm
[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.

ASM patch_<anything>.asm
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

ASM patch_<anything>.asm
.origin = codecave
someLabel:
li r3, 0
blr

Replacing instructions in the game's text section

ASM patch_<anything>.asm
.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 :

ASM patch_<anything>.asm
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.

ASM patch_<anything>.asm
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.

ASM patch_<anything>.asm
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:

ASM patch_<anything>.asm
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
ASM patch_<anything>.asm
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

TXT patches.txt
[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

ASM patch_<anything>.asm
[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

See also