Avanatro Tools

API Reference

Every public method in the AvanatroTools.SaveStack namespace, grouped by surface. Side-effects, events, and exceptions are noted with badges — no surprises after install.

v1.1.0 namespace: AvanatroTools.SaveStack package: com.avanatrotools.savestack

How to read this page

Each method shows its full signature, a one-sentence description, and badges for what to expect at runtime:

SaveSystem — main slot facade

Static entry point for all gameplay save operations. Slot-based (default slot = 0). All public methods serialize through the per-slot SemaphoreSlim; async paths hold the lock across await.

Configuration

Set or swap providers. All four are independently optional — leave a parameter null to keep the current provider.

static void Configure(ISerializer serializer = null, IStorageProvider storage = null, IEncryptionProvider encryption = null, IPathProvider pathProvider = null, MigrationHandler migrations = null)

Replaces one or more providers. Resets the slot cache so the next access lazily rebuilds against the new providers.

static void DisableVersioning()

Drops the MigrationHandler from the current configuration. New saves are written as raw JSON without an envelope.

static SaveSlot GetSlot(int index = 0)

Returns the cached SaveSlot instance for the given index. Slots are created lazily on first access.

Save & Load (sync)

Default path for gameplay code. Throws on serialization / storage failure — wrap with TryLoad if you need branch-on-error.

static void Save<T>(string key, T data, int slot = 0)

Serializes, encrypts, and writes data under key. Then writes slot meta (__meta) atomically under the same lock.

eventsthrows ArgumentException on path-traversal key

static T Load<T>(string key, int slot = 0)

Reads, decrypts, deserializes the value stored under key. Returns default(T) when the key is missing.

eventsside-effect versioned slots write .premigration_v{N}.bak once on stale-version load

static bool TryLoad<T>(string key, out T value, out string error, int slot = 0)

Non-throwing load. Returns true + populated value on success; false + descriptive error when the key is missing or any error occurs.

eventsside-effect

static bool HasKey(string key, int slot = 0)

Returns true when a value is stored under the given key in the given slot. Does not touch the storage backend beyond a single Exists check.

static bool HasValidSlot(int slot)

Returns true when the slot's __meta record can be successfully read and deserialized. Shallow check — individual user keys are not validated.

Save & Load (async)

Identical semantics to the sync API. I/O is dispatched via IAsyncStorageProvider when the storage supports it (e.g. FileStorage), otherwise offloads to Task.Run.

static async Task SaveAsync<T>(string key, T data, int slot = 0, CancellationToken ct = default)

Async Save. Serialization + encryption run inline on the caller thread; the write itself is awaited.

asyncevents

static async Task<T> LoadAsync<T>(string key, int slot = 0, CancellationToken ct = default)

Async Load. Throws KeyNotFoundException when the key is absent — use TryLoadAsync if you need the non-throwing form.

asynceventsside-effect

static async Task<(bool success, T value)> TryLoadAsync<T>(string key, int slot = 0, CancellationToken ct = default)

Non-throwing async load. Returns a tuple; OnLoadFailed still fires on errors but is not re-thrown.

asynceventsside-effect

Slot inventory & metadata

Use these for "Load Game" menus, save-thumbnail listings, and admin tooling.

static IReadOnlyList<SlotInfo> GetAllSlots()

Enumerates every slot directory currently under the configured root path. Returns one SlotInfo per slot — index, meta record, key list (excluding reserved keys).

static SlotInfo GetSlotInfo(int slot)

Inventory of a single slot. Reads the __meta record and lists user keys (no full decrypt of each value).

static SlotMeta GetSlotMeta(int slot = 0)

Returns the slot's meta record (SlotIndex / LastScene / SavedAt / Description / CustomFields / TotalPlaytimeTicks) or null if no meta exists.

static IEnumerable<int> GetUsedSlots()

Returns the sorted set of slot indices currently in use. Skips empty slots without a __meta record.

static void SetSlotDescription(int slot, string description)

Sets the user-facing slot description (shown in load menus). Atomic Read-Modify-Write on __meta — does not clobber other meta fields.

static string GetSlotDescription(int slot)

Returns the previously-set slot description, or null when none is set.

static void SetSlotMetaField(int slot, string key, string value)

Sets a custom meta field (key/value). Useful for save-screen extras: playtime label, character class, location name, etc.

static string GetSlotMetaField(int slot, string key)

Reads a previously-set custom meta field. Returns null on miss.

static void RemoveSlotMetaField(int slot, string key)

Removes a previously-set custom meta field. No-op when the key is absent.

static long GetTotalPlaytime(int slot = 0)

Returns the slot's accumulated playtime in DateTime.Ticks. Increment with PlaytimeTracker for monotonic-clock-safe tracking.

Slot lifecycle

Destructive operations — guard with confirmation in the UI.

static void DeleteSlot(int slot)

Deletes every key in the slot, including reserved keys (__meta, __quests, __flags, scene buckets, backups).

static void DeleteAll()

Wipes the entire storage root — every slot, plus the settings folder (when sharing a root). Recoverable only from filesystem backups.

Pre-migration backups

Recovery API for schema migrations gone wrong. Backups are written transparently by Load when a slot's stored version is below the current — this surface lets you list and roll back.

static bool RestoreBackup(int slot, string key, int fromVersion)

Restores the slot/key from its .premigration_v{fromVersion}.bak. Returns false when no such backup exists. Does not raise save/load events — restore is a maintenance operation.

static IReadOnlyList<int> ListBackupVersions(int slot, string key)

Returns the sorted list of pre-migration backup versions that exist for the given slot/key. Empty list when no backups exist.

Flag store

First-class boolean flags per slot — for "met NPC", "tutorial seen", "world state unlocked". Persisted under the reserved key __flags; reads use a TryLoad-fallback so a corrupt flag store does not throw.

static void SetFlag(string flag, bool value, int slot = 0)

Sets the boolean flag for the given slot. Read-Modify-Write under the slot lock — multiple parallel SetFlag calls are safe.

static bool GetFlag(string flag, int slot = 0)

Returns the flag value, or false when not set. Never throws.

static bool HasFlag(string flag, int slot = 0)

Distinguishes "flag is set to false" from "flag was never set". Useful for tutorial gates.

static void ClearFlag(string flag, int slot = 0)

Removes the flag entirely. Subsequent HasFlag returns false.

static IReadOnlyDictionary<string, bool> GetAllFlags(int slot = 0)

Returns the full flag map for the slot. Snapshot — safe to enumerate concurrently with SetFlag.

Scene-state bucket

Storage layer for SaveSystemSceneController and the saver components. Usually not called directly — use SlotSave / SlotLoad or the SaveSystemSceneController instead.

static IReadOnlyDictionary<string, string> GetSceneState(string sceneName, int slot = 0)

Returns the captured state of every Saver in the named scene, keyed by Saver.GetUniqueKey().

static void SetSceneState(string sceneName, IReadOnlyDictionary<string, string> state, int slot = 0)

Writes a scene-state bucket back to the slot. The scene's SaveSystemSceneController calls this on save.

SlotSave / SlotLoad — high-level scene capture

The "press the save button" / "press the load button" API. Iterates every SaveSystemSceneController currently in the loaded scene(s) and triggers their scene-state capture / restore.

static int SlotSave(int slot)

Captures every SaveSystemSceneController's scene state into the given slot. Returns the number of controllers processed.

static int SlotLoad(int slot)

Restores every SaveSystemSceneController's scene state from the given slot. Returns the number of controllers processed.

Path-traversal validation

Used internally by Save / Load. Exposed publicly so callers can validate user-supplied keys before forwarding.

static void SaveSlot.ValidateUserKey(string key)

