using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using UnityEngine;
using System.Xml.Serialization;
using UnityEngine.Events;
using System;
using System.Security.Cryptography;
using System.Text;
using System.IO.Compression;
namespace TigerForge
{
///
/// Easy File Save v.1.2
///
public class EasyFileSave
{
#region " VARIABLES & PROPERTIES "
// The file internal storage.
private Dictionary storage = new Dictionary();
///
/// The error information.
///
public string Error = "";
///
/// Disable the warning messages shown in the Console.
///
public bool suppressWarning = true;
// The instance file name.
private readonly string fileName = "";
// A custom Structure for containing all the Transform component data.
public struct UnityTransform
{
public Vector3 position;
public Quaternion rotation;
public Vector3 localScale;
public Vector3 localPosition;
public Quaternion localRotation;
public Vector3 lossyScale;
public Vector3 eulerAngles;
public Vector3 localEulerAngles;
}
// Easy File Save Extensions management.
private EasyFileSaveExtension customs = new EasyFileSaveExtension();
// The Structure used by Custom data method.
public struct CustomData
{
///
/// The raw object data.
///
public object data;
///
/// Cast the object into integer value.
///
///
public int ToInt()
{
try
{
return (int)data;
}
catch (System.Exception)
{
return 0;
}
}
///
/// Cast the object into float value.
///
///
public float ToFloat()
{
try
{
return (float)data;
}
catch (System.Exception)
{
return 0f;
}
}
///
/// Cast the object into byrte value.
///
///
public byte ToByte()
{
try
{
return (byte)data;
}
catch (System.Exception)
{
return 0;
}
}
///
/// Cast the object into a string.
///
///
public override string ToString()
{
try
{
return (string)data;
}
catch (System.Exception)
{
return "";
}
}
///
/// Cast the object into a boolean value.
///
///
public bool ToBool()
{
try
{
return (bool)data;
}
catch (System.Exception)
{
return false;
}
}
}
#endregion
#region " CONSTRUCTOR "
///
/// Initialize a new instance with the given fileName or with a default file name if it's not specified.
///
///
public EasyFileSave(string fileName = "")
{
// If no name is provided, 'gamedata' file name will be used.
if (fileName == "") fileName = "gamedata";
// Initialize the storage.
storage = new Dictionary();
// Initialize the file name with the right path, name and extension.
this.fileName = Application.persistentDataPath + "/" + fileName + ".dat";
Error = "";
// Keep track of all the created files so as you can delete them all.
Register(fileName);
// Activate all the Extensions defined in 'EasyFileSaveExtension.cs'.
customs.Start();
}
#endregion
#region " SAVE, APPEND, LOAD, ADD "
///
/// Save the 'system internal storage' data into the file. Return TRUE when done without errors.
/// If a password is specified, the method will save an encrypted and compressed file.
///
public bool Save(string password = "")
{
try
{
if (password == "")
{
BinaryFormatter bf = new BinaryFormatter();
FileStream saveFile = File.Create(fileName);
bf.Serialize(saveFile, storage);
saveFile.Close();
Dispose();
return true;
} else
{
return SaveSecure(password);
}
}
catch (System.Exception e)
{
Error = "[Easy File Save] This system exeption has been thrown during saving: " + e.Message;
return false;
}
}
///
/// Append the 'system internal storage' data at the end of the current file content.
/// By default, existing keys will be overwritten with new values. If 'overwrite' parameter is set to FALSE, existing keys will be ignored.
/// If a password is specified, the method will save an encrypted and compressed file.
///
public bool Append(bool overwrite = true, string password = "")
{
try
{
Dictionary fileStorage = new Dictionary();
if (FileExists())
{
// If the file exists, it's read and its content is mixed with the storage.
// How the current storage is joined to the existing file content depends on 'overwrite' option.
if (password == "")
{
BinaryFormatter bf2 = new BinaryFormatter();
FileStream openFile = File.Open(fileName, FileMode.Open);
fileStorage = (Dictionary)bf2.Deserialize(openFile);
openFile.Close();
} else
{
var loadPassword = (password + "easyfilesavesecure1234").Substring(0, 16);
byte[] key = Encoding.UTF8.GetBytes(loadPassword);
byte[] iv = Encoding.UTF8.GetBytes(loadPassword);
using (Stream s = File.OpenRead(fileName))
{
RijndaelManaged rm = new RijndaelManaged();
rm.Key = key;
rm.IV = iv;
using (CryptoStream cs = new CryptoStream(s, rm.CreateDecryptor(), CryptoStreamMode.Read))
{
using (GZipStream gs = new GZipStream(cs, CompressionMode.Decompress))
{
BinaryFormatter bf = new BinaryFormatter();
fileStorage = (Dictionary)bf.Deserialize(gs);
}
}
}
}
foreach (KeyValuePair item in storage)
{
if (fileStorage.ContainsKey(item.Key))
{
if (overwrite) fileStorage[item.Key] = item.Value;
}
else
{
fileStorage.Add(item.Key, item.Value);
}
}
}
else
{
fileStorage = storage;
}
if (password == "")
{
BinaryFormatter bf = new BinaryFormatter();
FileStream saveFile = File.Create(fileName);
bf.Serialize(saveFile, fileStorage);
saveFile.Close();
Dispose();
} else
{
var savePassword = (password + "easyfilesavesecure1234").Substring(0, 16);
byte[] key = Encoding.UTF8.GetBytes(savePassword);
byte[] iv = Encoding.UTF8.GetBytes(savePassword);
using (Stream s = File.Create(fileName))
{
RijndaelManaged rm = new RijndaelManaged();
rm.Key = key;
rm.IV = iv;
using (CryptoStream cs = new CryptoStream(s, rm.CreateEncryptor(), CryptoStreamMode.Write))
{
using (GZipStream gs = new GZipStream(cs, CompressionMode.Compress))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(gs, fileStorage);
}
}
}
}
return true;
}
catch (System.Exception e)
{
Error = "[Easy File Save] This system exeption has been thrown during append data: " + e.Message;
return false;
}
}
///
/// Load the file data into the 'system internal storage' and return TRUE if the loading has been completed. Return FALSE if something has gone wrong.
/// If a password is specified, the method will load an encrypted and compressed file.
///
///
public bool Load(string password = "")
{
// If the file doesn't exist, the method just returns false. A warning message is written into the Error property.
if (!FileExists())
{
Error = "[Easy File Save] The file " + fileName + " doesn't exist.";
return false;
}
try
{
if (password == "")
{
BinaryFormatter bf = new BinaryFormatter();
FileStream loadFile = File.Open(fileName, FileMode.Open);
storage = (Dictionary)bf.Deserialize(loadFile);
loadFile.Close();
return true;
} else
{
return LoadSecure(password);
}
}
catch (System.Exception e)
{
Error = "[Easy File Save] This system exeption has been thrown during loading: " + e.Message;
return false;
}
}
///
/// Add a value, with the given unique key, into the 'system internal storage'.
/// By default, if the given key already exists into the 'internal storage', the existing key value is overwritten by the new value.
/// Set the optional 'ignoreExistingKey' parameter to true so as to prevent the reuse of an existing key. In this case, the original value won't be overwritten and a non-blocking warning will be thrown.
///
public void Add(string key, object value, bool ignoreExistingKey = false)
{
if (KeyExists(key))
{
if (ignoreExistingKey)
{
Warning("[Easy File Save] Trying to reuse the key '" + key + "' to put a value into the storage!");
}
else
{
value = ConvertUnityTypes(value);
storage[key] = value;
}
}
else
{
value = ConvertUnityTypes(value);
storage.Add(key, value);
}
}
#endregion
#region " SAVE, APPEND, LOAD WITH AES "
///
/// Save the 'system internal storage' data into a password protected and compressed file. Return TRUE when done without errors.
///
///
///
private bool SaveSecure(string password)
{
password = (password + "easyfilesavesecure1234").Substring(0, 16);
try
{
byte[] key = Encoding.UTF8.GetBytes(password);
byte[] iv = Encoding.UTF8.GetBytes(password);
using (Stream s = File.Create(fileName))
{
RijndaelManaged rm = new RijndaelManaged();
rm.Key = key;
rm.IV = iv;
using (CryptoStream cs = new CryptoStream(s, rm.CreateEncryptor(), CryptoStreamMode.Write))
{
using (GZipStream gs = new GZipStream(cs, CompressionMode.Compress))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(gs, storage);
}
}
}
Dispose();
return true;
}
catch (System.Exception e)
{
Error = "[Easy File Save] This system exeption has been thrown during SaveSecure: " + e.Message;Debug.Log(e.Message);
return false;
}
}
///
/// Load a password protected and compressed file data into the 'system internal storage' and return TRUE if the loading has been completed. Return FALSE if something has gone wrong.
///
///
///
private bool LoadSecure(string password)
{
password = (password + "easyfilesavesecure1234").Substring(0, 16);
try
{
byte[] key = Encoding.UTF8.GetBytes(password);
byte[] iv = Encoding.UTF8.GetBytes(password);
using (Stream s = File.OpenRead(fileName))
{
RijndaelManaged rm = new RijndaelManaged();
rm.Key = key;
rm.IV = iv;
using (CryptoStream cs = new CryptoStream(s, rm.CreateDecryptor(), CryptoStreamMode.Read))
{
using (GZipStream gs = new GZipStream(cs, CompressionMode.Decompress))
{
BinaryFormatter bf = new BinaryFormatter();
storage = (Dictionary)bf.Deserialize(gs);
}
}
}
return true;
}
catch (System.Exception e)
{
Error = "[Easy File Save] This system exeption has been thrown during LoadSecure: " + e.Message; Debug.Log(e.Message);
return false;
}
}
#endregion
#region " UTILITY METHODS "
///
/// Return TRUE if the file exists.
///
///
public bool FileExists()
{
return File.Exists(fileName);
}
///
/// Save a backup copy of this file.
///
public void BackupSave()
{
if (File.Exists(fileName))
{
var backupFile = fileName + ".backup";
File.Copy(fileName, backupFile);
}
}
///
/// Restore a previously saved backup copy. Restoring process makes the backup copy the current file.
///
public void BackupRestore()
{
Delete();
var backupFile = fileName + ".backup";
File.Copy(backupFile, fileName);
}
///
/// Delete the backup file if it exists.
///
public void BackupDelete()
{
var backupFile = fileName + ".backup";
if (File.Exists(backupFile)) File.Delete(backupFile);
}
///
/// Delete this file (if it exists).
///
public void Delete()
{
if (FileExists())
{
File.Delete(fileName);
Dispose();
}
}
///
/// Return TRUE if the 'system internal storage' contains the given key.
///
///
///
public bool KeyExists(string key)
{
return storage.ContainsKey(key);
}
///
/// Remove the given key (and the associated value) from the 'system internal storage'.
///
///
public void KeyRemove(string key)
{
if (storage.ContainsKey(key)) storage.Remove(key);
}
///
/// Return the system internal storage.
///
///
public Dictionary GetStorage()
{
return storage;
}
///
/// Return the current file name with path.
///
///
public string GetFileName()
{
return fileName;
}
///
/// Delete the 'system internal storage' so to free this part of memory. This method is automatically called after saving. It should be manually called after loading data.
///
public void Dispose()
{
storage = new Dictionary();
Error = "";
}
///
/// Perform a test of data saving and loading, so as to verifiy if the provided data is correctly managed. Return true if the test is passed. The test results are shown on Console panel.
///
///
public bool TestDataSaveLoad()
{
Debug.Log("==== [Easy File Save] ==== TESTING SAVE AND LOAD DATA ==============================================\n");
if (Save())
{
Debug.Log("[Easy File Save] >> TEST #1 PASSED: data has been saved!\n");
if (Load())
{
Debug.Log("[Easy File Save] >> TEST #2 PASSED: data has been loaded!\n");
Debug.Log("====================================================================================\n");
return true;
}
else
{
Debug.Log("[Easy File Save] >> TEST #2 NOT PASSED: there is a problem loading data!\n");
Debug.Log(Error);
}
}
else
{
Debug.Log("[Easy File Save] >> TEST #1 NOT PASSED: there is a problem saving data!\n");
Debug.Log(Error);
}
Debug.Log("====================================================================================\n");
return false;
}
#endregion
#region " GETTERS (DEFAULT DATA-TYPES) "
///
/// Return the object data for the given key (or the defined defaultValue if nothing found).
/// Note that this method returns an 'object' that should be manually converted in the proper data-type.
///
///
///
public object GetData(string key, object defaultValue = null)
{
try
{
if (storage.ContainsKey(key)) return storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetData error using key: " + key);
return defaultValue;
}
}
///
/// Return the integer data for the given key (or the defined defaultValue if nothing found).
///
///
///
public int GetInt(string key, int defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (int)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetInt error using key: " + key);
return defaultValue;
}
}
///
/// Return the boolean data for the given key (or the defined defaultValue if nothing found).
///
///
///
public bool GetBool(string key, bool defaultValue = false)
{
try
{
if (storage.ContainsKey(key)) return (bool)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetBool error using key: " + key);
return defaultValue;
}
}
///
/// Return the float data for the given key (or the defined defaultValue if nothing found).
///
///
///
public float GetFloat(string key, float defaultValue = 0f)
{
try
{
if (storage.ContainsKey(key)) return (float)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetFloat error using key: " + key);
return defaultValue;
}
}
///
/// Return the string data for the given key (or the defined defaultValue if nothing found).
///
///
///
public string GetString(string key, string defaultValue = "")
{
try
{
if (storage.ContainsKey(key)) return (string)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetString error using key: " + key);
return defaultValue;
}
}
///
/// Return the byte data for the given key (or the defined defaultValue if nothing found).
///
///
///
public byte GetByte(string key, byte defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (byte)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetByte error using key: " + key);
return defaultValue;
}
}
///
/// Return the char data for the given key (or the defined defaultValue if nothing found).
///
///
///
public char GetChar(string key, char defaultValue = ' ')
{
try
{
if (storage.ContainsKey(key)) return (char)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetChar error using key: " + key);
return defaultValue;
}
}
///
/// Return the long data for the given key (or the defined defaultValue if nothing found).
///
///
///
public long GetLong(string key, long defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (long)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetLong error using key: " + key);
return defaultValue;
}
}
///
/// Return the short data for the given key (or the defined defaultValue if nothing found).
///
///
///
public short GetShort(string key, short defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (short)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetShort error using key: " + key);
return defaultValue;
}
}
///
/// Return the uint data for the given key (or the defined defaultValue if nothing found).
///
///
///
public uint GetUint(string key, uint defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (uint)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetUint error using key: " + key);
return defaultValue;
}
}
///
/// Return the ulong data for the given key (or the defined defaultValue if nothing found).
///
///
///
public ulong GetUlong(string key, ulong defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (ulong)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetUlong error using key: " + key);
return defaultValue;
}
}
///
/// Return the ushort data for the given key (or the defined defaultValue if nothing found).
///
///
///
public ushort GetUshort(string key, ushort defaultValue = 0)
{
try
{
if (storage.ContainsKey(key)) return (ushort)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetUshort error using key: " + key);
return defaultValue;
}
}
///
/// Return the IntPtr data for the given key (or the defined defaultValue if nothing found).
///
///
///
public IntPtr GetIntPtr(string key, IntPtr defaultValue = new IntPtr())
{
try
{
if (storage.ContainsKey(key)) return (IntPtr)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetIntPtr error using key: " + key);
return defaultValue;
}
}
///
/// Return the UIntPtr data for the given key (or the defined defaultValue if nothing found).
///
///
///
public UIntPtr GetUintPtr(string key, UIntPtr defaultValue = new UIntPtr())
{
try
{
if (storage.ContainsKey(key)) return (UIntPtr)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetUintPtr error using key: " + key);
return defaultValue;
}
}
///
/// Return the DateTime data for the given key (or the defined defaultValue if nothing found).
///
///
///
public DateTime GetDateTime(string key, DateTime defaultValue = new DateTime())
{
try
{
if (storage.ContainsKey(key)) return (DateTime)storage[key]; else return defaultValue;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetDateTime error using key: " + key);
return defaultValue;
}
}
///
/// Return an array of the specified data-type for the given key (or an empty array if nothing found).
///
///
///
///
public T[] GetArray(string key)
{
try
{
if (storage.ContainsKey(key)) return (T[]) storage[key]; else return Array.Empty();
}
catch (System.Exception)
{
Warning("[Easy File Save] GetArray error using key: " + key);
return Array.Empty();
}
}
///
/// Return a list of the specified data-type for the given key (or an empty list if nothing found).
///
///
///
///
public List GetList(string key)
{
try
{
if (storage.ContainsKey(key)) return (List)storage[key]; else return new List();
}
catch (System.Exception)
{
Warning("[Easy File Save] GetList error using key: " + key);
return new List();
}
}
///
/// Return a dictionary of the specified data-types for the given key (or an empty dictionary if nothing found).
///
///
///
///
///
public Dictionary GetDictionary(string key)
{
try
{
if (storage.ContainsKey(key)) return (Dictionary)storage[key]; else return new Dictionary();
}
catch (System.Exception)
{
Warning("[Easy File Save] GetDictionary error using key: " + key);
return new Dictionary();
}
}
#endregion
#region " SPECIAL TYPES (SETTERS & GETTERS) "
///
/// Serialize an object and add it, with the given unique key, into the 'system internal storage'.
///
///
///
public void AddSerialized(string key, object data, bool ignoreExistingKey = false)
{
var xml = Serialize(data);
Add(key, xml, ignoreExistingKey);
}
///
/// Return the object data, for the given key, deserialized with the given type (or null if nothing found).
///
///
///
///
public object GetDeserialized(string key, System.Type type)
{
try
{
var obj = GetData(key);
if (obj != null) return Deserialize(obj, type); else return null;
}
catch (System.Exception)
{
Warning("[Easy File Save] GetDeserializedObject error using key: " + key);
return null;
}
}
///
/// Add a new custom value, with the given unique key, into the 'system internal storage'.
///
public void AddCustom(string key, object data, string extensionName, bool ignoreExistingKey = false)
{
if (!customs.extensions.ContainsKey(extensionName))
{
Debug.LogWarning("[Easy File Save] AddCustom: an extension with name '" + extensionName + "doesn't exist.");
return;
}
UnityAction myExtension = customs.extensions[extensionName];
customs.data[extensionName] = data;
myExtension.Invoke();
List