Advertisement
Guest User

Untitled

a guest
Jun 11th, 2017
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 28.60 KB | None | 0 0
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using Pathfinding.Util;
  6. using Pathfinding.WindowsStore;
  7.  
  8. #if ASTAR_NO_ZIP
  9. using Pathfinding.Serialization.Zip;
  10. #elif NETFX_CORE
  11. // For Universal Windows Platform
  12. using ZipEntry = System.IO.Compression.ZipArchiveEntry;
  13. using ZipFile = System.IO.Compression.ZipArchive;
  14. #else
  15. using Pathfinding.Ionic.Zip;
  16. #endif
  17.  
  18. namespace Pathfinding.Serialization {
  19.     /** Holds information passed to custom graph serializers */
  20.     public class GraphSerializationContext {
  21.         private readonly GraphNode[] id2NodeMapping;
  22.  
  23.         /** Deserialization stream.
  24.          * Will only be set when deserializing
  25.          */
  26.         public readonly BinaryReader reader;
  27.  
  28.         /** Serialization stream.
  29.          * Will only be set when serializing
  30.          */
  31.         public readonly BinaryWriter writer;
  32.  
  33.         /** Index of the graph which is currently being processed.
  34.          * \version uint instead of int after 3.7.5
  35.          */
  36.         public readonly uint graphIndex;
  37.  
  38.         /** Metadata about graphs being deserialized */
  39.         public readonly GraphMeta meta;
  40.  
  41.         public GraphSerializationContext (BinaryReader reader, GraphNode[] id2NodeMapping, uint graphIndex, GraphMeta meta) {
  42.             this.reader = reader;
  43.             this.id2NodeMapping = id2NodeMapping;
  44.             this.graphIndex = graphIndex;
  45.             this.meta = meta;
  46.         }
  47.  
  48.         public GraphSerializationContext (BinaryWriter writer) {
  49.             this.writer = writer;
  50.         }
  51.  
  52.         public void SerializeNodeReference (GraphNode node) {
  53.             writer.Write(node == null ? -1 : node.NodeIndex);
  54.         }
  55.  
  56.         public GraphNode DeserializeNodeReference () {
  57.             var id = reader.ReadInt32();
  58.  
  59.             if (id2NodeMapping == null) throw new Exception("Calling DeserializeNodeReference when serializing");
  60.  
  61.             if (id == -1) return null;
  62.             GraphNode node = id2NodeMapping[id];
  63.             if (node == null) throw new Exception("Invalid id ("+id+")");
  64.             return node;
  65.         }
  66.  
  67.         /** Write a Vector3 */
  68.         public void SerializeVector3 (Vector3 v) {
  69.             writer.Write(v.x);
  70.             writer.Write(v.y);
  71.             writer.Write(v.z);
  72.         }
  73.  
  74.         /** Read a Vector3 */
  75.         public Vector3 DeserializeVector3 () {
  76.             return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  77.         }
  78.  
  79.         /** Write an Int3 */
  80.         public void SerializeInt3 (Int3 v) {
  81.             writer.Write(v.x);
  82.             writer.Write(v.y);
  83.             writer.Write(v.z);
  84.         }
  85.  
  86.         /** Read an Int3 */
  87.         public Int3 DeserializeInt3 () {
  88.             return new Int3(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32());
  89.         }
  90.  
  91.         public int DeserializeInt (int defaultValue) {
  92.             if (reader.BaseStream.Position <= reader.BaseStream.Length-4) {
  93.                 return reader.ReadInt32();
  94.             } else {
  95.                 return defaultValue;
  96.             }
  97.         }
  98.  
  99.         public float DeserializeFloat (float defaultValue) {
  100.             if (reader.BaseStream.Position <= reader.BaseStream.Length-4) {
  101.                 return reader.ReadSingle();
  102.             } else {
  103.                 return defaultValue;
  104.             }
  105.         }
  106.  
  107.         /** Read a UnityEngine.Object */
  108.         public UnityEngine.Object DeserializeUnityObject ( ) {
  109.             int inst = reader.ReadInt32();
  110.  
  111.             if (inst == int.MaxValue) {
  112.                 return null;
  113.             }
  114.  
  115.             string name = reader.ReadString();
  116.             string typename = reader.ReadString();
  117.             string guid = reader.ReadString();
  118.  
  119.             System.Type type = System.Type.GetType(typename);
  120.  
  121.             if (type == null) {
  122.                 Debug.LogError("Could not find type '"+typename+"'. Cannot deserialize Unity reference");
  123.                 return null;
  124.             }
  125.  
  126.             if (!string.IsNullOrEmpty(guid)) {
  127.                 UnityReferenceHelper[] helpers = UnityEngine.Object.FindObjectsOfType(typeof(UnityReferenceHelper)) as UnityReferenceHelper[];
  128.  
  129.                 for (int i = 0; i < helpers.Length; i++) {
  130.                     if (helpers[i].GetGUID() == guid) {
  131.                         if (type == typeof(GameObject)) {
  132.                             return helpers[i].gameObject;
  133.                         } else {
  134.                             return helpers[i].GetComponent(type);
  135.                         }
  136.                     }
  137.                 }
  138.             }
  139.  
  140.             //Try to load from resources
  141.             UnityEngine.Object[] objs = Resources.LoadAll(name, type);
  142.  
  143.             for (int i = 0; i < objs.Length; i++) {
  144.                 if (objs[i].name == name || objs.Length == 1) {
  145.                     return objs[i];
  146.                 }
  147.             }
  148.  
  149.             return null;
  150.         }
  151.     }
  152.  
  153.     /** Handles low level serialization and deserialization of graph settings and data.
  154.      * Mostly for internal use. You can use the methods in the AstarData class for
  155.      * higher level serialization and deserialization.
  156.      *
  157.      * \see AstarData
  158.      */
  159.     public class AstarSerializer {
  160.         private AstarData data;
  161.  
  162.         /** Zip which the data is loaded from */
  163.         private ZipFile zip;
  164.  
  165.         /** Memory stream with the zip data */
  166.         private MemoryStream zipStream;
  167.  
  168.         /** Graph metadata */
  169.         private GraphMeta meta;
  170.  
  171.         /** Settings for serialization */
  172.         private SerializeSettings settings;
  173.  
  174.         /** Graphs that are being serialized or deserialized */
  175.         private NavGraph[] graphs;
  176.  
  177.         /** Index used for the graph in the file.
  178.          * If some graphs were null in the file then graphIndexInZip[graphs[i]] may not equal i.
  179.          * Used for deserialization.
  180.          */
  181.         private Dictionary<NavGraph, int> graphIndexInZip;
  182.  
  183.         private int graphIndexOffset;
  184.  
  185.         /** Extension to use for binary files */
  186.         const string binaryExt = ".binary";
  187.  
  188.         /** Extension to use for json files */
  189.         const string jsonExt = ".json";
  190.  
  191.         /** Checksum for the serialized data.
  192.          * Used to provide a quick equality check in editor code
  193.          */
  194.         private uint checksum = 0xffffffff;
  195.  
  196.         System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
  197.  
  198.         /** Cached StringBuilder to avoid excessive allocations */
  199.         static System.Text.StringBuilder _stringBuilder = new System.Text.StringBuilder();
  200.  
  201.         /** Returns a cached StringBuilder.
  202.          * This function only has one string builder cached and should
  203.          * thus only be called from a single thread and should not be called while using an earlier got string builder.
  204.          */
  205.         static System.Text.StringBuilder GetStringBuilder () { _stringBuilder.Length = 0; return _stringBuilder; }
  206.  
  207.         public AstarSerializer (AstarData data) {
  208.             this.data = data;
  209.             settings = SerializeSettings.Settings;
  210.         }
  211.  
  212.         public AstarSerializer (AstarData data, SerializeSettings settings) {
  213.             this.data = data;
  214.             this.settings = settings;
  215.         }
  216.  
  217.         public void SetGraphIndexOffset (int offset) {
  218.             graphIndexOffset = offset;
  219.         }
  220.  
  221.         void AddChecksum (byte[] bytes) {
  222.             checksum = Checksum.GetChecksum(bytes, checksum);
  223.         }
  224.  
  225.         void AddEntry (string name, byte[] bytes) {
  226. #if NETFX_CORE
  227.             var entry = zip.CreateEntry(name);
  228.             using (var stream = entry.Open()) {
  229.                 stream.Write(bytes, 0, bytes.Length);
  230.             }
  231. #else
  232.             zip.AddEntry(name, bytes);
  233. #endif
  234.         }
  235.  
  236.         public uint GetChecksum () { return checksum; }
  237.  
  238.         #region Serialize
  239.  
  240.         public void OpenSerialize () {
  241.             // Create a new zip file, here we will store all the data
  242.             zipStream = new MemoryStream();
  243. #if NETFX_CORE
  244.             zip = new ZipFile(zipStream, System.IO.Compression.ZipArchiveMode.Create);
  245. #else
  246.             zip = new ZipFile();
  247.             zip.AlternateEncoding = System.Text.Encoding.UTF8;
  248.             zip.AlternateEncodingUsage = ZipOption.Always;
  249. #endif
  250.             meta = new GraphMeta();
  251.         }
  252.  
  253.         public byte[] CloseSerialize () {
  254.             // As the last step, serialize metadata
  255.             byte[] bytes = SerializeMeta();
  256.             AddChecksum(bytes);
  257.             AddEntry("meta"+jsonExt, bytes);
  258.  
  259. #if !ASTAR_NO_ZIP && !NETFX_CORE
  260.             // Set dummy dates on every file to prevent the binary data to change
  261.             // for identical settings and graphs.
  262.             // Prevents the scene from being marked as dirty in the editor
  263.             // If ASTAR_NO_ZIP is defined this is not relevant since the replacement zip
  264.             // implementation does not even store dates
  265.             var dummy = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  266.             foreach (var entry in zip.Entries) {
  267.                 entry.AccessedTime = dummy;
  268.                 entry.CreationTime = dummy;
  269.                 entry.LastModified = dummy;
  270.                 entry.ModifiedTime = dummy;
  271.             }
  272. #endif
  273.  
  274.             // Save all entries to a single byte array
  275. #if !NETFX_CORE
  276.             zip.Save(zipStream);
  277. #endif
  278.             zip.Dispose();
  279.             bytes = zipStream.ToArray();
  280.  
  281.             zip = null;
  282.             zipStream = null;
  283.             return bytes;
  284.         }
  285.  
  286.         public void SerializeGraphs (NavGraph[] _graphs) {
  287.             if (graphs != null) throw new InvalidOperationException("Cannot serialize graphs multiple times.");
  288.             graphs = _graphs;
  289.  
  290.             if (zip == null) throw new NullReferenceException("You must not call CloseSerialize before a call to this function");
  291.  
  292.             if (graphs == null) graphs = new NavGraph[0];
  293.  
  294.             for (int i = 0; i < graphs.Length; i++) {
  295.                 //Ignore graph if null
  296.                 if (graphs[i] == null) continue;
  297.  
  298.                 // Serialize the graph to a byte array
  299.                 byte[] bytes = Serialize(graphs[i]);
  300.  
  301.                 AddChecksum(bytes);
  302.                 AddEntry("graph"+i+jsonExt, bytes);
  303.             }
  304.         }
  305.  
  306.         /** Serialize metadata about all graphs */
  307.         byte[] SerializeMeta () {
  308.             if (graphs == null) throw new System.Exception("No call to SerializeGraphs has been done");
  309.  
  310.             meta.version = AstarPath.Version;
  311.             meta.graphs = graphs.Length;
  312.             meta.guids = new List<string>();
  313.             meta.typeNames = new List<string>();
  314.  
  315.             // For each graph, save the guid
  316.             // of the graph and the type of it
  317.             for (int i = 0; i < graphs.Length; i++) {
  318.                 if (graphs[i] != null) {
  319.                     meta.guids.Add(graphs[i].guid.ToString());
  320.                     meta.typeNames.Add(graphs[i].GetType().FullName);
  321.                 } else {
  322.                     meta.guids.Add(null);
  323.                     meta.typeNames.Add(null);
  324.                 }
  325.             }
  326.  
  327.             // Grab a cached string builder to avoid allocations
  328.             var output = GetStringBuilder();
  329.             TinyJsonSerializer.Serialize(meta, output);
  330.             return encoding.GetBytes(output.ToString());
  331.         }
  332.  
  333.         /** Serializes the graph settings to JSON and returns the data */
  334.         public byte[] Serialize (NavGraph graph) {
  335.             // Grab a cached string builder to avoid allocations
  336.             var output = GetStringBuilder();
  337.  
  338.             TinyJsonSerializer.Serialize(graph, output);
  339.             return encoding.GetBytes(output.ToString());
  340.         }
  341.  
  342.         /** Deprecated method to serialize node data.
  343.          * \deprecated Not used anymore
  344.          */
  345.         [System.Obsolete("Not used anymore. You can safely remove the call to this function.")]
  346.         public void SerializeNodes () {
  347.         }
  348.  
  349.         static int GetMaxNodeIndexInAllGraphs (NavGraph[] graphs) {
  350.             int maxIndex = 0;
  351.  
  352.             for (int i = 0; i < graphs.Length; i++) {
  353.                 if (graphs[i] == null) continue;
  354.                 graphs[i].GetNodes(node => {
  355.                     maxIndex = Math.Max(node.NodeIndex, maxIndex);
  356.                     if (node.NodeIndex == -1) {
  357.                         Debug.LogError("Graph contains destroyed nodes. This is a bug.");
  358.                     }
  359.                 });
  360.             }
  361.             return maxIndex;
  362.         }
  363.  
  364.         static byte[] SerializeNodeIndices (NavGraph[] graphs) {
  365.             var stream = new MemoryStream();
  366.             var writer = new BinaryWriter(stream);
  367.  
  368.             int maxNodeIndex = GetMaxNodeIndexInAllGraphs(graphs);
  369.  
  370.             writer.Write(maxNodeIndex);
  371.  
  372.             // While writing node indices, verify that the max node index is the same
  373.             // (user written graphs might have gotten it wrong)
  374.             int maxNodeIndex2 = 0;
  375.             for (int i = 0; i < graphs.Length; i++) {
  376.                 if (graphs[i] == null) continue;
  377.                 graphs[i].GetNodes(node => {
  378.                     maxNodeIndex2 = Math.Max(node.NodeIndex, maxNodeIndex2);
  379.                     writer.Write(node.NodeIndex);
  380.                 });
  381.             }
  382.  
  383.             // Nice to verify if users are writing their own graph types
  384.             if (maxNodeIndex2 != maxNodeIndex) throw new Exception("Some graphs are not consistent in their GetNodes calls, sequential calls give different results.");
  385.  
  386.             byte[] bytes = stream.ToArray();
  387.             writer.Close();
  388.  
  389.             return bytes;
  390.         }
  391.  
  392.         /** Serializes info returned by NavGraph.SerializeExtraInfo */
  393.         static byte[] SerializeGraphExtraInfo (NavGraph graph) {
  394.             var stream = new MemoryStream();
  395.             var writer = new BinaryWriter(stream);
  396.             var ctx = new GraphSerializationContext(writer);
  397.  
  398.             graph.SerializeExtraInfo(ctx);
  399.             byte[] bytes = stream.ToArray();
  400.             writer.Close();
  401.  
  402.             return bytes;
  403.         }
  404.  
  405.         /** Used to serialize references to other nodes e.g connections.
  406.          * Nodes use the GraphSerializationContext.GetNodeIdentifier and
  407.          * GraphSerializationContext.GetNodeFromIdentifier methods
  408.          * for serialization and deserialization respectively.
  409.          */
  410.         static byte[] SerializeGraphNodeReferences (NavGraph graph) {
  411.             var stream = new MemoryStream();
  412.             var writer = new BinaryWriter(stream);
  413.             var ctx = new GraphSerializationContext(writer);
  414.  
  415.             graph.GetNodes(node => node.SerializeReferences(ctx));
  416.             writer.Close();
  417.  
  418.             var bytes = stream.ToArray();
  419.             return bytes;
  420.         }
  421.  
  422.         public void SerializeExtraInfo () {
  423.             if (!settings.nodes) return;
  424.             if (graphs == null) throw new InvalidOperationException("Cannot serialize extra info with no serialized graphs (call SerializeGraphs first)");
  425.  
  426.             var bytes = SerializeNodeIndices(graphs);
  427.             AddChecksum(bytes);
  428.             AddEntry("graph_references"+binaryExt, bytes);
  429.  
  430.             for (int i = 0; i < graphs.Length; i++) {
  431.                 if (graphs[i] == null) continue;
  432.  
  433.                 bytes = SerializeGraphExtraInfo(graphs[i]);
  434.                 AddChecksum(bytes);
  435.                 AddEntry("graph"+i+"_extra"+binaryExt, bytes);
  436.  
  437.                 bytes = SerializeGraphNodeReferences(graphs[i]);
  438.                 AddChecksum(bytes);
  439.                 AddEntry("graph"+i+"_references"+binaryExt, bytes);
  440.             }
  441.  
  442.             bytes = SerializeNodeLinks();
  443.             AddChecksum(bytes);
  444.             AddEntry("node_link2" + binaryExt, bytes);
  445.         }
  446.  
  447.         byte[] SerializeNodeLinks () {
  448.             var stream = new MemoryStream();
  449.             var writer = new BinaryWriter(stream);
  450.             var ctx = new GraphSerializationContext(writer);
  451.  
  452.             NodeLink2.SerializeReferences(ctx);
  453.             return stream.ToArray();
  454.         }
  455.  
  456.         public void SerializeEditorSettings (GraphEditorBase[] editors) {
  457.             if (editors == null || !settings.editorSettings) return;
  458.  
  459.             for (int i = 0; i < editors.Length; i++) {
  460.                 if (editors[i] == null) return;
  461.  
  462.                 var output = GetStringBuilder();
  463.                 TinyJsonSerializer.Serialize(editors[i], output);
  464.                 var bytes = encoding.GetBytes(output.ToString());
  465.  
  466.                 //Less or equal to 2 bytes means that nothing was saved (file is "{}")
  467.                 if (bytes.Length <= 2)
  468.                     continue;
  469.  
  470.                 AddChecksum(bytes);
  471.                 AddEntry("graph"+i+"_editor"+jsonExt, bytes);
  472.             }
  473.         }
  474.  
  475.         #endregion
  476.  
  477.         #region Deserialize
  478.  
  479.         ZipEntry GetEntry (string name) {
  480. #if NETFX_CORE
  481.             return zip.GetEntry(name);
  482. #else
  483.             return zip[name];
  484. #endif
  485.         }
  486.  
  487.         bool ContainsEntry (string name) {
  488.             return GetEntry(name) != null;
  489.         }
  490.  
  491.         public bool OpenDeserialize (byte[] bytes) {
  492.             // Copy the bytes to a stream
  493.             zipStream = new MemoryStream();
  494.             zipStream.Write(bytes, 0, bytes.Length);
  495.             zipStream.Position = 0;
  496.             try {
  497. #if NETFX_CORE
  498.                 zip = new ZipFile(zipStream);
  499. #else
  500.                 zip = ZipFile.Read(zipStream);
  501. #endif
  502.             } catch (Exception e) {
  503.                 // Catches exceptions when an invalid zip file is found
  504.                 Debug.LogError("Caught exception when loading from zip\n"+e);
  505.  
  506.                 zipStream.Dispose();
  507.                 return false;
  508.             }
  509.  
  510.             if (ContainsEntry("meta" + jsonExt)) {
  511.                 meta = DeserializeMeta(GetEntry("meta" + jsonExt));
  512.             } else if (ContainsEntry("meta" + binaryExt)) {
  513.                 meta = DeserializeBinaryMeta(GetEntry("meta" + binaryExt));
  514.             } else {
  515.                 throw new Exception("No metadata found in serialized data.");
  516.             }
  517.  
  518.             if (FullyDefinedVersion(meta.version) > FullyDefinedVersion(AstarPath.Version)) {
  519.                 Debug.LogWarning("Trying to load data from a newer version of the A* Pathfinding Project\nCurrent version: "+AstarPath.Version+" Data version: "+meta.version +
  520.                     "\nThis is usually fine as the stored data is usually backwards and forwards compatible." +
  521.                     "\nHowever node data (not settings) can get corrupted between versions (even though I try my best to keep compatibility), so it is recommended " +
  522.                     "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");
  523.             } else if (FullyDefinedVersion(meta.version) < FullyDefinedVersion(AstarPath.Version)) {
  524.                 Debug.LogWarning("Upgrading serialized pathfinding data from version " + meta.version + " to " + AstarPath.Version +
  525.                     "\nThis is usually fine, it just means you have upgraded to a new version." +
  526.                     "\nHowever node data (not settings) can get corrupted between versions (even though I try my best to keep compatibility), so it is recommended " +
  527.                     "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");
  528.             }
  529.             return true;
  530.         }
  531.  
  532.         /** Returns a version with all fields fully defined.
  533.          * This is used because by default new Version(3,0,0) > new Version(3,0).
  534.          * This is not the desired behaviour so we make sure that all fields are defined here
  535.          */
  536.         static System.Version FullyDefinedVersion (System.Version v) {
  537.             return new System.Version(Mathf.Max(v.Major, 0), Mathf.Max(v.Minor, 0), Mathf.Max(v.Build, 0), Mathf.Max(v.Revision, 0));
  538.         }
  539.  
  540.         public void CloseDeserialize () {
  541.             zipStream.Dispose();
  542.             zip.Dispose();
  543.             zip = null;
  544.             zipStream = null;
  545.         }
  546.  
  547.         NavGraph DeserializeGraph (int zipIndex, int graphIndex) {
  548.             // Get the graph type from the metadata we deserialized earlier
  549.             var graphType = meta.GetGraphType(zipIndex);
  550.  
  551.             // Graph was null when saving, ignore
  552.             if (System.Type.Equals(graphType, null)) return null;
  553.  
  554.             // Create a new graph of the right type
  555.             NavGraph graph = data.CreateGraph(graphType);
  556.             graph.graphIndex = (uint)(graphIndex);
  557.  
  558.             var jsonName = "graph" + zipIndex + jsonExt;
  559.             var binName = "graph" + zipIndex + binaryExt;
  560.  
  561.             if (ContainsEntry(jsonName)) {
  562.                 // Read the graph settings
  563.                 TinyJsonDeserializer.Deserialize(GetString(GetEntry(jsonName)), graphType, graph);
  564.             } else if (ContainsEntry(binName)) {
  565.                 var reader = GetBinaryReader(GetEntry(binName));
  566.                 var ctx = new GraphSerializationContext(reader, null, graph.graphIndex, meta);
  567.                 graph.DeserializeSettingsCompatibility(ctx);
  568.             } else {
  569.                 throw new FileNotFoundException("Could not find data for graph " + zipIndex + " in zip. Entry 'graph" + zipIndex + jsonExt + "' does not exist");
  570.             }
  571.  
  572.             if (graph.guid.ToString() != meta.guids[zipIndex])
  573.                 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]);
  574.  
  575.             return graph;
  576.         }
  577.  
  578.         /** Deserializes graph settings.
  579.          * \note Stored in files named "graph#.json" where # is the graph number.
  580.          */
  581.         public NavGraph[] DeserializeGraphs () {
  582.             // Allocate a list of graphs to be deserialized
  583.             var graphList = new List<NavGraph>();
  584.  
  585.             graphIndexInZip = new Dictionary<NavGraph, int>();
  586.  
  587.             for (int i = 0; i < meta.graphs; i++) {
  588.                 var newIndex = graphList.Count + graphIndexOffset;
  589.                 var graph = DeserializeGraph(i, newIndex);
  590.                 if (graph != null) {
  591.                     graphList.Add(graph);
  592.                     graphIndexInZip[graph] = i;
  593.                 }
  594.             }
  595.  
  596.             graphs = graphList.ToArray();
  597.             return graphs;
  598.         }
  599.  
  600.         bool DeserializeExtraInfo (NavGraph graph) {
  601.             var zipIndex = graphIndexInZip[graph];
  602.             var entry = GetEntry("graph"+zipIndex+"_extra"+binaryExt);
  603.  
  604.             if (entry == null)
  605.                 return false;
  606.  
  607.             var reader = GetBinaryReader(entry);
  608.  
  609.             var ctx = new GraphSerializationContext(reader, null, graph.graphIndex, meta);
  610.  
  611.             // Call the graph to process the data
  612.             graph.DeserializeExtraInfo(ctx);
  613.             return true;
  614.         }
  615.  
  616.         bool AnyDestroyedNodesInGraphs () {
  617.             bool result = false;
  618.  
  619.             for (int i = 0; i < graphs.Length; i++) {
  620.                 graphs[i].GetNodes(node => {
  621.                     if (node.Destroyed) {
  622.                         result = true;
  623.                     }
  624.                 });
  625.             }
  626.             return result;
  627.         }
  628.  
  629.         GraphNode[] DeserializeNodeReferenceMap () {
  630.             // Get the file containing the list of all node indices
  631.             // This is correlated with the new indices of the nodes and a mapping from old to new
  632.             // is done so that references can be resolved
  633.             var entry = GetEntry("graph_references"+binaryExt);
  634.  
  635.             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?");
  636.  
  637.             var reader = GetBinaryReader(entry);
  638.             int maxNodeIndex = reader.ReadInt32();
  639.             var int2Node = new GraphNode[maxNodeIndex+1];
  640.  
  641.             try {
  642.                 for (int i = 0; i < graphs.Length; i++) {
  643.                     graphs[i].GetNodes(node => {
  644.                         var index = reader.ReadInt32();
  645.                         int2Node[index] = node;
  646.                     });
  647.                 }
  648.             } catch (Exception e) {
  649.                 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);
  650.             }
  651.  
  652. #if !NETFX_CORE
  653.             // For Windows Store apps the BaseStream.Position property is not supported
  654.             // so we have to disable this error check on that platform
  655.             if (reader.BaseStream.Position != reader.BaseStream.Length) {
  656.                 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.");
  657.             }
  658. #endif
  659.  
  660.             reader.Close();
  661.             return int2Node;
  662.         }
  663.  
  664.         void DeserializeNodeReferences (NavGraph graph, GraphNode[] int2Node) {
  665.             var zipIndex = graphIndexInZip[graph];
  666.             var entry = GetEntry("graph"+zipIndex+"_references"+binaryExt);
  667.  
  668.             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?");
  669.  
  670.             var reader = GetBinaryReader(entry);
  671.             var ctx = new GraphSerializationContext(reader, int2Node, graph.graphIndex, meta);
  672.  
  673.             graph.GetNodes(node => node.DeserializeReferences(ctx));
  674.         }
  675.  
  676.         /** Deserializes extra graph info.
  677.          * Extra graph info is specified by the graph types.
  678.          * \see Pathfinding.NavGraph.DeserializeExtraInfo
  679.          * \note Stored in files named "graph#_extra.binary" where # is the graph number.
  680.          */
  681.         public void DeserializeExtraInfo () {
  682.             bool anyDeserialized = false;
  683.  
  684.             // Loop through all graphs and deserialize the extra info
  685.             // if there is any such info in the zip file
  686.             for (int i = 0; i < graphs.Length; i++) {
  687.                 anyDeserialized |= DeserializeExtraInfo(graphs[i]);
  688.             }
  689.  
  690.             if (!anyDeserialized) {
  691.                 return;
  692.             }
  693.  
  694.             // Sanity check
  695.             // Make sure the graphs don't contain destroyed nodes
  696.             if (AnyDestroyedNodesInGraphs()) {
  697.                 Debug.LogError("Graph contains destroyed nodes. This is a bug.");
  698.             }
  699.  
  700.             // Deserialize map from old node indices to new nodes
  701.             var int2Node = DeserializeNodeReferenceMap();
  702.  
  703.             // Deserialize node references
  704.             for (int i = 0; i < graphs.Length; i++) {
  705.                 DeserializeNodeReferences(graphs[i], int2Node);
  706.             }
  707.  
  708.             DeserializeNodeLinks(int2Node);
  709.         }
  710.  
  711.         void DeserializeNodeLinks (GraphNode[] int2Node) {
  712.             var entry = GetEntry("node_link2"+binaryExt);
  713.  
  714.             if (entry == null)
  715.                 return;
  716.  
  717.             var reader = GetBinaryReader(entry);
  718.             var ctx = new GraphSerializationContext(reader, int2Node, 0, meta);
  719.             NodeLink2.DeserializeReferences(ctx);
  720.         }
  721.  
  722.         /** Calls PostDeserialization on all loaded graphs */
  723.         public void PostDeserialization () {
  724.             for (int i = 0; i < graphs.Length; i++) {
  725.                 graphs[i].PostDeserialization();
  726.             }
  727.         }
  728.  
  729.         /** Deserializes graph editor settings.
  730.          * For future compatibility this method does not assume that the \a graphEditors array matches the #graphs array in order and/or count.
  731.          * It searches for a matching graph (matching if graphEditor.target == graph) for every graph editor.
  732.          * Multiple graph editors should not refer to the same graph.\n
  733.          * \note Stored in files named "graph#_editor.json" where # is the graph number.
  734.          */
  735.         public void DeserializeEditorSettings (GraphEditorBase[] graphEditors) {
  736.             if (graphEditors == null) return;
  737.  
  738.             for (int i = 0; i < graphEditors.Length; i++) {
  739.                 if (graphEditors[i] == null) continue;
  740.                 for (int j = 0; j < graphs.Length; j++) {
  741.                     if (graphEditors[i].target != graphs[j]) continue;
  742.  
  743.                     var zipIndex = graphIndexInZip[graphs[j]];
  744.                     ZipEntry entry = GetEntry("graph"+zipIndex+"_editor"+jsonExt);
  745.                     if (entry == null) continue;
  746.  
  747.                     TinyJsonDeserializer.Deserialize(GetString(entry), graphEditors[i].GetType(), graphEditors[i]);
  748.                     break;
  749.                 }
  750.             }
  751.         }
  752.  
  753.         /** Returns a binary reader for the data in the zip entry */
  754.         private static BinaryReader GetBinaryReader (ZipEntry entry) {
  755. #if NETFX_CORE
  756.             return new BinaryReader(entry.Open());
  757. #else
  758.             var stream = new System.IO.MemoryStream();
  759.  
  760.             entry.Extract(stream);
  761.             stream.Position = 0;
  762.             return new System.IO.BinaryReader(stream);
  763. #endif
  764.         }
  765.  
  766.         /** Returns the data in the zip entry as a string */
  767.         private static string GetString (ZipEntry entry) {
  768. #if NETFX_CORE
  769.             var reader = new StreamReader(entry.Open());
  770. #else
  771.             var buffer = new MemoryStream();
  772.  
  773.             entry.Extract(buffer);
  774.             buffer.Position = 0;
  775.             var reader = new StreamReader(buffer);
  776. #endif
  777.             string s = reader.ReadToEnd();
  778.             reader.Dispose();
  779.             return s;
  780.         }
  781.  
  782.         private GraphMeta DeserializeMeta (ZipEntry entry) {
  783.             return TinyJsonDeserializer.Deserialize(GetString(entry), typeof(GraphMeta)) as GraphMeta;
  784.         }
  785.  
  786.         private GraphMeta DeserializeBinaryMeta (ZipEntry entry) {
  787.             var meta = new GraphMeta();
  788.  
  789.             var reader = GetBinaryReader(entry);
  790.  
  791.             if (reader.ReadString() != "A*") throw new System.Exception("Invalid magic number in saved data");
  792.             int major = reader.ReadInt32();
  793.             int minor = reader.ReadInt32();
  794.             int build = reader.ReadInt32();
  795.             int revision = reader.ReadInt32();
  796.  
  797.             // Required because when saving a version with a field not set, it will save it as -1
  798.             // and then the Version constructor will throw an exception (which we do not want)
  799.             if (major < 0) meta.version = new Version(0, 0);
  800.             else if (minor < 0) meta.version = new Version(major, 0);
  801.             else if (build < 0) meta.version = new Version(major, minor);
  802.             else if (revision < 0) meta.version = new Version(major, minor, build);
  803.             else meta.version = new Version(major, minor, build, revision);
  804.  
  805.             meta.graphs = reader.ReadInt32();
  806.  
  807.             meta.guids = new List<string>();
  808.             int count = reader.ReadInt32();
  809.             for (int i = 0; i < count; i++) meta.guids.Add(reader.ReadString());
  810.  
  811.             meta.typeNames = new List<string>();
  812.             count = reader.ReadInt32();
  813.             for (int i = 0; i < count; i++) meta.typeNames.Add(reader.ReadString());
  814.  
  815.             return meta;
  816.         }
  817.  
  818.  
  819.         #endregion
  820.  
  821.         #region Utils
  822.  
  823.         /** Save the specified data at the specified path */
  824.         public static void SaveToFile (string path, byte[] data) {
  825. #if NETFX_CORE
  826.             throw new System.NotSupportedException("Cannot save to file on this platform");
  827. #else
  828.             using (var stream = new FileStream(path, FileMode.Create)) {
  829.                 stream.Write(data, 0, data.Length);
  830.             }
  831. #endif
  832.         }
  833.  
  834.         /** Load the specified data from the specified path */
  835.         public static byte[] LoadFromFile (string path) {
  836. #if NETFX_CORE
  837.             throw new System.NotSupportedException("Cannot load from file on this platform");
  838. #else
  839.             using (var stream = new FileStream(path, FileMode.Open)) {
  840.                 var bytes = new byte[(int)stream.Length];
  841.                 stream.Read(bytes, 0, (int)stream.Length);
  842.                 return bytes;
  843.             }
  844. #endif
  845.         }
  846.  
  847.         #endregion
  848.     }
  849.  
  850.     /** Metadata for all graphs included in serialization */
  851.     public class GraphMeta {
  852.         /** Project version it was saved with */
  853.         public Version version;
  854.  
  855.         /** Number of graphs serialized */
  856.         public int graphs;
  857.  
  858.         /** Guids for all graphs */
  859.         public List<string> guids;
  860.  
  861.         /** Type names for all graphs */
  862.         public List<string> typeNames;
  863.  
  864.         /** Returns the Type of graph number \a i */
  865.         public Type GetGraphType (int i) {
  866.             // The graph was null when saving. Ignore it
  867.             if (String.IsNullOrEmpty(typeNames[i])) return null;
  868.  
  869. #if ASTAR_FAST_NO_EXCEPTIONS || UNITY_WEBGL
  870.             System.Type[] types = AstarData.DefaultGraphTypes;
  871.  
  872.             Type type = null;
  873.             for (int j = 0; j < types.Length; j++) {
  874.                 if (types[j].FullName == typeNames[i]) type = types[j];
  875.             }
  876. #else
  877.             // Note calling through assembly is more stable on e.g WebGL
  878.             Type type = WindowsStoreCompatibility.GetTypeInfo(typeof(AstarPath)).Assembly.GetType(typeNames[i]);
  879. #endif
  880.             if (!System.Type.Equals(type, null))
  881.                 return type;
  882.  
  883.             throw new Exception("No graph of type '" + typeNames[i] + "' could be created, type does not exist");
  884.         }
  885.     }
  886.  
  887.     /** Holds settings for how graphs should be serialized */
  888.     public class SerializeSettings {
  889.         /** Enable to include node data.
  890.          * If false, only settings will be saved
  891.          */
  892.         public bool nodes = true;
  893.  
  894.         /** Use pretty printing for the json data.
  895.          * Good if you want to open up the saved data and edit it manually
  896.          */
  897.         [System.Obsolete("There is no support for pretty printing the json anymore")]
  898.         public bool prettyPrint;
  899.  
  900.         /** Save editor settings.
  901.          * \warning Only applicable when saving from the editor using the AstarPathEditor methods
  902.          */
  903.         public bool editorSettings;
  904.  
  905.         /** Serialization settings for only saving graph settings */
  906.         public static SerializeSettings Settings {
  907.             get {
  908.                 return new SerializeSettings {
  909.                            nodes = false
  910.                 };
  911.             }
  912.         }
  913.     }
  914. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement