Skip to content

Examples

abduznik edited this page Jul 17, 2026 · 1 revision

Examples

Quick-start code showing how to use libsavesync from C and from Python via the IPC binary.

All examples use fictional identifiers only — no real game titles or IDs.

C ABI — Quick Start

#include "savesync.h"

int main(void) {
    // Initialize with a directory for versioned storage
    sv_init("/tmp/savesync-data");

    // Load an emulator manifest (auto-detects game identity)
    sv_manifest_t *manifest = sv_manifest_create();
    sv_manifest_load("manifests/dolphin_gc.cfg", manifest);

    // Register a save — game ID is extracted from the ROM header
    sv_register_opts_t opts = {0};
    opts.live_path = "/path/to/save/TEST01";
    sv_status_t st;
    sv_registration_t *reg = sv_register_with_manifest(&opts, manifest, &st);

    // Snapshot the current save into the versioned magazine
    sv_save_result_t save_result;
    sv_save(reg, NULL, &save_result);
    // save_result.entry_id now holds the new entry's ID
    // save_result.entry_created is true on first save

    // Pull (restore) the latest saved version to the live path
    sv_pull_result_t pull_result;
    sv_pull(reg, NULL, &pull_result);
    // pull_result.did_pull is true if a restore happened

    // List all saved versions
    sv_id_t ids[64];
    size_t count;
    sv_list_entries(save_result.entry_id, ids, 64, &count);

    sv_shutdown();
    return 0;
}

Build: make static && cc -o myapp main.c -Lbuild -lsavesync -Iinclude

Python via IPC Binary

import json, subprocess, tempfile, os

proc = subprocess.Popen(["build/savesync-ipc"],
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)

def send(method, params=None):
    msg = {"id": method, "method": method}
    if params: msg["params"] = params
    proc.stdin.write(json.dumps(msg) + "\n")
    proc.stdin.flush()
    return json.loads(proc.stdout.readline())

with tempfile.TemporaryDirectory() as tmpdir:
    os.makedirs(os.path.join(tmpdir, "live_save"))
    with open(os.path.join(tmpdir, "live_save", "data.bin"), "wb") as f:
        f.write(b"SYNTHETIC_SAVE_V1")

    send("init", {"base_path": os.path.join(tmpdir, "data")})

    resp = send("manifest_load", {"path": "manifests/dolphin_gc.cfg"})
    mid = resp["result"]["manifest_id"]

    resp = send("register_with_manifest", {
        "manifest_id": mid,
        "live_path": os.path.join(tmpdir, "live_save", "data.bin"),
        "shape": "file",
    })
    rid = resp["result"]["registration_id"]

    resp = send("save", {"registration_id": rid})
    print(f"Saved: {resp['result']['entry_id']}")

    resp = send("pull", {"registration_id": rid})
    print(f"Pull: did_pull={resp['result']['did_pull']}")

    send("shutdown")

See examples/python_client.py for the full, runnable example.

Walkthrough: Registering a GameCube Save

This walkthrough uses the Dolphin GC manifest to register a GameCube save automatically:

  1. Load the manifestmanifests/dolphin_gc.cfg tells the library to use SV_IDENTITY_ROM_HEADER, reading 4 bytes from the ROM file at a fixed offset to extract the game ID.

  2. Register with live_path — point live_path at a GameCube save file (e.g. a .gci file). The library reads the ROM header and extracts the game ID automatically — no manual game_id configuration needed.

  3. Save — the library copies the save into a versioned magazine entry with a unique ID, computing a content hash for deduplication.

  4. Pull — restores the latest (or a specific) magazine entry back to the live path via atomic copy (temp file + rename), never touching the live file in-place.

The same pattern works for any of the 8 supported manifests. For emulators where identity is caller-supplied (DuckStation, RetroArch SNES, PCSX2 file mode), simply set game_id in the register options instead of relying on manifest-based detection.


See also: API-Reference | Testing-Guide | Layer-1-Interface

Clone this wiki locally