@@ -0,0 +1,154 @@
//#define DBG

using System;
using System.IO;
using BX20Serializer;
using UnityEngine;
using Vexe.Runtime.Extensions;

namespace Vexe.FastSave.Serializers
{
public class ReflectiveComponentSerializer : ReflectiveSerializer
{
public override bool Handles(Type type)
{
return typeof(Component).IsAssignableFrom(type);
}

public override void Serialize(Stream stream, Type type, object value)
{
var members = GetMembers(type);
for (int i = 0; i < members.Length; i++)
{
var member = X20Member.WrapMember(members[i], value);

#if DBG
Debug.Log("Serializing: " + member.Name);
#endif

if (typeof(Component).IsAssignableFrom(member.Type))
{
SerializeComponentReference(stream, member.Value as Component);
}
else if (member.Type == typeof(GameObject))
{
SerializeGameObjectReference(stream, member.Value as GameObject);
}
else
{
ctx.Serializer.Serialize_Main(stream, member.Type, member.Value);
}
}
}

public override void Deserialize(Stream stream, Type type, ref object instance)
{
var members = GetMembers(type);
for (int i = 0; i < members.Length; i++)
{
var member = X20Member.WrapMember(members[i], instance);

#if DBG
Debug.Log("Deserializing: " + member.Name);
#endif

if (typeof(Component).IsAssignableFrom(member.Type))
{
member.Value = DeserializeComponentReference(stream, member.Type);
}
else if (member.Type == typeof(GameObject))
{
member.Value = DeserializeGameObjectReference(stream);
}
else
{
var memberValue = member.Value;
ctx.Serializer.Deserialize_Main(stream, member.Type, ref memberValue);
member.Value = memberValue;
}
}
}

static void SerializeComponentReference(Stream stream, Component value)
{
if (value == null)
{
Basic.WriteByte(stream, 0);
return;
}

var prefabId = PrefabStorage.Current.GetIndex(value.gameObject);
if (prefabId != -1)
{
Basic.WriteByte(stream, 1);
Write(stream, prefabId);
}
else
{
Basic.WriteByte(stream, 2);
int refId = value.GetOrAddComponent<FSReference>().GetPersistentId();
Write(stream, refId);
}
}

static Component DeserializeComponentReference(Stream stream, Type type)
{
byte b = Basic.ReadByte(stream);
if (b == 0)
return null;

if (b == 1)
{
var prefabId = stream.ReadInt();
var go = PrefabStorage.Current.Get(prefabId);
return go.GetComponent(type);
}
else
{
var refId = stream.ReadInt();
var go = FSReference.Get<Component>(refId);
return go.GetComponent(type);
}
}

public static void SerializeGameObjectReference(Stream stream, GameObject value)
{
if (value == null)
{
Basic.WriteByte(stream, 0);
return;
}

var prefabId = PrefabStorage.Current.GetIndex(value);
if (prefabId != -1)
{
Basic.WriteByte(stream, 1);
Write(stream, prefabId);
}
else
{
Basic.WriteByte(stream, 2);
int refId = value.GetOrAddComponent<FSReference>().GetPersistentId();
Write(stream, refId);
}
}

public static GameObject DeserializeGameObjectReference(Stream stream)
{
var b = Basic.ReadByte(stream);
if (b == 0)
return null;

if (b == 1)
{
var prefabId = stream.ReadInt();
return PrefabStorage.Current.Get(prefabId);
}
else
{
var refId = stream.ReadInt();
return FSReference.Get(refId);
}
}
}
}
@@ -0,0 +1,111 @@
//#define DBG

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Vexe.Runtime.Types;
using UnityObject = UnityEngine.Object;

#if UNITY_EDITOR
using UnityEditor;
using System.Linq;
#endif

