More actions
(Created page with "{{#seo: |title=Wii Rom Hacks (Hack Utilities) - GameBrew |title_mode=append |image=wiinjectwii.jpg |image_alt=Wiinject }} {{Infobox Wii Homebrews |title=Wiinject |image=wiinje...") |
No edit summary |
||
(10 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{Infobox Wii Homebrews | {{Infobox Wii Homebrews | ||
|title=Wiinject | |title=Wiinject | ||
|image= | |image=wiinject2.png | ||
|description= | |description=A cross-platform tool for injecting ASM hacks into Wii games using Riivolution memory patches. | ||
|author=Jonko | |author=Jonko | ||
|lastupdated= | |lastupdated=2022/07/07 | ||
|type=Hack Utilities | |type=Hack Utilities | ||
|version=1.1 | |version=1.2.1 | ||
|license=Mixed | |license=Mixed | ||
|download=https://dlhb.gamebrew.org/wiihomebrews/ | |download=https://dlhb.gamebrew.org/wiihomebrews/wiinject.7z?k33p0fil35 | ||
|website=https:// | |website=https://github.com/jonko0493/Wiinject | ||
|source=https://github.com/jonko0493/Wiinject | |source=https://github.com/jonko0493/Wiinject | ||
}} | }} | ||
https://www. | Wiinject is a tool that allows you to inject ASM hacks into Wii games using [[Riivolution Wii|Riivolution]] memory patches. It takes a folder containing PowerPC assembly files (.s) and C files (.c) as input, along with injection sites, and outputs a memory file and a series of Riivolution XML memory patches. It is cross-platform. | ||
Wiinject relies on the [https://www.keystone-engine.org/ Keystone Engine] to assemble code and [https://devkitpro.org/ devkitPro] to compile C code. | |||
== User guide == | |||
===Prerequisites=== | |||
* The [https://dotnet.microsoft.com/en-us/download/dotnet/6.0 .NET 6.0 runtime]. | |||
* [https://devkitpro.org/wiki/Getting_Started devkitPro] if compiling C code. | |||
===CLI Options=== | |||
* <code>-f|--folder</code> – The folder where your source files live | |||
* <code>-m|--dolphin-map|--map|--symbols</code> – A Dolphin symbols map for any built-in functions you want to reference by name | |||
* <code>-i|--injection-addresses</code> – The addresses to inject function code at, comma delimited. The code at these addresses should be safe to overwrite. | |||
* <code>-e|--injection-ends</code> – The addresses at which the above injection sites end (are no longer safe to overwrite), comma delimited. If the code is unable to fit in any of these injection sites, an error will be thrown. | |||
* <code>-o|--output-folder</code> – The folder to output the Riivolution patch.xml & assembled ASM bin file to. | |||
* <code>-n|--patch-name</code> – The name of the patch to output. The patch will be output to <code>{output_folder}/Riivolution/{patch_name}.xml</code> and the ASM bin will be output to <code>{output_folder}/{patch_name}/patch.bin</code>. | |||
* <code>-p|--input-patch</code> – The base Riivolution patch that will be modified by Wiinject to contain the memory patches. A blank base template will be created if this is not provided. | |||
* <code>d|devkitpro-path=</code> ‐ The path to a devkitPro installation containing devkitPPC (e.g. <code>C:\devkitPro</code> or <code>/opt/devkitpro</code>) | |||
* <code>--console-output</code> – Rather than producing an ASM patch, simply output the XML to the console. This will still save the ASM bin, however. | |||
* <code>--emit-c</code> – Emits assembled C functions to the console so you can modify your assembly calls to those functions to work with the registries used by the compiler. | |||
==== Structuring Your Source Code and Preparing Your Initial Patch ==== | |||
Wiinject expects the <code>folder</code> where your source lives to have one subdirectory for each patch element you wish to generate. For example, if you'd like your final Riivolution patch to contain one optional patch for translating the game and another for reducing monster spawns, name one subdirectory something like <code>Translation</code> and the other <code>ReduceMonsterSpawns</code>. Then, place your source files relevant to those patches in those directories. | |||
When preparing your input patch, make sure you set up the options yourself and ensure that the patch names in the options match the names of the subdirectories in your source <code>folder</code>. Finally, add any patch elements that have non-memory patches. Wiinject will automatically create patch elements that don't exist and append to ones that do. | |||
If you've followed along, your input patch should look something like this: | |||
<pre><wiidisc version="1"> | |||
<id game="R42069" /> | |||
<options> | |||
<section name="Translation"> | |||
<option name="Translation"> | |||
<choice name="Enabled"> | |||
<patch id="Translation" /> | |||
</choice> | |||
</option> | |||
</section> | |||
<section name="Quality of Life"> | |||
<option name="Reduce Monster Spawns"> | |||
<choice name="Enabled"> | |||
<patch id="ReduceMonsterSpawns" /> | |||
</choice> | |||
</option> | |||
</section> | |||
</options> | |||
<patch id="Translation"> | |||
<folder external="/Game/files" recursive="true" disc="/" /> | |||
<folder external="/Game/files" /> | |||
</patch> | |||
</wiidisc></pre> | |||
=== Writing ASM === | |||
Wiinject uses the Keystone Engine to assemble standard PowerPC assembly. To write an assembly file that Wiinject can parse, however, you need to use special function names. | |||
Here is a sample Wiinject-compatible assembly file: | |||
<pre>hook_80017250: | |||
start: | |||
add 5,5,0 | |||
mr 26,3 | |||
cmpwi 5,3 | |||
beq end | |||
li 5,2 | |||
end: | |||
blr | |||
hook_80017254: | |||
mr 3,26 | |||
blr | |||
repl_80017260: | |||
mr 5,25 | |||
li 6,7 | |||
ref_801BBB38: | |||
li 6,7 | |||
blr</pre> | |||
The <code>hook</code>s indicate which instructions to replace with a branch instruction to the function provided. The <code>repl</code> indicates a location to start overwriting instructions directly with the instructions provided. The <code>ref</code> indicates a location to write a reference to the function provided (useful for hooking into functions that use <code>bctrl</code>, etc.). | |||
=== Using Variables in ASM === | |||
Wiinject allows for a special notation to store and interact with variables. Variables can be defined anywhere in an ASM file with <code>$variable:</code>, e.g. <code>$array: .skip 16</code>. You can then load that variable into memory with the <code>lv</code> command. <code>lv</code> is resolved to an <code>lis</code> followed by an <code>addi</code> in the assembling process. | |||
Example: | |||
<pre>$variable: .int 0 | |||
$array: .skip 16 | |||
hook_80017250: | |||
lv 2,$variable | |||
lv 3,$array | |||
blr</pre> | |||
=== Writing C === | |||
For each ASM file, you may also provide a companion C file to compile and inject methods which may then be called from the ASM. In order to use this functionality, you must install [https://devkitpro.org/wiki/Getting_Started devkitPro] and provide Wiinject with the path to the devkitPro installation (e.g. <code>C:\devkitPro</code> or <code>/opt/devkitpro</code>). | |||
Injected C methods are called from the ASM via <code>bl =method_name</code>. The assembly function caller will need to handle stack manipulation and inputs itself. You can expect a compiled C method to accept inputs sequentially starting with <code>r3</code> and to place its return value in <code>r3</code>. However, in order to verify this, you can use the <code>--emit-c</code> flag while calling Wiinject to view the compiled C code's assembly so you can adjust your assembly caller appropriately. | |||
Here is a sample Wiinject-compatible C file named ''font_hack.c'': | |||
<pre>int font_offset(char character) | |||
{ | |||
switch (character) | |||
{ | |||
case 'A': | |||
return 0x180; | |||
case 'I': | |||
case 'i': | |||
case 'l': | |||
case '!': | |||
return 0x48; | |||
default: | |||
return 0x90; | |||
} | |||
}</pre> | |||
And here is its companion ASM file named ''font_hack.s'': | |||
<pre>hook_8001726C: | |||
stwu 1,-24(1) | |||
mflr 0 | |||
stw 0,20(1) | |||
stw 31,16(1) | |||
mr 31,1 | |||
stw 9,12(1) | |||
stw 3,8(1) | |||
mr 3,26 | |||
cmpwi 3,75 | |||
bl =font_offset | |||
lwz 0,20(1) | |||
mtlr 0 | |||
mr 0,3 | |||
lwz 9,12(1) | |||
lwz 3,8(1) | |||
addi 11,31,24 | |||
lwz 31,-4(11) | |||
mr 1,11 | |||
blr</pre> | |||
=== Using Symbols === | |||
You can provide Wiinject with a Dolphin symbols map and use that to reference functions existing in the ASM in the same way you would reference C functions. | |||
Map file: | |||
<div class="snippet-clipboard-content notranslate position-relative overflow-auto"> | |||
<pre class="notranslate">.text section layout | |||
80004000 00000050 80004000 0 memcpy</pre> | |||
Assembly: | |||
<pre>hook_8001726C: | |||
stwu 1,-24(1) | |||
mflr 0 | |||
stw 0,20(1) | |||
bl =memcpy | |||
lwz 0,20(1) | |||
mtlr 0 | |||
addi 1,24 | |||
blr</pre> | |||
=== Limitations === | |||
* Wiinject only supports the <code>bl</code> command for C functions or functions defined in your Dolphin symbols map; functions defined in assembly cannot currently be branched to. | |||
* The [https://wiibrew.org/wiki/Paired_single paired single operators] are not available. | |||
==Changelog== | |||
'''v1.2.1''' | |||
Fixes: | |||
*Complex switch statements written in C compile to jump tables which were not supported by Wiinject. This adds support for jump tables and thus makes these switch statements work as intended. | |||
*Wiinject errors were sometimes hard to debug due to vague error messages. This adds more detailed error messages for certain cases that should make things easier to debug. | |||
'''v1.2.0''' | |||
*Upgraded to .NET 6.0. | |||
*Variables in assembly implemented. | |||
*Added support for referencing built-in functions using a Dolphin symbols map. | |||
*Added ref and hex assembly tags which allow for specifying that the address should be written rather than a bl instruction and specifying raw hex to be written, respectively. | |||
*Refactored the CLI so Wiinject can be packaged as a NuGet package. | |||
*Add support for multiple patch elements. | |||
Not in this release but just noticed: | |||
*We actually do support multi-file C lmao. | |||
'''v1.1.0''' | |||
*Wiinject is now self-documenting (not providing arguments will print the options list). | |||
*Added repl assembly tag in addition to hook. repl allows for directly replacing assembly at the target address rather than inserting a branch instruction. | |||
*Added support for multiple injection sites so you can split your code among multiple safe-to-overwrite locations on the ROM. | |||
*Added support for compiling and injecting C code. Compilation is done using devkitPro and functions can be referenced from ASM via bl=c_function_here | |||
Fixes: | |||
*Customized Keystone's C# binding so it actually works cross-plat. | |||
*Keystone now throws errors when encountering assembly it can't assemble. | |||
'''v1.0.0''' | |||
*Initial release of Wiinject. | |||
== | ==External links== | ||
* GitHub - https://github.com/jonko0493/Wiinject | |||
* Romhacking.net - https://www.romhacking.net/utilities/1660/ |
Latest revision as of 10:32, 28 Ocak 2024
Wiinject | |
---|---|
General | |
Author | Jonko |
Type | Hack Utilities |
Version | 1.2.1 |
License | Mixed |
Last Updated | 2022/07/07 |
Links | |
Download | |
Website | |
Source | |
Wiinject is a tool that allows you to inject ASM hacks into Wii games using Riivolution memory patches. It takes a folder containing PowerPC assembly files (.s) and C files (.c) as input, along with injection sites, and outputs a memory file and a series of Riivolution XML memory patches. It is cross-platform.
Wiinject relies on the Keystone Engine to assemble code and devkitPro to compile C code.
User guide
Prerequisites
- The .NET 6.0 runtime.
- devkitPro if compiling C code.
CLI Options
-f|--folder
– The folder where your source files live-m|--dolphin-map|--map|--symbols
– A Dolphin symbols map for any built-in functions you want to reference by name-i|--injection-addresses
– The addresses to inject function code at, comma delimited. The code at these addresses should be safe to overwrite.-e|--injection-ends
– The addresses at which the above injection sites end (are no longer safe to overwrite), comma delimited. If the code is unable to fit in any of these injection sites, an error will be thrown.-o|--output-folder
– The folder to output the Riivolution patch.xml & assembled ASM bin file to.-n|--patch-name
– The name of the patch to output. The patch will be output to{output_folder}/Riivolution/{patch_name}.xml
and the ASM bin will be output to{output_folder}/{patch_name}/patch.bin
.-p|--input-patch
– The base Riivolution patch that will be modified by Wiinject to contain the memory patches. A blank base template will be created if this is not provided.d|devkitpro-path=
‐ The path to a devkitPro installation containing devkitPPC (e.g.C:\devkitPro
or/opt/devkitpro
)--console-output
– Rather than producing an ASM patch, simply output the XML to the console. This will still save the ASM bin, however.--emit-c
– Emits assembled C functions to the console so you can modify your assembly calls to those functions to work with the registries used by the compiler.
Structuring Your Source Code and Preparing Your Initial Patch
Wiinject expects the folder
where your source lives to have one subdirectory for each patch element you wish to generate. For example, if you'd like your final Riivolution patch to contain one optional patch for translating the game and another for reducing monster spawns, name one subdirectory something like Translation
and the other ReduceMonsterSpawns
. Then, place your source files relevant to those patches in those directories.
When preparing your input patch, make sure you set up the options yourself and ensure that the patch names in the options match the names of the subdirectories in your source folder
. Finally, add any patch elements that have non-memory patches. Wiinject will automatically create patch elements that don't exist and append to ones that do.
If you've followed along, your input patch should look something like this:
<wiidisc version="1"> <id game="R42069" /> <options> <section name="Translation"> <option name="Translation"> <choice name="Enabled"> <patch id="Translation" /> </choice> </option> </section> <section name="Quality of Life"> <option name="Reduce Monster Spawns"> <choice name="Enabled"> <patch id="ReduceMonsterSpawns" /> </choice> </option> </section> </options> <patch id="Translation"> <folder external="/Game/files" recursive="true" disc="/" /> <folder external="/Game/files" /> </patch> </wiidisc>
Writing ASM
Wiinject uses the Keystone Engine to assemble standard PowerPC assembly. To write an assembly file that Wiinject can parse, however, you need to use special function names.
Here is a sample Wiinject-compatible assembly file:
hook_80017250: start: add 5,5,0 mr 26,3 cmpwi 5,3 beq end li 5,2 end: blr hook_80017254: mr 3,26 blr repl_80017260: mr 5,25 li 6,7 ref_801BBB38: li 6,7 blr
The hook
s indicate which instructions to replace with a branch instruction to the function provided. The repl
indicates a location to start overwriting instructions directly with the instructions provided. The ref
indicates a location to write a reference to the function provided (useful for hooking into functions that use bctrl
, etc.).
Using Variables in ASM
Wiinject allows for a special notation to store and interact with variables. Variables can be defined anywhere in an ASM file with $variable:
, e.g. $array: .skip 16
. You can then load that variable into memory with the lv
command. lv
is resolved to an lis
followed by an addi
in the assembling process.
Example:
$variable: .int 0 $array: .skip 16 hook_80017250: lv 2,$variable lv 3,$array blr
Writing C
For each ASM file, you may also provide a companion C file to compile and inject methods which may then be called from the ASM. In order to use this functionality, you must install devkitPro and provide Wiinject with the path to the devkitPro installation (e.g. C:\devkitPro
or /opt/devkitpro
).
Injected C methods are called from the ASM via bl =method_name
. The assembly function caller will need to handle stack manipulation and inputs itself. You can expect a compiled C method to accept inputs sequentially starting with r3
and to place its return value in r3
. However, in order to verify this, you can use the --emit-c
flag while calling Wiinject to view the compiled C code's assembly so you can adjust your assembly caller appropriately.
Here is a sample Wiinject-compatible C file named font_hack.c:
int font_offset(char character) { switch (character) { case 'A': return 0x180; case 'I': case 'i': case 'l': case '!': return 0x48; default: return 0x90; } }
And here is its companion ASM file named font_hack.s:
hook_8001726C: stwu 1,-24(1) mflr 0 stw 0,20(1) stw 31,16(1) mr 31,1 stw 9,12(1) stw 3,8(1) mr 3,26 cmpwi 3,75 bl =font_offset lwz 0,20(1) mtlr 0 mr 0,3 lwz 9,12(1) lwz 3,8(1) addi 11,31,24 lwz 31,-4(11) mr 1,11 blr
Using Symbols
You can provide Wiinject with a Dolphin symbols map and use that to reference functions existing in the ASM in the same way you would reference C functions.
Map file:
.text section layout 80004000 00000050 80004000 0 memcpy
Assembly:
hook_8001726C: stwu 1,-24(1) mflr 0 stw 0,20(1) bl =memcpy lwz 0,20(1) mtlr 0 addi 1,24 blr
Limitations
- Wiinject only supports the
bl
command for C functions or functions defined in your Dolphin symbols map; functions defined in assembly cannot currently be branched to. - The paired single operators are not available.
Changelog
v1.2.1
Fixes:
- Complex switch statements written in C compile to jump tables which were not supported by Wiinject. This adds support for jump tables and thus makes these switch statements work as intended.
- Wiinject errors were sometimes hard to debug due to vague error messages. This adds more detailed error messages for certain cases that should make things easier to debug.
v1.2.0
- Upgraded to .NET 6.0.
- Variables in assembly implemented.
- Added support for referencing built-in functions using a Dolphin symbols map.
- Added ref and hex assembly tags which allow for specifying that the address should be written rather than a bl instruction and specifying raw hex to be written, respectively.
- Refactored the CLI so Wiinject can be packaged as a NuGet package.
- Add support for multiple patch elements.
Not in this release but just noticed:
- We actually do support multi-file C lmao.
v1.1.0
- Wiinject is now self-documenting (not providing arguments will print the options list).
- Added repl assembly tag in addition to hook. repl allows for directly replacing assembly at the target address rather than inserting a branch instruction.
- Added support for multiple injection sites so you can split your code among multiple safe-to-overwrite locations on the ROM.
- Added support for compiling and injecting C code. Compilation is done using devkitPro and functions can be referenced from ASM via bl=c_function_here
Fixes:
- Customized Keystone's C# binding so it actually works cross-plat.
- Keystone now throws errors when encountering assembly it can't assemble.
v1.0.0
- Initial release of Wiinject.
External links
- GitHub - https://github.com/jonko0493/Wiinject
- Romhacking.net - https://www.romhacking.net/utilities/1660/