Unity编辑器拓展-写一个查看当前所有PlayerPrefsKey的窗口
创建一个PlayerPrefsKey查看器
- 效果图
- 前言
- 开发
- 一、获取数据
- 目录结构
- 二、开发编辑器窗口
- 效果图
- 思路
- 一、绘制搜索栏
- 二、定义PlayerPrefs数据结构体
- 三、根据搜索框过滤数据
- 四、绘制主框体
- 五、完整代码
效果图
前言
PlayerPrefsKey现在其实用得并不多,不过也有一些应用场景。
最近开发过程中,发现要删除一个PlayerPrefsKey,有点麻烦。可以写代码删除 或者 去注册表删除。都是都感觉有点麻烦,而且也没法知道当前已经设置了哪些PlayerPrefsKey。 所以就想做一个窗口来查看当前所有key,并可以支持搜索和删除。
tip:修改功能就不添加了,因为一般删除PlayerPrefsKey让其恢复默认值是安全OK的。但是如果直接不知道它的用处或值域,直接修改,就可能有问题。所以不增加修改功能。
开发
开发分两步:
- 第一步获取所有PlayerPrefsKey数据;
- 第二步根据数据写编辑器窗口;
一、获取数据
获取数据我是直接在网上搜了一下获取方法,找到了这篇博客,获取所有PlayerPrefsKey windows下测试可以拿到所有key. 获取数据可以直接看这个链接。这里我们主要讲一下编辑器窗口。
目录结构
看文章可以知道需要两个工具类: PlayerPrefsExtension.cs 和 Plist.cs.
目录结构如下,包含上面两个工具类,和窗口编辑器PlayerPrefsWindow。工具类代码会在最后提供
二、开发编辑器窗口
效果图
思路
看效果图,可以知道大体分为两部分:最上面的搜索栏 和 下面的主框体。
怎么实现搜索呢:
- 首先,我们可以通过工具类获取到所有的key
- 如果搜索框是空的,我们就把所有数据交给主框体渲染;
- 如果搜索框有数据,我们就把第一步拿到的数据过滤一下,再交给主框体渲染。这样就可以了。
一、绘制搜索栏
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的窗口相关推荐
- unity编辑器拓展整理(主要是siki的视频教程)
编辑器拓展 https://blog.csdn.net/zxl321365712/article/details/80080586 蛮牛上一个详细的博客 http://www.manew.com/th ...
- Unity编辑器拓展之三:拓展Unity的Hierarchy面板
博客迁移 个人博客站点,欢迎访问,www.jiingfengji.tech 正文 效果图: 上图中在Hierarchy右侧绘制了Toggle,Label,以及自定义的texture和Unity原声的T ...
- Unity编辑器拓展之六:利用反射打开Unity Preferences Window
博客迁移 个人博客站点,欢迎访问,www.jiingfengji.tech 如何利用反射打开Unity Preferences Window Unity Preferences Window如下图所示 ...
- Unity编辑器下Console面板查看完整打印日志
最近在做客户端遇到这样一个问题,对于Console面板上的打印日志,每条打印的内容是有上限的,比如我有一个包含100个国王信息的消息打印日志 往下拖动 划到底也只是显示了63条国王信息. 解决方法: ...
- android仿简书编辑器,自己写一个类似知乎编辑器的编辑器(3)
吃完饭回办公室,放一首歌<can't take my eyes off you>,戴上耳机,接着把昨天想写的富文本编辑器做完. 昨天已经搭好一个模型了.今天就是核心问题了:如何将div变成 ...
- Unity编辑器拓展(Handles/EditorTool)
Handles Scene视图中的自定义 3D GUI 控件和绘制操作. UnityEditor.Handles - Unity 脚本 APIhttps://docs.unity.cn/cn/curr ...
- Unity编辑器拓展(一)-MenuItem的使用
MenuItem的使用 一.参数介绍 二.验证函数 三.优先级 四.快捷键 五.在Hierarchy层级窗口增加右键菜单 六.在Assets资源窗口增加右键菜单 一.参数介绍 MenuItem是一个特 ...
- Unity编辑器拓展(Gizmos)
Gizmos 辅助图标用于协助在 Scene 视图中进行视觉调试或设置. 所有辅助图标绘图都必须在此脚本的 OnDrawGizmos 或 OnDrawGizmosSelected 函数中进行. Uni ...
- Unity编辑器拓展之二十四:基于Unity Node Editor、ScriptableObject的配置化新手引导系统
博客迁移 个人博客站点,欢迎访问,www.jiingfengji.tech 本文主要介绍根据一款Node Editor库开发的新手引导系统 git地址如下: https://github.com/lu ...
最新文章
- 初学Java Web——Servlet(一)
- 一文彻底搞懂快速幂(原理、实现、矩阵快速幂)
- boost::fibers::buffered_channel< std::string >用法的测试程序
- 分数转小数C语言,这是把小数转换成分数的程序,可是输入0.6666无限循环
- html中article、section、aside的区别与联系
- OpenJudge NOI 1.5 16:买房子
- 支持 RISC-V 芯片的 Android 系统来了!
- okhttp3+retrofit2+rxjava2
- 微信公众平台的开发流程及其要点
- 「CSS畅想」好友想回忆童年,安排~为她做了一个果宝特攻的换装
- w ndows无法完成格式化,windows无法完成格式化怎么办【图文教程】
- 麦克劳林公式怎么记忆_怎么背麦克劳林公式?
- PostgreSQL psql 命令语法参数说明
- 2021年10月最新使用selenium爬取裁判文书数据(本文仅供技术交流使用)
- (信贷风控十五)评分卡分数切分、授信额度与利率定价
- Pr:Lumetri 范围
- 找不到apt和vim命令
- 网络爬虫:网页信息获取
- 基于WMI获取USB设备信息(即获取插即用设备信息)System.Management.ManagementObjectSearcher--ManagementObjectCollection
- Python环境搭建总结