You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

459 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace Otter {
/// <summary>
/// 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.
/// </summary>
public class DataSaver {
/// <summary>
/// The string to use when delimiting key data in data exports.
/// </summary>
public string KeyDelim = "::OTTERK::";
/// <summary>
/// The string to use when delimiting value data in data exports.
/// </summary>
public string ValueDelim = "::OTTERV::";
/// <summary>
/// The phrase to use as a salt when encrypting the data exports.
/// </summary>
public string EncryptionSalt = "otter";
/// <summary>
/// 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.
/// </summary>
public string SaltGuide = "{S}{D}{S}";
/// <summary>
/// The default path that the files will be imported from and exported to.
/// </summary>
public string DefaultPath = "";
/// <summary>
/// The default file name that the data will export as.
/// </summary>
public string DefaultFilename = "data";
/// <summary>
/// The export mode for the data.
/// Data: Semi-encrypted uneditable data.
/// Config: Easy to hand edit unencrypted data.
/// </summary>
public DataExportMode ExportMode = DataExportMode.Data;
/// <summary>
/// The export modes for the data.
/// </summary>
public enum DataExportMode {
Data,
Config
}
Dictionary<string, string> data = new Dictionary<string, string>();
/// <summary>
/// Initializes a new instance of the DataSaver class.
/// </summary>
/// <param name="defaultPath">The default path.</param>
public DataSaver(string defaultPath = "") {
DefaultPath = defaultPath;
}
/// <summary>
/// Verifies the specified string data. Only applies to DataExportMode.Data.
/// </summary>
/// <param name="stringData">The string data.</param>
/// <returns>True if the data is successfully verified.</returns>
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;
}
/// <summary>
/// Deletes the exported file for this save data.
/// </summary>
/// <param name="filename">The filename to delete (usually you don't have to set this.)</param>
public void ClearFile(string filename = "") {
if (filename == "") filename = DefaultFilename;
filename = DefaultPath + filename;
if (!File.Exists(filename)) return;
File.Delete(filename);
}
/// <summary>
/// Clears the data.
/// </summary>
public void Clear() {
data = new Dictionary<string, string>();
}
/// <summary>
/// Check if an exported file exists for this data.
/// </summary>
/// <param name="filename">The filename to check.</param>
/// <returns>True if the exported file exists, and is verified for encrypted files.</returns>
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;
}
/// <summary>
/// Imports the data in the specified file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="verify">if set to true verify the data before importing.</param>
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, '='));
}
}
}
}
/// <summary>
/// Exports the data to the specified file.
/// </summary>
/// <param name="filename">The filename.</param>
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);
}
}
/// <summary>
/// Gets the string with the specified key.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The string data or null.</returns>
public string this[string key] {
get {
if (data.ContainsKey(key)) {
return data[key];
}
return null;
}
}
/// <summary>
/// Gets the string with the specified key.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The string data or null.</returns>
public string this[Enum key] {
get {
return this[Util.EnumValueToString(key)];
}
}
/// <summary>
/// Gets a float from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>A float from the specified key.</returns>
public float GetFloat(string key) {
return float.Parse(data[key]);
}
/// <summary>
/// Gets a float from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>A float from the specified key.</returns>
public float GetFloat(Enum key) {
return GetFloat(Util.EnumValueToString(key));
}
/// <summary>
/// Gets a float or a default float value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if a value is not found.</returns>
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;
}
/// <summary>
/// Gets a float or a default float value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
public float GetFloatOrDefault(Enum key, float defaultIfNotFound = default(float)) {
return GetFloatOrDefault(Util.EnumValueToString(key), defaultIfNotFound);
}
/// <summary>
/// Gets an int from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>An int from the specified key.</returns>
public int GetInt(string key) {
return int.Parse(data[key]);
}
/// <summary>
/// Gets an int from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>An int from the specified key.</returns>
public int GetInt(Enum key) {
return GetInt(Util.EnumValueToString(key));
}
/// <summary>
/// Gets an int or a default int value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
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;
}
/// <summary>
/// Gets an int or a default int value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
public int GetIntOrDefault(Enum key, int defaultIfNotFound = default(int)) {
return GetIntOrDefault(Util.EnumValueToString(key), defaultIfNotFound);
}
/// <summary>
/// Gets a string from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>An int from the specified key.</returns>
public string GetString(string key) {
return this[key];
}
/// <summary>
/// Gets a string from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>An int from the specified key.</returns>
public string GetString(Enum key) {
return this[key];
}
/// <summary>
/// Gets a string or a default string value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
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;
}
/// <summary>
/// Gets a string or a default string value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
public string GetStringOrDefault(Enum key, string defaultIfNotFound = default(string)) {
return GetStringOrDefault(Util.EnumValueToString(key), defaultIfNotFound);
}
/// <summary>
/// Gets a bool from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>An int from the specified key.</returns>
public bool GetBool(string key) {
return bool.Parse(data[key]);
}
/// <summary>
/// Gets a bool from the data.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>An int from the specified key.</returns>
public bool GetBool(Enum key) {
return GetBool(Util.EnumValueToString(key));
}
/// <summary>
/// Gets a bool or a default bool value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
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;
}
/// <summary>
/// Gets a bool or a default bool value.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="defaultIfNotFound">The default if not found.</param>
/// <returns>The value or the default if the key is not found.</returns>
public bool GetBoolOrDefault(Enum key, bool defaultIfNotFound = default(bool)) {
return GetBoolOrDefault(Util.EnumValueToString(key), defaultIfNotFound);
}
/// <summary>
/// Sets the data.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="obj">The object.</param>
public void SetData(string key, object obj) {
if (data.ContainsKey(key)) {
data[key] = obj.ToString();
}
else {
data.Add(key, obj.ToString());
}
}
/// <summary>
/// Sets the data.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="obj">The object.</param>
public void SetData(Enum key, object obj) {
SetData(Util.EnumValueToString(key), obj);
}
}
}