├── .codacy.yml ├── .gitattributes ├── .gitignore ├── .travis.yml ├── Editor.meta ├── Editor ├── SaveLoadManagerEditor.cs ├── SaveLoadManagerEditor.cs.meta ├── com.gameframe.saveload.Editor.asmdef └── com.gameframe.saveload.Editor.asmdef.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── EncryptionUtility.cs ├── EncryptionUtility.cs.meta ├── ISerializationMethod.cs ├── ISerializationMethod.cs.meta ├── ISerializationMethodEncrypted.cs ├── ISerializationMethodEncrypted.cs.meta ├── SaveLoadManager.cs ├── SaveLoadManager.cs.meta ├── SaveLoadUtility.cs ├── SaveLoadUtility.cs.meta ├── SerializationMethodBinary.cs ├── SerializationMethodBinary.cs.meta ├── SerializationMethodBinaryEncrypted.cs ├── SerializationMethodBinaryEncrypted.cs.meta ├── SerializationMethodJsonDotNet.cs ├── SerializationMethodJsonDotNet.cs.meta ├── SerializationMethodJsonDotNetEncrypted.cs ├── SerializationMethodJsonDotNetEncrypted.cs.meta ├── SerializationMethodType.cs ├── SerializationMethodType.cs.meta ├── SerializationMethodUnityJson.cs ├── SerializationMethodUnityJson.cs.meta ├── SerializationMethodUnityJsonEncrypted.cs ├── SerializationMethodUnityJsonEncrypted.cs.meta ├── com.gameframe.saveload.asmdef ├── com.gameframe.saveload.asmdef.meta ├── csc.rsp └── csc.rsp.meta ├── Tests.meta ├── Tests ├── Editor.meta ├── Editor │ ├── SaveLoadUtilityTests.cs │ ├── SaveLoadUtilityTests.cs.meta │ ├── com.gameframe.saveload.editor.tests.asmdef │ └── com.gameframe.saveload.editor.tests.asmdef.meta ├── Runtime.meta └── Runtime │ ├── SaveLoadManagerTests.cs │ ├── SaveLoadManagerTests.cs.meta │ ├── com.gameframe.saveload.Tests.asmdef │ └── com.gameframe.saveload.Tests.asmdef.meta ├── package.json └── package.json.meta /.codacy.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - '*.md' 3 | - 'Tests/Runtime/*.cs' 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Mm]emoryCaptures/ 12 | 13 | # Never ignore Asset meta data 14 | !/[Aa]ssets/**/*.meta 15 | 16 | # Uncomment this line if you wish to ignore the asset store tools plugin 17 | # /[Aa]ssets/AssetStoreTools* 18 | 19 | # Autogenerated Jetbrains Rider plugin 20 | [Aa]ssets/Plugins/Editor/JetBrains* 21 | 22 | # Visual Studio cache directory 23 | .vs/ 24 | 25 | # Gradle cache directory 26 | .gradle/ 27 | 28 | # Autogenerated VS/MD/Consulo solution and project files 29 | ExportedObj/ 30 | .consulo/ 31 | *.csproj 32 | *.unityproj 33 | *.sln 34 | *.suo 35 | *.tmp 36 | *.user 37 | *.userprefs 38 | *.pidb 39 | *.booproj 40 | *.svd 41 | *.pdb 42 | *.mdb 43 | *.opendb 44 | *.VC.db 45 | 46 | # Unity3D generated meta files 47 | *.pidb.meta 48 | *.pdb.meta 49 | *.mdb.meta 50 | 51 | # Unity3D generated file on crash reports 52 | sysinfo.txt 53 | 54 | # Builds 55 | *.apk 56 | *.unitypackage 57 | 58 | # Crashlytics generated file 59 | crashlytics-build.properties 60 | 61 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | os: osx 3 | osx_image: xcode11.2 4 | branches: 5 | only: 6 | - master 7 | cache: 8 | timeout: 86400 9 | directories: 10 | - $HOME/unity_download_cache/ #Cache unity installer files 11 | - $HOME/project_cache/ #Cache for created unity projects 12 | before_install: 13 | - bash <(curl -fsSL https://raw.githubusercontent.com/coryleach/UnityTravisCI/master/bin/before-install.sh) 14 | install: 15 | - ./install.sh 16 | - source ./find-unity.sh #source this because it exports required env vars 17 | before_script: 18 | - ./get-license.sh 19 | script: 20 | - source ./setup-project.sh #source this because it exports project path 21 | - ./run-tests.sh playmode 22 | after_success: 23 | - ./return-license.sh 24 | - ./clean-project.sh 25 | after_failure: 26 | - ./return-license.sh 27 | - ./clean-project.sh 28 | 29 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7237aab39c2354afeb45924421f8dbc7 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/SaveLoadManagerEditor.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnityEditor; 3 | using UnityEngine; 4 | 5 | namespace Gameframe.SaveLoad.Editor 6 | { 7 | /// 8 | /// Editor for SaveLoadManager 9 | /// 10 | [CustomEditor(typeof(SaveLoadManager))] 11 | public class SaveLoadManagerEditor : UnityEditor.Editor 12 | { 13 | public override void OnInspectorGUI() 14 | { 15 | EditorGUILayout.BeginVertical("Box"); 16 | base.OnInspectorGUI(); 17 | EditorGUILayout.EndVertical(); 18 | 19 | var manager = (SaveLoadManager) target; 20 | 21 | if (manager.IsEncrypted && (string.IsNullOrEmpty(manager.Key))) 22 | { 23 | EditorGUILayout.HelpBox("Encryption key cannot be null or empty.",MessageType.Error); 24 | } 25 | 26 | if (manager.IsEncrypted && (string.IsNullOrEmpty(manager.Salt))) 27 | { 28 | EditorGUILayout.HelpBox("Salt cannot be null or empty.",MessageType.Error); 29 | } 30 | 31 | var savePath = SaveLoadUtility.GetRuntimeSavePath(manager.DefaultFolder, manager.BaseFolder); 32 | 33 | EditorGUILayout.BeginVertical("Box"); 34 | EditorGUILayout.LabelField("Default Save Path"); 35 | EditorGUILayout.LabelField(savePath); 36 | EditorGUILayout.EndVertical(); 37 | 38 | if (GUILayout.Button("Open Data Folder")) 39 | { 40 | if (!Directory.Exists(savePath)) 41 | { 42 | //Create directory if it does not exist 43 | Directory.CreateDirectory(savePath); 44 | } 45 | EditorUtility.RevealInFinder(savePath); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Editor/SaveLoadManagerEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2eb6021040d8244a9d0ccbe547c7d51 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/com.gameframe.saveload.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { "name": "com.gameframe.saveload.Editor", "references": [ "com.gameframe.saveload" ], "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], "excludePlatforms": [] } -------------------------------------------------------------------------------- /Editor/com.gameframe.saveload.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8460321a66021460f9f0ff8e7dcd619a 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Cory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ccfbd4aa49301dc43a3c4f0934fe7d60 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

Gameframe.SaveLoad 👋

