owned this note
owned this note
Published
Linked with GitHub
# N64 Recomp Modding
### Mod file(s)
Mods are distributed as a .nrm (N64 Recomp Mod). The .nrm file is generated by the RecompModTool utility in the N64: Recompiled repo. Because this is generated by the mod tool authors don't need to worry about the internals of the format, but details are listed below:
An .nrm is just a renamed zip archive with the following contents:
* A mod manifest file, which is a json file that describes the mod. It contains the target game id, mod id, mod version, mod author(s), and more.
* This is the only mandatory file in a mod, though a mod with just a manifest wouldn't do anything.
* (Optional) For mods with code: a mod symbol file and a mod binary.
* The mod binary is simply a self-contained MIPS binary. This will be recompiled when the mod is loaded (explained more later). The binary will also be loaded into memory when the mod is active.
* The mod symbol file describes the layout of sections and functions in the mod binary, as well as mod dependencies, exports, and imports (explained more later). It also describes relocations in the mod binary, which are used to shift the mod in RAM so that multiple mods can be loaded at once.
* (Optional) Texture files for a texture pack.
* Recomp will also accept .rtz texture packs. Recomp will recognize a recomp mod manifest in a .rtz texture pack, but will only allow fields that affect how the mod is displayed, meaning .rtz files can't include code. Recomp will populate a default manifest for .rtz texture packs if none is present.
* (Optional) A patch file for the base ROM.
* Two mods containing patches will be marked as incompatible.
* Code that is modified by the patch file is not loaded. Code patches will need to be ported into a mod binary + mod symbol file.
* Loading modified code from a patch file will be supported in the future. When that happens, modified code will not be detected automatically. Mod authors will need to create a list of modified functions in order for the modified versions to replace their original counterparts.
* (Optional) Images to show when the mod is selected in the mod manager.
* TODO Exact details will be added later.
### Code mod types
Mods with code can take two forms:
* Normal mods, which get recompiled into Lua (LuaJIT specifically) when the game is started.
* This is the recommended form of code mod.
* Offline recompiled mods, which generate C ahead of time and compile it into a dynamic library (DLL). This is separate from the ability for a mod to include dynamic libraries alongside the mod's code as mentioned previously.
* This is not recommended, as it means the author will have to distribute platform-specific binaries.
* Mainly intended for validating that LuaJIT recompilation is working correctly for a mod.
### Writing code mods
Mods can provide code in a few ways:
* The primary way is to compile an elf file and run the recomp mod tool to generate a mod.
* The most common approach for this will be to copy functions from the corresponding decompilation for a given recomp and edit them.
* Mod authors can also copy asm functions from the game and edit them, which is useful for games without a complete decompilation.
* Mod authors can also write their own mod binary and mod symbol file.
* This is an incredibly niche option, but can be useful for supporting existing tools or porting existing mods.
* Mods can also include additional dynamic libraries alongside the .nrm file.
* The mod's manifest specifies dynamic libraries and the functions to export from them (more on exports later).
* Mods authors will have to distribute a build of the dynamic library for each platform. The runtime will automatically pick the right one based on the platform's standard dynamic library format (.dll, .so, .dylib).
### Recomp mod tool
The recomp mod tool is used build a mod file (.nrm). It takes a toml input for configuration.
TODO The schema of the toml will be added later, potentially in another document that will get linked here.
### Mod interactions
The modding framework provides the following set of functionality to allow mods to interact with each other:
* Mods can replace code (i.e. individual functions) from the base game
* If two mods replace the same function, they will be marked as incompatible.
* If a mod replaces a function that was patched in the base recomp, the mod will fail to load.
* Mods can use a forced replacement to replace a function that's patched in the base recomp, but this can be dangerous and is not recommended.
* When using the standard recomp mod template, the `RECOMP_PATCH` macro can be added to a function with the same name as an original ROM function to make a patch. The names come from the symbol files used to run the recomp mod tool. For example, if a mod were replacing a function named "func_808333CC" it would look like this:
```c
RECOMP_PATCH s32 func_808333CC(Player* this) { /* function body here */ }
```
* Mods can **export** functions for other mods to **import**.
* For example, mod A can create a function called "foo" and export it, which would allow mod B to call it.
* The `RECOMP_EXPORT` macro can be added to a function to export it. For example, if a mod named "testmod" was exporting a function called "testmod_do_stuff" it would look like this:
```c
RECOMP_EXPORT u8 testmod_do_stuff(s32 param) { /* function body here */ }
```
* The `RECOMP_IMPORT` macro can be used to import a function from another mod. The first parameter is the name of the mod to import from, the second is the return type of the import, the third is the imported function's name, and the remaining ones are the imported function's arguments. For example, if another mod was importing the previous "testmod_do_stuff" function, it would look like this:
```c
RECOMP_IMPORT("testmod", u8 testmod_do_stuff(s32 param));
```
* The base recomp can also create exported functions that mods can import. To import those, use `*` as the mod name in the `RECOMP_IMPORT` macro.
* Mods can import a symbol from themselves. This is useful when importing a native function from a dynamic library provided with the mod. To do so, use `.` as the mod name in the `RECOMP_IMPORT` macro.
* Mods can create **events** that other mods can register **callbacks** for.
* Events are called by the mod that creates them as if they were a function.
* When an event is called, the runtime will run any callbacks registered to that event.
* For example, mod A can create an event called "lorem" and then mod B can register a callback called "ipsum".
* Mod A can then call the "lorem" event at any point in its code, which will in turn run mod B's "ipsum" callback.
* The `RECOMP_DECLARE_EVENT` macro can be used to declare an event. The macro takes the event's function arguments as extra parameters. For example, if a mod named "testmod" declared an event named "testmod_event" that takes a "Player*" as an argument, it would look like this:
```c
RECOMP_DECLARE_EVENT(testmod_event(Player* player));
```
The mod can then call the event as if it were a function with the specified arguments. In this case, that would look like this:
```c
testmod_event(player);
```
* The `RECOMP_CALLBACK` macro can be added to a function to register it as a callback for an event. The macro takes one argument, which is the name of the event to register the callback for. For example, if a mod was registering a function called "my_callback" to the previous "testmod_event" event, it would look like this:
```c
RECOMP_CALLBACK("testmod", testmod_event) void my_callback(int val) { /* ... */ }
```
* Callbacks must have the same signature as the event. If they differ, undefined behavior will occur.
* The base recomp can also create events that mods can register callbacks to. To register a callback for those, use `*` as the mod name in the `RECOMP_CALLBACK` macro.
* This is useful in cases where mods want to add some code to a vanilla function but can't replace the function to avoid conflicts.
* Mods can attach callbacks to their own events. To do so, use `.` as the mod name in the `RECOMP_CALLBACK` macro.
### Modding API
The standard recomp runtime will provide a common set of functionality to mods in the form of a modding API, which will be present in all recomps. This includes memory allocation functions. TODO The exact set of functionality provided will be added later.
Individual recomps will also be able to add their own functionality to the modding API. This will come in the form of **exports** and **events** provided by the base recomp. These can come from the base recomp's patches or from the recomp's native code.