Throws ArgumentException when key contains .., /, \, null bytes, or a Windows drive letter. Safe pre-flight for user-input-driven save keys.

throws ArgumentException on violation

Events

Subscribe to observe save/load lifecycle. Subscriber exceptions are swallowed — your subscribers cannot break the save pipeline.

static event Action<int, string> OnSaved

Fired after a successful Save / SaveAsync. Arguments: (slot, key).

static event Action<int, string> OnLoaded

Fired after a successful Load / LoadAsync / TryLoad. Arguments: (slot, key).

static event Action<int, string, Exception> OnSaveFailed

Fired on save failure. Arguments: (slot, key, exception). The exception is re-thrown after — subscribers run synchronously before the throw.

static event Action<int, string, Exception> OnLoadFailed

Fired on load failure or key-not-found (for TryLoad). Arguments: (slot, key, exception).

SettingsSystem — non-slot global settings

Parallel facade for game settings (audio volume, quality, key bindings, language). No slots, no versioning, no events, no migration. Get is exception-free. Storage root defaults to <persistentDataPath>/settings/, kept separate from slot data. All operations are serialized through a static lock — safe to call from multiple threads.

Read & Write

static void Set<T>(string key, T value)

Stores value under key. Throws on serialization / storage failure — settings writes are considered fatal.

throws on serialize / storage failure

static T Get<T>(string key, T defaultValue = default)

Reads + decrypts + deserializes. Returns defaultValue when the key is missing OR any read / decrypt / deserialize step fails. Never throws.

static bool HasKey(string key)

Returns true when a value is stored under key.

static void Delete(string key)

Removes the value stored under key. No-op when missing.

static void Clear()

Removes every setting. Equivalent to the "Reset to Defaults" button at the top of a settings screen.

static IEnumerable<string> Keys

Materialized snapshot of every settings key. Stable across Set / Delete / Clear calls that happen during iteration.

static void Configure(ISerializer serializer = null, IStorageProvider storage = null, IEncryptionProvider encryption = null, IPathProvider pathProvider = null)

Swap providers. Like SaveSystem.Configure but the storage defaults to <root>/settings/ when only a path provider is given.

QuestStore — RPG quest tracking

Optional sub-module under AvanatroTools.SaveStack.Quests (separate asmdef). Persists under the reserved slot key __quests. Active/completed/failed states with stage progression, counters, and arbitrary key/value variables.

Construction

new QuestStore(int slot = 0)

Creates a quest store bound to slot. State is loaded lazily on first access. Multiple stores against the same slot share state via __quests.

Lifecycle

void Start(string questId)

Marks the quest as Active and timestamps it. No-op if already Active.

void Advance(string questId, int newStage)

Sets the quest stage. Active-only — throws if the quest is not currently Active.

throws on inactive quest

void Complete(string questId)

Marks Complete + timestamps. Idempotent on already-completed quests.

void Fail(string questId)

Marks Failed + timestamps. Idempotent on already-failed quests.

Counters & variables (per quest)

void IncrementCounter(string questId, string counterKey, int by = 1)

"Kill 10 boars": store.IncrementCounter("boar_quest", "boars_killed"). Active-only.

int GetCounter(string questId, string counterKey)

Returns the counter value, or 0 when missing.

void SetVariable(string questId, string key, string value)

Arbitrary per-quest key/value: "chosen_dialogue_branch" = "trader_kind". Active-only.

string GetVariable(string questId, string key)

Returns the variable value, or null when missing.

Queries & persistence

QuestState Get(string questId)

Returns the quest's full state record (status / stage / counters / variables / timestamps), or null when the quest has never been started.

IEnumerable<QuestState> GetActive() / GetCompleted()

Enumerates all quests currently Active / Completed. Use for quest-log UIs.

bool IsActive(string questId) / IsComplete(string questId)

Short-form status checks. Useful in dialog tree conditionals.

