In order to edit this wiki, you must register and verify your account.
Cemu patches: Difference between revisions
ElijahPepe (talk | contribs) No edit summary |
ElijahPepe (talk | contribs) No edit summary |
||
Line 1: | Line 1: | ||
Starting with version [[Release_1.17.0|1.17.0]], Cemu supports patching gamecode via graphic packs. In previous versions of Cemu, patching gamecode was done through [[Cemuhook]] | Starting with version [[Release_1.17.0|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: | There are currently two different formats for patches supported: | ||
Line 5: | Line 5: | ||
* Cemu's own patch format (.asm) | * Cemu's own patch format (.asm) | ||
This | 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 [https://cemuhook.sshnuke.net Cemuhook] releases. | ||
== | == File structure == | ||
When a graphic pack is applied according to rules.txt | When a graphic pack is applied according to [[Graphic packs creation|rules.txt]], Cemu will scan the graphic pack folder for any files matching the pattern <span style="font-family: monospace; font-size: 95%">patch_<anything>.asm</span>. For every match that is found, the file is parsed according to the rules laid out below. | ||
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. | 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: | The header of a group should always look like this: | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
[group_name] | |||
moduleMatches = 0x11223344, 0xCFF30E4E | |||
</pre> | |||
The group name is generally only used for debugging. Any generated error message will refer to the group name and line number.<br /> | The group name is generally only used for debugging. Any generated error message will refer to the group name and line number.<br /> | ||
Line 25: | Line 26: | ||
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. | 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. | The most basic use case for patches is to replace individual instructions within a target module. | ||
Line 33: | Line 32: | ||
To do this, first prepend a line with <code>address = </code> Cemu will then put the directive that follows at the specified location. | To do this, first prepend a line with <code>address = </code> Cemu will then put the directive that follows at the specified location. | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
0x0200E3A4 = li r3, 0 | |||
0x0200E400 = nop | |||
</pre> | |||
=== 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. | 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. | ||
For this Cemu offers a write cursor that automatically increments with output. | |||
The current value of this pointer can be set with the <code>.origin = </code> directive. It is followed by a single parameter that specifies the unrelocated address. It also supports the keyword <code>codecave</code>, which will set the cursor to a location with unused space within the codecave region. | The current value of this pointer can be set with the <code>.origin = </code> directive. It is followed by a single parameter that specifies the unrelocated address. It also supports the keyword <code>codecave</code>, which will set the cursor to a location with unused space within the codecave region. | ||
==== | ==== Assemble method in codecave ==== | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
.origin = codecave | |||
someLabel: | |||
li r3, 0 | |||
blr | |||
</pre> | |||
==== | ==== Replacing instructions in the game's text section ==== | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
.origin = 0x0200E3A4 | |||
bla someLabel # written to 0x0200E3A4 | |||
blr # written to 0x0200E3A8 | |||
</pre> | |||
Note that the origin always has a lower priority than the per-line address. Using the <code>address = </code> prefix will also not increment the current write cursor. | Note that the origin always has a lower priority than the per-line address. Using the <code>address = </code> 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 :<br /> | Labels simplify branching and addressing of variables. To define a label write the name of the label followed by :<br /> | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
OurLabel: | |||
</pre> | |||
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. | 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. | You can even put labels inside the code or data section of the module. | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
0x0202034C = ExternalLabel: | |||
</pre> | |||
This is useful if you want to reference an external variable or branch to a game function from your own code. | This is useful if you want to reference an external variable or branch to a game function from your own code. | ||
Line 73: | Line 88: | ||
The format also has support for compile-time constants and expressions. | The format also has support for compile-time constants and expressions. | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
myConst = $presetVariable + 5 | |||
li r3, myConst | |||
</pre> | |||
Here myConst is assigned a value from a preset variable. | Here myConst is assigned a value from a preset variable. | ||
Expressions are supported almost everywhere, you could also write: | Expressions are supported almost everywhere, you could also write: | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
li r3, $presetVariable + 5 | |||
</pre> | |||
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]]. | 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]]. | ||
Line 108: | Line 129: | ||
|} | |} | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
SomeFloat: | |||
.float 123.45 # writes to the current write cursor position | |||
# load float from memory: | |||
lis r12, SomeFloat@ha | |||
lfs f0, SomeFloat@l(r12) | |||
</pre> | |||
== Porting Cemuhook patches to Cemu's format == | == Porting Cemuhook patches to Cemu's format == | ||
Line 129: | Line 153: | ||
'''Cemuhook''' | '''Cemuhook''' | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">TXT</span> <strong>patch_<anything>.asm</strong></div> | |||
</div><pre class="captioned"> | |||
[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 | |||
</pre> | |||
'''Cemu''' | '''Cemu''' | ||
<div style="background-color: #e4e8e9" class="box-caption"><span class="label" style="margin-right: .5em; background-color: #3498db">ASM</span> <strong>patches.txt</strong></div> | |||
</div><pre class="captioned"> | |||
[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 | |||
</pre> | |||
=See also= | =See also= |
Revision as of 01:07, 13 November 2021
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 | Unsigned 8bit byte |
.int | Signed 32bit integer |
.uint | Unsigned 32bit integer |
.ptr | Pointer; Alias for .uint |
.float | 32bit float |
.double | 64bit double |
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