🧰 Zion Tools
Unity Editor Asset Tools
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.