-
Notifications
You must be signed in to change notification settings - Fork 0
Philosophy
Core design principles that apply to every layer of libsavesync.
The library never inspects processes, never decides when to act on its own. Every operation is explicitly triggered by the caller. Process-lifecycle detection is the caller's problem, not this library's — cross-platform process introspection is a different, riskier class of problem (permissions, PID races, security software false-positives) that doesn't belong inside a save-integrity library.
All reads of the live save happen via atomic copy-out into a versioned "magazine" entry. All writes to the live save happen via atomic replace (temp file + rename), never in-place overwrite. The live path is only ever touched at two chokepoints: copy-out (save) and copy-in (pull/pull_select).
Conflicts are always reported by default, never silently resolved. Every function that could clobber existing data takes an explicit on_conflict policy; the default is always the safe, non-destructive option.
No field is ever required unless it's structurally necessary (e.g. live_path). Unset fields are zeroed/NULL and treated as "unknown," never as an error. This is both a usability rule (users can do a bare 1:1 sync with zero metadata) and an ABI-stability rule (new fields can be added later without breaking old callers, since old callers simply leave them zeroed).
No struct is ever handed across the API boundary by value for callers to allocate — only opaque handles + accessor functions, or read-only output snapshots. This is standard practice for ABI-stable C libraries: C's minimal binary surface is why it's used as the stable boundary even for C++/Rust projects. Never reorder or remove struct fields once shipped.
A magazine entry ("child") belongs to a registration ("parent") by reference, not by hard binding. Parents can be deleted, leaving orphaned entries; entries can be explicitly re-parented. Nothing is silently deleted as a side effect of an unrelated operation.
The core never contains socket/HTTP code. Cross-device sync is delegated entirely to caller-supplied function pointers (a push/pull contract), so USB, HTTP, LAN, or terminal-based transports can all be implemented externally without touching library source — same pattern as libcurl/libgit2/rclone.
Game identity resolution is tiered: embedded serial extraction first (PSP/PS1/PS2 SYSTEM.CNF/PARAM.SFO, Saturn IP.BIN, GameCube/Wii boot headers — zero external data needed), header checksums second (SNES/Genesis internal checksum + file size), and an optional pluggable hash-database module last, for the remaining edge cases only. The core library ships database-free.
SV_MODE_DEFAULT allows registering and exercising the full pipeline (register → save → pull → rotate) against any arbitrary file or folder, with zero platform/emulator-specific logic. This is what makes the library testable on day one with fake files, before a single real emulator strategy is implemented.
See also: Architecture-Overview | Layer-1-Interface | Data-Types-Reference
libsavesync Wiki