Mod Loaders
The Mod Loaders are Dynamic Link Libraries (DLL) files that inject themselves into the Sonic Adventure titles by replacing existing DLL files from the game. This is what allows mods to exist external to editing the existing files within the game as well as inject their own code into the game.
Below you'll find links to documentation pertaining to each mod loader respectively. These include in-depth looks at each one's specific APIs.
Mod Loader Export References
Below is an overview of the general exports that are available in the mod loaders. An example of a complete export can be seen below.
Variable Exports
ModInfo
ModInfo is a required export from any code based mod. Without it, the mod loader will not load the mod and will throw an error alerting the user that a mod is not a valid mod.
// Example of exporting the ModInfo for SADX.
__declspec(dllexport) ModInfo SADXModInfo = { ModLoaderVer };
// Example of exporting the ModInfo for SA2B.
__declspec(dllexport) ModInfo SA2ModInfo = { ModLoaderVer };
Function Exports
The mod loaders support loading various function exports from a mod. While these are all optional, many mods make use of at least one of these exports as their entry point. This is most commonly the Init export.
Inside these exported functions you can put your own C++ code to manipulate data, replace assets or interface with the game.
Init
The Init (short for Initialize) export is used for initializing information within your mod and passing some variables from the mod loader into the mod itself. The include the mod's path (local path to the mod folder from the game's root), HelperFunctions (see each respective page for each mod loader for more information on this struct), and the mod's index in the list.
This is fired for all enabled mods first. It's fired in the order in which mods are loaded (top to bottom).
__declspec(dllexport) void Init(const char* path, HelperFunctions& helperFunctions, const unsigned int index)
{
}
OnInitEnd
This is run after all mods have finished running their Init, but before the game has completely loaded. This is useful for any mods making APIs for other mods to utilize.
OnFrame
This export will run every frame. This can be useful if you need to check something within the game constantly. Users who utilize this should consider cautionary measures to prevent running large amounts of logic every frame if it can be avoided for performance reasons.
OnInput
This export will run when the game processes input. More specifically, it will run just prior to the game actually processing that input.
OnControl
This export will run while the game is actively processing input.
OnRenderSceneStart
This export will run just before a game scene is rendered.
OnRenderSceneEnd
This export will run just after a game scene has finished rendering.
OnRenderDeviceReset
This export will run whenever the render device for the game is reset. For example, resizing the game window will cause the render device to be reset.
OnRenderDeviceLost
This export will run whenever a game scene fails to render.
OnExit
This export runs just prior to the game completely closing.
Useful Functions and Macros
Inside any of the exported functions above, apart from your own custom C or C++ code, you can use the following functions and macros to replace game code and data in memory.
WriteData
WriteData writes the specified data at the specified memory address. You can use it to make small code patches, overwrite values, replace pointers etc.
Useful hacks:
- Write
0xC3to kill a function by writing aretinstruction. - Write
0x90five times to kill a function call by writing fivenopinstructions.
WriteData<1>((Uint8*)0x005ADC40, 0xC3); // Writes 0xC3 (one byte) to the address 0x005ADC40, effectively disabling the function located at that address.
WriteData<5>((Uint8*)0x0062EC3C, 0x90); // Writes 0x90 (five bytes) to the address 0x0062EC3C, effectively disabling the function call located at that address.
WriteData((Float*)0x00652F74, 800.0f); // Writes the floating point value of 800.0 to the address 0x00652F74.
WriteData((Float**)0x004D73FB, &MyFloat); // Replaces the pointer located at 0x004D73FB with the pointer to MyFloat.
WriteJump
WriteJump writes a jmp instruction at the specified memory address. You can use this to replace entire functions in the game with your own code.
WriteJump((void*)0x005BBFE0, MyFunction); // Replaces the function at 0x005BBFE0 with a jump to MyFunction.
WriteJump(Function1, MyFunction); // Replaces the function Function1 (if defined with a FunctionPointer) with a jump to MyFunction.
WriteCall
WriteCall writes a call instruction at the specified memory address. You can use this to call your own code from the game's functions without replacing them entirely.
WriteCall((void*)0x005AEF29, MyFunction); // Replaces the function call at 0x005AEF29 with a call to MyFunction.
Exportable Lists
The mod loaders also have support for exporting different types of lists. These are all optional exports.
Patch List
A PatchList is comprised of an array of PatchInfo entries. The mod loader will generate these using WriteData.
Jumps, Calls, and Pointer Lists
These three exportable lists are all comprised of an array of PointerInfo entries. On the mod loader's side, these arrays are handled like so:
- Jumps are generated using WriteJump.
- Calls are generated using WriteCall.
- Pointers are generated using WriteData.
Example
// Inside of your mod's "main" cpp file (or whichever one houses your exports).
#include "pch.h"
HelperFunctions globalHelperFunctions;
const char* globalPath;
extern "C"
{
__declspec(dllexport) SADXModInfo = { ModLoaderVer };
__declspec(dllexport) Init(const char* path, HelperFunctions &helperFunctions, const unsigned int index)
{
// This passes the Mod Loader's HelperFunctions struct to your mod's.
// This is necessary so the API related functions can work properly.
globalHelperFunctions = helperFunctions;
// This passes the mod's local folder path to the mod itself.
// This is helpful for loading custom files.
globalPath = path;
}
}