namespace Vexe.FastSave
{
public class AssetStorage : BaseScriptableObject
{
[Display(Dict.HorizontalPairs)]
public AssetLookup Assets = new AssetLookup();

[Hide] public static readonly List<Type> SupportedTypes = new List<Type>()
{
typeof(Mesh),
typeof(AudioClip),
typeof(Material),
typeof(PhysicMaterial),
typeof(PhysicsMaterial2D),
typeof(Flare),
typeof(GUIStyle),
typeof(Texture),
typeof(RuntimeAnimatorController),
typeof(AnimationClip),
typeof(UnityObject), // for text assets etc
};

static AssetStorage _Current;
public static AssetStorage Current
{
get
{
if (_Current == null)
_Current = GetStore();
return _Current;
}
}

public string Store(UnityObject asset)
{
var name = asset.name;
Assets[name] = asset;
return name;
}

public UnityObject Get(string key)
{
UnityObject result;
if (!Assets.TryGetValue(key, out result))
return null;
return result;
}

#if UNITY_EDITOR
[Show] void FilterNulls()
{
var cleaned = new AssetLookup();
var iter = Assets.GetEnumerator();
while(iter.MoveNext())
{
var current = iter.Current;

var value = current.Value;
if (value == null)
return;

cleaned.Add(current.Key, value);
}
Assets = cleaned;
}
#endif

static AssetStorage GetStore()
{
AssetStorage store = null;
#if UNITY_EDITOR
var storeName = typeof(AssetStorage).Name + ".asset";
var directory = Directory.GetDirectories("Assets", "FastSave", SearchOption.AllDirectories).FirstOrDefault();
if (directory == null)
Debug.LogError("Couldn't find FastSave directory!");
else
{
var storePath = directory + "/Resources/" + storeName;
store = AssetDatabase.LoadAssetAtPath<AssetStorage>(storePath);
if (store == null)
{
store = ScriptableObject.CreateInstance<AssetStorage>();
AssetDatabase.CreateAsset(store, storePath);
AssetDatabase.ImportAsset(storePath, ImportAssetOptions.ForceUpdate);
AssetDatabase.Refresh();
}
}
#endif
if (store == null)
store = Resources.Load<AssetStorage>(typeof(AssetStorage).Name);
return store;
}
}

[Serializable]
public class AssetLookup : SerializableDictionary<string, UnityObject> { }
}
@@ -0,0 +1,67 @@
using UnityEngine;
using System.Collections.Generic;
using Vexe.Runtime.Types;
using Vexe.Runtime.Helpers;
using Vexe.Runtime.Extensions;

#if UNITY_EDITOR
using UnityEditor;
using System.IO;
#endif

namespace Vexe.FastSave
{
[CreateAssetMenu(menuName = "Vexe/PrefabStorage")]
public class PrefabStorage : BaseScriptableObject
{
public List<GameObject> Prefabs = new List<GameObject>();

public GameObject Get(int id)
{
Assert.InBounds(Prefabs, id, "Prefabs");
return Prefabs[id];
}

public bool IsPrefab(GameObject go)
{
return GetIndex(go) != -1;
}

public int GetIndex(GameObject prefab)
{
return Prefabs.IndexOf(prefab);
}

static PrefabStorage _Current;
public static PrefabStorage Current
{
get
{
if (_Current == null)
{
_Current = Resources.Load<PrefabStorage>(typeof(PrefabStorage).Name);
if (_Current == null)
{
Debug.LogError("No prefab storage was found! Please create one under Resources/Storage");
return null;
}
}
return _Current;
}
}

#if UNITY_EDITOR
[Show] void Populate()
{
Prefabs.Clear();
var prefabFiles = Directory.GetFiles("Assets", "*.prefab", SearchOption.AllDirectories);
for (int i = 0; i < prefabFiles.Length; i++)
{
var path = prefabFiles[i];
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
Prefabs.Add(prefab);
}
}
#endif
}
}
@@ -0,0 +1,323 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
SceneSettings:
m_ObjectHideFlags: 0
m_PVSData:
m_PVSObjectsArray: []
m_PVSPortalsArray: []
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: .25
backfaceThreshold: 100
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 6
m_Fog: 0
m_FogColor: {r: .5, g: .5, b: .5, a: 1}
m_FogMode: 3
m_FogDensity: .00999999978
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: .211999997, g: .226999998, b: .259000003, a: 1}
m_AmbientEquatorColor: {r: .114, g: .125, b: .133000001, a: 1}
m_AmbientGroundColor: {r: .0469999984, g: .0430000015, b: .0350000001, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: .5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
--- !u!127 &3
LevelGameManager:
m_ObjectHideFlags: 0
--- !u!157 &4
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 5
m_GIWorkflowMode: 0
m_LightmapsMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_TemporalCoherenceThreshold: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 1
m_LightmapEditorSettings:
serializedVersion: 3
m_Resolution: 2
m_BakeResolution: 40
m_TextureWidth: 1024
m_TextureHeight: 1024
m_AOMaxDistance: 1
m_Padding: 2
m_CompAOExponent: 0
m_LightmapParameters: {fileID: 0}
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherRayCount: 1024
m_LightmapSnapshot: {fileID: 0}
m_RuntimeCPUUsage: 25
--- !u!196 &5
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentRadius: .5
agentHeight: 2
agentSlope: 45
agentClimb: .400000006
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
accuratePlacement: 0
minRegionArea: 2
cellSize: .166666672
manualCellSize: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &1068538704
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 4
m_Component:
- 4: {fileID: 1068538705}
- 114: {fileID: 1068538706}
m_Layer: 0
m_Name: Ref1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1068538705
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1068538704}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 44.9715767, y: 28.4876804, z: 4.39548206}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
--- !u!114 &1068538706
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1068538704}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5db7c82b72f30a44081b8ed7827d1c46, type: 3}
m_Name:
m_EditorClassIdentifier:
_id: -1079222
dbg: 0
--- !u!1 &1081347494
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 4
m_Component:
- 4: {fileID: 1081347495}
- 114: {fileID: 1081347496}
m_Layer: 0
m_Name: Ref2
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1081347495
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1081347494}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 44.9715767, y: 28.4876804, z: 4.39548206}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
--- !u!114 &1081347496
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1081347494}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5db7c82b72f30a44081b8ed7827d1c46, type: 3}
m_Name:
m_EditorClassIdentifier:
_id: -1079224
dbg: 0
--- !u!1 &1376626298
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 4
m_Component:
- 4: {fileID: 1376626300}
- 114: {fileID: 1376626299}
m_Layer: 0
m_Name: Test
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1376626299
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1376626298}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 34fefd8a5c450ba4ab312b4ec6d19755, type: 3}
m_Name:
m_EditorClassIdentifier:
_id: -934346
dbg: 0
target: {fileID: 1403682384}
output: AAAAAAAHAAAABwAAAFNhdmUgTWUBAAAAAAgAAAAIAAAAVW50YWdnZWQAAAAAAAEFAAAAAgAAAABZAAAAWQAAAFVuaXR5RW5naW5lLlRyYW5zZm9ybSwgVW5pdHlFbmdpbmUsIFZlcnNpb249MC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsAAAAAAAAAK5HOEIKTt5BmNQ4QAAAAIAAAAAAAAAAAGPdiUBjwMM/oIMhQAAAAAAAWgAAAFoAAABVbml0eUVuZ2luZS5NZXNoRmlsdGVyLCBVbml0eUVuZ2luZSwgVmVyc2lvbj0wLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwAAAAAAAAAAAEAAAAAAAEAAAAABAAAAAQAAABDdWJlAAAAAABbAAAAWwAAAFVuaXR5RW5naW5lLkJveENvbGxpZGVyLCBVbml0eUVuZ2luZSwgVmVyc2lvbj0wLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwEAAAAAAFwAAABcAAAAVW5pdHlFbmdpbmUuTWVzaFJlbmRlcmVyLCBVbml0eUVuZ2luZSwgVmVyc2lvbj0wLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwAAAAAAAAAAQAAAAEAAQAAAAAAAQAAAAACAAAAAAABAAAAAAQAAAAEAAAAQmx1ZQEBAAAA/////wAAAAAAUAAAAFAAAABTb21lRGF0YSwgQXNzZW1ibHktQ1NoYXJwLCBWZXJzaW9uPTAuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAAAAAAAAAAAAQAAAAAAAgAAAAEAAAAACgAAAAoAAABTb21lIEtleSAx6AMAAAIAAAAACgAAAAoAAABTb21lIEtleSAy6gAAAP////8AAgAAAAAABQAAACIAAACzFQAABgAAAH0NAAAHAwAAAAMAAAAAAAMAAADhOmpDAA0qR57vCEIAAAAASojv/wAAAABIiO//AQAAAAAAAAAA
--- !u!4 &1376626300
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1376626298}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 40.5026474, y: 27.1177406, z: 1.8959446}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
--- !u!1 &1403682384
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 4
m_Component:
- 4: {fileID: 1403682389}
- 33: {fileID: 1403682388}
- 65: {fileID: 1403682387}
- 23: {fileID: 1403682386}
- 114: {fileID: 1403682385}
m_Layer: 0
m_Name: Save Me
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1403682385
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1403682384}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e16b5756b5b9274ba0e621e01843cc9, type: 3}
m_Name:
m_EditorClassIdentifier:
_id: -945064
dbg: 0
container:
_Buckets: 0000000001000000ffffffff
_HashCodes: 84f41c5285f41c5200000000
_Next: ffffffffffffffff00000000
_Count: 2
_Version: 19327
_FreeList: -1
_FreeCount: 0
_Keys:
- Some Key 1
- Some Key 2
-
_Values: e8030000ea00000000000000
stringField:
intArray: 22000000b3150000060000007d0d000007030000
floatList:
- 234.229996
- 43533
- 34.2340012
testTransform: {fileID: 1068538705}
testGameObject: {fileID: 1081347494}
testPrefab: {fileID: 106520, guid: 66fbf79b06b9dfe4d953ef8e6884a68e, type: 2}
--- !u!23 &1403682386
MeshRenderer:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1403682384}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_Materials:
- {fileID: 2100000, guid: ba76a0dc5e56cad468e9b2d125d2c1e8, type: 2}
m_SubsetIndices:
m_StaticBatchRoot: {fileID: 0}
m_UseLightProbes: 1
m_ReflectionProbeUsage: 1
m_ProbeAnchor: {fileID: 0}
m_ScaleInLightmap: 1
m_PreserveUVs: 1
m_ImportantGI: 0
m_AutoUVMaxDistance: .5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingOrder: 0
--- !u!65 &1403682387
BoxCollider:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1403682384}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!33 &1403682388
MeshFilter:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1403682384}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &1403682389
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1403682384}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 46.0699997, y: 27.788105, z: 2.88797569}
m_LocalScale: {x: 4.30827475, y: 1.52930868, z: 2.52365875}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
@@ -0,0 +1,30 @@
using UnityEngine;
using Vexe.FastSave;
using Vexe.Runtime.Extensions;
using Vexe.Runtime.Types;

