ZionGames Docs

Home / Zion Tools

🧰 Zion Tools

Unity Editor Asset Tools

Adding a Custom Source

Implement ISaveSource to add any storage backend - ScriptableObject snapshots, cloud saves, in-memory test fixtures. Drop the class in an editor assembly with a public parameterless constructor and it auto-discovers on the next domain reload.

Interface shape

public sealed class PlayerPrefsSource : ISaveSource
{
    public string DisplayName => "PlayerPrefs";
    public int Order => 5;

    public IEnumerable<SaveEntry> List() { /* enumerate your keys */ }
    public byte[] Load(SaveEntry e)      { /* return raw bytes for the key */ }
    public void Save(SaveEntry e, byte[] payload) { /* write back */ }
    public void Delete(SaveEntry e)      { /* delete key */ }
    public void Reveal(SaveEntry e)      { /* no-op or focus settings */ }
}

Worked example - PlayerPrefs via a manifest key

A stripped-down teaching version of the shipping source. The same pattern applies to any custom backend.

using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using Zion.SaveInspector.Editor;

public sealed class PlayerPrefsSource : ISaveSource
{
    public string DisplayName => "PlayerPrefs";
    public int Order => 5;

    private const string ManifestKey = "ZionSaveInspector.Manifest";

    public IEnumerable<SaveEntry> List()
    {
        var manifest = PlayerPrefs.GetString(ManifestKey, string.Empty);
        if (string.IsNullOrEmpty(manifest)) yield break;

        foreach (var key in manifest.Split(';'))
        {
            if (string.IsNullOrEmpty(key) || !PlayerPrefs.HasKey(key)) continue;

            var value = PlayerPrefs.GetString(key, string.Empty);
            yield return new SaveEntry
            {
                DisplayName    = key,
                Identifier     = key,
                SizeBytes      = Encoding.UTF8.GetByteCount(value),
                LastModified   = System.DateTime.Now, // PlayerPrefs has no timestamps
                SourceTypeName = nameof(PlayerPrefsSource),
                FormatHint     = value.TrimStart().StartsWith("{") ? "json" : null
            };
        }
    }

    public byte[] Load(SaveEntry entry)
        => Encoding.UTF8.GetBytes(PlayerPrefs.GetString(entry.Identifier, string.Empty));

    public void Save(SaveEntry entry, byte[] payload)
    {
        PlayerPrefs.SetString(entry.Identifier, Encoding.UTF8.GetString(payload));
        PlayerPrefs.Save();
    }

    public void Delete(SaveEntry entry)
    {
        PlayerPrefs.DeleteKey(entry.Identifier);

        // Also strip the key from the manifest so it doesn't reappear.
        var manifest = PlayerPrefs.GetString(ManifestKey, string.Empty);
        var keys = manifest.Split(';').Where(k => k != entry.Identifier);
        PlayerPrefs.SetString(ManifestKey, string.Join(";", keys));
        PlayerPrefs.Save();
    }

    public void Reveal(SaveEntry entry)
    {
        Debug.Log($"PlayerPrefs key: {entry.Identifier}");
    }
}

After Unity recompiles, the source appears in the Source dropdown. Any registered key shows up as a save entry, and if the value is JSON the existing JsonSaveFormat parses it just like a file.

Opting in to backup diffs - ISaveBackupProvider 1.1

Additionally implement ISaveBackupProvider and the inspector's Compare > With Backup action lights up for your source, exactly like the built-ins:

public sealed class MyCloudSource : ISaveSource, ISaveBackupProvider
{
    // ... ISaveSource members ...

    public bool TryLoadBackup(SaveEntry entry, out byte[] payload,
                              out string backupLabel)
    {
        payload = LoadPreviousRevision(entry.Identifier);
        backupLabel = "previous cloud revision";
        return payload != null;
    }
}
  • Return false when no backup exists - the menu item shows as disabled.
  • backupLabel appears in the diff window title so the user knows what they're comparing against.
  • The interface is opt-in; sources without it simply don't offer the menu item. Nothing else changes.
Continue to Custom Format.