// n = {4, 8} public static double ReadNBytesReal(byte[] bytes, int n, ulong at) { switch (n) { case 4: var f32 = new Float32Bits(bytes, at); return(f32.Value); case 8: var f64 = new Float64Bits(bytes, at); return(f64.Value); default: throw new PlistException("undefined byte size"); } }
private static ulong WriteObjectBinary(Stream stream, object root, byte objectRefSize, byte[] encodeBuffer, ref ulong objectIndex, List <ulong> offsetTable) { ulong objectRef = objectIndex++; ulong objectAt = (ulong)stream.Position; offsetTable.Add(objectAt); if (root == null) { throw new PlistException("unsupported null"); } else if (root is bool) { if ((bool)root) { stream.WriteByte(TypeBitsBoolOrNil | TrueBit); } else { stream.WriteByte(TypeBitsBoolOrNil | FalseBit); } } else if (root is int) { WriteInteger((int)root, stream); } else if (root is double) { // real, 2^3 = 8 byte // 0010 0011 // force use double byte headbyte = TypeBitsReal | 0x03; stream.WriteByte(headbyte); var f64 = new Float64Bits((double)root); f64.Write(stream); } else if (root is DateTime) { DateTime date = (DateTime)root; TimeSpan elapsedSpan = date.ToUniversalTime() - BaseTime; double elapsed = elapsedSpan.TotalSeconds; byte headbyte = TypeBitsDate | 0x03; stream.WriteByte(headbyte); var f64 = new Float64Bits(elapsed); f64.Write(stream); } else if (root is string) { string text = root as string; byte typebit; int count; int writeBytes; if (IsASCIIEncodable(text)) { typebit = TypeBitsASCIIString; writeBytes = ASCII.GetBytes(text, 0, text.Length, encodeBuffer, 0); count = writeBytes; } else { typebit = TypeBitsUTF16String; writeBytes = UTF16BE.GetBytes(text, 0, text.Length, encodeBuffer, 0); count = writeBytes >> 1; } if (count < 15) { byte headbyte = (byte)(typebit | count); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger((long)count, stream); } stream.Write(encodeBuffer, 0, writeBytes); } else if (root is byte[]) { byte[] data = root as byte[]; byte typebit = TypeBitsBinaryData; if (data.Length < 15) { byte headbyte = (byte)(typebit | data.Length); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger(data.LongLength, stream); } stream.Write(data, 0, data.Length); } else if (root is PlistList) { PlistList objectArray = root as PlistList; byte typebit = TypeBitsArray; if (objectArray.Count < 15) { byte headbyte = (byte)(typebit | objectArray.Count); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger(objectArray.Count, stream); } ulong arrayAt = (ulong)stream.Position; // reserve data ulong reserveBytes = (ulong)objectArray.Count * objectRefSize; for (ulong i = 0; i < reserveBytes; ++i) { stream.WriteByte(0); } for (int i = 0; i < objectArray.Count; ++i) { ulong arrayObjectRef = WriteObjectBinary(stream, objectArray[i], objectRefSize, encodeBuffer, ref objectIndex, offsetTable); stream.Seek((long)arrayAt + i * objectRefSize, SeekOrigin.Begin); BigEndianWriter.WriteNBytesUnsignedInteger(stream, arrayObjectRef, objectRefSize); stream.Seek(0, SeekOrigin.End); } } else if (root is PlistDictionary) { PlistDictionary objectDictionary = root as PlistDictionary; byte typebit = TypeBitsDictionary; if (objectDictionary.Count < 15) { byte headbyte = (byte)(typebit | objectDictionary.Count); stream.WriteByte(headbyte); } else { byte headbyte = (byte)(typebit | 0x0F); stream.WriteByte(headbyte); WriteInteger(objectDictionary.Count, stream); } ulong dictionaryAt = (ulong)stream.Position; // reserve data ulong reserveBytes = (ulong)objectDictionary.Count * 2 * objectRefSize; for (ulong j = 0; j < reserveBytes; ++j) { stream.WriteByte(0); } /* key, key, key, value, value, value... */ long keyBytes = objectRefSize * (long)objectDictionary.Count; int i = 0; foreach (var objectKeyValue in objectDictionary) { ulong arrayKeyRef = WriteObjectBinary(stream, objectKeyValue.Key, objectRefSize, encodeBuffer, ref objectIndex, offsetTable); ulong arrayValueRef = WriteObjectBinary(stream, objectKeyValue.Value, objectRefSize, encodeBuffer, ref objectIndex, offsetTable); stream.Seek((long)dictionaryAt + i * objectRefSize, SeekOrigin.Begin); BigEndianWriter.WriteNBytesUnsignedInteger(stream, arrayKeyRef, objectRefSize); stream.Seek((long)dictionaryAt + keyBytes + i * objectRefSize, SeekOrigin.Begin); BigEndianWriter.WriteNBytesUnsignedInteger(stream, arrayValueRef, objectRefSize); stream.Seek(0, SeekOrigin.End); i++; } } else { throw new PlistException(); } return(objectRef); }