BS 0.1 基础构建完成 / 0.2 Visual 同为Unity UI控件部分
This commit is contained in:
8
Convention/[ES3]/Easy Save 3/Scripts/Attributes.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Attributes.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f71580da063e964fb401ef50309f6d8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class ES3Serializable : Attribute{}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class ES3NonSerializable : Attribute { }
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed09c1515c82aa7438f2dc6146085418
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Auto Save.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Auto Save.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5e7cfad151801e4587b8ba4615dbd74
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class ES3AutoSave : MonoBehaviour, ISerializationCallbackReceiver
|
||||
{
|
||||
public bool saveLayer = true;
|
||||
public bool saveTag = true;
|
||||
public bool saveName = true;
|
||||
public bool saveHideFlags = true;
|
||||
public bool saveActive = true;
|
||||
public bool saveChildren = false;
|
||||
|
||||
private bool isQuitting = false;
|
||||
|
||||
//[HideInInspector]
|
||||
public List<Component> componentsToSave = new List<Component>();
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Initialise saveLayer (etc) to false for all new Components.
|
||||
saveLayer = false;
|
||||
saveTag = false;
|
||||
saveName = false;
|
||||
saveHideFlags = false;
|
||||
saveActive = false;
|
||||
saveChildren = false;
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
if (ES3AutoSaveMgr.Current == null)
|
||||
ES3Internal.ES3Debug.LogWarning("<b>No GameObjects in this scene will be autosaved</b> because there is no Easy Save 3 Manager. To add a manager to this scene, exit playmode and go to Assets > Easy Save 3 > Add Manager to Scene.", this);
|
||||
else
|
||||
ES3AutoSaveMgr.AddAutoSave(this);
|
||||
}
|
||||
|
||||
public void OnApplicationQuit()
|
||||
{
|
||||
isQuitting = true;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
// If this is being destroyed, but not because the application is quitting,
|
||||
// remove the AutoSave from the manager.
|
||||
if (!isQuitting)
|
||||
ES3AutoSaveMgr.RemoveAutoSave(this);
|
||||
}
|
||||
public void OnBeforeSerialize() { }
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
// Remove any null Components
|
||||
componentsToSave.RemoveAll(c => c == null || c.GetType() == typeof(Component));
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 132d687c0c0cb9e489632d423b4c376d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
160
Convention/[ES3]/Easy Save 3/Scripts/Auto Save/ES3AutoSaveMgr.cs
Normal file
160
Convention/[ES3]/Easy Save 3/Scripts/Auto Save/ES3AutoSaveMgr.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Linq;
|
||||
|
||||
#if UNITY_VISUAL_SCRIPTING
|
||||
[Unity.VisualScripting.IncludeInSettings(true)]
|
||||
#elif BOLT_VISUAL_SCRIPTING
|
||||
[Ludiq.IncludeInSettings(true)]
|
||||
#endif
|
||||
public class ES3AutoSaveMgr : MonoBehaviour
|
||||
{
|
||||
public static ES3AutoSaveMgr _current = null;
|
||||
public static ES3AutoSaveMgr Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_current == null /*|| _current.gameObject.scene != SceneManager.GetActiveScene()*/)
|
||||
{
|
||||
var scene = SceneManager.GetActiveScene();
|
||||
var roots = scene.GetRootGameObjects();
|
||||
|
||||
// First, look for Easy Save 3 Manager in the top-level.
|
||||
foreach (var root in roots)
|
||||
if (root.name == "Easy Save 3 Manager")
|
||||
return _current = root.GetComponent<ES3AutoSaveMgr>();
|
||||
|
||||
// If the user has moved or renamed the Easy Save 3 Manager, we need to perform a deep search.
|
||||
foreach (var root in roots)
|
||||
if ((_current = root.GetComponentInChildren<ES3AutoSaveMgr>()) != null)
|
||||
return _current;
|
||||
}
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
// Included for backwards compatibility.
|
||||
public static ES3AutoSaveMgr Instance
|
||||
{
|
||||
get { return Current; }
|
||||
}
|
||||
|
||||
public enum LoadEvent { None, Awake, Start }
|
||||
public enum SaveEvent { None, OnApplicationQuit, OnApplicationPause }
|
||||
|
||||
public string key = System.Guid.NewGuid().ToString();
|
||||
public SaveEvent saveEvent = SaveEvent.OnApplicationQuit;
|
||||
public LoadEvent loadEvent = LoadEvent.Awake;
|
||||
public ES3SerializableSettings settings = new ES3SerializableSettings("AutoSave.es3", ES3.Location.Cache);
|
||||
|
||||
public HashSet<ES3AutoSave> autoSaves = new HashSet<ES3AutoSave>();
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (autoSaves == null || autoSaves.Count == 0)
|
||||
return;
|
||||
|
||||
// If we're using caching and we've not already cached this file, cache it.
|
||||
if (settings.location == ES3.Location.Cache && !ES3.FileExists(settings))
|
||||
ES3.CacheFile(settings);
|
||||
|
||||
if (autoSaves == null || autoSaves.Count == 0)
|
||||
{
|
||||
ES3.DeleteKey(key, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
var gameObjects = new List<GameObject>();
|
||||
foreach (var autoSave in autoSaves)
|
||||
{
|
||||
// If the ES3AutoSave component is disabled, don't save it.
|
||||
if (autoSave != null && autoSave.enabled)
|
||||
gameObjects.Add(autoSave.gameObject);
|
||||
}
|
||||
// Save in the same order as their depth in the hierarchy.
|
||||
ES3.Save<GameObject[]>(key, gameObjects.OrderBy(x => GetDepth(x.transform)).ToArray(), settings);
|
||||
}
|
||||
|
||||
if(settings.location == ES3.Location.Cache && ES3.FileExists(settings))
|
||||
ES3.StoreCachedFile(settings);
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we're using caching and we've not already cached this file, cache it.
|
||||
if (settings.location == ES3.Location.Cache && !ES3.FileExists(settings))
|
||||
ES3.CacheFile(settings);
|
||||
}
|
||||
catch { }
|
||||
|
||||
ES3.Load<GameObject[]>(key, new GameObject[0], settings);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if(loadEvent == LoadEvent.Start)
|
||||
Load();
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
GetAutoSaves();
|
||||
|
||||
if (loadEvent == LoadEvent.Awake)
|
||||
Load();
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
if(saveEvent == SaveEvent.OnApplicationQuit)
|
||||
Save();
|
||||
}
|
||||
|
||||
void OnApplicationPause(bool paused)
|
||||
{
|
||||
if( (saveEvent == SaveEvent.OnApplicationPause ||
|
||||
(Application.isMobilePlatform && saveEvent == SaveEvent.OnApplicationQuit)) && paused)
|
||||
Save();
|
||||
}
|
||||
|
||||
/* Register an ES3AutoSave with the ES3AutoSaveMgr, if there is one */
|
||||
public static void AddAutoSave(ES3AutoSave autoSave)
|
||||
{
|
||||
if(ES3AutoSaveMgr.Current != null)
|
||||
ES3AutoSaveMgr.Current.autoSaves.Add(autoSave);
|
||||
}
|
||||
|
||||
/* Remove an ES3AutoSave from the ES3AutoSaveMgr, for example if it's GameObject has been destroyed */
|
||||
public static void RemoveAutoSave(ES3AutoSave autoSave)
|
||||
{
|
||||
if(ES3AutoSaveMgr.Current != null)
|
||||
ES3AutoSaveMgr.Current.autoSaves.Remove(autoSave);
|
||||
}
|
||||
|
||||
/* Gathers all of the ES3AutoSave Components in the scene and registers them with the manager */
|
||||
public void GetAutoSaves()
|
||||
{
|
||||
autoSaves = new HashSet<ES3AutoSave>();
|
||||
|
||||
foreach (var go in this.gameObject.scene.GetRootGameObjects())
|
||||
autoSaves.UnionWith(go.GetComponentsInChildren<ES3AutoSave>(true));
|
||||
}
|
||||
|
||||
// Gets the depth of a Transform in the hierarchy.
|
||||
static int GetDepth(Transform t)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
while (t.parent != null)
|
||||
{
|
||||
t = t.parent;
|
||||
depth++;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc4c084043dc014459977604947a0271
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Debugging.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Debugging.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5da0e0b897dee2a4f85041d11656a7e4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
52
Convention/[ES3]/Easy Save 3/Scripts/Debugging/ES3Debug.cs
Normal file
52
Convention/[ES3]/Easy Save 3/Scripts/Debugging/ES3Debug.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
internal static class ES3Debug
|
||||
{
|
||||
private const string disableInfoMsg = "\n<i>To disable these messages from Easy Save, go to Window > Easy Save 3 > Settings, and uncheck 'Log Info'</i>";
|
||||
private const string disableWarningMsg = "\n<i>To disable warnings from Easy Save, go to Window > Easy Save 3 > Settings, and uncheck 'Log Warnings'</i>";
|
||||
private const string disableErrorMsg = "\n<i>To disable these error messages from Easy Save, go to Window > Easy Save 3 > Settings, and uncheck 'Log Errors'</i>";
|
||||
|
||||
private const char indentChar = '-';
|
||||
|
||||
public static void Log(string msg, Object context = null, int indent=0)
|
||||
{
|
||||
if (!ES3Settings.defaultSettingsScriptableObject.logDebugInfo)
|
||||
return;
|
||||
else if (context != null)
|
||||
Debug.LogFormat(context, Indent(indent) + msg + disableInfoMsg);
|
||||
else
|
||||
Debug.LogFormat(context, Indent(indent) + msg);
|
||||
}
|
||||
|
||||
public static void LogWarning(string msg, Object context=null, int indent = 0)
|
||||
{
|
||||
if (!ES3Settings.defaultSettingsScriptableObject.logWarnings)
|
||||
return;
|
||||
else if (context != null)
|
||||
Debug.LogWarningFormat(context, Indent(indent) + msg + disableWarningMsg);
|
||||
else
|
||||
Debug.LogWarningFormat(context, Indent(indent) + msg + disableWarningMsg);
|
||||
}
|
||||
|
||||
public static void LogError(string msg, Object context = null, int indent = 0)
|
||||
{
|
||||
if (!ES3Settings.defaultSettingsScriptableObject.logErrors)
|
||||
return;
|
||||
else if (context != null)
|
||||
Debug.LogErrorFormat(context, Indent(indent) + msg + disableErrorMsg);
|
||||
else
|
||||
Debug.LogErrorFormat(context, Indent(indent) + msg + disableErrorMsg);
|
||||
}
|
||||
|
||||
private static string Indent(int size)
|
||||
{
|
||||
if (size < 0)
|
||||
return "";
|
||||
return new string(indentChar, size);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8972072d44c5f945879f39db5fd0711
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1648
Convention/[ES3]/Easy Save 3/Scripts/ES3.cs
Normal file
1648
Convention/[ES3]/Easy Save 3/Scripts/ES3.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Convention/[ES3]/Easy Save 3/Scripts/ES3.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76736e9582352734e8ba55170546de81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
208
Convention/[ES3]/Easy Save 3/Scripts/ES3Crypto.cs
Normal file
208
Convention/[ES3]/Easy Save 3/Scripts/ES3Crypto.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
#if !DISABLE_ENCRYPTION
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
#if NETFX_CORE
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
using Windows.Storage.Streams;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
#endif
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public static class ES3Hash
|
||||
{
|
||||
#if NETFX_CORE
|
||||
public static string SHA1Hash(string input)
|
||||
{
|
||||
return System.Text.Encoding.UTF8.GetString(UnityEngine.Windows.Crypto.ComputeSHA1Hash(System.Text.Encoding.UTF8.GetBytes(input)));
|
||||
}
|
||||
#else
|
||||
public static string SHA1Hash(string input)
|
||||
{
|
||||
using (SHA1Managed sha1 = new SHA1Managed())
|
||||
return System.Text.Encoding.UTF8.GetString(sha1.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public abstract class EncryptionAlgorithm
|
||||
{
|
||||
public abstract byte[] Encrypt(byte[] bytes, string password, int bufferSize);
|
||||
public abstract byte[] Decrypt(byte[] bytes, string password, int bufferSize);
|
||||
public abstract void Encrypt(Stream input, Stream output, string password, int bufferSize);
|
||||
public abstract void Decrypt(Stream input, Stream output, string password, int bufferSize);
|
||||
|
||||
protected static void CopyStream(Stream input, Stream output, int bufferSize)
|
||||
{
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int read;
|
||||
while ((read = input.Read(buffer, 0, bufferSize)) > 0)
|
||||
output.Write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
public class AESEncryptionAlgorithm : EncryptionAlgorithm
|
||||
{
|
||||
private const int ivSize = 16;
|
||||
private const int keySize = 16;
|
||||
private const int pwIterations = 100;
|
||||
|
||||
public override byte[] Encrypt(byte[] bytes, string password, int bufferSize)
|
||||
{
|
||||
using (var input = new MemoryStream(bytes))
|
||||
{
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
Encrypt(input, output, password, bufferSize);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Decrypt(byte[] bytes, string password, int bufferSize)
|
||||
{
|
||||
using (var input = new MemoryStream(bytes))
|
||||
{
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
Decrypt(input, output, password, bufferSize);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt(Stream input, Stream output, string password, int bufferSize)
|
||||
{
|
||||
input.Position = 0;
|
||||
|
||||
#if NETFX_CORE
|
||||
// Generate an IV and write it to the output.
|
||||
var iv = CryptographicBuffer.GenerateRandom(ivSize);
|
||||
output.Write(iv.ToArray(), 0, ivSize);
|
||||
|
||||
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8);
|
||||
var keyDerivationProvider = KeyDerivationAlgorithmProvider.OpenAlgorithm("PBKDF2_SHA1");
|
||||
KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2(iv, pwIterations);
|
||||
// Create a key based on original key and derivation parmaters
|
||||
CryptographicKey keyOriginal = keyDerivationProvider.CreateKey(pwBuffer);
|
||||
IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial(keyOriginal, pbkdf2Parms, keySize);
|
||||
|
||||
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
|
||||
var key = provider.CreateSymmetricKey(keyMaterial);
|
||||
|
||||
// Get the input stream as an IBuffer.
|
||||
IBuffer msg;
|
||||
using(var ms = new MemoryStream())
|
||||
{
|
||||
input.CopyTo(ms);
|
||||
msg = ms.ToArray().AsBuffer();
|
||||
}
|
||||
|
||||
var buffEncrypt = CryptographicEngine.Encrypt(key, msg, iv);
|
||||
|
||||
|
||||
output.Write(buffEncrypt.ToArray(), 0, (int)buffEncrypt.Length);
|
||||
output.Dispose();
|
||||
#else
|
||||
using (var alg = Aes.Create())
|
||||
{
|
||||
alg.Mode = CipherMode.CBC;
|
||||
alg.Padding = PaddingMode.PKCS7;
|
||||
alg.GenerateIV();
|
||||
var key = new Rfc2898DeriveBytes(password, alg.IV, pwIterations);
|
||||
alg.Key = key.GetBytes(keySize);
|
||||
// Write the IV to the output stream.
|
||||
output.Write(alg.IV, 0, ivSize);
|
||||
using(var encryptor = alg.CreateEncryptor())
|
||||
using(var cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
|
||||
CopyStream(input, cs, bufferSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Decrypt(Stream input, Stream output, string password, int bufferSize)
|
||||
{
|
||||
#if NETFX_CORE
|
||||
var thisIV = new byte[ivSize];
|
||||
input.Read(thisIV, 0, ivSize);
|
||||
var iv = thisIV.AsBuffer();
|
||||
|
||||
var pwBuffer = CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8);
|
||||
|
||||
var keyDerivationProvider = KeyDerivationAlgorithmProvider.OpenAlgorithm("PBKDF2_SHA1");
|
||||
KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2(iv, pwIterations);
|
||||
// Create a key based on original key and derivation parameters.
|
||||
CryptographicKey keyOriginal = keyDerivationProvider.CreateKey(pwBuffer);
|
||||
IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial(keyOriginal, pbkdf2Parms, keySize);
|
||||
|
||||
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
|
||||
var key = provider.CreateSymmetricKey(keyMaterial);
|
||||
|
||||
// Get the input stream as an IBuffer.
|
||||
IBuffer msg;
|
||||
using(var ms = new MemoryStream())
|
||||
{
|
||||
input.CopyTo(ms);
|
||||
msg = ms.ToArray().AsBuffer();
|
||||
}
|
||||
|
||||
var buffDecrypt = CryptographicEngine.Decrypt(key, msg, iv);
|
||||
|
||||
output.Write(buffDecrypt.ToArray(), 0, (int)buffDecrypt.Length);
|
||||
#else
|
||||
using (var alg = Aes.Create())
|
||||
{
|
||||
var thisIV = new byte[ivSize];
|
||||
input.Read(thisIV, 0, ivSize);
|
||||
alg.IV = thisIV;
|
||||
|
||||
var key = new Rfc2898DeriveBytes(password, alg.IV, pwIterations);
|
||||
alg.Key = key.GetBytes(keySize);
|
||||
|
||||
using(var decryptor = alg.CreateDecryptor())
|
||||
using(var cryptoStream = new CryptoStream(input, decryptor, CryptoStreamMode.Read))
|
||||
CopyStream(cryptoStream, output, bufferSize);
|
||||
|
||||
}
|
||||
#endif
|
||||
output.Position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnbufferedCryptoStream : MemoryStream
|
||||
{
|
||||
private readonly Stream stream;
|
||||
private readonly bool isReadStream;
|
||||
private string password;
|
||||
private int bufferSize;
|
||||
private EncryptionAlgorithm alg;
|
||||
private bool disposed = false;
|
||||
|
||||
public UnbufferedCryptoStream(Stream stream, bool isReadStream, string password, int bufferSize, EncryptionAlgorithm alg) : base()
|
||||
{
|
||||
this.stream = stream;
|
||||
this.isReadStream = isReadStream;
|
||||
this.password = password;
|
||||
this.bufferSize = bufferSize;
|
||||
this.alg = alg;
|
||||
|
||||
|
||||
if (isReadStream)
|
||||
alg.Decrypt(stream, this, password, bufferSize);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
disposed = true;
|
||||
|
||||
if (!isReadStream)
|
||||
alg.Encrypt(this, stream, password, bufferSize);
|
||||
stream.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Crypto.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Crypto.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17f1525fffc9e7647b3aba297290ed33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
516
Convention/[ES3]/Easy Save 3/Scripts/ES3File.cs
Normal file
516
Convention/[ES3]/Easy Save 3/Scripts/ES3File.cs
Normal file
@@ -0,0 +1,516 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
using ES3Types;
|
||||
using UnityEngine;
|
||||
using ES3Internal;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>Represents a cached file which can be saved to and loaded from, and commited to storage when necessary.</summary>
|
||||
public class ES3File
|
||||
{
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public static Dictionary<string, ES3File> cachedFiles = new Dictionary<string, ES3File>();
|
||||
|
||||
public ES3Settings settings;
|
||||
private Dictionary<string, ES3Data> cache = new Dictionary<string, ES3Data>();
|
||||
private bool syncWithFile = false;
|
||||
private DateTime timestamp = DateTime.UtcNow;
|
||||
|
||||
/// <summary>Creates a new ES3File and loads the default file into the ES3File if there is data to load.</summary>
|
||||
public ES3File() : this(new ES3Settings(), true) { }
|
||||
|
||||
/// <summary>Creates a new ES3File and loads the specified file into the ES3File if there is data to load.</summary>
|
||||
/// <param name="filepath">The relative or absolute path of the file in storage our ES3File is associated with.</param>
|
||||
public ES3File(string filePath) : this(new ES3Settings(filePath), true) { }
|
||||
|
||||
/// <summary>Creates a new ES3File and loads the specified file into the ES3File if there is data to load.</summary>
|
||||
/// <param name="filepath">The relative or absolute path of the file in storage our ES3File is associated with.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public ES3File(string filePath, ES3Settings settings) : this(new ES3Settings(filePath, settings), true) { }
|
||||
|
||||
/// <summary>Creates a new ES3File and loads the specified file into the ES3File if there is data to load.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public ES3File(ES3Settings settings) : this(settings, true) { }
|
||||
|
||||
/// <summary>Creates a new ES3File and only loads the default file into it if syncWithFile is set to true.</summary>
|
||||
/// <param name="syncWithFile">Whether we should sync this ES3File with the one in storage immediately after creating it.</param>
|
||||
public ES3File(bool syncWithFile) : this(new ES3Settings(), syncWithFile) { }
|
||||
/// <summary>Creates a new ES3File and only loads the specified file into it if syncWithFile is set to true.</summary>
|
||||
/// <param name="filepath">The relative or absolute path of the file in storage our ES3File is associated with.</param>
|
||||
/// <param name="syncWithFile">Whether we should sync this ES3File with the one in storage immediately after creating it.</param>
|
||||
public ES3File(string filePath, bool syncWithFile) : this(new ES3Settings(filePath), syncWithFile) { }
|
||||
/// <summary>Creates a new ES3File and only loads the specified file into it if syncWithFile is set to true.</summary>
|
||||
/// <param name="filepath">The relative or absolute path of the file in storage our ES3File is associated with.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
/// <param name="syncWithFile">Whether we should sync this ES3File with the one in storage immediately after creating it.</param>
|
||||
public ES3File(string filePath, ES3Settings settings, bool syncWithFile) : this(new ES3Settings(filePath, settings), syncWithFile) { }
|
||||
|
||||
/// <summary>Creates a new ES3File and loads the specified file into the ES3File if there is data to load.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
/// <param name="syncWithFile">Whether we should sync this ES3File with the one in storage immediately after creating it.</param>
|
||||
public ES3File(ES3Settings settings, bool syncWithFile)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.syncWithFile = syncWithFile;
|
||||
if (syncWithFile)
|
||||
{
|
||||
// Type checking must be enabled when syncing.
|
||||
var settingsWithTypeChecking = (ES3Settings)settings.Clone();
|
||||
settingsWithTypeChecking.typeChecking = true;
|
||||
|
||||
using (var reader = ES3Reader.Create(settingsWithTypeChecking))
|
||||
{
|
||||
if (reader == null)
|
||||
return;
|
||||
foreach (KeyValuePair<string, ES3Data> kvp in reader.RawEnumerator)
|
||||
cache[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
timestamp = ES3.GetTimestamp(settingsWithTypeChecking);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3File and loads the bytes into the ES3File. Note the bytes must represent that of a file.</summary>
|
||||
/// <param name="bytes">The bytes representing our file.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
/// <param name="syncWithFile">Whether we should sync this ES3File with the one in storage immediately after creating it.</param>
|
||||
public ES3File(byte[] bytes, ES3Settings settings = null)
|
||||
{
|
||||
if (settings == null)
|
||||
this.settings = new ES3Settings();
|
||||
else
|
||||
this.settings = settings;
|
||||
|
||||
syncWithFile = true; // This ensures that the file won't be merged, which would prevent deleted keys from being deleted.
|
||||
|
||||
SaveRaw(bytes, settings);
|
||||
}
|
||||
|
||||
/// <summary>Synchronises this ES3File with a file in storage.</summary>
|
||||
public void Sync()
|
||||
{
|
||||
Sync(this.settings);
|
||||
}
|
||||
|
||||
/// <summary>Synchronises this ES3File with a file in storage.</summary>
|
||||
/// <param name="filepath">The relative or absolute path of the file in storage we want to synchronise with.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public void Sync(string filePath, ES3Settings settings = null)
|
||||
{
|
||||
Sync(new ES3Settings(filePath, settings));
|
||||
}
|
||||
|
||||
/// <summary>Synchronises this ES3File with a file in storage.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public void Sync(ES3Settings settings = null)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = new ES3Settings();
|
||||
|
||||
if (cache.Count == 0)
|
||||
{
|
||||
ES3.DeleteFile(settings);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var baseWriter = ES3Writer.Create(settings, true, !syncWithFile, false))
|
||||
{
|
||||
foreach (var kvp in cache)
|
||||
{
|
||||
// If we change the name of a type, the type may be null.
|
||||
// In this case, use System.Object as the type.
|
||||
Type type;
|
||||
if (kvp.Value.type == null)
|
||||
type = typeof(System.Object);
|
||||
else
|
||||
type = kvp.Value.type.type;
|
||||
baseWriter.Write(kvp.Key, type, kvp.Value.bytes);
|
||||
}
|
||||
baseWriter.Save(!syncWithFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Removes the data stored in this ES3File. The ES3File will be empty after calling this method.</summary>
|
||||
public void Clear()
|
||||
{
|
||||
cache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>Returns an array of all of the key names in this ES3File.</summary>
|
||||
public string[] GetKeys()
|
||||
{
|
||||
var keyCollection = cache.Keys;
|
||||
var keys = new string[keyCollection.Count];
|
||||
keyCollection.CopyTo(keys, 0);
|
||||
return keys;
|
||||
}
|
||||
|
||||
#region Save Methods
|
||||
|
||||
/// <summary>Saves a value to a key in this ES3File.</summary>
|
||||
/// <param name="key">The key we want to use to identify our value in the file.</param>
|
||||
/// <param name="value">The value we want to save.</param>
|
||||
public void Save<T>(string key, T value)
|
||||
{
|
||||
var unencryptedSettings = (ES3Settings)settings.Clone();
|
||||
unencryptedSettings.encryptionType = ES3.EncryptionType.None;
|
||||
unencryptedSettings.compressionType = ES3.CompressionType.None;
|
||||
|
||||
// If T is object, use the value to get it's type. Otherwise, use T so that it works with inheritence.
|
||||
Type type;
|
||||
if (value == null)
|
||||
type = typeof(T);
|
||||
else
|
||||
type = value.GetType();
|
||||
|
||||
cache[key] = new ES3Data(ES3TypeMgr.GetOrCreateES3Type(type), ES3.Serialize(value, unencryptedSettings));
|
||||
}
|
||||
|
||||
/// <summary>Merges the data specified by the bytes parameter into this ES3File.</summary>
|
||||
/// <param name="bytes">The bytes we want to merge with this ES3File.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public void SaveRaw(byte[] bytes, ES3Settings settings = null)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = new ES3Settings();
|
||||
|
||||
// Type checking must be enabled when syncing.
|
||||
var settingsWithTypeChecking = (ES3Settings)settings.Clone();
|
||||
settingsWithTypeChecking.typeChecking = true;
|
||||
|
||||
using (var reader = ES3Reader.Create(bytes, settingsWithTypeChecking))
|
||||
{
|
||||
if (reader == null)
|
||||
return;
|
||||
foreach (KeyValuePair<string, ES3Data> kvp in reader.RawEnumerator)
|
||||
cache[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Merges the data specified by the bytes parameter into this ES3File.</summary>
|
||||
/// <param name="bytes">The bytes we want to merge with this ES3File.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public void AppendRaw(byte[] bytes, ES3Settings settings = null)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = new ES3Settings();
|
||||
// AppendRaw just does the same thing as SaveRaw in ES3File.
|
||||
SaveRaw(bytes, settings);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load Methods
|
||||
|
||||
/* Standard load methods */
|
||||
|
||||
/// <summary>Loads the value from this ES3File with the given key.</summary>
|
||||
/// <param name="key">The key which identifies the value we want to load.</param>
|
||||
public object Load(string key)
|
||||
{
|
||||
return Load<object>(key);
|
||||
}
|
||||
|
||||
/// <summary>Loads the value from this ES3File with the given key.</summary>
|
||||
/// <param name="key">The key which identifies the value we want to load.</param>
|
||||
/// <param name="defaultValue">The value we want to return if the key does not exist in this ES3File.</param>
|
||||
public object Load(string key, object defaultValue)
|
||||
{
|
||||
return Load<object>(key, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>Loads the value from this ES3File with the given key.</summary>
|
||||
/// <param name="key">The key which identifies the value we want to load.</param>
|
||||
public T Load<T>(string key)
|
||||
{
|
||||
ES3Data es3Data;
|
||||
|
||||
if (!cache.TryGetValue(key, out es3Data))
|
||||
throw new KeyNotFoundException("Key \"" + key + "\" was not found in this ES3File. Use Load<T>(key, defaultValue) if you want to return a default value if the key does not exist.");
|
||||
|
||||
var unencryptedSettings = (ES3Settings)this.settings.Clone();
|
||||
unencryptedSettings.encryptionType = ES3.EncryptionType.None;
|
||||
unencryptedSettings.compressionType = ES3.CompressionType.None;
|
||||
|
||||
if (typeof(T) == typeof(object))
|
||||
return (T)ES3.Deserialize(es3Data.type, es3Data.bytes, unencryptedSettings);
|
||||
return ES3.Deserialize<T>(es3Data.bytes, unencryptedSettings);
|
||||
}
|
||||
|
||||
/// <summary>Loads the value from this ES3File with the given key.</summary>
|
||||
/// <param name="key">The key which identifies the value we want to load.</param>
|
||||
/// <param name="defaultValue">The value we want to return if the key does not exist in this ES3File.</param>
|
||||
public T Load<T>(string key, T defaultValue)
|
||||
{
|
||||
ES3Data es3Data;
|
||||
|
||||
if (!cache.TryGetValue(key, out es3Data))
|
||||
return defaultValue;
|
||||
var unencryptedSettings = (ES3Settings)this.settings.Clone();
|
||||
unencryptedSettings.encryptionType = ES3.EncryptionType.None;
|
||||
unencryptedSettings.compressionType = ES3.CompressionType.None;
|
||||
|
||||
if (typeof(T) == typeof(object))
|
||||
return (T)ES3.Deserialize(es3Data.type, es3Data.bytes, unencryptedSettings);
|
||||
return ES3.Deserialize<T>(es3Data.bytes, unencryptedSettings);
|
||||
}
|
||||
|
||||
/// <summary>Loads the value from this ES3File with the given key into an existing object.</summary>
|
||||
/// <param name="key">The key which identifies the value we want to load.</param>
|
||||
/// <param name="obj">The object we want to load the value into.</param>
|
||||
public void LoadInto<T>(string key, T obj) where T : class
|
||||
{
|
||||
ES3Data es3Data;
|
||||
|
||||
if (!cache.TryGetValue(key, out es3Data))
|
||||
throw new KeyNotFoundException("Key \"" + key + "\" was not found in this ES3File. Use Load<T>(key, defaultValue) if you want to return a default value if the key does not exist.");
|
||||
|
||||
var unencryptedSettings = (ES3Settings)this.settings.Clone();
|
||||
unencryptedSettings.encryptionType = ES3.EncryptionType.None;
|
||||
unencryptedSettings.compressionType = ES3.CompressionType.None;
|
||||
|
||||
if (typeof(T) == typeof(object))
|
||||
ES3.DeserializeInto(es3Data.type, es3Data.bytes, obj, unencryptedSettings);
|
||||
else
|
||||
ES3.DeserializeInto(es3Data.bytes, obj, unencryptedSettings);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load Raw Methods
|
||||
|
||||
/// <summary>Loads the ES3File as a raw, unencrypted, uncompressed byte array.</summary>
|
||||
public byte[] LoadRawBytes()
|
||||
{
|
||||
var newSettings = (ES3Settings)settings.Clone();
|
||||
if (!newSettings.postprocessRawCachedData)
|
||||
{
|
||||
newSettings.encryptionType = ES3.EncryptionType.None;
|
||||
newSettings.compressionType = ES3.CompressionType.None;
|
||||
}
|
||||
return GetBytes(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>Loads the ES3File as a raw, unencrypted, uncompressed string, using the encoding defined in the ES3File's settings variable.</summary>
|
||||
public string LoadRawString()
|
||||
{
|
||||
if (cache.Count == 0)
|
||||
return "";
|
||||
return settings.encoding.GetString(LoadRawBytes());
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as LoadRawString, except it will return an encrypted/compressed file if these are enabled.
|
||||
*/
|
||||
internal byte[] GetBytes(ES3Settings settings = null)
|
||||
{
|
||||
if (cache.Count == 0)
|
||||
return new byte[0];
|
||||
|
||||
if (settings == null)
|
||||
settings = this.settings;
|
||||
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
var memorySettings = (ES3Settings)settings.Clone();
|
||||
memorySettings.location = ES3.Location.InternalMS;
|
||||
// Ensure we return unencrypted bytes.
|
||||
if (!memorySettings.postprocessRawCachedData)
|
||||
{
|
||||
memorySettings.encryptionType = ES3.EncryptionType.None;
|
||||
memorySettings.compressionType = ES3.CompressionType.None;
|
||||
}
|
||||
|
||||
using (var baseWriter = ES3Writer.Create(ES3Stream.CreateStream(ms, memorySettings, ES3FileMode.Write), memorySettings, true, false))
|
||||
{
|
||||
foreach (var kvp in cache)
|
||||
baseWriter.Write(kvp.Key, kvp.Value.type.type, kvp.Value.bytes);
|
||||
baseWriter.Save(false);
|
||||
}
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other ES3 Methods
|
||||
|
||||
/// <summary>Deletes a key from this ES3File.</summary>
|
||||
/// <param name="key">The key we want to delete.</param>
|
||||
public void DeleteKey(string key)
|
||||
{
|
||||
cache.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>Checks whether a key exists in this ES3File.</summary>
|
||||
/// <param name="key">The key we want to check the existence of.</param>
|
||||
/// <returns>True if the key exists, otherwise False.</returns>
|
||||
public bool KeyExists(string key)
|
||||
{
|
||||
return cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>Gets the size of the cached data in bytes.</summary>
|
||||
public int Size()
|
||||
{
|
||||
int size = 0;
|
||||
foreach (var kvp in cache)
|
||||
size += kvp.Value.bytes.Length;
|
||||
return size;
|
||||
}
|
||||
|
||||
public Type GetKeyType(string key)
|
||||
{
|
||||
ES3Data es3data;
|
||||
if (!cache.TryGetValue(key, out es3data))
|
||||
throw new KeyNotFoundException("Key \"" + key + "\" was not found in this ES3File. Use Load<T>(key, defaultValue) if you want to return a default value if the key does not exist.");
|
||||
|
||||
return es3data.type.type;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static ES3File GetOrCreateCachedFile(ES3Settings settings)
|
||||
{
|
||||
ES3File cachedFile;
|
||||
if (!cachedFiles.TryGetValue(settings.path, out cachedFile))
|
||||
{
|
||||
cachedFile = new ES3File(settings, false);
|
||||
cachedFiles.Add(settings.path, cachedFile);
|
||||
cachedFile.syncWithFile = true; // This ensures that the file won't be merged, which would prevent deleted keys from being deleted.
|
||||
}
|
||||
// Settings might refer to the same file, but might have changed.
|
||||
// To account for this, we update the settings of the ES3File each time we access it.
|
||||
cachedFile.settings = settings;
|
||||
return cachedFile;
|
||||
}
|
||||
|
||||
internal static void CacheFile(ES3Settings settings)
|
||||
{
|
||||
// If we're still using cached settings, set it to the default location.
|
||||
if (settings.location == ES3.Location.Cache)
|
||||
{
|
||||
settings = (ES3Settings)settings.Clone();
|
||||
// If the default settings are also set to cache, assume ES3.Location.File. Otherwise, set it to the default location.
|
||||
settings.location = ES3Settings.defaultSettings.location == ES3.Location.Cache ? ES3.Location.File : ES3Settings.defaultSettings.location;
|
||||
}
|
||||
|
||||
if (!ES3.FileExists(settings))
|
||||
return;
|
||||
|
||||
// Disable compression and encryption when loading the raw bytes, and the ES3File constructor will expect encrypted/compressed bytes.
|
||||
var loadSettings = (ES3Settings)settings.Clone();
|
||||
loadSettings.compressionType = ES3.CompressionType.None;
|
||||
loadSettings.encryptionType = ES3.EncryptionType.None;
|
||||
|
||||
cachedFiles[settings.path] = new ES3File(ES3.LoadRawBytes(loadSettings), settings);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static void Store(ES3Settings settings = null)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = new ES3Settings(ES3.Location.File);
|
||||
// If we're still using cached settings, set it to the default location.
|
||||
else if (settings.location == ES3.Location.Cache)
|
||||
{
|
||||
settings = (ES3Settings)settings.Clone();
|
||||
// If the default settings are also set to cache, assume ES3.Location.File. Otherwise, set it to the default location.
|
||||
settings.location = ES3Settings.defaultSettings.location == ES3.Location.Cache ? ES3.Location.File : ES3Settings.defaultSettings.location;
|
||||
}
|
||||
|
||||
ES3File cachedFile;
|
||||
if (!cachedFiles.TryGetValue(settings.path, out cachedFile))
|
||||
throw new FileNotFoundException("The file '" + settings.path + "' could not be stored because it could not be found in the cache.");
|
||||
cachedFile.Sync(settings);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static void RemoveCachedFile(ES3Settings settings)
|
||||
{
|
||||
cachedFiles.Remove(settings.path);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static void CopyCachedFile(ES3Settings oldSettings, ES3Settings newSettings)
|
||||
{
|
||||
ES3File cachedFile;
|
||||
if (!cachedFiles.TryGetValue(oldSettings.path, out cachedFile))
|
||||
throw new FileNotFoundException("The file '" + oldSettings.path + "' could not be copied because it could not be found in the cache.");
|
||||
if (cachedFiles.ContainsKey(newSettings.path))
|
||||
throw new InvalidOperationException("Cannot copy file '" + oldSettings.path + "' to '" + newSettings.path + "' because '" + newSettings.path + "' already exists");
|
||||
|
||||
cachedFiles.Add(newSettings.path, (ES3File)cachedFile.MemberwiseClone());
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static void DeleteKey(string key, ES3Settings settings)
|
||||
{
|
||||
ES3File cachedFile;
|
||||
if (cachedFiles.TryGetValue(settings.path, out cachedFile))
|
||||
cachedFile.DeleteKey(key);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static bool KeyExists(string key, ES3Settings settings)
|
||||
{
|
||||
ES3File cachedFile;
|
||||
if (cachedFiles.TryGetValue(settings.path, out cachedFile))
|
||||
return cachedFile.KeyExists(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static bool FileExists(ES3Settings settings)
|
||||
{
|
||||
return cachedFiles.ContainsKey(settings.path);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static string[] GetKeys(ES3Settings settings)
|
||||
{
|
||||
ES3File cachedFile;
|
||||
if (!cachedFiles.TryGetValue(settings.path, out cachedFile))
|
||||
throw new FileNotFoundException("Could not get keys from the file '" + settings.path + "' because it could not be found in the cache.");
|
||||
return cachedFile.cache.Keys.ToArray();
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal static string[] GetFiles()
|
||||
{
|
||||
return cachedFiles.Keys.ToArray();
|
||||
}
|
||||
|
||||
internal static DateTime GetTimestamp(ES3Settings settings)
|
||||
{
|
||||
ES3File cachedFile;
|
||||
if (!cachedFiles.TryGetValue(settings.path, out cachedFile))
|
||||
return new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
return cachedFile.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public struct ES3Data
|
||||
{
|
||||
public ES3Type type;
|
||||
public byte[] bytes;
|
||||
|
||||
public ES3Data(Type type, byte[] bytes)
|
||||
{
|
||||
this.type = type == null ? null : ES3TypeMgr.GetOrCreateES3Type(type);
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public ES3Data(ES3Type type, byte[] bytes)
|
||||
{
|
||||
this.type = type;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3File.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3File.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b440b2568aee31f4ba9da59780f53abc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
20
Convention/[ES3]/Easy Save 3/Scripts/ES3GameObject.cs
Normal file
20
Convention/[ES3]/Easy Save 3/Scripts/ES3GameObject.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class ES3GameObject : MonoBehaviour
|
||||
{
|
||||
public List<Component> components = new List<Component>();
|
||||
|
||||
/* Ensures that this Component is always last in the List to guarantee that it's loaded after any Components it references */
|
||||
private void Update()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
return;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
UnityEditorInternal.ComponentUtility.MoveComponentDown(this);
|
||||
#endif
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3GameObject.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3GameObject.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c57ae9ded6c6fe443af44501f3208f8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
178
Convention/[ES3]/Easy Save 3/Scripts/ES3IO.cs
Normal file
178
Convention/[ES3]/Easy Save 3/Scripts/ES3IO.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public static class ES3IO
|
||||
{
|
||||
#if UNITY_SWITCH
|
||||
internal static readonly string persistentDataPath = "";
|
||||
internal static readonly string dataPath = "";
|
||||
#else
|
||||
internal static readonly string persistentDataPath = Application.persistentDataPath;
|
||||
internal static readonly string dataPath = Application.dataPath;
|
||||
#endif
|
||||
|
||||
internal const string backupFileSuffix = ".bac";
|
||||
internal const string temporaryFileSuffix = ".tmp";
|
||||
|
||||
public enum ES3FileMode { Read, Write, Append }
|
||||
|
||||
public static DateTime GetTimestamp(string filePath)
|
||||
{
|
||||
if (!FileExists(filePath))
|
||||
return new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
return File.GetLastWriteTime(filePath).ToUniversalTime();
|
||||
}
|
||||
|
||||
public static string GetExtension(string path)
|
||||
{
|
||||
return Path.GetExtension(path);
|
||||
}
|
||||
|
||||
public static void DeleteFile(string filePath)
|
||||
{
|
||||
if (FileExists(filePath))
|
||||
File.Delete(filePath);
|
||||
}
|
||||
|
||||
public static bool FileExists(string filePath) { return File.Exists(filePath); }
|
||||
public static void MoveFile(string sourcePath, string destPath) { File.Move(sourcePath, destPath); }
|
||||
public static void CopyFile(string sourcePath, string destPath) { File.Copy(sourcePath, destPath); }
|
||||
|
||||
public static void MoveDirectory(string sourcePath, string destPath) { Directory.Move(sourcePath, destPath); }
|
||||
public static void CreateDirectory(string directoryPath) { Directory.CreateDirectory(directoryPath); }
|
||||
public static bool DirectoryExists(string directoryPath) { return Directory.Exists(directoryPath); }
|
||||
|
||||
/*
|
||||
* Given a path, it returns the directory that path points to.
|
||||
* eg. "C:/myFolder/thisFolder/myFile.txt" will return "C:/myFolder/thisFolder".
|
||||
*/
|
||||
public static string GetDirectoryPath(string path, char seperator = '/')
|
||||
{
|
||||
//return Path.GetDirectoryName(path);
|
||||
// Path.GetDirectoryName turns forward slashes to backslashes in some cases on Windows, which is why
|
||||
// Substring is used instead.
|
||||
char slashChar = UsesForwardSlash(path) ? '/' : '\\';
|
||||
|
||||
int slash = path.LastIndexOf(slashChar);
|
||||
|
||||
// If this path ends in a slash it is assumed to already be a path to a Directory.
|
||||
if (slash == path.Length - 1)
|
||||
return path;
|
||||
|
||||
// Ignore trailing slash if necessary.
|
||||
if (slash == (path.Length - 1))
|
||||
slash = path.Substring(0, slash).LastIndexOf(slashChar);
|
||||
if (slash == -1)
|
||||
ES3Debug.LogError("Path provided is not a directory path as it contains no slashes.");
|
||||
return path.Substring(0, slash);
|
||||
}
|
||||
|
||||
public static bool UsesForwardSlash(string path)
|
||||
{
|
||||
if (path.Contains("/"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Takes a directory path and a file or directory name and combines them into a single path.
|
||||
public static string CombinePathAndFilename(string directoryPath, string fileOrDirectoryName)
|
||||
{
|
||||
if (directoryPath[directoryPath.Length - 1] != '/' && directoryPath[directoryPath.Length - 1] != '\\')
|
||||
directoryPath += '/';
|
||||
return directoryPath + fileOrDirectoryName;
|
||||
}
|
||||
|
||||
public static string[] GetDirectories(string path, bool getFullPaths = true)
|
||||
{
|
||||
var paths = Directory.GetDirectories(path);
|
||||
for (int i = 0; i < paths.Length; i++)
|
||||
{
|
||||
if (!getFullPaths)
|
||||
paths[i] = Path.GetFileName(paths[i]);
|
||||
// GetDirectories sometimes returns backslashes, so we need to convert them to
|
||||
// forward slashes.
|
||||
paths[i].Replace("\\", "/");
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
public static void DeleteDirectory(string directoryPath)
|
||||
{
|
||||
if (DirectoryExists(directoryPath))
|
||||
Directory.Delete(directoryPath, true);
|
||||
}
|
||||
|
||||
// Note: Paths not ending in a slash are assumed to be a path to a file.
|
||||
// In this case the Directory containing the file will be searched.
|
||||
public static string[] GetFiles(string path, bool getFullPaths = true)
|
||||
{
|
||||
var paths = Directory.GetFiles(GetDirectoryPath(path));
|
||||
if (!getFullPaths)
|
||||
{
|
||||
for (int i = 0; i < paths.Length; i++)
|
||||
paths[i] = Path.GetFileName(paths[i]);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
public static byte[] ReadAllBytes(string path)
|
||||
{
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public static void WriteAllBytes(string path, byte[] bytes)
|
||||
{
|
||||
File.WriteAllBytes(path, bytes);
|
||||
}
|
||||
|
||||
public static void CommitBackup(ES3Settings settings)
|
||||
{
|
||||
ES3Debug.Log("Committing backup for " + settings.path + " to storage location " + settings.location);
|
||||
|
||||
var temporaryFilePath = settings.FullPath + temporaryFileSuffix;
|
||||
|
||||
if (settings.location == ES3.Location.File)
|
||||
{
|
||||
var oldFileBackup = settings.FullPath + temporaryFileSuffix + ".bak";
|
||||
|
||||
// If there's existing save data to overwrite ...
|
||||
if (FileExists(settings.FullPath))
|
||||
{
|
||||
// Delete any old backups.
|
||||
DeleteFile(oldFileBackup);
|
||||
// Rename the old file so we can restore it if it fails.
|
||||
CopyFile(settings.FullPath, oldFileBackup);
|
||||
|
||||
try
|
||||
{
|
||||
// Delete the old file so that we can move it.
|
||||
DeleteFile(settings.FullPath);
|
||||
// Now rename the temporary file to the name of the save file.
|
||||
MoveFile(temporaryFilePath, settings.FullPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// If any exceptions occur, restore the original save file.
|
||||
try { DeleteFile(settings.FullPath); } catch { }
|
||||
MoveFile(oldFileBackup, settings.FullPath);
|
||||
throw e;
|
||||
}
|
||||
|
||||
DeleteFile(oldFileBackup);
|
||||
}
|
||||
// Else just rename the temporary file to the main file.
|
||||
else
|
||||
MoveFile(temporaryFilePath, settings.FullPath);
|
||||
}
|
||||
else if (settings.location == ES3.Location.PlayerPrefs)
|
||||
{
|
||||
PlayerPrefs.SetString(settings.FullPath, PlayerPrefs.GetString(temporaryFilePath));
|
||||
PlayerPrefs.DeleteKey(temporaryFilePath);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3IO.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3IO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 384defb640bb5cc429878919bc66bfed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
19
Convention/[ES3]/Easy Save 3/Scripts/ES3InspectorInfo.cs
Normal file
19
Convention/[ES3]/Easy Save 3/Scripts/ES3InspectorInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/*
|
||||
* Displays an info message in the inspector.
|
||||
* Only available in the Editor.
|
||||
*/
|
||||
public class ES3InspectorInfo : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public string message = "";
|
||||
|
||||
public void SetMessage(string message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d1f89293f50fdf4e813a8d554d6d135
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
218
Convention/[ES3]/Easy Save 3/Scripts/ES3Prefab.cs
Normal file
218
Convention/[ES3]/Easy Save 3/Scripts/ES3Prefab.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using ES3Internal;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public class ES3Prefab : MonoBehaviour
|
||||
{
|
||||
public long prefabId = GetNewRefID();
|
||||
/*
|
||||
* We need to store references to all dependencies of the prefab because only supported scripts will be serialised.
|
||||
* This means that although supported scripts have their dependencies added to the reference manager when we load the prefab,
|
||||
* there will not be any dependencies in the reference manager for scripts which are not supported. So it will not be possible to save any reference to these.
|
||||
*/
|
||||
public ES3RefIdDictionary localRefs = new ES3RefIdDictionary();
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
// Add the references to the reference list when this prefab is instantiated.
|
||||
var mgr = ES3ReferenceMgrBase.Current;
|
||||
|
||||
if (mgr == null)
|
||||
return;
|
||||
|
||||
foreach (var kvp in localRefs)
|
||||
if (kvp.Key != null)
|
||||
mgr.Add(kvp.Key);
|
||||
}
|
||||
|
||||
public long Get(UnityEngine.Object obj)
|
||||
{
|
||||
long id;
|
||||
if (localRefs.TryGetValue(obj, out id))
|
||||
return id;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long Add(UnityEngine.Object obj)
|
||||
{
|
||||
long id;
|
||||
if (localRefs.TryGetValue(obj, out id))
|
||||
return id;
|
||||
|
||||
if (!ES3ReferenceMgr.CanBeSaved(obj))
|
||||
return -1;
|
||||
id = GetNewRefID();
|
||||
localRefs.Add(obj, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetReferences()
|
||||
{
|
||||
var localToGlobal = new Dictionary<string, string>();
|
||||
|
||||
var refMgr = ES3ReferenceMgr.Current;
|
||||
|
||||
if (refMgr == null)
|
||||
return localToGlobal;
|
||||
|
||||
foreach (var kvp in localRefs)
|
||||
{
|
||||
long id = refMgr.Add(kvp.Key);
|
||||
localToGlobal.Add(kvp.Value.ToString(), id.ToString());
|
||||
}
|
||||
return localToGlobal;
|
||||
}
|
||||
|
||||
public void ApplyReferences(Dictionary<long, long> localToGlobal)
|
||||
{
|
||||
if (ES3ReferenceMgrBase.Current == null)
|
||||
return;
|
||||
|
||||
foreach (var localRef in localRefs)
|
||||
{
|
||||
long globalId;
|
||||
if (localToGlobal.TryGetValue(localRef.Value, out globalId))
|
||||
ES3ReferenceMgrBase.Current.Add(localRef.Key, globalId);
|
||||
}
|
||||
}
|
||||
|
||||
public static long GetNewRefID()
|
||||
{
|
||||
return ES3ReferenceMgrBase.GetNewRefID();
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
public void GeneratePrefabReferences()
|
||||
{
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
if (this.gameObject.scene.name != null || UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null)
|
||||
#elif UNITY_2018_3_OR_NEWER
|
||||
if (this.gameObject.scene.name != null || UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null)
|
||||
#else
|
||||
if (this.gameObject.scene.name != null)
|
||||
#endif
|
||||
return;
|
||||
|
||||
// Create a new reference list so that any objects which are no longer dependencies are removed.
|
||||
var tempLocalRefs = new ES3RefIdDictionary();
|
||||
|
||||
// Get dependencies of children also.
|
||||
var transforms = GetComponentsInChildren<Transform>();
|
||||
var gos = new GameObject[transforms.Length];
|
||||
for (int i = 0; i < transforms.Length; i++)
|
||||
gos[i] = transforms[i].gameObject;
|
||||
|
||||
bool addedNewReference = false;
|
||||
|
||||
// Add the GameObject's dependencies to the reference list.
|
||||
foreach (var obj in EditorUtility.CollectDependencies(gos))
|
||||
{
|
||||
var dependency = (UnityEngine.Object)obj;
|
||||
if (obj == null || !ES3ReferenceMgr.CanBeSaved(dependency))
|
||||
continue;
|
||||
|
||||
var id = Get(dependency);
|
||||
// If we're adding a new reference, do an Undo.RecordObject to ensure it persists.
|
||||
if (id == -1)
|
||||
{
|
||||
addedNewReference = true;
|
||||
Undo.RecordObject(this, "Update Easy Save 3 Prefab");
|
||||
EditorUtility.SetDirty(this);
|
||||
}
|
||||
tempLocalRefs.Add(dependency, id == -1 ? GetNewRefID() : id);
|
||||
}
|
||||
|
||||
if (addedNewReference || tempLocalRefs.Count != localRefs.Count)
|
||||
localRefs = tempLocalRefs;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a blank ES3Type for ES3Prefab as it does not require serialising/deserialising when stored as a Component.
|
||||
*/
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3Type_ES3Prefab : ES3Type
|
||||
{
|
||||
public static ES3Type Instance = null;
|
||||
|
||||
public ES3Type_ES3Prefab() : base(typeof(ES3Prefab)) { Instance = this; }
|
||||
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this ES3Type to serialise the .
|
||||
*/
|
||||
public class ES3Type_ES3PrefabInternal : ES3Type
|
||||
{
|
||||
public static ES3Type Instance = new ES3Type_ES3PrefabInternal();
|
||||
|
||||
public ES3Type_ES3PrefabInternal() : base(typeof(ES3Type_ES3PrefabInternal)) { Instance = this; }
|
||||
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
ES3Prefab es3Prefab = (ES3Prefab)obj;
|
||||
|
||||
writer.WriteProperty("prefabId", es3Prefab.prefabId.ToString(), ES3Type_string.Instance);
|
||||
writer.WriteProperty("refs", es3Prefab.GetReferences());
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
var prefabId = reader.ReadRefProperty();
|
||||
|
||||
if (ES3ReferenceMgrBase.Current == null)
|
||||
return null;
|
||||
|
||||
var es3Prefab = ES3ReferenceMgrBase.Current.GetPrefab(prefabId);
|
||||
if (es3Prefab == null)
|
||||
throw new MissingReferenceException("Prefab with ID " + prefabId + " could not be found.\nPress the 'Refresh References' button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in the scene to refresh prefabs.");
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Use PrefabUtility.InstantiatePrefab if we're saving in the Editor and the application isn't playing.
|
||||
// This keeps the connection to the prefab, which is useful for Editor scripts saving data outside of runtime.
|
||||
var instance = Application.isPlaying ? GameObject.Instantiate(es3Prefab.gameObject) : PrefabUtility.InstantiatePrefab(es3Prefab.gameObject);
|
||||
#else
|
||||
var instance = GameObject.Instantiate(es3Prefab.gameObject);
|
||||
#endif
|
||||
var instanceES3Prefab = ((GameObject)instance).GetComponent<ES3Prefab>();
|
||||
if (instanceES3Prefab == null)
|
||||
throw new MissingReferenceException("Prefab with ID " + prefabId + " was found, but it does not have an ES3Prefab component attached.");
|
||||
|
||||
ReadInto<T>(reader, instance);
|
||||
|
||||
return instanceES3Prefab.gameObject;
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
// Load as ES3Refs and convert to longs.
|
||||
var localToGlobal_refs = reader.ReadProperty<Dictionary<ES3Ref, ES3Ref>>(ES3Type_ES3RefDictionary.Instance);
|
||||
var localToGlobal = new Dictionary<long, long>();
|
||||
foreach (var kvp in localToGlobal_refs)
|
||||
localToGlobal.Add(kvp.Key.id, kvp.Value.id);
|
||||
|
||||
if (ES3ReferenceMgrBase.Current == null)
|
||||
return;
|
||||
|
||||
((GameObject)obj).GetComponent<ES3Prefab>().ApplyReferences(localToGlobal);
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Prefab.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Prefab.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88ac1228c4c6ddb4fafe1ea75dd5eec9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
249
Convention/[ES3]/Easy Save 3/Scripts/ES3ReferenceMgr.cs
Normal file
249
Convention/[ES3]/Easy Save 3/Scripts/ES3ReferenceMgr.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using ES3Internal;
|
||||
using UnityEngine.SceneManagement;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
#if UNITY_VISUAL_SCRIPTING
|
||||
using Unity.VisualScripting;
|
||||
[IncludeInSettings(true)]
|
||||
#endif
|
||||
public class ES3ReferenceMgr : ES3ReferenceMgrBase
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void RefreshDependencies(bool isEnteringPlayMode = false)
|
||||
{
|
||||
// Empty the refId so it has to be refreshed.
|
||||
refId = null;
|
||||
|
||||
ES3ReferenceMgrBase.isEnteringPlayMode = isEnteringPlayMode;
|
||||
|
||||
// This will get the dependencies for all GameObjects and Components from the active scene.
|
||||
AddDependencies(this.gameObject.scene.GetRootGameObjects());
|
||||
AddDependenciesFromFolders();
|
||||
AddPrefabsToManager();
|
||||
RemoveNullOrInvalidValues();
|
||||
|
||||
ES3ReferenceMgrBase.isEnteringPlayMode = false;
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Easy Save 3/Refresh References for All Scenes", false, 150)]
|
||||
static void RefreshDependenciesInAllScenes()
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Refresh references in all scenes", "This will open each scene which is enabled in your Build Settings, refresh each reference manager, and save the scene.\n\nWe recommend making a backup of your project before doing this for the first time.", "Ok", "Cancel", DialogOptOutDecisionType.ForThisMachine, "ES3RefreshAllOptOut"))
|
||||
return;
|
||||
|
||||
// Get a list of loaded scenes so we know whether we need to close them after refreshing references or not.
|
||||
var loadedScenePaths = new string[SceneManager.sceneCount];
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
loadedScenePaths[i] = SceneManager.GetSceneAt(i).path;
|
||||
|
||||
var scenes = EditorBuildSettings.scenes;
|
||||
var sceneNameList = ""; // We use this so we can display a list of scenes at the end.
|
||||
|
||||
for (int i = 0; i < scenes.Length; i++)
|
||||
{
|
||||
var buildSettingsScene = scenes[i];
|
||||
|
||||
if (!buildSettingsScene.enabled)
|
||||
continue;
|
||||
|
||||
if (EditorUtility.DisplayCancelableProgressBar("Refreshing references", $"Refreshing references for scene {buildSettingsScene.path}.", i / scenes.Length))
|
||||
return;
|
||||
|
||||
var sceneWasOpen = loadedScenePaths.Contains(buildSettingsScene.path);
|
||||
var scene = EditorSceneManager.OpenScene(buildSettingsScene.path, OpenSceneMode.Additive);
|
||||
|
||||
var mgr = ES3ReferenceMgr.GetManagerFromScene(scene);
|
||||
|
||||
if (mgr != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
((ES3ReferenceMgr)mgr).RefreshDependencies();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Debug.LogError($"Couldn't update references for scene {scene.name} as the following exception occurred:\n\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
sceneNameList += $"{scene.name}\n";
|
||||
|
||||
// If the scene wasn't originally open, save it and close it.
|
||||
if (!sceneWasOpen)
|
||||
{
|
||||
// Temporarily disable refreshing on save so that it doesn't refresh again.
|
||||
var updateReferencesOnSave = ES3Settings.defaultSettingsScriptableObject.updateReferencesWhenSceneIsSaved;
|
||||
ES3Settings.defaultSettingsScriptableObject.updateReferencesWhenSceneIsSaved = false;
|
||||
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
EditorSceneManager.CloseScene(scene, true);
|
||||
|
||||
ES3Settings.defaultSettingsScriptableObject.updateReferencesWhenSceneIsSaved = updateReferencesOnSave;
|
||||
}
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
EditorUtility.DisplayDialog("References refreshed", $"Refrences updated for scenes:\n\n{sceneNameList}", "Ok", DialogOptOutDecisionType.ForThisMachine, "ES3RefreshAllCompleteOptOut");
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void Optimize()
|
||||
{
|
||||
var dependencies = EditorUtility.CollectDependencies(this.gameObject.scene.GetRootGameObjects());
|
||||
var notDependenciesOfScene = new HashSet<UnityEngine.Object>();
|
||||
|
||||
foreach (var kvp in idRef)
|
||||
if (!dependencies.Contains(kvp.Value))
|
||||
notDependenciesOfScene.Add(kvp.Value);
|
||||
|
||||
foreach (var obj in notDependenciesOfScene)
|
||||
Remove(obj);
|
||||
}
|
||||
|
||||
/* Adds all dependencies from this scene to the manager */
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void AddDependencies()
|
||||
{
|
||||
var rootGameObjects = gameObject.scene.GetRootGameObjects();
|
||||
|
||||
for (int j = 0; j < rootGameObjects.Length; j++)
|
||||
{
|
||||
var go = rootGameObjects[j];
|
||||
|
||||
if (EditorUtility.DisplayCancelableProgressBar("Gathering references", "Populating reference manager with your scene dependencies so they can be saved and loaded by reference.", j / rootGameObjects.Length))
|
||||
return;
|
||||
|
||||
AddDependencies(go);
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void AddDependencies(UnityEngine.Object[] objs)
|
||||
{
|
||||
var timeStarted = EditorApplication.timeSinceStartup;
|
||||
var timeout = ES3Settings.defaultSettingsScriptableObject.collectDependenciesTimeout;
|
||||
|
||||
foreach (var obj in objs)
|
||||
{
|
||||
if (obj == null || obj.name == "Easy Save 3 Manager")
|
||||
continue;
|
||||
|
||||
foreach (var dependency in EditorUtility.CollectDependencies(new UnityEngine.Object[] { obj }))
|
||||
{
|
||||
if (EditorApplication.timeSinceStartup - timeStarted > timeout)
|
||||
{
|
||||
Debug.LogWarning($"Easy Save cancelled gathering of references for object {obj.name} because it took longer than {timeout} seconds. You can increase the timeout length in Tools > Easy Save 3 > Settings > Reference Gathering Timeout, or adjust the settings so that fewer objects are referenced in your scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
Add(dependency);
|
||||
|
||||
if (obj is ES3Prefab prefab)
|
||||
AddPrefabToManager(prefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void AddDependenciesFromFolders()
|
||||
{
|
||||
var folders = ES3Settings.defaultSettingsScriptableObject.referenceFolders;
|
||||
|
||||
if (folders == null || folders.Length == 0)
|
||||
return;
|
||||
|
||||
var guids = AssetDatabase.FindAssets("t:Object", folders);
|
||||
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
|
||||
|
||||
if(obj != null)
|
||||
AddDependencies(obj);
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void AddDependenciesLegacy(UnityEngine.Object[] objs)
|
||||
{
|
||||
for (int i = 0; i < objs.Length; i++)
|
||||
{
|
||||
var obj = objs[i];
|
||||
|
||||
if (obj.name == "Easy Save 3 Manager")
|
||||
continue;
|
||||
|
||||
var dependencies = CollectDependenciesLegacy(obj);
|
||||
|
||||
foreach (var dependency in dependencies)
|
||||
{
|
||||
if (dependency != null)
|
||||
{
|
||||
Add(dependency);
|
||||
|
||||
// Add the prefab if it's referenced by this scene.
|
||||
if (dependency.GetType() == typeof(ES3Prefab))
|
||||
AddPrefabToManager((ES3Prefab)dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Undo.RecordObject(this, "Update Easy Save 3 Reference List");
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void AddDependencies(UnityEngine.Object obj)
|
||||
{
|
||||
AddDependencies(new UnityEngine.Object[] { obj });
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void GeneratePrefabReferences()
|
||||
{
|
||||
AddPrefabsToManager();
|
||||
foreach (var es3Prefab in prefabs)
|
||||
es3Prefab.GeneratePrefabReferences();
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public void AddPrefabsToManager()
|
||||
{
|
||||
if (ES3Settings.defaultSettingsScriptableObject.addAllPrefabsToManager)
|
||||
{
|
||||
// Clear any null values. This isn't necessary if we're not adding all prefabs to manager as the list is cleared each time.
|
||||
if (this.prefabs.RemoveAll(item => item == null) > 0)
|
||||
Undo.RecordObject(this, "Update Easy Save 3 Reference List");
|
||||
|
||||
foreach (var es3Prefab in Resources.FindObjectsOfTypeAll<ES3Prefab>())
|
||||
AddPrefabToManager(es3Prefab);
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
private void AddPrefabToManager(ES3Prefab es3Prefab)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (es3Prefab != null && EditorUtility.IsPersistent(es3Prefab))
|
||||
if(AddPrefab(es3Prefab))
|
||||
Undo.RecordObject(this, "Update Easy Save 3 Reference List");
|
||||
es3Prefab.GeneratePrefabReferences();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
#endif
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3ReferenceMgr.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3ReferenceMgr.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3502e3a1458ca7c439188a7ae6dfd808
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
721
Convention/[ES3]/Easy Save 3/Scripts/ES3ReferenceMgrBase.cs
Normal file
721
Convention/[ES3]/Easy Save 3/Scripts/ES3ReferenceMgrBase.cs
Normal file
@@ -0,0 +1,721 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
[System.Serializable]
|
||||
[DisallowMultipleComponent]
|
||||
public abstract class ES3ReferenceMgrBase : MonoBehaviour
|
||||
{
|
||||
internal object _lock = new object();
|
||||
|
||||
public const string referencePropertyName = "_ES3Ref";
|
||||
private static ES3ReferenceMgrBase _current = null;
|
||||
private static HashSet<ES3ReferenceMgrBase> mgrs = new HashSet<ES3ReferenceMgrBase>();
|
||||
#if UNITY_EDITOR
|
||||
protected static bool isEnteringPlayMode = false;
|
||||
static readonly HideFlags[] invalidHideFlags = new HideFlags[] { HideFlags.DontSave, HideFlags.DontSaveInBuild, HideFlags.DontSaveInEditor, HideFlags.HideAndDontSave };
|
||||
#endif
|
||||
|
||||
#if !UNITY_EDITOR
|
||||
[NonSerialized]
|
||||
#endif
|
||||
public List<UnityEngine.Object> excludeObjects = new List<UnityEngine.Object>();
|
||||
|
||||
private static System.Random rng;
|
||||
|
||||
[HideInInspector]
|
||||
public bool openPrefabs = false; // Whether the prefab list should be open in the Editor.
|
||||
|
||||
public List<ES3Prefab> prefabs = new List<ES3Prefab>();
|
||||
|
||||
public static ES3ReferenceMgrBase Current
|
||||
{
|
||||
get
|
||||
{
|
||||
// If the reference manager hasn't been assigned, or we've got a reference to a manager in a different scene which isn't marked as DontDestroyOnLoad, look for this scene's manager.
|
||||
if (_current == null /*|| (_current.gameObject.scene.buildIndex != -1 && _current.gameObject.scene != SceneManager.GetActiveScene())*/)
|
||||
{
|
||||
ES3ReferenceMgrBase mgr = GetManagerFromScene(SceneManager.GetActiveScene());
|
||||
if(mgr != null)
|
||||
mgrs.Add(_current = mgr);
|
||||
}
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
public static ES3ReferenceMgrBase GetManagerFromScene(Scene scene)
|
||||
{
|
||||
// This has been removed as isLoaded is false during the initial Awake().
|
||||
/*if (!scene.isLoaded)
|
||||
return null;*/
|
||||
|
||||
GameObject[] roots;
|
||||
try
|
||||
{
|
||||
roots = scene.GetRootGameObjects();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ES3ReferenceMgr mgr = null;
|
||||
|
||||
// First, look for Easy Save 3 Manager in the top-level.
|
||||
foreach (var root in roots)
|
||||
{
|
||||
if (root.name == "Easy Save 3 Manager")
|
||||
{
|
||||
mgr = root.GetComponent<ES3ReferenceMgr>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the user has moved or renamed the Easy Save 3 Manager, we need to perform a deep search.
|
||||
if (mgr == null)
|
||||
foreach (var root in roots)
|
||||
if ((mgr = root.GetComponentInChildren<ES3ReferenceMgr>()) != null)
|
||||
break;
|
||||
|
||||
return mgr;
|
||||
}
|
||||
|
||||
public bool IsInitialised { get { return idRef.Count > 0; } }
|
||||
|
||||
[SerializeField]
|
||||
public ES3IdRefDictionary idRef = new ES3IdRefDictionary();
|
||||
private ES3RefIdDictionary _refId = null;
|
||||
|
||||
public ES3RefIdDictionary refId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_refId == null)
|
||||
{
|
||||
_refId = new ES3RefIdDictionary();
|
||||
// Populate the reverse dictionary with the items from the normal dictionary.
|
||||
foreach (var kvp in idRef)
|
||||
if (kvp.Value != null)
|
||||
_refId[kvp.Value] = kvp.Key;
|
||||
}
|
||||
return _refId;
|
||||
}
|
||||
set
|
||||
{
|
||||
_refId = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ES3GlobalReferences GlobalReferences
|
||||
{
|
||||
get
|
||||
{
|
||||
return ES3GlobalReferences.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset static variables to handle disabled domain reloading.
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void Init()
|
||||
{
|
||||
_current = null;
|
||||
mgrs = new HashSet<ES3ReferenceMgrBase>();
|
||||
#if UNITY_EDITOR
|
||||
isEnteringPlayMode = false;
|
||||
#endif
|
||||
rng = null;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_current != null && _current != this)
|
||||
{
|
||||
var existing = _current;
|
||||
|
||||
/* We intentionally use Current rather than _current here, as _current may contain a reference to a manager in another scene,
|
||||
* but Current only returns the Manager for the active scene. */
|
||||
if (Current != null)
|
||||
{
|
||||
RemoveNullValues();
|
||||
|
||||
existing.Merge(this);
|
||||
Destroy(this);
|
||||
_current = existing; // Undo the call to Current, which may have set it to NULL.
|
||||
}
|
||||
}
|
||||
else
|
||||
_current = this;
|
||||
mgrs.Add(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_current == this)
|
||||
_current = null;
|
||||
mgrs.Remove(this);
|
||||
}
|
||||
|
||||
// Merges two managers, not allowing any clashes of IDs
|
||||
public void Merge(ES3ReferenceMgrBase otherMgr)
|
||||
{
|
||||
foreach (var kvp in otherMgr.idRef)
|
||||
Add(kvp.Value, kvp.Key);
|
||||
}
|
||||
|
||||
public long Get(UnityEngine.Object obj)
|
||||
{
|
||||
if (!mgrs.Contains(this))
|
||||
mgrs.Add(this);
|
||||
|
||||
foreach (var mgr in mgrs)
|
||||
{
|
||||
if (mgr == null)
|
||||
continue;
|
||||
|
||||
if (obj == null)
|
||||
return -1;
|
||||
|
||||
long id;
|
||||
if (mgr.refId.TryGetValue(obj, out id))
|
||||
return id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal UnityEngine.Object Get(long id, Type type, bool suppressWarnings=false)
|
||||
{
|
||||
if (!mgrs.Contains(this))
|
||||
mgrs.Add(this);
|
||||
|
||||
foreach (var mgr in mgrs)
|
||||
{
|
||||
if (mgr == null)
|
||||
continue;
|
||||
|
||||
if (id == -1)
|
||||
return null;
|
||||
|
||||
UnityEngine.Object obj;
|
||||
if (mgr.idRef.TryGetValue(id, out obj))
|
||||
{
|
||||
if (obj == null) // If obj has been marked as destroyed but not yet destroyed, don't return it.
|
||||
return null;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (GlobalReferences != null)
|
||||
{
|
||||
var globalRef = GlobalReferences.Get(id);
|
||||
if (globalRef != null)
|
||||
return globalRef;
|
||||
}
|
||||
|
||||
if(type != null)
|
||||
ES3Debug.LogWarning("Reference for " + type + " with ID " + id + " could not be found in Easy Save's reference manager. If you are loading objects dynamically (i.e. objects created at runtime), this warning is expected and can be ignored.", this);
|
||||
else
|
||||
ES3Debug.LogWarning("Reference with ID " + id + " could not be found in Easy Save's reference manager. If you are loading objects dynamically (i.e. objects created at runtime), this warning is expected and can be ignored.", this);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public UnityEngine.Object Get(long id, bool suppressWarnings = false)
|
||||
{
|
||||
return Get(id, null, suppressWarnings);
|
||||
}
|
||||
|
||||
public ES3Prefab GetPrefab(long id, bool suppressWarnings = false)
|
||||
{
|
||||
if (!mgrs.Contains(this))
|
||||
mgrs.Add(this);
|
||||
|
||||
foreach (var mgr in mgrs)
|
||||
{
|
||||
if (mgr == null)
|
||||
continue;
|
||||
|
||||
foreach (var prefab in mgr.prefabs)
|
||||
if (prefabs != null && prefab.prefabId == id)
|
||||
return prefab;
|
||||
}
|
||||
if (!suppressWarnings)
|
||||
ES3Debug.LogWarning("Prefab with ID " + id + " could not be found in Easy Save's reference manager. Try pressing the Refresh References button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in your scene.", this);
|
||||
return null;
|
||||
}
|
||||
|
||||
public long GetPrefab(ES3Prefab prefabToFind, bool suppressWarnings = false)
|
||||
{
|
||||
if (!mgrs.Contains(this))
|
||||
mgrs.Add(this);
|
||||
|
||||
foreach (var mgr in mgrs)
|
||||
{
|
||||
if (mgr == null)
|
||||
continue;
|
||||
|
||||
foreach (var prefab in prefabs)
|
||||
if (prefab == prefabToFind)
|
||||
return prefab.prefabId;
|
||||
}
|
||||
if (!suppressWarnings)
|
||||
ES3Debug.LogWarning("Prefab with name " + prefabToFind.name + " could not be found in Easy Save's reference manager. Try pressing the Refresh References button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in your scene.", prefabToFind);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long Add(UnityEngine.Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return -1;
|
||||
|
||||
if (!CanBeSaved(obj))
|
||||
return -1;
|
||||
|
||||
long id;
|
||||
// If it already exists in the list, do nothing.
|
||||
if (refId.TryGetValue(obj, out id))
|
||||
return id;
|
||||
|
||||
if (GlobalReferences != null)
|
||||
{
|
||||
id = GlobalReferences.GetOrAdd(obj);
|
||||
if (id != -1)
|
||||
{
|
||||
Add(obj, id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// Add the reference to the Dictionary.
|
||||
id = GetNewRefID();
|
||||
return Add(obj, id);
|
||||
}
|
||||
}
|
||||
|
||||
public long Add(UnityEngine.Object obj, long id)
|
||||
{
|
||||
if (obj == null)
|
||||
return -1;
|
||||
|
||||
if (!CanBeSaved(obj))
|
||||
return -1;
|
||||
|
||||
// If the ID is -1, auto-generate an ID.
|
||||
if (id == -1)
|
||||
id = GetNewRefID();
|
||||
// Add the reference to the Dictionary.
|
||||
lock (_lock)
|
||||
{
|
||||
idRef[id] = obj;
|
||||
if(obj != null)
|
||||
refId[obj] = id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public bool AddPrefab(ES3Prefab prefab)
|
||||
{
|
||||
if (!prefabs.Contains(prefab))
|
||||
{
|
||||
prefabs.Add(prefab);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Remove(UnityEngine.Object obj)
|
||||
{
|
||||
if (!mgrs.Contains(this))
|
||||
mgrs.Add(this);
|
||||
|
||||
foreach (var mgr in mgrs)
|
||||
{
|
||||
if (mgr == null)
|
||||
continue;
|
||||
|
||||
// Only remove from this manager if we're in the Editor.
|
||||
if (!Application.isPlaying && mgr != this)
|
||||
continue;
|
||||
|
||||
lock (mgr._lock)
|
||||
{
|
||||
mgr.refId.Remove(obj);
|
||||
// There may be multiple references with the same ID, so remove them all.
|
||||
foreach (var item in mgr.idRef.Where(kvp => kvp.Value == obj).ToList())
|
||||
mgr.idRef.Remove(item.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(long referenceID)
|
||||
{
|
||||
foreach (var mgr in mgrs)
|
||||
{
|
||||
if (mgr == null)
|
||||
continue;
|
||||
|
||||
lock (mgr._lock)
|
||||
{
|
||||
mgr.idRef.Remove(referenceID);
|
||||
// There may be multiple references with the same ID, so remove them all.
|
||||
foreach (var item in mgr.refId.Where(kvp => kvp.Value == referenceID).ToList())
|
||||
mgr.refId.Remove(item.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveNullValues()
|
||||
{
|
||||
var nullKeys = idRef.Where(pair => pair.Value == null).Select(pair => pair.Key).ToList();
|
||||
foreach (var key in nullKeys)
|
||||
idRef.Remove(key);
|
||||
}
|
||||
|
||||
public void RemoveNullOrInvalidValues()
|
||||
{
|
||||
var nullKeys = idRef.Where(pair => pair.Value == null || !CanBeSaved(pair.Value) || excludeObjects.Contains(pair.Value)).Select(pair => pair.Key).ToList();
|
||||
foreach (var key in nullKeys)
|
||||
idRef.Remove(key);
|
||||
|
||||
if (GlobalReferences != null)
|
||||
GlobalReferences.RemoveInvalidKeys();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
refId.Clear();
|
||||
idRef.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(UnityEngine.Object obj)
|
||||
{
|
||||
return refId.ContainsKey(obj);
|
||||
}
|
||||
|
||||
public bool Contains(long referenceID)
|
||||
{
|
||||
return idRef.ContainsKey(referenceID);
|
||||
}
|
||||
|
||||
public void ChangeId(long oldId, long newId)
|
||||
{
|
||||
idRef.ChangeKey(oldId, newId);
|
||||
// Empty the refId so it has to be refreshed.
|
||||
refId = null;
|
||||
}
|
||||
|
||||
internal static long GetNewRefID()
|
||||
{
|
||||
if (rng == null)
|
||||
rng = new System.Random();
|
||||
|
||||
byte[] buf = new byte[8];
|
||||
rng.NextBytes(buf);
|
||||
long longRand = BitConverter.ToInt64(buf, 0);
|
||||
|
||||
return (System.Math.Abs(longRand % (long.MaxValue - 0)) + 0);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static HashSet<UnityEngine.Object> CollectDependenciesLegacy(UnityEngine.Object obj, HashSet<UnityEngine.Object> dependencies = null, int depth = int.MinValue)
|
||||
{
|
||||
return CollectDependenciesLegacy(new UnityEngine.Object[] { obj }, dependencies, depth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Collects all top-level dependencies of an object.
|
||||
* For GameObjects, it will traverse all children.
|
||||
* For Components or ScriptableObjects, it will get all serialisable UnityEngine.Object fields/properties as dependencies.
|
||||
*/
|
||||
public static HashSet<UnityEngine.Object> CollectDependenciesLegacy(UnityEngine.Object[] objs, HashSet<UnityEngine.Object> dependencies = null, int depth = int.MinValue)
|
||||
{
|
||||
if (depth == int.MinValue)
|
||||
depth = ES3Settings.defaultSettingsScriptableObject.collectDependenciesDepth;
|
||||
|
||||
if (depth < 0)
|
||||
return dependencies;
|
||||
|
||||
if (dependencies == null)
|
||||
dependencies = new HashSet<UnityEngine.Object>();
|
||||
|
||||
foreach (var obj in objs)
|
||||
{
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
||||
var type = obj.GetType();
|
||||
|
||||
// Skip types which don't need processing
|
||||
if (type == typeof(ES3ReferenceMgr) || type == typeof(ES3AutoSaveMgr) || type == typeof(ES3AutoSave) || type == typeof(ES3InspectorInfo))
|
||||
continue;
|
||||
|
||||
// Add the prefab to the manager but don't process it. We'll use this to work out what prefabs to add to the prefabs list later.
|
||||
if (type == typeof(ES3Prefab))
|
||||
{
|
||||
dependencies.Add(obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's a GameObject, get the GameObject's Components and collect their dependencies.
|
||||
if (type == typeof(GameObject))
|
||||
{
|
||||
var go = (GameObject)obj;
|
||||
// If we've not already processed this GameObject ...
|
||||
if (dependencies.Add(go))
|
||||
{
|
||||
// Get the dependencies of each Component in the GameObject.
|
||||
CollectDependenciesLegacy(go.GetComponents<Component>(), dependencies, depth - 1);
|
||||
// Get the dependencies of each child in the GameObject.
|
||||
foreach (Transform child in go.transform)
|
||||
CollectDependenciesLegacy(child.gameObject, dependencies, depth); // Don't decrement child, as we consider this a top-level object.
|
||||
}
|
||||
}
|
||||
// Else if it's a Component or ScriptableObject, add the values of any UnityEngine.Object fields as dependencies.
|
||||
else
|
||||
CollectDependenciesFromFieldsLegacy(obj, dependencies, depth - 1);
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private static void CollectDependenciesFromFieldsLegacy(UnityEngine.Object obj, HashSet<UnityEngine.Object> dependencies, int depth)
|
||||
{
|
||||
// If we've already collected dependencies for this, do nothing.
|
||||
if (!dependencies.Add(obj))
|
||||
return;
|
||||
|
||||
if (depth == int.MinValue)
|
||||
depth = ES3Settings.defaultSettingsScriptableObject.collectDependenciesDepth;
|
||||
|
||||
if (depth < 0)
|
||||
return;
|
||||
|
||||
var type = obj.GetType();
|
||||
|
||||
if (isEnteringPlayMode && type == typeof(UnityEngine.UI.Text))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// SerializedObject is expensive, so for known classes we manually gather references.
|
||||
|
||||
if (type == typeof(Animator) || obj is Transform || type == typeof(CanvasRenderer) || type == typeof(Mesh) || type == typeof(AudioClip) || type == typeof(Rigidbody) || obj is HorizontalOrVerticalLayoutGroup)
|
||||
return;
|
||||
|
||||
if(obj is Texture)
|
||||
{
|
||||
// This ensures that Sprites which are children of the Texture are also added. In the Editor you would otherwise need to expand the Texture to add the Sprite.
|
||||
foreach(var dependency in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(UnityEditor.AssetDatabase.GetAssetPath(obj)))
|
||||
if (dependency != obj)
|
||||
dependencies.Add(dependency);
|
||||
}
|
||||
|
||||
if (obj is Graphic)
|
||||
{
|
||||
var m = (Graphic)obj;
|
||||
dependencies.Add(m.material);
|
||||
dependencies.Add(m.defaultMaterial);
|
||||
dependencies.Add(m.mainTexture);
|
||||
|
||||
if (type == typeof(Text))
|
||||
{
|
||||
var text = (Text)obj;
|
||||
dependencies.Add(text.font);
|
||||
}
|
||||
else if (type == typeof(Image))
|
||||
{
|
||||
var img = (Image)obj;
|
||||
dependencies.Add(img.sprite);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(Mesh))
|
||||
{
|
||||
if (UnityEditor.AssetDatabase.Contains(obj))
|
||||
dependencies.Add(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(Material))
|
||||
{
|
||||
var material = (Material)obj;
|
||||
var shader = material.shader;
|
||||
if (shader != null)
|
||||
{
|
||||
dependencies.Add(material.shader);
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
for (int i = 0; i < shader.GetPropertyCount(); i++)
|
||||
if (shader.GetPropertyType(i) == UnityEngine.Rendering.ShaderPropertyType.Texture)
|
||||
dependencies.Add(material.GetTexture(shader.GetPropertyName(i)));
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(MeshFilter))
|
||||
{
|
||||
dependencies.Add(((MeshFilter)obj).sharedMesh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(MeshCollider))
|
||||
{
|
||||
var mc = (MeshCollider)obj;
|
||||
dependencies.Add(mc.sharedMesh);
|
||||
dependencies.Add(mc.sharedMaterial);
|
||||
dependencies.Add(mc.attachedRigidbody);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(Camera))
|
||||
{
|
||||
var c = (Camera)obj;
|
||||
dependencies.Add(c.targetTexture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(SkinnedMeshRenderer))
|
||||
dependencies.Add(((SkinnedMeshRenderer)obj).sharedMesh); // Don't return. Let this fall through to the if(obj is renderer) call.
|
||||
else if (type == typeof(SpriteRenderer))
|
||||
dependencies.Add(((SpriteRenderer)obj).sprite); // Don't return. Let this fall through to the if(obj is renderer) call.
|
||||
else if (type == typeof(ParticleSystemRenderer))
|
||||
dependencies.Add(((ParticleSystemRenderer)obj).mesh); // Don't return. Let this fall through to the if(obj is renderer) call.
|
||||
|
||||
if (obj is Renderer)
|
||||
{
|
||||
var renderer = (Renderer)obj;
|
||||
foreach (var material in renderer.sharedMaterials)
|
||||
CollectDependenciesFromFieldsLegacy(material, dependencies, depth - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
var so = new UnityEditor.SerializedObject(obj);
|
||||
if (so == null)
|
||||
return;
|
||||
|
||||
var property = so.GetIterator();
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
// Iterate through each of this object's properties.
|
||||
while (property.NextVisible(true))
|
||||
{
|
||||
try
|
||||
{
|
||||
// If it's an array which contains UnityEngine.Objects, add them as dependencies.
|
||||
if (property.isArray && property.propertyType != UnityEditor.SerializedPropertyType.String)
|
||||
{
|
||||
for (int i = 0; i < property.arraySize; i++)
|
||||
{
|
||||
var element = property.GetArrayElementAtIndex(i);
|
||||
|
||||
// If the array contains UnityEngine.Object types, add them to the dependencies.
|
||||
if (element.propertyType == UnityEditor.SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
var elementValue = element.objectReferenceValue;
|
||||
var elementType = elementValue.GetType();
|
||||
|
||||
// If it's a GameObject, use CollectDependencies so that Components are also added.
|
||||
if (elementType == typeof(GameObject))
|
||||
CollectDependenciesLegacy(elementValue, dependencies, depth - 1);
|
||||
else
|
||||
CollectDependenciesFromFieldsLegacy(elementValue, dependencies, depth - 1);
|
||||
}
|
||||
// Otherwise this array does not contain UnityEngine.Object types, so we should stop.
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Else if it's a normal UnityEngine.Object field, add it.
|
||||
else if (property.propertyType == UnityEditor.SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
var propertyValue = property.objectReferenceValue;
|
||||
if (propertyValue == null)
|
||||
continue;
|
||||
|
||||
// If it's a GameObject, use CollectDependencies so that Components are also added.
|
||||
if (propertyValue.GetType() == typeof(GameObject))
|
||||
CollectDependenciesLegacy(propertyValue, dependencies, depth - 1);
|
||||
else
|
||||
CollectDependenciesFromFieldsLegacy(propertyValue, dependencies, depth - 1);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// Called in the Editor when this Component is added.
|
||||
private void Reset()
|
||||
{
|
||||
// Ensure that Component can only be added by going to Assets > Easy Save 3 > Add Manager to Scene.
|
||||
if (gameObject.name != "Easy Save 3 Manager")
|
||||
{
|
||||
UnityEditor.EditorUtility.DisplayDialog("Cannot add ES3ReferenceMgr directly", "Please go to 'Tools > Easy Save 3 > Add Manager to Scene' to add an Easy Save 3 Manager to your scene.", "Ok");
|
||||
DestroyImmediate(this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static bool CanBeSaved(UnityEngine.Object obj)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (obj == null)
|
||||
return true;
|
||||
|
||||
foreach (var flag in invalidHideFlags)
|
||||
if ((obj.hideFlags & flag) != 0 && obj.hideFlags != HideFlags.HideInHierarchy && obj.hideFlags != HideFlags.HideInInspector && obj.hideFlags != HideFlags.NotEditable)
|
||||
if (!(obj is Mesh || obj is Material))
|
||||
return false;
|
||||
|
||||
// Exclude the Easy Save 3 Manager, and all components attached to it.
|
||||
if (obj.name == "Easy Save 3 Manager")
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class ES3IdRefDictionary : ES3SerializableDictionary<long, UnityEngine.Object>
|
||||
{
|
||||
protected override bool KeysAreEqual(long a, long b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
protected override bool ValuesAreEqual(UnityEngine.Object a, UnityEngine.Object b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[System.Serializable]
|
||||
public class ES3RefIdDictionary : ES3SerializableDictionary<UnityEngine.Object, long>
|
||||
{
|
||||
protected override bool KeysAreEqual(UnityEngine.Object a, UnityEngine.Object b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
protected override bool ValuesAreEqual(long a, long b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6f9bc25ccfaad643abbbac47d623959
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
792
Convention/[ES3]/Easy Save 3/Scripts/ES3Reflection.cs
Normal file
792
Convention/[ES3]/Easy Save 3/Scripts/ES3Reflection.cs
Normal file
@@ -0,0 +1,792 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
using ES3Types;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public static class ES3Reflection
|
||||
{
|
||||
public const string memberFieldPrefix = "m_";
|
||||
public const string componentTagFieldName = "tag";
|
||||
public const string componentNameFieldName = "name";
|
||||
public static readonly string[] excludedPropertyNames = new string[] { "runInEditMode", "useGUILayout", "hideFlags" };
|
||||
|
||||
public static readonly Type serializableAttributeType = typeof(System.SerializableAttribute);
|
||||
public static readonly Type serializeFieldAttributeType = typeof(SerializeField);
|
||||
public static readonly Type obsoleteAttributeType = typeof(System.ObsoleteAttribute);
|
||||
public static readonly Type nonSerializedAttributeType = typeof(System.NonSerializedAttribute);
|
||||
public static readonly Type es3SerializableAttributeType = typeof(ES3Serializable);
|
||||
public static readonly Type es3NonSerializableAttributeType = typeof(ES3NonSerializable);
|
||||
|
||||
public static Type[] EmptyTypes = new Type[0];
|
||||
|
||||
private static Assembly[] _assemblies = null;
|
||||
private static Assembly[] Assemblies
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
if (_assemblies == null)
|
||||
{
|
||||
var assemblyNames = new ES3Settings().assemblyNames;
|
||||
var assemblyList = new List<Assembly>();
|
||||
|
||||
/* We only use a try/catch block for UWP because exceptions can be disabled on some other platforms (e.g. WebGL), but the non-try/catch method doesn't work on UWP */
|
||||
#if NETFX_CORE
|
||||
for (int i = 0; i < assemblyNames.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.Load(new AssemblyName(assemblyNames[i]));
|
||||
if (assembly != null)
|
||||
assemblyList.Add(assembly);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#else
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// This try/catch block is here to catch errors such as assemblies containing double-byte characters in their path.
|
||||
// This obviously won't work if exceptions are disabled.
|
||||
try
|
||||
{
|
||||
if (assemblyNames.Contains(assembly.GetName().Name))
|
||||
assemblyList.Add(assembly);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
#endif
|
||||
_assemblies = assemblyList.ToArray();
|
||||
}
|
||||
return _assemblies;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the element type of a collection or array.
|
||||
* Returns null if type is not a collection type.
|
||||
*/
|
||||
public static Type[] GetElementTypes(Type type)
|
||||
{
|
||||
if (IsGenericType(type))
|
||||
return ES3Reflection.GetGenericArguments(type);
|
||||
else if (type.IsArray)
|
||||
return new Type[] { ES3Reflection.GetElementType(type) };
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<FieldInfo> GetSerializableFields(Type type, List<FieldInfo> serializableFields = null, bool safe = true, string[] memberNames = null, BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
{
|
||||
if (type == null)
|
||||
return new List<FieldInfo>();
|
||||
|
||||
var fields = type.GetFields(bindings);
|
||||
|
||||
if (serializableFields == null)
|
||||
serializableFields = new List<FieldInfo>();
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var fieldName = field.Name;
|
||||
|
||||
// If a members array was provided as a parameter, only include the field if it's in the array.
|
||||
if (memberNames != null)
|
||||
if (!memberNames.Contains(fieldName))
|
||||
continue;
|
||||
|
||||
var fieldType = field.FieldType;
|
||||
|
||||
if (AttributeIsDefined(field, es3SerializableAttributeType))
|
||||
{
|
||||
serializableFields.Add(field);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AttributeIsDefined(field, es3NonSerializableAttributeType))
|
||||
continue;
|
||||
|
||||
if (safe)
|
||||
{
|
||||
// If the field is private, only serialize it if it's explicitly marked as serializable.
|
||||
if (!field.IsPublic && !AttributeIsDefined(field, serializeFieldAttributeType))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude const or readonly fields.
|
||||
if (field.IsLiteral || field.IsInitOnly)
|
||||
continue;
|
||||
|
||||
// Don't store fields whose type is the same as the class the field is housed in unless it's stored by reference (to prevent cyclic references)
|
||||
if (fieldType == type && !IsAssignableFrom(typeof(UnityEngine.Object), fieldType))
|
||||
continue;
|
||||
|
||||
// If property is marked as obsolete or non-serialized, don't serialize it.
|
||||
if (AttributeIsDefined(field, nonSerializedAttributeType) || AttributeIsDefined(field, obsoleteAttributeType))
|
||||
continue;
|
||||
|
||||
if (!TypeIsSerializable(field.FieldType))
|
||||
continue;
|
||||
|
||||
// Don't serialize member fields.
|
||||
if (safe && fieldName.StartsWith(memberFieldPrefix) && field.DeclaringType.Namespace != null && field.DeclaringType.Namespace.Contains("UnityEngine"))
|
||||
continue;
|
||||
|
||||
serializableFields.Add(field);
|
||||
}
|
||||
|
||||
var baseType = BaseType(type);
|
||||
if (baseType != null && baseType != typeof(System.Object) && baseType != typeof(UnityEngine.Object))
|
||||
GetSerializableFields(BaseType(type), serializableFields, safe, memberNames);
|
||||
|
||||
return serializableFields;
|
||||
}
|
||||
|
||||
public static List<PropertyInfo> GetSerializableProperties(Type type, List<PropertyInfo> serializableProperties = null, bool safe = true, string[] memberNames = null, BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
{
|
||||
bool isComponent = IsAssignableFrom(typeof(UnityEngine.Component), type);
|
||||
|
||||
// Only get private properties if we're not getting properties safely.
|
||||
if (!safe)
|
||||
bindings = bindings | BindingFlags.NonPublic;
|
||||
|
||||
var properties = type.GetProperties(bindings);
|
||||
|
||||
if (serializableProperties == null)
|
||||
serializableProperties = new List<PropertyInfo>();
|
||||
|
||||
foreach (var p in properties)
|
||||
{
|
||||
if (AttributeIsDefined(p, es3SerializableAttributeType))
|
||||
{
|
||||
serializableProperties.Add(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AttributeIsDefined(p, es3NonSerializableAttributeType))
|
||||
continue;
|
||||
|
||||
var propertyName = p.Name;
|
||||
|
||||
if (excludedPropertyNames.Contains(propertyName))
|
||||
continue;
|
||||
|
||||
// If a members array was provided as a parameter, only include the property if it's in the array.
|
||||
if (memberNames != null)
|
||||
if (!memberNames.Contains(propertyName))
|
||||
continue;
|
||||
|
||||
if (safe)
|
||||
{
|
||||
// If safe serialization is enabled, only get properties which are explicitly marked as serializable.
|
||||
if (!AttributeIsDefined(p, serializeFieldAttributeType) && !AttributeIsDefined(p, es3SerializableAttributeType))
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyType = p.PropertyType;
|
||||
|
||||
// Don't store properties whose type is the same as the class the property is housed in unless it's stored by reference (to prevent cyclic references)
|
||||
if (propertyType == type && !IsAssignableFrom(typeof(UnityEngine.Object), propertyType))
|
||||
continue;
|
||||
|
||||
if (!p.CanRead || !p.CanWrite)
|
||||
continue;
|
||||
|
||||
// Only support properties with indexing if they're an array.
|
||||
if (p.GetIndexParameters().Length != 0 && !propertyType.IsArray)
|
||||
continue;
|
||||
|
||||
// Check that the type of the property is one which we can serialize.
|
||||
// Also check whether an ES3Type exists for it.
|
||||
if (!TypeIsSerializable(propertyType))
|
||||
continue;
|
||||
|
||||
// Ignore certain properties on components.
|
||||
if (isComponent)
|
||||
{
|
||||
// Ignore properties which are accessors for GameObject fields.
|
||||
if (propertyName == componentTagFieldName || propertyName == componentNameFieldName)
|
||||
continue;
|
||||
}
|
||||
|
||||
// If property is marked as obsolete or non-serialized, don't serialize it.
|
||||
if (AttributeIsDefined(p, obsoleteAttributeType) || AttributeIsDefined(p, nonSerializedAttributeType))
|
||||
continue;
|
||||
|
||||
serializableProperties.Add(p);
|
||||
}
|
||||
|
||||
var baseType = BaseType(type);
|
||||
if (baseType != null && baseType != typeof(System.Object))
|
||||
GetSerializableProperties(baseType, serializableProperties, safe, memberNames);
|
||||
|
||||
return serializableProperties;
|
||||
}
|
||||
|
||||
public static bool TypeIsSerializable(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
if (AttributeIsDefined(type, es3NonSerializableAttributeType))
|
||||
return false;
|
||||
|
||||
if (IsPrimitive(type) || IsValueType(type) || IsAssignableFrom(typeof(UnityEngine.Component), type) || IsAssignableFrom(typeof(UnityEngine.ScriptableObject), type))
|
||||
return true;
|
||||
|
||||
var es3Type = ES3TypeMgr.GetOrCreateES3Type(type, false);
|
||||
|
||||
if (es3Type != null && !es3Type.isUnsupported)
|
||||
return true;
|
||||
|
||||
if (TypeIsArray(type))
|
||||
{
|
||||
if (TypeIsSerializable(type.GetElementType()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
var genericArgs = type.GetGenericArguments();
|
||||
for (int i = 0; i < genericArgs.Length; i++)
|
||||
if (!TypeIsSerializable(genericArgs[i]))
|
||||
return false;
|
||||
|
||||
/*if (HasParameterlessConstructor(type))
|
||||
return true;*/
|
||||
return false;
|
||||
}
|
||||
|
||||
public static System.Object CreateInstance(Type type)
|
||||
{
|
||||
if (IsAssignableFrom(typeof(UnityEngine.Component), type))
|
||||
return ES3ComponentType.CreateComponent(type);
|
||||
else if (IsAssignableFrom(typeof(ScriptableObject), type))
|
||||
return ScriptableObject.CreateInstance(type);
|
||||
else if (ES3Reflection.HasParameterlessConstructor(type))
|
||||
return Activator.CreateInstance(type);
|
||||
else
|
||||
{
|
||||
#if NETFX_CORE
|
||||
throw new NotSupportedException($"Cannot create an instance of {type} because it does not have a parameterless constructor, which is required on Universal Windows platform.");
|
||||
#else
|
||||
return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Object CreateInstance(Type type, params object[] args)
|
||||
{
|
||||
if (IsAssignableFrom(typeof(UnityEngine.Component), type))
|
||||
return ES3ComponentType.CreateComponent(type);
|
||||
else if (IsAssignableFrom(typeof(ScriptableObject), type))
|
||||
return ScriptableObject.CreateInstance(type);
|
||||
return Activator.CreateInstance(type, args);
|
||||
}
|
||||
|
||||
public static Array ArrayCreateInstance(Type type, int length)
|
||||
{
|
||||
return Array.CreateInstance(type, new int[] { length });
|
||||
}
|
||||
|
||||
public static Array ArrayCreateInstance(Type type, int[] dimensions)
|
||||
{
|
||||
return Array.CreateInstance(type, dimensions);
|
||||
}
|
||||
|
||||
public static Type MakeGenericType(Type type, Type genericParam)
|
||||
{
|
||||
return type.MakeGenericType(genericParam);
|
||||
}
|
||||
|
||||
public static ES3ReflectedMember[] GetSerializableMembers(Type type, bool safe = true, string[] memberNames = null)
|
||||
{
|
||||
if (type == null)
|
||||
return new ES3ReflectedMember[0];
|
||||
|
||||
var fieldInfos = GetSerializableFields(type, new List<FieldInfo>(), safe, memberNames);
|
||||
var propertyInfos = GetSerializableProperties(type, new List<PropertyInfo>(), safe, memberNames);
|
||||
var reflectedFields = new ES3ReflectedMember[fieldInfos.Count + propertyInfos.Count];
|
||||
|
||||
for (int i = 0; i < fieldInfos.Count; i++)
|
||||
reflectedFields[i] = new ES3ReflectedMember(fieldInfos[i]);
|
||||
for (int i = 0; i < propertyInfos.Count; i++)
|
||||
reflectedFields[i + fieldInfos.Count] = new ES3ReflectedMember(propertyInfos[i]);
|
||||
|
||||
return reflectedFields;
|
||||
}
|
||||
|
||||
public static ES3ReflectedMember GetES3ReflectedProperty(Type type, string propertyName)
|
||||
{
|
||||
var propertyInfo = ES3Reflection.GetProperty(type, propertyName);
|
||||
return new ES3ReflectedMember(propertyInfo);
|
||||
}
|
||||
|
||||
public static ES3ReflectedMember GetES3ReflectedMember(Type type, string fieldName)
|
||||
{
|
||||
var fieldInfo = ES3Reflection.GetField(type, fieldName);
|
||||
return new ES3ReflectedMember(fieldInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds all classes of a specific type, and then returns an instance of each.
|
||||
* Ignores classes which can't be instantiated (i.e. abstract classes, those without parameterless constructors).
|
||||
*/
|
||||
public static IList<T> GetInstances<T>()
|
||||
{
|
||||
var instances = new List<T>();
|
||||
foreach (var assembly in Assemblies)
|
||||
foreach (var type in assembly.GetTypes())
|
||||
if (IsAssignableFrom(typeof(T), type) && ES3Reflection.HasParameterlessConstructor(type) && !ES3Reflection.IsAbstract(type))
|
||||
instances.Add((T)Activator.CreateInstance(type));
|
||||
return instances;
|
||||
}
|
||||
|
||||
public static IList<Type> GetDerivedTypes(Type derivedType)
|
||||
{
|
||||
return
|
||||
(
|
||||
from assembly in Assemblies
|
||||
from type in assembly.GetTypes()
|
||||
where IsAssignableFrom(derivedType, type)
|
||||
select type
|
||||
).ToList();
|
||||
}
|
||||
|
||||
public static bool IsAssignableFrom(Type a, Type b)
|
||||
{
|
||||
return a.IsAssignableFrom(b);
|
||||
}
|
||||
|
||||
public static Type GetGenericTypeDefinition(Type type)
|
||||
{
|
||||
return type.GetGenericTypeDefinition();
|
||||
}
|
||||
|
||||
public static Type[] GetGenericArguments(Type type)
|
||||
{
|
||||
return type.GetGenericArguments();
|
||||
}
|
||||
|
||||
public static int GetArrayRank(Type type)
|
||||
{
|
||||
return type.GetArrayRank();
|
||||
}
|
||||
|
||||
public static string GetAssemblyQualifiedName(Type type)
|
||||
{
|
||||
return type.AssemblyQualifiedName;
|
||||
}
|
||||
|
||||
public static ES3ReflectedMethod GetMethod(Type type, string methodName, Type[] genericParameters, Type[] parameterTypes)
|
||||
{
|
||||
return new ES3ReflectedMethod(type, methodName, genericParameters, parameterTypes);
|
||||
}
|
||||
|
||||
public static bool TypeIsArray(Type type)
|
||||
{
|
||||
return type.IsArray;
|
||||
}
|
||||
|
||||
public static Type GetElementType(Type type)
|
||||
{
|
||||
return type.GetElementType();
|
||||
}
|
||||
|
||||
#if NETFX_CORE
|
||||
public static bool IsAbstract(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsAbstract;
|
||||
}
|
||||
|
||||
public static bool IsInterface(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsInterface;
|
||||
}
|
||||
|
||||
public static bool IsGenericType(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsGenericType;
|
||||
}
|
||||
|
||||
public static bool IsValueType(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsValueType;
|
||||
}
|
||||
|
||||
public static bool IsEnum(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsEnum;
|
||||
}
|
||||
|
||||
public static bool HasParameterlessConstructor(Type type)
|
||||
{
|
||||
return GetParameterlessConstructor(type) != null;
|
||||
}
|
||||
|
||||
public static ConstructorInfo GetParameterlessConstructor(Type type)
|
||||
{
|
||||
foreach (var cInfo in type.GetTypeInfo().DeclaredConstructors)
|
||||
if (!cInfo.IsStatic && cInfo.GetParameters().Length == 0)
|
||||
return cInfo;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetShortAssemblyQualifiedName(Type type)
|
||||
{
|
||||
if (IsPrimitive (type))
|
||||
return type.ToString ();
|
||||
return type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name;
|
||||
}
|
||||
|
||||
public static PropertyInfo GetProperty(Type type, string propertyName)
|
||||
{
|
||||
var property = type.GetTypeInfo().GetDeclaredProperty(propertyName);
|
||||
if (property == null && type.BaseType != typeof(object))
|
||||
return GetProperty(type.BaseType, propertyName);
|
||||
return property;
|
||||
}
|
||||
|
||||
public static FieldInfo GetField(Type type, string fieldName)
|
||||
{
|
||||
return type.GetTypeInfo().GetDeclaredField(fieldName);
|
||||
}
|
||||
|
||||
public static MethodInfo[] GetMethods(Type type, string methodName)
|
||||
{
|
||||
return type.GetTypeInfo().GetDeclaredMethods(methodName);
|
||||
}
|
||||
|
||||
public static bool IsPrimitive(Type type)
|
||||
{
|
||||
return (type.GetTypeInfo().IsPrimitive || type == typeof(string) || type == typeof(decimal));
|
||||
}
|
||||
|
||||
public static bool AttributeIsDefined(MemberInfo info, Type attributeType)
|
||||
{
|
||||
var attributes = info.GetCustomAttributes(attributeType, true);
|
||||
foreach(var attribute in attributes)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool AttributeIsDefined(Type type, Type attributeType)
|
||||
{
|
||||
var attributes = type.GetTypeInfo().GetCustomAttributes(attributeType, true);
|
||||
foreach(var attribute in attributes)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ImplementsInterface(Type type, Type interfaceType)
|
||||
{
|
||||
return type.GetTypeInfo().ImplementedInterfaces.Contains(interfaceType);
|
||||
}
|
||||
|
||||
public static Type BaseType(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().BaseType;
|
||||
}
|
||||
#else
|
||||
public static bool IsAbstract(Type type)
|
||||
{
|
||||
return type.IsAbstract;
|
||||
}
|
||||
|
||||
public static bool IsInterface(Type type)
|
||||
{
|
||||
return type.IsInterface;
|
||||
}
|
||||
|
||||
public static bool IsGenericType(Type type)
|
||||
{
|
||||
return type.IsGenericType;
|
||||
}
|
||||
|
||||
public static bool IsValueType(Type type)
|
||||
{
|
||||
return type.IsValueType;
|
||||
}
|
||||
|
||||
public static bool IsEnum(Type type)
|
||||
{
|
||||
return type.IsEnum;
|
||||
}
|
||||
|
||||
public static bool HasParameterlessConstructor(Type type)
|
||||
{
|
||||
if (IsValueType(type) || GetParameterlessConstructor(type) != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ConstructorInfo GetParameterlessConstructor(Type type)
|
||||
{
|
||||
var constructors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
foreach (var constructor in constructors)
|
||||
if (constructor.GetParameters().Length == 0)
|
||||
return constructor;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetShortAssemblyQualifiedName(Type type)
|
||||
{
|
||||
if (IsPrimitive(type))
|
||||
return type.ToString();
|
||||
return type.FullName + "," + type.Assembly.GetName().Name;
|
||||
}
|
||||
|
||||
public static PropertyInfo GetProperty(Type type, string propertyName)
|
||||
{
|
||||
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (property == null && BaseType(type) != typeof(object))
|
||||
return GetProperty(BaseType(type), propertyName);
|
||||
return property;
|
||||
}
|
||||
|
||||
public static FieldInfo GetField(Type type, string fieldName)
|
||||
{
|
||||
var field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field == null && BaseType(type) != typeof(object))
|
||||
return GetField(BaseType(type), fieldName);
|
||||
return field;
|
||||
}
|
||||
|
||||
public static MethodInfo[] GetMethods(Type type, string methodName)
|
||||
{
|
||||
return type.GetMethods().Where(t => t.Name == methodName).ToArray();
|
||||
}
|
||||
|
||||
public static bool IsPrimitive(Type type)
|
||||
{
|
||||
return (type.IsPrimitive || type == typeof(string) || type == typeof(decimal));
|
||||
}
|
||||
|
||||
public static bool AttributeIsDefined(MemberInfo info, Type attributeType)
|
||||
{
|
||||
return Attribute.IsDefined(info, attributeType, true);
|
||||
}
|
||||
|
||||
public static bool AttributeIsDefined(Type type, Type attributeType)
|
||||
{
|
||||
return type.IsDefined(attributeType, true);
|
||||
}
|
||||
|
||||
public static bool ImplementsInterface(Type type, Type interfaceType)
|
||||
{
|
||||
return (type.GetInterface(interfaceType.Name) != null);
|
||||
}
|
||||
|
||||
public static Type BaseType(Type type)
|
||||
{
|
||||
return type.BaseType;
|
||||
}
|
||||
|
||||
public static Type GetType(string typeString)
|
||||
{
|
||||
switch (typeString)
|
||||
{
|
||||
case "bool":
|
||||
return typeof(bool);
|
||||
case "byte":
|
||||
return typeof(byte);
|
||||
case "sbyte":
|
||||
return typeof(sbyte);
|
||||
case "char":
|
||||
return typeof(char);
|
||||
case "decimal":
|
||||
return typeof(decimal);
|
||||
case "double":
|
||||
return typeof(double);
|
||||
case "float":
|
||||
return typeof(float);
|
||||
case "int":
|
||||
return typeof(int);
|
||||
case "uint":
|
||||
return typeof(uint);
|
||||
case "long":
|
||||
return typeof(long);
|
||||
case "ulong":
|
||||
return typeof(ulong);
|
||||
case "short":
|
||||
return typeof(short);
|
||||
case "ushort":
|
||||
return typeof(ushort);
|
||||
case "string":
|
||||
return typeof(string);
|
||||
case "Vector2":
|
||||
return typeof(Vector2);
|
||||
case "Vector3":
|
||||
return typeof(Vector3);
|
||||
case "Vector4":
|
||||
return typeof(Vector4);
|
||||
case "Color":
|
||||
return typeof(Color);
|
||||
case "Transform":
|
||||
return typeof(Transform);
|
||||
case "Component":
|
||||
return typeof(UnityEngine.Component);
|
||||
case "GameObject":
|
||||
return typeof(GameObject);
|
||||
case "MeshFilter":
|
||||
return typeof(MeshFilter);
|
||||
case "Material":
|
||||
return typeof(Material);
|
||||
case "Texture2D":
|
||||
return typeof(Texture2D);
|
||||
case "UnityEngine.Object":
|
||||
return typeof(UnityEngine.Object);
|
||||
case "System.Object":
|
||||
return typeof(object);
|
||||
default:
|
||||
return Type.GetType(typeString);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetTypeString(Type type)
|
||||
{
|
||||
if (type == typeof(bool))
|
||||
return "bool";
|
||||
else if (type == typeof(byte))
|
||||
return "byte";
|
||||
else if (type == typeof(sbyte))
|
||||
return "sbyte";
|
||||
else if (type == typeof(char))
|
||||
return "char";
|
||||
else if (type == typeof(decimal))
|
||||
return "decimal";
|
||||
else if (type == typeof(double))
|
||||
return "double";
|
||||
else if (type == typeof(float))
|
||||
return "float";
|
||||
else if (type == typeof(int))
|
||||
return "int";
|
||||
else if (type == typeof(uint))
|
||||
return "uint";
|
||||
else if (type == typeof(long))
|
||||
return "long";
|
||||
else if (type == typeof(ulong))
|
||||
return "ulong";
|
||||
else if (type == typeof(short))
|
||||
return "short";
|
||||
else if (type == typeof(ushort))
|
||||
return "ushort";
|
||||
else if (type == typeof(string))
|
||||
return "string";
|
||||
else if (type == typeof(Vector2))
|
||||
return "Vector2";
|
||||
else if (type == typeof(Vector3))
|
||||
return "Vector3";
|
||||
else if (type == typeof(Vector4))
|
||||
return "Vector4";
|
||||
else if (type == typeof(Color))
|
||||
return "Color";
|
||||
else if (type == typeof(Transform))
|
||||
return "Transform";
|
||||
else if (type == typeof(UnityEngine.Component))
|
||||
return "Component";
|
||||
else if (type == typeof(GameObject))
|
||||
return "GameObject";
|
||||
else if (type == typeof(MeshFilter))
|
||||
return "MeshFilter";
|
||||
else if (type == typeof(Material))
|
||||
return "Material";
|
||||
else if (type == typeof(Texture2D))
|
||||
return "Texture2D";
|
||||
else if (type == typeof(UnityEngine.Object))
|
||||
return "UnityEngine.Object";
|
||||
else if (type == typeof(object))
|
||||
return "System.Object";
|
||||
else
|
||||
return GetShortAssemblyQualifiedName(type);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allows us to use FieldInfo and PropertyInfo interchangably.
|
||||
*/
|
||||
public struct ES3ReflectedMember
|
||||
{
|
||||
// The FieldInfo or PropertyInfo for this field.
|
||||
private FieldInfo fieldInfo;
|
||||
private PropertyInfo propertyInfo;
|
||||
public bool isProperty;
|
||||
|
||||
public bool IsNull { get { return fieldInfo == null && propertyInfo == null; } }
|
||||
public string Name { get { return (isProperty ? propertyInfo.Name : fieldInfo.Name); } }
|
||||
public Type MemberType { get { return (isProperty ? propertyInfo.PropertyType : fieldInfo.FieldType); } }
|
||||
public bool IsPublic { get { return (isProperty ? (propertyInfo.GetGetMethod(true).IsPublic && propertyInfo.GetSetMethod(true).IsPublic) : fieldInfo.IsPublic); } }
|
||||
public bool IsProtected { get { return (isProperty ? (propertyInfo.GetGetMethod(true).IsFamily) : fieldInfo.IsFamily); } }
|
||||
public bool IsStatic { get { return (isProperty ? (propertyInfo.GetGetMethod(true).IsStatic) : fieldInfo.IsStatic); } }
|
||||
|
||||
public ES3ReflectedMember(System.Object fieldPropertyInfo)
|
||||
{
|
||||
if (fieldPropertyInfo == null)
|
||||
{
|
||||
this.propertyInfo = null;
|
||||
this.fieldInfo = null;
|
||||
isProperty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
isProperty = ES3Reflection.IsAssignableFrom(typeof(PropertyInfo), fieldPropertyInfo.GetType());
|
||||
if (isProperty)
|
||||
{
|
||||
this.propertyInfo = (PropertyInfo)fieldPropertyInfo;
|
||||
this.fieldInfo = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fieldInfo = (FieldInfo)fieldPropertyInfo;
|
||||
this.propertyInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue(System.Object obj, System.Object value)
|
||||
{
|
||||
if (isProperty)
|
||||
propertyInfo.SetValue(obj, value, null);
|
||||
else
|
||||
fieldInfo.SetValue(obj, value);
|
||||
}
|
||||
|
||||
public System.Object GetValue(System.Object obj)
|
||||
{
|
||||
if (isProperty)
|
||||
return propertyInfo.GetValue(obj, null);
|
||||
else
|
||||
return fieldInfo.GetValue(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public class ES3ReflectedMethod
|
||||
{
|
||||
private MethodInfo method;
|
||||
|
||||
public ES3ReflectedMethod(Type type, string methodName, Type[] genericParameters, Type[] parameterTypes)
|
||||
{
|
||||
MethodInfo nonGenericMethod = type.GetMethod(methodName, parameterTypes);
|
||||
this.method = nonGenericMethod.MakeGenericMethod(genericParameters);
|
||||
}
|
||||
|
||||
public ES3ReflectedMethod(Type type, string methodName, Type[] genericParameters, Type[] parameterTypes, BindingFlags bindingAttr)
|
||||
{
|
||||
MethodInfo nonGenericMethod = type.GetMethod(methodName, bindingAttr, null, parameterTypes, null);
|
||||
this.method = nonGenericMethod.MakeGenericMethod(genericParameters);
|
||||
}
|
||||
|
||||
public object Invoke(object obj, object[] parameters = null)
|
||||
{
|
||||
return method.Invoke(obj, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Reflection.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Reflection.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbcab9b92de6423439971180b39e3fcf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
[System.Serializable]
|
||||
public abstract class ES3SerializableDictionary<TKey,TVal> : Dictionary<TKey,TVal>, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField]
|
||||
private List<TKey> _Keys;
|
||||
[SerializeField]
|
||||
private List<TVal> _Values;
|
||||
|
||||
protected abstract bool KeysAreEqual(TKey a, TKey b);
|
||||
protected abstract bool ValuesAreEqual(TVal a, TVal b);
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
_Keys = new List<TKey>();
|
||||
_Values = new List<TVal>();
|
||||
foreach(KeyValuePair<TKey, TVal> pair in this)
|
||||
{
|
||||
try
|
||||
{
|
||||
_Keys.Add(pair.Key);
|
||||
_Values.Add(pair.Value);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// load dictionary from lists
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
// There are some situations where Unity will not get the serialized data correctly, returning null.
|
||||
// In this case we don't want to change anything, otherwise we'll lose the data entirely.
|
||||
if (_Keys == null || _Values == null)
|
||||
return;
|
||||
|
||||
if(_Keys.Count != _Values.Count)
|
||||
throw new System.Exception(string.Format("Key count is different to value count after deserialising dictionary."));
|
||||
|
||||
this.Clear();
|
||||
|
||||
for (int i = 0; i < _Keys.Count; i++)
|
||||
{
|
||||
if (_Keys[i] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Add(_Keys[i], _Values[i]);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
_Keys = null;
|
||||
_Values = null;
|
||||
}
|
||||
|
||||
public int RemoveNullValues()
|
||||
{
|
||||
var nullKeys = this.Where(pair => pair.Value == null)
|
||||
.Select(pair => pair.Key)
|
||||
.ToList();
|
||||
foreach (var nullKey in nullKeys)
|
||||
Remove(nullKey);
|
||||
return nullKeys.Count;
|
||||
}
|
||||
|
||||
// Changes the key of a value without changing it's position in the underlying Lists.
|
||||
// Mainly used in the Editor where position might otherwise change while the user is editing it.
|
||||
// Returns true if a change was made.
|
||||
public bool ChangeKey(TKey oldKey, TKey newKey)
|
||||
{
|
||||
if(KeysAreEqual(oldKey, newKey))
|
||||
return false;
|
||||
|
||||
var val = this [oldKey];
|
||||
Remove(oldKey);
|
||||
this [newKey] = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c987bbda255311b478c1818e9e3819aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
307
Convention/[ES3]/Easy Save 3/Scripts/ES3Spreadsheet.cs
Normal file
307
Convention/[ES3]/Easy Save 3/Scripts/ES3Spreadsheet.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using ES3Internal;
|
||||
|
||||
#if UNITY_VISUAL_SCRIPTING
|
||||
[Unity.VisualScripting.IncludeInSettings(true)]
|
||||
#elif BOLT_VISUAL_SCRIPTING
|
||||
[Ludiq.IncludeInSettings(true)]
|
||||
#endif
|
||||
public class ES3Spreadsheet
|
||||
{
|
||||
private int cols = 0;
|
||||
private int rows = 0;
|
||||
private Dictionary<Index, string> cells = new Dictionary<Index, string>();
|
||||
|
||||
private const string QUOTE = "\"";
|
||||
private const char QUOTE_CHAR = '"';
|
||||
private const char COMMA_CHAR = ',';
|
||||
private const char NEWLINE_CHAR = '\n';
|
||||
private const string ESCAPED_QUOTE = "\"\"";
|
||||
private static char[] CHARS_TO_ESCAPE = { ',', '"', '\n', ' ' };
|
||||
|
||||
public int ColumnCount
|
||||
{
|
||||
get{ return cols; }
|
||||
}
|
||||
|
||||
public int RowCount
|
||||
{
|
||||
get{ return rows; }
|
||||
}
|
||||
|
||||
public int GetColumnLength(int col)
|
||||
{
|
||||
if (col >= cols)
|
||||
return 0;
|
||||
|
||||
int maxRow = -1;
|
||||
|
||||
foreach(var index in cells.Keys)
|
||||
if (index.col == col && index.row > maxRow)
|
||||
maxRow = index.row;
|
||||
|
||||
return maxRow+1;
|
||||
}
|
||||
|
||||
public int GetRowLength(int row)
|
||||
{
|
||||
if (row >= rows)
|
||||
return 0;
|
||||
|
||||
int maxCol = -1;
|
||||
|
||||
foreach (var index in cells.Keys)
|
||||
if (index.row == row && index.col > maxCol)
|
||||
maxCol = index.col;
|
||||
|
||||
return maxCol + 1;
|
||||
}
|
||||
|
||||
public void SetCell(int col, int row, object value)
|
||||
{
|
||||
var type = value.GetType();
|
||||
|
||||
// If we're writing a string, add it without formatting.
|
||||
if (type == typeof(string))
|
||||
{
|
||||
SetCellString(col, row, (string)value);
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = new ES3Settings();
|
||||
if (ES3Reflection.IsPrimitive(type))
|
||||
SetCellString(col, row, value.ToString());
|
||||
else
|
||||
SetCellString(col, row, settings.encoding.GetString(ES3.Serialize(value, ES3TypeMgr.GetOrCreateES3Type(type))));
|
||||
|
||||
// Expand the spreadsheet if necessary.
|
||||
if (col >= cols)
|
||||
cols = (col + 1);
|
||||
if (row >= rows)
|
||||
rows = (row + 1);
|
||||
}
|
||||
|
||||
private void SetCellString(int col, int row, string value)
|
||||
{
|
||||
cells [new Index (col, row)] = value;
|
||||
|
||||
// Expand the spreadsheet if necessary.
|
||||
if(col >= cols)
|
||||
cols = (col+1);
|
||||
if (row >= rows)
|
||||
rows = (row + 1);
|
||||
}
|
||||
|
||||
|
||||
// Don't create non-generic version of this. Generic parameter is necessary as no type data is stored in the CSV file.
|
||||
public T GetCell<T>(int col, int row)
|
||||
{
|
||||
var val = GetCell(typeof(T), col, row);
|
||||
|
||||
if (val == null)
|
||||
return default(T);
|
||||
return (T)val;
|
||||
}
|
||||
|
||||
public object GetCell(System.Type type, int col, int row)
|
||||
{
|
||||
string value;
|
||||
|
||||
if (col >= cols || row >= rows)
|
||||
throw new System.IndexOutOfRangeException("Cell (" + col + ", " + row + ") is out of bounds of spreadsheet (" + cols + ", " + rows + ").");
|
||||
|
||||
if (!cells.TryGetValue(new Index(col, row), out value) || value == null)
|
||||
return null;
|
||||
|
||||
// If we're loading a string, simply return the string value.
|
||||
if (type == typeof(string))
|
||||
{
|
||||
var str = (object)value;
|
||||
return str;
|
||||
}
|
||||
|
||||
var settings = new ES3Settings();
|
||||
return ES3.Deserialize(ES3TypeMgr.GetOrCreateES3Type(type, true), settings.encoding.GetBytes(value), settings);
|
||||
}
|
||||
|
||||
public void Load(string filePath)
|
||||
{
|
||||
Load(new ES3Settings (filePath));
|
||||
}
|
||||
|
||||
public void Load(string filePath, ES3Settings settings)
|
||||
{
|
||||
Load(new ES3Settings (filePath, settings));
|
||||
}
|
||||
|
||||
public void Load(ES3Settings settings)
|
||||
{
|
||||
Load(ES3Stream.CreateStream(settings, ES3FileMode.Read), settings);
|
||||
}
|
||||
|
||||
public void LoadRaw(string str)
|
||||
{
|
||||
Load(new MemoryStream (((new ES3Settings ()).encoding).GetBytes(str)), new ES3Settings());
|
||||
}
|
||||
|
||||
public void LoadRaw(string str, ES3Settings settings)
|
||||
{
|
||||
Load(new MemoryStream ((settings.encoding).GetBytes(str)), settings);
|
||||
}
|
||||
|
||||
private void Load(Stream stream, ES3Settings settings)
|
||||
{
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
int c_int;
|
||||
char c;
|
||||
string value = "";
|
||||
int col = 0;
|
||||
int row = 0;
|
||||
|
||||
// Read until the end of the stream.
|
||||
while(true)
|
||||
{
|
||||
c_int = reader.Read();
|
||||
c = (char)c_int;
|
||||
if(c == QUOTE_CHAR)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
c = (char)reader.Read();
|
||||
|
||||
if(c == QUOTE_CHAR)
|
||||
{
|
||||
// If this quote isn't escaped by another, it is the last quote, so we should stop parsing this value.
|
||||
if(((char)reader.Peek()) != QUOTE_CHAR)
|
||||
break;
|
||||
else
|
||||
c = (char)reader.Read();
|
||||
}
|
||||
value += c;
|
||||
}
|
||||
}
|
||||
// If this is the end of a column, row, or the stream, add the value to the spreadsheet.
|
||||
else if(c == COMMA_CHAR || c == NEWLINE_CHAR || c_int == -1)
|
||||
{
|
||||
SetCell(col, row, value);
|
||||
value = "";
|
||||
if(c == COMMA_CHAR)
|
||||
col++;
|
||||
else if(c == NEWLINE_CHAR)
|
||||
{
|
||||
col = 0;
|
||||
row++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
value += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(string filePath)
|
||||
{
|
||||
Save(new ES3Settings (filePath), false);
|
||||
}
|
||||
|
||||
public void Save(string filePath, ES3Settings settings)
|
||||
{
|
||||
Save(new ES3Settings (filePath, settings), false);
|
||||
}
|
||||
|
||||
public void Save(ES3Settings settings)
|
||||
{
|
||||
Save(settings, false);
|
||||
}
|
||||
|
||||
public void Save(string filePath, bool append)
|
||||
{
|
||||
Save(new ES3Settings (filePath), append);
|
||||
}
|
||||
|
||||
public void Save(string filePath, ES3Settings settings, bool append)
|
||||
{
|
||||
Save(new ES3Settings (filePath, settings), append);
|
||||
}
|
||||
|
||||
public void Save(ES3Settings settings, bool append)
|
||||
{
|
||||
using (var writer = new StreamWriter(ES3Stream.CreateStream(settings, append ? ES3FileMode.Append : ES3FileMode.Write)))
|
||||
{
|
||||
// If data already exists and we're appending, we need to prepend a newline.
|
||||
if(append && ES3.FileExists(settings))
|
||||
writer.Write(NEWLINE_CHAR);
|
||||
|
||||
var array = ToArray();
|
||||
for(int row = 0; row < rows; row++)
|
||||
{
|
||||
if(row != 0)
|
||||
writer.Write(NEWLINE_CHAR);
|
||||
|
||||
for(int col = 0; col < cols; col++)
|
||||
{
|
||||
if(col != 0)
|
||||
writer.Write(COMMA_CHAR);
|
||||
|
||||
writer.Write( Escape(array [col, row]) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!append)
|
||||
ES3IO.CommitBackup(settings);
|
||||
}
|
||||
|
||||
private static string Escape(string str, bool isAlreadyWrappedInQuotes=false)
|
||||
{
|
||||
if (str == "")
|
||||
return "\"\"";
|
||||
else if(str == null)
|
||||
return null;
|
||||
|
||||
// Now escape any other quotes.
|
||||
if(str.Contains(QUOTE))
|
||||
str = str.Replace(QUOTE, ESCAPED_QUOTE);
|
||||
|
||||
// If there's chars to escape, wrap the value in quotes.
|
||||
if(str.IndexOfAny(CHARS_TO_ESCAPE) > -1)
|
||||
str = QUOTE + str + QUOTE;
|
||||
return str;
|
||||
}
|
||||
|
||||
private static string Unescape(string str)
|
||||
{
|
||||
if(str.StartsWith(QUOTE) && str.EndsWith(QUOTE))
|
||||
{
|
||||
str = str.Substring(1, str.Length-2);
|
||||
if(str.Contains(ESCAPED_QUOTE))
|
||||
str = str.Replace(ESCAPED_QUOTE, QUOTE);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private string[,] ToArray()
|
||||
{
|
||||
var array = new string[cols, rows];
|
||||
foreach (var cell in cells)
|
||||
array [cell.Key.col, cell.Key.row] = cell.Value;
|
||||
return array;
|
||||
}
|
||||
|
||||
protected struct Index
|
||||
{
|
||||
public int col;
|
||||
public int row;
|
||||
|
||||
public Index(int col, int row)
|
||||
{
|
||||
this.col = col;
|
||||
this.row = row;
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Spreadsheet.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/ES3Spreadsheet.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a15384968025ea48a2b761b7a354425
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Readers.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Readers.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53ceedac58f38cd48950560f221fd6f9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
571
Convention/[ES3]/Easy Save 3/Scripts/Readers/ES3JSONReader.cs
Normal file
571
Convention/[ES3]/Easy Save 3/Scripts/Readers/ES3JSONReader.cs
Normal file
@@ -0,0 +1,571 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System;
|
||||
using ES3Types;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
/*
|
||||
* Specific ES3Reader for reading JSON data.
|
||||
*
|
||||
* Note: Leading & trailing whitespace is ignored whenever
|
||||
* reading characters which are part of the JSON syntax,
|
||||
* i.e. { } [ ] , " " :
|
||||
*/
|
||||
public class ES3JSONReader : ES3Reader
|
||||
{
|
||||
private const char endOfStreamChar = (char)65535;
|
||||
|
||||
public StreamReader baseReader;
|
||||
|
||||
internal ES3JSONReader(Stream stream, ES3Settings settings, bool readHeaderAndFooter = true) : base(settings, readHeaderAndFooter)
|
||||
{
|
||||
this.baseReader = new StreamReader(stream);
|
||||
|
||||
// Read opening brace from file if we're loading straight from file.
|
||||
if(readHeaderAndFooter)
|
||||
{
|
||||
try
|
||||
{
|
||||
SkipOpeningBraceOfFile();
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.Dispose();
|
||||
throw new FormatException("Cannot load from file because the data in it is not JSON data, or the data is encrypted.\nIf the save data is encrypted, please ensure that encryption is enabled when you load, and that you are using the same password used to encrypt the data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Property/Key Methods
|
||||
|
||||
/*
|
||||
* Reads the name of a property, and must be positioned (with or without whitespace) either:
|
||||
* - Before the '"' of a property name.
|
||||
* - Before the ',' separating properties.
|
||||
* - Before the '}' or ']' terminating this list of properties.
|
||||
* Can be used in conjunction with Read(ES3Type) to read a property.
|
||||
*/
|
||||
public override string ReadPropertyName()
|
||||
{
|
||||
char c = PeekCharIgnoreWhitespace();
|
||||
|
||||
// Check whether there are any properties left to read.
|
||||
if(IsTerminator(c))
|
||||
return null;
|
||||
else if(c == ',')
|
||||
ReadCharIgnoreWhitespace();
|
||||
else if(!IsQuotationMark(c))
|
||||
throw new FormatException("Expected ',' separating properties or '\"' before property name, found '"+c+"'.");
|
||||
|
||||
var propertyName = Read_string();
|
||||
if(propertyName == null)
|
||||
throw new FormatException("Stream isn't positioned before a property.");
|
||||
|
||||
ES3Debug.Log("<b>"+propertyName+"</b> (reading property)", null, serializationDepth);
|
||||
|
||||
// Skip the ':' seperating property and value.
|
||||
ReadCharIgnoreWhitespace(':');
|
||||
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the type data prefixed to this key.
|
||||
* If ignore is true, it will return null to save the computation of converting
|
||||
* the string to a Type.
|
||||
*/
|
||||
protected override Type ReadKeyPrefix(bool ignoreType=false)
|
||||
{
|
||||
StartReadObject();
|
||||
|
||||
Type dataType = null;
|
||||
|
||||
string propertyName = ReadPropertyName();
|
||||
if(propertyName == ES3Type.typeFieldName)
|
||||
{
|
||||
string typeString = Read_string();
|
||||
dataType = ignoreType ? null : ES3Reflection.GetType(typeString);
|
||||
propertyName = ReadPropertyName();
|
||||
}
|
||||
|
||||
if(propertyName != "value")
|
||||
throw new FormatException("This data is not Easy Save Key Value data. Expected property name \"value\", found \""+propertyName+"\".");
|
||||
|
||||
return dataType;
|
||||
}
|
||||
|
||||
protected override void ReadKeySuffix()
|
||||
{
|
||||
EndReadObject();
|
||||
}
|
||||
|
||||
|
||||
internal override bool StartReadObject()
|
||||
{
|
||||
base.StartReadObject();
|
||||
return ReadNullOrCharIgnoreWhitespace('{');
|
||||
}
|
||||
|
||||
internal override void EndReadObject()
|
||||
{
|
||||
ReadCharIgnoreWhitespace('}');
|
||||
base.EndReadObject();
|
||||
}
|
||||
|
||||
|
||||
internal override bool StartReadDictionary()
|
||||
{
|
||||
return StartReadObject();
|
||||
}
|
||||
|
||||
internal override void EndReadDictionary(){}
|
||||
|
||||
internal override bool StartReadDictionaryKey()
|
||||
{
|
||||
// If this is an empty Dictionary, return false.
|
||||
if(PeekCharIgnoreWhitespace() == '}')
|
||||
{
|
||||
ReadCharIgnoreWhitespace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override void EndReadDictionaryKey()
|
||||
{
|
||||
ReadCharIgnoreWhitespace(':');
|
||||
}
|
||||
|
||||
internal override void StartReadDictionaryValue(){}
|
||||
|
||||
internal override bool EndReadDictionaryValue()
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(c == '}')
|
||||
return true;
|
||||
// Else, we should expect a comma.
|
||||
else if(c != ',')
|
||||
throw new FormatException("Expected ',' seperating Dictionary items or '}' terminating Dictionary, found '"+c+"'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override bool StartReadCollection()
|
||||
{
|
||||
return ReadNullOrCharIgnoreWhitespace('[');
|
||||
}
|
||||
|
||||
internal override void EndReadCollection(){}
|
||||
|
||||
internal override bool StartReadCollectionItem()
|
||||
{
|
||||
// If this is an empty collection, return false.
|
||||
if(PeekCharIgnoreWhitespace() == ']')
|
||||
{
|
||||
ReadCharIgnoreWhitespace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override bool EndReadCollectionItem()
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(c == ']')
|
||||
return true;
|
||||
// Else, we should expect a comma.
|
||||
else if(c != ',')
|
||||
throw new FormatException("Expected ',' seperating collection items or ']' terminating collection, found '"+c+"'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Seeking Methods
|
||||
|
||||
/*
|
||||
* Reads a string value into a StreamWriter.
|
||||
* Reader should be positioned after the opening quotation mark.
|
||||
* Will also read the closing quotation mark.
|
||||
* If the 'skip' parameter is true, data will not be written into a StreamWriter and will return null.
|
||||
*/
|
||||
private void ReadString(StreamWriter writer, bool skip=false)
|
||||
{
|
||||
bool endOfString = false;
|
||||
// Read to end of string, or throw error if we reach end of stream.
|
||||
while(!endOfString)
|
||||
{
|
||||
char c = ReadOrSkipChar(writer, skip);
|
||||
switch(c)
|
||||
{
|
||||
case endOfStreamChar:
|
||||
throw new FormatException("String without closing quotation mark detected.");
|
||||
case '\\':
|
||||
ReadOrSkipChar(writer, skip);
|
||||
break;
|
||||
default:
|
||||
if(IsQuotationMark(c))
|
||||
endOfString = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the current object in the stream.
|
||||
* Stream position should be somewhere before the opening brace for the object.
|
||||
* When this method successfully exits, it will be on the closing brace for the object.
|
||||
* If the 'skip' parameter is true, data will not be written into a StreamWriter and will return null.
|
||||
*/
|
||||
internal override byte[] ReadElement(bool skip=false)
|
||||
{
|
||||
// If 'skip' is enabled, don't create a stream or writer as we'll discard all bytes we read.
|
||||
StreamWriter writer = skip ? null : new StreamWriter(new MemoryStream(settings.bufferSize));
|
||||
|
||||
using(writer)
|
||||
{
|
||||
int nesting = 0;
|
||||
char c = (char)baseReader.Peek();
|
||||
|
||||
// Determine if we're skipping a primitive type.
|
||||
// First check if it's an opening object or array brace.
|
||||
if(!IsOpeningBrace(c))
|
||||
{
|
||||
// If we're skipping a string, use SkipString().
|
||||
if(c == '\"')
|
||||
{
|
||||
// Skip initial quotation mark as SkipString() requires this.
|
||||
ReadOrSkipChar(writer, skip);
|
||||
ReadString(writer, skip);
|
||||
}
|
||||
// Else we just need to read until we reach a closing brace.
|
||||
else
|
||||
// While we've not peeked a closing brace.
|
||||
while(!IsEndOfValue((char)baseReader.Peek()))
|
||||
ReadOrSkipChar(writer, skip);
|
||||
|
||||
if(skip)
|
||||
return null;
|
||||
writer.Flush();
|
||||
return ((MemoryStream)writer.BaseStream).ToArray();
|
||||
}
|
||||
|
||||
// Else, we're skipping a type surrounded by braces.
|
||||
// Iterate through every character, logging nesting.
|
||||
while(true)
|
||||
{
|
||||
c = ReadOrSkipChar(writer, skip);
|
||||
|
||||
if(c == endOfStreamChar) // Detect premature end of stream, which denotes missing closing brace.
|
||||
throw new FormatException("Missing closing brace detected, as end of stream was reached before finding it.");
|
||||
|
||||
// Handle quoted strings.
|
||||
// According to the RFC, only '\' and '"' must be escaped in strings.
|
||||
if(IsQuotationMark(c))
|
||||
{
|
||||
ReadString(writer, skip);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle braces and other characters.
|
||||
switch(c)
|
||||
{
|
||||
case '{': // Entered another level of nesting.
|
||||
case '[':
|
||||
nesting++;
|
||||
break;
|
||||
case '}': // Exited a level of nesting.
|
||||
case ']':
|
||||
nesting--;
|
||||
// If nesting < 1, we've come to the end of the object.
|
||||
if(nesting<1)
|
||||
{
|
||||
if(skip)
|
||||
return null;
|
||||
writer.Flush();
|
||||
return ((MemoryStream)writer.BaseStream).ToArray();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the next char into a stream, or ignores it if 'skip' is true.
|
||||
*/
|
||||
private char ReadOrSkipChar(StreamWriter writer, bool skip)
|
||||
{
|
||||
char c = (char)baseReader.Read();
|
||||
if(!skip) writer.Write(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region JSON-specific methods.
|
||||
|
||||
/*
|
||||
* Reads a char from the stream and ignores leading and trailing whitespace.
|
||||
*/
|
||||
private char ReadCharIgnoreWhitespace(bool ignoreTrailingWhitespace=true)
|
||||
{
|
||||
char c;
|
||||
// Skip leading whitespace and read char.
|
||||
while(IsWhiteSpace(c = (char)baseReader.Read()))
|
||||
{}
|
||||
|
||||
// Skip trailing whitespace.
|
||||
if(ignoreTrailingWhitespace)
|
||||
while(IsWhiteSpace((char)baseReader.Peek()))
|
||||
baseReader.Read();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a char, or the NULL value, from the stream and ignores leading and trailing whitespace.
|
||||
* Returns true if NULL was read.
|
||||
*/
|
||||
private bool ReadNullOrCharIgnoreWhitespace(char expectedChar)
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
|
||||
// Check for null
|
||||
if(c == 'n')
|
||||
{
|
||||
var chars = new char[3];
|
||||
baseReader.ReadBlock(chars, 0, 3);
|
||||
if((char)chars[0] == 'u' && (char)chars[1] == 'l' && (char)chars[2] == 'l')
|
||||
return true;
|
||||
}
|
||||
|
||||
if(c != expectedChar)
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached when expecting '"+expectedChar+"'.");
|
||||
else
|
||||
throw new FormatException("Expected \'"+expectedChar+"\' or \"null\", found \'"+c+"\'.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a char from the stream and ignores leading and trailing whitespace.
|
||||
* Throws an error if the char isn't equal to the one specificed as a parameter, or if it's the end of stream.
|
||||
*/
|
||||
private char ReadCharIgnoreWhitespace(char expectedChar)
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
if(c != expectedChar)
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached when expecting '"+expectedChar+"'.");
|
||||
else
|
||||
throw new FormatException("Expected \'"+expectedChar+"\', found \'"+c+"\'.");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private bool ReadQuotationMarkOrNullIgnoreWhitespace()
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace(false); // Don't read trailing whitespace as this is the value.
|
||||
|
||||
if(c == 'n')
|
||||
{
|
||||
var chars = new char[3];
|
||||
baseReader.ReadBlock(chars, 0, 3);
|
||||
if((char)chars[0] == 'u' && (char)chars[1] == 'l' && (char)chars[2] == 'l')
|
||||
return true;
|
||||
}
|
||||
else if(!IsQuotationMark(c))
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached when expecting quotation mark.");
|
||||
else
|
||||
throw new FormatException("Expected quotation mark, found \'"+c+"\'.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Peeks the next char in the stream, ignoring leading whitespace, but not trailing whitespace.
|
||||
*/
|
||||
private char PeekCharIgnoreWhitespace(char expectedChar)
|
||||
{
|
||||
char c = PeekCharIgnoreWhitespace();
|
||||
if(c != expectedChar)
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached while peeking, when expecting '"+expectedChar+"'.");
|
||||
else
|
||||
throw new FormatException("Expected \'"+expectedChar+"\', found \'"+c+"\'.");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Peeks the next char in the stream, ignoring leading whitespace, but not trailing whitespace.
|
||||
* Throws an error if the char isn't equal to the one specificed as a parameter.
|
||||
*/
|
||||
private char PeekCharIgnoreWhitespace()
|
||||
{
|
||||
char c;
|
||||
// Skip leading whitespace and read char.
|
||||
while(IsWhiteSpace(c = (char)baseReader.Peek()))
|
||||
baseReader.Read();
|
||||
return c;
|
||||
}
|
||||
|
||||
// Skips all whitespace immediately after the current position.
|
||||
private void SkipWhiteSpace()
|
||||
{
|
||||
while(IsWhiteSpace((char)baseReader.Peek()))
|
||||
baseReader.Read();
|
||||
}
|
||||
|
||||
private void SkipOpeningBraceOfFile()
|
||||
{
|
||||
// Skip the whitespace and '{' at the beginning of the JSON file.
|
||||
char firstChar = ReadCharIgnoreWhitespace();
|
||||
if(firstChar != '{') // If first char isn't '{', it's not valid JSON.
|
||||
throw new FormatException("File is not valid JSON. Expected '{' at beginning of file, but found '"+firstChar+"'.");
|
||||
}
|
||||
|
||||
private static bool IsWhiteSpace(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
|
||||
}
|
||||
|
||||
private static bool IsOpeningBrace(char c)
|
||||
{
|
||||
return (c == '{' || c == '[');
|
||||
}
|
||||
|
||||
private static bool IsEndOfValue(char c)
|
||||
{
|
||||
return (c == '}' || c == ' ' || c == '\t' || c == ']' || c == ',' || c== ':' || c == endOfStreamChar || c == '\n' || c == '\r');
|
||||
}
|
||||
|
||||
private static bool IsTerminator(char c)
|
||||
{
|
||||
return (c == '}' || c == ']');
|
||||
}
|
||||
|
||||
private static bool IsQuotationMark(char c)
|
||||
{
|
||||
return c == '\"' || c == '“' || c == '”';
|
||||
}
|
||||
|
||||
private static bool IsEndOfStream(char c)
|
||||
{
|
||||
return c == endOfStreamChar;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a value (i.e. non-string, non-object) from the stream as a string.
|
||||
* Used mostly in Read_[type]() methods.
|
||||
*/
|
||||
private string GetValueString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
while(!IsEndOfValue(PeekCharIgnoreWhitespace()))
|
||||
builder.Append((char)baseReader.Read());
|
||||
|
||||
// If it's an empty value, return null.
|
||||
if(builder.Length == 0)
|
||||
return null;
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Primitive Read() Methods.
|
||||
|
||||
internal override string Read_string()
|
||||
{
|
||||
if(ReadQuotationMarkOrNullIgnoreWhitespace())
|
||||
return null;
|
||||
char c;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
while(!IsQuotationMark((c = (char)baseReader.Read())))
|
||||
{
|
||||
// If escape mark is found, generate correct escaped character.
|
||||
if(c == '\\')
|
||||
{
|
||||
c = (char)baseReader.Read();
|
||||
if(IsEndOfStream(c))
|
||||
throw new FormatException("Reached end of stream while trying to read string literal.");
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case 'b':
|
||||
c = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
c = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
c = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
c = '\r';
|
||||
break;
|
||||
case 't':
|
||||
c = '\t';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.Append(c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal override long Read_ref()
|
||||
{
|
||||
if (ES3ReferenceMgr.Current == null)
|
||||
throw new InvalidOperationException("An Easy Save 3 Manager is required to load references. To add one to your scene, exit playmode and go to Tools > Easy Save 3 > Add Manager to Scene");
|
||||
if (IsQuotationMark(PeekCharIgnoreWhitespace()))
|
||||
return long.Parse(Read_string());
|
||||
return Read_long();
|
||||
}
|
||||
|
||||
internal override char Read_char() { return char.Parse( Read_string()); }
|
||||
internal override float Read_float() { return float.Parse( GetValueString(), CultureInfo.InvariantCulture); }
|
||||
internal override int Read_int() { return int.Parse( GetValueString()); }
|
||||
internal override bool Read_bool() { return bool.Parse( GetValueString()); }
|
||||
internal override decimal Read_decimal() { return decimal.Parse( GetValueString(), CultureInfo.InvariantCulture); }
|
||||
internal override double Read_double() { return double.Parse( GetValueString(), CultureInfo.InvariantCulture); }
|
||||
internal override long Read_long() { return long.Parse( GetValueString()); }
|
||||
internal override ulong Read_ulong() { return ulong.Parse( GetValueString()); }
|
||||
internal override uint Read_uint() { return uint.Parse( GetValueString()); }
|
||||
internal override byte Read_byte() { return (byte)int.Parse( GetValueString()); }
|
||||
internal override sbyte Read_sbyte() { return (sbyte)int.Parse( GetValueString()); }
|
||||
internal override short Read_short() { return (short)int.Parse( GetValueString()); }
|
||||
internal override ushort Read_ushort() { return (ushort)int.Parse( GetValueString()); }
|
||||
|
||||
internal override byte[] Read_byteArray(){ return System.Convert.FromBase64String(Read_string()); }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
baseReader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5eedc5128a57d504d9cb206ccc982e79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
455
Convention/[ES3]/Easy Save 3/Scripts/Readers/ES3Reader.cs
Normal file
455
Convention/[ES3]/Easy Save 3/Scripts/Readers/ES3Reader.cs
Normal file
@@ -0,0 +1,455 @@
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using ES3Types;
|
||||
using ES3Internal;
|
||||
|
||||
public abstract class ES3Reader : System.IDisposable
|
||||
{
|
||||
/// <summary>The settings used to create this reader.</summary>
|
||||
public ES3Settings settings;
|
||||
|
||||
protected int serializationDepth = 0;
|
||||
|
||||
#region ES3Reader Abstract Methods
|
||||
|
||||
internal abstract int Read_int();
|
||||
internal abstract float Read_float();
|
||||
internal abstract bool Read_bool();
|
||||
internal abstract char Read_char();
|
||||
internal abstract decimal Read_decimal();
|
||||
internal abstract double Read_double();
|
||||
internal abstract long Read_long();
|
||||
internal abstract ulong Read_ulong();
|
||||
internal abstract byte Read_byte();
|
||||
internal abstract sbyte Read_sbyte();
|
||||
internal abstract short Read_short();
|
||||
internal abstract ushort Read_ushort();
|
||||
internal abstract uint Read_uint();
|
||||
internal abstract string Read_string();
|
||||
internal abstract byte[] Read_byteArray();
|
||||
internal abstract long Read_ref();
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public abstract string ReadPropertyName();
|
||||
|
||||
protected abstract Type ReadKeyPrefix(bool ignore = false);
|
||||
protected abstract void ReadKeySuffix();
|
||||
internal abstract byte[] ReadElement(bool skip=false);
|
||||
|
||||
/// <summary>Disposes of the reader and it's underlying stream.</summary>
|
||||
public abstract void Dispose();
|
||||
|
||||
// Seeks to the given key. Note that the stream position will not be reset.
|
||||
internal virtual bool Goto(string key)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("Key cannot be NULL when loading data.");
|
||||
|
||||
string currentKey;
|
||||
while ((currentKey = ReadPropertyName()) != key)
|
||||
{
|
||||
if (currentKey == null)
|
||||
return false;
|
||||
Skip();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal virtual bool StartReadObject()
|
||||
{
|
||||
serializationDepth++;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal virtual void EndReadObject()
|
||||
{
|
||||
serializationDepth--;
|
||||
}
|
||||
|
||||
internal abstract bool StartReadDictionary();
|
||||
internal abstract void EndReadDictionary();
|
||||
internal abstract bool StartReadDictionaryKey();
|
||||
internal abstract void EndReadDictionaryKey();
|
||||
internal abstract void StartReadDictionaryValue();
|
||||
internal abstract bool EndReadDictionaryValue();
|
||||
|
||||
internal abstract bool StartReadCollection();
|
||||
internal abstract void EndReadCollection();
|
||||
internal abstract bool StartReadCollectionItem();
|
||||
internal abstract bool EndReadCollectionItem();
|
||||
|
||||
#endregion
|
||||
|
||||
internal ES3Reader(ES3Settings settings, bool readHeaderAndFooter = true)
|
||||
{
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
// If this is not null, the next call to the Properties will return this name.
|
||||
internal string overridePropertiesName = null;
|
||||
/// <summary>Allows you to enumerate over each field name. This should only be used within an ES3Type file.</summary>
|
||||
public virtual ES3ReaderPropertyEnumerator Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ES3ReaderPropertyEnumerator (this);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual ES3ReaderRawEnumerator RawEnumerator
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ES3ReaderRawEnumerator (this);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Skips the current object in the stream.
|
||||
* Stream position should be somewhere before the opening brace for the object.
|
||||
* When this method successfully exits, it will be on the closing brace for the object.
|
||||
*/
|
||||
/// <summary>Skips the current object in the stream.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public virtual void Skip()
|
||||
{
|
||||
ReadElement(true);
|
||||
}
|
||||
|
||||
/// <summary>Reads a value of type T from the reader.</summary>
|
||||
public virtual T Read<T>()
|
||||
{
|
||||
return Read<T>(ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
|
||||
}
|
||||
|
||||
/// <summary>Reads a value of type T from the reader into an existing object.</summary>
|
||||
/// <param name="obj">The object we want to read our value into.</param>
|
||||
public virtual void ReadInto<T>(object obj)
|
||||
{
|
||||
ReadInto<T>(obj, ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
|
||||
}
|
||||
|
||||
/// <summary>Reads a property (i.e. a property name and value) from the reader, ignoring the property name and only returning the value.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public T ReadProperty<T>()
|
||||
{
|
||||
return ReadProperty<T>(ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public T ReadProperty<T>(ES3Type type)
|
||||
{
|
||||
ReadPropertyName();
|
||||
return Read<T>(type);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public long ReadRefProperty()
|
||||
{
|
||||
ReadPropertyName();
|
||||
return Read_ref();
|
||||
}
|
||||
|
||||
internal Type ReadType()
|
||||
{
|
||||
return ES3Reflection.GetType(Read<string>(ES3Type_string.Instance));
|
||||
}
|
||||
|
||||
/// <summary>Sets the value of a private property on an object.</summary>
|
||||
/// <param name="name">The name of the property we want to set.</param>
|
||||
/// <param name="value">The value we want to set the property to.</param>
|
||||
/// <param name="objectContainingProperty">The object containing the property we want to set.</param>
|
||||
/// <returns>The objectContainingProperty object. This is helpful if you're setting a private property on a struct or other immutable type and need to return the boxed value.</returns>
|
||||
public object SetPrivateProperty(string name, object value, object objectContainingProperty)
|
||||
{
|
||||
var property = ES3Reflection.GetES3ReflectedProperty(objectContainingProperty.GetType(), name);
|
||||
if (property.IsNull)
|
||||
throw new MissingMemberException("A private property named " + name + " does not exist in the type " + objectContainingProperty.GetType());
|
||||
property.SetValue(objectContainingProperty, value);
|
||||
return objectContainingProperty;
|
||||
}
|
||||
|
||||
/// <summary>Sets the value of a private field on an object.</summary>
|
||||
/// <param name="name">The name of the field we want to set.</param>
|
||||
/// <param name="value">The value we want to set the field to.</param>
|
||||
/// <param name="objectContainingField">The object containing the field we want to set.</param>
|
||||
/// <returns>The objectContainingField object. This is helpful if you're setting a private property on a struct or other immutable type and need to return the boxed value.</returns>
|
||||
public object SetPrivateField(string name, object value, object objectContainingField)
|
||||
{
|
||||
var field = ES3Reflection.GetES3ReflectedMember(objectContainingField.GetType(), name);
|
||||
if(field.IsNull)
|
||||
throw new MissingMemberException("A private field named "+ name + " does not exist in the type "+objectContainingField.GetType());
|
||||
field.SetValue(objectContainingField, value);
|
||||
return objectContainingField;
|
||||
}
|
||||
|
||||
#region Read(key) & Read(key, obj) methods
|
||||
|
||||
/// <summary>Reads a value from the reader with the given key.</summary>
|
||||
/// <param name="key">The key which uniquely identifies our value.</param>
|
||||
public virtual T Read<T>(string key)
|
||||
{
|
||||
if(!Goto(key))
|
||||
throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \""+settings.FullPath+"\". Use Load<T>(key, defaultValue) if you want to return a default value if the key does not exist.");
|
||||
|
||||
Type type = ReadTypeFromHeader<T>();
|
||||
|
||||
T obj = Read<T>(ES3TypeMgr.GetOrCreateES3Type(type));
|
||||
|
||||
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>Reads a value from the reader with the given key, returning the default value if the key does not exist.</summary>
|
||||
/// <param name="key">The key which uniquely identifies our value.</param>
|
||||
/// <param name="defaultValue">The value we want to return if this key does not exist in the reader.</param>
|
||||
public virtual T Read<T>(string key, T defaultValue)
|
||||
{
|
||||
if(!Goto(key))
|
||||
return defaultValue;
|
||||
|
||||
Type type = ReadTypeFromHeader<T>();
|
||||
T obj = Read<T>(ES3TypeMgr.GetOrCreateES3Type(type));
|
||||
|
||||
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>Reads a value from the reader with the given key into the provided object.</summary>
|
||||
/// <param name="key">The key which uniquely identifies our value.</param>
|
||||
/// <param name="obj">The object we want to load the value into.</param>
|
||||
public virtual void ReadInto<T>(string key, T obj) where T : class
|
||||
{
|
||||
if(!Goto(key))
|
||||
throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \""+settings.FullPath+"\"");
|
||||
|
||||
Type type = ReadTypeFromHeader<T>();
|
||||
|
||||
ReadInto<T>(obj, ES3TypeMgr.GetOrCreateES3Type(type));
|
||||
|
||||
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
|
||||
}
|
||||
|
||||
protected virtual void ReadObject<T>(object obj, ES3Type type)
|
||||
{
|
||||
// Check for null.
|
||||
if(StartReadObject())
|
||||
return;
|
||||
|
||||
type.ReadInto<T>(this, obj);
|
||||
|
||||
EndReadObject();
|
||||
}
|
||||
|
||||
protected virtual T ReadObject<T>(ES3Type type)
|
||||
{
|
||||
if(StartReadObject())
|
||||
return default(T);
|
||||
|
||||
object obj = type.Read<T>(this);
|
||||
|
||||
EndReadObject();
|
||||
return (T)obj;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read(ES3Type) & Read(obj,ES3Type) methods
|
||||
|
||||
/*
|
||||
* Parses the next JSON Object in the stream (i.e. must be between '{' and '}' chars).
|
||||
* If the first character in the Stream is not a '{', it will throw an error.
|
||||
* Will also read the terminating '}'.
|
||||
* If we have reached the end of stream, it will return null.
|
||||
*/
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public virtual T Read<T>(ES3Type type)
|
||||
{
|
||||
if (type == null || type.isUnsupported)
|
||||
throw new NotSupportedException("Type of " + type + " is not currently supported, and could not be loaded using reflection.");
|
||||
else if (type.isPrimitive)
|
||||
return (T)type.Read<T>(this);
|
||||
else if (type.isCollection)
|
||||
return (T)((ES3CollectionType)type).Read(this);
|
||||
else if (type.isDictionary)
|
||||
return (T)((ES3DictionaryType)type).Read(this);
|
||||
else
|
||||
return ReadObject<T>(type);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public virtual void ReadInto<T>(object obj, ES3Type type)
|
||||
{
|
||||
if(type == null || type.isUnsupported)
|
||||
throw new NotSupportedException("Type of "+obj.GetType()+" is not currently supported, and could not be loaded using reflection.");
|
||||
|
||||
else if(type.isCollection)
|
||||
((ES3CollectionType)type).ReadInto(this, obj);
|
||||
else if(type.isDictionary)
|
||||
((ES3DictionaryType)type).ReadInto(this, obj);
|
||||
else
|
||||
ReadObject<T>(obj, type);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal Type ReadTypeFromHeader<T>()
|
||||
{
|
||||
// Check whether we need to determine the type by reading the header.
|
||||
if(typeof(T) == typeof(object))
|
||||
return ReadKeyPrefix();
|
||||
else if(settings.typeChecking)
|
||||
{
|
||||
Type type = ReadKeyPrefix();
|
||||
if(type != typeof(T))
|
||||
throw new InvalidOperationException("Trying to load data of type "+typeof(T)+", but data contained in file is type of "+type+".");
|
||||
return type;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadKeyPrefix(true);
|
||||
return typeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Reader and loads the default file into it.</summary>
|
||||
public static ES3Reader Create()
|
||||
{
|
||||
return Create(new ES3Settings());
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Reader and loads a file in storage into it.</summary>
|
||||
/// <param name="filePath">The relative or absolute path of the file we want to load into the reader.</param>
|
||||
public static ES3Reader Create(string filePath)
|
||||
{
|
||||
return Create(new ES3Settings(filePath));
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Reader and loads a file in storage into it.</summary>
|
||||
/// <param name="filePath">The relative or absolute path of the file we want to load into the reader.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public static ES3Reader Create(string filePath, ES3Settings settings)
|
||||
{
|
||||
return Create(new ES3Settings(filePath, settings));
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Reader and loads a file in storage into it.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public static ES3Reader Create(ES3Settings settings)
|
||||
{
|
||||
Stream stream = ES3Stream.CreateStream(settings, ES3FileMode.Read);
|
||||
if(stream == null)
|
||||
return null;
|
||||
|
||||
// Get the baseWriter using the given Stream.
|
||||
if (settings.format == ES3.Format.JSON)
|
||||
return new ES3JSONReader(stream, settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Reader and loads the bytes provided into it.</summary>
|
||||
public static ES3Reader Create(byte[] bytes)
|
||||
{
|
||||
return Create(bytes, new ES3Settings());
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Reader and loads the bytes provided into it.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public static ES3Reader Create(byte[] bytes, ES3Settings settings)
|
||||
{
|
||||
Stream stream = ES3Stream.CreateStream(new MemoryStream(bytes), settings, ES3FileMode.Read);
|
||||
if(stream == null)
|
||||
return null;
|
||||
|
||||
// Get the baseWriter using the given Stream.
|
||||
if(settings.format == ES3.Format.JSON)
|
||||
return new ES3JSONReader(stream, settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static ES3Reader Create(Stream stream, ES3Settings settings)
|
||||
{
|
||||
stream = ES3Stream.CreateStream(stream, settings, ES3FileMode.Read);
|
||||
|
||||
// Get the baseWriter using the given Stream.
|
||||
if(settings.format == ES3.Format.JSON)
|
||||
return new ES3JSONReader(stream, settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static ES3Reader Create(Stream stream, ES3Settings settings, bool readHeaderAndFooter)
|
||||
{
|
||||
// Get the baseWriter using the given Stream.
|
||||
if(settings.format == ES3.Format.JSON)
|
||||
return new ES3JSONReader(stream, settings, readHeaderAndFooter);
|
||||
return null;
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class ES3ReaderPropertyEnumerator
|
||||
{
|
||||
public ES3Reader reader;
|
||||
|
||||
public ES3ReaderPropertyEnumerator(ES3Reader reader)
|
||||
{
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
string propertyName;
|
||||
while(true)
|
||||
{
|
||||
// Allows us to repeat a property name or insert one of our own.
|
||||
if(reader.overridePropertiesName != null)
|
||||
{
|
||||
string tempName = reader.overridePropertiesName;
|
||||
reader.overridePropertiesName = null;
|
||||
yield return tempName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((propertyName = reader.ReadPropertyName()) == null)
|
||||
yield break;
|
||||
yield return propertyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class ES3ReaderRawEnumerator
|
||||
{
|
||||
public ES3Reader reader;
|
||||
|
||||
public ES3ReaderRawEnumerator(ES3Reader reader)
|
||||
{
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
string key = reader.ReadPropertyName();
|
||||
if(key == null)
|
||||
yield break;
|
||||
|
||||
Type type = reader.ReadTypeFromHeader<object>();
|
||||
|
||||
byte[] bytes = reader.ReadElement();
|
||||
|
||||
reader.ReadKeySuffix();
|
||||
|
||||
if(type != null)
|
||||
yield return new KeyValuePair<string,ES3Data>(key, new ES3Data(type, bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29f55012df690f34984d485a5983d775
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,5 @@
|
||||
|
||||
public class ES3XMLReader
|
||||
{
|
||||
// Not Implemented
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e951676a466b3e640829190b151f54f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Referencing.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Referencing.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 941de1bc52ea5424c965f05b04548d19
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,129 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public class ES3GlobalReferences : ScriptableObject
|
||||
{
|
||||
#if !UNITY_EDITOR || ES3GLOBAL_DISABLED
|
||||
public static ES3GlobalReferences Instance{ get{ return null; } }
|
||||
public UnityEngine.Object Get(long id){return null;}
|
||||
public long GetOrAdd(UnityEngine.Object obj){return -1;}
|
||||
public void RemoveInvalidKeys(){}
|
||||
#else
|
||||
|
||||
#if ES3GLOBAL_DISABLED
|
||||
private static bool useGlobalReferences = false;
|
||||
#else
|
||||
private static bool useGlobalReferences = true;
|
||||
#endif
|
||||
|
||||
private const string globalReferencesPath = "ES3/ES3GlobalReferences";
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public ES3RefIdDictionary refId = new ES3RefIdDictionary();
|
||||
|
||||
private static ES3GlobalReferences _globalReferences = null;
|
||||
public static ES3GlobalReferences Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
return null;
|
||||
|
||||
if (!useGlobalReferences)
|
||||
return null;
|
||||
|
||||
if (_globalReferences == null)
|
||||
{
|
||||
_globalReferences = Resources.Load<ES3GlobalReferences>(globalReferencesPath);
|
||||
|
||||
if (_globalReferences == null)
|
||||
{
|
||||
_globalReferences = ScriptableObject.CreateInstance<ES3GlobalReferences>();
|
||||
|
||||
// If this is the version being submitted to the Asset Store, don't include ES3Defaults.
|
||||
if (Application.productName.Contains("ES3 Release"))
|
||||
{
|
||||
Debug.Log("This has been identified as a release build as the title contains 'ES3 Release', so ES3GlobalReferences will not be created.");
|
||||
return _globalReferences;
|
||||
}
|
||||
|
||||
ES3Settings.CreateDefaultSettingsFolder();
|
||||
UnityEditor.AssetDatabase.CreateAsset(_globalReferences, PathToGlobalReferences());
|
||||
UnityEditor.AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return _globalReferences;
|
||||
}
|
||||
}
|
||||
|
||||
private long Get(UnityEngine.Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return -1;
|
||||
|
||||
long id;
|
||||
if (!refId.TryGetValue(obj, out id))
|
||||
return -1;
|
||||
return id;
|
||||
}
|
||||
|
||||
public UnityEngine.Object Get(long id)
|
||||
{
|
||||
foreach(var kvp in refId)
|
||||
if (kvp.Value == id)
|
||||
return kvp.Key;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void RemoveInvalidKeys()
|
||||
{
|
||||
var newRefId = new ES3RefIdDictionary();
|
||||
foreach (var kvp in refId)
|
||||
{
|
||||
var obj = kvp.Key;
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
||||
if ((((obj.hideFlags & HideFlags.DontSave) == HideFlags.DontSave) ||
|
||||
((obj.hideFlags & HideFlags.DontSaveInBuild) == HideFlags.DontSaveInBuild) ||
|
||||
((obj.hideFlags & HideFlags.DontSaveInEditor) == HideFlags.DontSaveInEditor) ||
|
||||
((obj.hideFlags & HideFlags.HideAndDontSave) == HideFlags.HideAndDontSave)))
|
||||
{
|
||||
var type = obj.GetType();
|
||||
// Meshes are marked with HideAndDontSave, but shouldn't be ignored.
|
||||
if (type != typeof(Mesh) && type != typeof(Material))
|
||||
continue;
|
||||
}
|
||||
newRefId[obj] = kvp.Value;
|
||||
}
|
||||
refId = newRefId;
|
||||
}
|
||||
|
||||
public long GetOrAdd(UnityEngine.Object obj)
|
||||
{
|
||||
var id = Get(obj);
|
||||
|
||||
// Only add items to global references when it's not playing.
|
||||
if (!Application.isPlaying && id == -1 && UnityEditor.AssetDatabase.Contains(obj) && ES3ReferenceMgr.CanBeSaved(obj))
|
||||
{
|
||||
id = ES3ReferenceMgrBase.GetNewRefID();
|
||||
refId.Add(obj, id);
|
||||
|
||||
UnityEditor.EditorUtility.SetDirty(this);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private static string PathToGlobalReferences()
|
||||
{
|
||||
return ES3Settings.PathToEasySaveFolder() + "Resources/" + globalReferencesPath +".asset";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd02c44c1a359004aa90fbf8185f1d7f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Settings.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a819892279b0644893fa3b64ff9f28c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public class ES3DefaultSettings : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
public ES3SerializableSettings settings = null;
|
||||
|
||||
public bool autoUpdateReferences = true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84a5899aa564c8942912995dbef21451
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
24
Convention/[ES3]/Easy Save 3/Scripts/Settings/ES3Defaults.cs
Normal file
24
Convention/[ES3]/Easy Save 3/Scripts/Settings/ES3Defaults.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class ES3Defaults : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
public ES3SerializableSettings settings = new ES3SerializableSettings();
|
||||
|
||||
public bool addMgrToSceneAutomatically = false;
|
||||
public bool autoUpdateReferences = true;
|
||||
public bool addAllPrefabsToManager = true;
|
||||
public int collectDependenciesDepth = 4;
|
||||
public int collectDependenciesTimeout = 10;
|
||||
public bool updateReferencesWhenSceneChanges = true;
|
||||
public bool updateReferencesWhenSceneIsSaved = true;
|
||||
public bool updateReferencesWhenSceneIsOpened = true;
|
||||
[Tooltip("Folders listed here will be searched for references every time the reference manager is refreshed. Path should be relative to the project folder.")]
|
||||
public string[] referenceFolders = new string[0];
|
||||
|
||||
public bool logDebugInfo = false;
|
||||
public bool logWarnings = true;
|
||||
public bool logErrors = true;
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 046a2c1130f1d594dbf05ad57a8b0265
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
386
Convention/[ES3]/Easy Save 3/Scripts/Settings/ES3Settings.cs
Normal file
386
Convention/[ES3]/Easy Save 3/Scripts/Settings/ES3Settings.cs
Normal file
@@ -0,0 +1,386 @@
|
||||
using UnityEngine;
|
||||
using ES3Internal;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if UNITY_VISUAL_SCRIPTING
|
||||
[Unity.VisualScripting.IncludeInSettings(true)]
|
||||
#elif BOLT_VISUAL_SCRIPTING
|
||||
[Ludiq.IncludeInSettings(true)]
|
||||
#endif
|
||||
public class ES3Settings : System.ICloneable
|
||||
{
|
||||
|
||||
#region Default settings
|
||||
private static ES3Settings _defaults = null;
|
||||
private static ES3Defaults _defaultSettingsScriptableObject;
|
||||
private const string defaultSettingsPath = "ES3/ES3Defaults";
|
||||
|
||||
public static ES3Defaults defaultSettingsScriptableObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_defaultSettingsScriptableObject == null)
|
||||
{
|
||||
_defaultSettingsScriptableObject = Resources.Load<ES3Defaults>(defaultSettingsPath);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (_defaultSettingsScriptableObject == null)
|
||||
{
|
||||
_defaultSettingsScriptableObject = ScriptableObject.CreateInstance<ES3Defaults>();
|
||||
|
||||
// If this is the version being submitted to the Asset Store, don't include ES3Defaults.
|
||||
if (Application.productName.Contains("ES3 Release"))
|
||||
{
|
||||
Debug.Log("This has been identified as a release build as the title contains 'ES3 Release', so ES3Defaults will not be created.");
|
||||
return _defaultSettingsScriptableObject;
|
||||
}
|
||||
|
||||
// Convert the old settings to the new settings if necessary.
|
||||
var oldSettings = GetOldSettings();
|
||||
if (oldSettings != null)
|
||||
{
|
||||
oldSettings.CopyInto(_defaultSettingsScriptableObject.settings);
|
||||
// Only enable warning logs by default for new installs as this may look like unexpected behaviour to some.
|
||||
_defaultSettingsScriptableObject.logWarnings = false;
|
||||
RemoveOldSettings();
|
||||
}
|
||||
|
||||
CreateDefaultSettingsFolder();
|
||||
AssetDatabase.CreateAsset(_defaultSettingsScriptableObject, PathToDefaultSettings());
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return _defaultSettingsScriptableObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static ES3Settings defaultSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_defaults == null)
|
||||
{
|
||||
if(defaultSettingsScriptableObject != null)
|
||||
_defaults = defaultSettingsScriptableObject.settings;
|
||||
}
|
||||
return _defaults;
|
||||
}
|
||||
}
|
||||
|
||||
private static ES3Settings _unencryptedUncompressedSettings = null;
|
||||
internal static ES3Settings unencryptedUncompressedSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_unencryptedUncompressedSettings == null)
|
||||
_unencryptedUncompressedSettings = new ES3Settings(ES3.EncryptionType.None, ES3.CompressionType.None);
|
||||
return _unencryptedUncompressedSettings;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private static readonly string[] resourcesExtensions = new string[]{".txt", ".htm", ".html", ".xml", ".bytes", ".json", ".csv", ".yaml", ".fnt" };
|
||||
|
||||
[SerializeField]
|
||||
private ES3.Location _location;
|
||||
/// <summary>The location where we wish to store data. As it's not possible to save/load from File in WebGL, if the default location is File it will use PlayerPrefs instead.</summary>
|
||||
public ES3.Location location
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_location == ES3.Location.File && (Application.platform == RuntimePlatform.WebGLPlayer || Application.platform == RuntimePlatform.tvOS))
|
||||
return ES3.Location.PlayerPrefs;
|
||||
return _location;
|
||||
}
|
||||
set{ _location = value; }
|
||||
}
|
||||
|
||||
/// <summary>The path associated with this ES3Settings object, if any.</summary>
|
||||
public string path = "SaveFile.es3";
|
||||
/// <summary>The type of encryption to use when encrypting data, if any.</summary>
|
||||
public ES3.EncryptionType encryptionType = ES3.EncryptionType.None;
|
||||
/// <summary>The type of encryption to use when encrypting data, if any.</summary>
|
||||
public ES3.CompressionType compressionType = ES3.CompressionType.None;
|
||||
/// <summary>The password to use when encrypting data.</summary>
|
||||
public string encryptionPassword = "password";
|
||||
/// <summary>The default directory in which to store files, and the location which relative paths should be relative to.</summary>
|
||||
public ES3.Directory directory = ES3.Directory.PersistentDataPath;
|
||||
/// <summary>What format to use when serialising and deserialising data.</summary>
|
||||
public ES3.Format format = ES3.Format.JSON;
|
||||
/// <summary>Whether we want to pretty print JSON.</summary>
|
||||
public bool prettyPrint = true;
|
||||
/// <summary>Any stream buffers will be set to this length in bytes.</summary>
|
||||
public int bufferSize = 2048;
|
||||
/// <summary>The text encoding to use for text-based format. Note that changing this may invalidate previous save data.</summary>
|
||||
public System.Text.Encoding encoding = System.Text.Encoding.UTF8;
|
||||
// <summary>Whether we should serialise children when serialising a GameObject.</summary>
|
||||
public bool saveChildren = true;
|
||||
// <summary>Whether we should apply encryption and/or compression to raw cached data if they're specified in the cached data's settings.</summary>
|
||||
public bool postprocessRawCachedData = false;
|
||||
|
||||
/// <summary>Whether we should check that the data we are loading from a file matches the method we are using to load it.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public bool typeChecking = true;
|
||||
|
||||
/// <summary>Enabling this ensures that only serialisable fields are serialised. Otherwise, possibly unsafe fields and properties will be serialised.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public bool safeReflection = true;
|
||||
/// <summary>Whether UnityEngine.Object members should be stored by value, reference or both.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public ES3.ReferenceMode memberReferenceMode = ES3.ReferenceMode.ByRef;
|
||||
/// <summary>Whether the main save methods should save UnityEngine.Objects by value, reference, or both.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public ES3.ReferenceMode referenceMode = ES3.ReferenceMode.ByRefAndValue;
|
||||
|
||||
/// <summary>How many levels of hierarchy Easy Save will serialise. This is used to protect against cyclic references.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public int serializationDepthLimit = 64;
|
||||
|
||||
/// <summary>The names of the Assemblies we should try to load our ES3Types from.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public string[] assemblyNames = new string[] { "Assembly-CSharp-firstpass", "Assembly-CSharp"};
|
||||
|
||||
/// <summary>Gets the full, absolute path which this ES3Settings object identifies.</summary>
|
||||
public string FullPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (path == null)
|
||||
throw new System.NullReferenceException("The 'path' field of this ES3Settings is null, indicating that it was not possible to load the default settings from Resources. Please check that the ES3 Default Settings.prefab exists in Assets/Plugins/Resources/ES3/");
|
||||
|
||||
if(IsAbsolute(path))
|
||||
return path;
|
||||
|
||||
if(location == ES3.Location.File)
|
||||
{
|
||||
if(directory == ES3.Directory.PersistentDataPath)
|
||||
return ES3IO.persistentDataPath + "/" + path;
|
||||
if(directory == ES3.Directory.DataPath)
|
||||
return Application.dataPath + "/" + path;
|
||||
throw new System.NotImplementedException("File directory \""+directory+"\" has not been implemented.");
|
||||
}
|
||||
if(location == ES3.Location.Resources)
|
||||
{
|
||||
// Check that it has valid extension
|
||||
var extension = System.IO.Path.GetExtension(path);
|
||||
bool hasValidExtension = false;
|
||||
foreach (var ext in resourcesExtensions)
|
||||
{
|
||||
if (extension == ext)
|
||||
{
|
||||
hasValidExtension = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasValidExtension)
|
||||
throw new System.ArgumentException("Extension of file in Resources must be .json, .bytes, .txt, .csv, .htm, .html, .xml, .yaml or .fnt, but path given was \"" + path + "\"");
|
||||
|
||||
// Remove extension
|
||||
string resourcesPath = path.Replace(extension, "");
|
||||
return resourcesPath;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>Creates a new ES3Settings object with the given path.</summary>
|
||||
/// <param name="path">The path associated with this ES3Settings object.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public ES3Settings(string path = null, ES3Settings settings = null) : this(true)
|
||||
{
|
||||
// if there are settings to merge, merge them.
|
||||
if (settings != null)
|
||||
settings.CopyInto(this);
|
||||
|
||||
if (path != null)
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Settings object with the given path.</summary>
|
||||
/// <param name="path">The path associated with this ES3Settings object.</param>
|
||||
/// <param name="enums">Accepts an ES3.EncryptionType, ES3.CompressionType, ES3.Location, ES3.Directory or ES3.ReferenceMode.</param>
|
||||
public ES3Settings(string path, params System.Enum[] enums) : this(enums)
|
||||
{
|
||||
if (path != null)
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Creates a new ES3Settings object with the given path.</summary>
|
||||
/// <param name="path">The path associated with this ES3Settings object.</param>
|
||||
/// <param name="enums">Accepts an ES3.EncryptionType, ES3.CompressionType, ES3.Location, ES3.Directory or ES3.ReferenceMode.</param>
|
||||
public ES3Settings(params System.Enum[] enums) : this(true)
|
||||
{
|
||||
foreach (var setting in enums)
|
||||
{
|
||||
if (setting is ES3.EncryptionType)
|
||||
this.encryptionType = (ES3.EncryptionType)setting;
|
||||
else if (setting is ES3.Location)
|
||||
this.location = (ES3.Location)setting;
|
||||
else if (setting is ES3.CompressionType)
|
||||
this.compressionType = (ES3.CompressionType)setting;
|
||||
else if (setting is ES3.ReferenceMode)
|
||||
this.referenceMode = (ES3.ReferenceMode)setting;
|
||||
else if (setting is ES3.Format)
|
||||
this.format = (ES3.Format)setting;
|
||||
else if (setting is ES3.Directory)
|
||||
this.directory = (ES3.Directory)setting;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Settings object with the given encryption settings.</summary>
|
||||
/// <param name="encryptionType">The type of encryption to use, if any.</param>
|
||||
/// <param name="encryptionPassword">The password to use when encrypting data.</param>
|
||||
public ES3Settings(ES3.EncryptionType encryptionType, string encryptionPassword) : this(true)
|
||||
{
|
||||
this.encryptionType = encryptionType;
|
||||
this.encryptionPassword = encryptionPassword;
|
||||
}
|
||||
|
||||
/// <summary>Creates a new ES3Settings object with the given path and encryption settings.</summary>
|
||||
/// <param name="path">The path associated with this ES3Settings object.</param>
|
||||
/// <param name="encryptionType">The type of encryption to use, if any.</param>
|
||||
/// <param name="encryptionPassword">The password to use when encrypting data.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public ES3Settings(string path, ES3.EncryptionType encryptionType, string encryptionPassword, ES3Settings settings = null) : this(path, settings)
|
||||
{
|
||||
this.encryptionType = encryptionType;
|
||||
this.encryptionPassword = encryptionPassword;
|
||||
}
|
||||
|
||||
/* Base constructor which allows us to bypass defaults so it can be called by Editor serialization */
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public ES3Settings(bool applyDefaults)
|
||||
{
|
||||
if (applyDefaults)
|
||||
if (defaultSettings != null)
|
||||
_defaults.CopyInto(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Editor methods
|
||||
#if UNITY_EDITOR
|
||||
public static string pathToEasySaveFolder = null;
|
||||
|
||||
public static string PathToEasySaveFolder()
|
||||
{
|
||||
// If the path has not yet been cached, get the path and cache it.
|
||||
if (string.IsNullOrEmpty(pathToEasySaveFolder))
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("ES3Window");
|
||||
if (guids.Length == 0)
|
||||
ES3Debug.LogError("Could not locate the Easy Save 3 folder because the ES3Window script has been moved or removed.");
|
||||
if (guids.Length > 1)
|
||||
ES3Debug.LogError("Could not locate the Easy Save 3 folder because more than one ES3Window script exists in the project, but this needs to be unique to locate the folder.");
|
||||
|
||||
pathToEasySaveFolder = AssetDatabase.GUIDToAssetPath(guids[0]).Split(new string[] { "Editor" }, System.StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
}
|
||||
return pathToEasySaveFolder;
|
||||
}
|
||||
|
||||
internal static string PathToDefaultSettings()
|
||||
{
|
||||
return PathToEasySaveFolder() + "Resources/"+defaultSettingsPath+".asset";
|
||||
}
|
||||
|
||||
internal static void CreateDefaultSettingsFolder()
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(PathToEasySaveFolder() + "Resources/ES3"))
|
||||
return;
|
||||
// Remove leading slash from PathToEasySaveFolder.
|
||||
AssetDatabase.CreateFolder(PathToEasySaveFolder().Remove(PathToEasySaveFolder().Length - 1, 1), "Resources");
|
||||
AssetDatabase.CreateFolder(PathToEasySaveFolder() + "Resources", "ES3");
|
||||
}
|
||||
|
||||
private static ES3SerializableSettings GetOldSettings()
|
||||
{
|
||||
var go = Resources.Load<GameObject>(defaultSettingsPath.Replace("ES3Defaults", "ES3 Default Settings"));
|
||||
if(go != null)
|
||||
{
|
||||
var c = go.GetComponent<ES3DefaultSettings>();
|
||||
if (c != null && c.settings != null)
|
||||
return c.settings;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void RemoveOldSettings()
|
||||
{
|
||||
AssetDatabase.DeleteAsset(PathToDefaultSettings().Replace("ES3Defaults.asset", "ES3 Default Settings.prefab"));
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Utility methods
|
||||
|
||||
private static bool IsAbsolute(string path)
|
||||
{
|
||||
if (path.Length > 0 && (path[0] == '/' || path[0] == '\\'))
|
||||
return true;
|
||||
if (path.Length > 1 && path[1] == ':')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public object Clone()
|
||||
{
|
||||
var settings = new ES3Settings();
|
||||
CopyInto(settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
private void CopyInto(ES3Settings newSettings)
|
||||
{
|
||||
newSettings._location = _location;
|
||||
newSettings.directory = directory;
|
||||
newSettings.format = format;
|
||||
newSettings.prettyPrint = prettyPrint;
|
||||
newSettings.path = path;
|
||||
newSettings.encryptionType = encryptionType;
|
||||
newSettings.encryptionPassword = encryptionPassword;
|
||||
newSettings.compressionType = compressionType;
|
||||
newSettings.bufferSize = bufferSize;
|
||||
newSettings.encoding = encoding;
|
||||
newSettings.typeChecking = typeChecking;
|
||||
newSettings.safeReflection = safeReflection;
|
||||
newSettings.referenceMode = referenceMode;
|
||||
newSettings.memberReferenceMode = memberReferenceMode;
|
||||
newSettings.assemblyNames = assemblyNames;
|
||||
newSettings.saveChildren = saveChildren;
|
||||
newSettings.serializationDepthLimit = serializationDepthLimit;
|
||||
newSettings.postprocessRawCachedData = postprocessRawCachedData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/*
|
||||
* A serializable version of the settings we can use as a field in the Editor, which doesn't automatically
|
||||
* assign defaults to itself, so we get no serialization errors.
|
||||
*/
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[System.Serializable]
|
||||
public class ES3SerializableSettings : ES3Settings
|
||||
{
|
||||
public ES3SerializableSettings() : base(false){}
|
||||
public ES3SerializableSettings(bool applyDefaults) : base(applyDefaults){}
|
||||
public ES3SerializableSettings(string path) : base(false) { this.path = path; }
|
||||
public ES3SerializableSettings(string path, ES3.Location location) : base(false) { this.location = location; }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public bool showAdvancedSettings = false;
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf4d7bb311b6afc41bd5773bf9d623fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Streams.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Streams.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b3a89e58c65c1b418218918643c426f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,68 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public enum ES3FileMode {Read, Write, Append}
|
||||
|
||||
public class ES3FileStream : FileStream
|
||||
{
|
||||
private bool isDisposed = false;
|
||||
|
||||
public ES3FileStream( string path, ES3FileMode fileMode, int bufferSize, bool useAsync)
|
||||
: base( GetPath(path, fileMode), GetFileMode(fileMode), GetFileAccess(fileMode), FileShare.None, bufferSize, useAsync)
|
||||
{
|
||||
}
|
||||
|
||||
// Gets a temporary path if necessary.
|
||||
protected static string GetPath(string path, ES3FileMode fileMode)
|
||||
{
|
||||
string directoryPath = ES3IO.GetDirectoryPath(path);
|
||||
// Attempt to create the directory incase it does not exist if we are storing data.
|
||||
if (fileMode != ES3FileMode.Read && directoryPath != ES3IO.persistentDataPath)
|
||||
ES3IO.CreateDirectory(directoryPath);
|
||||
if(fileMode != ES3FileMode.Write || fileMode == ES3FileMode.Append)
|
||||
return path;
|
||||
return (fileMode == ES3FileMode.Write) ? path + ES3IO.temporaryFileSuffix : path;
|
||||
}
|
||||
|
||||
protected static FileMode GetFileMode(ES3FileMode fileMode)
|
||||
{
|
||||
if (fileMode == ES3FileMode.Read)
|
||||
return FileMode.Open;
|
||||
else if (fileMode == ES3FileMode.Write)
|
||||
return FileMode.Create;
|
||||
else
|
||||
return FileMode.Append;
|
||||
}
|
||||
|
||||
protected static FileAccess GetFileAccess(ES3FileMode fileMode)
|
||||
{
|
||||
if (fileMode == ES3FileMode.Read)
|
||||
return FileAccess.Read;
|
||||
else if (fileMode == ES3FileMode.Write)
|
||||
return FileAccess.Write;
|
||||
else
|
||||
return FileAccess.Write;
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
// Ensure we only perform disposable once.
|
||||
if(isDisposed)
|
||||
return;
|
||||
isDisposed = true;
|
||||
|
||||
base.Dispose(disposing);
|
||||
|
||||
|
||||
// If this is a file writer, we need to replace the temp file.
|
||||
/*if(fileMode == ES3FileMode.Write && fileMode != ES3FileMode.Append)
|
||||
{
|
||||
// Delete the old file before overwriting it.
|
||||
ES3IO.DeleteFile(path);
|
||||
// Rename temporary file to new file.
|
||||
ES3IO.MoveFile(path + ES3.temporaryFileSuffix, path);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 202b4849a1146f84cbb875d7f17a56b6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,63 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
internal class ES3PlayerPrefsStream : MemoryStream
|
||||
{
|
||||
private string path;
|
||||
private bool append;
|
||||
private bool isWriteStream = false;
|
||||
private bool isDisposed = false;
|
||||
|
||||
// This constructor should be used for read streams only.
|
||||
public ES3PlayerPrefsStream(string path) : base(GetData(path,false))
|
||||
{
|
||||
this.path = path;
|
||||
this.append = false;
|
||||
}
|
||||
|
||||
// This constructor should be used for write streams only.
|
||||
public ES3PlayerPrefsStream(string path, int bufferSize, bool append=false) : base(bufferSize)
|
||||
{
|
||||
this.path = path;
|
||||
this.append = append;
|
||||
this.isWriteStream = true;
|
||||
}
|
||||
|
||||
private static byte[] GetData(string path, bool isWriteStream)
|
||||
{
|
||||
if(!PlayerPrefs.HasKey(path))
|
||||
throw new FileNotFoundException("File \""+path+"\" could not be found in PlayerPrefs");
|
||||
return System.Convert.FromBase64String(PlayerPrefs.GetString(path));
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if(isDisposed)
|
||||
return;
|
||||
isDisposed = true;
|
||||
if(isWriteStream && this.Length > 0)
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
// Convert data back to bytes before appending, as appending Base-64 strings directly can corrupt the data.
|
||||
var sourceBytes = System.Convert.FromBase64String(PlayerPrefs.GetString(path));
|
||||
var appendBytes = this.ToArray();
|
||||
var finalBytes = new byte[sourceBytes.Length + appendBytes.Length];
|
||||
System.Buffer.BlockCopy(sourceBytes, 0, finalBytes, 0, sourceBytes.Length);
|
||||
System.Buffer.BlockCopy(appendBytes, 0, finalBytes, sourceBytes.Length, appendBytes.Length);
|
||||
|
||||
PlayerPrefs.SetString(path, System.Convert.ToBase64String(finalBytes));
|
||||
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
else
|
||||
PlayerPrefs.SetString(path + ES3IO.temporaryFileSuffix, System.Convert.ToBase64String(this.ToArray()));
|
||||
// Save the timestamp to a separate key.
|
||||
PlayerPrefs.SetString("timestamp_" + path, System.DateTime.UtcNow.Ticks.ToString());
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b9abb6e182b38e44b4060906605701f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,32 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
internal class ES3ResourcesStream : MemoryStream
|
||||
{
|
||||
// Check that data exists by checking stream is not empty.
|
||||
public bool Exists{ get{ return this.Length > 0; } }
|
||||
|
||||
// Used when creating
|
||||
public ES3ResourcesStream(string path) : base(GetData(path))
|
||||
{
|
||||
}
|
||||
|
||||
private static byte[] GetData(string path)
|
||||
{
|
||||
var textAsset = Resources.Load(path) as TextAsset;
|
||||
|
||||
// If data doesn't exist in Resources, return an empty byte array.
|
||||
if(textAsset == null)
|
||||
return new byte[0];
|
||||
|
||||
return textAsset.bytes;
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f791d53f385e2f94681f83648eb6a5d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
122
Convention/[ES3]/Easy Save 3/Scripts/Streams/ES3Stream.cs
Normal file
122
Convention/[ES3]/Easy Save 3/Scripts/Streams/ES3Stream.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public static class ES3Stream
|
||||
{
|
||||
public static Stream CreateStream(ES3Settings settings, ES3FileMode fileMode)
|
||||
{
|
||||
bool isWriteStream = (fileMode != ES3FileMode.Read);
|
||||
Stream stream = null;
|
||||
|
||||
// Check that the path is in a valid format. This will throw an exception if not.
|
||||
new FileInfo(settings.FullPath);
|
||||
|
||||
try
|
||||
{
|
||||
if (settings.location == ES3.Location.InternalMS)
|
||||
{
|
||||
// There's no point in creating an empty MemoryStream if we're only reading from it.
|
||||
if (!isWriteStream)
|
||||
return null;
|
||||
stream = new MemoryStream(settings.bufferSize);
|
||||
}
|
||||
else if (settings.location == ES3.Location.File)
|
||||
{
|
||||
if (!isWriteStream && !ES3IO.FileExists(settings.FullPath))
|
||||
return null;
|
||||
stream = new ES3FileStream(settings.FullPath, fileMode, settings.bufferSize, false);
|
||||
}
|
||||
else if (settings.location == ES3.Location.PlayerPrefs)
|
||||
{
|
||||
if (isWriteStream)
|
||||
stream = new ES3PlayerPrefsStream(settings.FullPath, settings.bufferSize, (fileMode == ES3FileMode.Append));
|
||||
else
|
||||
{
|
||||
if (!PlayerPrefs.HasKey(settings.FullPath))
|
||||
return null;
|
||||
stream = new ES3PlayerPrefsStream(settings.FullPath);
|
||||
}
|
||||
}
|
||||
else if (settings.location == ES3.Location.Resources)
|
||||
{
|
||||
if (!isWriteStream)
|
||||
{
|
||||
var resourcesStream = new ES3ResourcesStream(settings.FullPath);
|
||||
if (resourcesStream.Exists)
|
||||
stream = resourcesStream;
|
||||
else
|
||||
{
|
||||
resourcesStream.Dispose();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (UnityEngine.Application.isEditor)
|
||||
throw new System.NotSupportedException("Cannot write directly to Resources folder. Try writing to a directory outside of Resources, and then manually move the file there.");
|
||||
else
|
||||
throw new System.NotSupportedException("Cannot write to Resources folder at runtime. Use a different save location at runtime instead.");
|
||||
}
|
||||
|
||||
return CreateStream(stream, settings, fileMode);
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Dispose();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Stream CreateStream(Stream stream, ES3Settings settings, ES3FileMode fileMode)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isWriteStream = (fileMode != ES3FileMode.Read);
|
||||
|
||||
#if !DISABLE_ENCRYPTION
|
||||
// Encryption
|
||||
if(settings.encryptionType != ES3.EncryptionType.None && stream.GetType() != typeof(UnbufferedCryptoStream))
|
||||
{
|
||||
EncryptionAlgorithm alg = null;
|
||||
if(settings.encryptionType == ES3.EncryptionType.AES)
|
||||
alg = new AESEncryptionAlgorithm();
|
||||
stream = new UnbufferedCryptoStream(stream, !isWriteStream, settings.encryptionPassword, settings.bufferSize, alg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compression
|
||||
if (settings.compressionType != ES3.CompressionType.None && stream.GetType() != typeof(GZipStream))
|
||||
{
|
||||
if (settings.compressionType == ES3.CompressionType.Gzip)
|
||||
stream = isWriteStream ? new GZipStream(stream, CompressionMode.Compress) : new GZipStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Dispose();
|
||||
if (e.GetType() == typeof(System.Security.Cryptography.CryptographicException))
|
||||
throw new System.Security.Cryptography.CryptographicException("Could not decrypt file. Please ensure that you are using the same password used to encrypt the file.");
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyTo(Stream source, Stream destination)
|
||||
{
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
source.CopyTo(destination);
|
||||
#else
|
||||
byte[] buffer = new byte[2048];
|
||||
int bytesRead;
|
||||
while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
|
||||
destination.Write(buffer, 0, bytesRead);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95fcefc93e7d25546819082901d9607c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[ES3]/Easy Save 3/Scripts/Types.meta
Normal file
8
Convention/[ES3]/Easy Save 3/Scripts/Types.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d191aa55b73707439fb0f2c17a89c49
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08b45565427bf3e41aa704e8d6727206
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
using System.Linq;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
public class ES32DArrayType : ES3CollectionType
|
||||
{
|
||||
public ES32DArrayType(Type type) : base(type){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode unityObjectType)
|
||||
{
|
||||
var array = (System.Array)obj;
|
||||
|
||||
if(elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
//writer.StartWriteCollection();
|
||||
|
||||
for(int i=0; i < array.GetLength(0); i++)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.StartWriteCollection();
|
||||
for(int j=0; j < array.GetLength(1); j++)
|
||||
{
|
||||
writer.StartWriteCollectionItem(j);
|
||||
writer.Write(array.GetValue(i,j), elementType, unityObjectType);
|
||||
writer.EndWriteCollectionItem(j);
|
||||
}
|
||||
writer.EndWriteCollection();
|
||||
writer.EndWriteCollectionItem(i);
|
||||
}
|
||||
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
/*if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
// Create a List to store the items as a 1D array, which we can work out the positions of by calculating the lengths of the two dimensions.
|
||||
var items = new List<T>();
|
||||
int length1 = 0;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
ReadICollection<T>(reader, items, elementType);
|
||||
length1++;
|
||||
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
int length2 = items.Count / length1;
|
||||
|
||||
var array = new T[length1,length2];
|
||||
|
||||
for(int i=0; i<length1; i++)
|
||||
for(int j=0; j<length2; j++)
|
||||
array[i,j] = items[ (i * length2) + j ];
|
||||
|
||||
return array;*/
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
// Create a List to store the items as a 1D array, which we can work out the positions of by calculating the lengths of the two dimensions.
|
||||
var items = new List<object>();
|
||||
int length1 = 0;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
ReadICollection<object>(reader, items, elementType);
|
||||
length1++;
|
||||
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
int length2 = items.Count / length1;
|
||||
|
||||
var array = ES3Reflection.ArrayCreateInstance(elementType.type, new int[]{length1, length2});
|
||||
|
||||
for(int i=0; i<length1; i++)
|
||||
for(int j=0; j<length2; j++)
|
||||
array.SetValue(items[ (i * length2) + j ], i, j);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadInto(reader, obj);
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
var array = (Array)obj;
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
bool iHasBeenRead = false;
|
||||
|
||||
for(int i=0; i < array.GetLength(0); i++)
|
||||
{
|
||||
bool jHasBeenRead = false;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
throw new IndexOutOfRangeException("The collection we are loading is smaller than the collection provided as a parameter.");
|
||||
|
||||
reader.StartReadCollection();
|
||||
for(int j=0; j < array.GetLength(1); j++)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
throw new IndexOutOfRangeException("The collection we are loading is smaller than the collection provided as a parameter.");
|
||||
reader.ReadInto<object>(array.GetValue(i,j), elementType);
|
||||
jHasBeenRead = reader.EndReadCollectionItem();
|
||||
}
|
||||
|
||||
if(!jHasBeenRead)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is larger than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
iHasBeenRead = reader.EndReadCollectionItem();
|
||||
}
|
||||
|
||||
if(!iHasBeenRead)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is larger than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0813a324efb0f1948a7aec763c86d909
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
public class ES33DArrayType : ES3CollectionType
|
||||
{
|
||||
public ES33DArrayType(Type type) : base(type){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
var array = (System.Array)obj;
|
||||
|
||||
if(elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
//writer.StartWriteCollection();
|
||||
|
||||
for(int i=0; i < array.GetLength(0); i++)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.StartWriteCollection();
|
||||
|
||||
for(int j=0; j < array.GetLength(1); j++)
|
||||
{
|
||||
writer.StartWriteCollectionItem(j);
|
||||
writer.StartWriteCollection();
|
||||
|
||||
for(int k=0; k < array.GetLength(2); k++)
|
||||
{
|
||||
writer.StartWriteCollectionItem(k);
|
||||
writer.Write(array.GetValue(i,j,k), elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(k);
|
||||
}
|
||||
writer.EndWriteCollection();
|
||||
writer.EndWriteCollectionItem(j);
|
||||
}
|
||||
writer.EndWriteCollection();
|
||||
writer.EndWriteCollectionItem(i);
|
||||
}
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
// Create a List to store the items as a 1D array, which we can work out the positions of by calculating the lengths of the two dimensions.
|
||||
var items = new List<object>();
|
||||
int length1 = 0;
|
||||
int length2 = 0;
|
||||
|
||||
// Iterate through each sub-array
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
reader.StartReadCollection();
|
||||
|
||||
length1++;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
ReadICollection<object>(reader, items, elementType);
|
||||
length2++;
|
||||
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
length2 = length2/length1;
|
||||
int length3 = items.Count / length2 / length1;
|
||||
|
||||
var array = ES3Reflection.ArrayCreateInstance(elementType.type, new int[]{length1,length2,length3});
|
||||
|
||||
for(int i=0; i<length1; i++)
|
||||
for(int j=0; j<length2; j++)
|
||||
for(int k=0; k<length3; k++)
|
||||
array.SetValue(items[i * (length2*length3) + (j * length3) + k], i, j, k);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadInto(reader, obj);
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
var array = (Array)obj;
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
bool iHasBeenRead = false;
|
||||
|
||||
for(int i=0; i < array.GetLength(0); i++)
|
||||
{
|
||||
bool jHasBeenRead = false;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
throw new IndexOutOfRangeException("The collection we are loading is smaller than the collection provided as a parameter.");
|
||||
|
||||
reader.StartReadCollection();
|
||||
|
||||
for(int j=0; j < array.GetLength(1); j++)
|
||||
{
|
||||
bool kHasBeenRead = false;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
throw new IndexOutOfRangeException("The collection we are loading is smaller than the collection provided as a parameter.");
|
||||
|
||||
reader.StartReadCollection();
|
||||
|
||||
for(int k=0; k < array.GetLength(2); k++)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
throw new IndexOutOfRangeException("The collection we are loading is smaller than the collection provided as a parameter.");
|
||||
reader.ReadInto<object>(array.GetValue(i,j,k), elementType);
|
||||
kHasBeenRead = reader.EndReadCollectionItem();
|
||||
}
|
||||
|
||||
if(!kHasBeenRead)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is larger than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
jHasBeenRead = reader.EndReadCollectionItem();
|
||||
}
|
||||
|
||||
if(!jHasBeenRead)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is larger than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
iHasBeenRead = reader.EndReadCollectionItem();
|
||||
}
|
||||
|
||||
if(!iHasBeenRead)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is larger than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38dd37192ea78be47bfd431c8058f3a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3ArrayType : ES3CollectionType
|
||||
{
|
||||
public ES3ArrayType(Type type) : base(type){}
|
||||
public ES3ArrayType(Type type, ES3Type elementType) : base(type, elementType){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
var array = (System.Array)obj;
|
||||
|
||||
if(elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
//writer.StartWriteCollection();
|
||||
|
||||
for(int i=0; i<array.Length; i++)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(array.GetValue(i), elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
}
|
||||
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
var list = new List<object>();
|
||||
if (!ReadICollection(reader, list, elementType))
|
||||
return null;
|
||||
|
||||
var array = ES3Reflection.ArrayCreateInstance(elementType.type, list.Count);
|
||||
int i = 0;
|
||||
foreach (var item in list)
|
||||
{
|
||||
array.SetValue(item, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadICollectionInto(reader, (ICollection)obj, elementType);
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
var collection = (IList)obj;
|
||||
|
||||
if (collection.Count == 0)
|
||||
ES3Debug.LogWarning("LoadInto/ReadInto expects a collection containing instances to load data in to, but the collection is empty.");
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in collection)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<object>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eebff3a1c253e2e4392b6992c65c6e21
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public abstract class ES3CollectionType : ES3Type
|
||||
{
|
||||
public ES3Type elementType;
|
||||
|
||||
/*protected ES3Reflection.ES3ReflectedMethod readMethod = null;
|
||||
protected ES3Reflection.ES3ReflectedMethod readIntoMethod = null;*/
|
||||
|
||||
public abstract object Read(ES3Reader reader);
|
||||
public abstract void ReadInto(ES3Reader reader, object obj);
|
||||
public abstract void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode);
|
||||
|
||||
public ES3CollectionType(Type type) : base(type)
|
||||
{
|
||||
elementType = ES3TypeMgr.GetOrCreateES3Type(ES3Reflection.GetElementTypes(type)[0], false);
|
||||
isCollection = true;
|
||||
|
||||
// If the element type is null (i.e. unsupported), make this ES3Type null.
|
||||
if(elementType == null)
|
||||
isUnsupported = true;
|
||||
}
|
||||
|
||||
public ES3CollectionType(Type type, ES3Type elementType) : base(type)
|
||||
{
|
||||
this.elementType = elementType;
|
||||
isCollection = true;
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
Write(obj, writer, ES3.ReferenceMode.ByRefAndValue);
|
||||
}
|
||||
|
||||
protected virtual bool ReadICollection<T>(ES3Reader reader, ICollection<T> collection, ES3Type elementType)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
return false;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
collection.Add(reader.Read<T>(elementType));
|
||||
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void ReadICollectionInto<T>(ES3Reader reader, ICollection<T> collection, ES3Type elementType)
|
||||
{
|
||||
ReadICollectionInto(reader, collection, elementType);
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
protected virtual void ReadICollectionInto(ES3Reader reader, ICollection collection, ES3Type elementType)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in collection)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<object>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls the Read method using reflection so we don't need to provide a generic parameter.
|
||||
*/
|
||||
/*public virtual object Read(ES3Reader reader)
|
||||
{
|
||||
if(readMethod == null)
|
||||
readMethod = ES3Reflection.GetMethod(this.GetType(), "Read", new Type[]{elementType.type}, new Type[]{typeof(ES3Reader)});
|
||||
return readMethod.Invoke(this, new object[]{reader});
|
||||
}
|
||||
|
||||
public virtual void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
if(readIntoMethod == null)
|
||||
readIntoMethod = ES3Reflection.GetMethod(this.GetType(), "ReadInto", new Type[]{elementType.type}, new Type[]{typeof(ES3Reader), typeof(object)});
|
||||
readIntoMethod.Invoke(this, new object[]{reader, obj});
|
||||
}*/
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa931a93f042fa641a12a73910b90ba4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3ConcurrentDictionaryType : ES3Type
|
||||
{
|
||||
public ES3Type keyType;
|
||||
public ES3Type valueType;
|
||||
|
||||
protected ES3Reflection.ES3ReflectedMethod readMethod = null;
|
||||
protected ES3Reflection.ES3ReflectedMethod readIntoMethod = null;
|
||||
|
||||
public ES3ConcurrentDictionaryType(Type type) : base(type)
|
||||
{
|
||||
var types = ES3Reflection.GetElementTypes(type);
|
||||
keyType = ES3TypeMgr.GetOrCreateES3Type(types[0], false);
|
||||
valueType = ES3TypeMgr.GetOrCreateES3Type(types[1], false);
|
||||
|
||||
// If either the key or value type is unsupported, make this type NULL.
|
||||
if(keyType == null || valueType == null)
|
||||
isUnsupported = true;;
|
||||
|
||||
isDictionary = true;
|
||||
}
|
||||
|
||||
public ES3ConcurrentDictionaryType(Type type, ES3Type keyType, ES3Type valueType) : base(type)
|
||||
{
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
|
||||
// If either the key or value type is unsupported, make this type NULL.
|
||||
if (keyType == null || valueType == null)
|
||||
isUnsupported = true; ;
|
||||
|
||||
isDictionary = true;
|
||||
}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
Write(obj, writer, writer.settings.memberReferenceMode);
|
||||
}
|
||||
|
||||
public void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
var dict = (IDictionary)obj;
|
||||
|
||||
//writer.StartWriteDictionary(dict.Count);
|
||||
|
||||
int i=0;
|
||||
foreach(System.Collections.DictionaryEntry kvp in dict)
|
||||
{
|
||||
writer.StartWriteDictionaryKey(i);
|
||||
writer.Write(kvp.Key, keyType, memberReferenceMode);
|
||||
writer.EndWriteDictionaryKey(i);
|
||||
writer.StartWriteDictionaryValue(i);
|
||||
writer.Write(kvp.Value, valueType, memberReferenceMode);
|
||||
writer.EndWriteDictionaryValue(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
//writer.EndWriteDictionary();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadInto(reader, obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows us to call the generic Read method using Reflection so we can define the generic parameter at runtime.
|
||||
* It also caches the method to improve performance in later calls.
|
||||
*/
|
||||
public object Read(ES3Reader reader)
|
||||
{
|
||||
if(reader.StartReadDictionary())
|
||||
return null;
|
||||
|
||||
var dict = (IDictionary)ES3Reflection.CreateInstance(type);
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadDictionaryKey())
|
||||
return dict;
|
||||
var key = reader.Read<object>(keyType);
|
||||
reader.EndReadDictionaryKey();
|
||||
|
||||
reader.StartReadDictionaryValue();
|
||||
var value = reader.Read<object>(valueType);
|
||||
|
||||
dict.Add(key,value);
|
||||
|
||||
if(reader.EndReadDictionaryValue())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadDictionary();
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
if(reader.StartReadDictionary())
|
||||
throw new NullReferenceException("The Dictionary we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
var dict = (IDictionary)obj;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadDictionaryKey())
|
||||
return;
|
||||
var key = reader.Read<object>(keyType);
|
||||
|
||||
if(!dict.Contains(key))
|
||||
throw new KeyNotFoundException("The key \"" + key + "\" in the Dictionary we are loading does not exist in the Dictionary we are loading into");
|
||||
var value = dict[key];
|
||||
reader.EndReadDictionaryKey();
|
||||
|
||||
reader.StartReadDictionaryValue();
|
||||
|
||||
reader.ReadInto<object>(value, valueType);
|
||||
|
||||
if(reader.EndReadDictionaryValue())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadDictionary();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aba965aeff592474aa7bb680d68bbb8b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3DictionaryType : ES3Type
|
||||
{
|
||||
public ES3Type keyType;
|
||||
public ES3Type valueType;
|
||||
|
||||
protected ES3Reflection.ES3ReflectedMethod readMethod = null;
|
||||
protected ES3Reflection.ES3ReflectedMethod readIntoMethod = null;
|
||||
|
||||
public ES3DictionaryType(Type type) : base(type)
|
||||
{
|
||||
var types = ES3Reflection.GetElementTypes(type);
|
||||
keyType = ES3TypeMgr.GetOrCreateES3Type(types[0], false);
|
||||
valueType = ES3TypeMgr.GetOrCreateES3Type(types[1], false);
|
||||
|
||||
// If either the key or value type is unsupported, make this type NULL.
|
||||
if(keyType == null || valueType == null)
|
||||
isUnsupported = true;;
|
||||
|
||||
isDictionary = true;
|
||||
}
|
||||
|
||||
public ES3DictionaryType(Type type, ES3Type keyType, ES3Type valueType) : base(type)
|
||||
{
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
|
||||
// If either the key or value type is unsupported, make this type NULL.
|
||||
if (keyType == null || valueType == null)
|
||||
isUnsupported = true; ;
|
||||
|
||||
isDictionary = true;
|
||||
}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
Write(obj, writer, writer.settings.memberReferenceMode);
|
||||
}
|
||||
|
||||
public void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
var dict = (IDictionary)obj;
|
||||
|
||||
//writer.StartWriteDictionary(dict.Count);
|
||||
|
||||
int i=0;
|
||||
foreach(System.Collections.DictionaryEntry kvp in dict)
|
||||
{
|
||||
writer.StartWriteDictionaryKey(i);
|
||||
writer.Write(kvp.Key, keyType, memberReferenceMode);
|
||||
writer.EndWriteDictionaryKey(i);
|
||||
writer.StartWriteDictionaryValue(i);
|
||||
writer.Write(kvp.Value, valueType, memberReferenceMode);
|
||||
writer.EndWriteDictionaryValue(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
//writer.EndWriteDictionary();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadInto(reader, obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows us to call the generic Read method using Reflection so we can define the generic parameter at runtime.
|
||||
* It also caches the method to improve performance in later calls.
|
||||
*/
|
||||
public object Read(ES3Reader reader)
|
||||
{
|
||||
if(reader.StartReadDictionary())
|
||||
return null;
|
||||
|
||||
var dict = (IDictionary)ES3Reflection.CreateInstance(type);
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadDictionaryKey())
|
||||
return dict;
|
||||
var key = reader.Read<object>(keyType);
|
||||
reader.EndReadDictionaryKey();
|
||||
|
||||
reader.StartReadDictionaryValue();
|
||||
var value = reader.Read<object>(valueType);
|
||||
|
||||
dict.Add(key,value);
|
||||
|
||||
if(reader.EndReadDictionaryValue())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadDictionary();
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
if(reader.StartReadDictionary())
|
||||
throw new NullReferenceException("The Dictionary we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
var dict = (IDictionary)obj;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadDictionaryKey())
|
||||
return;
|
||||
var key = reader.Read<object>(keyType);
|
||||
|
||||
if(!dict.Contains(key))
|
||||
throw new KeyNotFoundException("The key \"" + key + "\" in the Dictionary we are loading does not exist in the Dictionary we are loading into");
|
||||
var value = dict[key];
|
||||
reader.EndReadDictionaryKey();
|
||||
|
||||
reader.StartReadDictionaryValue();
|
||||
|
||||
reader.ReadInto<object>(value, valueType);
|
||||
|
||||
if(reader.EndReadDictionaryValue())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadDictionary();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fb5a5ad7107dcd46b3d0f8eba4e347c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3HashSetType : ES3CollectionType
|
||||
{
|
||||
public ES3HashSetType(Type type) : base(type){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
if (obj == null) { writer.WriteNull(); return; };
|
||||
|
||||
var list = (IEnumerable)obj;
|
||||
|
||||
if (elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
int count = 0;
|
||||
foreach (var item in list)
|
||||
count++;
|
||||
|
||||
//writer.StartWriteCollection(count);
|
||||
|
||||
int i = 0;
|
||||
foreach (object item in list)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(item, elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
var val = Read(reader);
|
||||
if (val == null)
|
||||
return default(T);
|
||||
return (T)val;
|
||||
}
|
||||
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
/*var method = typeof(ES3CollectionType).GetMethod("ReadICollection", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(elementType.type);
|
||||
if(!(bool)method.Invoke(this, new object[] { reader, list, elementType }))
|
||||
return null;*/
|
||||
|
||||
var genericParam = ES3Reflection.GetGenericArguments(type)[0];
|
||||
var listType = ES3Reflection.MakeGenericType(typeof(List<>), genericParam);
|
||||
var list = (IList)ES3Reflection.CreateInstance(listType);
|
||||
|
||||
if (!reader.StartReadCollection())
|
||||
{
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while (true)
|
||||
{
|
||||
if (!reader.StartReadCollectionItem())
|
||||
break;
|
||||
list.Add(reader.Read<object>(elementType));
|
||||
|
||||
if (reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
|
||||
return ES3Reflection.CreateInstance(type, list);
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadInto(reader, obj);
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
throw new NotImplementedException("Cannot use LoadInto/ReadInto with HashSet because HashSets do not maintain the order of elements");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47613c14eaadb6247a534a8bd539c59d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3ListType : ES3CollectionType
|
||||
{
|
||||
public ES3ListType(Type type) : base(type){}
|
||||
public ES3ListType(Type type, ES3Type elementType) : base(type, elementType){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
if(obj == null){ writer.WriteNull(); return; };
|
||||
|
||||
var list = (IList)obj;
|
||||
|
||||
if(elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
//writer.StartWriteCollection();
|
||||
|
||||
int i = 0;
|
||||
foreach(object item in list)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(item, elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
|
||||
/*var list = new List<T>();
|
||||
if(!ReadICollection<T>(reader, list, elementType))
|
||||
return null;
|
||||
return list;*/
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadICollectionInto(reader, (ICollection)obj, elementType);
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
var instance = (IList)ES3Reflection.CreateInstance(type);
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
instance.Add(reader.Read<object>(elementType));
|
||||
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
var collection = (IList)obj;
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in collection)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<object>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 501388e28c8120c4bacc4cc60e24cba2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3NativeArrayType : ES3CollectionType
|
||||
{
|
||||
public ES3NativeArrayType(Type type) : base(type){}
|
||||
public ES3NativeArrayType(Type type, ES3Type elementType) : base(type, elementType){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
if (elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
var enumerable = (IEnumerable)obj;
|
||||
|
||||
int i = 0;
|
||||
foreach(var item in enumerable)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(item, elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
var array = ReadAsArray(reader);
|
||||
|
||||
return ES3Reflection.CreateInstance(type, new object[] { array, Allocator.Persistent });
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadInto(reader, obj);
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
var array = ReadAsArray(reader);
|
||||
var copyFromMethod = ES3Reflection.GetMethods(type, "CopyFrom").First(m => ES3Reflection.TypeIsArray(m.GetParameters()[0].GetType()));
|
||||
copyFromMethod.Invoke(obj, new object[] { array });
|
||||
}
|
||||
|
||||
private System.Array ReadAsArray(ES3Reader reader)
|
||||
{
|
||||
var list = new List<object>();
|
||||
if (!ReadICollection(reader, list, elementType))
|
||||
return null;
|
||||
|
||||
var array = ES3Reflection.ArrayCreateInstance(elementType.type, list.Count);
|
||||
int i = 0;
|
||||
foreach (var item in list)
|
||||
{
|
||||
array.SetValue(item, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d70922f971776da4484db1c7072f2de5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3QueueType : ES3CollectionType
|
||||
{
|
||||
public ES3QueueType(Type type) : base(type){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
var list = (ICollection)obj;
|
||||
|
||||
if(elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
//writer.StartWriteCollection();
|
||||
|
||||
int i = 0;
|
||||
foreach(object item in list)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(item, elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
/*if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
var queue = new Queue<T>();
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
queue.Enqueue(reader.Read<T>(elementType));
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
return queue;*/
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
var queue = (Queue<T>)obj;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in queue)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<T>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == queue.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != queue.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
var instance = (IList)ES3Reflection.CreateInstance(ES3Reflection.MakeGenericType(typeof(List<>), elementType.type));
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if (!reader.StartReadCollectionItem())
|
||||
break;
|
||||
instance.Add(reader.Read<object>(elementType));
|
||||
|
||||
if (reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
return ES3Reflection.CreateInstance(type, instance);
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
var collection = (ICollection)obj;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in collection)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<object>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51092dda0b02d4b4fb116a034303929f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3StackType : ES3CollectionType
|
||||
{
|
||||
public ES3StackType(Type type) : base(type){}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
var list = (ICollection)obj;
|
||||
|
||||
if(elementType == null)
|
||||
throw new ArgumentNullException("ES3Type argument cannot be null.");
|
||||
|
||||
//writer.StartWriteCollection();
|
||||
|
||||
int i = 0;
|
||||
foreach(object item in list)
|
||||
{
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(item, elementType, memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
//writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
/*if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
var stack = new Stack<T>();
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
stack.Push(reader.Read<T>(elementType));
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
return stack;*/
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
var stack = (Stack<T>)obj;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in stack)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<T>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == stack.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != stack.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
|
||||
public override object Read(ES3Reader reader)
|
||||
{
|
||||
var instance = (IList)ES3Reflection.CreateInstance(ES3Reflection.MakeGenericType(typeof(List<>), elementType.type));
|
||||
|
||||
if(reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
// Iterate through each character until we reach the end of the array.
|
||||
while(true)
|
||||
{
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
instance.Add(reader.Read<object>(elementType));
|
||||
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
ES3Reflection.GetMethods(instance.GetType(), "Reverse").FirstOrDefault(t => !t.IsStatic).Invoke(instance, new object[]{});
|
||||
return ES3Reflection.CreateInstance(type, instance);
|
||||
|
||||
}
|
||||
|
||||
public override void ReadInto(ES3Reader reader, object obj)
|
||||
{
|
||||
if(reader.StartReadCollection())
|
||||
throw new NullReferenceException("The Collection we are trying to load is stored as null, which is not allowed when using ReadInto methods.");
|
||||
|
||||
int itemsLoaded = 0;
|
||||
|
||||
var collection = (ICollection)obj;
|
||||
|
||||
// Iterate through each item in the collection and try to load it.
|
||||
foreach(var item in collection)
|
||||
{
|
||||
itemsLoaded++;
|
||||
|
||||
if(!reader.StartReadCollectionItem())
|
||||
break;
|
||||
|
||||
reader.ReadInto<object>(item, elementType);
|
||||
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(reader.EndReadCollectionItem())
|
||||
break;
|
||||
// If there's still items to load, but we've reached the end of the collection we're loading into, throw an error.
|
||||
if(itemsLoaded == collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is longer than the collection provided as a parameter.");
|
||||
}
|
||||
|
||||
// If we loaded fewer items than the parameter collection, throw index out of range exception.
|
||||
if(itemsLoaded != collection.Count)
|
||||
throw new IndexOutOfRangeException("The collection we are loading is shorter than the collection provided as a parameter.");
|
||||
|
||||
reader.EndReadCollection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ffdad2eecfa1cb42a4c1b5c18e66c9f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public class ES3TupleType : ES3Type
|
||||
{
|
||||
public ES3Type[] es3Types;
|
||||
public Type[] types;
|
||||
|
||||
protected ES3Reflection.ES3ReflectedMethod readMethod = null;
|
||||
protected ES3Reflection.ES3ReflectedMethod readIntoMethod = null;
|
||||
|
||||
public ES3TupleType(Type type) : base(type)
|
||||
{
|
||||
types = ES3Reflection.GetElementTypes(type);
|
||||
es3Types = new ES3Type[types.Length];
|
||||
|
||||
for(int i=0; i<types.Length; i++)
|
||||
{
|
||||
es3Types[i] = ES3TypeMgr.GetOrCreateES3Type(types[i], false);
|
||||
if (es3Types[i] == null)
|
||||
isUnsupported = true;
|
||||
}
|
||||
|
||||
isTuple = true;
|
||||
}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
Write(obj, writer, writer.settings.memberReferenceMode);
|
||||
}
|
||||
|
||||
public void Write(object obj, ES3Writer writer, ES3.ReferenceMode memberReferenceMode)
|
||||
{
|
||||
if (obj == null) { writer.WriteNull(); return; };
|
||||
|
||||
writer.StartWriteCollection();
|
||||
|
||||
for (int i=0; i<es3Types.Length; i++)
|
||||
{
|
||||
var itemProperty = ES3Reflection.GetProperty(type, "Item"+(i+1));
|
||||
var item = itemProperty.GetValue(obj);
|
||||
writer.StartWriteCollectionItem(i);
|
||||
writer.Write(item, es3Types[i], memberReferenceMode);
|
||||
writer.EndWriteCollectionItem(i);
|
||||
}
|
||||
|
||||
writer.EndWriteCollection();
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
var objects = new object[types.Length];
|
||||
|
||||
if (reader.StartReadCollection())
|
||||
return null;
|
||||
|
||||
for(int i=0; i<types.Length; i++)
|
||||
{
|
||||
reader.StartReadCollectionItem();
|
||||
objects[i] = reader.Read<object>(es3Types[i]);
|
||||
reader.EndReadCollectionItem();
|
||||
}
|
||||
|
||||
reader.EndReadCollection();
|
||||
|
||||
var constructor = type.GetConstructor(types);
|
||||
var instance = constructor.Invoke(objects);
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 017bab08a5ebeaf4d9b14a121c5aa8ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
145
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3ComponentType.cs
Normal file
145
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3ComponentType.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public abstract class ES3ComponentType : ES3UnityObjectType
|
||||
{
|
||||
public ES3ComponentType(Type type) : base(type) { }
|
||||
|
||||
protected abstract void WriteComponent(object obj, ES3Writer writer);
|
||||
protected abstract void ReadComponent<T>(ES3Reader reader, object obj);
|
||||
|
||||
protected const string gameObjectPropertyName = "goID";
|
||||
|
||||
protected override void WriteUnityObject(object obj, ES3Writer writer)
|
||||
{
|
||||
var instance = obj as Component;
|
||||
if (obj != null && instance == null)
|
||||
throw new ArgumentException("Only types of UnityEngine.Component can be written with this method, but argument given is type of " + obj.GetType());
|
||||
|
||||
var refMgr = ES3ReferenceMgrBase.Current;
|
||||
|
||||
if (refMgr != null)
|
||||
{
|
||||
// Write the reference of the GameObject so we know what one to attach it to.
|
||||
writer.WriteProperty(gameObjectPropertyName, refMgr.Add(instance.gameObject).ToString(), ES3Type_string.Instance);
|
||||
}
|
||||
WriteComponent(instance, writer);
|
||||
}
|
||||
|
||||
protected override void ReadUnityObject<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadComponent<T>(reader, obj);
|
||||
}
|
||||
|
||||
protected override object ReadUnityObject<T>(ES3Reader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/*
|
||||
* It's IMPORTANT that we override ReadObject in ES3UnityObjectType rather than use ReadUnityObject because otherwise the first IF statement will never be called,
|
||||
* and we will never get the reference ID for the Component we're loading, so if we create a new Component we cannot assign it's correct reference ID.
|
||||
*/
|
||||
protected override object ReadObject<T>(ES3Reader reader)
|
||||
{
|
||||
var refMgr = ES3ReferenceMgrBase.Current;
|
||||
long id = -1;
|
||||
UnityEngine.Object instance = null;
|
||||
|
||||
foreach (string propertyName in reader.Properties)
|
||||
{
|
||||
if (propertyName == ES3ReferenceMgrBase.referencePropertyName)
|
||||
{
|
||||
id = reader.Read_ref();
|
||||
instance = refMgr.Get(id, true);
|
||||
|
||||
/*if (instance != null)
|
||||
break;*/
|
||||
}
|
||||
else if (propertyName == gameObjectPropertyName)
|
||||
{
|
||||
long goID = reader.Read_ref();
|
||||
|
||||
// If we already have an instance for this Component, don't attempt to create a new GameObject for it.
|
||||
if (instance != null)
|
||||
break;
|
||||
|
||||
var go = (GameObject)refMgr.Get(goID, type);
|
||||
|
||||
if (go == null)
|
||||
{
|
||||
go = new GameObject("Easy Save 3 Loaded GameObject");
|
||||
#if UNITY_EDITOR
|
||||
go.AddComponent<ES3InspectorInfo>().SetMessage("This GameObject was created because Easy Save could not find a GameObject in the scene with the same instance ID as the GameObject the Component we are loading is attached to.\nTo prevent this from being created, use the LoadInto methods to tell Easy Save what Component the data should be loaded in to.");
|
||||
#endif
|
||||
refMgr.Add(go, goID);
|
||||
}
|
||||
instance = GetOrAddComponent(go, type);
|
||||
refMgr.Add(instance, id);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.overridePropertiesName = propertyName;
|
||||
if (instance == null)
|
||||
{
|
||||
var go = new GameObject("Easy Save 3 Loaded GameObject");
|
||||
#if UNITY_EDITOR
|
||||
go.AddComponent<ES3InspectorInfo>().SetMessage("This GameObject was created because Easy Save could not find a GameObject in the scene with the same instance ID as the GameObject the Component we are loading is attached to.\nTo prevent this from being created, use the LoadInto methods to tell Easy Save what Component the data should be loaded in to.");
|
||||
#endif
|
||||
instance = GetOrAddComponent(go, type);
|
||||
refMgr.Add(instance, id);
|
||||
refMgr.Add(go);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(instance != null)
|
||||
ReadComponent<T>(reader, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static Component GetOrAddComponent(GameObject go, Type type)
|
||||
{
|
||||
var c = go.GetComponent(type);
|
||||
if (c != null)
|
||||
return c;
|
||||
return go.AddComponent(type);
|
||||
|
||||
/*if (type == typeof(Transform))
|
||||
return go.GetComponent(type);
|
||||
// Manage types which can only have a single Component attached.
|
||||
else if (type == typeof(MeshFilter) || type.Name.ToString().Contains("Renderer") || ES3Reflection.AttributeIsDefined(type, typeof(DisallowMultipleComponent)))
|
||||
return GetOrCreateComponentIfNotExists(go, type);
|
||||
return go.AddComponent(type);*/
|
||||
}
|
||||
|
||||
public static Component CreateComponent(Type type)
|
||||
{
|
||||
GameObject go = new GameObject("Easy Save 3 Loaded Component");
|
||||
#if UNITY_EDITOR
|
||||
// If we're running in the Editor, add a description explaining why this object was created.
|
||||
go.AddComponent<ES3InspectorInfo>().SetMessage("This GameObject was created because Easy Save tried to load a Component with an instance ID which does not exist in this scene.\nTo prevent this from being created, use the LoadInto methods to tell Easy Save what Component the data should be loaded in to.\nThis can also happen if you load a class which references another object, but that object has not yet been loaded. In this case, you should load the object the class references before loading the class.");
|
||||
#endif
|
||||
if (type == typeof(Transform))
|
||||
return go.GetComponent(type);
|
||||
return GetOrAddComponent(go, type);
|
||||
}
|
||||
|
||||
// Creates a Component if one doesn't exist, or returns the existing instance.
|
||||
/*public static Component GetOrCreateComponentIfNotExists(GameObject go, Type type)
|
||||
{
|
||||
Component mf;
|
||||
if ((mf = go.GetComponent(type)) != null)
|
||||
return mf;
|
||||
return go.AddComponent(type);
|
||||
}*/
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d80e364752810aa44a16e5715ef4086f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
80
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3ObjectType.cs
Normal file
80
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3ObjectType.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public abstract class ES3ObjectType : ES3Type
|
||||
{
|
||||
public ES3ObjectType(Type type) : base(type) {}
|
||||
|
||||
protected abstract void WriteObject(object obj, ES3Writer writer);
|
||||
protected abstract object ReadObject<T>(ES3Reader reader);
|
||||
|
||||
protected virtual void ReadObject<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
throw new NotSupportedException("ReadInto is not supported for type "+type);
|
||||
}
|
||||
|
||||
public override void Write(object obj, ES3Writer writer)
|
||||
{
|
||||
if (!WriteUsingDerivedType(obj, writer))
|
||||
{
|
||||
var baseType = ES3Reflection.BaseType(obj.GetType());
|
||||
if (baseType != typeof(object))
|
||||
{
|
||||
var es3Type = ES3TypeMgr.GetOrCreateES3Type(baseType, false);
|
||||
// If it's a Dictionary or Collection, we need to write it as a field with a property name.
|
||||
if (es3Type != null && (es3Type.isDictionary || es3Type.isCollection))
|
||||
writer.WriteProperty("_Values", obj, es3Type);
|
||||
}
|
||||
|
||||
WriteObject(obj, writer);
|
||||
}
|
||||
}
|
||||
|
||||
public override object Read<T>(ES3Reader reader)
|
||||
{
|
||||
string propertyName;
|
||||
while(true)
|
||||
{
|
||||
propertyName = ReadPropertyName(reader);
|
||||
|
||||
if(propertyName == ES3Type.typeFieldName)
|
||||
return ES3TypeMgr.GetOrCreateES3Type(reader.ReadType()).Read<T>(reader);
|
||||
else
|
||||
{
|
||||
reader.overridePropertiesName = propertyName;
|
||||
|
||||
return ReadObject<T>(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
string propertyName;
|
||||
while(true)
|
||||
{
|
||||
propertyName = ReadPropertyName(reader);
|
||||
|
||||
if(propertyName == ES3Type.typeFieldName)
|
||||
{
|
||||
ES3TypeMgr.GetOrCreateES3Type(reader.ReadType()).ReadInto<T>(reader, obj);
|
||||
return;
|
||||
}
|
||||
// This is important we return if the enumerator returns null, otherwise we will encounter an endless cycle.
|
||||
else if (propertyName == null)
|
||||
return;
|
||||
else
|
||||
{
|
||||
reader.overridePropertiesName = propertyName;
|
||||
ReadObject<T>(reader, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9834650b1a7f7554bb7970a0bb11419a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
30
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3Property.cs
Normal file
30
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3Property.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
public class ES3Member
|
||||
{
|
||||
public string name;
|
||||
public Type type;
|
||||
public bool isProperty;
|
||||
public ES3Reflection.ES3ReflectedMember reflectedMember;
|
||||
public bool useReflection = false;
|
||||
|
||||
public ES3Member(string name, Type type, bool isProperty)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
public ES3Member(ES3Reflection.ES3ReflectedMember reflectedMember)
|
||||
{
|
||||
this.reflectedMember = reflectedMember;
|
||||
this.name = reflectedMember.Name;
|
||||
this.type = reflectedMember.MemberType;
|
||||
this.isProperty = reflectedMember.isProperty;
|
||||
this.useReflection = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c8fe77b52f5a9047b465351685c0e53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public abstract class ES3ScriptableObjectType : ES3UnityObjectType
|
||||
{
|
||||
public ES3ScriptableObjectType(Type type) : base(type) {}
|
||||
|
||||
protected abstract void WriteScriptableObject(object obj, ES3Writer writer);
|
||||
protected abstract void ReadScriptableObject<T>(ES3Reader reader, object obj);
|
||||
|
||||
protected override void WriteUnityObject(object obj, ES3Writer writer)
|
||||
{
|
||||
var instance = obj as ScriptableObject;
|
||||
if(obj != null && instance == null)
|
||||
throw new ArgumentException("Only types of UnityEngine.ScriptableObject can be written with this method, but argument given is type of "+obj.GetType());
|
||||
|
||||
// If this object is in the instance manager, store it's instance ID with it.
|
||||
/*var refMgr = ES3ReferenceMgrBase.Current;
|
||||
if(refMgr != null)
|
||||
writer.WriteRef(instance);*/
|
||||
WriteScriptableObject(instance, writer);
|
||||
}
|
||||
|
||||
protected override void ReadUnityObject<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ReadScriptableObject<T>(reader, obj);
|
||||
}
|
||||
|
||||
protected override object ReadUnityObject<T>(ES3Reader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
protected override object ReadObject<T>(ES3Reader reader)
|
||||
{
|
||||
var refMgr = ES3ReferenceMgrBase.Current;
|
||||
long id = -1;
|
||||
UnityEngine.Object instance = null;
|
||||
|
||||
foreach(string propertyName in reader.Properties)
|
||||
{
|
||||
if(propertyName == ES3ReferenceMgrBase.referencePropertyName && refMgr != null)
|
||||
{
|
||||
id = reader.Read_ref();
|
||||
instance = refMgr.Get(id, type);
|
||||
|
||||
if (instance != null)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.overridePropertiesName = propertyName;
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
instance = ScriptableObject.CreateInstance(type);
|
||||
if (refMgr != null)
|
||||
refMgr.Add(instance, id);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReadScriptableObject<T>(reader, instance);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 797dde7b0ae2b5643aa93db2d0f3f16c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
193
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3Type.cs
Normal file
193
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3Type.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ES3Internal;
|
||||
using System.Linq;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public abstract class ES3Type
|
||||
{
|
||||
public const string typeFieldName = "__type";
|
||||
|
||||
public ES3Member[] members;
|
||||
public Type type;
|
||||
public bool isPrimitive = false;
|
||||
public bool isValueType = false;
|
||||
public bool isCollection = false;
|
||||
public bool isDictionary = false;
|
||||
public bool isTuple = false;
|
||||
public bool isEnum = false;
|
||||
public bool isES3TypeUnityObject = false;
|
||||
public bool isReflectedType = false;
|
||||
public bool isUnsupported = false;
|
||||
public int priority = 0;
|
||||
|
||||
protected ES3Type(Type type)
|
||||
{
|
||||
ES3TypeMgr.Add(type, this);
|
||||
this.type = type;
|
||||
this.isValueType = ES3Reflection.IsValueType(type);
|
||||
}
|
||||
|
||||
public abstract void Write(object obj, ES3Writer writer);
|
||||
public abstract object Read<T>(ES3Reader reader);
|
||||
|
||||
public virtual void ReadInto<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
throw new NotImplementedException("Self-assigning Read is not implemented or supported on this type.");
|
||||
}
|
||||
|
||||
protected bool WriteUsingDerivedType(object obj, ES3Writer writer)
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
|
||||
if(objType != this.type)
|
||||
{
|
||||
writer.WriteType(objType);
|
||||
ES3TypeMgr.GetOrCreateES3Type(objType).Write(obj, writer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void ReadUsingDerivedType<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
ES3TypeMgr.GetOrCreateES3Type(reader.ReadType()).ReadInto<T>(reader, obj);
|
||||
}
|
||||
|
||||
internal string ReadPropertyName(ES3Reader reader)
|
||||
{
|
||||
if(reader.overridePropertiesName != null)
|
||||
{
|
||||
string propertyName = reader.overridePropertiesName;
|
||||
reader.overridePropertiesName = null;
|
||||
return propertyName;
|
||||
}
|
||||
return reader.ReadPropertyName();
|
||||
}
|
||||
|
||||
#region Reflection Methods
|
||||
|
||||
protected void WriteProperties(object obj, ES3Writer writer)
|
||||
{
|
||||
if(members == null)
|
||||
GetMembers(writer.settings.safeReflection);
|
||||
|
||||
for(int i=0; i<members.Length; i++)
|
||||
{
|
||||
var property = members[i];
|
||||
writer.WriteProperty(property.name, property.reflectedMember.GetValue(obj), ES3TypeMgr.GetOrCreateES3Type(property.type), writer.settings.memberReferenceMode);
|
||||
}
|
||||
}
|
||||
|
||||
protected object ReadProperties(ES3Reader reader, object obj)
|
||||
{
|
||||
// Iterate through each property in the file and try to load it using the appropriate
|
||||
// ES3Member in the members array.
|
||||
foreach (string propertyName in reader.Properties)
|
||||
{
|
||||
// Find the property.
|
||||
ES3Member property = null;
|
||||
for(int i=0; i<members.Length; i++)
|
||||
{
|
||||
if(members[i].name == propertyName)
|
||||
{
|
||||
property = members[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a class which derives directly from a Collection, we need to load it's dictionary first.
|
||||
if(propertyName == "_Values")
|
||||
{
|
||||
var baseType = ES3TypeMgr.GetOrCreateES3Type(ES3Reflection.BaseType(obj.GetType()));
|
||||
if(baseType.isDictionary)
|
||||
{
|
||||
var dict = (IDictionary)obj;
|
||||
var loaded = (IDictionary)baseType.Read<IDictionary>(reader);
|
||||
foreach (DictionaryEntry kvp in loaded)
|
||||
dict[kvp.Key] = kvp.Value;
|
||||
}
|
||||
else if(baseType.isCollection)
|
||||
{
|
||||
var loaded = (IEnumerable)baseType.Read<IEnumerable>(reader);
|
||||
|
||||
var type = baseType.GetType();
|
||||
|
||||
if (type == typeof(ES3ListType))
|
||||
foreach (var item in loaded)
|
||||
((IList)obj).Add(item);
|
||||
else if (type == typeof(ES3QueueType))
|
||||
{
|
||||
var method = baseType.type.GetMethod("Enqueue");
|
||||
foreach (var item in loaded)
|
||||
method.Invoke(obj, new object[] { item });
|
||||
}
|
||||
else if (type == typeof(ES3StackType))
|
||||
{
|
||||
var method = baseType.type.GetMethod("Push");
|
||||
foreach (var item in loaded)
|
||||
method.Invoke(obj, new object[] { item });
|
||||
}
|
||||
else if (type == typeof(ES3HashSetType))
|
||||
{
|
||||
var method = baseType.type.GetMethod("Add");
|
||||
foreach (var item in loaded)
|
||||
method.Invoke(obj, new object[] { item });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (property == null)
|
||||
reader.Skip();
|
||||
else
|
||||
{
|
||||
var type = ES3TypeMgr.GetOrCreateES3Type(property.type);
|
||||
|
||||
if(ES3Reflection.IsAssignableFrom(typeof(ES3DictionaryType), type.GetType()))
|
||||
property.reflectedMember.SetValue(obj, ((ES3DictionaryType)type).Read(reader));
|
||||
else if(ES3Reflection.IsAssignableFrom(typeof(ES3CollectionType), type.GetType()))
|
||||
property.reflectedMember.SetValue(obj, ((ES3CollectionType)type).Read(reader));
|
||||
else
|
||||
{
|
||||
object readObj = reader.Read<object>(type);
|
||||
property.reflectedMember.SetValue(obj, readObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected void GetMembers(bool safe)
|
||||
{
|
||||
GetMembers(safe, null);
|
||||
}
|
||||
|
||||
protected void GetMembers(bool safe, string[] memberNames)
|
||||
{
|
||||
var serializedMembers = ES3Reflection.GetSerializableMembers(type, safe, memberNames);
|
||||
|
||||
members = new ES3Member[serializedMembers.Length];
|
||||
for(int i=0; i<serializedMembers.Length; i++)
|
||||
members[i] = new ES3Member(serializedMembers[i]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ES3PropertiesAttribute : System.Attribute
|
||||
{
|
||||
public readonly string[] members;
|
||||
|
||||
public ES3PropertiesAttribute(params string[] members)
|
||||
{
|
||||
this.members = members;
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3Type.cs.meta
Normal file
11
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3Type.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c69e67314bb13b4d8b3416975b0b4fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
152
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3TypeMgr.cs
Normal file
152
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3TypeMgr.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using ES3Types;
|
||||
|
||||
namespace ES3Internal
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public static class ES3TypeMgr
|
||||
{
|
||||
private static object _lock = new object();
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public static Dictionary<Type, ES3Type> types = null;
|
||||
|
||||
// We cache the last accessed type as we quite often use the same type multiple times,
|
||||
// so this improves performance as another lookup is not required.
|
||||
private static ES3Type lastAccessedType = null;
|
||||
|
||||
public static ES3Type GetOrCreateES3Type(Type type, bool throwException = true)
|
||||
{
|
||||
if(types == null)
|
||||
Init();
|
||||
|
||||
if (type != typeof(object) && lastAccessedType != null && lastAccessedType.type == type)
|
||||
return lastAccessedType;
|
||||
|
||||
// If type doesn't exist, create one.
|
||||
if(types.TryGetValue(type, out lastAccessedType))
|
||||
return lastAccessedType;
|
||||
return (lastAccessedType = CreateES3Type(type, throwException));
|
||||
}
|
||||
|
||||
public static ES3Type GetES3Type(Type type)
|
||||
{
|
||||
if(types == null)
|
||||
Init();
|
||||
|
||||
if(types.TryGetValue(type, out lastAccessedType))
|
||||
return lastAccessedType;
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static void Add(Type type, ES3Type es3Type)
|
||||
{
|
||||
if(types == null)
|
||||
Init();
|
||||
|
||||
var existingType = GetES3Type(type);
|
||||
if (existingType != null && existingType.priority > es3Type.priority)
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
types[type] = es3Type;
|
||||
}
|
||||
}
|
||||
|
||||
internal static ES3Type CreateES3Type(Type type, bool throwException = true)
|
||||
{
|
||||
ES3Type es3Type;
|
||||
|
||||
if(ES3Reflection.IsEnum(type))
|
||||
return new ES3Type_enum(type);
|
||||
else if(ES3Reflection.TypeIsArray(type))
|
||||
{
|
||||
int rank = ES3Reflection.GetArrayRank(type);
|
||||
if(rank == 1)
|
||||
es3Type = new ES3ArrayType(type);
|
||||
else if(rank == 2)
|
||||
es3Type = new ES32DArrayType(type);
|
||||
else if(rank == 3)
|
||||
es3Type = new ES33DArrayType(type);
|
||||
else if(throwException)
|
||||
throw new NotSupportedException("Only arrays with up to three dimensions are supported by Easy Save.");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if(ES3Reflection.IsGenericType(type) && ES3Reflection.ImplementsInterface(type, typeof(IEnumerable)))
|
||||
{
|
||||
Type genericType = ES3Reflection.GetGenericTypeDefinition(type);
|
||||
if (typeof(List<>).IsAssignableFrom(genericType))
|
||||
es3Type = new ES3ListType(type);
|
||||
else if (typeof(Dictionary<,>).IsAssignableFrom(genericType))
|
||||
es3Type = new ES3DictionaryType(type);
|
||||
else if (genericType == typeof(Queue<>))
|
||||
es3Type = new ES3QueueType(type);
|
||||
else if (genericType == typeof(Stack<>))
|
||||
es3Type = new ES3StackType(type);
|
||||
else if (genericType == typeof(HashSet<>))
|
||||
es3Type = new ES3HashSetType(type);
|
||||
else if (genericType == typeof(Unity.Collections.NativeArray<>))
|
||||
es3Type = new ES3NativeArrayType(type);
|
||||
else if (throwException)
|
||||
throw new NotSupportedException("Generic type \"" + type.ToString() + "\" is not supported by Easy Save.");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if(ES3Reflection.IsPrimitive(type)) // ERROR: We should not have to create an ES3Type for a primitive.
|
||||
{
|
||||
if(types == null || types.Count == 0) // If the type list is not initialised, it is most likely an initialisation error.
|
||||
throw new TypeLoadException("ES3Type for primitive could not be found, and the type list is empty. Please contact Easy Save developers at http://www.moodkie.com/contact");
|
||||
else // Else it's a different error, possibly an error in the specific ES3Type for that type.
|
||||
throw new TypeLoadException("ES3Type for primitive could not be found, but the type list has been initialised and is not empty. Please contact Easy Save developers on mail@moodkie.com");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ES3Reflection.IsAssignableFrom(typeof(Component), type))
|
||||
es3Type = new ES3ReflectedComponentType(type);
|
||||
else if (ES3Reflection.IsValueType(type))
|
||||
es3Type = new ES3ReflectedValueType(type);
|
||||
else if (ES3Reflection.IsAssignableFrom(typeof(ScriptableObject), type))
|
||||
es3Type = new ES3ReflectedScriptableObjectType(type);
|
||||
else if (ES3Reflection.IsAssignableFrom(typeof(UnityEngine.Object), type))
|
||||
es3Type = new ES3ReflectedUnityObjectType(type);
|
||||
/*else if (ES3Reflection.HasParameterlessConstructor(type) || ES3Reflection.IsAbstract(type) || ES3Reflection.IsInterface(type))
|
||||
es3Type = new ES3ReflectedObjectType(type);*/
|
||||
else if (type.Name.StartsWith("Tuple`"))
|
||||
es3Type = new ES3TupleType(type);
|
||||
/*else if (throwException)
|
||||
throw new NotSupportedException("Type of " + type + " is not supported as it does not have a parameterless constructor. Only value types, Components or ScriptableObjects are supportable without a parameterless constructor. However, you may be able to create an ES3Type script to add support for it.");*/
|
||||
else
|
||||
es3Type = new ES3ReflectedObjectType(type);
|
||||
}
|
||||
|
||||
if(es3Type.type == null || es3Type.isUnsupported)
|
||||
{
|
||||
if(throwException)
|
||||
throw new NotSupportedException(string.Format("ES3Type.type is null when trying to create an ES3Type for {0}, possibly because the element type is not supported.", type));
|
||||
return null;
|
||||
}
|
||||
|
||||
Add(type, es3Type);
|
||||
return es3Type;
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
types = new Dictionary<Type, ES3Type>();
|
||||
// ES3Types add themselves to the types Dictionary.
|
||||
ES3Reflection.GetInstances<ES3Type>();
|
||||
|
||||
// Check that the type list was initialised correctly.
|
||||
if (types == null || types.Count == 0)
|
||||
throw new TypeLoadException("Type list could not be initialised. Please contact Easy Save developers on mail@moodkie.com.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3e6e9b18d956524cabb68473650fc57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
123
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3UnityObjectType.cs
Normal file
123
Convention/[ES3]/Easy Save 3/Scripts/Types/ES3UnityObjectType.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using ES3Internal;
|
||||
|
||||
namespace ES3Types
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public abstract class ES3UnityObjectType : ES3ObjectType
|
||||
{
|
||||
public ES3UnityObjectType(Type type) : base(type)
|
||||
{
|
||||
this.isValueType = false;
|
||||
isES3TypeUnityObject = true;
|
||||
}
|
||||
|
||||
protected abstract void WriteUnityObject(object obj, ES3Writer writer);
|
||||
protected abstract void ReadUnityObject<T>(ES3Reader reader, object obj);
|
||||
protected abstract object ReadUnityObject<T>(ES3Reader reader);
|
||||
|
||||
protected override void WriteObject(object obj, ES3Writer writer)
|
||||
{
|
||||
WriteObject(obj, writer, ES3.ReferenceMode.ByRefAndValue);
|
||||
}
|
||||
|
||||
public virtual void WriteObject(object obj, ES3Writer writer, ES3.ReferenceMode mode)
|
||||
{
|
||||
if(WriteUsingDerivedType(obj, writer, mode))
|
||||
return;
|
||||
var instance = obj as UnityEngine.Object;
|
||||
if(obj != null && instance == null)
|
||||
throw new ArgumentException("Only types of UnityEngine.Object can be written with this method, but argument given is type of "+obj.GetType());
|
||||
|
||||
// If this object is in the instance manager, store it's instance ID with it.
|
||||
if(mode != ES3.ReferenceMode.ByValue)
|
||||
{
|
||||
var refMgr = ES3ReferenceMgrBase.Current;
|
||||
if (refMgr == null)
|
||||
throw new InvalidOperationException("An Easy Save 3 Manager is required to load references. To add one to your scene, exit playmode and go to Tools > Easy Save 3 > Add Manager to Scene");
|
||||
writer.WriteRef(instance);
|
||||
if(mode == ES3.ReferenceMode.ByRef)
|
||||
return;
|
||||
}
|
||||
WriteUnityObject(instance, writer);
|
||||
}
|
||||
|
||||
protected override void ReadObject<T>(ES3Reader reader, object obj)
|
||||
{
|
||||
var refMgr = ES3ReferenceMgrBase.Current;
|
||||
if (refMgr != null)
|
||||
{
|
||||
foreach (string propertyName in reader.Properties)
|
||||
{
|
||||
if (propertyName == ES3ReferenceMgrBase.referencePropertyName)
|
||||
// If the object we're loading into isn't registered with the reference manager, register it.
|
||||
refMgr.Add((UnityEngine.Object)obj, reader.Read_ref());
|
||||
else
|
||||
{
|
||||
reader.overridePropertiesName = propertyName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadUnityObject<T>(reader, obj);
|
||||
}
|
||||
|
||||
protected override object ReadObject<T>(ES3Reader reader)
|
||||
{
|
||||
var refMgr = ES3ReferenceMgrBase.Current;
|
||||
if(refMgr == null)
|
||||
return ReadUnityObject<T>(reader);
|
||||
|
||||
long id = -1;
|
||||
UnityEngine.Object instance = null;
|
||||
|
||||
foreach(string propertyName in reader.Properties)
|
||||
{
|
||||
if(propertyName == ES3ReferenceMgrBase.referencePropertyName)
|
||||
{
|
||||
if(refMgr == null)
|
||||
throw new InvalidOperationException("An Easy Save 3 Manager is required to load references. To add one to your scene, exit playmode and go to Tools > Easy Save 3 > Add Manager to Scene");
|
||||
id = reader.Read_ref();
|
||||
instance = refMgr.Get(id, type);
|
||||
|
||||
if(instance != null)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.overridePropertiesName = propertyName;
|
||||
if (instance == null)
|
||||
{
|
||||
instance = (UnityEngine.Object)ReadUnityObject<T>(reader);
|
||||
refMgr.Add(instance, id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReadUnityObject<T>(reader, instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected bool WriteUsingDerivedType(object obj, ES3Writer writer, ES3.ReferenceMode mode)
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
|
||||
if (objType != this.type)
|
||||
{
|
||||
writer.WriteType(objType);
|
||||
|
||||
var es3Type = ES3TypeMgr.GetOrCreateES3Type(objType);
|
||||
if (es3Type is ES3UnityObjectType)
|
||||
((ES3UnityObjectType)es3Type).WriteObject(obj, writer, mode);
|
||||
else
|
||||
es3Type.Write(obj, writer);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user