Docs menu · C interface+

    You are reading the docs of SDK 1.1.0, an older release. See the latest (1.11.0).

    Reference · SDK 1.1.0

    The C interface

    Everything the port hands a native mod: openrac_mod.h. A mod is built against this header alone. Each item says which interface version added it; check api->api_minor before you use something newer than your manifest's api

    The interface

    NameValueWhat it is
    OPENRAC_MOD_API_MAJOR1
    OPENRAC_MOD_API_MINOR1 1.1: script mods (docs/port/MODS.md, section 11); table unchanged
    OPENRAC_MOD_EXPORT__declspec(dllexport) or __attribute__((visibility("default")))

    openrac_gaddr

    Type
    Declaration
    typedef uint32_t openrac_gaddr;
    A game address: an offset into game memory (the console's address).
    +Example (1)
    From sdk/examples/frame-counter/src/frame_counter.c
    static void pad_process_report(openrac_gaddr pad, openrac_gaddr report, int32_t size) {
        ++s_reports;
        s_pad_original(pad, report, size);
    }

    openrac_mod

    Type
    Declaration
    typedef struct openrac_mod openrac_mod;
    The port's handle for one loaded mod, passed back to the functions that are the mod's own (its log lines, its settings, its hooks).
    +Examples (2)
    From the guide “Native mods in C”
    #include "openrac/openrac_mod.h"
    #include "openrac/games/rac1.h"
    
    OPENRAC_MOD_DEFINE("my-mod", "0.1.0")   /* openrac_mod_query: the interface version, the id */
    
    OPENRAC_MOD_EXPORT int openrac_mod_load(const openrac_api* api, openrac_mod* mod) {
        if (strcmp(api->game, "rac1") != 0) {
            api->fail(mod, "this mod is for Ratchet & Clank");
            return 1;                          /* not loaded; the reason is shown */
        }
        api->log(mod, OPENRAC_LOG_INFO, "hello");
        return 0;                              /* loaded */
    }
    From sdk/examples/frame-counter/src/frame_counter.c
    #include "openrac/openrac_mod.h"
    #include "openrac/games/rac1.h"
    NameValueWhat it is
    OPENRAC_LOG_DEBUG0 Log levels for openrac_api.log.
    OPENRAC_LOG_INFO1
    OPENRAC_LOG_WARN2
    OPENRAC_LOG_ERROR3
    OPENRAC_HOOK_OK0 openrac_api.hook's results.
    OPENRAC_HOOK_UNKNOWN(-1) this port has no hook point of that name (on this disc)
    OPENRAC_HOOK_SIGNATURE(-2) the hook point's signature is another
    OPENRAC_HOOK_TOO_LATE(-3) hooks are taken in openrac_mod_load only

    struct openrac_mod_info

    What openrac_mod_query tells the port before it loads the mod.
    MemberWhat it is
    uint32_t sizesizeof(openrac_mod_info), set by the port
    uint32_t api_majorOPENRAC_MOD_API_MAJOR as the mod was built
    uint32_t api_minorOPENRAC_MOD_API_MINOR as the mod was built
    const char* idthe manifest's id: the port checks they agree
    const char* versionthe mod's version, as its manifest's
    +Example (1)
    From sdk/examples/frame-counter/src/frame_counter.c
        if (OPENRAC_RAC1_HOOK(api, mod, Pad_ProcessReport, pad_process_report, &s_pad_original) != OPENRAC_HOOK_OK) {
            api->fail(mod, "this OpenRAC has no Pad_ProcessReport hook point");
            return 1;
        }
        api->on_frame(mod, on_frame, NULL);
        char line[96];
        snprintf(line, sizeof line, "counting on %s/%s, a line every %lld ticks", api->game, api->version,
                 (long long)s_every);
        api->log(mod, OPENRAC_LOG_INFO, line);
        return 0;
    }

    openrac_frame_fn

    Callback type
    Declaration
    typedef void (*openrac_frame_fn)(void* user);

    openrac_pad_fn

    Callback type
    Declaration
    typedef void (*openrac_pad_fn)(void* user, int port, uint8_t* report, int size);
    A pad report as the console's pad library hands it to the game (its bytes, size of them), which the callback may change before the game reads it. port is the controller's port, 0 or 1.
    What the port offers a mod. Filled by the port; valid for the life of the program.

    game

    Value
    Field
    const char* game;
    "rac1": the game's id (games/<game>/game.json)
    +Examples (2)
    From the guide “Native mods in C”
    #include "openrac/openrac_mod.h"
    #include "openrac/games/rac1.h"
    
    OPENRAC_MOD_DEFINE("my-mod", "0.1.0")   /* openrac_mod_query: the interface version, the id */
    
    OPENRAC_MOD_EXPORT int openrac_mod_load(const openrac_api* api, openrac_mod* mod) {
        if (strcmp(api->game, "rac1") != 0) {
            api->fail(mod, "this mod is for Ratchet & Clank");
            return 1;                          /* not loaded; the reason is shown */
        }
        api->log(mod, OPENRAC_LOG_INFO, "hello");
        return 0;                              /* loaded */
    }
    From sdk/examples/frame-counter/src/frame_counter.c
    OPENRAC_MOD_EXPORT int openrac_mod_load(const openrac_api* api, openrac_mod* mod) {
        s_api = api;
        s_mod = mod;
        if (strcmp(api->game, "rac1") != 0) {
            api->fail(mod, "frame-counter is for Ratchet & Clank (rac1)");
            return 1;
        }
        s_every = api->setting_int(mod, "every");
        if (s_every <= 0) {
            s_every = 600;

    version

    Value
    Field
    const char* version;
    "pal", "ntsc": the disc's version
    +Example (1)
    From sdk/examples/frame-counter/src/frame_counter.c
        if (OPENRAC_RAC1_HOOK(api, mod, Pad_ProcessReport, pad_process_report, &s_pad_original) != OPENRAC_HOOK_OK) {
            api->fail(mod, "this OpenRAC has no Pad_ProcessReport hook point");
            return 1;
        }
        api->on_frame(mod, on_frame, NULL);
        char line[96];
        snprintf(line, sizeof line, "counting on %s/%s, a line every %lld ticks", api->game, api->version,
                 (long long)s_every);
        api->log(mod, OPENRAC_LOG_INFO, line);
        return 0;
    }

    openrac_version

    Value
    Field
    const char* openrac_version;
    OpenRAC's version, "0.9.0"

    platform

    Value
    Field
    const char* platform;
    "macos-arm64", "linux-x86_64", "windows-x86_64", ...

    memory

    Value
    Field
    uint8_t* memory;
    The base of game memory: game address a is memory + a. Fixed for the life of the program.
    +Example (1)
    From sdk/template/src/mod.cpp
    // Once a game tick: read or change game memory through mod.memory().
    mod.on_frame([] {});

    log()

    Signature
    void log(openrac_mod* mod, int level, const char* text);
    
    // how a call looks
    api->log(mod, level, text);
    A line in the session's log, after the mod's id.

    Parameters

    • mod (openrac_mod*): your mod's handle, as openrac_mod_load got it.
    • level (int)
    • text (const char*)
    +Examples (2)
    From the guide “Native mods in C”
    #include "openrac/openrac_mod.h"
    #include "openrac/games/rac1.h"
    
    OPENRAC_MOD_DEFINE("my-mod", "0.1.0")   /* openrac_mod_query: the interface version, the id */
    
    OPENRAC_MOD_EXPORT int openrac_mod_load(const openrac_api* api, openrac_mod* mod) {
        if (strcmp(api->game, "rac1") != 0) {
            api->fail(mod, "this mod is for Ratchet & Clank");
            return 1;                          /* not loaded; the reason is shown */
        }
        api->log(mod, OPENRAC_LOG_INFO, "hello");
        return 0;                              /* loaded */
    }
    From sdk/examples/frame-counter/src/frame_counter.c
        if (++s_ticks % (uint64_t)s_every != 0) {
            return;
        }
        char line[128];
        snprintf(line, sizeof line, "%llu ticks, %llu pad reports, level %d", (unsigned long long)s_ticks,
                 (unsigned long long)s_reports, s_api->loaded_level());
        s_api->log(s_mod, s_verbose ? OPENRAC_LOG_INFO : OPENRAC_LOG_DEBUG, line);
    }

    fail()

    Signature
    void fail(openrac_mod* mod, const char* reason);
    
    // how a call looks
    api->fail(mod, reason);
    During openrac_mod_load: why the mod refuses to load (status.json and the launcher show it).

    Parameters

    • mod (openrac_mod*): your mod's handle, as openrac_mod_load got it.
    • reason (const char*)
    +Examples (2)
    From the guide “Native mods in C”
    #include "openrac/openrac_mod.h"
    #include "openrac/games/rac1.h"
    
    OPENRAC_MOD_DEFINE("my-mod", "0.1.0")   /* openrac_mod_query: the interface version, the id */
    
    OPENRAC_MOD_EXPORT int openrac_mod_load(const openrac_api* api, openrac_mod* mod) {
        if (strcmp(api->game, "rac1") != 0) {
            api->fail(mod, "this mod is for Ratchet & Clank");
            return 1;                          /* not loaded; the reason is shown */
        }
        api->log(mod, OPENRAC_LOG_INFO, "hello");
        return 0;                              /* loaded */
    }
    From the guide “Native mods in C”
    static openrac_rac1_Hero_SetPlanarVelocity s_original;
    
    static void my_velocity(float angle) {
        s_original(angle);   /* the game's own, or the mod before yours */
    }
    
    /* in openrac_mod_load: */
    if (OPENRAC_RAC1_HOOK(api, mod, Hero_SetPlanarVelocity, my_velocity, &s_original) != OPENRAC_HOOK_OK) {
        api->fail(mod, "no Hero_SetPlanarVelocity in this OpenRAC");
        return 1;
    }

    setting_bool(), setting_int(), setting_float(), setting_string()

    Signature
    int setting_bool(openrac_mod* mod, const char* key);
    int64_t setting_int(openrac_mod* mod, const char* key);
    double setting_float(openrac_mod* mod, const char* key);
    const char* setting_string(openrac_mod* mod, const char* key);
    The mod's settings (its manifest's "settings"; the values the launcher wrote). A key the manifest does not declare, or of another type, gives 0, 0.0 or "". The string stays valid.
    +Examples (2)
    From sdk/examples/frame-counter/src/frame_counter.c
        return 1;
    }
    s_every = api->setting_int(mod, "every");
    if (s_every <= 0) {
        s_every = 600;
    }
    s_verbose = api->setting_bool(mod, "verbose");
    if (OPENRAC_RAC1_HOOK(api, mod, Pad_ProcessReport, pad_process_report, &s_pad_original) != OPENRAC_HOOK_OK) {
        api->fail(mod, "this OpenRAC has no Pad_ProcessReport hook point");
        return 1;
    }
    api->on_frame(mod, on_frame, NULL);
    char line[96];
    From sdk/examples/frame-counter/src/frame_counter.c
    s_api = api;
    s_mod = mod;
    if (strcmp(api->game, "rac1") != 0) {
        api->fail(mod, "frame-counter is for Ratchet & Clank (rac1)");
        return 1;
    }
    s_every = api->setting_int(mod, "every");
    if (s_every <= 0) {
        s_every = 600;
    }
    s_verbose = api->setting_bool(mod, "verbose");
    if (OPENRAC_RAC1_HOOK(api, mod, Pad_ProcessReport, pad_process_report, &s_pad_original) != OPENRAC_HOOK_OK) {
        api->fail(mod, "this OpenRAC has no Pad_ProcessReport hook point");

    hook()

    Only while loading
    Signature
    int hook(
        openrac_mod* mod,
        const char* name,
        const char* signature,
        void* replacement,
        void** original
    );
    
    // how a call looks
    int result = api->hook(mod, name, signature, replacement, original);
    Takes the hook point name (games/<game>.h: its name and signature) for replacement, a function of the hook point's signature. *original is set to what the replacement goes on to: the game's own function, or the replacement of a mod loaded earlier. So the mod loaded last runs first. Only during openrac_mod_load.

    Parameters

    • mod (openrac_mod*): your mod's handle, as openrac_mod_load got it.
    • name (const char*)
    • signature (const char*)
    • replacement (void*)
    • original (void**)

    Returns

    int
    +Examples (2)
    From the guide “Native mods in C++”
    mod.hook<openrac::rac1::Hero_TurnToHeading>(
        [trace, &mod](auto original, int32_t m, float accel, float damping, float limit) {
            if (trace) mod.info("turning");
            original(m, accel, damping, limit);
        });
    From sdk/template/src/mod.cpp
    // A hook: the lambda gets the way on (the game's function, or a mod's loaded earlier) first,
    // then the hook point's arguments. An exception thrown here is logged, and the original runs.
    mod.hook<openrac::rac1::Hero_SetPlanarVelocity>([](auto original, float angle) {
        // ... before the game's own ...
        original(angle);
        // ... after it ...
    });

    on_frame()

    Only while loading
    Signature
    int on_frame(openrac_mod* mod, openrac_frame_fn fn, void* user);
    
    // how a call looks
    int result = api->on_frame(mod, fn, user);
    fn(user) after each game tick, in load order. Only during openrac_mod_load.

    Parameters

    • mod (openrac_mod*): your mod's handle, as openrac_mod_load got it.
    • fn (openrac_frame_fn)
    • user (void*)

    Returns

    int
    +Examples (2)
    From the guide “Native mods in C”
    static void tick(void* user) { /* after each game tick */ }
    static void pad(void* user, int port, uint8_t* report, int size) { /* change report[] */ }
    
    api->on_frame(mod, tick, NULL);
    api->on_pad(mod, pad, NULL);
    From sdk/examples/frame-counter/src/frame_counter.c
        }
        s_verbose = api->setting_bool(mod, "verbose");
        if (OPENRAC_RAC1_HOOK(api, mod, Pad_ProcessReport, pad_process_report, &s_pad_original) != OPENRAC_HOOK_OK) {
            api->fail(mod, "this OpenRAC has no Pad_ProcessReport hook point");
            return 1;
        }
        api->on_frame(mod, on_frame, NULL);
        char line[96];
        snprintf(line, sizeof line, "counting on %s/%s, a line every %lld ticks", api->game, api->version,
                 (long long)s_every);
        api->log(mod, OPENRAC_LOG_INFO, line);
        return 0;
    }

    on_pad()

    Only while loading
    Signature
    int on_pad(openrac_mod* mod, openrac_pad_fn fn, void* user);
    
    // how a call looks
    int result = api->on_pad(mod, fn, user);
    fn(user, port, report, size) for each pad report, before the game reads it, in load order. Only during openrac_mod_load.

    Parameters

    • mod (openrac_mod*): your mod's handle, as openrac_mod_load got it.
    • fn (openrac_pad_fn)
    • user (void*)

    Returns

    int
    +Example (1)
    From the guide “Native mods in C”
    static void tick(void* user) { /* after each game tick */ }
    static void pad(void* user, int port, uint8_t* report, int size) { /* change report[] */ }
    
    api->on_frame(mod, tick, NULL);
    api->on_pad(mod, pad, NULL);

    function()

    Signature
    void* function(const char* name);
    
    // how a call looks
    void* result = api->function(name);
    A game function by its name in this disc's build ("func_001F9F90" on PAL, "FUN_001f9dc8" on NTSC-U), as a host function to call with its signature; null when there is none.

    Parameters

    • name (const char*)

    Returns

    void*

    function_at()

    Signature
    void* function_at(openrac_gaddr address);
    
    // how a call looks
    void* result = api->function_at(address);
    The function at a code address, in the level loaded now (a function pointer the game keeps); null when there is none.

    Parameters

    • address (openrac_gaddr)

    Returns

    void*

    relocate_data(), relocate_level_data()

    Signature
    openrac_gaddr relocate_data(openrac_gaddr address);
    openrac_gaddr relocate_level_data(int level, openrac_gaddr address);
    A global's address for the code loaded now: one of the executable's (guest.h, OPENRAC_DATA), or of level level's program (OPENRAC_LDATA). Ask each time: a level moves its globals.

    loaded_level()

    Signature
    int loaded_level();
    
    // how a call looks
    int result = api->loaded_level();
    The level whose program is loaded, -1 for none (the executable's).

    Returns

    int
    +Example (1)
    From sdk/examples/frame-counter/src/frame_counter.c
        (void)user;
        if (++s_ticks % (uint64_t)s_every != 0) {
            return;
        }
        char line[128];
        snprintf(line, sizeof line, "%llu ticks, %llu pad reports, level %d", (unsigned long long)s_ticks,
                 (unsigned long long)s_reports, s_api->loaded_level());
        s_api->log(s_mod, s_verbose ? OPENRAC_LOG_INFO : OPENRAC_LOG_DEBUG, line);
    }

    stack_push(), stack_pop()

    Signature
    openrac_gaddr stack_push(uint32_t size, openrac_gaddr* saved);
    void stack_pop(openrac_gaddr saved);
    Room on the game's stack for a call that takes the address of a local: stack_push gives size bytes (aligned to 16) and keeps the stack as it was in *saved; stack_pop(saved) gives them back. Pushes and pops pair, innermost first.

    openrac_mod_query_fn

    Callback type
    Declaration
    typedef void (*openrac_mod_query_fn)(openrac_mod_info* info);
    The entry points a mod exports.

    openrac_mod_load_fn

    Callback type
    Declaration
    typedef int (*openrac_mod_load_fn)(const openrac_api* api, openrac_mod* mod);
    NameValueWhat it is
    OPENRAC_MOD_QUERY"openrac_mod_query"
    OPENRAC_MOD_LOAD"openrac_mod_load"
    OPENRAC_MOD_UNLOAD"openrac_mod_unload"

    OPENRAC_G()

    Macro
    Declaration
    #define OPENRAC_G(api, a) ((void*)((api)->memory + (openrac_gaddr)(a)))
    Helpers for a mod: a game address's bytes, and a typed value there.

    OPENRAC_GREF()

    Macro
    Declaration
    #define OPENRAC_GREF(api, T, a) (*(T*)OPENRAC_G(api, a))

    OPENRAC_MOD_DEFINE()

    Macro
    Declaration
    #define OPENRAC_MOD_DEFINE(mod_id, mod_version) ...
    The usual openrac_mod_query: OPENRAC_MOD_DEFINE("lock-strafe", "1.0.0").
    +Examples (2)
    From the guide “Native mods in C”
    #include "openrac/openrac_mod.h"
    #include "openrac/games/rac1.h"
    
    OPENRAC_MOD_DEFINE("my-mod", "0.1.0")   /* openrac_mod_query: the interface version, the id */
    
    OPENRAC_MOD_EXPORT int openrac_mod_load(const openrac_api* api, openrac_mod* mod) {
        if (strcmp(api->game, "rac1") != 0) {
            api->fail(mod, "this mod is for Ratchet & Clank");
            return 1;                          /* not loaded; the reason is shown */
        }
        api->log(mod, OPENRAC_LOG_INFO, "hello");
        return 0;                              /* loaded */
    }
    From sdk/examples/frame-counter/src/frame_counter.c
    OPENRAC_MOD_DEFINE("frame-counter", "1.0.0")

    Generated from sdk/include/openrac/openrac_mod.h at sdk-v1.1.0. Edit this page or report a problem.