这是我的第一篇博客,代码可能不够高效简洁,希望大家多多指点!
用过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里扒拉)
那就是它使用的是注册表。
而众所周知,有个东西叫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保存的数据/
……
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,不过经测试目前没有问题,我也会以后再更新。