创建一个PlayerPrefsKey查看器

  • 效果图
  • 前言
  • 开发
    • 一、获取数据
      • 目录结构
    • 二、开发编辑器窗口
      • 效果图
      • 思路
        • 一、绘制搜索栏
        • 二、定义PlayerPrefs数据结构体
        • 三、根据搜索框过滤数据
        • 四、绘制主框体
        • 五、完整代码

效果图

前言

PlayerPrefsKey现在其实用得并不多,不过也有一些应用场景。

最近开发过程中,发现要删除一个PlayerPrefsKey,有点麻烦。可以写代码删除 或者 去注册表删除。都是都感觉有点麻烦,而且也没法知道当前已经设置了哪些PlayerPrefsKey。 所以就想做一个窗口来查看当前所有key,并可以支持搜索和删除。

tip:修改功能就不添加了,因为一般删除PlayerPrefsKey让其恢复默认值是安全OK的。但是如果直接不知道它的用处或值域,直接修改,就可能有问题。所以不增加修改功能。

开发

开发分两步:

  1. 第一步获取所有PlayerPrefsKey数据;
  2. 第二步根据数据写编辑器窗口;

一、获取数据

获取数据我是直接在网上搜了一下获取方法,找到了这篇博客,获取所有PlayerPrefsKey windows下测试可以拿到所有key. 获取数据可以直接看这个链接。这里我们主要讲一下编辑器窗口。

目录结构

看文章可以知道需要两个工具类: PlayerPrefsExtension.cs 和 Plist.cs.
目录结构如下,包含上面两个工具类,和窗口编辑器PlayerPrefsWindow。工具类代码会在最后提供

二、开发编辑器窗口

效果图

思路

看效果图,可以知道大体分为两部分:最上面的搜索栏 和 下面的主框体。
怎么实现搜索呢:

  1. 首先,我们可以通过工具类获取到所有的key
  2. 如果搜索框是空的,我们就把所有数据交给主框体渲染;
  3. 如果搜索框有数据,我们就把第一步拿到的数据过滤一下,再交给主框体渲染。这样就可以了。

一、绘制搜索栏

    private void DrawSearchBar(){//绘制label:GUIStyle labelStyle = new GUIStyle(EditorStyles.boldLabel);labelStyle.normal.textColor = Color.cyan;EditorGUILayout.LabelField("输入要搜索的key:",labelStyle,GUILayout.Width(120));//绘制输入框:var style = new GUIStyle(EditorStyles.textField);style.fontStyle = FontStyle.Bold;searchKey = GUILayout.TextField(searchKey,style,GUILayout.ExpandWidth(true));//绘制 清空输入 按钮if (GUILayout.Button("x",btnWidthOpt)){searchKey = string.Empty;}//绘制 删除所有Key 按钮if (GUILayout.Button("删除所有",btnWidthOpt)){if (EditorUtility.DisplayDialog("删除提示", "确认删除所有key吗?", "确定", "取消")){PlayerPrefs.DeleteAll();};}}

二、定义PlayerPrefs数据结构体

我们用这个结构体来描述每一个PlayerPrefs

[Serializable]
public struct PlayerPrefPair
{public string Key { get; set; }public object Value { get; set; }
}

三、根据搜索框过滤数据

searchKey就是搜索框输入的内容,如果有输入,我们就过滤数据

 private void FillDataList(ref List<PlayerPrefPair> list){list.Clear();list = PlayerPrefsExtension.GetAll().ToList();if (!string.IsNullOrEmpty(searchKey)){for (int i = 0; i < list.Count; i++){if (!list[i].Key.ToLower().Contains(searchKey.ToLower())){list.RemoveAt(i);i--;}}}}

四、绘制主框体

因为内容有可能超过窗口,所以这里用了ScrollView,当内容超出时,可以滑动窗口

    private void DrawMainWindow(List<PlayerPrefPair> dataList){Rect rect = new Rect(0,searchBarHeight + 10,position.width,position.height);GUILayout.BeginArea(rect);scrollViewPos = GUILayout.BeginScrollView(scrollViewPos,false,true,GUILayout.Height(1000));GUILayout.BeginVertical();foreach (PlayerPrefPair pair in dataList){if (!PlayerPrefs.HasKey(pair.Key)){continue;}GUILayout.BeginHorizontal();    EditorGUILayout.TextField(pair.Key+":",pair.Value.ToString());GUILayout.ExpandWidth(true);if (GUILayout.Button("删除Key",btnWidthOpt)){if (PlayerPrefs.HasKey(pair.Key)){PlayerPrefs.DeleteKey(pair.Key);Debug.Log($"{GetType()} delete key success,key:{pair.Key}");}}GUILayout.EndHorizontal();}GUILayout.EndVertical();GUILayout.EndScrollView();GUILayout.EndArea();}

五、完整代码

PlayerPrefsWindow.cs

using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;[Serializable]
public struct PlayerPrefPair
{public string Key { get; set; }public object Value { get; set; }
}public class PlayerPrefsWindow : EditorWindow
{private Vector2 scrollViewPos;private string searchKey = string.Empty;private List<PlayerPrefPair> dataList = new List<PlayerPrefPair>();private float searchBarHeight = 30;private GUILayoutOption btnWidthOpt = GUILayout.Width(125);[MenuItem("Tool/PlayerPrefsWindow %q")]public static void OpenWindow(){PlayerPrefsWindow window = UnityEditor.EditorWindow.GetWindow<PlayerPrefsWindow>();window.name = "PlayerPrefs窗口";}public void OnGUI(){GUILayout.BeginVertical();Rect rect = new Rect(0, 0, position.width, searchBarHeight);GUILayout.BeginArea(rect);{GUI.Box(rect,"");//搜索GUILayout.BeginHorizontal();DrawSearchBar();GUILayout.EndHorizontal();}GUILayout.EndArea();//数据FillDataList(ref dataList);//主框体DrawMainWindow(dataList);GUILayout.EndVertical();}private void DrawSearchBar(){//绘制label:GUIStyle labelStyle = new GUIStyle(EditorStyles.boldLabel);labelStyle.normal.textColor = Color.cyan;EditorGUILayout.LabelField("输入要搜索的key:",labelStyle,GUILayout.Width(120));//绘制输入框:var style = new GUIStyle(EditorStyles.textField);style.fontStyle = FontStyle.Bold;searchKey = GUILayout.TextField(searchKey,style,GUILayout.ExpandWidth(true));//绘制 清空输入 按钮if (GUILayout.Button("x",btnWidthOpt)){searchKey = string.Empty;}//绘制 删除所有Key 按钮if (GUILayout.Button("删除所有",btnWidthOpt)){if (EditorUtility.DisplayDialog("删除提示", "确认删除所有key吗?", "确定", "取消")){PlayerPrefs.DeleteAll();};}}private void FillDataList(ref List<PlayerPrefPair> list){list.Clear();list = PlayerPrefsExtension.GetAll().ToList();if (!string.IsNullOrEmpty(searchKey)){for (int i = 0; i < list.Count; i++){if (!list[i].Key.ToLower().Contains(searchKey.ToLower())){list.RemoveAt(i);i--;}}}}private void DrawMainWindow(List<PlayerPrefPair> dataList){Rect rect = new Rect(0,searchBarHeight + 10,position.width,position.height);GUILayout.BeginArea(rect);scrollViewPos = GUILayout.BeginScrollView(scrollViewPos,false,true,GUILayout.Height(1000));GUILayout.BeginVertical();foreach (PlayerPrefPair pair in dataList){if (!PlayerPrefs.HasKey(pair.Key)){continue;}GUILayout.BeginHorizontal();    EditorGUILayout.TextField(pair.Key+":",pair.Value.ToString());GUILayout.ExpandWidth(true);if (GUILayout.Button("删除Key",btnWidthOpt)){if (PlayerPrefs.HasKey(pair.Key)){PlayerPrefs.DeleteKey(pair.Key);Debug.Log($"{GetType()} delete key success,key:{pair.Key}");}}GUILayout.EndHorizontal();}GUILayout.EndVertical();GUILayout.EndScrollView();GUILayout.EndArea();}
}

PlayerPrefsExtension.cs

using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.IO;
using PlistCS;
using UnityEditor;
using UnityEngine;public static class PlayerPrefsExtension{public static PlayerPrefPair[] GetAll(){return GetAll(PlayerSettings.companyName, PlayerSettings.productName);}public static PlayerPrefPair[] GetAll(string companyName, string productName){if (Application.platform == RuntimePlatform.OSXEditor){// From Unity docs: On Mac OS X PlayerPrefs are stored in ~/Library/Preferences folder, in a file named unity.[company name].[product name].plist, where company and product names are the names set up in Project Settings. The same .plist file is used for both Projects run in the Editor and standalone players.// Construct the plist filename from the project's settingsstring plistFilename = string.Format("unity.{0}.{1}.plist", companyName, productName);// Now construct the fully qualified pathstring playerPrefsPath = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library/Preferences"),plistFilename);// Parse the player prefs file if it existsif (File.Exists(playerPrefsPath)){// Parse the plist then cast it to a Dictionaryobject plist = Plist.readPlist(playerPrefsPath);Dictionary<string, object> parsed = plist as Dictionary<string, object>;// Convert the dictionary data into an array of PlayerPrefPairsPlayerPrefPair[] tempPlayerPrefs = new PlayerPrefPair[parsed.Count];int i = 0;foreach (KeyValuePair<string, object> pair in parsed){if (pair.Value.GetType() == typeof(double)){// Some float values may come back as double, so convert them back to floatstempPlayerPrefs[i] = new PlayerPrefPair() {Key = pair.Key, Value = (float) (double) pair.Value};}else{tempPlayerPrefs[i] = new PlayerPrefPair() {Key = pair.Key, Value = pair.Value};}i++;}// Return the resultsreturn tempPlayerPrefs;}else{// No existing player prefs saved (which is valid), so just return an empty arrayreturn new PlayerPrefPair[0];}}else if (Application.platform == RuntimePlatform.WindowsEditor){// From Unity docs: On Windows, PlayerPrefs are stored in the registry under HKCU\Software\[company name]\[product name] key, where company and product names are the names set up in Project Settings.
#if UNITY_5_5_OR_NEWER// From Unity 5.5 editor player prefs moved to a specific locationMicrosoft.Win32.RegistryKey registryKey =Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Unity\\UnityEditor\\" + companyName + "\\" + productName);
#elseMicrosoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\" + companyName + "\\" + productName);
#endif// Parse the registry if the specified registryKey existsif (registryKey != null){// Get an array of what keys (registry value names) are storedstring[] valueNames = registryKey.GetValueNames();// Create the array of the right size to take the saved player prefsPlayerPrefPair[] tempPlayerPrefs = new PlayerPrefPair[valueNames.Length];// Parse and convert the registry saved player prefs into our arrayint i = 0;foreach (string valueName in valueNames){string key = valueName;// Remove the _h193410979 style suffix used on player pref keys in Windows registryint index = key.LastIndexOf("_");key = key.Remove(index, key.Length - index);// Get the value from the registryobject ambiguousValue = registryKey.GetValue(valueName);// Unfortunately floats will come back as an int (at least on 64 bit) because the float is stored as// 64 bit but marked as 32 bit - which confuses the GetValue() method greatly! if (ambiguousValue.GetType() == typeof(int)){// If the player pref is not actually an int then it must be a float, this will evaluate to true// (impossible for it to be 0 and -1 at the same time)if (PlayerPrefs.GetInt(key, -1) == -1 && PlayerPrefs.GetInt(key, 0) == 0){// Fetch the float value from PlayerPrefs in memoryambiguousValue = PlayerPrefs.GetFloat(key);}}else if (ambiguousValue.GetType() == typeof(byte[])){// On Unity 5 a string may be stored as binary, so convert it back to a stringambiguousValue = System.Text.Encoding.Default.GetString((byte[]) ambiguousValue);}// Assign the key and value into the respective record in our output arraytempPlayerPrefs[i] = new PlayerPrefPair() {Key = key, Value = ambiguousValue};i++;}// Return the resultsreturn tempPlayerPrefs;}else{// No existing player prefs saved (which is valid), so just return an empty arrayreturn new PlayerPrefPair[0];}}else{throw new NotSupportedException("PlayerPrefsEditor doesn't support this Unity Editor platform");}}}

Plist.cs

//
//   PlistCS Property List (plist) serialization and parsing library.
//
//   https://github.com/animetrics/PlistCS
//
//   Copyright (c) 2011 Animetrics Inc. (marc@animetrics.com)
//
//   Permission is hereby granted, free of charge, to any person obtaining a copy
//   of this software and associated documentation files (the "Software"), to deal
//   in the Software without restriction, including without limitation the rights
//   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//   copies of the Software, and to permit persons to whom the Software is
//   furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//   THE SOFTWARE.using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;namespace PlistCS
{public static class Plist{private static List<int> offsetTable = new List<int>();private static List<byte> objectTable = new List<byte>();private static int refCount;private static int objRefSize;private static int offsetByteSize;private static long offsetTableOffset;#region Public Functionspublic static object readPlist(string path){using (FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read)){return readPlist(f, plistType.Auto);}}public static object readPlistSource(string source){return readPlist(System.Text.Encoding.UTF8.GetBytes(source));}public static object readPlist(byte[] data){return readPlist(new MemoryStream(data), plistType.Auto);}public static plistType getPlistType(Stream stream){byte[] magicHeader = new byte[8];stream.Read(magicHeader, 0, 8);if (BitConverter.ToInt64(magicHeader, 0) == 3472403351741427810){return plistType.Binary;}else{return plistType.Xml;}}public static object readPlist(Stream stream, plistType type){if (type == plistType.Auto){type = getPlistType(stream);stream.Seek(0, SeekOrigin.Begin);}if (type == plistType.Binary){using (BinaryReader reader = new BinaryReader(stream)){byte[] data = reader.ReadBytes((int) reader.BaseStream.Length);return readBinary(data);}}else{XmlDocument xml = new XmlDocument();xml.XmlResolver = null;xml.Load(stream);return readXml(xml);}}public static void writeXml(object value, string path){using (StreamWriter writer = new StreamWriter(path)){writer.Write(writeXml(value));}}public static void writeXml(object value, Stream stream){using (StreamWriter writer = new StreamWriter(stream)){writer.Write(writeXml(value));}}public static string writeXml(object value){using (MemoryStream ms = new MemoryStream()){XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();xmlWriterSettings.Encoding = new System.Text.UTF8Encoding(false);xmlWriterSettings.ConformanceLevel = ConformanceLevel.Document;xmlWriterSettings.Indent = true;using (XmlWriter xmlWriter = XmlWriter.Create(ms, xmlWriterSettings)){xmlWriter.WriteStartDocument(); //xmlWriter.WriteComment("DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");//xmlWriter.WriteDocType("plist", "-//Apple Computer//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd", null);xmlWriter.WriteStartElement("plist");xmlWriter.WriteAttributeString("version", "1.0");compose(value, xmlWriter);xmlWriter.WriteEndElement();xmlWriter.WriteEndDocument();xmlWriter.Flush();xmlWriter.Close();return System.Text.Encoding.UTF8.GetString(ms.ToArray());}}}public static void writeBinary(object value, string path){using (BinaryWriter writer = new BinaryWriter(new FileStream(path, FileMode.Create))){writer.Write(writeBinary(value));}}public static void writeBinary(object value, Stream stream){using (BinaryWriter writer = new BinaryWriter(stream)){writer.Write(writeBinary(value));}}public static byte[] writeBinary(object value){offsetTable.Clear();objectTable.Clear();refCount = 0;objRefSize = 0;offsetByteSize = 0;offsetTableOffset = 0;//Do not count the root node, subtract by 1int totalRefs = countObject(value) - 1;refCount = totalRefs;objRefSize = RegulateNullBytes(BitConverter.GetBytes(refCount)).Length;composeBinary(value);writeBinaryString("bplist00", false);offsetTableOffset = (long)objectTable.Count;offsetTable.Add(objectTable.Count - 8);offsetByteSize = RegulateNullBytes(BitConverter.GetBytes(offsetTable[offsetTable.Count-1])).Length;List<byte> offsetBytes = new List<byte>();offsetTable.Reverse();for (int i = 0; i < offsetTable.Count; i++){offsetTable[i] = objectTable.Count - offsetTable[i];byte[] buffer = RegulateNullBytes(BitConverter.GetBytes(offsetTable[i]), offsetByteSize);Array.Reverse(buffer);offsetBytes.AddRange(buffer);}objectTable.AddRange(offsetBytes);objectTable.AddRange(new byte[6]);objectTable.Add(Convert.ToByte(offsetByteSize));objectTable.Add(Convert.ToByte(objRefSize));var a = BitConverter.GetBytes((long) totalRefs + 1);Array.Reverse(a);objectTable.AddRange(a);objectTable.AddRange(BitConverter.GetBytes((long)0));a = BitConverter.GetBytes(offsetTableOffset);Array.Reverse(a);objectTable.AddRange(a);return objectTable.ToArray();}#endregion#region Private Functionsprivate static object readXml(XmlDocument xml){XmlNode rootNode = xml.DocumentElement.ChildNodes[0];return parse(rootNode);}private static object readBinary(byte[] data){offsetTable.Clear();List<byte> offsetTableBytes = new List<byte>();objectTable.Clear();refCount = 0;objRefSize = 0;offsetByteSize = 0;offsetTableOffset = 0;List<byte> bList = new List<byte>(data);List<byte> trailer = bList.GetRange(bList.Count - 32, 32);parseTrailer(trailer);objectTable = bList.GetRange(0, (int)offsetTableOffset);offsetTableBytes = bList.GetRange((int)offsetTableOffset, bList.Count - (int)offsetTableOffset - 32);parseOffsetTable(offsetTableBytes);return parseBinary(0);}private static Dictionary<string, object> parseDictionary(XmlNode node){XmlNodeList children = node.ChildNodes;if (children.Count % 2 != 0){throw new DataMisalignedException("Dictionary elements must have an even number of child nodes");}Dictionary<string, object> dict = new Dictionary<string, object>();for (int i = 0; i < children.Count; i += 2){XmlNode keynode = children[i];XmlNode valnode = children[i + 1];if (keynode.Name != "key"){throw new ApplicationException("expected a key node");}object result = parse(valnode);if (result != null){dict.Add(keynode.InnerText, result);}}return dict;}private static List<object> parseArray(XmlNode node){List<object> array = new List<object>();foreach (XmlNode child in node.ChildNodes){object result = parse(child);if (result != null){array.Add(result);}}return array;}private static void composeArray(List<object> value, XmlWriter writer){writer.WriteStartElement("array");foreach (object obj in value){compose(obj, writer);}writer.WriteEndElement();}private static object parse(XmlNode node){switch (node.Name){case "dict":return parseDictionary(node);case "array":return parseArray(node);case "string":return node.InnerText;case "integer"://  int result;//int.TryParse(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo, out result);return Convert.ToInt32(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo);case "real":return Convert.ToDouble(node.InnerText,System.Globalization.NumberFormatInfo.InvariantInfo);case "false":return false;case "true":return true;case "null":return null;case "date":return XmlConvert.ToDateTime(node.InnerText, XmlDateTimeSerializationMode.Utc);case "data":return Convert.FromBase64String(node.InnerText);}throw new ApplicationException(String.Format("Plist Node `{0}' is not supported", node.Name));}private static void compose(object value, XmlWriter writer){if (value == null || value is string){writer.WriteElementString("string", value as string);}else if (value is int || value is long){writer.WriteElementString("integer", ((int)value).ToString(System.Globalization.NumberFormatInfo.InvariantInfo));}else if (value is System.Collections.Generic.Dictionary<string, object> ||value.GetType().ToString().StartsWith("System.Collections.Generic.Dictionary`2[System.String")){//Convert to Dictionary<string, object>Dictionary<string, object> dic = value as Dictionary<string, object>;if (dic == null){dic = new Dictionary<string, object>();IDictionary idic = (IDictionary)value;foreach (var key in idic.Keys){dic.Add(key.ToString(), idic[key]);}}writeDictionaryValues(dic, writer);}else if (value is List<object>){composeArray((List<object>)value, writer);}else if (value is byte[]){writer.WriteElementString("data", Convert.ToBase64String((Byte[])value));}else if (value is float || value is double){writer.WriteElementString("real", ((double)value).ToString(System.Globalization.NumberFormatInfo.InvariantInfo));}else if (value is DateTime){DateTime time = (DateTime)value;string theString = XmlConvert.ToString(time, XmlDateTimeSerializationMode.Utc);writer.WriteElementString("date", theString);//, "yyyy-MM-ddTHH:mm:ssZ"));}else if (value is bool){writer.WriteElementString(value.ToString().ToLower(), "");}else{throw new Exception(String.Format("Value type '{0}' is unhandled", value.GetType().ToString()));}}private static void writeDictionaryValues(Dictionary<string, object> dictionary, XmlWriter writer){writer.WriteStartElement("dict");foreach (string key in dictionary.Keys){object value = dictionary[key];writer.WriteElementString("key", key);compose(value, writer);}writer.WriteEndElement();}private static int countObject(object value){int count = 0;switch (value.GetType().ToString()){case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":Dictionary<string, object> dict = (Dictionary<string, object>)value;foreach (string key in dict.Keys){count += countObject(dict[key]);}count += dict.Keys.Count;count++;break;case "System.Collections.Generic.List`1[System.Object]":List<object> list = (List<object>)value;foreach (object obj in list){count += countObject(obj);}count++;break;default:count++;break;}return count;}private static byte[] writeBinaryDictionary(Dictionary<string, object> dictionary){List<byte> buffer = new List<byte>();List<byte> header = new List<byte>();List<int> refs = new List<int>();for (int i = dictionary.Count - 1; i >= 0; i--){var o = new object[dictionary.Count];dictionary.Values.CopyTo(o, 0);composeBinary(o[i]);offsetTable.Add(objectTable.Count);refs.Add(refCount);refCount--;}for (int i = dictionary.Count - 1; i >= 0; i--){var o = new string[dictionary.Count];dictionary.Keys.CopyTo(o, 0);composeBinary(o[i]);//);offsetTable.Add(objectTable.Count);refs.Add(refCount);refCount--;}if (dictionary.Count < 15){header.Add(Convert.ToByte(0xD0 | Convert.ToByte(dictionary.Count)));}else{header.Add(0xD0 | 0xf);header.AddRange(writeBinaryInteger(dictionary.Count, false));}foreach (int val in refs){byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);Array.Reverse(refBuffer);buffer.InsertRange(0, refBuffer);}buffer.InsertRange(0, header);objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] composeBinaryArray(List<object> objects){List<byte> buffer = new List<byte>();List<byte> header = new List<byte>();List<int> refs = new List<int>();for (int i = objects.Count - 1; i >= 0; i--){composeBinary(objects[i]);offsetTable.Add(objectTable.Count);refs.Add(refCount);refCount--;}if (objects.Count < 15){header.Add(Convert.ToByte(0xA0 | Convert.ToByte(objects.Count)));}else{header.Add(0xA0 | 0xf);header.AddRange(writeBinaryInteger(objects.Count, false));}foreach (int val in refs){byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);Array.Reverse(refBuffer);buffer.InsertRange(0, refBuffer);}buffer.InsertRange(0, header);objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] composeBinary(object obj){byte[] value;switch (obj.GetType().ToString()){case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":value = writeBinaryDictionary((Dictionary<string, object>)obj);return value;case "System.Collections.Generic.List`1[System.Object]":value = composeBinaryArray((List<object>)obj);return value;case "System.Byte[]":value = writeBinaryByteArray((byte[])obj);return value;case "System.Double":value = writeBinaryDouble((double)obj);return value;case "System.Int32":value = writeBinaryInteger((int)obj, true);return value;case "System.String":value = writeBinaryString((string)obj, true);return value;case "System.DateTime":value = writeBinaryDate((DateTime)obj);return value;case "System.Boolean":value = writeBinaryBool((bool)obj);return value;default:return new byte[0];}}public static byte[] writeBinaryDate(DateTime obj){List<byte> buffer =new List<byte>(RegulateNullBytes(BitConverter.GetBytes(PlistDateConverter.ConvertToAppleTimeStamp(obj)), 8));buffer.Reverse();buffer.Insert(0, 0x33);objectTable.InsertRange(0, buffer);return buffer.ToArray();}public static byte[] writeBinaryBool(bool obj){List<byte> buffer = new List<byte>(new byte[1] { (bool)obj ? (byte)9 : (byte)8 });objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] writeBinaryInteger(int value, bool write){List<byte> buffer = new List<byte>(BitConverter.GetBytes((long) value));buffer =new List<byte>(RegulateNullBytes(buffer.ToArray()));while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))buffer.Add(0);int header = 0x10 | (int)(Math.Log(buffer.Count) / Math.Log(2));buffer.Reverse();buffer.Insert(0, Convert.ToByte(header));if (write)objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] writeBinaryDouble(double value){List<byte> buffer =new List<byte>(RegulateNullBytes(BitConverter.GetBytes(value), 4));while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))buffer.Add(0);int header = 0x20 | (int)(Math.Log(buffer.Count) / Math.Log(2));buffer.Reverse();buffer.Insert(0, Convert.ToByte(header));objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] writeBinaryByteArray(byte[] value){List<byte> buffer = new List<byte>(value);List<byte> header = new List<byte>();if (value.Length < 15){header.Add(Convert.ToByte(0x40 | Convert.ToByte(value.Length)));}else{header.Add(0x40 | 0xf);header.AddRange(writeBinaryInteger(buffer.Count, false));}buffer.InsertRange(0, header);objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] writeBinaryString(string value, bool head){List<byte> buffer = new List<byte>();List<byte> header = new List<byte>();foreach (char chr in value.ToCharArray())buffer.Add(Convert.ToByte(chr));if (head){if (value.Length < 15){header.Add(Convert.ToByte(0x50 | Convert.ToByte(value.Length)));}else{header.Add(0x50 | 0xf);header.AddRange(writeBinaryInteger(buffer.Count, false));}}buffer.InsertRange(0, header);objectTable.InsertRange(0, buffer);return buffer.ToArray();}private static byte[] RegulateNullBytes(byte[] value){return RegulateNullBytes(value, 1);}private static byte[] RegulateNullBytes(byte[] value, int minBytes){Array.Reverse(value);List<byte> bytes = new List<byte>(value);for (int i = 0; i < bytes.Count; i++){if (bytes[i] == 0 && bytes.Count > minBytes){bytes.Remove(bytes[i]);i--;}elsebreak;}if (bytes.Count < minBytes){int dist = minBytes - bytes.Count;for (int i = 0; i < dist; i++)bytes.Insert(0, 0);}value = bytes.ToArray();Array.Reverse(value);return value;}private static void parseTrailer(List<byte> trailer){offsetByteSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(6, 1).ToArray(), 4), 0);objRefSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(7, 1).ToArray(), 4), 0);byte[] refCountBytes = trailer.GetRange(12, 4).ToArray();Array.Reverse(refCountBytes);refCount = BitConverter.ToInt32(refCountBytes, 0);byte[] offsetTableOffsetBytes = trailer.GetRange(24, 8).ToArray();Array.Reverse(offsetTableOffsetBytes);offsetTableOffset = BitConverter.ToInt64(offsetTableOffsetBytes, 0);}private static void parseOffsetTable(List<byte> offsetTableBytes){for (int i = 0; i < offsetTableBytes.Count; i += offsetByteSize){byte[] buffer = offsetTableBytes.GetRange(i, offsetByteSize).ToArray();Array.Reverse(buffer);offsetTable.Add(BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0));}}private static object parseBinaryDictionary(int objRef){Dictionary<string, object> buffer = new Dictionary<string, object>();List<int> refs = new List<int>();int refCount = 0;int refStartPosition;refCount = getCount(offsetTable[objRef], out refStartPosition);if (refCount < 15)refStartPosition = offsetTable[objRef] + 1;elserefStartPosition = offsetTable[objRef] + 2 + RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;for (int i = refStartPosition; i < refStartPosition + refCount * 2 * objRefSize; i += objRefSize){byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();Array.Reverse(refBuffer);refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));}for (int i = 0; i < refCount; i++){buffer.Add((string)parseBinary(refs[i]), parseBinary(refs[i + refCount]));}return buffer;}private static object parseBinaryArray(int objRef){List<object> buffer = new List<object>();List<int> refs = new List<int>();int refCount = 0;int refStartPosition;refCount = getCount(offsetTable[objRef], out refStartPosition);if (refCount < 15)refStartPosition = offsetTable[objRef] + 1;else//The following integer has a header aswell so we increase the refStartPosition by two to account for that.refStartPosition = offsetTable[objRef] + 2 + RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;for (int i = refStartPosition; i < refStartPosition + refCount * objRefSize; i += objRefSize){byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();Array.Reverse(refBuffer);refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));}for (int i = 0; i < refCount; i++){buffer.Add(parseBinary(refs[i]));}return buffer;}private static int getCount(int bytePosition, out int newBytePosition){byte headerByte = objectTable[bytePosition];byte headerByteTrail = Convert.ToByte(headerByte & 0xf);int count;if (headerByteTrail < 15){count = headerByteTrail;newBytePosition = bytePosition + 1;}elsecount = (int)parseBinaryInt(bytePosition + 1, out newBytePosition);return count;}private static object parseBinary(int objRef){byte header = objectTable[offsetTable[objRef]];switch (header & 0xF0){case 0:{//If the byte is//0 return null//9 return true//8 return falsereturn (objectTable[offsetTable[objRef]] == 0) ? (object)null : ((objectTable[offsetTable[objRef]] == 9) ? true : false);}case 0x10:{return parseBinaryInt(offsetTable[objRef]);}case 0x20:{return parseBinaryReal(offsetTable[objRef]);}case 0x30:{return parseBinaryDate(offsetTable[objRef]);}case 0x40:{return parseBinaryByteArray(offsetTable[objRef]);}case 0x50://String ASCII{return parseBinaryAsciiString(offsetTable[objRef]);}case 0x60://String Unicode{return parseBinaryUnicodeString(offsetTable[objRef]);}case 0xD0:{return parseBinaryDictionary(objRef);}case 0xA0:{return parseBinaryArray(objRef);}}throw new Exception("This type is not supported");}public static object parseBinaryDate(int headerPosition){byte[] buffer = objectTable.GetRange(headerPosition + 1, 8).ToArray();Array.Reverse(buffer);double appleTime = BitConverter.ToDouble(buffer, 0);DateTime result = PlistDateConverter.ConvertFromAppleTimeStamp(appleTime);return result;}private static object parseBinaryInt(int headerPosition){int output;return parseBinaryInt(headerPosition, out output);}private static object parseBinaryInt(int headerPosition, out int newHeaderPosition){byte header = objectTable[headerPosition];int byteCount = (int)Math.Pow(2, header & 0xf);byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();Array.Reverse(buffer);//Add one to account for the header bytenewHeaderPosition = headerPosition + byteCount + 1;return BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0);}private static object parseBinaryReal(int headerPosition){byte header = objectTable[headerPosition];int byteCount = (int)Math.Pow(2, header & 0xf);byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();Array.Reverse(buffer);return BitConverter.ToDouble(RegulateNullBytes(buffer, 8), 0);}private static object parseBinaryAsciiString(int headerPosition){int charStartPosition;int charCount = getCount(headerPosition, out charStartPosition);var buffer = objectTable.GetRange(charStartPosition, charCount);return buffer.Count > 0 ? Encoding.ASCII.GetString(buffer.ToArray()) : string.Empty;}private static object parseBinaryUnicodeString(int headerPosition){int charStartPosition;int charCount = getCount(headerPosition, out charStartPosition);charCount = charCount * 2;byte[] buffer = new byte[charCount];byte one, two;for (int i = 0; i < charCount; i+=2){one = objectTable.GetRange(charStartPosition+i,1)[0];two = objectTable.GetRange(charStartPosition + i+1, 1)[0];if (BitConverter.IsLittleEndian){buffer[i] = two;buffer[i + 1] = one;}else{buffer[i] = one;buffer[i + 1] = two;}}return Encoding.Unicode.GetString(buffer);}private static object parseBinaryByteArray(int headerPosition){int byteStartPosition;int byteCount = getCount(headerPosition, out byteStartPosition);return objectTable.GetRange(byteStartPosition, byteCount).ToArray();}#endregion}public enum plistType{Auto, Binary, Xml}public static class PlistDateConverter{public static long timeDifference = 978307200;public static long GetAppleTime(long unixTime){return unixTime - timeDifference;}public static long GetUnixTime(long appleTime){return appleTime + timeDifference;}public static DateTime ConvertFromAppleTimeStamp(double timestamp){DateTime origin = new DateTime(2001, 1, 1, 0, 0, 0, 0);return origin.AddSeconds(timestamp);}public static double ConvertToAppleTimeStamp(DateTime date){DateTime begin = new DateTime(2001, 1, 1, 0, 0, 0, 0);TimeSpan diff = date - begin;return Math.Floor(diff.TotalSeconds);}}
}

Unity编辑器拓展-写一个查看当前所有PlayerPrefsKey的窗口相关推荐

  1. unity编辑器拓展整理(主要是siki的视频教程)

    编辑器拓展 https://blog.csdn.net/zxl321365712/article/details/80080586 蛮牛上一个详细的博客 http://www.manew.com/th ...

  2. Unity编辑器拓展之三:拓展Unity的Hierarchy面板

    博客迁移 个人博客站点,欢迎访问,www.jiingfengji.tech 正文 效果图: 上图中在Hierarchy右侧绘制了Toggle,Label,以及自定义的texture和Unity原声的T ...

  3. Unity编辑器拓展之六:利用反射打开Unity Preferences Window

    博客迁移 个人博客站点,欢迎访问,www.jiingfengji.tech 如何利用反射打开Unity Preferences Window Unity Preferences Window如下图所示 ...

  4. Unity编辑器下Console面板查看完整打印日志

    最近在做客户端遇到这样一个问题,对于Console面板上的打印日志,每条打印的内容是有上限的,比如我有一个包含100个国王信息的消息打印日志 往下拖动 划到底也只是显示了63条国王信息. 解决方法: ...

  5. android仿简书编辑器,自己写一个类似知乎编辑器的编辑器(3)

    吃完饭回办公室,放一首歌<can't take my eyes off you>,戴上耳机,接着把昨天想写的富文本编辑器做完. 昨天已经搭好一个模型了.今天就是核心问题了:如何将div变成 ...

  6. Unity编辑器拓展(Handles/EditorTool)

    Handles Scene视图中的自定义 3D GUI 控件和绘制操作. UnityEditor.Handles - Unity 脚本 APIhttps://docs.unity.cn/cn/curr ...

  7. Unity编辑器拓展(一)-MenuItem的使用

    MenuItem的使用 一.参数介绍 二.验证函数 三.优先级 四.快捷键 五.在Hierarchy层级窗口增加右键菜单 六.在Assets资源窗口增加右键菜单 一.参数介绍 MenuItem是一个特 ...

  8. Unity编辑器拓展(Gizmos)

    Gizmos 辅助图标用于协助在 Scene 视图中进行视觉调试或设置. 所有辅助图标绘图都必须在此脚本的 OnDrawGizmos 或 OnDrawGizmosSelected 函数中进行. Uni ...

  9. Unity编辑器拓展之二十四:基于Unity Node Editor、ScriptableObject的配置化新手引导系统

    博客迁移 个人博客站点,欢迎访问,www.jiingfengji.tech 本文主要介绍根据一款Node Editor库开发的新手引导系统 git地址如下: https://github.com/lu ...

最新文章

  1. 初学Java Web——Servlet(一)
  2. 一文彻底搞懂快速幂(原理、实现、矩阵快速幂)
  3. boost::fibers::buffered_channel< std::string >用法的测试程序
  4. 分数转小数C语言,这是把小数转换成分数的程序,可是输入0.6666无限循环
  5. html中article、section、aside的区别与联系
  6. OpenJudge NOI 1.5 16:买房子
  7. 支持 RISC-V 芯片的 Android 系统来了!
  8. okhttp3+retrofit2+rxjava2
  9. 微信公众平台的开发流程及其要点
  10. 「CSS畅想」好友想回忆童年,安排~为她做了一个果宝特攻的换装
  11. w ndows无法完成格式化,windows无法完成格式化怎么办【图文教程】
  12. 麦克劳林公式怎么记忆_怎么背麦克劳林公式?
  13. PostgreSQL psql 命令语法参数说明
  14. 2021年10月最新使用selenium爬取裁判文书数据(本文仅供技术交流使用)
  15. (信贷风控十五)评分卡分数切分、授信额度与利率定价
  16. Pr:Lumetri 范围
  17. 找不到apt和vim命令
  18. 网络爬虫:网页信息获取
  19. 基于WMI获取USB设备信息(即获取插即用设备信息)System.Management.ManagementObjectSearcher--ManagementObjectCollection
  20. Python环境搭建总结

热门文章

  1. 11.11即将到来,华为云学院精品课程免费推荐奉上
  2. 如何用golang编写单元测试用例
  3. 【Python 爬虫】从入门到放弃(11 个有趣的 Python 爬虫例子)
  4. Juce音频算法demo
  5. OSI七层模型与TCP\IP协议
  6. Ribbon基础原理
  7. 2023年软件著作权如何申请?需要什么材料?
  8. 一阶低通滤波器的传递函数分析
  9. META 連接 MT6572 平台手機後發生錯誤
  10. 爱数之DB2备份与恢复