/// <summary> /// Reads the texts and interprets as PList definition. /// </summary> public static PropertyList Read(TextReader text) { try { XmlReaderSettings settings = new XmlReaderSettings(); #if NET_2_COMPATIBLE && !PocketPC && !WINDOWS_PHONE // obsolete, but will cause an exception at start-up if not set to false (crazy!) settings.ProhibitDtd = false; #endif #if !NET_2_COMPATIBLE settings.DtdProcessing = DtdProcessing.Ignore; #endif settings.IgnoreComments = true; settings.IgnoreProcessingInstructions = true; settings.IgnoreWhitespace = true; Version resultVersion = null; IPropertyListItem result = null; int intNumber; double doubleNumber; string key = null; Stack <IPropertyListItem> items = new Stack <IPropertyListItem>(); using (XmlReader reader = XmlReader.Create(text, settings)) { while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: if (reader.Name == "plist") { if (resultVersion != null) { throw new FormatException("Secondary plist definition not expected"); } if (reader.MoveToAttribute("version")) { if (reader.ReadAttributeValue()) { resultVersion = new Version(reader.Value); } } break; } if (reader.Name == "key") { if (key != null) { throw new FormatException("Two keys in a row are not expected"); } if (reader.Read()) { key = reader.Value; } break; } if (reader.Name == "dict") { if (key == null) { if (result == null) { result = new PropertyListDictionary(null); if (!reader.IsEmptyElement) { items.Push(result); } break; } if (items.Peek().Type != PropertyListItemTypes.Array) { throw new FormatException("Found dictionary definition without key name"); } } // add new dictionary to the top collection: var newDictionary = items.Peek().AddNewDictionary(key); if (!reader.IsEmptyElement) { items.Push(newDictionary); } key = null; break; } if (reader.Name == "array") { if (key == null) { if (result == null) { result = new PropertyListArray(null); if (!reader.IsEmptyElement) { items.Push(result); } break; } if (items.Peek().Type != PropertyListItemTypes.Array) { throw new FormatException("Found array definition without key name"); } } // add new array to the top collection: var newArray = items.Peek().AddNewArray(key); if (!reader.IsEmptyElement) { items.Push(newArray); } key = null; break; } if (reader.Name == "string") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found string definition without key name"); } if (reader.Read()) { items.Peek().Add(key, reader.Value); } key = null; break; } if (reader.Name == "integer") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found integer definition without key name"); } if (reader.Read()) { if (NumericHelper.TryParseInt32(reader.Value, out intNumber)) { items.Peek().Add(key, intNumber); } else { throw new FormatException("Can not parse number: '" + reader.Value + "'"); } } key = null; break; } if (reader.Name == "real") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found real definition without key name"); } if (reader.Read()) { if (NumericHelper.TryParseDouble(reader.Value, NumberStyles.Float, out doubleNumber)) { items.Peek().Add(key, doubleNumber); } else { throw new FormatException("Can not parse number: '" + reader.Value + "'"); } } key = null; break; } if (reader.Name == "true") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found boolean definition without key name"); } items.Peek().Add(key, true); key = null; break; } if (reader.Name == "false") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found boolean definition without key name"); } items.Peek().Add(key, false); key = null; break; } if (reader.Name == "date") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found date definition without key name"); } if (reader.Read()) { items.Peek().Add(key, DateTime.Parse(reader.Value, CultureInfo.InvariantCulture)); } key = null; break; } if (reader.Name == "data") { if (key == null && items.Peek().Type == PropertyListItemTypes.Dictionary) { throw new FormatException("Found data definition without key name"); } if (reader.Read()) { items.Peek().Add(key, Convert.FromBase64String(reader.Value)); } key = null; break; } break; case XmlNodeType.EndElement: if (EndElement(items, reader.Name, "dict", PropertyListItemTypes.Dictionary)) { break; } if (EndElement(items, reader.Name, "array", PropertyListItemTypes.Array)) { break; } break; } } } if (items.Count > 0) { throw new FormatException("Not all items have been closed"); } return(new PropertyList(result, resultVersion)); } catch (Exception ex) { DebugLog.WriteCoreException(ex); throw; } }
object ParseObject(ulong objectIndex) { Seek(offsetTable [objectIndex]); var header = reader.ReadByte(); var kind = (MarkerKind)(header & 0xF0); var subKind = (MarkerKind)(header); var length = header & 0x0F; switch ((MarkerKind)header) { case MarkerKind.Date: return(PropertyListDate.FromAbsoluteTime(ReadSizedDouble(8))); } switch (kind) { case MarkerKind.Data: case MarkerKind.AsciiString: case MarkerKind.Unicode16String: case MarkerKind.Array: case MarkerKind.Set: case MarkerKind.Dict: if (length == 0xF) { length = (int)ReadInt(); } break; } switch (kind) { case MarkerKind.Null: switch (subKind) { case MarkerKind.Null: case MarkerKind.Fill: return(null); case MarkerKind.False: return(false); case MarkerKind.True: return(true); } break; case MarkerKind.Uid: var buffer = new byte [16]; reader.Read(buffer, 0, length + 1); return(new Guid(buffer)); case MarkerKind.Int: return((long)ReadSizedInt(1UL << length)); case MarkerKind.Real: return(ReadSizedDouble(1UL << length)); case MarkerKind.Data: return(reader.ReadBytes(length)); case MarkerKind.AsciiString: var strBytes = reader.ReadBytes(length); return(Encoding.ASCII.GetString(strBytes)); case MarkerKind.Unicode16String: var uniBytes = reader.ReadBytes(length); return(Encoding.BigEndianUnicode.GetString(uniBytes)); case MarkerKind.Array: case MarkerKind.Set: ICollection <object> array; if (kind == MarkerKind.Array) { array = new PropertyListArray(); } else { array = new PropertyListSet(); } var arrayStart = (ulong)stream.Position; for (var i = 0UL; i < (ulong)length; i++) { Seek(arrayStart + i * trailer.ObjectRefSize); var valueRef = ReadSizedInt(trailer.ObjectRefSize); array.Add(ParseObject(valueRef)); } return((object)array); case MarkerKind.Dict: var dict = new PropertyListDictionary(); var dictStart = (ulong)stream.Position; for (var i = 0UL; i < (ulong)length; i++) { Seek(dictStart + i * trailer.ObjectRefSize); var keyRef = ReadSizedInt(trailer.ObjectRefSize); Seek(dictStart + i * trailer.ObjectRefSize + (ulong)length * trailer.ObjectRefSize); var valueRef = ReadSizedInt(trailer.ObjectRefSize); dict.Add((string)ParseObject(keyRef), ParseObject(valueRef)); } return(dict); } Fatal("Unhandled object kind: {0}", kind); return(null); }