void Persist() / Reload()

Persist flushes pending changes to __quests; Reload re-reads from disk and discards in-memory state. Use Persist from your save trigger, Reload from your load trigger.

Saver components — scene-state drop-ins

Add to GameObjects in the Inspector. The scene's SaveSystemSceneController captures and restores them on SaveScene / LoadScene (or via SaveSystem.SlotSave / SlotLoad).

Saver — base class

Inherit if you need a custom saver. Override RecordState + ApplyState; the rest comes for free.

virtual string GetUniqueKey()

Returns OverrideKey when set, otherwise {sceneName}/{hierarchyPath}/{typeName}. The basis for cross-scene-stable persistence.

abstract string RecordState()

Capture the current GameObject state as a serialized string. Called at save time.

abstract void ApplyState(string serialized)

Restore from a previously-recorded string. Called at load time, before the next frame.

public string OverrideKey

Optional explicit key. Set this when you need cross-scene persistence on a GameObject that gets re-instantiated.

Built-in saver components

Drop-in components for the common cases. Each is one file, one purpose, no hidden state.

PositionSaver / RotationSaver

Captures + restores transform.position / transform.rotation. World-space.

ActiveSaver / EnabledSaver

ActiveSaver: GameObject's activeSelf. EnabledSaver: target Component's enabled field.

AnimatorParametersSaver

Captures all Animator parameters (Float / Int / Bool / Trigger) and current state hash per layer.

RigidbodySaver

Captures linearVelocity + angularVelocity + position/rotation (Unity 6 API; falls back via RigidbodyState container).

SaveSystemSceneController

One per scene. Maintains a static registry of all Saver instances; orchestrates capture / restore.

public int TargetSlot

The slot index this controller saves into / loads from. Inspector-exposed.

public bool AutoRestoreOnLoad

When true, restores scene state automatically on SceneManager.sceneLoaded. Default true.

public void SaveScene() / LoadScene()

Manual triggers — hook up to UI buttons or save-checkpoint events.

static void Register(Saver saver) / Unregister(Saver saver)

Called automatically by Saver.Awake / OnDestroy. Rarely called by user code — useful for runtime-instantiated savers that bypass Awake.

AutoSaver — periodic + lifecycle saves

Optional MonoBehaviour for "auto-save every N seconds" and "save on quit". One per scene.

public int TargetSlot

Slot index to save into. Default 0.

public float IntervalSeconds

Auto-save interval. Set to 0 to disable periodic saves (keep lifecycle hooks active).

public bool SaveOnApplicationQuit

Trigger a final save on OnApplicationQuit. Default true.

public bool SaveOnApplicationPause

Trigger save on OnApplicationPause(true) — mobile-recommended. Default true.

public bool SaveOnDisable

Trigger save on OnDisable. Useful for sub-scenes / mini-games.

public UnityEvent OnAutoSave

Inspector-wireable event raised before each auto-save. Hook UI "saving…" indicators here.

public void TriggerSave()

Programmatic trigger — bypass the timer + call out to SaveSystem.SlotSave(TargetSlot) immediately.

PlaytimeTracker

Monotonic-clock playtime accumulation. Robust against system-clock jumps + sleep/wake. Caps absurd intervals (>24h between flushes) defensively.

public int SlotIndex

The slot index this tracker accumulates into. Read-only after construction.

public void SetSlot(int slot)

Switches the target slot. Flushes pending ticks to the old slot first.

public void Flush()

Forces an accumulation pass — reads elapsed ticks via Stopwatch.GetTimestamp, calls SaveSystem.GetSlot(slot).AccumulatePlaytime, resets the timer.

Provider interfaces — swap the engine room

Four single-responsibility interfaces. Implement and pass to SaveSystem.Configure when the defaults don't fit. None of the four depend on the others.

ISerializer

Object ↔ JSON. Default: JsonNetSerializer (Newtonsoft, TypeNameHandling.Auto, Unity converters for Vector3/Color/Quaternion/Rect).

