using System; using System.IO; using UnityEngine; /// <summary> /// Handles writing and reading of the Json save file created with or without encryption /// </summary> public class FileDataHandler { readonly string dataDirectoryPath = string.Empty; // Directory path reference for Path.Combine usage readonly string fileName = string.Empty; // Folder name for the directory path reference that holds the data file readonly string dataFileName = string.Empty; // Data file name reference for Path.Combine usage readonly bool useEncryption = false; // Optional encryption usage const string Encryption_Code_Word = "h0m3B00u1nDA4DB370Nd"; // Constant code word for encryption usage /// <summary> /// Constructor for the File Data Handler /// </summary> /// <param name="dataDirectoryPath">The main path to the directory that holds the folder and data</param> /// <param name="fileName">Name of the folder that holds the data</param> /// <param name="dataFileName">Name of the data file itself</param> /// <param name="useEncryption">True to use encryption, false otherwise</param> public FileDataHandler(string dataDirectoryPath, string fileName, string dataFileName, bool useEncryption) { this.dataDirectoryPath = dataDirectoryPath; this.fileName = fileName; this.dataFileName = dataFileName; this.useEncryption = useEncryption; } /// <summary> /// Loads the game data from the Json on the player's hardware /// </summary> /// <returns>Game Data that was stored within the file that was retrieved</returns> /// <exception cref="ArgumentException">If any of the path parameters are empty than unable to load without error</exception> public GameData Load() { if (dataDirectoryPath == string.Empty || dataFileName == string.Empty) throw new ArgumentException("Data file name or directory needs to not be empty."); string fullPathName = Path.Combine(dataDirectoryPath, fileName, dataFileName); GameData loadedData = null; if (File.Exists(fullPathName)) { try { // Loads the serialized data from the file string dataToLoad = string.Empty; using (FileStream stream = new(fullPathName, FileMode.Open)) { using (StreamReader reader = new(stream)) { dataToLoad = reader.ReadToEnd(); } } // Optional Decryption if (useEncryption) dataToLoad = EncryptDecrypt(dataToLoad); // Deserializes the Json into the C# Game Data format to be referenced from all of the DataPersistence Interface using objects loadedData = JsonUtility.FromJson<GameData>(dataToLoad); } catch (Exception e) { Debug.LogError("Error occurred when trying to load the game file from file: " + fullPathName + "\n" + e); } } return loadedData; } /// <summary> /// Saves the GameData into a Json onto player hardware /// </summary> /// <param name="data">The Game Data in C# form to be stored as a serialized Json format</param> /// <exception cref="ArgumentException">If any of the path parameters are empty than unable to save without error</exception> public void Save(GameData data) { if (dataDirectoryPath == string.Empty || dataFileName == string.Empty) throw new ArgumentException("Fullpath name needs to not be empty."); string fullPathName = Path.Combine(dataDirectoryPath, fileName, dataFileName); try { // Creates the directory the file will be written to if does not already exist Directory.CreateDirectory(Path.GetDirectoryName(fullPathName)); // Serializes the game data into Json string dataToStore = JsonUtility.ToJson(data, true); // Optional Encryption if (useEncryption) dataToStore = EncryptDecrypt(dataToStore); // Writes the data into the new/overwritten file using (FileStream stream = new(fullPathName, FileMode.Create)) { using (StreamWriter writer = new(stream)) { writer.Write(dataToStore); } } } catch (Exception e) { Debug.LogError("Error occurred when saving game data to: " + fullPathName + "\n" + e); } } /// <summary> /// Deletes the current GameData from the player's hardware /// </summary> /// <exception cref="ArgumentException">If any of the path parameters are empty than unable to delete without error</exception> public void Delete() { if (dataDirectoryPath == string.Empty || dataFileName == string.Empty) throw new ArgumentException("Fullpath name needs to not be empty."); string fullPathName = Path.Combine(dataDirectoryPath, fileName, dataFileName); // Deletes the file File.Delete(fullPathName); // Refreshes the Unity Editor when testing deleting methods #if UNITY_EDITOR UnityEditor.AssetDatabase.Refresh(); #endif } /// <summary> /// Checks the existence of a save file for the player /// </summary> /// <returns>True if the directory and file exist otherwise false</returns> public bool DataState() { string fullPathName = Path.Combine(dataDirectoryPath, fileName, dataFileName); if (File.Exists(fullPathName)) return true; else return false; } /// <summary> /// Encrypts or Decrypts the string based on the constant codeword chosen /// </summary> /// <param name="data">The data that was parsed from the Json or going to be parsed to the Json</param> /// <returns>The data in either the encrypted or decrypted form</returns> private string EncryptDecrypt(string data) { string modifiedData = string.Empty; for (int i = 0; i < data.Length; i++) { modifiedData += (char)(data[i] ^ Encryption_Code_Word[i % Encryption_Code_Word.Length]); } return modifiedData; } }