using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.IO; public class FileDataHandler { private string dataDirPath = ""; private string dataFileName = ""; private bool useEncryption = false; private readonly string encryptionCodeWord = "word"; private readonly string backupExtension = ".bak"; public FileDataHandler(string dataDirPath, string dataFileName, bool useEncryption) { this.dataDirPath = dataDirPath; this.dataFileName = dataFileName; this.useEncryption = useEncryption; } public GameData Load(bool allowRestoreFromBackup = true) { // use Path.Combine to account for different OS's having different path separators string fullPath = Path.Combine(dataDirPath, dataFileName); GameData loadedData = null; if (File.Exists(fullPath)) { try { // load the serialized data from the file string dataToLoad = ""; using (FileStream stream = new FileStream(fullPath, FileMode.Open)) { using (StreamReader reader = new StreamReader(stream)) { dataToLoad = reader.ReadToEnd(); } } // optionally decrypt the data if (useEncryption) { dataToLoad = EncryptDecrypt(dataToLoad); } // deserialize the data from Json back into the C# object loadedData = JsonUtility.FromJson(dataToLoad); } catch (Exception e) { // since we're calling Load(..) recursively, we need to account for the case where // the rollback succeeds, but data is still failing to load for some other reason, // which without this check may cause an infinite recursion loop. if (allowRestoreFromBackup) { Debug.LogWarning("Failed to load data file. Attempting to roll back.\n" + e); bool rollbackSuccess = AttemptRollback(fullPath); if (rollbackSuccess) { // try to load again recursively loadedData = Load(false); } } // if we hit this else block, one possibility is that the backup file is also corrupt else { Debug.LogError("Error occured when trying to load file at path: " + fullPath + " and backup did not work.\n" + e); } } } return loadedData; } public void Save(GameData data) { // use Path.Combine to account for different OS's having different path separators string fullPath = Path.Combine(dataDirPath, dataFileName); Debug.Log("Saving to " + fullPath); string backupFilePath = fullPath + backupExtension; try { // create the directory the file will be written to if it doesn't already exist Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // serialize the C# game data object into Json string dataToStore = JsonUtility.ToJson(data, true); // optionally encrypt the data if (useEncryption) { dataToStore = EncryptDecrypt(dataToStore); } // write the serialized data to the file using (FileStream stream = new FileStream(fullPath, FileMode.Create)) { using (StreamWriter writer = new StreamWriter(stream)) { writer.Write(dataToStore); } } // verify the newly saved file can be loaded successfully GameData verifiedGameData = Load(); // if the data can be verified, back it up if (verifiedGameData != null) { File.Copy(fullPath, backupFilePath, true); } // otherwise, something went wrong and we should throw an exception else { throw new Exception("Save file could not be verified and backup could not be created."); } } catch (Exception e) { Debug.LogError("Error occured when trying to save data to file: " + fullPath + "\n" + e); } } public void Delete() { string fullPath = Path.Combine(dataDirPath, dataFileName); try { // ensure the data file exists at this path before deleting the directory if (File.Exists(fullPath)) { // delete the profile folder and everything within it Directory.Delete(Path.GetDirectoryName(fullPath), true); } else { Debug.LogWarning("Tried to delete profile data, but data was not found at path: " + fullPath); } } catch (Exception e) { Debug.LogError("Failed to delete data at path: " + fullPath + "\n" + e); } } // the below is a simple implementation of XOR encryption private string EncryptDecrypt(string data) { string modifiedData = ""; for (int i = 0; i < data.Length; i++) { modifiedData += (char) (data[i] ^ encryptionCodeWord[i % encryptionCodeWord.Length]); } return modifiedData; } private bool AttemptRollback(string fullPath) { bool success = false; string backupFilePath = fullPath + backupExtension; try { // if the file exists, attempt to roll back to it by overwriting the original file if (File.Exists(backupFilePath)) { File.Copy(backupFilePath, fullPath, true); success = true; Debug.LogWarning("Had to roll back to backup file at: " + backupFilePath); } // otherwise, we don't yet have a backup file - so there's nothing to roll back to else { throw new Exception("Tried to roll back, but no backup file exists to roll back to."); } } catch (Exception e) { Debug.LogError("Error occured when trying to roll back to backup file at: " + backupFilePath + "\n" + e); } return success; } }