C#手写UserPref存档

C#手写UserPref存档

这是我的第一篇博客,代码可能不够高效简洁,希望大家多多指点!

用过unity的肯定都知道PlayerPrefs类:

PlayerPrefs.GetInt("key_i");
PlayerPrefs.GetFloat("key_f");
PlayerPrefs.GetString("key_s");
PlayerPrefs.SetInt("key_i",0);
PlayerPrefs.SetFloat("key_f",0f);
PlayerPrefs.SetString("key_s","hello world!");

这是unity里自带的存档系统,通过修改注册表数值存档,相当简洁而好用。
诶?这么好用,我们为什么不把它从unity里搬出来,给其他C#程序用呢?(别告诉我从Dnspy里扒拉)

但是PlayerPrefs有个问题。

那就是它使用的是注册表。
而众所周知,有个东西叫Regedit。(win+r,然后输入“Regedit”回车)它可以编辑注册表,也就是说如果你用PlayerPrefs不加密保存数据,只要用户懂点技术,就可以修改存档。

所以,

1.我们为什么不用文件存档?
2.为什么不自带加密系统呢?

于是,

我做了一个类似PlayerPref的类-UserPref!(又不一定是做游戏用)

原理:

1. 存档地址:C:\Users\Administrator\AppData\Roaming\程序名\prefs.dat
2. 加密算法:DES
3. 存储格式:

(解密后的prefs.dat)
/数值名/
/数值类型,默认是string/
/以string保存的数据/
/数值名/
/数值类型,默认是string/
/以string保存的数据/
/数值名/
/数值类型,默认是string/
/以string保存的数据/
……

