Plist.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Text;
  6. using System.Xml;
  7. namespace TapTap.Common.Editor
  8. {
  9. public static class Plist
  10. {
  11. private static List<int> offsetTable = new List<int>();
  12. private static List<byte> objectTable = new List<byte>();
  13. private static int refCount;
  14. private static int objRefSize;
  15. private static int offsetByteSize;
  16. private static long offsetTableOffset;
  17. #region Public Functions
  18. public static object readPlist(string path)
  19. {
  20. using (FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read))
  21. {
  22. return readPlist(f, plistType.Auto);
  23. }
  24. }
  25. public static object readPlistSource(string source)
  26. {
  27. return readPlist(System.Text.Encoding.UTF8.GetBytes(source));
  28. }
  29. public static object readPlist(byte[] data)
  30. {
  31. return readPlist(new MemoryStream(data), plistType.Auto);
  32. }
  33. public static plistType getPlistType(Stream stream)
  34. {
  35. byte[] magicHeader = new byte[8];
  36. stream.Read(magicHeader, 0, 8);
  37. if (BitConverter.ToInt64(magicHeader, 0) == 3472403351741427810)
  38. {
  39. return plistType.Binary;
  40. }
  41. else
  42. {
  43. return plistType.Xml;
  44. }
  45. }
  46. public static object readPlist(Stream stream, plistType type)
  47. {
  48. if (type == plistType.Auto)
  49. {
  50. type = getPlistType(stream);
  51. stream.Seek(0, SeekOrigin.Begin);
  52. }
  53. if (type == plistType.Binary)
  54. {
  55. using (BinaryReader reader = new BinaryReader(stream))
  56. {
  57. byte[] data = reader.ReadBytes((int) reader.BaseStream.Length);
  58. return readBinary(data);
  59. }
  60. }
  61. else
  62. {
  63. XmlDocument xml = new XmlDocument();
  64. xml.XmlResolver = null;
  65. xml.Load(stream);
  66. return readXml(xml);
  67. }
  68. }
  69. public static void writeXml(object value, string path)
  70. {
  71. using (StreamWriter writer = new StreamWriter(path))
  72. {
  73. writer.Write(writeXml(value));
  74. }
  75. }
  76. public static void writeXml(object value, Stream stream)
  77. {
  78. using (StreamWriter writer = new StreamWriter(stream))
  79. {
  80. writer.Write(writeXml(value));
  81. }
  82. }
  83. public static string writeXml(object value)
  84. {
  85. using (MemoryStream ms = new MemoryStream())
  86. {
  87. XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
  88. xmlWriterSettings.Encoding = new System.Text.UTF8Encoding(false);
  89. xmlWriterSettings.ConformanceLevel = ConformanceLevel.Document;
  90. xmlWriterSettings.Indent = true;
  91. using (XmlWriter xmlWriter = XmlWriter.Create(ms, xmlWriterSettings))
  92. {
  93. xmlWriter.WriteStartDocument();
  94. //xmlWriter.WriteComment("DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
  95. xmlWriter.WriteDocType("plist", "-//Apple Computer//DTD PLIST 1.0//EN",
  96. "http://www.apple.com/DTDs/PropertyList-1.0.dtd", null);
  97. xmlWriter.WriteStartElement("plist");
  98. xmlWriter.WriteAttributeString("version", "1.0");
  99. compose(value, xmlWriter);
  100. xmlWriter.WriteEndElement();
  101. xmlWriter.WriteEndDocument();
  102. xmlWriter.Flush();
  103. xmlWriter.Close();
  104. return System.Text.Encoding.UTF8.GetString(ms.ToArray());
  105. }
  106. }
  107. }
  108. public static void writeBinary(object value, string path)
  109. {
  110. using (BinaryWriter writer = new BinaryWriter(new FileStream(path, FileMode.Create)))
  111. {
  112. writer.Write(writeBinary(value));
  113. }
  114. }
  115. public static void writeBinary(object value, Stream stream)
  116. {
  117. using (BinaryWriter writer = new BinaryWriter(stream))
  118. {
  119. writer.Write(writeBinary(value));
  120. }
  121. }
  122. public static byte[] writeBinary(object value)
  123. {
  124. offsetTable.Clear();
  125. objectTable.Clear();
  126. refCount = 0;
  127. objRefSize = 0;
  128. offsetByteSize = 0;
  129. offsetTableOffset = 0;
  130. //Do not count the root node, subtract by 1
  131. int totalRefs = countObject(value) - 1;
  132. refCount = totalRefs;
  133. objRefSize = RegulateNullBytes(BitConverter.GetBytes(refCount)).Length;
  134. composeBinary(value);
  135. writeBinaryString("bplist00", false);
  136. offsetTableOffset = (long) objectTable.Count;
  137. offsetTable.Add(objectTable.Count - 8);
  138. offsetByteSize = RegulateNullBytes(BitConverter.GetBytes(offsetTable[offsetTable.Count - 1])).Length;
  139. List<byte> offsetBytes = new List<byte>();
  140. offsetTable.Reverse();
  141. for (int i = 0; i < offsetTable.Count; i++)
  142. {
  143. offsetTable[i] = objectTable.Count - offsetTable[i];
  144. byte[] buffer = RegulateNullBytes(BitConverter.GetBytes(offsetTable[i]), offsetByteSize);
  145. Array.Reverse(buffer);
  146. offsetBytes.AddRange(buffer);
  147. }
  148. objectTable.AddRange(offsetBytes);
  149. objectTable.AddRange(new byte[6]);
  150. objectTable.Add(Convert.ToByte(offsetByteSize));
  151. objectTable.Add(Convert.ToByte(objRefSize));
  152. var a = BitConverter.GetBytes((long) totalRefs + 1);
  153. Array.Reverse(a);
  154. objectTable.AddRange(a);
  155. objectTable.AddRange(BitConverter.GetBytes((long) 0));
  156. a = BitConverter.GetBytes(offsetTableOffset);
  157. Array.Reverse(a);
  158. objectTable.AddRange(a);
  159. return objectTable.ToArray();
  160. }
  161. #endregion
  162. #region Private Functions
  163. private static object readXml(XmlDocument xml)
  164. {
  165. XmlNode rootNode = xml.DocumentElement.ChildNodes[0];
  166. return parse(rootNode);
  167. }
  168. private static object readBinary(byte[] data)
  169. {
  170. offsetTable.Clear();
  171. List<byte> offsetTableBytes = new List<byte>();
  172. objectTable.Clear();
  173. refCount = 0;
  174. objRefSize = 0;
  175. offsetByteSize = 0;
  176. offsetTableOffset = 0;
  177. List<byte> bList = new List<byte>(data);
  178. List<byte> trailer = bList.GetRange(bList.Count - 32, 32);
  179. parseTrailer(trailer);
  180. objectTable = bList.GetRange(0, (int) offsetTableOffset);
  181. offsetTableBytes = bList.GetRange((int) offsetTableOffset, bList.Count - (int) offsetTableOffset - 32);
  182. parseOffsetTable(offsetTableBytes);
  183. return parseBinary(0);
  184. }
  185. private static Dictionary<string, object> parseDictionary(XmlNode node)
  186. {
  187. XmlNodeList children = node.ChildNodes;
  188. if (children.Count % 2 != 0)
  189. {
  190. throw new DataMisalignedException("Dictionary elements must have an even number of child nodes");
  191. }
  192. Dictionary<string, object> dict = new Dictionary<string, object>();
  193. for (int i = 0; i < children.Count; i += 2)
  194. {
  195. XmlNode keynode = children[i];
  196. XmlNode valnode = children[i + 1];
  197. if (keynode.Name != "key")
  198. {
  199. throw new ApplicationException("expected a key node");
  200. }
  201. object result = parse(valnode);
  202. if (result != null)
  203. {
  204. dict.Add(keynode.InnerText, result);
  205. }
  206. }
  207. return dict;
  208. }
  209. private static List<object> parseArray(XmlNode node)
  210. {
  211. List<object> array = new List<object>();
  212. foreach (XmlNode child in node.ChildNodes)
  213. {
  214. object result = parse(child);
  215. if (result != null)
  216. {
  217. array.Add(result);
  218. }
  219. }
  220. return array;
  221. }
  222. private static void composeArray(List<object> value, XmlWriter writer)
  223. {
  224. writer.WriteStartElement("array");
  225. foreach (object obj in value)
  226. {
  227. compose(obj, writer);
  228. }
  229. writer.WriteEndElement();
  230. }
  231. private static object parse(XmlNode node)
  232. {
  233. switch (node.Name)
  234. {
  235. case "dict":
  236. return parseDictionary(node);
  237. case "array":
  238. return parseArray(node);
  239. case "string":
  240. return node.InnerText;
  241. case "integer":
  242. // int result;
  243. //int.TryParse(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo, out result);
  244. return Convert.ToInt32(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo);
  245. case "real":
  246. return Convert.ToDouble(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo);
  247. case "false":
  248. return false;
  249. case "true":
  250. return true;
  251. case "null":
  252. return null;
  253. case "date":
  254. return XmlConvert.ToDateTime(node.InnerText, XmlDateTimeSerializationMode.Utc);
  255. case "data":
  256. return Convert.FromBase64String(node.InnerText);
  257. }
  258. throw new ApplicationException(String.Format("Plist Node `{0}' is not supported", node.Name));
  259. }
  260. private static void compose(object value, XmlWriter writer)
  261. {
  262. if (value == null || value is string)
  263. {
  264. writer.WriteElementString("string", value as string);
  265. }
  266. else if (value is int || value is long)
  267. {
  268. writer.WriteElementString("integer",
  269. ((int) value).ToString(System.Globalization.NumberFormatInfo.InvariantInfo));
  270. }
  271. else if (value is System.Collections.Generic.Dictionary<string, object> ||
  272. value.GetType().ToString().StartsWith("System.Collections.Generic.Dictionary`2[System.String"))
  273. {
  274. //Convert to Dictionary<string, object>
  275. Dictionary<string, object> dic = value as Dictionary<string, object>;
  276. if (dic == null)
  277. {
  278. dic = new Dictionary<string, object>();
  279. IDictionary idic = (IDictionary) value;
  280. foreach (var key in idic.Keys)
  281. {
  282. dic.Add(key.ToString(), idic[key]);
  283. }
  284. }
  285. writeDictionaryValues(dic, writer);
  286. }
  287. else if (value is List<object>)
  288. {
  289. composeArray((List<object>) value, writer);
  290. }
  291. else if (value is byte[])
  292. {
  293. writer.WriteElementString("data", Convert.ToBase64String((Byte[]) value));
  294. }
  295. else if (value is float || value is double)
  296. {
  297. writer.WriteElementString("real",
  298. ((double) value).ToString(System.Globalization.NumberFormatInfo.InvariantInfo));
  299. }
  300. else if (value is DateTime)
  301. {
  302. DateTime time = (DateTime) value;
  303. string theString = XmlConvert.ToString(time, XmlDateTimeSerializationMode.Utc);
  304. writer.WriteElementString("date", theString); //, "yyyy-MM-ddTHH:mm:ssZ"));
  305. }
  306. else if (value is bool)
  307. {
  308. writer.WriteElementString(value.ToString().ToLower(), "");
  309. }
  310. else
  311. {
  312. throw new Exception(String.Format("Value type '{0}' is unhandled", value.GetType().ToString()));
  313. }
  314. }
  315. private static void writeDictionaryValues(Dictionary<string, object> dictionary, XmlWriter writer)
  316. {
  317. writer.WriteStartElement("dict");
  318. foreach (string key in dictionary.Keys)
  319. {
  320. object value = dictionary[key];
  321. writer.WriteElementString("key", key);
  322. compose(value, writer);
  323. }
  324. writer.WriteEndElement();
  325. }
  326. private static int countObject(object value)
  327. {
  328. int count = 0;
  329. switch (value.GetType().ToString())
  330. {
  331. case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":
  332. Dictionary<string, object> dict = (Dictionary<string, object>) value;
  333. foreach (string key in dict.Keys)
  334. {
  335. count += countObject(dict[key]);
  336. }
  337. count += dict.Keys.Count;
  338. count++;
  339. break;
  340. case "System.Collections.Generic.List`1[System.Object]":
  341. List<object> list = (List<object>) value;
  342. foreach (object obj in list)
  343. {
  344. count += countObject(obj);
  345. }
  346. count++;
  347. break;
  348. default:
  349. count++;
  350. break;
  351. }
  352. return count;
  353. }
  354. private static byte[] writeBinaryDictionary(Dictionary<string, object> dictionary)
  355. {
  356. List<byte> buffer = new List<byte>();
  357. List<byte> header = new List<byte>();
  358. List<int> refs = new List<int>();
  359. for (int i = dictionary.Count - 1; i >= 0; i--)
  360. {
  361. var o = new object[dictionary.Count];
  362. dictionary.Values.CopyTo(o, 0);
  363. composeBinary(o[i]);
  364. offsetTable.Add(objectTable.Count);
  365. refs.Add(refCount);
  366. refCount--;
  367. }
  368. for (int i = dictionary.Count - 1; i >= 0; i--)
  369. {
  370. var o = new string[dictionary.Count];
  371. dictionary.Keys.CopyTo(o, 0);
  372. composeBinary(o[i]); //);
  373. offsetTable.Add(objectTable.Count);
  374. refs.Add(refCount);
  375. refCount--;
  376. }
  377. if (dictionary.Count < 15)
  378. {
  379. header.Add(Convert.ToByte(0xD0 | Convert.ToByte(dictionary.Count)));
  380. }
  381. else
  382. {
  383. header.Add(0xD0 | 0xf);
  384. header.AddRange(writeBinaryInteger(dictionary.Count, false));
  385. }
  386. foreach (int val in refs)
  387. {
  388. byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);
  389. Array.Reverse(refBuffer);
  390. buffer.InsertRange(0, refBuffer);
  391. }
  392. buffer.InsertRange(0, header);
  393. objectTable.InsertRange(0, buffer);
  394. return buffer.ToArray();
  395. }
  396. private static byte[] composeBinaryArray(List<object> objects)
  397. {
  398. List<byte> buffer = new List<byte>();
  399. List<byte> header = new List<byte>();
  400. List<int> refs = new List<int>();
  401. for (int i = objects.Count - 1; i >= 0; i--)
  402. {
  403. composeBinary(objects[i]);
  404. offsetTable.Add(objectTable.Count);
  405. refs.Add(refCount);
  406. refCount--;
  407. }
  408. if (objects.Count < 15)
  409. {
  410. header.Add(Convert.ToByte(0xA0 | Convert.ToByte(objects.Count)));
  411. }
  412. else
  413. {
  414. header.Add(0xA0 | 0xf);
  415. header.AddRange(writeBinaryInteger(objects.Count, false));
  416. }
  417. foreach (int val in refs)
  418. {
  419. byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);
  420. Array.Reverse(refBuffer);
  421. buffer.InsertRange(0, refBuffer);
  422. }
  423. buffer.InsertRange(0, header);
  424. objectTable.InsertRange(0, buffer);
  425. return buffer.ToArray();
  426. }
  427. private static byte[] composeBinary(object obj)
  428. {
  429. byte[] value;
  430. switch (obj.GetType().ToString())
  431. {
  432. case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":
  433. value = writeBinaryDictionary((Dictionary<string, object>) obj);
  434. return value;
  435. case "System.Collections.Generic.List`1[System.Object]":
  436. value = composeBinaryArray((List<object>) obj);
  437. return value;
  438. case "System.Byte[]":
  439. value = writeBinaryByteArray((byte[]) obj);
  440. return value;
  441. case "System.Double":
  442. value = writeBinaryDouble((double) obj);
  443. return value;
  444. case "System.Int32":
  445. value = writeBinaryInteger((int) obj, true);
  446. return value;
  447. case "System.String":
  448. value = writeBinaryString((string) obj, true);
  449. return value;
  450. case "System.DateTime":
  451. value = writeBinaryDate((DateTime) obj);
  452. return value;
  453. case "System.Boolean":
  454. value = writeBinaryBool((bool) obj);
  455. return value;
  456. default:
  457. return new byte[0];
  458. }
  459. }
  460. public static byte[] writeBinaryDate(DateTime obj)
  461. {
  462. List<byte> buffer =
  463. new List<byte>(RegulateNullBytes(BitConverter.GetBytes(PlistDateConverter.ConvertToAppleTimeStamp(obj)),
  464. 8));
  465. buffer.Reverse();
  466. buffer.Insert(0, 0x33);
  467. objectTable.InsertRange(0, buffer);
  468. return buffer.ToArray();
  469. }
  470. public static byte[] writeBinaryBool(bool obj)
  471. {
  472. List<byte> buffer = new List<byte>(new byte[1] {(bool) obj ? (byte) 9 : (byte) 8});
  473. objectTable.InsertRange(0, buffer);
  474. return buffer.ToArray();
  475. }
  476. private static byte[] writeBinaryInteger(int value, bool write)
  477. {
  478. List<byte> buffer = new List<byte>(BitConverter.GetBytes((long) value));
  479. buffer = new List<byte>(RegulateNullBytes(buffer.ToArray()));
  480. while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))
  481. buffer.Add(0);
  482. int header = 0x10 | (int) (Math.Log(buffer.Count) / Math.Log(2));
  483. buffer.Reverse();
  484. buffer.Insert(0, Convert.ToByte(header));
  485. if (write)
  486. objectTable.InsertRange(0, buffer);
  487. return buffer.ToArray();
  488. }
  489. private static byte[] writeBinaryDouble(double value)
  490. {
  491. List<byte> buffer = new List<byte>(RegulateNullBytes(BitConverter.GetBytes(value), 4));
  492. while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))
  493. buffer.Add(0);
  494. int header = 0x20 | (int) (Math.Log(buffer.Count) / Math.Log(2));
  495. buffer.Reverse();
  496. buffer.Insert(0, Convert.ToByte(header));
  497. objectTable.InsertRange(0, buffer);
  498. return buffer.ToArray();
  499. }
  500. private static byte[] writeBinaryByteArray(byte[] value)
  501. {
  502. List<byte> buffer = new List<byte>(value);
  503. List<byte> header = new List<byte>();
  504. if (value.Length < 15)
  505. {
  506. header.Add(Convert.ToByte(0x40 | Convert.ToByte(value.Length)));
  507. }
  508. else
  509. {
  510. header.Add(0x40 | 0xf);
  511. header.AddRange(writeBinaryInteger(buffer.Count, false));
  512. }
  513. buffer.InsertRange(0, header);
  514. objectTable.InsertRange(0, buffer);
  515. return buffer.ToArray();
  516. }
  517. private static byte[] writeBinaryString(string value, bool head)
  518. {
  519. List<byte> buffer = new List<byte>();
  520. List<byte> header = new List<byte>();
  521. foreach (char chr in value.ToCharArray())
  522. buffer.Add(Convert.ToByte(chr));
  523. if (head)
  524. {
  525. if (value.Length < 15)
  526. {
  527. header.Add(Convert.ToByte(0x50 | Convert.ToByte(value.Length)));
  528. }
  529. else
  530. {
  531. header.Add(0x50 | 0xf);
  532. header.AddRange(writeBinaryInteger(buffer.Count, false));
  533. }
  534. }
  535. buffer.InsertRange(0, header);
  536. objectTable.InsertRange(0, buffer);
  537. return buffer.ToArray();
  538. }
  539. private static byte[] RegulateNullBytes(byte[] value)
  540. {
  541. return RegulateNullBytes(value, 1);
  542. }
  543. private static byte[] RegulateNullBytes(byte[] value, int minBytes)
  544. {
  545. Array.Reverse(value);
  546. List<byte> bytes = new List<byte>(value);
  547. for (int i = 0; i < bytes.Count; i++)
  548. {
  549. if (bytes[i] == 0 && bytes.Count > minBytes)
  550. {
  551. bytes.Remove(bytes[i]);
  552. i--;
  553. }
  554. else
  555. break;
  556. }
  557. if (bytes.Count < minBytes)
  558. {
  559. int dist = minBytes - bytes.Count;
  560. for (int i = 0; i < dist; i++)
  561. bytes.Insert(0, 0);
  562. }
  563. value = bytes.ToArray();
  564. Array.Reverse(value);
  565. return value;
  566. }
  567. private static void parseTrailer(List<byte> trailer)
  568. {
  569. offsetByteSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(6, 1).ToArray(), 4), 0);
  570. objRefSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(7, 1).ToArray(), 4), 0);
  571. byte[] refCountBytes = trailer.GetRange(12, 4).ToArray();
  572. Array.Reverse(refCountBytes);
  573. refCount = BitConverter.ToInt32(refCountBytes, 0);
  574. byte[] offsetTableOffsetBytes = trailer.GetRange(24, 8).ToArray();
  575. Array.Reverse(offsetTableOffsetBytes);
  576. offsetTableOffset = BitConverter.ToInt64(offsetTableOffsetBytes, 0);
  577. }
  578. private static void parseOffsetTable(List<byte> offsetTableBytes)
  579. {
  580. for (int i = 0; i < offsetTableBytes.Count; i += offsetByteSize)
  581. {
  582. byte[] buffer = offsetTableBytes.GetRange(i, offsetByteSize).ToArray();
  583. Array.Reverse(buffer);
  584. offsetTable.Add(BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0));
  585. }
  586. }
  587. private static object parseBinaryDictionary(int objRef)
  588. {
  589. Dictionary<string, object> buffer = new Dictionary<string, object>();
  590. List<int> refs = new List<int>();
  591. int refCount = 0;
  592. int refStartPosition;
  593. refCount = getCount(offsetTable[objRef], out refStartPosition);
  594. if (refCount < 15)
  595. refStartPosition = offsetTable[objRef] + 1;
  596. else
  597. refStartPosition = offsetTable[objRef] + 2 +
  598. RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;
  599. for (int i = refStartPosition; i < refStartPosition + refCount * 2 * objRefSize; i += objRefSize)
  600. {
  601. byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();
  602. Array.Reverse(refBuffer);
  603. refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));
  604. }
  605. for (int i = 0; i < refCount; i++)
  606. {
  607. buffer.Add((string) parseBinary(refs[i]), parseBinary(refs[i + refCount]));
  608. }
  609. return buffer;
  610. }
  611. private static object parseBinaryArray(int objRef)
  612. {
  613. List<object> buffer = new List<object>();
  614. List<int> refs = new List<int>();
  615. int refCount = 0;
  616. int refStartPosition;
  617. refCount = getCount(offsetTable[objRef], out refStartPosition);
  618. if (refCount < 15)
  619. refStartPosition = offsetTable[objRef] + 1;
  620. else
  621. //The following integer has a header aswell so we increase the refStartPosition by two to account for that.
  622. refStartPosition = offsetTable[objRef] + 2 +
  623. RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;
  624. for (int i = refStartPosition; i < refStartPosition + refCount * objRefSize; i += objRefSize)
  625. {
  626. byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();
  627. Array.Reverse(refBuffer);
  628. refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));
  629. }
  630. for (int i = 0; i < refCount; i++)
  631. {
  632. buffer.Add(parseBinary(refs[i]));
  633. }
  634. return buffer;
  635. }
  636. private static int getCount(int bytePosition, out int newBytePosition)
  637. {
  638. byte headerByte = objectTable[bytePosition];
  639. byte headerByteTrail = Convert.ToByte(headerByte & 0xf);
  640. int count;
  641. if (headerByteTrail < 15)
  642. {
  643. count = headerByteTrail;
  644. newBytePosition = bytePosition + 1;
  645. }
  646. else
  647. count = (int) parseBinaryInt(bytePosition + 1, out newBytePosition);
  648. return count;
  649. }
  650. private static object parseBinary(int objRef)
  651. {
  652. byte header = objectTable[offsetTable[objRef]];
  653. switch (header & 0xF0)
  654. {
  655. case 0:
  656. {
  657. //If the byte is
  658. //0 return null
  659. //9 return true
  660. //8 return false
  661. return (objectTable[offsetTable[objRef]] == 0)
  662. ? (object) null
  663. : ((objectTable[offsetTable[objRef]] == 9) ? true : false);
  664. }
  665. case 0x10:
  666. {
  667. return parseBinaryInt(offsetTable[objRef]);
  668. }
  669. case 0x20:
  670. {
  671. return parseBinaryReal(offsetTable[objRef]);
  672. }
  673. case 0x30:
  674. {
  675. return parseBinaryDate(offsetTable[objRef]);
  676. }
  677. case 0x40:
  678. {
  679. return parseBinaryByteArray(offsetTable[objRef]);
  680. }
  681. case 0x50: //String ASCII
  682. {
  683. return parseBinaryAsciiString(offsetTable[objRef]);
  684. }
  685. case 0x60: //String Unicode
  686. {
  687. return parseBinaryUnicodeString(offsetTable[objRef]);
  688. }
  689. case 0xD0:
  690. {
  691. return parseBinaryDictionary(objRef);
  692. }
  693. case 0xA0:
  694. {
  695. return parseBinaryArray(objRef);
  696. }
  697. }
  698. throw new Exception("This type is not supported");
  699. }
  700. public static object parseBinaryDate(int headerPosition)
  701. {
  702. byte[] buffer = objectTable.GetRange(headerPosition + 1, 8).ToArray();
  703. Array.Reverse(buffer);
  704. double appleTime = BitConverter.ToDouble(buffer, 0);
  705. DateTime result = PlistDateConverter.ConvertFromAppleTimeStamp(appleTime);
  706. return result;
  707. }
  708. private static object parseBinaryInt(int headerPosition)
  709. {
  710. int output;
  711. return parseBinaryInt(headerPosition, out output);
  712. }
  713. private static object parseBinaryInt(int headerPosition, out int newHeaderPosition)
  714. {
  715. byte header = objectTable[headerPosition];
  716. int byteCount = (int) Math.Pow(2, header & 0xf);
  717. byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();
  718. Array.Reverse(buffer);
  719. //Add one to account for the header byte
  720. newHeaderPosition = headerPosition + byteCount + 1;
  721. return BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0);
  722. }
  723. private static object parseBinaryReal(int headerPosition)
  724. {
  725. byte header = objectTable[headerPosition];
  726. int byteCount = (int) Math.Pow(2, header & 0xf);
  727. byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();
  728. Array.Reverse(buffer);
  729. return BitConverter.ToDouble(RegulateNullBytes(buffer, 8), 0);
  730. }
  731. private static object parseBinaryAsciiString(int headerPosition)
  732. {
  733. int charStartPosition;
  734. int charCount = getCount(headerPosition, out charStartPosition);
  735. var buffer = objectTable.GetRange(charStartPosition, charCount);
  736. return buffer.Count > 0 ? Encoding.ASCII.GetString(buffer.ToArray()) : string.Empty;
  737. }
  738. private static object parseBinaryUnicodeString(int headerPosition)
  739. {
  740. int charStartPosition;
  741. int charCount = getCount(headerPosition, out charStartPosition);
  742. charCount = charCount * 2;
  743. byte[] buffer = new byte[charCount];
  744. byte one, two;
  745. for (int i = 0; i < charCount; i += 2)
  746. {
  747. one = objectTable.GetRange(charStartPosition + i, 1)[0];
  748. two = objectTable.GetRange(charStartPosition + i + 1, 1)[0];
  749. if (BitConverter.IsLittleEndian)
  750. {
  751. buffer[i] = two;
  752. buffer[i + 1] = one;
  753. }
  754. else
  755. {
  756. buffer[i] = one;
  757. buffer[i + 1] = two;
  758. }
  759. }
  760. return Encoding.Unicode.GetString(buffer);
  761. }
  762. private static object parseBinaryByteArray(int headerPosition)
  763. {
  764. int byteStartPosition;
  765. int byteCount = getCount(headerPosition, out byteStartPosition);
  766. return objectTable.GetRange(byteStartPosition, byteCount).ToArray();
  767. }
  768. #endregion
  769. }
  770. public enum plistType
  771. {
  772. Auto,
  773. Binary,
  774. Xml
  775. }
  776. public static class PlistDateConverter
  777. {
  778. public static long timeDifference = 978307200;
  779. public static long GetAppleTime(long unixTime)
  780. {
  781. return unixTime - timeDifference;
  782. }
  783. public static long GetUnixTime(long appleTime)
  784. {
  785. return appleTime + timeDifference;
  786. }
  787. public static DateTime ConvertFromAppleTimeStamp(double timestamp)
  788. {
  789. DateTime origin = new DateTime(2001, 1, 1, 0, 0, 0, 0);
  790. return origin.AddSeconds(timestamp);
  791. }
  792. public static double ConvertToAppleTimeStamp(DateTime date)
  793. {
  794. DateTime begin = new DateTime(2001, 1, 1, 0, 0, 0, 0);
  795. TimeSpan diff = date - begin;
  796. return Math.Floor(diff.TotalSeconds);
  797. }
  798. }
  799. }