namespace FSExamples
{
public class GameObjectTest : BaseBehaviour
{
public GameObject target;

[HideInInspector]
public string output;

[Show] void SaveGo()
{
output = Save.GameObjectToMemory(target).GetString();
}

[Show] void LoadIntoNewGo()
{
Load.GameObjectFromMemory(output.GetBytes(), new GameObject());
}

[Show] void LoadIntoTargetGo()
{
target.LoadFromMemory(output.GetBytes());
}
}
}

Large diffs are not rendered by default.

@@ -0,0 +1,31 @@
using UnityEngine;
using Vexe.FastSave;
using Vexe.Runtime.Extensions;
using Vexe.Runtime.Types;

namespace FSExamples
{
public class HierarchyTest : BaseBehaviour
{
public GameObject target;

[HideInInspector]
public string output;

[Show] void SaveHierarchy()
{
output = target.SaveHierarchyToMemory().GetString();
}

[Show] void LoadIntoNewHierarchy()
{
Load.HierarchyFromMemory(output.GetBytes(), new GameObject("ROOT"));
}

[Show] void LoadIntoTargetHierarchy()
{
Load.HierarchyFromMemory(output.GetBytes(), target);
}
}

}

Large diffs are not rendered by default.

@@ -0,0 +1,23 @@
using UnityEngine;
using Vexe.FastSave;
using Vexe.Runtime.Extensions;
using Vexe.Runtime.Types;

namespace FSExamples
{
public class MarkedTest : BaseBehaviour
{
[HideInInspector]
public string output;

[Show] void SaveMarked()
{
output = Save.MarkedToMemory().GetString();
}

[Show] void LoadMarked()
{
Load.MarkedFromMemory(output.GetBytes());
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Vexe.Runtime.Types;

namespace FSExamples
{
public class SomeData : BaseBehaviour
{
public Container container;
public string stringField;
public int[] intArray;
public List<float> floatList;
public MeshRenderer testRenderer;
public GameObject testGameObject;
public GameObject testPrefab;

[Serializable]
public class Container : SerializableDictionary<string, int> { }
}
}