Skip to content

Spice SDK

bicarus edited this page Sep 7, 2026 · 41 revisions

Spice SDK is a flat C, header-only library that can be included in your hook DLL to write spice plugins.

Once initialized, the plugin call directly into helper routines provided by Spicetools executable, making low-latency and high-throughput interactions possible. There are no network packets or JSON marshalling; it's just function pointers.

Plugins are meant to be an alternative to writing applications over Spice API; as the API uses JSON over the network (or serial), there are issues with performance, latency, and reliability.

For example, you could write a plugin that communicates to your custom controller over COM, knowing that if you call set_button from your plugin, it will be consumed by Spice the next time the game polls for I/O.

It could also be used by DLL hooks that act as game mods. While you might not need most of the functionality that the SDK provides, you could still make use of the log functions and skip the part where you previously had to attach AVS hooks. You could also call get_avs_info and easily figure out the datecode.

The goal of writing this in C was so that it could be used in C++ (which is what most DLL hooks are written in) but also you could write wrappers in other languages such as Rust and C#, though we don't plan on providing that within this project.

Currently, the SDK does not implement everything the API has. They are not meant to be in sync; two surfaces will diverge.

Instructions

Include header files

Grab the latest SDK header files from the -full release package. Look inside spice2x\extras\sdk\include.

There are currently two files - sdkspice.h and spicesdk_io.h. First one is mandatory and must be included in your project; second one is only needed if you need named enum types for buttons/analogs/lights.

Add spice_sdk_entry_point dllexport

See one of the .def files in the sample section for an example.

Implement spice_sdk_entry_point in your project

Here is a minimal C++ example snippet:

#include "sdk/include/spicesdk.h"
#include "sdk/include/spicesdk_io.h"

SPICE_SDK_V0 spice_sdk = {};
spice_sdk_destroy_callback_func destroy_callback;

SPICE_SDK_ENTRY_POINT
spice_sdk_entry_point(
    spice_sdk_init_func *init
)
{
    SPICE_SDK_STATUS_CODE status;

    spice.size = sizeof(spice);

    // ask spice to fill out the function table (spice_sdk v0)
    status = init(0, destroy_callback, &spice_sdk);

    // always check the return status since the underlying executable might not implement the SDK version
    if (status != SPICE_SDK_STATUS_SUCCESS) {
        return 0;
    }

    // after this point, you can call into the functions in "spice_sdk" function table
    spice.log(SPICE_SDK_LOG_LEVEL_INFO, "my_hook", "plugin loaded successfully!");

    // TODO: spin up a worker thread here and do work there (see C++ sample below on how to do that)

    // you must return quickly from this routine; this is a blocking call so the game won't boot if you stay here
    // return value is not checked, but it is logged
    return 1;
}

void
__cdecl
destroy_callback(
    void
)
{
    // signal the worker thread to stop, unload
}

Compile & run

Compile the DLL, and add to spice as a DLL hook (using -k parameter).

It currently doesn't work as an early hook.

SDK API reference

See comments in the header file.

Sample code

C++ sample code

C Sample code

Limitations

Currently, DLL hooks are not loaded in spicecfg.exe. This means we can't do things like DLL adding a new device that can be bound in the UI. This is by design (for stability reasons) but perhaps we can think of a better way to solve this.

Forwards / Backwards compatibility

You can assume that all future versions of spice2x will implement previously published SDK functions. There may be cases where an implementation is turned into a stub (i.e., does nothing) though, so check for status codes.

Do watch out for situations where a hook DLL compiled against a future version is running on a past version of spice2x:

SDK not available

Your spice_sdk_entry_point DLL export may never be called because the underlying spice2x does not implement the SDK.

Major version not implemented

When you call init(), it may fail because the underlying spice2x doesn't implement the version you are requesting.

Minor version mismatch (struct size mismatch)

The function table you receive from init() may not be fully implemented; i.e., some of the function pointers may be nullptr, if the underlying spice2x does not support the minor version you were expecting.

How to address compatibility problems

Build against SDK published in the latest stable version of spice2x. Indicate to users which version of spice2x you tested with.

Encourage users to update to the latest stable version of spice2x.

If init() fails, fallback gracefully to older SDK version, or refuse to load the DLL. If init() succeeds but the function you need is nullptr, fail to load the DLL.

Contributing, suggestions

Please file an issue or a pull request.

Some ideas for the future:

  • scanning and modifying memory (Spice API already has this)
  • installing DLL hooks (expose detour library?)
  • having direct hooks into I/O emulation modules (not sure if needed - feels like existing I/O routines should be sufficient)
  • getting full tape LEDs (Spice API has this for some games)
  • having a callback into DX9 Present call??
  • drawing an ImGui window???

Clone this wiki locally