summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization
diff options
context:
space:
mode:
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs1004
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs.meta7
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs80
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs.meta8
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs527
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs.meta12
6 files changed, 1638 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs
new file mode 100644
index 0000000..1b55ceb
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs
@@ -0,0 +1,1004 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using UnityEngine;
+using Pathfinding.Util;
+
+
+#if ASTAR_NO_ZIP
+using Pathfinding.Serialization.Zip;
+#elif NETFX_CORE
+// For Universal Windows Platform
+using ZipEntry = System.IO.Compression.ZipArchiveEntry;
+using ZipFile = System.IO.Compression.ZipArchive;
+#else
+using Pathfinding.Ionic.Zip;
+#endif
+
+namespace Pathfinding.Serialization {
+ /// <summary>Holds information passed to custom graph serializers</summary>
+ public class GraphSerializationContext {
+ private readonly GraphNode[] id2NodeMapping;
+
+ /// <summary>
+ /// Deserialization stream.
+ /// Will only be set when deserializing
+ /// </summary>
+ public readonly BinaryReader reader;
+
+ /// <summary>
+ /// Serialization stream.
+ /// Will only be set when serializing
+ /// </summary>
+ public readonly BinaryWriter writer;
+
+ /// <summary>
+ /// Index of the graph which is currently being processed.
+ /// Version: uint instead of int after 3.7.5
+ /// </summary>
+ public readonly uint graphIndex;
+
+ /// <summary>Metadata about graphs being deserialized</summary>
+ public readonly GraphMeta meta;
+
+ public bool[] persistentGraphs;
+
+ public GraphSerializationContext (BinaryReader reader, GraphNode[] id2NodeMapping, uint graphIndex, GraphMeta meta) {
+ this.reader = reader;
+ this.id2NodeMapping = id2NodeMapping;
+ this.graphIndex = graphIndex;
+ this.meta = meta;
+ }
+
+ public GraphSerializationContext (BinaryWriter writer, bool[] persistentGraphs) {
+ this.writer = writer;
+ this.persistentGraphs = persistentGraphs;
+ }
+
+ public void SerializeNodeReference (GraphNode node) {
+ writer.Write(node == null ? -1 : (int)node.NodeIndex);
+ }
+
+ public void SerializeConnections (Connection[] connections, bool serializeMetadata) {
+ if (connections == null) {
+ writer.Write(-1);
+ } else {
+ int persistentConnections = 0;
+ for (int i = 0; i < connections.Length; i++) persistentConnections += persistentGraphs[connections[i].node.GraphIndex] ? 1 : 0;
+ writer.Write(persistentConnections);
+ for (int i = 0; i < connections.Length; i++) {
+ // Ignore connections to nodes in graphs which are not serialized
+ if (!persistentGraphs[connections[i].node.GraphIndex]) continue;
+
+ SerializeNodeReference(connections[i].node);
+ writer.Write(connections[i].cost);
+ if (serializeMetadata) writer.Write(connections[i].shapeEdgeInfo);
+ }
+ }
+ }
+
+ public Connection[] DeserializeConnections (bool deserializeMetadata) {
+ int count = reader.ReadInt32();
+
+ if (count == -1) {
+ return null;
+ } else {
+ var connections = ArrayPool<Connection>.ClaimWithExactLength(count);
+
+ for (int i = 0; i < count; i++) {
+ var target = DeserializeNodeReference();
+ var cost = reader.ReadUInt32();
+ if (deserializeMetadata) {
+ byte shapeEdgeInfo = Connection.NoSharedEdge;
+ if (meta.version < AstarSerializer.V4_1_0) {
+ // Read nothing
+ } else if (meta.version < AstarSerializer.V4_3_68) {
+ // Read, but discard data
+ reader.ReadByte();
+ } else {
+ shapeEdgeInfo = reader.ReadByte();
+ }
+ if (meta.version < AstarSerializer.V4_3_85) {
+ // Previously some additional bits were set to 1
+ shapeEdgeInfo &= 0b1111 | (1 << 6);
+ }
+ if (meta.version < AstarSerializer.V4_3_87) {
+ shapeEdgeInfo |= Connection.IncomingConnection | Connection.OutgoingConnection;
+ }
+
+ connections[i] = new Connection(
+ target,
+ cost,
+ shapeEdgeInfo
+ );
+ } else {
+ connections[i] = new Connection(target, cost, true, true);
+ }
+ }
+ return connections;
+ }
+
+ // TODO: Do we need to patch one way connections after deserializing?
+ }
+
+ public GraphNode DeserializeNodeReference () {
+ var id = reader.ReadInt32();
+
+ if (id2NodeMapping == null) throw new Exception("Calling DeserializeNodeReference when not deserializing node references");
+
+ if (id == -1) return null;
+ GraphNode node = id2NodeMapping[id];
+ if (node == null) throw new Exception("Invalid id ("+id+")");
+ return node;
+ }
+
+ /// <summary>Write a Vector3</summary>
+ public void SerializeVector3 (Vector3 v) {
+ writer.Write(v.x);
+ writer.Write(v.y);
+ writer.Write(v.z);
+ }
+
+ /// <summary>Read a Vector3</summary>
+ public Vector3 DeserializeVector3 () {
+ return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
+ }
+
+ /// <summary>Write an Int3</summary>
+ public void SerializeInt3 (Int3 v) {
+ writer.Write(v.x);
+ writer.Write(v.y);
+ writer.Write(v.z);
+ }
+
+ /// <summary>Read an Int3</summary>
+ public Int3 DeserializeInt3 () {
+ return new Int3(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32());
+ }
+
+ public int DeserializeInt (int defaultValue) {
+ if (reader.BaseStream.Position <= reader.BaseStream.Length-4) {
+ return reader.ReadInt32();
+ } else {
+ return defaultValue;
+ }
+ }
+
+ public float DeserializeFloat (float defaultValue) {
+ if (reader.BaseStream.Position <= reader.BaseStream.Length-4) {
+ return reader.ReadSingle();
+ } else {
+ return defaultValue;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Handles low level serialization and deserialization of graph settings and data.
+ /// Mostly for internal use. You can use the methods in the AstarData class for
+ /// higher level serialization and deserialization.
+ ///
+ /// See: AstarData
+ /// </summary>
+ public class AstarSerializer {
+ private AstarData data;
+
+ /// <summary>Zip which the data is loaded from</summary>
+ private ZipFile zip;
+
+ /// <summary>Memory stream with the zip data</summary>
+ private MemoryStream zipStream;
+
+ /// <summary>Graph metadata</summary>
+ private GraphMeta meta;
+
+ /// <summary>Settings for serialization</summary>
+ private SerializeSettings settings;
+
+ /// <summary>
+ /// Root GameObject used for deserialization.
+ /// This should be the GameObject which holds the AstarPath component.
+ /// Important when deserializing when the component is on a prefab.
+ /// </summary>
+ private GameObject contextRoot;
+
+ /// <summary>Graphs that are being serialized or deserialized</summary>
+ private NavGraph[] graphs;
+ bool[] persistentGraphs;
+
+ /// <summary>
+ /// Index used for the graph in the file.
+ /// If some graphs were null in the file then graphIndexInZip[graphs[i]] may not equal i.
+ /// Used for deserialization.
+ /// </summary>
+ private Dictionary<NavGraph, int> graphIndexInZip;
+
+ private int graphIndexOffset;
+
+ /// <summary>Extension to use for binary files</summary>
+ const string binaryExt = ".binary";
+
+ /// <summary>Extension to use for json files</summary>
+ const string jsonExt = ".json";
+
+ /// <summary>
+ /// Checksum for the serialized data.
+ /// Used to provide a quick equality check in editor code
+ /// </summary>
+ private uint checksum = 0xffffffff;
+
+ System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
+
+ /// <summary>Cached StringBuilder to avoid excessive allocations</summary>
+ static System.Text.StringBuilder _stringBuilder = new System.Text.StringBuilder();
+
+ /// <summary>
+ /// Returns a cached StringBuilder.
+ /// This function only has one string builder cached and should
+ /// thus only be called from a single thread and should not be called while using an earlier got string builder.
+ /// </summary>
+ static System.Text.StringBuilder GetStringBuilder () { _stringBuilder.Length = 0; return _stringBuilder; }
+
+ /// <summary>Cached version object for 3.8.3</summary>
+ public static readonly System.Version V3_8_3 = new System.Version(3, 8, 3);
+
+ /// <summary>Cached version object for 3.9.0</summary>
+ public static readonly System.Version V3_9_0 = new System.Version(3, 9, 0);
+
+ /// <summary>Cached version object for 4.1.0</summary>
+ public static readonly System.Version V4_1_0 = new System.Version(4, 1, 0);
+
+ /// <summary>Cached version object for 4.3.2</summary>
+ public static readonly System.Version V4_3_2 = new System.Version(4, 3, 2);
+
+ /// <summary>Cached version object for 4.3.6</summary>
+ public static readonly System.Version V4_3_6 = new System.Version(4, 3, 6);
+
+ /// <summary>Cached version object for 4.3.37</summary>
+ public static readonly System.Version V4_3_37 = new System.Version(4, 3, 37);
+
+ /// <summary>Cached version object for 4.3.12</summary>
+ public static readonly System.Version V4_3_12 = new System.Version(4, 3, 12);
+
+ /// <summary>Cached version object for 4.3.68</summary>
+ public static readonly System.Version V4_3_68 = new System.Version(4, 3, 68);
+
+ /// <summary>Cached version object for 4.3.74</summary>
+ public static readonly System.Version V4_3_74 = new System.Version(4, 3, 74);
+
+ /// <summary>Cached version object for 4.3.80</summary>
+ public static readonly System.Version V4_3_80 = new System.Version(4, 3, 80);
+
+ /// <summary>Cached version object for 4.3.83</summary>
+ public static readonly System.Version V4_3_83 = new System.Version(4, 3, 83);
+
+ /// <summary>Cached version object for 4.3.85</summary>
+ public static readonly System.Version V4_3_85 = new System.Version(4, 3, 85);
+
+ /// <summary>Cached version object for 4.3.87</summary>
+ public static readonly System.Version V4_3_87 = new System.Version(4, 3, 87);
+
+ /// <summary>Cached version object for 5.1.0</summary>
+ public static readonly System.Version V5_1_0 = new System.Version(5, 1, 0);
+
+ public AstarSerializer (AstarData data, GameObject contextRoot) : this(data, SerializeSettings.Settings, contextRoot) {
+ }
+
+ public AstarSerializer (AstarData data, SerializeSettings settings, GameObject contextRoot) {
+ this.data = data;
+ this.contextRoot = contextRoot;
+ this.settings = settings;
+ }
+
+ public void SetGraphIndexOffset (int offset) {
+ graphIndexOffset = offset;
+ }
+
+ void AddChecksum (byte[] bytes) {
+ checksum = Checksum.GetChecksum(bytes, checksum);
+ }
+
+ void AddEntry (string name, byte[] bytes) {
+#if NETFX_CORE
+ var entry = zip.CreateEntry(name);
+ using (var stream = entry.Open()) {
+ stream.Write(bytes, 0, bytes.Length);
+ }
+#else
+ zip.AddEntry(name, bytes);
+#endif
+ }
+
+ public uint GetChecksum () { return checksum; }
+
+ #region Serialize
+
+ public void OpenSerialize () {
+ // Create a new zip file, here we will store all the data
+ zipStream = new MemoryStream();
+#if NETFX_CORE
+ zip = new ZipFile(zipStream, System.IO.Compression.ZipArchiveMode.Create);
+#else
+ zip = new ZipFile();
+ zip.AlternateEncoding = System.Text.Encoding.UTF8;
+ zip.AlternateEncodingUsage = ZipOption.Always;
+ // Don't use parallel defate
+ zip.ParallelDeflateThreshold = -1;
+#endif
+ meta = new GraphMeta();
+ }
+
+ public byte[] CloseSerialize () {
+ // As the last step, serialize metadata
+ byte[] bytes = SerializeMeta();
+ AddChecksum(bytes);
+ AddEntry("meta"+jsonExt, bytes);
+
+#if !ASTAR_NO_ZIP && !NETFX_CORE
+ // Set dummy dates on every file to prevent the binary data to change
+ // for identical settings and graphs.
+ // Prevents the scene from being marked as dirty in the editor
+ // If ASTAR_NO_ZIP is defined this is not relevant since the replacement zip
+ // implementation does not even store dates
+ var dummy = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ foreach (var entry in zip.Entries) {
+ entry.AccessedTime = dummy;
+ entry.CreationTime = dummy;
+ entry.LastModified = dummy;
+ entry.ModifiedTime = dummy;
+ }
+#endif
+
+ // Save all entries to a single byte array
+#if !NETFX_CORE
+ zip.Save(zipStream);
+#endif
+ zip.Dispose();
+ bytes = zipStream.ToArray();
+
+ zip = null;
+ zipStream = null;
+ return bytes;
+ }
+
+ public void SerializeGraphs (NavGraph[] _graphs) {
+ if (graphs != null) throw new InvalidOperationException("Cannot serialize graphs multiple times.");
+ graphs = _graphs;
+
+ if (zip == null) throw new NullReferenceException("You must not call CloseSerialize before a call to this function");
+
+ if (graphs == null) graphs = new NavGraph[0];
+
+ persistentGraphs = new bool[graphs.Length];
+ for (int i = 0; i < graphs.Length; i++) {
+ //Ignore graph if null or if it should not persist
+ persistentGraphs[i] = graphs[i] != null && graphs[i].persistent;
+
+ if (!persistentGraphs[i]) continue;
+
+ // Serialize the graph to a byte array
+ byte[] bytes = Serialize(graphs[i]);
+
+ AddChecksum(bytes);
+ AddEntry("graph"+i+jsonExt, bytes);
+ }
+ }
+
+ /// <summary>Serialize metadata about all graphs</summary>
+ byte[] SerializeMeta () {
+ if (graphs == null) throw new System.Exception("No call to SerializeGraphs has been done");
+
+ meta.version = AstarPath.Version;
+ meta.graphs = graphs.Length;
+ meta.guids = new List<string>();
+ meta.typeNames = new List<string>();
+
+ // For each graph, save the guid
+ // of the graph and the type of it
+ for (int i = 0; i < graphs.Length; i++) {
+ if (persistentGraphs[i]) {
+ meta.guids.Add(graphs[i].guid.ToString());
+ meta.typeNames.Add(graphs[i].GetType().FullName);
+ } else {
+ meta.guids.Add(null);
+ meta.typeNames.Add(null);
+ }
+ }
+
+ // Grab a cached string builder to avoid allocations
+ var output = GetStringBuilder();
+ TinyJsonSerializer.Serialize(meta, output);
+ return encoding.GetBytes(output.ToString());
+ }
+
+ /// <summary>Serializes the graph settings to JSON and returns the data</summary>
+ public byte[] Serialize (NavGraph graph) {
+ // Grab a cached string builder to avoid allocations
+ var output = GetStringBuilder();
+
+ TinyJsonSerializer.Serialize(graph, output);
+ return encoding.GetBytes(output.ToString());
+ }
+
+ /// <summary>
+ /// Deprecated method to serialize node data.
+ /// Deprecated: Not used anymore
+ /// </summary>
+ [System.Obsolete("Not used anymore. You can safely remove the call to this function.")]
+ public void SerializeNodes () {
+ }
+
+ static int GetMaxNodeIndexInAllGraphs (NavGraph[] graphs) {
+ int maxIndex = 0;
+
+ for (int i = 0; i < graphs.Length; i++) {
+ if (graphs[i] == null || !graphs[i].persistent) continue;
+ graphs[i].GetNodes(node => {
+ maxIndex = Math.Max((int)node.NodeIndex, maxIndex);
+ if (node.Destroyed) {
+ Debug.LogError("Graph contains destroyed nodes. This is a bug.");
+ }
+ });
+ }
+ return maxIndex;
+ }
+
+ static byte[] SerializeNodeIndices (NavGraph[] graphs) {
+ var stream = new MemoryStream();
+ var writer = new BinaryWriter(stream);
+
+ int maxNodeIndex = GetMaxNodeIndexInAllGraphs(graphs);
+
+ writer.Write(maxNodeIndex);
+
+ // While writing node indices, verify that the max node index is the same
+ // (user written graphs might have gotten it wrong)
+ int maxNodeIndex2 = 0;
+ for (int i = 0; i < graphs.Length; i++) {
+ if (graphs[i] == null || !graphs[i].persistent) continue;
+ graphs[i].GetNodes(node => {
+ maxNodeIndex2 = Math.Max((int)node.NodeIndex, maxNodeIndex2);
+ writer.Write(node.NodeIndex);
+ });
+ }
+
+ // Nice to verify if users are writing their own graph types
+ if (maxNodeIndex2 != maxNodeIndex) throw new Exception("Some graphs are not consistent in their GetNodes calls, sequential calls give different results.");
+
+ byte[] bytes = stream.ToArray();
+ writer.Close();
+
+ return bytes;
+ }
+
+ /// <summary>Serializes info returned by NavGraph.SerializeExtraInfo</summary>
+ static byte[] SerializeGraphExtraInfo (NavGraph graph, bool[] persistentGraphs) {
+ var stream = new MemoryStream();
+ var writer = new BinaryWriter(stream);
+ var ctx = new GraphSerializationContext(writer, persistentGraphs);
+
+ ((IGraphInternals)graph).SerializeExtraInfo(ctx);
+ byte[] bytes = stream.ToArray();
+ writer.Close();
+
+ return bytes;
+ }
+
+ /// <summary>
+ /// Used to serialize references to other nodes e.g connections.
+ /// Nodes use the GraphSerializationContext.GetNodeIdentifier and
+ /// GraphSerializationContext.GetNodeFromIdentifier methods
+ /// for serialization and deserialization respectively.
+ /// </summary>
+ static byte[] SerializeGraphNodeReferences (NavGraph graph, bool[] persistentGraphs) {
+ var stream = new MemoryStream();
+ var writer = new BinaryWriter(stream);
+ var ctx = new GraphSerializationContext(writer, persistentGraphs);
+
+ graph.GetNodes(node => node.SerializeReferences(ctx));
+ writer.Close();
+
+ var bytes = stream.ToArray();
+ return bytes;
+ }
+
+ public void SerializeExtraInfo () {
+ if (!settings.nodes) return;
+ if (graphs == null) throw new InvalidOperationException("Cannot serialize extra info with no serialized graphs (call SerializeGraphs first)");
+
+ var bytes = SerializeNodeIndices(graphs);
+ AddChecksum(bytes);
+ AddEntry("graph_references"+binaryExt, bytes);
+
+ for (int i = 0; i < graphs.Length; i++) {
+ if (graphs[i] == null || !graphs[i].persistent) continue;
+
+ bytes = SerializeGraphExtraInfo(graphs[i], persistentGraphs);
+ AddChecksum(bytes);
+ AddEntry("graph"+i+"_extra"+binaryExt, bytes);
+
+ bytes = SerializeGraphNodeReferences(graphs[i], persistentGraphs);
+ AddChecksum(bytes);
+ AddEntry("graph"+i+"_references"+binaryExt, bytes);
+ }
+ }
+
+ #endregion
+
+ #region Deserialize
+
+ ZipEntry GetEntry (string name) {
+#if NETFX_CORE
+ return zip.GetEntry(name);
+#else
+ return zip[name];
+#endif
+ }
+
+ bool ContainsEntry (string name) {
+ return GetEntry(name) != null;
+ }
+
+ public bool OpenDeserialize (byte[] bytes) {
+ // Copy the bytes to a stream
+ zipStream = new MemoryStream();
+ zipStream.Write(bytes, 0, bytes.Length);
+ zipStream.Position = 0;
+ try {
+#if NETFX_CORE
+ zip = new ZipFile(zipStream);
+#else
+ zip = ZipFile.Read(zipStream);
+ // Don't use parallel defate
+ zip.ParallelDeflateThreshold = -1;
+#endif
+ } catch (Exception e) {
+ // Catches exceptions when an invalid zip file is found
+ Debug.LogError("Caught exception when loading from zip\n"+e);
+
+ zipStream.Dispose();
+ return false;
+ }
+
+ if (ContainsEntry("meta" + jsonExt)) {
+ meta = DeserializeMeta(GetEntry("meta" + jsonExt));
+ } else if (ContainsEntry("meta" + binaryExt)) {
+ meta = DeserializeBinaryMeta(GetEntry("meta" + binaryExt));
+ } else {
+ throw new Exception("No metadata found in serialized data.");
+ }
+
+ if (FullyDefinedVersion(meta.version) > FullyDefinedVersion(AstarPath.Version)) {
+ Debug.LogWarning("Trying to load data from a newer version of the A* Pathfinding Project\nCurrent version: "+AstarPath.Version+" Data version: "+meta.version +
+ "\nThis is usually fine as the stored data is usually backwards and forwards compatible." +
+ "\nHowever node data (not settings) can get corrupted between versions (even though I try my best to keep compatibility), so it is recommended " +
+ "to recalculate any caches (those for faster startup) and resave any files. Even if it seems to load fine, it might cause subtle bugs.\n");
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns a version with all fields fully defined.
+ /// This is used because by default new Version(3,0,0) > new Version(3,0).
+ /// This is not the desired behaviour so we make sure that all fields are defined here
+ /// </summary>
+ static System.Version FullyDefinedVersion (System.Version v) {
+ return new System.Version(Mathf.Max(v.Major, 0), Mathf.Max(v.Minor, 0), Mathf.Max(v.Build, 0), Mathf.Max(v.Revision, 0));
+ }
+
+ public void CloseDeserialize () {
+ zipStream.Dispose();
+ zip.Dispose();
+ zip = null;
+ zipStream = null;
+ }
+
+ NavGraph DeserializeGraph (int zipIndex, int graphIndex, System.Type[] availableGraphTypes) {
+ // Get the graph type from the metadata we deserialized earlier
+ var graphType = meta.GetGraphType(zipIndex, availableGraphTypes);
+
+ // Graph was null when saving, ignore
+ if (System.Type.Equals(graphType, null)) return null;
+
+ // Create a new graph of the right type
+ NavGraph graph = data.CreateGraph(graphType);
+ graph.graphIndex = (uint)(graphIndex);
+
+ var jsonName = "graph" + zipIndex + jsonExt;
+
+ if (ContainsEntry(jsonName)) {
+ // Read the graph settings
+ TinyJsonDeserializer.Deserialize(GetString(GetEntry(jsonName)), graphType, graph, contextRoot);
+ } else {
+ throw new FileNotFoundException("Could not find data for graph " + zipIndex + " in zip. Entry 'graph" + zipIndex + jsonExt + "' does not exist");
+ }
+
+ if (graph.guid.ToString() != meta.guids[zipIndex])
+ throw new Exception("Guid in graph file not equal to guid defined in meta file. Have you edited the data manually?\n"+graph.guid+" != "+meta.guids[zipIndex]);
+
+ return graph;
+ }
+
+ /// <summary>
+ /// Deserializes graph settings.
+ /// Note: Stored in files named "graph<see cref=".json"/>" where # is the graph number.
+ /// </summary>
+ public NavGraph[] DeserializeGraphs (System.Type[] availableGraphTypes) {
+ // Allocate a list of graphs to be deserialized
+ var graphList = new List<NavGraph>();
+
+ graphIndexInZip = new Dictionary<NavGraph, int>();
+
+ for (int i = 0; i < meta.graphs; i++) {
+ var newIndex = graphList.Count + graphIndexOffset;
+ var graph = DeserializeGraph(i, newIndex, availableGraphTypes);
+ if (graph != null) {
+ graphList.Add(graph);
+ graphIndexInZip[graph] = i;
+ }
+ }
+
+ graphs = graphList.ToArray();
+
+ DeserializeEditorSettingsCompatibility();
+ DeserializeExtraInfo();
+
+ return graphs;
+ }
+
+ bool DeserializeExtraInfo (NavGraph graph) {
+ var zipIndex = graphIndexInZip[graph];
+ var entry = GetEntry("graph"+zipIndex+"_extra"+binaryExt);
+
+ if (entry == null)
+ return false;
+
+ var reader = GetBinaryReader(entry);
+
+ var ctx = new GraphSerializationContext(reader, null, graph.graphIndex, meta);
+
+ // Call the graph to process the data
+ ((IGraphInternals)graph).DeserializeExtraInfo(ctx);
+ return true;
+ }
+
+ bool AnyDestroyedNodesInGraphs () {
+ bool result = false;
+
+ for (int i = 0; i < graphs.Length; i++) {
+ graphs[i].GetNodes(node => {
+ if (node.Destroyed) {
+ result = true;
+ }
+ });
+ }
+ return result;
+ }
+
+ GraphNode[] DeserializeNodeReferenceMap () {
+ // Get the file containing the list of all node indices
+ // This is correlated with the new indices of the nodes and a mapping from old to new
+ // is done so that references can be resolved
+ var entry = GetEntry("graph_references"+binaryExt);
+
+ if (entry == null) throw new Exception("Node references not found in the data. Was this loaded from an older version of the A* Pathfinding Project?");
+
+ var reader = GetBinaryReader(entry);
+ int maxNodeIndex = reader.ReadInt32();
+ var int2Node = new GraphNode[maxNodeIndex+1];
+
+ try {
+ for (int i = 0; i < graphs.Length; i++) {
+ graphs[i].GetNodes(node => {
+ var index = reader.ReadInt32();
+ int2Node[index] = node;
+ });
+ }
+ } catch (Exception e) {
+ throw new Exception("Some graph(s) has thrown an exception during GetNodes, or some graph(s) have deserialized more or fewer nodes than were serialized", e);
+ }
+
+#if !NETFX_CORE
+ // For Windows Store apps the BaseStream.Position property is not supported
+ // so we have to disable this error check on that platform
+ if (reader.BaseStream.Position != reader.BaseStream.Length) {
+ throw new Exception((reader.BaseStream.Length / 4) + " nodes were serialized, but only data for " + (reader.BaseStream.Position / 4) + " nodes was found. The data looks corrupt.");
+ }
+#endif
+
+ reader.Close();
+ return int2Node;
+ }
+
+ void DeserializeNodeReferences (NavGraph graph, GraphNode[] int2Node) {
+ var zipIndex = graphIndexInZip[graph];
+ var entry = GetEntry("graph"+zipIndex+"_references"+binaryExt);
+
+ if (entry == null) throw new Exception("Node references for graph " + zipIndex + " not found in the data. Was this loaded from an older version of the A* Pathfinding Project?");
+
+ var reader = GetBinaryReader(entry);
+ var ctx = new GraphSerializationContext(reader, int2Node, graph.graphIndex, meta);
+
+ graph.GetNodes(node => node.DeserializeReferences(ctx));
+ }
+
+ void DeserializeAndRemoveOldNodeLinks (GraphSerializationContext ctx) {
+ var count = ctx.reader.ReadInt32();
+ for (int i = 0; i < count; i++) {
+ var linkID = ctx.reader.ReadUInt64();
+ var startNode = ctx.DeserializeNodeReference();
+ var endNode = ctx.DeserializeNodeReference();
+ var connectedNode1 = ctx.DeserializeNodeReference();
+ var connectedNode2 = ctx.DeserializeNodeReference();
+ var clamped1 = ctx.DeserializeVector3();
+ var clamped2 = ctx.DeserializeVector3();
+ var postScanCalled = ctx.reader.ReadBoolean();
+
+ startNode.ClearConnections(true);
+ endNode.ClearConnections(true);
+ startNode.Walkable = false;
+ endNode.Walkable = false;
+ // In case of one-way links
+ GraphNode.Disconnect(connectedNode1, startNode);
+ GraphNode.Disconnect(connectedNode2, endNode);
+ }
+
+ bool graphRemoved = false;
+ for (int i = 0; i < graphs.Length && !graphRemoved; i++) {
+ if (graphs[i] != null && graphs[i] is PointGraph pointGraph) {
+ bool anyWalkable = false;
+ int count2 = 0;
+ pointGraph.GetNodes(node => {
+ anyWalkable |= node.Walkable;
+ count2++;
+ });
+ if (!anyWalkable && pointGraph.root == null && 2*count == count2 && (count2 > 0 || pointGraph.name.Contains("used for node links"))) {
+ // This is very likely an off-mesh link graph that was automatically created
+ // by the system in an earlier version
+ // It is not used anymore and should be removed
+ ((IGraphInternals)graphs[i]).DestroyAllNodes();
+ var ls = new List<NavGraph>(graphs);
+ ls.RemoveAt(i);
+ graphs = ls.ToArray();
+ graphRemoved = true;
+ }
+ if (pointGraph.name == "PointGraph (used for node links)") {
+ pointGraph.name = "PointGraph";
+ }
+ }
+ }
+
+ if (!graphRemoved && count > 0) {
+ Debug.LogWarning("Old off-mesh links were present in the serialized graph data. Not everything could be cleaned up properly. It is recommended that you re-scan all graphs and save the cache or graph file again. An attempt to migrate the old links was made, but a stray point graph may have been left behind.");
+ }
+ }
+
+
+
+ /// <summary>
+ /// Deserializes extra graph info.
+ /// Extra graph info is specified by the graph types.
+ /// See: Pathfinding.NavGraph.DeserializeExtraInfo
+ /// Note: Stored in files named "graph<see cref="_extra.binary"/>" where # is the graph number.
+ /// </summary>
+ void DeserializeExtraInfo () {
+ bool anyDeserialized = false;
+
+ // Loop through all graphs and deserialize the extra info
+ // if there is any such info in the zip file
+ for (int i = 0; i < graphs.Length; i++) {
+ anyDeserialized |= DeserializeExtraInfo(graphs[i]);
+ }
+
+ if (!anyDeserialized) {
+ return;
+ }
+
+ // Sanity check
+ // Make sure the graphs don't contain destroyed nodes
+ if (AnyDestroyedNodesInGraphs()) {
+ Debug.LogError("Graph contains destroyed nodes. This is a bug.");
+ }
+
+ // Deserialize map from old node indices to new nodes
+ var int2Node = DeserializeNodeReferenceMap();
+
+ // Deserialize node references
+ for (int i = 0; i < graphs.Length; i++) {
+ DeserializeNodeReferences(graphs[i], int2Node);
+ }
+
+ if (meta.version < V4_3_85) {
+ var entry = GetEntry("node_link2"+binaryExt);
+
+ if (entry != null) {
+ var reader = GetBinaryReader(entry);
+ var ctx = new GraphSerializationContext(reader, int2Node, 0, meta);
+ DeserializeAndRemoveOldNodeLinks(ctx);
+ }
+ }
+ }
+
+ /// <summary>Calls PostDeserialization on all loaded graphs</summary>
+ public void PostDeserialization () {
+ for (int i = 0; i < graphs.Length; i++) {
+ var ctx = new GraphSerializationContext(null, null, 0, meta);
+ ((IGraphInternals)graphs[i]).PostDeserialization(ctx);
+ }
+ }
+
+ /// <summary>
+ /// Deserializes graph editor settings.
+ /// For future compatibility this method does not assume that the graphEditors array matches the <see cref="graphs"/> array in order and/or count.
+ /// It searches for a matching graph (matching if graphEditor.target == graph) for every graph editor.
+ /// Multiple graph editors should not refer to the same graph.
+ /// Note: Stored in files named "graph<see cref="_editor.json"/>" where # is the graph number.
+ ///
+ /// Note: This method is only used for compatibility, newer versions store everything in the graph.serializedEditorSettings field which is already serialized.
+ /// </summary>
+ void DeserializeEditorSettingsCompatibility () {
+ for (int i = 0; i < graphs.Length; i++) {
+ var zipIndex = graphIndexInZip[graphs[i]];
+ ZipEntry entry = GetEntry("graph"+zipIndex+"_editor"+jsonExt);
+ if (entry == null) continue;
+
+ (graphs[i] as IGraphInternals).SerializedEditorSettings = GetString(entry);
+ }
+ }
+
+ /// <summary>Returns a binary reader for the data in the zip entry</summary>
+ private static BinaryReader GetBinaryReader (ZipEntry entry) {
+#if NETFX_CORE
+ return new BinaryReader(entry.Open());
+#else
+ var stream = new System.IO.MemoryStream();
+
+ entry.Extract(stream);
+ stream.Position = 0;
+ return new System.IO.BinaryReader(stream);
+#endif
+ }
+
+ /// <summary>Returns the data in the zip entry as a string</summary>
+ private static string GetString (ZipEntry entry) {
+#if NETFX_CORE
+ var reader = new StreamReader(entry.Open());
+#else
+ var buffer = new MemoryStream();
+
+ entry.Extract(buffer);
+ buffer.Position = 0;
+ var reader = new StreamReader(buffer);
+#endif
+ string s = reader.ReadToEnd();
+ reader.Dispose();
+ return s;
+ }
+
+ private GraphMeta DeserializeMeta (ZipEntry entry) {
+ return TinyJsonDeserializer.Deserialize(GetString(entry), typeof(GraphMeta)) as GraphMeta;
+ }
+
+ private GraphMeta DeserializeBinaryMeta (ZipEntry entry) {
+ var meta = new GraphMeta();
+
+ var reader = GetBinaryReader(entry);
+
+ if (reader.ReadString() != "A*") throw new System.Exception("Invalid magic number in saved data");
+ int major = reader.ReadInt32();
+ int minor = reader.ReadInt32();
+ int build = reader.ReadInt32();
+ int revision = reader.ReadInt32();
+
+ // Required because when saving a version with a field not set, it will save it as -1
+ // and then the Version constructor will throw an exception (which we do not want)
+ if (major < 0) meta.version = new Version(0, 0);
+ else if (minor < 0) meta.version = new Version(major, 0);
+ else if (build < 0) meta.version = new Version(major, minor);
+ else if (revision < 0) meta.version = new Version(major, minor, build);
+ else meta.version = new Version(major, minor, build, revision);
+
+ meta.graphs = reader.ReadInt32();
+
+ meta.guids = new List<string>();
+ int count = reader.ReadInt32();
+ for (int i = 0; i < count; i++) meta.guids.Add(reader.ReadString());
+
+ meta.typeNames = new List<string>();
+ count = reader.ReadInt32();
+ for (int i = 0; i < count; i++) meta.typeNames.Add(reader.ReadString());
+ reader.Close();
+
+ return meta;
+ }
+
+
+ #endregion
+
+ #region Utils
+
+ /// <summary>Save the specified data at the specified path</summary>
+ public static void SaveToFile (string path, byte[] data) {
+#if NETFX_CORE
+ throw new System.NotSupportedException("Cannot save to file on this platform");
+#else
+ using (var stream = new FileStream(path, FileMode.Create)) {
+ stream.Write(data, 0, data.Length);
+ }
+#endif
+ }
+
+ /// <summary>Load the specified data from the specified path</summary>
+ public static byte[] LoadFromFile (string path) {
+#if NETFX_CORE
+ throw new System.NotSupportedException("Cannot load from file on this platform");
+#else
+ using (var stream = new FileStream(path, FileMode.Open)) {
+ var bytes = new byte[(int)stream.Length];
+ stream.Read(bytes, 0, (int)stream.Length);
+ return bytes;
+ }
+#endif
+ }
+
+ #endregion
+ }
+
+ /// <summary>Metadata for all graphs included in serialization</summary>
+ public class GraphMeta {
+ /// <summary>Project version it was saved with</summary>
+ public Version version;
+
+ /// <summary>Number of graphs serialized</summary>
+ public int graphs;
+
+ /// <summary>Guids for all graphs</summary>
+ public List<string> guids;
+
+ /// <summary>Type names for all graphs</summary>
+ public List<string> typeNames;
+
+ /// <summary>Returns the Type of graph number index</summary>
+ public Type GetGraphType (int index, System.Type[] availableGraphTypes) {
+ // The graph was null when saving. Ignore it
+ if (String.IsNullOrEmpty(typeNames[index])) return null;
+
+ for (int j = 0; j < availableGraphTypes.Length; j++) {
+ if (availableGraphTypes[j].FullName == typeNames[index]) return availableGraphTypes[j];
+ }
+
+ throw new Exception("No graph of type '" + typeNames[index] + "' could be created, type does not exist");
+ }
+ }
+
+ /// <summary>Holds settings for how graphs should be serialized</summary>
+ public class SerializeSettings {
+ /// <summary>
+ /// Enable to include node data.
+ /// If false, only settings will be saved
+ /// </summary>
+ public bool nodes = true;
+
+ /// <summary>
+ /// Use pretty printing for the json data.
+ /// Good if you want to open up the saved data and edit it manually
+ /// </summary>
+ [System.Obsolete("There is no support for pretty printing the json anymore")]
+ public bool prettyPrint;
+
+ /// <summary>
+ /// Save editor settings.
+ /// Warning: Only applicable when saving from the editor using the AstarPathEditor methods
+ /// </summary>
+ public bool editorSettings;
+
+ /// <summary>Serialization settings for only saving graph settings</summary>
+ public static SerializeSettings Settings {
+ get {
+ return new SerializeSettings {
+ nodes = false
+ };
+ }
+ }
+ }
+}
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs.meta
new file mode 100644
index 0000000..4c4b58f
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/JsonSerializer.cs.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: edd6db33319e94141bb849f4f58261c3
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs
new file mode 100644
index 0000000..28f69b2
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs
@@ -0,0 +1,80 @@
+#if ASTAR_NO_ZIP
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Pathfinding.Serialization.Zip {
+ public enum ZipOption {
+ Always
+ }
+
+ public class ZipFile {
+ public System.Text.Encoding AlternateEncoding;
+ public ZipOption AlternateEncodingUsage = ZipOption.Always;
+ public int ParallelDeflateThreshold = 0;
+
+ Dictionary<string, ZipEntry> dict = new Dictionary<string, ZipEntry>();
+
+ public void AddEntry (string name, byte[] bytes) {
+ dict[name] = new ZipEntry(name, bytes);
+ }
+
+ public bool ContainsEntry (string name) {
+ return dict.ContainsKey(name);
+ }
+
+ public void Save (System.IO.Stream stream) {
+ var writer = new System.IO.BinaryWriter(stream);
+
+ writer.Write(dict.Count);
+ foreach (KeyValuePair<string, ZipEntry> pair in dict) {
+ writer.Write(pair.Key);
+ writer.Write(pair.Value.bytes.Length);
+ writer.Write(pair.Value.bytes);
+ }
+ }
+
+ public static ZipFile Read (System.IO.Stream stream) {
+ ZipFile file = new ZipFile();
+
+ var reader = new System.IO.BinaryReader(stream);
+ int count = reader.ReadInt32();
+
+ for (int i = 0; i < count; i++) {
+ var name = reader.ReadString();
+ var length = reader.ReadInt32();
+ var bytes = reader.ReadBytes(length);
+
+ file.dict[name] = new ZipEntry(name, bytes);
+ }
+
+ return file;
+ }
+
+ public ZipEntry this[string index] {
+ get {
+ ZipEntry v;
+ dict.TryGetValue(index, out v);
+ return v;
+ }
+ }
+
+ public void Dispose () {
+ }
+ }
+
+ public class ZipEntry {
+ internal string name;
+ internal byte[] bytes;
+
+ public ZipEntry (string name, byte[] bytes) {
+ this.name = name;
+ this.bytes = bytes;
+ }
+
+ public void Extract (System.IO.Stream stream) {
+ stream.Write(bytes, 0, bytes.Length);
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs.meta
new file mode 100644
index 0000000..55b4b94
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/SimpleZipReplacement.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e4ab3c0aea7564a9c9af3bf72ed95858
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs
new file mode 100644
index 0000000..b8fcfcb
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs
@@ -0,0 +1,527 @@
+using UnityEngine;
+using System.Collections.Generic;
+using Pathfinding.Util;
+using Pathfinding.WindowsStore;
+using System;
+using System.Linq;
+#if NETFX_CORE
+using WinRTLegacy;
+#endif
+
+namespace Pathfinding.Serialization {
+ public class JsonMemberAttribute : System.Attribute {
+ }
+ public class JsonOptInAttribute : System.Attribute {
+ }
+ /// <summary>Indicates that the full type of the instance will always be serialized. This allows inheritance to work properly.</summary>
+ public class JsonDynamicTypeAttribute : System.Attribute {
+ }
+
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+ public class JsonDynamicTypeAliasAttribute : System.Attribute {
+ public string alias;
+ public Type type;
+
+ public JsonDynamicTypeAliasAttribute (string alias, Type type) {
+ this.alias = alias;
+ this.type = type;
+ }
+ }
+
+ // Make sure the class is not stripped out when using code stripping (see https://docs.unity3d.com/Manual/ManagedCodeStripping.html)
+ [Pathfinding.Util.Preserve]
+ class SerializableAnimationCurve {
+ public WrapMode preWrapMode, postWrapMode;
+ public Keyframe[] keys;
+ }
+
+ /// <summary>
+ /// A very tiny json serializer.
+ /// It is not supposed to have lots of features, it is only intended to be able to serialize graph settings
+ /// well enough.
+ /// </summary>
+ public class TinyJsonSerializer {
+ System.Text.StringBuilder output = new System.Text.StringBuilder();
+
+ Dictionary<Type, Action<System.Object> > serializers = new Dictionary<Type, Action<object> >();
+
+ static readonly System.Globalization.CultureInfo invariantCulture = System.Globalization.CultureInfo.InvariantCulture;
+
+ public static void Serialize (System.Object obj, System.Text.StringBuilder output) {
+ new TinyJsonSerializer() {
+ output = output
+ }.Serialize(obj);
+ }
+
+ TinyJsonSerializer () {
+ serializers[typeof(float)] = v => output.Append(((float)v).ToString("R", invariantCulture));
+ serializers[typeof(bool)] = v => output.Append((bool)v ? "true" : "false");
+ serializers[typeof(Version)] = serializers[typeof(uint)] = serializers[typeof(int)] = v => output.Append(v.ToString());
+ serializers[typeof(string)] = v => output.AppendFormat("\"{0}\"", v.ToString().Replace("\"", "\\\""));
+ serializers[typeof(Vector2)] = v => output.AppendFormat("{{ \"x\": {0}, \"y\": {1} }}", ((Vector2)v).x.ToString("R", invariantCulture), ((Vector2)v).y.ToString("R", invariantCulture));
+ serializers[typeof(Vector3)] = v => output.AppendFormat("{{ \"x\": {0}, \"y\": {1}, \"z\": {2} }}", ((Vector3)v).x.ToString("R", invariantCulture), ((Vector3)v).y.ToString("R", invariantCulture), ((Vector3)v).z.ToString("R", invariantCulture));
+ serializers[typeof(Pathfinding.Util.Guid)] = v => output.AppendFormat("{{ \"value\": \"{0}\" }}", v.ToString());
+ serializers[typeof(LayerMask)] = v => output.AppendFormat("{{ \"value\": {0} }}", ((int)(LayerMask)v).ToString());
+ }
+
+ void Serialize (System.Object obj, bool serializePrivateFieldsByDefault = false) {
+ if (obj == null) {
+ output.Append("null");
+ return;
+ }
+
+ var type = obj.GetType();
+ var typeInfo = WindowsStoreCompatibility.GetTypeInfo(type);
+ if (serializers.ContainsKey(type)) {
+ serializers[type] (obj);
+ } else if (typeInfo.IsEnum) {
+ output.Append('"' + obj.ToString() + '"');
+ } else if (obj is System.Collections.IList) {
+ output.Append("[");
+ var arr = obj as System.Collections.IList;
+ for (int i = 0; i < arr.Count; i++) {
+ if (i != 0)
+ output.Append(", ");
+ Serialize(arr[i], serializePrivateFieldsByDefault);
+ }
+ output.Append("]");
+ } else if (obj is AnimationCurve) {
+ var curve = obj as AnimationCurve;
+ Serialize(new SerializableAnimationCurve { preWrapMode = curve.preWrapMode, postWrapMode = curve.postWrapMode, keys = curve.keys }, true);
+ } else if (obj is UnityEngine.Object) {
+ SerializeUnityObject(obj as UnityEngine.Object);
+ } else {
+#if NETFX_CORE
+ var optIn = typeInfo.CustomAttributes.Any(attr => attr.GetType() == typeof(JsonOptInAttribute));
+#else
+ var optIn = typeInfo.GetCustomAttributes(typeof(JsonOptInAttribute), true).Length > 0;
+#endif
+ output.Append("{");
+ bool earlier = false;
+
+ if (typeInfo.GetCustomAttributes(typeof(JsonDynamicTypeAttribute), true).Length > 0) {
+ output.AppendFormat("\"@type\": \"{0}\"", typeInfo.AssemblyQualifiedName);
+ earlier = true;
+ }
+
+ while (true) {
+#if NETFX_CORE
+ var fields = typeInfo.DeclaredFields.Where(f => !f.IsStatic).ToArray();
+#else
+ var fields = type.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
+#endif
+ foreach (var field in fields) {
+ if (field.DeclaringType != type) continue;
+ if ((!optIn && (field.IsPublic || serializePrivateFieldsByDefault)) ||
+#if NETFX_CORE
+ field.CustomAttributes.Any(attr => attr.GetType() == typeof(JsonMemberAttribute))
+#else
+ field.GetCustomAttributes(typeof(JsonMemberAttribute), true).Length > 0
+#endif
+ ) {
+ if (earlier) {
+ output.Append(", ");
+ }
+
+ earlier = true;
+ output.AppendFormat("\"{0}\": ", field.Name);
+ Serialize(field.GetValue(obj), serializePrivateFieldsByDefault);
+ }
+ }
+
+#if NETFX_CORE
+ typeInfo = typeInfo.BaseType;
+ if (typeInfo == null) break;
+#else
+ type = type.BaseType;
+ if (type == null) break;
+#endif
+ }
+ output.Append("}");
+ }
+ }
+
+ void QuotedField (string name, string contents) {
+ output.AppendFormat("\"{0}\": \"{1}\"", name, contents);
+ }
+
+ void SerializeUnityObject (UnityEngine.Object obj) {
+ // Note that a unityengine can be destroyed as well
+ if (obj == null) {
+ Serialize(null);
+ return;
+ }
+
+ output.Append("{");
+ var path = obj.name;
+#if UNITY_EDITOR
+ // Figure out the path of the object relative to a Resources folder.
+ // In a standalone player this cannot be done unfortunately, so we will assume it is at the top level in the Resources folder.
+ // Fortunately it should be extremely rare to have to serialize references to unity objects in a standalone player.
+ var realPath = UnityEditor.AssetDatabase.GetAssetPath(obj);
+ var match = System.Text.RegularExpressions.Regex.Match(realPath, @"Resources/(.*?)(\.\w+)?$");
+ if (match.Success) path = match.Groups[1].Value;
+#endif
+ QuotedField("Name", path);
+ output.Append(", ");
+ QuotedField("Type", obj.GetType().FullName);
+
+ //Write scene path if the object is a Component or GameObject
+ var component = obj as Component;
+ var go = obj as GameObject;
+
+ if (component != null || go != null) {
+ if (component != null) {
+ go = component.gameObject;
+ }
+
+ var helper = go.GetComponent<UnityReferenceHelper>();
+
+ if (helper == null) {
+ Debug.Log("Adding UnityReferenceHelper to Unity Reference '"+obj.name+"'");
+ helper = go.AddComponent<UnityReferenceHelper>();
+ }
+
+ //Make sure it has a unique GUID
+ helper.Reset();
+ output.Append(", ");
+ QuotedField("GUID", helper.GetGUID().ToString());
+ }
+ output.Append("}");
+ }
+ }
+
+ /// <summary>
+ /// A very tiny json deserializer.
+ /// It is not supposed to have lots of features, it is only intended to be able to deserialize graph settings
+ /// well enough. Not much validation of the input is done.
+ /// </summary>
+ public class TinyJsonDeserializer {
+ System.IO.TextReader reader;
+ string fullTextDebug;
+ GameObject contextRoot;
+
+ static readonly System.Globalization.NumberFormatInfo numberFormat = System.Globalization.NumberFormatInfo.InvariantInfo;
+
+ /// <summary>
+ /// Deserializes an object of the specified type.
+ /// Will load all fields into the populate object if it is set (only works for classes).
+ /// </summary>
+ public static System.Object Deserialize (string text, Type type, System.Object populate = null, GameObject contextRoot = null) {
+ return new TinyJsonDeserializer() {
+ reader = new System.IO.StringReader(text),
+ fullTextDebug = text,
+ contextRoot = contextRoot,
+ }.Deserialize(type, populate);
+ }
+
+ /// <summary>
+ /// Deserializes an object of type tp.
+ /// Will load all fields into the populate object if it is set (only works for classes).
+ /// </summary>
+ System.Object Deserialize (Type tp, System.Object populate = null) {
+ var tpInfo = WindowsStoreCompatibility.GetTypeInfo(tp);
+
+ if (tpInfo.IsEnum) {
+ return Enum.Parse(tp, EatField());
+ } else if (TryEat('n')) {
+ Eat("ull");
+ TryEat(',');
+ return null;
+ } else if (Type.Equals(tp, typeof(float))) {
+ return float.Parse(EatField(), numberFormat);
+ } else if (Type.Equals(tp, typeof(int))) {
+ return int.Parse(EatField(), numberFormat);
+ } else if (Type.Equals(tp, typeof(uint))) {
+ return uint.Parse(EatField(), numberFormat);
+ } else if (Type.Equals(tp, typeof(bool))) {
+ return bool.Parse(EatField());
+ } else if (Type.Equals(tp, typeof(string))) {
+ return EatField();
+ } else if (Type.Equals(tp, typeof(Version))) {
+ return new Version(EatField());
+ } else if (Type.Equals(tp, typeof(Vector2))) {
+ Eat("{");
+ var result = new Vector2();
+ EatField();
+ result.x = float.Parse(EatField(), numberFormat);
+ EatField();
+ result.y = float.Parse(EatField(), numberFormat);
+ Eat("}");
+ return result;
+ } else if (Type.Equals(tp, typeof(Vector3))) {
+ Eat("{");
+ var result = new Vector3();
+ EatField();
+ result.x = float.Parse(EatField(), numberFormat);
+ EatField();
+ result.y = float.Parse(EatField(), numberFormat);
+ EatField();
+ result.z = float.Parse(EatField(), numberFormat);
+ Eat("}");
+ return result;
+ } else if (Type.Equals(tp, typeof(Pathfinding.Util.Guid))) {
+ Eat("{");
+ EatField();
+ var result = Pathfinding.Util.Guid.Parse(EatField());
+ Eat("}");
+ return result;
+ } else if (Type.Equals(tp, typeof(LayerMask))) {
+ Eat("{");
+ EatField();
+ var result = (LayerMask)int.Parse(EatField());
+ Eat("}");
+ return result;
+ } else if (tp.IsGenericType && Type.Equals(tp.GetGenericTypeDefinition(), typeof(List<>))) {
+ System.Collections.IList result = (System.Collections.IList)System.Activator.CreateInstance(tp);
+ var elementType = tp.GetGenericArguments()[0];
+
+ Eat("[");
+ while (!TryEat(']')) {
+ result.Add(Deserialize(elementType));
+ TryEat(',');
+ }
+ return result;
+ } else if (tpInfo.IsArray) {
+ List<System.Object> ls = new List<System.Object>();
+ Eat("[");
+ while (!TryEat(']')) {
+ ls.Add(Deserialize(tp.GetElementType()));
+ TryEat(',');
+ }
+ var arr = Array.CreateInstance(tp.GetElementType(), ls.Count);
+ ls.ToArray().CopyTo(arr, 0);
+ return arr;
+ } else if (typeof(UnityEngine.Object).IsAssignableFrom(tp)) {
+ return DeserializeUnityObject();
+ } else {
+ Eat("{");
+
+ if (tpInfo.GetCustomAttributes(typeof(JsonDynamicTypeAttribute), true).Length > 0) {
+ string name = EatField();
+ if (name != "@type") {
+ throw new System.Exception("Expected field '@type' but found '" + name + "'" + "\n\nWhen trying to deserialize: " + fullTextDebug);
+ }
+
+ string typeName = EatField();
+
+ var aliases = tpInfo.GetCustomAttributes(typeof(JsonDynamicTypeAliasAttribute), true) as JsonDynamicTypeAliasAttribute[];
+ var simpleTypeName = typeName.Split(',')[0];
+ Type newType = null;
+ foreach (var alias in aliases) {
+ if (alias.alias == simpleTypeName) newType = alias.type;
+ }
+
+ if (newType == null) newType = Type.GetType(typeName);
+ tp = newType ?? throw new System.Exception("Could not find a type with the name '" + typeName + "'" + "\n\nWhen trying to deserialize: " + fullTextDebug);
+ tpInfo = WindowsStoreCompatibility.GetTypeInfo(tp);
+ }
+
+ var obj = populate ?? Activator.CreateInstance(tp);
+ while (!TryEat('}')) {
+ var name = EatField();
+ var tmpType = tp;
+ System.Reflection.FieldInfo field = null;
+ while (field == null && tmpType != null) {
+ field = tmpType.GetField(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
+ tmpType = tmpType.BaseType;
+ }
+
+ if (field == null) {
+ // Try a property instead
+ System.Reflection.PropertyInfo prop = null;
+ tmpType = tp;
+ while (prop == null && tmpType != null) {
+ prop = tmpType.GetProperty(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
+ tmpType = tmpType.BaseType;
+ }
+
+ if (prop == null) {
+ SkipFieldData();
+ } else {
+ prop.SetValue(obj, Deserialize(prop.PropertyType));
+ }
+ } else {
+ field.SetValue(obj, Deserialize(field.FieldType));
+ }
+ TryEat(',');
+ }
+ return obj;
+ }
+ }
+
+ UnityEngine.Object DeserializeUnityObject () {
+ Eat("{");
+ var result = DeserializeUnityObjectInner();
+ Eat("}");
+ return result;
+ }
+
+ UnityEngine.Object DeserializeUnityObjectInner () {
+ // Ignore InstanceID field (compatibility only)
+ var fieldName = EatField();
+
+ if (fieldName == "InstanceID") {
+ EatField();
+ fieldName = EatField();
+ }
+
+ if (fieldName != "Name") throw new Exception("Expected 'Name' field");
+ string name = EatField();
+
+ if (name == null) return null;
+
+ if (EatField() != "Type") throw new Exception("Expected 'Type' field");
+ string typename = EatField();
+
+ // Remove assembly information
+ if (typename.IndexOf(',') != -1) {
+ typename = typename.Substring(0, typename.IndexOf(','));
+ }
+
+ // Note calling through assembly is more stable on e.g WebGL
+ var type = WindowsStoreCompatibility.GetTypeInfo(typeof(AstarPath)).Assembly.GetType(typename);
+ type = type ?? WindowsStoreCompatibility.GetTypeInfo(typeof(Transform)).Assembly.GetType(typename);
+
+ if (Type.Equals(type, null)) {
+ Debug.LogError("Could not find type '"+typename+"'. Cannot deserialize Unity reference");
+ return null;
+ }
+
+ // Check if there is another field there
+ EatWhitespace();
+ if ((char)reader.Peek() == '"') {
+ if (EatField() != "GUID") throw new Exception("Expected 'GUID' field");
+ string guid = EatField();
+
+ if (contextRoot != null) {
+ foreach (var helper in contextRoot.GetComponentsInChildren<UnityReferenceHelper>(true)) {
+ if (helper.GetGUID() == guid) {
+ if (Type.Equals(type, typeof(GameObject))) {
+ return helper.gameObject;
+ } else {
+ return helper.GetComponent(type);
+ }
+ }
+ }
+ }
+
+ foreach (var helper in UnityCompatibility.FindObjectsByTypeUnsortedWithInactive<UnityReferenceHelper>()) {
+ if (helper.GetGUID() == guid) {
+ if (Type.Equals(type, typeof(GameObject))) {
+ return helper.gameObject;
+ } else {
+ return helper.GetComponent(type);
+ }
+ }
+ }
+ }
+
+ // Note: calling LoadAll with an empty string will make it load the whole resources folder, which is probably a bad idea.
+ if (!string.IsNullOrEmpty(name)) {
+ // Try to load from resources
+ UnityEngine.Object[] objs = Resources.LoadAll(name, type);
+
+ for (int i = 0; i < objs.Length; i++) {
+ if (objs[i].name == name || objs.Length == 1) {
+ return objs[i];
+ }
+ }
+ }
+
+ return null;
+ }
+
+ void EatWhitespace () {
+ while (char.IsWhiteSpace((char)reader.Peek()))
+ reader.Read();
+ }
+
+ void Eat (string s) {
+ EatWhitespace();
+ for (int i = 0; i < s.Length; i++) {
+ var c = (char)reader.Read();
+ if (c != s[i]) {
+ throw new Exception("Expected '" + s[i] + "' found '" + c + "'\n\n..." + reader.ReadLine() + "\n\nWhen trying to deserialize: " + fullTextDebug);
+ }
+ }
+ }
+
+ System.Text.StringBuilder builder = new System.Text.StringBuilder();
+ string EatUntil (string c, bool inString) {
+ builder.Length = 0;
+ bool escape = false;
+ while (true) {
+ var readInt = reader.Peek();
+ if (!escape && (char)readInt == '"') {
+ inString = !inString;
+ }
+
+ var readChar = (char)readInt;
+ if (readInt == -1) {
+ throw new Exception("Unexpected EOF" + "\n\nWhen trying to deserialize: " + fullTextDebug);
+ } else if (!escape && readChar == '\\') {
+ escape = true;
+ reader.Read();
+ } else if (!inString && c.IndexOf(readChar) != -1) {
+ break;
+ } else {
+ builder.Append(readChar);
+ reader.Read();
+ escape = false;
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ bool TryEat (char c) {
+ EatWhitespace();
+ if ((char)reader.Peek() == c) {
+ reader.Read();
+ return true;
+ }
+ return false;
+ }
+
+ string EatField () {
+ var result = EatUntil("\",}]", TryEat('"'));
+
+ TryEat('\"');
+ TryEat(':');
+ TryEat(',');
+ return result;
+ }
+
+ void SkipFieldData () {
+ var indent = 0;
+
+ while (true) {
+ EatUntil(",{}[]", false);
+ var last = (char)reader.Peek();
+
+ switch (last) {
+ case '{':
+ case '[':
+ indent++;
+ break;
+ case '}':
+ case ']':
+ indent--;
+ if (indent < 0) return;
+ break;
+ case ',':
+ if (indent == 0) {
+ reader.Read();
+ return;
+ }
+ break;
+ default:
+ throw new System.Exception("Should not reach this part");
+ }
+
+ reader.Read();
+ }
+ }
+ }
+}
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs.meta
new file mode 100644
index 0000000..84772f6
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Serialization/TinyJson.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 761fd44fdff0b40648c9b7ce7763a06f
+timeCreated: 1463169419
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: