Something went wrong. Try again.
Monorepo for PackTools - tooling for Minecraft Modpacks!
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136/* packutils' C ABI: the only boundary between Packwand (Zig) and packutils (C++23), the pack * utilities that work on a game instance beside a pack: linking a pack's folders into it, and * (later) harvesting changes back. MIT licensed, like the rest of packutils. SPEC.md describes * the design. Every function is noexcept on the C++ side: no exception and no C++ type crosses * it, and nothing is linked from the host by symbol. * * Each operation takes one JSON request and answers one JSON document; SPEC.md section 3 gives * their members. A later operation is a new function, so a document can grow without a new * struct. */#ifndef PACKUTILS_H#define PACKUTILS_H
#include <stdbool.h>#include <stddef.h>#include <stdint.h>
#ifdef __cplusplus#define PU_NOEXCEPT noexceptextern "C" {#else#define PU_NOEXCEPT#endif
/* Bumped on any change to these declarations or to what the documents mean. Changing it is a * change to Packwand too (root AGENTS.md): ask first. */#define PACKUTILS_ABI 1
/* UTF-8 bytes, not NUL-terminated. */typedef struct pu_str { const uint8_t *ptr; size_t len;} pu_str;
typedef enum pu_status { PU_OK = 0, PU_FAILED = 1, /* the operation refused or failed; pu_error says how */ PU_INVALID = 2, /* a malformed request or host: a bug on the host's side */ PU_CANCELED = 3, /* the host's cancel byte was set */ PU_INTERNAL = 4, /* a bug: an escaped exception or a broken invariant */} pu_status;
/* How the host's commands classify a PU_FAILED failure. */typedef enum pu_kind { PU_KIND_NONE = 0, /* any status but PU_FAILED */ PU_KIND_INVALID_REQUEST = 1, PU_KIND_NOT_FOUND = 2, PU_KIND_CONFLICT = 3, PU_KIND_OPERATION_FAILED = 4,} pu_kind;
/* Filled on any status but PU_OK. The strings stay valid until the next call on the same runtime * or packutils_free_error; a failed packutils_create points at static text. */typedef struct pu_error { pu_status status; pu_kind kind; /* With PU_FAILED, a stable name for the failure (SPEC.md section 4), such as "LinkConflict"; * "FileSystem" when the file system refused. */ pu_str code; /* What went wrong, for every code but "FileSystem". */ pu_str message; /* "FileSystem" only: the path, the cause (FileNotFound, NotDir, AccessDenied, IsDir, * NoSpaceLeft, PathAlreadyExists, DirNotEmpty, or another error's name), and whether the * operation was on a directory. The host words the message, as it words its own. */ pu_str path; pu_str cause; bool directory; /* The failure goes away with a newer build (a file a newer one wrote). */ bool upgrade;} pu_error;
/* The host's side of the boundary, passed by pointer to packutils_create and borrowed for the * runtime's life. `size` is sizeof(pu_host): later ABIs only append members. Hashing comes from * the host, so packutils carries no cryptography of its own. */typedef struct pu_host { size_t size; void *ctx; /* Nonzero stops the running operation with PU_CANCELED at its next check. */ const volatile uint8_t *cancel; /* SHA-256 of `bytes` into `out`. */ void (*sha256)(void *ctx, pu_str bytes, uint8_t out[32]); /* SHA-512 of the file at the full path `path` into `out`, read through; false when it cannot * be read. */ bool (*sha512_file)(void *ctx, pu_str path, uint8_t out[64]);} pu_host;
typedef struct pu_runtime pu_runtime;
/* A result document: valid until the next call on the same runtime or packutils_free_result. */typedef struct pu_result { pu_str json;} pu_result;
/* PACKUTILS_ABI as this library was built. */uint32_t packutils_abi(void) PU_NOEXCEPT;
/* Checks `host_abi` against PACKUTILS_ABI, `host->size` against sizeof(pu_host) and that every * host function is set, then makes a runtime. */pu_status packutils_create(const pu_host *host, uint32_t host_abi, pu_runtime **out, pu_error *err) PU_NOEXCEPT;
/* The links a pack recorded (`links.json`). */pu_status packutils_links_load(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* A game directory's real path, refused unless it is a directory apart from the pack. */pu_status packutils_resolve_instance(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* What each named folder's slot in the game directory holds. Nothing is changed. Which of the * pack's folders are linked is the host's rule, so it names them. */pu_status packutils_link_check(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* Links every checked folder. */pu_status packutils_link(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* Removes recorded links and restores what a forced link set aside. */pu_status packutils_unlink(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* One pass of a copy link. */pu_status packutils_sync(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* What the instance changed that the pack does not have; reads only, under the links lock. */pu_status packutils_harvest_plan(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;/* Takes the pack's links lock and keeps it in the runtime until packutils_unlock, so the host can * read an instance and commit to the pack with no sync between; operations on that pack meanwhile * do not take it again. A runtime holds one lock at a time. */pu_status packutils_lock(pu_runtime *rt, pu_str request, pu_result *out, pu_error *err) PU_NOEXCEPT;void packutils_unlock(pu_runtime *rt) PU_NOEXCEPT;
void packutils_free_result(pu_runtime *rt, pu_result *result) PU_NOEXCEPT;void packutils_free_error(pu_runtime *rt, pu_error *err) PU_NOEXCEPT;void packutils_destroy(pu_runtime *rt) PU_NOEXCEPT;
#ifdef __cplusplus}#endif
#endif /* PACKUTILS_H */