🧰 Zion Tools
Unity Editor Asset Tools
Overview
Animation Diagnostics
Zion Scene Backup
Zion Save Inspector
Zion Dependency Graph
Zion Asset Auditor
Overview
Installation
Quick Start
Concepts
Window Reference
Editing
Tree Search
Save Diff
Schema & Reports
Built-in Providers
PlayerPrefs
Custom Source
Custom Format
Tutorial
API Surface
Save Safety
Troubleshooting
Adding a Custom Format
Implement ISaveFormat to teach the inspector a format it doesn't know - binary protocols, MessagePack, Protobuf, or your own layout.
Interface shape
public sealed class MyBinaryFormat : ISaveFormat
{
public string DisplayName => "My Binary";
public int Order => 20;
public bool CanParse(SaveEntry entry, byte[] payload) => /* sniff header */;
public SaveNode Parse(byte[] payload) { /* build SaveNode tree */ }
public byte[] Serialize(SaveNode root) { /* serialise tree */ }
}
Worked example - a magic-byte binary layout
Suppose your game writes a binary save: a 4-byte magic MGS1, an int32 version, a length-prefixed string name, then int32 level and gold.
using System.IO;
using Zion.SaveInspector.Editor;
public sealed class MyBinaryFormat : ISaveFormat
{
public string DisplayName => "MyGame Binary v1";
public int Order => 20; // tried after JSON but before raw text
public bool CanParse(SaveEntry entry, byte[] payload)
{
return payload != null && payload.Length >= 4
&& payload[0] == 0x4D && payload[1] == 0x47 // 'M','G'
&& payload[2] == 0x53 && payload[3] == 0x31; // 'S','1'
}
public SaveNode Parse(byte[] payload)
{
var root = SaveNode.NewObject("(root)");
using var ms = new MemoryStream(payload);
using var r = new BinaryReader(ms);
r.ReadBytes(4); // magic
root.Children.Add(SaveNode.NewNumber("version", r.ReadInt32()));
root.Children.Add(SaveNode.NewString("playerName", r.ReadString()));
root.Children.Add(SaveNode.NewNumber("level", r.ReadInt32()));
root.Children.Add(SaveNode.NewNumber("gold", r.ReadInt32()));
return root;
}
public byte[] Serialize(SaveNode root)
{
using var ms = new MemoryStream();
using var w = new BinaryWriter(ms);
w.Write(new byte[] { 0x4D, 0x47, 0x53, 0x31 });
// Look up each field by name so the order matches the original layout
// regardless of how the user has folded the tree.
int version = (int)FindNumber(root, "version");
string playerName = FindString(root, "playerName");
int level = (int)FindNumber(root, "level");
int gold = (int)FindNumber(root, "gold");
w.Write(version);
w.Write(playerName ?? string.Empty);
w.Write(level);
w.Write(gold);
return ms.ToArray();
}
private static double FindNumber(SaveNode root, string name)
{
var n = root.Children.Find(c => c.Name == name);
return n != null ? n.NumberValue : 0;
}
private static string FindString(SaveNode root, string name)
{
var n = root.Children.Find(c => c.Name == name);
return n?.StringValue;
}
}
Things worth noticing
Order = 20runs beforeRawTextFormat(which isint.MaxValue) but after the built-in JSON (which is10). With the magic-byte check, JSON rejects the payload, your format claims it, and Raw text never runs.Serializereads children by name rather than position - the safer pattern, since users can rearrange or collapse the tree without breaking output order.- Number nodes are stored as
double. Cast back tointwhen serializing to integer fields, otherwise you'll write aBinaryWriter.Write(double)by accident. - Once the format compiles, diff, schema validation, search and clipboard all work on your binary saves too 1.1 - they operate on the parsed tree, not the raw bytes.
Continue to Tutorial.