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);
}
}
}