瞎BB 说 了,直接上代码。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace UserPreference
{
	public class UserPrefs
	{
		/// 
		/// 应用程序的名称
		/// 
		public static string AppName;

		/// 
		/// 存储加密密码,必须是八位。
		/// 
		public static string DataKey;

		/// 
		/// "C:/"
		/// 
		private static string SystemDiskCode
		{
			get
			{
				return Environment.GetEnvironmentVariable("systemdrive") + '\\';
			}
		}

		/// 
		/// @"C:\Users\Administrator\AppData\Roaming\"+程序名
		/// 
		private static string DataFolderPath
		{
			get
			{
				return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + AppName;
			}
		}

		/// 
		/// 清除文件里的数据
		/// 
		private static void ClearDataFileText()
		{
			File.WriteAllText(DataPath, "");
		}

		public static string DataPath
		{
			get
			{
				return DataFolderPath + @"\prefs.dat";
			}
		}

		/// 
		/// 逐行读取文件,并解密数据
		/// 
		/// 
		private static string[] ReadFileDataInLines()
		{
			try
			{
				if (File.Exists(DataPath))
				{
					string[] num = File.ReadAllLines(DataPath);
					for (int i = 0; i < num.Length; i++)
					{
						num[i] = SystemSafety.StringDecryptByDES(num[i], DataKey);

					}
					return num;
				}
				else
				{
					Directory.CreateDirectory(DataFolderPath);
					File.WriteAllText(DataPath, "");
					return new string[0];
				}
			}
			catch (Exception)
			{
				Directory.CreateDirectory(DataFolderPath);
				File.WriteAllText(DataPath, "");
				return new string[0];
			}
		}

		/// 
		/// 读取数据。
		/// 
		/// 读取到的数据。
		private static Data[] ReadData()
		{
			string[] list = ReadFileDataInLines();
			if (list.Length % 3 != 0)
			{
				ClearDataFileText();
				return new Data[0];
			}
			else
			{
				Data[] data = new Data[list.Length / 3];
				for (int i = 0; i < list.Length; i += 3)
				{
					data[i / 3].DataName = list[i];
					switch (list[i + 1])
					{
						case "int":
							data[i / 3].dataType = Data.DataType.Integer;
							break;
						case "str":
							data[i / 3].dataType = Data.DataType.String;
							break;
						case "flt":
							data[i / 3].dataType = Data.DataType.Float;
							break;
						default:
							data[i / 3].dataType = Data.DataType.String;
							break;
					}
					data[i / 3].DataValue = list[i + 2];
				}
				return data;
			}
		}

		/// 
		/// 将数据加密并保存。
		/// 
		/// 数据
		private static void SaveData(Data[] datas)
		{
			string[] texts = new string[datas.Length * 3];
			for (int i = 0; i < datas.Length; i++)
			{
				texts[i * 3] = datas[i].DataName;
				switch (datas[i].dataType)
				{
					case Data.DataType.Integer:
						texts[i * 3 + 1] = "int";
						break;
					case Data.DataType.String:
						texts[i * 3 + 1] = "str";
						break;
					case Data.DataType.Float:
						texts[i * 3 + 1] = "flt";
						break;
				}
				texts[i * 3 + 2] = datas[i].DataValue;
			}
			for (int i = 0; i < texts.Length; i++)
			{
				texts[i] = SystemSafety.StringEncryptByDES(texts[i], DataKey);
			}
			File.WriteAllLines(DataPath, texts);
		}

		/// 
		/// 读取指定名字的整数数值
		/// 
		/// 整数的名字
		/// 默认返回值,当该数值不存在或读取出现问题时返回。
		/// 获取到的整数
		public static int GetInt(string key, int defaultValue = 0)
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == Data.DataType.Integer)
				{
					int o;
					try
					{
						o = int.Parse(r.DataValue);
					}
					catch (FormatException)
					{
						SetInt(key, defaultValue);
						return defaultValue;
					}
					return o;
				}
			}
			return defaultValue;
		}

		/// 
		/// 读取指定名字的浮点数数值
		/// 
		/// 浮点数的名字
		/// 默认返回值,当该数值不存在或读取出现问题时返回。
		/// 获取到的浮点数
		public static float GetFloat(string key, float defaultValue = 0)
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == Data.DataType.Float)
				{
					float o;
					try
					{
						o = float.Parse(r.DataValue);
					}
					catch (FormatException)
					{
						SetFloat(key, defaultValue);
						return defaultValue;
					}
					return o;
				}
			}
			return defaultValue;
		}

		/// 
		/// 读取指定名字的字符串数值
		/// 
		/// 字符串的名字
		/// 默认返回值,当该数值不存在或读取出现问题时返回。
		/// 获取到的字符串
		public static string GetString(string key, string defaultValue = "")
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == Data.DataType.Integer)
				{
					return r.DataValue;
				}
			}
			return defaultValue;
		}

		/// 
		/// 写入一个整数,如果该数值已存在,则修改该数值。
		/// 
		/// 数值的名字
		/// 需要存入的整数
		public static void SetInt(string key, int value)
		{
			Data[] datas = ReadData();
			int x = GetKeyIndex(key, Data.DataType.Integer, ReadData());
			if (x == -1)
			{
				Array.Resize<Data>(ref datas, datas.Length + 1);
				datas[datas.Length - 1].DataName = key;
				datas[datas.Length - 1].DataValue = value.ToString();
				datas[datas.Length - 1].dataType = Data.DataType.Integer;
			}
			else
			{
				datas[x].DataName = key;
				datas[x].DataValue = value.ToString();
				datas[x].dataType = Data.DataType.Integer;
			}
			SaveData(datas);
		}

		/// 
		/// 将数据中的所有数值都删除。
		/// 
		public static void DeleteAllKeys()
		{
			ClearDataFileText();
		}

		/// 
		/// 指定一个数值,如果该数值存在,就将其删除。
		/// 
		/// 
		/// 
		public static void DeleteKey(string key, Data.DataType type)
		{
			Data[] d = ReadData();
			int x = GetKeyIndex(key, type, d);
			if (x != -1)
			{
				List<Data> l = d.ToList();
				l.RemoveAt(x);
				SaveData(l.ToArray());
			}
		}

		/// 
		/// 写入一个浮点数,如果该数值已存在,则修改该数值。
		/// 
		/// 数值的名字
		/// 需要存入的浮点数
		public static void SetFloat(string key, float value)
		{
			Data[] datas = ReadData();
			int x = GetKeyIndex(key, Data.DataType.Float, ReadData());
			if (x == -1)
			{
				Array.Resize<Data>(ref datas, datas.Length + 1);
				datas[datas.Length - 1].DataName = key;
				datas[datas.Length - 1].DataValue = value.ToString();
				datas[datas.Length - 1].dataType = Data.DataType.Float;
			}
			else
			{
				datas[x].DataName = key;
				datas[x].DataValue = value.ToString();
				datas[x].dataType = Data.DataType.Float;
			}
			SaveData(datas);
		}

		/// 
		/// 写入一个字符串值,如果该数值已存在,则修改该数值。
		/// 
		/// 数值的名字
		/// 需要存入的字符串
		public static void SetString(string key, string value)
		{
			Data[] datas = ReadData();
			int x = GetKeyIndex(key, Data.DataType.String, ReadData());
			if (x == -1)
			{
				Array.Resize<Data>(ref datas, datas.Length + 1);
				datas[datas.Length - 1].DataName = key;
				datas[datas.Length - 1].DataValue = value;
				datas[datas.Length - 1].dataType = Data.DataType.String;
			}
			else
			{
				datas[x].DataName = key;
				datas[x].DataValue = value;
				datas[x].dataType = Data.DataType.String;
			}
			SaveData(datas);
		}

		/// 
		/// 检查指定数值是否存在。
		/// 
		/// 指定数值名字
		/// 指定数值类型
		/// 是否存在。
		public static bool HasKey(string key, Data.DataType type)
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == type)
				{
					return true;
				}
			}
			return false;
		}

		/// 
		/// 获取指定数据在数组里的索引,如果不存在则输出-1。
		/// 
		/// 指定数值名字
		/// 指定数值类型
		/// 数据的数组
		/// 指定数据在数组里的索引,如果不存在则为-1
		private static int GetKeyIndex(string key, Data.DataType type, Data[] datas)
		{
			for (int i = 0; i < datas.Length; i++)
			{
				if (datas[i].DataName == key && datas[i].dataType == type)
				{
					return i;
				}
			}
			return -1;
		}
	}

	/// 
	/// 一个数据值的结构体。
	/// 
	public struct Data
	{
		public enum DataType
		{
			Integer,
			Float,
			String
		}
		public DataType dataType;//类型
		public string DataName;//数据的名字
		public string DataValue;//数据的值(string)
	}

	/// 
	/// 这个用来加解密的类是我在破解滚动的天空时从文件里翻出来的,改了改。(无耻)(滑稽)
	/// 
	public class SystemSafety
	{
		public static string StringEncryptByDES(string encryptInfo, string key, string iv = "12345678")
		{
			string result;
			try
			{
				byte[] bytes = Encoding.UTF8.GetBytes(key);
				byte[] bytes2 = Encoding.UTF8.GetBytes(iv);
				DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider();
				using (MemoryStream memoryStream = new MemoryStream())
				{
					byte[] bytes3 = Encoding.UTF8.GetBytes(encryptInfo);
					try
					{
						using (CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateEncryptor(bytes, bytes2), CryptoStreamMode.Write))
						{
							cryptoStream.Write(bytes3, 0, bytes3.Length);
							cryptoStream.FlushFinalBlock();
						}
						result = Convert.ToBase64String(memoryStream.ToArray());
					}
					catch
					{
						result = encryptInfo;
					}
				}
			}
			catch
			{
				result = "DES加密出错";
			}
			return result;
		}

		public static string StringDecryptByDES(string encryptedString, string key, string iv = "12345678")
		{
			byte[] bytes = Encoding.UTF8.GetBytes(key);
			byte[] bytes2 = Encoding.UTF8.GetBytes(iv);
			DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider();
			string result;
			using (MemoryStream memoryStream = new MemoryStream())
			{
				byte[] array = Convert.FromBase64String(encryptedString);
				try
				{
					using (CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateDecryptor(bytes, bytes2), CryptoStreamMode.Write))
					{
						cryptoStream.Write(array, 0, array.Length);
						cryptoStream.FlushFinalBlock();
					}
					result = Encoding.UTF8.GetString(memoryStream.ToArray());
				}
				catch
				{
					result = encryptedString;
				}
			}
			return result;
		}
	}
}

使用方法(非常建议你认真看一看)

1. UserPrefs.AppName 必须设置!写上你的程序名就行,不写会报错。
2. UserPrefs.DataKey 必须设置!这是数据加解密的密码,且必须有8个字符。
3. 可能还有Bug,不过经测试目前没有问题,我也会以后再更新。

欢迎指出不足,谢谢各位大佬们!

你可能感兴趣的:(C#,c#,windows)