string Serialize<T>(T value)

Converts a value to its JSON string representation.

T Deserialize<T>(string json)

Parses JSON back to a typed instance.

IStorageProvider

byte[] ↔ filesystem-or-equivalent. Defaults: FileStorage (atomic), PlayerPrefsStorage (WebGL/mobile fallback). Optionally implement IAsyncStorageProvider for native async I/O.

void Write(string key, byte[] data) / byte[] Read(string key)

Core byte-level write/read. Read returns null when the key is absent.

bool Exists(string key) / Delete(string key) / Clear()

Existence check, single-key delete, whole-storage wipe.

IEnumerable<string> List(string prefix = null)

Enumerates all keys, optionally filtered by prefix. Used by slot listings + scene controllers.

IEncryptionProvider

byte[] ↔ byte[]. Defaults: NoEncryption, AesEncryption (AES-256-CBC, prod default), AesGcmEncryption (AES-GCM, IL2CPP only in v1.0).

byte[] Encrypt(byte[] plaintext)

Encrypts and returns the cipher bytes. Implementations are expected to handle their own IV / nonce generation.

byte[] Decrypt(byte[] cipher)

Decrypts back to plaintext. Implementations should accept their own past output without breaking Backwards-Compat across versions.

IPathProvider

Returns the root directory under which slot folders are created. Default: UnityPathProviderApplication.persistentDataPath/saves.

string GetRootDirectory()

Returns the absolute path to the save-root. Slots are folders below, e.g. <root>/slot_0/.

v1.1 New APIs

All additions are fully backwards-compatible. v1.0 code compiles and runs unchanged.

Streaming Save/Load (P.1)

Chunked async I/O for payloads over 50 MB. Peak RAM stays under 2× chunk size regardless of total payload.

static async Task SaveStreamAsync<T>(int slot, string key, T data, StreamSaveOptions options = null, IProgress<StreamProgress> progress = null, CancellationToken ct = default)

Serializes data in chunks and streams to storage. Use when a single-shot SaveAsync would spike RAM on large world states.

asyncevents

static async Task<T> LoadStreamAsync<T>(int slot, string key, IProgress<StreamProgress> progress = null, CancellationToken ct = default)

Reads and deserializes in chunks. Fires progress with phase label, bytes read, and percent complete.

asynceventsside-effect

class StreamSaveOptions { int ChunkSizeBytes = 2097152; }

Configuration for streaming writes. Default chunk is 2 MB. Larger chunks reduce I/O round-trips at the cost of higher peak memory.

class StreamProgress { string Phase; long BytesProcessed; double? PercentComplete; }

Progress token passed to IProgress<StreamProgress>. PercentComplete is null when total size is unknown.

Save-Batching / Transactions (P.2)

Write multiple keys atomically via 2-phase commit. Either all writes succeed or none are visible.

static BatchResult SaveBatch(int slot, IReadOnlyList<BatchEntry> entries)

Synchronous batch write. All entries commit together; on any failure the slot state is rolled back to its pre-batch state.

eventsthrows BatchCommitException on partial failure

static bool TrySaveBatch(int slot, IReadOnlyList<BatchEntry> entries, out BatchResult result)

Non-throwing variant. Returns false on failure; result.Error carries the exception.

events

static async Task<BatchResult> SaveBatchAsync(int slot, IReadOnlyList<BatchEntry> entries, CancellationToken ct = default)

Async batch write. I/O is awaited per chunk; rollback is synchronous on failure.

asyncevents

class BatchEntry { string Key; object Data; Type DataType; }

One entry in a batch. Use BatchEntry.For<T>(key, value) factory for typed construction.

class BatchResult { bool Success; int KeysWritten; Exception Error; }

Result of a batch operation. KeysWritten is 0 on failure (all-or-nothing semantics).

Security Stack (Z.1, Z.2)

HMAC-SHA256 tamper detection and HKDF-derived machine-bound encryption keys. Both are optional — pass to SaveSystem.Configure.

class HmacSha256Provider : IHmacProvider { public HmacSha256Provider(byte[] hmacKey); }

Appends a 32-byte HMAC-SHA256 tag to every save. Throws HmacVerificationFailedException on load when the tag does not match (format byte 0x07 on disk).

throws HmacVerificationFailedException on tamper

interface IHmacProvider { byte[] Sign(byte[] data); bool Verify(byte[] data, byte[] tag); }

Implement to supply a custom HMAC algorithm. SaveSystem.Configure(hmac: provider) accepts it.

static class MachineBoundKey { static byte[] DeriveForLocalMachine(byte[] masterKey, string info = "savestack-v1"); }

Derives a 32-byte AES key via HKDF-SHA256 using masterKey + the device’s IMachineIdentityProvider output as salt. Save files copied to another machine fail to decrypt.

interface IMachineIdentityProvider { byte[] GetMachineId(); }

Default implementation reads SystemInfo.deviceUniqueIdentifier. Swap with a custom provider for dedicated hardware tokens or server-issued IDs.

static class Hkdf { static byte[] DeriveKey(byte[] ikm, byte[] salt, byte[] info, int length = 32); }

Public HKDF-SHA256 primitive exposed for callers who need custom key derivation outside of MachineBoundKey.

Window → SaveStack → Repair Save File

Save-File-Repair Tool (Z.3) — EditorWindow that runs 5 diagnostic checks (header, HMAC, encryption, migration version, slot meta) and offers single-click repair or export. Also available headless via CLI: dotnet run --project SaveStackRepair.csproj -- --slot 0.

editor

Test Framework (M.1, Q.1, Q.2)

Fluent migration TDD, stress benchmarking, and fuzz mutation — all runnable from NUnit or Unity Test Runner.

class MigrationTestBed

Fluent Given/When/Then builder for migration handler tests. MigrationTestBed.Given(version, json).When(handler).Then(expectedVersion).AssertJsonPath(path, value).Run(). Supports handler chains for multi-step migrations.

class StressTestRunner { static StressResult Run(StressOptions options); }

Executes a configurable number of concurrent save/load cycles and collects P50/P95/P99/StdDev latency statistics. Reports the first failure with its iteration index and exception.

class FuzzerEngine { static FuzzResult RunAll(FuzzOptions options); }

Applies 5 byte-level mutators (BitFlip, ByteDeletion, RandomInjection, HeaderCorruption, Truncation) against every save format and asserts graceful exception behavior. Suitable for CI gating.

class StressOptions { int Iterations = 10000; int Concurrency = 4; int PayloadSizeBytes = 1024; }

Configure iteration count, concurrency level, and payload size for stress runs. Results include per-iteration latency histogram.

class FuzzOptions { int MutationsPerFormat = 100; bool IncludeHeaderMutations = true; }

Configure fuzz depth and which mutation categories to apply. IncludeHeaderMutations = false skips format-byte corruption for faster smoke tests.

Editor tools

In UNITY_EDITOR only — separate asmdef so they're not shipped to runtime.

SaveSystemWindow

Inspect / delete / export slots. Window → SaveStack → Inspect Saves. Pure-C# SaveSystemWindowViewModel for testability — covered by 13+ unit tests.

Window → SaveStack → Inspect Saves

Slot list, key detail, JSON-decoded preview (when not encrypted), Delete + Export-JSON actions.

editor

SaveWizard

Bulk-add saver components to a Selection. Window → SaveStack → Save Wizard.

Window → SaveStack → Save Wizard

Toggle Position / Rotation / Active / Enabled / Animator / Rigidbody, optional overrideKey prefix, "Add to N selected GameObjects" button. Snapshot-diff prevents overwriting existing overrideKeys.

editor