5 | 6 | 17 | 18 | Serialization helper utility that supports save, load and encryption. 19 | 20 | ## Quick Package Install 21 | 22 | #### Using UnityPackageManager (for Unity 2019.3 or later) 23 | Open the package manager window (menu: Window > Package Manager)
24 | Select "Add package from git URL...", fill in the pop-up with the following link:
25 | https://github.com/coryleach/UnitySaveLoad.git#1.0.10
26 | 27 | #### Using UnityPackageManager (for Unity 2019.1 or later) 28 | 29 | Find the manifest.json file in the Packages folder of your project and edit it to look like this: 30 | ```js 31 | { 32 | "dependencies": { 33 | "com.gameframe.saveload": "https://github.com/coryleach/UnitySaveLoad.git#1.0.10", 34 | ... 35 | }, 36 | } 37 | ``` 38 | 39 | 40 | 43 | 44 | ## Usage 45 | 46 | SaveLoadManager is not a singleton. Multiple instances may be used and created.
47 | In the project tab menu select Create->Gameframe->SaveLoad->SaveLoadManager
48 | This will create an instance of a SaveLoadManager asset.
49 | Select the created object and configure options via the inspector.
50 | 51 | ```C# 52 | //Use the Project tab's create menu GameFrame->SaveLoad->SaveLoadManager to create a manager 53 | //You can then use public or serialized fields to reference your save system. 54 | // OR 55 | //Create a Manager at Runtime like this 56 | manager = SaveLoadManager.Create("BaseDirectory","SaveDirectory",SerializationMethod.Default); 57 | 58 | //Save object to disk in a file named "MySave.data" 59 | manager.Save("MySave.data",objectToBeSaved); 60 | 61 | //Load from disk 62 | //loadedObject will be null if the file does not exist 63 | var loadedObject = manager.Load("MySave.data"); 64 | 65 | //Delete saved file 66 | manager.DeleteSave("MySave.data"); 67 | 68 | //Setup a Custom Save/Load Method by passing any object that implements ISerializationMethod 69 | manager.SetCustomSerializationMethod(new MyCustomSerializationMethod()); 70 | 71 | //Save a ScriptableObject or any object derived from UnityEngine.Object directly to disk 72 | var myScriptableObject = ScriptableObject.CreateInstance(); 73 | manager.SaveUnityObject(myScriptableObject,"MyUnityObjectData.dat"); 74 | 75 | //Loading a UnityEngine.Object type requires an existing object to overwrite 76 | //The following method will overwrite all the serialized fields on myScriptableObject with values loaded from disk 77 | manager.LoadUnityObjectOverwrite(myScriptableObject,"MyUnityObjectData.data"); 78 | ``` 79 | 80 | ## Enable Json.Net Support 81 | 82 | This package has been tested with version 3.0.2 of the newtonsoft json package. 83 | Import the Netwonsoft Json package from the package manager or copy and paste the below into your package manifest. 84 | ```C# 85 | "com.unity.nuget.newtonsoft-json": "3.0.2" 86 | ``` 87 | In player settings add the string 'JSON_DOT_NET' to Scripting Define Symbols. 88 | 89 | 90 | 91 | ## Author 92 | 93 | 👤 **Cory Leach** 94 | 95 | * Twitter: [@coryleach](https://twitter.com/coryleach) 96 | * Github: [@coryleach](https://github.com/coryleach) 97 | 98 | 99 | ## Show your support 100 | Give a ⭐️ if this project helped you! 101 | 102 | 103 | *** 104 | _This README was generated with ❤️ by [Gameframe.Packages](https://github.com/coryleach/unitypackages)_ 105 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 49313290095b441d3b6620ef487b5d20 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fc3e020d626c4f93b89105982d4d1cb 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/EncryptionUtility.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | 5 | namespace Gameframe.SaveLoad 6 | { 7 | public static class EncryptionUtility 8 | { 9 | /// 10 | /// Encrypt an input stream 11 | /// 12 | /// Stream to be encrypted 13 | /// Stream ecrypted data will be output to 14 | /// Encryption Key 15 | /// Encryption Salt 16 | public static void Encrypt(Stream inputStream, Stream outputStream, string key, string salt) 17 | { 18 | var cryptoMethod = new RijndaelManaged(); 19 | var cryptoKey = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(salt)); 20 | cryptoMethod.Key = cryptoKey.GetBytes(cryptoMethod.KeySize / 8); 21 | cryptoMethod.IV = cryptoKey.GetBytes(cryptoMethod.BlockSize / 8); 22 | using (var cryptostream = new CryptoStream(inputStream, cryptoMethod.CreateEncryptor(), CryptoStreamMode.Read)) 23 | { 24 | cryptostream.CopyTo(outputStream); 25 | } 26 | } 27 | 28 | /// 29 | /// Decrypt an input stream to an output stream. 30 | /// 31 | /// Stream with encrypted bytes to be read from 32 | /// Stream that decrypted bytes will be written to 33 | /// Encryption Key 34 | /// Encryption Salt 35 | public static void Decrypt(Stream inputStream, Stream outputStream, string key, string salt) 36 | { 37 | var cryptoMethod = new RijndaelManaged(); 38 | var cryptoKey = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(salt)); 39 | cryptoMethod.Key = cryptoKey.GetBytes(cryptoMethod.KeySize / 8); 40 | cryptoMethod.IV = cryptoKey.GetBytes(cryptoMethod.BlockSize / 8); 41 | using (var cryptostream = new CryptoStream(inputStream, cryptoMethod.CreateDecryptor(), CryptoStreamMode.Read)) 42 | { 43 | cryptostream.CopyTo(outputStream); 44 | } 45 | } 46 | } 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /Runtime/EncryptionUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5cfa2a0edb04aa748a79f4a756d30148 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ISerializationMethod.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Gameframe.SaveLoad 4 | { 5 | /// 6 | /// Interface for a method of serializing and deserializing an object tagged with the Serializable attribute 7 | /// 8 | public interface ISerializationMethod 9 | { 10 | void Save(object savedObject, FileStream fileStream); 11 | object Load(System.Type savedObjectType, FileStream fileStream); 12 | object Copy(object copyObject); 13 | } 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /Runtime/ISerializationMethod.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1ddf8d2e1522cf6408ed75c49182d026 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ISerializationMethodEncrypted.cs: -------------------------------------------------------------------------------- 1 | namespace Gameframe.SaveLoad 2 | { 3 | /// 4 | /// Interface for an encrypted method of serializing and deserializing an object tagged with the Serializable attribute 5 | /// 6 | public interface ISerializationMethodEncrypted : ISerializationMethod 7 | { 8 | void SetEncryption(string key, string salt); 9 | } 10 | } -------------------------------------------------------------------------------- /Runtime/ISerializationMethodEncrypted.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f2ca7b1f5ba36c044b4af60afa6f982a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SaveLoadManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace Gameframe.SaveLoad 6 | { 7 | /// 8 | /// SaveLoadManager 9 | /// Manager for saving and loading objects to/from disk. 10 | /// 11 | [CreateAssetMenu(menuName = "Gameframe/SaveLoad/SaveLoadManager")] 12 | public class SaveLoadManager : ScriptableObject 13 | { 14 | [Header("Settings"),SerializeField] private string defaultFolder = "SaveData"; 15 | public string DefaultFolder => defaultFolder; 16 | 17 | [SerializeField] private string baseFolder = "GameData"; 18 | public string BaseFolder => baseFolder; 19 | 20 | [SerializeField] private SerializationMethodType saveMethod = SerializationMethodType.Default; 21 | 22 | [Header("Encryption"),SerializeField] protected string key = string.Empty; 23 | public string Key => key; 24 | 25 | [SerializeField] protected string salt = string.Empty; 26 | public string Salt => salt; 27 | 28 | private Dictionary _methods; 29 | 30 | private void OnEnable() 31 | { 32 | //OnEnabled will be called when entering play mode in editor but also when selecting the object in editor 33 | //Constructor may only be called once which may lead to some weird behaviour if we use it for initializing this dictionary 34 | //Using OnEnabled ensures we do this initialization and the dictionary is fresh when we hit the play button in editor 35 | _methods = new Dictionary(); 36 | } 37 | 38 | /// 39 | /// Helper Method for constructing a new instance of SaveLoadManager which specific protected settings 40 | /// 41 | /// Base directory folder 42 | /// Default folder to save files to 43 | /// Method to use to save and load files 44 | /// Encryption key is required if using an encrypted method. 45 | /// Encryption salt is required if using an ecrypted method. 46 | /// Newly created instance of SaveLoadManager 47 | public static SaveLoadManager Create(string baseFolder, string defaultFolder, SerializationMethodType saveMethod, string key = null, string salt = null) 48 | { 49 | var instance = CreateInstance(); 50 | 51 | instance.baseFolder = baseFolder; 52 | instance.defaultFolder = defaultFolder; 53 | instance.key = key; 54 | instance.salt = salt; 55 | instance.saveMethod = saveMethod; 56 | 57 | return instance; 58 | } 59 | 60 | /// 61 | /// Save an object to disk 62 | /// 63 | /// object to be saved 64 | /// Name of file that will be written to 65 | /// Name of the folder that should contain the file 66 | public void Save(object obj, string filename, string folder = null) 67 | { 68 | if (string.IsNullOrEmpty(folder)) 69 | { 70 | folder = defaultFolder; 71 | } 72 | var saveLoadMethod = GetSaveLoadMethod(saveMethod); 73 | SaveLoadUtility.Save(obj,saveLoadMethod,filename,folder, baseFolder); 74 | } 75 | 76 | /// 77 | /// Gets the list of save files that have been created 78 | /// 79 | /// sub folder 80 | /// 81 | /// Will use Application.streamingAssetsPath as base path if true otherwise Application.persistentDataPath 82 | /// list of file names (excludes the path) 83 | public string[] GetFiles(string folder = null, string extension = null, bool streamingAssets = false) 84 | { 85 | if (string.IsNullOrEmpty(folder)) 86 | { 87 | folder = defaultFolder; 88 | } 89 | return SaveLoadUtility.GetSavedFiles(folder,baseFolder, extension, streamingAssets); 90 | } 91 | 92 | /// 93 | /// Gets the list of save files that have been created 94 | /// 95 | /// list to be populated with file names 96 | /// sub folder 97 | /// 98 | /// Will use Application.streamingAssetsPath as base path if true otherwise Application.persistentDataPath 99 | /// list of file names (excludes the path) 100 | public void GetFiles(List list, string folder = null, string extension = null, bool streamingAssets = false) 101 | { 102 | if (string.IsNullOrEmpty(folder)) 103 | { 104 | folder = defaultFolder; 105 | } 106 | 107 | SaveLoadUtility.GetSavedFiles(list, folder,baseFolder, extension, streamingAssets); 108 | } 109 | 110 | /// 111 | /// Creat a copy of an object by serializing and deserializing it. 112 | /// Not compatible with unity objects. 113 | /// 114 | /// object to be copied 115 | /// duplicated instance 116 | public object Copy(object obj) 117 | { 118 | if (obj is UnityEngine.Object) 119 | { 120 | throw new ArgumentException("UnityEngine.Object and child types not supported by copy method."); 121 | } 122 | var saveLoadMethod = GetSaveLoadMethod(saveMethod); 123 | return saveLoadMethod.Copy(obj); 124 | } 125 | 126 | /// 127 | /// Creat a copy of an object by serializing and deserializing it. 128 | /// Not compatible with Unity objects. 129 | /// 130 | /// object to be copied 131 | /// Type of object to be copied. 132 | /// duplicated instance 133 | public T Copy(T obj) 134 | { 135 | if (obj is UnityEngine.Object) 136 | { 137 | throw new ArgumentException("UnityEngine.Object and child types not supported by copy method."); 138 | } 139 | var saveLoadMethod = GetSaveLoadMethod(saveMethod); 140 | return (T)saveLoadMethod.Copy(obj); 141 | } 142 | 143 | /// 144 | /// Get the full path to a save file 145 | /// 146 | /// Name of file 147 | /// Name of folder containing file 148 | /// true if saves are from streaming assets 149 | /// full path to the file on disk 150 | public string GetPath(string filename, string folder = null, bool streamingAssets = false) 151 | { 152 | if (string.IsNullOrEmpty(folder)) 153 | { 154 | folder = defaultFolder; 155 | } 156 | var savePath = SaveLoadUtility.GetSavePath(folder, baseFolder, streamingAssets); 157 | var saveFilename = savePath + filename; 158 | return saveFilename; 159 | } 160 | 161 | /// 162 | /// Load an object from disk 163 | /// 164 | /// Name of file to load from 165 | /// Name of folder containing the file 166 | /// Load file from streaming assets 167 | /// Type of object to be loaded from file 168 | /// Instance of object loaded from file 169 | public T Load(string filename, string folder = null, bool streamingAssets = false) 170 | { 171 | return (T)Load(typeof(T), filename, folder, streamingAssets); 172 | } 173 | 174 | /// 175 | /// Load an object from disk 176 | /// 177 | /// Type of object to be loaded 178 | /// Name of file to load object from 179 | /// Name of folder containing the file to be loaded 180 | /// Load file from streaming assets 181 | /// Instance of object to be loaded. Null if file did not exist. 182 | public object Load(Type type, string filename, string folder = null, bool streamingAssets = false) 183 | { 184 | if (string.IsNullOrEmpty(folder)) 185 | { 186 | folder = defaultFolder; 187 | } 188 | var saveLoadMethod = GetSaveLoadMethod(saveMethod); 189 | return SaveLoadUtility.Load(type, saveLoadMethod,filename,folder, baseFolder, streamingAssets); 190 | } 191 | 192 | /// 193 | /// Delete saved file from disk 194 | /// 195 | /// 196 | /// 197 | public void DeleteSave(string filename, string folder = null) 198 | { 199 | if (string.IsNullOrEmpty(folder)) 200 | { 201 | folder = defaultFolder; 202 | } 203 | SaveLoadUtility.DeleteSavedFile(filename,folder, baseFolder); 204 | } 205 | 206 | /// 207 | /// Save object to file and specify the method of save/load 208 | /// 209 | /// Method to be used to save the file to disk. 210 | /// Object to be written to disk. 211 | /// Name of file to write to. 212 | /// Name of folder to save to. If null the default folder will be used. 213 | public void SaveWithMethod(SerializationMethodType methodType, object obj, string filename, string folder = null) 214 | { 215 | var saveLoadMethod = GetSaveLoadMethod(methodType); 216 | SaveLoadUtility.Save(obj,saveLoadMethod,filename,folder); 217 | } 218 | 219 | /// 220 | /// Load object from file and specify the method of save/load 221 | /// 222 | /// Method to be used to save the file to disk. 223 | /// Name of file to be read from. 224 | /// Name of the folder containing the file. 225 | /// Type of object to be loaded from the file. 226 | /// Object instance loaded from file. Null if file does not exist or load failed. 227 | public object LoadWithMethod(SerializationMethodType methodType, string filename, string folder = null) 228 | { 229 | return (T)LoadWithMethod(methodType, typeof(T), filename, folder); 230 | } 231 | 232 | /// 233 | /// Load object from file and specify the method of save/load 234 | /// 235 | /// Method to be used to save the file to disk. 236 | /// Name of file to be read from. 237 | /// Name of the folder containing the file. 238 | /// Type of object to be loaded from the file. 239 | /// Object instance loaded from file. Null if file does not exist or load failed. 240 | public object LoadWithMethod(SerializationMethodType methodType, Type type, string filename, string folder = null) 241 | { 242 | var saveLoadMethod = GetSaveLoadMethod(methodType); 243 | return SaveLoadUtility.Load(type, saveLoadMethod,filename,folder); 244 | } 245 | 246 | /// 247 | /// Saves a unity object to a file. 248 | /// First converts the object to JSON using the Unity Json utility. 249 | /// Then wraps the json string in an object so that object can be serialized with the normal methods. 250 | /// 251 | /// Object to be saved. 252 | /// Name of file to save to 253 | /// Name of folder to save the file in. Uses defualt folder when null. 254 | public void SaveUnityObject(UnityEngine.Object unityObj, string filename, string folder = null) 255 | { 256 | var savedObj = new JsonSerializedUnityObject 257 | { 258 | jsonData = JsonUtility.ToJson(unityObj) 259 | }; 260 | 261 | Save(savedObj,filename,folder); 262 | } 263 | 264 | /// 265 | /// Loads from file and overwrites the given object with the saved values. 266 | /// File must have been saved with SaveUnityObject method. 267 | /// Uses Unity's JsonUtility and has the same limitations. 268 | /// 269 | /// Object onto which the values from file will be written 270 | /// Name of file to read from. 271 | /// Name of folder containing file. If null the default folder will be used. 272 | /// True when something is loaded. False if file or data was not found. 273 | public bool LoadUnityObjectOverwrite(UnityEngine.Object objectToOverwrite, string filename, string folder = null) 274 | { 275 | var savedObj = Load(filename, folder); 276 | 277 | if (savedObj == null || string.IsNullOrEmpty(savedObj.jsonData)) 278 | { 279 | return false; 280 | } 281 | 282 | JsonUtility.FromJsonOverwrite(savedObj.jsonData,objectToOverwrite); 283 | return true; 284 | } 285 | 286 | /// 287 | /// Copies the serializable fields from one UnityEngine.Object to another 288 | /// 289 | /// object which should be copied 290 | /// object onto which copied fields should be written 291 | public void CopyUnityObjectOverwrite(UnityEngine.Object toCopy, UnityEngine.Object toOverwrite) 292 | { 293 | var jsonData = JsonUtility.ToJson(toCopy); 294 | JsonUtility.FromJsonOverwrite(jsonData,toOverwrite); 295 | } 296 | 297 | /// 298 | /// JsonSerializedUnityObject 299 | /// Wrapper for json data created when using Unity's JsonUtility to serialize an object derived from UnityEngine.Object 300 | /// This allows us to bypass some of the limitations of BinaryFormatter and serializing unity types such as Vector3, Quaternion etc. 301 | /// Many Unity structs are oddly not marked as serializable. 302 | /// 303 | [Serializable] 304 | private class JsonSerializedUnityObject 305 | { 306 | public string jsonData; 307 | } 308 | 309 | public bool IsEncrypted => (saveMethod == SerializationMethodType.BinaryEncrypted || saveMethod == SerializationMethodType.UnityJsonEncrypted); 310 | 311 | /// 312 | /// Sets a custom serialization method 313 | /// 314 | /// 315 | public void SetCustomSerializationMethod(ISerializationMethod customSerializationMethod) 316 | { 317 | if (_methods == null) 318 | { 319 | _methods = new Dictionary(); 320 | } 321 | _methods[SerializationMethodType.Custom] = customSerializationMethod; 322 | } 323 | 324 | private ISerializationMethod GetSaveLoadMethod(SerializationMethodType methodType) 325 | { 326 | if (_methods == null) 327 | { 328 | _methods = new Dictionary(); 329 | } 330 | 331 | if (_methods.TryGetValue(methodType, out var method)) 332 | { 333 | return method; 334 | } 335 | 336 | //Create method if it did not yet exist 337 | switch (methodType) 338 | { 339 | case SerializationMethodType.Default: 340 | method = GetSaveLoadMethod(SerializationMethodType.UnityJson); 341 | break; 342 | case SerializationMethodType.Binary: 343 | method = new SerializationMethodBinary(); 344 | break; 345 | case SerializationMethodType.UnityJson: 346 | method = new SerializationMethodUnityJson(); 347 | break; 348 | 349 | case SerializationMethodType.BinaryEncrypted: 350 | method = new SerializationMethodBinaryEncrypted(key,salt); 351 | break; 352 | case SerializationMethodType.UnityJsonEncrypted: 353 | method = new SerializationMethodUnityJsonEncrypted(key,salt); 354 | break; 355 | 356 | #if JSON_DOT_NET 357 | case SerializationMethodType.JsonDotNet: 358 | method = new SerializationMethodJsonDotNet(); 359 | break; 360 | case SerializationMethodType.JsonDotNetEncrypted: 361 | method = new SerializationMethodJsonDotNetEncrypted(key,salt); 362 | break; 363 | #endif 364 | 365 | case SerializationMethodType.Custom: 366 | throw new MissingComponentException("SerializationMethodType is Custom but no custom ISerializationMethod was found."); 367 | default: 368 | throw new ArgumentOutOfRangeException(nameof(methodType), methodType, "SaveLoadMethodType not supported"); 369 | } 370 | 371 | _methods[methodType] = method; 372 | 373 | return method; 374 | } 375 | 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /Runtime/SaveLoadManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b7aea37aa7edc45cf8b8ecbba9748cc3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SaveLoadUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace Gameframe.SaveLoad 7 | { 8 | public static class SaveLoadUtility 9 | { 10 | //Default folder name will be used if none is provided. 11 | private const string DefaultFolderName = "SaveLoad"; 12 | private const string DefaultBaseFolderPath = "GameData"; 13 | 14 | public static string GetSavePath(string folderName = null, string baseFolderPath = null, bool streamingAssets = false) 15 | { 16 | return !streamingAssets ? GetRuntimeSavePath(folderName, baseFolderPath) : GetStreamingAssetsSavePath(folderName, baseFolderPath); 17 | } 18 | 19 | public static string GetRuntimeSavePath(string folderName = null, string baseFolderPath = null) 20 | { 21 | if (string.IsNullOrEmpty(folderName)) 22 | { 23 | folderName = DefaultFolderName; 24 | } 25 | 26 | if (string.IsNullOrEmpty(baseFolderPath)) 27 | { 28 | baseFolderPath = DefaultBaseFolderPath; 29 | } 30 | 31 | var savePath = $"{Application.persistentDataPath}/{baseFolderPath}/"; 32 | savePath = $"{savePath}{folderName}/"; 33 | return savePath; 34 | } 35 | 36 | public static string GetStreamingAssetsSavePath(string folderName = null, string baseFolderPath = null) 37 | { 38 | if (string.IsNullOrEmpty(folderName)) 39 | { 40 | folderName = DefaultFolderName; 41 | } 42 | 43 | if (string.IsNullOrEmpty(baseFolderPath)) 44 | { 45 | baseFolderPath = DefaultBaseFolderPath; 46 | } 47 | 48 | var savePath = $"{Application.streamingAssetsPath}/{baseFolderPath}/"; 49 | savePath = $"{savePath}{folderName}/"; 50 | return savePath; 51 | } 52 | 53 | /// 54 | /// 55 | /// 56 | /// 57 | /// 58 | private static string GetSaveFileName(string fileName) 59 | { 60 | return fileName; 61 | } 62 | 63 | /// 64 | /// 65 | /// 66 | /// 67 | /// 68 | /// 69 | /// 70 | /// 71 | public static void Save(object saveObject, ISerializationMethod serializationMethod, string filename, string folderName = null, string baseFolderPath = null) 72 | { 73 | var savePath = GetSavePath(folderName,baseFolderPath); 74 | var saveFilename = GetSaveFileName(filename); 75 | 76 | //Create directory if it does not exist 77 | if (!Directory.Exists(savePath)) 78 | { 79 | Directory.CreateDirectory(savePath); 80 | } 81 | 82 | using (var saveFile = File.Create(savePath + saveFilename)) 83 | { 84 | serializationMethod.Save(saveObject,saveFile); 85 | saveFile.Close(); 86 | } 87 | } 88 | 89 | /// 90 | /// Load object from file 91 | /// 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// 97 | /// 98 | /// 99 | public static object Load(System.Type objectType, ISerializationMethod serializationMethod, string filename, string folderName = null, string baseFolderPath = null, bool streamingAssets = false) 100 | { 101 | var savePath = GetSavePath(folderName, baseFolderPath, streamingAssets); 102 | var saveFilename = savePath + GetSaveFileName(filename); 103 | 104 | object returnObject = null; 105 | 106 | if (!Directory.Exists(savePath) || !File.Exists(saveFilename)) 107 | { 108 | return null; 109 | } 110 | 111 | using (var saveFile = File.Open(saveFilename, FileMode.Open, FileAccess.Read, FileShare.Read)) 112 | { 113 | returnObject = serializationMethod.Load(objectType, saveFile); 114 | saveFile.Close(); 115 | } 116 | 117 | return returnObject; 118 | } 119 | 120 | /// 121 | /// Enumerate files in the save directory 122 | /// 123 | /// folder containing the save files 124 | /// base path to the folder 125 | /// include only files with this extension 126 | /// Will use Application.streamingAssetsPath as base path if true otherwise Application.persistentDataPath 127 | /// list of file names 128 | public static IEnumerable EnumerateSavedFiles(string folderName = null, string baseFolderPath = null, string extension = null, bool streamingAssets = false) 129 | { 130 | var savePath = GetSavePath(folderName,baseFolderPath,streamingAssets); 131 | 132 | //If directory does not exist we're done 133 | if (!Directory.Exists(savePath)) 134 | { 135 | yield break; 136 | } 137 | 138 | var searchPattern = string.IsNullOrEmpty(extension) ? "*" : $"*.{extension}"; 139 | foreach ( var file in Directory.EnumerateFiles(savePath,searchPattern,SearchOption.AllDirectories) ) 140 | { 141 | yield return Path.GetFileName(file); 142 | } 143 | } 144 | 145 | /// 146 | /// Creates an array list of save files in the given folder and path 147 | /// 148 | /// 149 | /// 150 | /// include only files with this extension 151 | /// Will use Application.streamingAssetsPath as base path if true otherwise Application.persistentDataPath 152 | /// Array of file names 153 | public static string[] GetSavedFiles(string folderName = null, string baseFolderPath = null, string extension = null, bool streamingAssets = false) 154 | { 155 | return EnumerateSavedFiles(folderName, baseFolderPath, extension, streamingAssets).ToArray(); 156 | } 157 | 158 | /// 159 | /// Populates a given array with a list of save files in the given folder and path 160 | /// 161 | /// list to be populated with file names 162 | /// 163 | /// 164 | /// include only files with this extension 165 | /// Will use Application.streamingAssetsPath as base path if true otherwise Application.persistentDataPath 166 | /// Array of file names 167 | public static void GetSavedFiles(List list, string folderName = null, string baseFolderPath = null, string extension = null, bool streamingAssets = false) 168 | { 169 | list.Clear(); 170 | list.AddRange(EnumerateSavedFiles(folderName, baseFolderPath, extension, streamingAssets)); 171 | } 172 | 173 | /// 174 | /// Check if a saved file exists 175 | /// 176 | /// 177 | /// 178 | /// 179 | /// 180 | public static bool Exists(string filename, string folderName = null, string baseFolderPath = null) 181 | { 182 | var savePath = GetSavePath(folderName, baseFolderPath); 183 | var saveFilename = savePath + GetSaveFileName(filename); 184 | return Directory.Exists(savePath) && File.Exists(saveFilename); 185 | } 186 | 187 | /// 188 | /// Delete a savedd file 189 | /// 190 | /// 191 | /// 192 | /// 193 | public static void DeleteSavedFile(string filename, string folderName = null, string baseFolderPath = null) 194 | { 195 | var saveFilename = GetSavePath(folderName,baseFolderPath) + GetSaveFileName(filename); 196 | if (File.Exists(saveFilename)) 197 | { 198 | File.Delete(saveFilename); 199 | } 200 | } 201 | 202 | public static void DeleteDirectory(string path) 203 | { 204 | var files = Directory.GetFiles(path); 205 | var dirs = Directory.GetDirectories(path); 206 | 207 | foreach (var file in files) 208 | { 209 | File.Delete(file); 210 | } 211 | 212 | foreach (var dir in dirs) 213 | { 214 | DeleteDirectory(dir); 215 | } 216 | 217 | Directory.Delete(path,false); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /Runtime/SaveLoadUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e4ec672a805d4c189f9f1e9bca2a310 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodBinary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.Serialization.Formatters.Binary; 4 | 5 | namespace Gameframe.SaveLoad 6 | { 7 | public class SerializationMethodBinary : ISerializationMethod 8 | { 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | public void Save(object savedObject, FileStream fileStream) 15 | { 16 | //Creating the formatter every time because this may get used in a task and I'm not sure if it's thread safe 17 | var formatter = new BinaryFormatter(); 18 | formatter.Serialize(fileStream, savedObject); 19 | } 20 | 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | public object Load(Type savedObjectType, FileStream fileStream) 28 | { 29 | object loadedObj = null; 30 | //Creating the formatter every time because this may get used in a task and I'm not sure if it's thread safe 31 | var formatter = new BinaryFormatter(); 32 | loadedObj = formatter.Deserialize(fileStream); 33 | return loadedObj; 34 | } 35 | 36 | public object Copy(object copyObject) 37 | { 38 | using (var stream = new MemoryStream()) 39 | { 40 | var formatter = new BinaryFormatter(); 41 | formatter.Serialize(stream, copyObject); 42 | stream.Position = 0; 43 | return formatter.Deserialize(stream); 44 | } 45 | } 46 | 47 | } 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodBinary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ebd6753c90488724a8f045860f9786dd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodBinaryEncrypted.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.Serialization.Formatters.Binary; 4 | 5 | namespace Gameframe.SaveLoad 6 | { 7 | public class SerializationMethodBinaryEncrypted : ISerializationMethod 8 | { 9 | private string _key; 10 | private string _salt; 11 | 12 | public SerializationMethodBinaryEncrypted(string key, string salt) 13 | { 14 | SetEncryption(key,salt); 15 | } 16 | 17 | public void Save(object savedObject, FileStream fileStream) 18 | { 19 | using (var serializationStream = new MemoryStream()) 20 | { 21 | //Creating the formatter every time because this may get used in a task and I'm not sure if it's thread safe 22 | var formatter = new BinaryFormatter(); 23 | formatter.Serialize(serializationStream, savedObject); 24 | serializationStream.Flush(); 25 | serializationStream.Position = 0; 26 | EncryptionUtility.Encrypt(serializationStream,fileStream,_key,_salt); 27 | } 28 | } 29 | 30 | public object Load(Type savedObjectType, FileStream fileStream) 31 | { 32 | object loadedObj = null; 33 | 34 | using (var memoryStream = new MemoryStream()) 35 | { 36 | EncryptionUtility.Decrypt(fileStream,memoryStream,_key,_salt); 37 | memoryStream.Position = 0; 38 | //Creating the formatter every time because this may get used in a task and I'm not sure if it's thread safe 39 | var formatter = new BinaryFormatter(); 40 | loadedObj = formatter.Deserialize(memoryStream); 41 | } 42 | 43 | return loadedObj; 44 | } 45 | 46 | public void SetEncryption(string key, string salt) 47 | { 48 | if (string.IsNullOrEmpty(key)) 49 | { 50 | throw new ArgumentNullException(nameof(key)); 51 | } 52 | 53 | if (string.IsNullOrEmpty(salt)) 54 | { 55 | throw new ArgumentNullException(nameof(salt)); 56 | } 57 | 58 | _key = key; 59 | _salt = salt; 60 | } 61 | 62 | public object Copy(object copyObject) 63 | { 64 | using (var stream = new MemoryStream()) 65 | { 66 | var formatter = new BinaryFormatter(); 67 | formatter.Serialize(stream, copyObject); 68 | stream.Position = 0; 69 | return formatter.Deserialize(stream); 70 | } 71 | } 72 | } 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodBinaryEncrypted.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a29866f639295e342978b5f9210cd24e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodJsonDotNet.cs: -------------------------------------------------------------------------------- 1 | #if JSON_DOT_NET 2 | 3 | using System; 4 | using System.IO; 5 | using Newtonsoft.Json; 6 | 7 | namespace Gameframe.SaveLoad 8 | { 9 | public class SerializationMethodJsonDotNet : ISerializationMethod 10 | { 11 | public void Save(object savedObject, FileStream fileStream) 12 | { 13 | var json = JsonConvert.SerializeObject(savedObject); 14 | using (var streamWriter = new StreamWriter(fileStream)) 15 | { 16 | streamWriter.Write(json); 17 | streamWriter.Close(); 18 | } 19 | } 20 | 21 | public object Load(Type savedObjectType, FileStream fileStream) 22 | { 23 | object loadedObj = null; 24 | 25 | using (var streamReader = new StreamReader(fileStream)) 26 | { 27 | var json = streamReader.ReadToEnd(); 28 | streamReader.Close(); 29 | loadedObj = JsonConvert.DeserializeObject(json,savedObjectType); 30 | } 31 | 32 | return loadedObj; 33 | } 34 | 35 | public object Copy(object copyObject) 36 | { 37 | using (var stream = new MemoryStream()) 38 | { 39 | var writeJson = JsonConvert.SerializeObject(copyObject); 40 | 41 | var streamWriter = new StreamWriter(stream); 42 | streamWriter.Write(writeJson); 43 | streamWriter.Flush(); 44 | 45 | stream.Position = 0; 46 | 47 | using (var streamReader = new StreamReader(stream)) 48 | { 49 | var readJson = streamReader.ReadToEnd(); 50 | return JsonConvert.DeserializeObject(readJson, copyObject.GetType()); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | #endif -------------------------------------------------------------------------------- /Runtime/SerializationMethodJsonDotNet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6d6b7773beac984a93e8a0219034c81 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodJsonDotNetEncrypted.cs: -------------------------------------------------------------------------------- 1 | #if JSON_DOT_NET 2 | 3 | using System; 4 | using System.IO; 5 | using Newtonsoft.Json; 6 | 7 | namespace Gameframe.SaveLoad 8 | { 9 | public class SerializationMethodJsonDotNetEncrypted : ISerializationMethodEncrypted 10 | { 11 | private string _key = null; 12 | private string _salt = null; 13 | 14 | public SerializationMethodJsonDotNetEncrypted(string key, string salt) 15 | { 16 | SetEncryption(key,salt); 17 | } 18 | 19 | public void Save(object savedObject, FileStream fileStream) 20 | { 21 | //TODO: Using Unity's json serializer... does not support dictionaries. Do better. 22 | var json = JsonConvert.SerializeObject(savedObject); 23 | using (var memoryStream = new MemoryStream()) 24 | { 25 | using (var streamWriter = new StreamWriter(memoryStream)) 26 | { 27 | streamWriter.Write(json); 28 | streamWriter.Flush(); 29 | memoryStream.Position = 0; 30 | EncryptionUtility.Encrypt(memoryStream,fileStream,_key,_salt); 31 | } 32 | } 33 | } 34 | 35 | public object Load(Type savedObjectType, FileStream fileStream) 36 | { 37 | object loadedObj = null; 38 | 39 | using (var memoryStream = new MemoryStream()) 40 | { 41 | EncryptionUtility.Decrypt(fileStream, memoryStream, _key, _salt); 42 | memoryStream.Position = 0; 43 | using (var streamReader = new StreamReader(memoryStream)) 44 | { 45 | var json = streamReader.ReadToEnd(); 46 | loadedObj = JsonConvert.DeserializeObject(json, savedObjectType); 47 | streamReader.Close(); 48 | } 49 | } 50 | 51 | return loadedObj; 52 | } 53 | 54 | public void SetEncryption(string key, string salt) 55 | { 56 | if (string.IsNullOrEmpty(key)) 57 | { 58 | throw new ArgumentNullException(nameof(key)); 59 | } 60 | 61 | if (string.IsNullOrEmpty(salt)) 62 | { 63 | throw new ArgumentNullException(nameof(salt)); 64 | } 65 | 66 | _key = key; 67 | _salt = salt; 68 | } 69 | 70 | public object Copy(object copyObject) 71 | { 72 | using (var stream = new MemoryStream()) 73 | { 74 | var writeJson = JsonConvert.SerializeObject(copyObject); 75 | 76 | var streamWriter = new StreamWriter(stream); 77 | streamWriter.Write(writeJson); 78 | streamWriter.Flush(); 79 | 80 | stream.Position = 0; 81 | 82 | using (var streamReader = new StreamReader(stream)) 83 | { 84 | var readJson = streamReader.ReadToEnd(); 85 | return JsonConvert.DeserializeObject(readJson, copyObject.GetType()); 86 | } 87 | } 88 | } 89 | 90 | } 91 | } 92 | 93 | #endif -------------------------------------------------------------------------------- /Runtime/SerializationMethodJsonDotNetEncrypted.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 022e3bf0dc9178945ba7b689a956de70 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodType.cs: -------------------------------------------------------------------------------- 1 | namespace Gameframe.SaveLoad 2 | { 3 | public enum SerializationMethodType 4 | { 5 | Default = 0, 6 | 7 | Binary = 1, 8 | BinaryEncrypted = 101, 9 | 10 | UnityJson = 2, 11 | UnityJsonEncrypted = 102, 12 | 13 | #if JSON_DOT_NET 14 | JsonDotNet = 3, 15 | JsonDotNetEncrypted = 103, 16 | #endif 17 | 18 | Custom = 1000 19 | } 20 | } -------------------------------------------------------------------------------- /Runtime/SerializationMethodType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e6e90cfc267139438e3c50ea17db84d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodUnityJson.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using UnityEngine; 4 | 5 | namespace Gameframe.SaveLoad 6 | { 7 | public class SerializationMethodUnityJson : ISerializationMethod 8 | { 9 | public void Save(object savedObject, FileStream fileStream) 10 | { 11 | //TODO: Using Unity's json serializer... does not support dictionaries. Do better. 12 | var json = JsonUtility.ToJson(savedObject); 13 | using (var streamWriter = new StreamWriter(fileStream)) 14 | { 15 | streamWriter.Write(json); 16 | streamWriter.Close(); 17 | } 18 | } 19 | 20 | public object Load(Type savedObjectType, FileStream fileStream) 21 | { 22 | object loadedObj = null; 23 | 24 | using (var streamReader = new StreamReader(fileStream)) 25 | { 26 | var json = streamReader.ReadToEnd(); 27 | streamReader.Close(); 28 | loadedObj = JsonUtility.FromJson(json, savedObjectType); 29 | } 30 | 31 | return loadedObj; 32 | } 33 | 34 | public object Copy(object copyObject) 35 | { 36 | using (var stream = new MemoryStream()) 37 | { 38 | var writeJson = JsonUtility.ToJson(copyObject); 39 | var streamWriter = new StreamWriter(stream); 40 | streamWriter.Write(writeJson); 41 | streamWriter.Flush(); 42 | 43 | stream.Position = 0; 44 | 45 | using (var streamReader = new StreamReader(stream)) 46 | { 47 | var readJson = streamReader.ReadToEnd(); 48 | streamReader.Close(); 49 | return JsonUtility.FromJson(readJson, copyObject.GetType()); 50 | } 51 | } 52 | } 53 | 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodUnityJson.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c270d5b8e4d49d4c9102562c929b599 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodUnityJsonEncrypted.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using UnityEngine; 4 | 5 | namespace Gameframe.SaveLoad 6 | { 7 | public class SerializationMethodUnityJsonEncrypted : ISerializationMethodEncrypted 8 | { 9 | private string _key; 10 | private string _salt; 11 | 12 | public SerializationMethodUnityJsonEncrypted(string key, string salt) 13 | { 14 | SetEncryption(key,salt); 15 | } 16 | 17 | public void Save(object savedObject, FileStream fileStream) 18 | { 19 | //Note: Use JsonDotNet method for Dictionary serialization 20 | var json = JsonUtility.ToJson(savedObject); 21 | using (var memoryStream = new MemoryStream()) 22 | { 23 | using (var streamWriter = new StreamWriter(memoryStream)) 24 | { 25 | streamWriter.Write(json); 26 | streamWriter.Flush(); 27 | memoryStream.Position = 0; 28 | EncryptionUtility.Encrypt(memoryStream,fileStream,_key,_salt); 29 | } 30 | } 31 | } 32 | 33 | public object Load(Type savedObjectType, FileStream fileStream) 34 | { 35 | object loadedObj = null; 36 | 37 | using (var memoryStream = new MemoryStream()) 38 | { 39 | EncryptionUtility.Decrypt(fileStream, memoryStream, _key, _salt); 40 | memoryStream.Position = 0; 41 | using (var streamReader = new StreamReader(memoryStream)) 42 | { 43 | var json = streamReader.ReadToEnd(); 44 | loadedObj = JsonUtility.FromJson(json, savedObjectType); 45 | streamReader.Close(); 46 | } 47 | } 48 | 49 | return loadedObj; 50 | } 51 | 52 | public void SetEncryption(string key, string salt) 53 | { 54 | if (string.IsNullOrEmpty(key)) 55 | { 56 | throw new ArgumentNullException(nameof(key)); 57 | } 58 | 59 | if (string.IsNullOrEmpty(salt)) 60 | { 61 | throw new ArgumentNullException(nameof(salt)); 62 | } 63 | 64 | _key = key; 65 | _salt = salt; 66 | } 67 | 68 | public object Copy(object copyObject) 69 | { 70 | using (var stream = new MemoryStream()) 71 | { 72 | var writeJson = JsonUtility.ToJson(copyObject); 73 | var streamWriter = new StreamWriter(stream); 74 | streamWriter.Write(writeJson); 75 | streamWriter.Flush(); 76 | 77 | stream.Position = 0; 78 | 79 | using (var streamReader = new StreamReader(stream)) 80 | { 81 | var readJson = streamReader.ReadToEnd(); 82 | streamReader.Close(); 83 | return JsonUtility.FromJson(readJson, copyObject.GetType()); 84 | } 85 | } 86 | } 87 | 88 | } 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /Runtime/SerializationMethodUnityJsonEncrypted.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c19fd85bc76de1742bac0a8cbb30bb17 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/com.gameframe.saveload.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.gameframe.saveload", 3 | "references": [], 4 | "optionalUnityReferences": [], 5 | "includePlatforms": [], 6 | "excludePlatforms": [], 7 | "allowUnsafeCode": false, 8 | "overrideReferences": false, 9 | "precompiledReferences": [], 10 | "autoReferenced": true, 11 | "defineConstraints": [], 12 | "versionDefines": [] 13 | } -------------------------------------------------------------------------------- /Runtime/com.gameframe.saveload.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b894308b0ac81480cb726cb405d83944 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/csc.rsp: -------------------------------------------------------------------------------- 1 | -nowarn:CS0649 2 | -------------------------------------------------------------------------------- /Runtime/csc.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f4cbf938e4839c64f9b94dec3d64d438 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Tests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4d8793a46ee6e4234a75d69ab4613bba 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82c2c60cb8dc3430787b7a0ac6533c5a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Editor/SaveLoadUtilityTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using NUnit.Framework; 5 | using UnityEngine; 6 | using Object = UnityEngine.Object; 7 | 8 | namespace Gameframe.SaveLoad.Tests 9 | { 10 | public class SaveLoadUtilityTests 11 | { 12 | private string TestKey = "TestKey"; 13 | private string TestSalt = "TestSalt"; 14 | 15 | private ISerializationMethod GetSerializationMethod(SerializationMethodType method) 16 | { 17 | switch (method) 18 | { 19 | case SerializationMethodType.Default: 20 | return new SerializationMethodUnityJson(); 21 | case SerializationMethodType.Binary: 22 | return new SerializationMethodBinary(); 23 | case SerializationMethodType.BinaryEncrypted: 24 | return new SerializationMethodBinaryEncrypted(TestKey,TestSalt); 25 | case SerializationMethodType.UnityJson: 26 | return new SerializationMethodUnityJson(); 27 | case SerializationMethodType.UnityJsonEncrypted: 28 | return new SerializationMethodUnityJsonEncrypted(TestKey,TestSalt); 29 | #if JSON_DOT_NET 30 | case SerializationMethodType.JsonDotNet: 31 | return new SerializationMethodJsonDotNet(); 32 | case SerializationMethodType.JsonDotNetEncrypted: 33 | return new SerializationMethodJsonDotNetEncrypted(TestKey,TestSalt); 34 | #endif 35 | case SerializationMethodType.Custom: 36 | return new SerializationMethodBinary(); 37 | default: 38 | throw new ArgumentOutOfRangeException(nameof(method), method, null); 39 | } 40 | } 41 | 42 | [Serializable] 43 | public class SaveLoadTestObject 44 | { 45 | public string testData; 46 | } 47 | 48 | [Test] 49 | public void SaveLoadAndDelete([Values]SerializationMethodType method) 50 | { 51 | var testSave = new SaveLoadTestObject() {testData = "SaveFileExists"}; 52 | var serializationMethod = GetSerializationMethod(method); 53 | var filename = "TestSave.sav"; 54 | 55 | SaveLoadUtility.Save(testSave,serializationMethod,filename); 56 | 57 | Assert.IsTrue(SaveLoadUtility.Exists(filename)); 58 | 59 | var loadedSave = (SaveLoadTestObject)SaveLoadUtility.Load(typeof(SaveLoadTestObject), serializationMethod, filename); 60 | Assert.NotNull(loadedSave); 61 | Assert.IsTrue(loadedSave.testData == testSave.testData); 62 | 63 | SaveLoadUtility.DeleteSavedFile(filename); 64 | 65 | Assert.IsFalse(SaveLoadUtility.Exists(filename)); 66 | } 67 | 68 | [Test] 69 | public void CanGetFiles_Empty() 70 | { 71 | var files = SaveLoadUtility.GetSavedFiles(); 72 | Assert.IsTrue(files.Length == 0); 73 | } 74 | 75 | [Test] 76 | public void CanGetFiles() 77 | { 78 | var testSave = new SaveLoadTestObject() {testData = "SaveFileExists"}; 79 | var serializationMethod = GetSerializationMethod(SerializationMethodType.Binary); 80 | var filename = "TestSave.sav"; 81 | var folder = "TestFolder"; 82 | 83 | SaveLoadUtility.Save(testSave,serializationMethod,filename,folder); 84 | 85 | var files = SaveLoadUtility.GetSavedFiles(folder); 86 | Assert.IsTrue(files.Length == 1,$"Total Save Files: {files.Length} Expected 1"); 87 | 88 | //Files should contain a list of names that exactly match the file name used 89 | //omits the path of the file 90 | Assert.IsTrue(files[0] == filename); 91 | 92 | SaveLoadUtility.DeleteSavedFile(filename,folder); 93 | 94 | files = SaveLoadUtility.GetSavedFiles(); 95 | Assert.IsTrue(files.Length == 0); 96 | } 97 | 98 | [Test] 99 | public void CanGetFilesWithExtension() 100 | { 101 | var testSave = new SaveLoadTestObject() {testData = "SaveFileExists"}; 102 | var serializationMethod = GetSerializationMethod(SerializationMethodType.Binary); 103 | var filename = "TestSave.sav"; 104 | var folder = "TestFolder"; 105 | 106 | SaveLoadUtility.Save(testSave,serializationMethod,filename,folder); 107 | 108 | var files = SaveLoadUtility.GetSavedFiles(folder,null, "sav"); 109 | Assert.IsTrue(files.Length == 1); 110 | 111 | //Files should contain a list of names that exactly match the file name used 112 | //omits the path of the file 113 | Assert.IsTrue(files[0] == filename); 114 | 115 | SaveLoadUtility.DeleteSavedFile(filename,folder); 116 | 117 | files = SaveLoadUtility.GetSavedFiles(); 118 | Assert.IsTrue(files.Length == 0); 119 | } 120 | 121 | [TearDown] 122 | public void TearDown() 123 | { 124 | var path = SaveLoadUtility.GetSavePath(); 125 | string[] files = {"TestSave.sav"}; 126 | 127 | foreach (var file in files) 128 | { 129 | var filename = path + file; 130 | if (File.Exists(filename)) 131 | { 132 | File.Delete(filename); 133 | } 134 | } 135 | } 136 | 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /Tests/Editor/SaveLoadUtilityTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fb0d9e8f35733426d91318d9dbc2bbf7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Editor/com.gameframe.saveload.editor.tests.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.gameframe.saveload.editor.tests", 3 | "references": [ 4 | "GUID:b894308b0ac81480cb726cb405d83944", 5 | "GUID:0acc523941302664db1f4e527237feb3", 6 | "GUID:27619889b8ba8c24980f49ee34dbb44a" 7 | ], 8 | "includePlatforms": [ 9 | "Editor" 10 | ], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": false, 13 | "overrideReferences": true, 14 | "precompiledReferences": [ 15 | "nunit.framework.dll" 16 | ], 17 | "autoReferenced": false, 18 | "defineConstraints": [], 19 | "versionDefines": [], 20 | "noEngineReferences": false 21 | } -------------------------------------------------------------------------------- /Tests/Editor/com.gameframe.saveload.editor.tests.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e17acf757b454b0c965a783efb26873 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Tests/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e94cb5d8801bd4e80958aa690d01f560 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Runtime/SaveLoadManagerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using NUnit.Framework; 5 | using UnityEngine; 6 | using Object = UnityEngine.Object; 7 | 8 | namespace Gameframe.SaveLoad.Tests 9 | { 10 | public class SaveLoadManagerTests 11 | { 12 | 13 | [Serializable] 14 | public class SaveLoadTestObject 15 | { 16 | public List listOfStrings = new List(); 17 | public int count; 18 | } 19 | 20 | [Serializable] 21 | public class SaveLoadTestUnityObject : ScriptableObject 22 | { 23 | public string textValue = ""; 24 | public Vector3 pt; 25 | public Quaternion rot; 26 | } 27 | 28 | [Serializable] 29 | public class SaveLoadDictionaryTestObject 30 | { 31 | public Dictionary dict = new Dictionary(); 32 | public string name = ""; 33 | } 34 | 35 | private static readonly string BaseDirectory = "GameData"; 36 | private static readonly string SaveDirectory = "SaveData"; 37 | private static readonly string TestEncryptionKey = "SaveLoadTestEncryptionKey"; 38 | private static readonly string TestEncryptionSalt = "SaveLoadTestEncryptionSalt"; 39 | 40 | private static SaveLoadManager CreateManager(SerializationMethodType method = SerializationMethodType.Default) 41 | { 42 | var manager = SaveLoadManager.Create(BaseDirectory,SaveDirectory,method,TestEncryptionKey, TestEncryptionSalt); 43 | if (method == SerializationMethodType.Custom) 44 | { 45 | manager.SetCustomSerializationMethod(new SerializationMethodUnityJson()); 46 | } 47 | return manager; 48 | } 49 | 50 | private static void CleanupFiles() 51 | { 52 | //Cleaning up some files that were made by tests 53 | const string filename = "Testfile"; 54 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 55 | if (File.Exists(filepath)) 56 | { 57 | File.Delete(filepath); 58 | } 59 | } 60 | 61 | [SetUp] 62 | public void Setup() 63 | { 64 | CleanupFiles(); 65 | } 66 | 67 | [TearDown] 68 | public void Teardown() 69 | { 70 | CleanupFiles(); 71 | } 72 | 73 | [Test] 74 | public void CanCreateManager([Values] SerializationMethodType method) 75 | { 76 | var manager = CreateManager(method); 77 | Assert.IsTrue(manager != null); 78 | Object.Destroy(manager); 79 | } 80 | 81 | [Test] 82 | public void CanSave([Values] SerializationMethodType method) 83 | { 84 | var manager = CreateManager(method); 85 | 86 | var testObject = new SaveLoadTestObject() 87 | { 88 | listOfStrings = new List {"one", "two"}, 89 | count = 10, 90 | }; 91 | 92 | const string filename = "Testfile"; 93 | 94 | manager.Save(testObject,filename); 95 | 96 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 97 | Assert.IsTrue(File.Exists(filepath)); 98 | 99 | manager.DeleteSave(filename); 100 | Assert.IsFalse(File.Exists(filepath)); 101 | 102 | Object.Destroy(manager); 103 | } 104 | 105 | [Test] 106 | public void GetPath([Values] SerializationMethodType method) 107 | { 108 | var manager = CreateManager(method); 109 | Debug.Log(manager.GetPath("MyFile.sav")); 110 | } 111 | 112 | [Test] 113 | public void GetFiles([Values] SerializationMethodType method) 114 | { 115 | var manager = CreateManager(method); 116 | 117 | var testObject = new SaveLoadTestObject() 118 | { 119 | listOfStrings = new List {"one", "two"}, 120 | count = 10, 121 | }; 122 | 123 | const string filename = "Testfile"; 124 | 125 | manager.Save(testObject,filename); 126 | 127 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 128 | Assert.IsTrue(File.Exists(filepath)); 129 | 130 | var files = manager.GetFiles(); 131 | Assert.IsTrue(files.Length == 1,$"Expected 1 file but found {files.Length}"); 132 | Assert.IsTrue(files[0] == filename); 133 | 134 | manager.DeleteSave(filename); 135 | Assert.IsFalse(File.Exists(filepath)); 136 | 137 | Object.Destroy(manager); 138 | } 139 | 140 | 141 | 142 | [Test] 143 | public void CanSaveAndLoad([Values] SerializationMethodType method) 144 | { 145 | var manager = CreateManager(method); 146 | 147 | var testObject = new SaveLoadTestObject() 148 | { 149 | listOfStrings = new List {"one", "two"}, 150 | count = 10, 151 | }; 152 | 153 | const string filename = "Testfile"; 154 | 155 | manager.Save(testObject,filename); 156 | 157 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 158 | Assert.IsTrue(File.Exists(filepath)); 159 | 160 | var loadedObject = manager.Load(filename); 161 | 162 | Assert.IsTrue(loadedObject.listOfStrings.Count == testObject.listOfStrings.Count); 163 | 164 | for (int i = 0; i < testObject.listOfStrings.Count; i++) 165 | { 166 | Assert.IsTrue(testObject.listOfStrings[i] == loadedObject.listOfStrings[i]); 167 | } 168 | 169 | Assert.IsTrue(testObject.count == loadedObject.count); 170 | 171 | manager.DeleteSave(filename); 172 | Assert.IsFalse(File.Exists(filepath)); 173 | 174 | Object.Destroy(manager); 175 | } 176 | 177 | [Test] 178 | public void CanCopy([Values] SerializationMethodType method) 179 | { 180 | var manager = CreateManager(method); 181 | 182 | var testObject = new SaveLoadTestObject() 183 | { 184 | listOfStrings = new List {"one", "two"}, 185 | count = 10, 186 | }; 187 | 188 | var loadedObject = manager.Copy(testObject); 189 | 190 | Assert.NotNull(loadedObject); 191 | Assert.IsFalse(ReferenceEquals(testObject,loadedObject)); 192 | Assert.NotNull(loadedObject.listOfStrings); 193 | Assert.IsTrue(loadedObject.listOfStrings.Count == testObject.listOfStrings.Count); 194 | 195 | for (int i = 0; i < testObject.listOfStrings.Count; i++) 196 | { 197 | Assert.IsTrue(testObject.listOfStrings[i] == loadedObject.listOfStrings[i]); 198 | } 199 | 200 | Assert.IsTrue(testObject.count == loadedObject.count); 201 | 202 | Object.Destroy(manager); 203 | } 204 | 205 | [Test] 206 | public void LoadReturnsNullWhenFileDoesnotExist([Values] SerializationMethodType method) 207 | { 208 | var manager = CreateManager(method); 209 | const string filename = "Testfile"; 210 | 211 | var loadedObject = manager.Load(filename); 212 | Assert.IsTrue(loadedObject == null); 213 | 214 | Object.Destroy(manager); 215 | } 216 | 217 | [Test] 218 | public void CanSaveUnityObject([Values] SerializationMethodType method) 219 | { 220 | const string filename = "Testfile"; 221 | 222 | var manager = CreateManager(method); 223 | var testObj = ScriptableObject.CreateInstance(); 224 | 225 | testObj.textValue = "MyValue"; 226 | 227 | manager.SaveUnityObject(testObj,filename); 228 | 229 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 230 | Assert.IsTrue(File.Exists(filepath)); 231 | 232 | manager.DeleteSave(filename); 233 | Assert.IsFalse(File.Exists(filepath)); 234 | 235 | Object.Destroy(testObj); 236 | Object.Destroy(manager); 237 | } 238 | 239 | [Test] 240 | public void CanSaveLoadUnityObject([Values] SerializationMethodType method) 241 | { 242 | const string filename = "Testfile"; 243 | 244 | var manager = CreateManager(method); 245 | var testObj = ScriptableObject.CreateInstance(); 246 | var loadedTestObj = ScriptableObject.CreateInstance(); 247 | 248 | testObj.textValue = "MyValue"; 249 | 250 | manager.SaveUnityObject(testObj,filename); 251 | 252 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 253 | Assert.IsTrue(File.Exists(filepath)); 254 | 255 | manager.LoadUnityObjectOverwrite(loadedTestObj,filename); 256 | Assert.IsTrue(loadedTestObj.textValue == testObj.textValue); 257 | 258 | manager.DeleteSave(filename); 259 | Assert.IsFalse(File.Exists(filepath)); 260 | 261 | Object.Destroy(testObj); 262 | Object.Destroy(loadedTestObj); 263 | Object.Destroy(manager); 264 | } 265 | 266 | [Test] 267 | public void CanCopyUnityObject([Values] SerializationMethodType method) 268 | { 269 | var manager = CreateManager(method); 270 | var testObj = ScriptableObject.CreateInstance(); 271 | var loadedTestObj = ScriptableObject.CreateInstance(); 272 | 273 | testObj.textValue = "MyValue"; 274 | 275 | manager.CopyUnityObjectOverwrite(testObj,loadedTestObj); 276 | 277 | Assert.IsTrue(loadedTestObj.textValue == testObj.textValue); 278 | 279 | Object.Destroy(testObj); 280 | Object.Destroy(loadedTestObj); 281 | Object.Destroy(manager); 282 | } 283 | 284 | [Test] 285 | public void CopyThrowsArgumentExceptionOnUnityObject([Values] SerializationMethodType method) 286 | { 287 | var manager = CreateManager(method); 288 | var testObj = ScriptableObject.CreateInstance(); 289 | 290 | Assert.Throws(() => 291 | { 292 | manager.Copy(testObj); 293 | }); 294 | 295 | Object.Destroy(testObj); 296 | Object.Destroy(manager); 297 | } 298 | 299 | [Test] 300 | public void CanSaveAndLoadDictionary([Values( 301 | SerializationMethodType.Binary, 302 | SerializationMethodType.BinaryEncrypted 303 | #if JSON_DOT_NET 304 | ,SerializationMethodType.JsonDotNet, 305 | SerializationMethodType.JsonDotNetEncrypted 306 | #endif 307 | )] SerializationMethodType method) 308 | { 309 | var manager = CreateManager(method); 310 | 311 | var testObject = new SaveLoadDictionaryTestObject() 312 | { 313 | dict = new Dictionary 314 | { 315 | {"one", 1}, 316 | {"two", 2} 317 | }, 318 | name = "Test", 319 | }; 320 | 321 | const string filename = "Testfile"; 322 | 323 | manager.Save(testObject,filename); 324 | 325 | var filepath = $"{SaveLoadUtility.GetSavePath(SaveDirectory, BaseDirectory)}/{filename}"; 326 | Assert.IsTrue(File.Exists(filepath)); 327 | 328 | var loadedObject = manager.Load(filename); 329 | 330 | Assert.IsTrue(loadedObject.name == testObject.name); 331 | Assert.IsTrue(loadedObject.dict.Count == testObject.dict.Count); 332 | Assert.IsTrue(loadedObject.dict.ContainsKey("one")); 333 | Assert.IsTrue(loadedObject.dict.ContainsKey("two")); 334 | Assert.IsTrue(loadedObject.dict["one"] == 1); 335 | Assert.IsTrue(loadedObject.dict["two"] == 2); 336 | 337 | manager.DeleteSave(filename); 338 | Assert.IsFalse(File.Exists(filepath)); 339 | 340 | Object.Destroy(manager); 341 | } 342 | 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /Tests/Runtime/SaveLoadManagerTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 874e7f7b182ed7c429b077818517b220 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/com.gameframe.saveload.Tests.asmdef: -------------------------------------------------------------------------------- 1 | { "name": "com.gameframe.saveload.Tests", "references": [ "com.gameframe.saveload" ], "optionalUnityReferences": ["TestAssemblies"], "includePlatforms": [], "excludePlatforms": [] } -------------------------------------------------------------------------------- /Tests/Runtime/com.gameframe.saveload.Tests.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8114fb25aa37d4aeeb48586537359895 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.gameframe.saveload", 3 | "displayName": "Gameframe.SaveLoad", 4 | "version": "1.0.10", 5 | "description": "Serialization helper utility that supports save, load and encryption.", 6 | "keywords": [], 7 | "author": { 8 | "name": "Cory Leach", 9 | "email": "cory.leach@gmail.com", 10 | "url": "https://github.com/coryleach", 11 | "github": "coryleach", 12 | "twitter": "coryleach" 13 | }, 14 | "repositoryName": "UnitySaveLoad", 15 | "type": "library", 16 | "hideInEditor": false 17 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4cad32cb7b3b3412699c08facccd45ce 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------