Homebound / Scripts / DataHandling / FileDataHandler.cs
FileDataHandler.cs
Raw
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;
    }
}