using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; namespace Otter { /// /// Class that is used for storing strings, ints, floats, or bools with keys of enum or string. The /// saver can output data in an semi-encrypted format, and also an editable config file format. /// public class DataSaver { /// /// The string to use when delimiting key data in data exports. /// public string KeyDelim = "::OTTERK::"; /// /// The string to use when delimiting value data in data exports. /// public string ValueDelim = "::OTTERV::"; /// /// The phrase to use as a salt when encrypting the data exports. /// public string EncryptionSalt = "otter"; /// /// The guide to salt the data string. {S} is the salt, {D} is the data. /// It is recommended to change this from the default for your game, but /// only if you really care about hacking save data. /// public string SaltGuide = "{S}{D}{S}"; /// /// The default path that the files will be imported from and exported to. /// public string DefaultPath = ""; /// /// The default file name that the data will export as. /// public string DefaultFilename = "data"; /// /// The export mode for the data. /// Data: Semi-encrypted uneditable data. /// Config: Easy to hand edit unencrypted data. /// public DataExportMode ExportMode = DataExportMode.Data; /// /// The export modes for the data. /// public enum DataExportMode { Data, Config } Dictionary data = new Dictionary(); /// /// Initializes a new instance of the DataSaver class. /// /// The default path. public DataSaver(string defaultPath = "") { DefaultPath = defaultPath; } /// /// Verifies the specified string data. Only applies to DataExportMode.Data. /// /// The string data. /// True if the data is successfully verified. public bool Verify(string stringData) { string[] split = Regex.Split(stringData, ":"); if (split.Length != 2) return false; string dataToHash = SaltGuide; dataToHash = dataToHash.Replace("{S}", EncryptionSalt); dataToHash = dataToHash.Replace("{D}", split[1]); string hash = Util.MD5Hash(dataToHash); if (hash == split[0]) return true; return false; } /// /// Deletes the exported file for this save data. /// /// The filename to delete (usually you don't have to set this.) public void ClearFile(string filename = "") { if (filename == "") filename = DefaultFilename; filename = DefaultPath + filename; if (!File.Exists(filename)) return; File.Delete(filename); } /// /// Clears the data. /// public void Clear() { data = new Dictionary(); } /// /// Check if an exported file exists for this data. /// /// The filename to check. /// True if the exported file exists, and is verified for encrypted files. public bool FileExists(string filename = "") { if (filename == "") filename = DefaultFilename; filename = DefaultPath + filename; if (File.Exists(filename)) { string loaded = File.ReadAllText(filename); if (ExportMode == DataExportMode.Data) { return Verify(loaded); } else { return true; } } return false; } /// /// Imports the data in the specified file. /// /// The filename. /// if set to true verify the data before importing. public void Import(string filename = "", bool verify = true) { if (filename == "") filename = DefaultFilename; filename = DefaultPath + filename; if (!File.Exists(filename)) return; string loaded = File.ReadAllText(filename); if (ExportMode == DataExportMode.Data) { if (Verify(loaded) || !verify) { string[] split = Regex.Split(loaded, ":"); loaded = Util.DecompressString(split[1]); var splitData = Regex.Split(loaded, ValueDelim); foreach (var s in splitData) { var entry = Regex.Split(s, KeyDelim); var key = entry[0]; var value = entry[1]; if (data.ContainsKey(key)) data[key] = value; else data.Add(key, value); } } else { Util.Log("Data load failed: corrupt or modified data."); } } else { var split = loaded.Split('\n'); foreach (var s in split) { string[] entry = s.Split('='); if (entry.Length == 2) { SetData(entry[0].Replace((char)16, '='), entry[1].Replace((char)16, '=')); } } } } /// /// Exports the data to the specified file. /// /// The filename. public void Export(string filename = "") { if (filename == "") filename = DefaultFilename; filename = DefaultPath + filename; if (ExportMode == DataExportMode.Data) { string str = Util.CompressString(Util.DictionaryToString(data, KeyDelim, ValueDelim)); string dataToHash = SaltGuide; dataToHash = dataToHash.Replace("{S}", EncryptionSalt); dataToHash = dataToHash.Replace("{D}", str); str = Util.MD5Hash(dataToHash) + ":" + str; File.WriteAllText(filename, str); } else { string str = ""; foreach (var e in data) { str += "" + e.Key.Replace('=', (char)16) + "=" + e.Value.Replace('=', (char)16) + "\n"; } str = str.Trim(); File.WriteAllText(filename, str); } } /// /// Gets the string with the specified key. /// /// The key. /// The string data or null. public string this[string key] { get { if (data.ContainsKey(key)) { return data[key]; } return null; } } /// /// Gets the string with the specified key. /// /// The key. /// The string data or null. public string this[Enum key] { get { return this[Util.EnumValueToString(key)]; } } /// /// Gets a float from the data. /// /// The key. /// A float from the specified key. public float GetFloat(string key) { return float.Parse(data[key]); } /// /// Gets a float from the data. /// /// The key. /// A float from the specified key. public float GetFloat(Enum key) { return GetFloat(Util.EnumValueToString(key)); } /// /// Gets a float or a default float value. /// /// The key. /// The default if not found. /// The value or the default if a value is not found. public float GetFloatOrDefault(string key, float defaultIfNotFound = default(float)) { if (data.ContainsKey(key)) { if (string.IsNullOrEmpty(data[key])) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } float test = 0; if (!float.TryParse(data[key], out test)) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } return GetFloat(key); } SetData(key, defaultIfNotFound); return defaultIfNotFound; } /// /// Gets a float or a default float value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public float GetFloatOrDefault(Enum key, float defaultIfNotFound = default(float)) { return GetFloatOrDefault(Util.EnumValueToString(key), defaultIfNotFound); } /// /// Gets an int from the data. /// /// The key. /// An int from the specified key. public int GetInt(string key) { return int.Parse(data[key]); } /// /// Gets an int from the data. /// /// The key. /// An int from the specified key. public int GetInt(Enum key) { return GetInt(Util.EnumValueToString(key)); } /// /// Gets an int or a default int value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public int GetIntOrDefault(string key, int defaultIfNotFound = default(int)) { if (data.ContainsKey(key)) { if (string.IsNullOrEmpty(data[key])) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } int test = 0; if (!int.TryParse(data[key], out test)) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } return GetInt(key); } SetData(key, defaultIfNotFound); return defaultIfNotFound; } /// /// Gets an int or a default int value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public int GetIntOrDefault(Enum key, int defaultIfNotFound = default(int)) { return GetIntOrDefault(Util.EnumValueToString(key), defaultIfNotFound); } /// /// Gets a string from the data. /// /// The key. /// An int from the specified key. public string GetString(string key) { return this[key]; } /// /// Gets a string from the data. /// /// The key. /// An int from the specified key. public string GetString(Enum key) { return this[key]; } /// /// Gets a string or a default string value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public string GetStringOrDefault(string key, string defaultIfNotFound = default(string)) { if (data.ContainsKey(key)) { if (string.IsNullOrEmpty(data[key])) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } return GetString(key); } SetData(key, defaultIfNotFound); return defaultIfNotFound; } /// /// Gets a string or a default string value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public string GetStringOrDefault(Enum key, string defaultIfNotFound = default(string)) { return GetStringOrDefault(Util.EnumValueToString(key), defaultIfNotFound); } /// /// Gets a bool from the data. /// /// The key. /// An int from the specified key. public bool GetBool(string key) { return bool.Parse(data[key]); } /// /// Gets a bool from the data. /// /// The key. /// An int from the specified key. public bool GetBool(Enum key) { return GetBool(Util.EnumValueToString(key)); } /// /// Gets a bool or a default bool value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public bool GetBoolOrDefault(string key, bool defaultIfNotFound = default(bool)) { if (data.ContainsKey(key)) { if (string.IsNullOrEmpty(data[key])) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } bool test = false; if (!bool.TryParse(data[key], out test)) { SetData(key, defaultIfNotFound); return defaultIfNotFound; } return GetBool(key); } SetData(key, defaultIfNotFound); return defaultIfNotFound; } /// /// Gets a bool or a default bool value. /// /// The key. /// The default if not found. /// The value or the default if the key is not found. public bool GetBoolOrDefault(Enum key, bool defaultIfNotFound = default(bool)) { return GetBoolOrDefault(Util.EnumValueToString(key), defaultIfNotFound); } /// /// Sets the data. /// /// The key. /// The object. public void SetData(string key, object obj) { if (data.ContainsKey(key)) { data[key] = obj.ToString(); } else { data.Add(key, obj.ToString()); } } /// /// Sets the data. /// /// The key. /// The object. public void SetData(Enum key, object obj) { SetData(Util.EnumValueToString(key), obj); } } }