Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit f64f1157f9dc0ed6887504426c93783ee6255038 @ChadBurggraf committed Mar 14, 2011
Showing with 2,609 additions and 0 deletions.
  1. +8 −0 .gitignore
  2. +26 −0 LICENSE.txt
  3. +2 −0 README.md
  4. +10 −0 Source/LocalTestRun.testrunconfig
  5. +23 −0 Source/SolutionInfo.cs
  6. +266 −0 Source/System.Runtime.Serialization.Plists.Test/BinaryPlistTests.cs
  7. BIN Source/System.Runtime.Serialization.Plists.Test/Calculator.plist
  8. BIN Source/System.Runtime.Serialization.Plists.Test/Nested.plist
  9. +16 −0 Source/System.Runtime.Serialization.Plists.Test/Properties/AssemblyInfo.cs
  10. +9 −0 Source/System.Runtime.Serialization.Plists.Test/Settings.StyleCop
  11. +68 −0 Source/System.Runtime.Serialization.Plists.Test/System.Runtime.Serialization.Plists.Test.csproj
  12. BIN Source/System.Runtime.Serialization.Plists.Test/Types.plist
  13. +39 −0 Source/System.Runtime.Serialization.Plists.sln
  14. +6 −0 Source/System.Runtime.Serialization.Plists.vsmdi
  15. +125 −0 Source/System.Runtime.Serialization.Plists/BinaryPlistArray.cs
  16. +174 −0 Source/System.Runtime.Serialization.Plists/BinaryPlistDictionary.cs
  17. +543 −0 Source/System.Runtime.Serialization.Plists/BinaryPlistReader.cs
  18. +682 −0 Source/System.Runtime.Serialization.Plists/BinaryPlistWriter.cs
  19. +331 −0 Source/System.Runtime.Serialization.Plists/EndianConverter.cs
  20. +55 −0 Source/System.Runtime.Serialization.Plists/Extensions.cs
  21. +36 −0 Source/System.Runtime.Serialization.Plists/IPlistSerializable.cs
  22. +17 −0 Source/System.Runtime.Serialization.Plists/Properties/AssemblyInfo.cs
  23. +9 −0 Source/System.Runtime.Serialization.Plists/Settings.StyleCop
  24. +67 −0 Source/System.Runtime.Serialization.Plists/System.Runtime.Serialization.Plists.csproj
  25. +47 −0 System.Runtime.Serialization.Plists.FxCop
  26. +3 −0 build.bat
  27. +47 −0 build.proj
8 .gitignore
@@ -0,0 +1,8 @@
+*.csproj.user
+*.suo
+*.snk
+StyleCop.Cache
+bin/
+obj/
+Build/
+TestResults/
26 LICENSE.txt
@@ -0,0 +1,26 @@
+System.Runtime.Serialization.Plists
+Authors:
+ Chad Burggraf <chad.burggraf@gmail.com>
+
+Copyright (c) 2011 Chad Burggraf
+
+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.
2 README.md
@@ -0,0 +1,2 @@
+# Serialization.Plists
+#### Plist serialization and de-serialization with C# and .NET
10 Source/LocalTestRun.testrunconfig
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<TestRunConfiguration name="Local Test Run" id="4c6ea626-eb6d-4822-a3c7-905de6cf1311" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006">
+ <Description>This is a default test run configuration for a local test run.</Description>
+ <Deployment>
+ <DeploymentItem filename="System.Runtime.Serialization.Plists.Test\Nested.plist" />
+ <DeploymentItem filename="System.Runtime.Serialization.Plists.Test\Calculator.plist" />
+ <DeploymentItem filename="System.Runtime.Serialization.Plists.Test\Types.plist" />
+ </Deployment>
+ <TestTypeSpecific />
+</TestRunConfiguration>
23 Source/SolutionInfo.cs
@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------
+// <copyright file="SolutionInfo.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Tasty Codes")]
+[assembly: AssemblyCopyright("Copyright © Tasty Codes 2010")]
+[assembly: AssemblyProduct("System.Runtime.Serialization.Plists")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: CLSCompliant(true)]
+[assembly: ComVisible(false)]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+
+[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Signed during a release build.")][assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "Assembly", Target = "System.Runtime.Serialization.Plists.dll", Justification = "The spelling is correct.")]
266 Source/System.Runtime.Serialization.Plists.Test/BinaryPlistTests.cs
@@ -0,0 +1,266 @@
+//-----------------------------------------------------------------------
+// <copyright file="BinaryPlistTests.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace System.Runtime.Serialization.Plists.Test
+{
+ using System;
+ using System.Collections;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ /// <summary>
+ /// Unit tests for binary plist serialization.
+ /// </summary>
+ [TestClass]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public class BinaryPlistTests
+ {
+ private const string CalculatorPListPath = "Calculator.plist";
+ private const string NestedPListpath = "Nested.plist";
+ private const string TypesPListPath = "Types.plist";
+
+ /// <summary>
+ /// Read object from Calculator.plist tests.
+ /// </summary>
+ [TestMethod]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public void BinaryPlistReadObjectCalculator()
+ {
+ BinaryPlistReader reader = new BinaryPlistReader();
+ IDictionary dictionary;
+
+ using (Stream stream = File.OpenRead(CalculatorPListPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ Assert.AreEqual(5, dictionary.Count);
+ Assert.AreEqual("520 71 228 289 0 0 1440 878 ", dictionary["NSWindow Frame Calc_History_Window"]);
+ Assert.AreEqual("368 425 423 283 0 0 1440 878 ", dictionary["NSWindow Frame Calc_Main_Window"]);
+ Assert.AreEqual(true, dictionary["Programmer_BinaryIsHidden"]);
+ Assert.AreEqual((short)10, dictionary["Programmer_InputMode"]);
+ Assert.AreEqual("Scientific", dictionary["ViewDefaultsKey"]);
+ }
+
+ /// <summary>
+ /// Read object from Nested.plist tests.
+ /// </summary>
+ [TestMethod]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public void BinaryPlistReadObjectNested()
+ {
+ BinaryPlistReader reader = new BinaryPlistReader();
+ IDictionary dictionary;
+
+ using (Stream stream = File.OpenRead(NestedPListpath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ Assert.AreEqual(2, dictionary.Count);
+ Assert.IsInstanceOfType(dictionary["Array"], typeof(object[]));
+ Assert.IsInstanceOfType(dictionary["Dictionary"], typeof(IDictionary));
+
+ object[] arr = dictionary["Array"] as object[];
+ IDictionary dict = dictionary["Dictionary"] as IDictionary;
+ Assert.AreEqual(3, arr.Length);
+ Assert.AreEqual(2, dictionary.Count);
+
+ Assert.AreEqual("First", arr[0]);
+ Assert.AreEqual("Second", arr[1]);
+ Assert.AreEqual("Third", arr[2]);
+
+ Assert.AreEqual(1.0000000000000011, dict["Double"]);
+ Assert.AreEqual(4, ((object[])dict["Array"]).Length);
+
+ arr = dict["Array"] as object[];
+ Assert.AreEqual("1", arr[0]);
+ Assert.AreEqual((short)2, arr[1]);
+ Assert.AreEqual(true, arr[2]);
+ Assert.AreEqual("4", arr[3]);
+ }
+
+ /// <summary>
+ /// Read object from Types.plist tests.
+ /// </summary>
+ [TestMethod]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public void BinaryPlistReadObjectTypes()
+ {
+ BinaryPlistReader reader = new BinaryPlistReader();
+ IDictionary dictionary;
+
+ using (Stream stream = File.OpenRead(TypesPListPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ Assert.AreEqual(7, dictionary.Count);
+ Assert.AreEqual("¡™£¢∞§¶•ªº–≠πøˆ¨¥†®´∑œ", dictionary["Unicode"]);
+ Assert.AreEqual(false, dictionary["False"]);
+ Assert.IsInstanceOfType(dictionary["Data"], typeof(byte[]));
+ Assert.AreEqual(new DateTime(1982, 05, 28, 7, 0, 0), dictionary["Date"]);
+ Assert.AreEqual(true, dictionary["True"]);
+ Assert.AreEqual(3.14159, dictionary["Pi"]);
+ Assert.AreEqual("World", dictionary["Hello"]);
+ }
+
+ /// <summary>
+ /// Write object from Calculator.plist tests.
+ /// </summary>
+ [TestMethod]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public void BinaryPlistWriteObjectCalculator()
+ {
+ string outputPath = Guid.NewGuid().ToString() + ".plist";
+ BinaryPlistReader reader = new BinaryPlistReader();
+ IDictionary dictionary;
+
+ using (Stream stream = File.OpenRead(CalculatorPListPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ dictionary["NSWindow Frame Calc_History_Window"] = "620 71 228 289 0 0 1440 870 ";
+ dictionary["NSWindow Frame Calc_Main_Window"] = "668 425 423 283 0 0 1440 870 ";
+ dictionary["Programmer_BinaryIsHidden"] = false;
+ dictionary["Programmer_InputMode"] = 11;
+ dictionary["ViewDefaultsKey"] = "Religious";
+
+ BinaryPlistWriter writer = new BinaryPlistWriter();
+
+ using (Stream stream = File.Create(outputPath))
+ {
+ writer.WriteObject(stream, dictionary);
+ }
+
+ using (Stream stream = File.OpenRead(outputPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ Assert.AreEqual(5, dictionary.Count);
+ Assert.AreEqual("620 71 228 289 0 0 1440 870 ", dictionary["NSWindow Frame Calc_History_Window"]);
+ Assert.AreEqual("668 425 423 283 0 0 1440 870 ", dictionary["NSWindow Frame Calc_Main_Window"]);
+ Assert.AreEqual(false, dictionary["Programmer_BinaryIsHidden"]);
+ Assert.AreEqual((short)11, dictionary["Programmer_InputMode"]);
+ Assert.AreEqual("Religious", dictionary["ViewDefaultsKey"]);
+ }
+
+ /// <summary>
+ /// Write object from Nested.plist tests.
+ /// </summary>
+ [TestMethod]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public void BinaryPlistWriteObjectNested()
+ {
+ string outputPath = Guid.NewGuid().ToString() + ".plist";
+ BinaryPlistReader reader = new BinaryPlistReader();
+ IDictionary dictionary;
+
+ using (Stream stream = File.OpenRead(NestedPListpath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ object[] arr = dictionary["Array"] as object[];
+ IDictionary dict = dictionary["Dictionary"] as IDictionary;
+
+ arr[0] = "1st";
+ arr[1] = "2nd";
+ arr[2] = "3rd";
+
+ dict["Double"] = 2.0000000000000011;
+
+ arr = dict["Array"] as object[];
+ arr[0] = "One";
+ arr[1] = 3;
+ arr[2] = false;
+ arr[3] = "Four";
+
+ BinaryPlistWriter writer = new BinaryPlistWriter();
+
+ using (Stream stream = File.Create(outputPath))
+ {
+ writer.WriteObject(stream, dictionary);
+ }
+
+ using (Stream stream = File.OpenRead(outputPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ Assert.AreEqual(2, dictionary.Count);
+ Assert.IsInstanceOfType(dictionary["Array"], typeof(object[]));
+ Assert.IsInstanceOfType(dictionary["Dictionary"], typeof(IDictionary));
+
+ arr = dictionary["Array"] as object[];
+ dict = dictionary["Dictionary"] as IDictionary;
+ Assert.AreEqual(3, arr.Length);
+ Assert.AreEqual(2, dictionary.Count);
+
+ Assert.AreEqual("1st", arr[0]);
+ Assert.AreEqual("2nd", arr[1]);
+ Assert.AreEqual("3rd", arr[2]);
+
+ Assert.AreEqual(2.0000000000000011, dict["Double"]);
+ Assert.AreEqual(4, ((object[])dict["Array"]).Length);
+
+ arr = dict["Array"] as object[];
+ Assert.AreEqual("One", arr[0]);
+ Assert.AreEqual((short)3, arr[1]);
+ Assert.AreEqual(false, arr[2]);
+ Assert.AreEqual("Four", arr[3]);
+ }
+
+ /// <summary>
+ /// Write object from Types.plist tests.
+ /// </summary>
+ [TestMethod]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public void BinaryPlistWriteObjectTypes()
+ {
+ string outputPath = Guid.NewGuid().ToString() + ".plist";
+ BinaryPlistReader reader = new BinaryPlistReader();
+ IDictionary dictionary;
+
+ using (Stream stream = File.OpenRead(TypesPListPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ dictionary["Unicode"] = "ºª•¶§∞¢£™¬˚∆¨˙©ƒ´ßƒç";
+ dictionary["False"] = true;
+ dictionary["Data"] = new byte[3];
+ dictionary["Date"] = new DateTime(2011, 3, 13);
+ dictionary["True"] = false;
+ dictionary["Pi"] = 2.71828;
+ dictionary["Hello"] = "Japan";
+
+ BinaryPlistWriter writer = new BinaryPlistWriter();
+
+ using (Stream stream = File.Create(outputPath))
+ {
+ writer.WriteObject(stream, dictionary);
+ }
+
+ using (Stream stream = File.OpenRead(outputPath))
+ {
+ dictionary = reader.ReadObject(stream);
+ }
+
+ Assert.AreEqual(7, dictionary.Count);
+ Assert.AreEqual("ºª•¶§∞¢£™¬˚∆¨˙©ƒ´ßƒç", dictionary["Unicode"]);
+ Assert.AreEqual(true, dictionary["False"]);
+ Assert.IsInstanceOfType(dictionary["Data"], typeof(byte[]));
+ Assert.AreEqual(new DateTime(2011, 3, 13), dictionary["Date"]);
+ Assert.AreEqual(false, dictionary["True"]);
+ Assert.AreEqual(2.71828, dictionary["Pi"]);
+ Assert.AreEqual("Japan", dictionary["Hello"]);
+ }
+ }
+}
BIN Source/System.Runtime.Serialization.Plists.Test/Calculator.plist
Binary file not shown.
BIN Source/System.Runtime.Serialization.Plists.Test/Nested.plist
Binary file not shown.
16 Source/System.Runtime.Serialization.Plists.Test/Properties/AssemblyInfo.cs
@@ -0,0 +1,16 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssemblyInfo.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("System.Runtime.Serialization.Plists.Test")]
+[assembly: AssemblyDescription("Unit tests for System.Runtime.Serialization.Plists.dll.")]
+[assembly: Guid("209b5f61-bf17-4cb4-bb06-30934f27c431")]
+
+[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "Assembly", Target = "System.Runtime.Serialization.Plists.Test.dll", Justification = "The spelling is correct.")]
+[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "Namespace", Target = "System.Runtime.Serialization.Plists.Test", Justification = "The spelling is correct.")]
9 Source/System.Runtime.Serialization.Plists.Test/Settings.StyleCop
@@ -0,0 +1,9 @@
+<StyleCopSettings Version="4.3">
+ <Analyzers>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.DocumentationRules">
+ <AnalyzerSettings>
+ <BooleanProperty Name="IncludeFields">False</BooleanProperty>
+ </AnalyzerSettings>
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings>
68 .../System.Runtime.Serialization.Plists.Test/System.Runtime.Serialization.Plists.Test.csproj
@@ -0,0 +1,68 @@
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{4B67173A-8676-484A-AAC1-7DEB22B06D3A}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>System.Runtime.Serialization.Plists.Test</RootNamespace>
+ <AssemblyName>System.Runtime.Serialization.Plists.Test</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;CODE_ANALYSIS</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\SolutionInfo.cs">
+ <Link>Properties\SolutionInfo.cs</Link>
+ </Compile>
+ <Compile Include="BinaryPlistTests.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\System.Runtime.Serialization.Plists\System.Runtime.Serialization.Plists.csproj">
+ <Project>{75220789-BE0B-4A41-8D8D-865E757A76FB}</Project>
+ <Name>System.Runtime.Serialization.Plists</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Calculator.plist" />
+ <None Include="Nested.plist" />
+ <None Include="Types.plist" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
BIN Source/System.Runtime.Serialization.Plists.Test/Types.plist
Binary file not shown.
39 Source/System.Runtime.Serialization.Plists.sln
@@ -0,0 +1,39 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Serialization.Plists", "System.Runtime.Serialization.Plists\System.Runtime.Serialization.Plists.csproj", "{75220789-BE0B-4A41-8D8D-865E757A76FB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Serialization.Plists.Test", "System.Runtime.Serialization.Plists.Test\System.Runtime.Serialization.Plists.Test.csproj", "{4B67173A-8676-484A-AAC1-7DEB22B06D3A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{52D05851-466A-4101-B65C-33DCC7DF62AF}"
+ ProjectSection(SolutionItems) = preProject
+ ..\build.proj = ..\build.proj
+ ..\LICENSE.txt = ..\LICENSE.txt
+ LocalTestRun.testrunconfig = LocalTestRun.testrunconfig
+ ..\README.md = ..\README.md
+ SolutionInfo.cs = SolutionInfo.cs
+ System.Runtime.Serialization.Plists.vsmdi = System.Runtime.Serialization.Plists.vsmdi
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(TestCaseManagementSettings) = postSolution
+ CategoryFile = System.Runtime.Serialization.Plists.vsmdi
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {75220789-BE0B-4A41-8D8D-865E757A76FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75220789-BE0B-4A41-8D8D-865E757A76FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75220789-BE0B-4A41-8D8D-865E757A76FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75220789-BE0B-4A41-8D8D-865E757A76FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4B67173A-8676-484A-AAC1-7DEB22B06D3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4B67173A-8676-484A-AAC1-7DEB22B06D3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4B67173A-8676-484A-AAC1-7DEB22B06D3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4B67173A-8676-484A-AAC1-7DEB22B06D3A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
6 Source/System.Runtime.Serialization.Plists.vsmdi
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006">
+ <TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
+ <RunConfiguration id="4c6ea626-eb6d-4822-a3c7-905de6cf1311" name="Local Test Run" storage="localtestrun.testrunconfig" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, PublicKeyToken=b03f5f7f11d50a3a" />
+ </TestList>
+</TestLists>
125 Source/System.Runtime.Serialization.Plists/BinaryPlistArray.cs
@@ -0,0 +1,125 @@
+//-----------------------------------------------------------------------
+// <copyright file="BinaryPlistArray.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// Inspired by BinaryPListParser.java, copyright (c) 2005 Werner Randelshofer
+// http://www.java2s.com/Open-Source/Java-Document/Swing-Library/jide-common/com/jidesoft/plaf/aqua/BinaryPListParser.java.htm
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace System.Runtime.Serialization.Plists
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Text;
+
+ /// <summary>
+ /// Represents an array value in a binary plist.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ internal class BinaryPlistArray
+ {
+ /// <summary>
+ /// Initializes a new instance of the BinaryPlistArray class.
+ /// </summary>
+ /// <param name="objectTable">A reference to the binary plist's object table.</param>
+ public BinaryPlistArray(IList<object> objectTable)
+ : this(objectTable, 0)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the BinaryPlistArray class.
+ /// </summary>
+ /// <param name="objectTable">A reference to the binary plist's object table.</param>
+ /// <param name="size">The size of the array.</param>
+ public BinaryPlistArray(IList<object> objectTable, int size)
+ {
+ this.ObjectReference = new List<int>(size);
+ this.ObjectTable = objectTable;
+ }
+
+ /// <summary>
+ /// Gets the array's object reference collection.
+ /// </summary>
+ public IList<int> ObjectReference { get; private set; }
+
+ /// <summary>
+ /// Gets a reference to the binary plist's object table.
+ /// </summary>
+ public IList<object> ObjectTable { get; private set; }
+
+ /// <summary>
+ /// Converts this instance into an <see cref="T:object[]"/> array.
+ /// </summary>
+ /// <returns>The <see cref="T:object[]"/> array representation of this instance.</returns>
+ public object[] ToArray()
+ {
+ object[] array = new object[this.ObjectReference.Count];
+ int objectRef;
+ object objectValue;
+ BinaryPlistArray innerArray;
+ BinaryPlistDictionary innerDict;
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ objectRef = this.ObjectReference[i];
+
+ if (objectRef >= 0 && objectRef < this.ObjectTable.Count && this.ObjectTable[objectRef] != this)
+ {
+ objectValue = this.ObjectTable[objectRef];
+ innerDict = objectValue as BinaryPlistDictionary;
+
+ if (innerDict != null)
+ {
+ objectValue = innerDict.ToDictionary();
+ }
+ else
+ {
+ innerArray = objectValue as BinaryPlistArray;
+
+ if (innerArray != null)
+ {
+ objectValue = innerArray.ToArray();
+ }
+ }
+
+ array[i] = objectValue;
+ }
+ }
+
+ return array;
+ }
+
+ /// <summary>
+ /// Returns the string representation of this instance.
+ /// </summary>
+ /// <returns>This instance's string representation.</returns>
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder("[");
+ int objectRef;
+
+ for (int i = 0; i < this.ObjectReference.Count; i++)
+ {
+ if (i > 0)
+ {
+ sb.Append(",");
+ }
+
+ objectRef = this.ObjectReference[i];
+
+ if (this.ObjectTable.Count > objectRef && this.ObjectTable[objectRef] != this)
+ {
+ sb.Append(this.ObjectReference[objectRef]);
+ }
+ else
+ {
+ sb.Append("*" + objectRef);
+ }
+ }
+
+ return sb.ToString() + "]";
+ }
+ }
+}
174 Source/System.Runtime.Serialization.Plists/BinaryPlistDictionary.cs
@@ -0,0 +1,174 @@
+//-----------------------------------------------------------------------
+// <copyright file="BinaryPlistDictionary.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// Inspired by BinaryPListParser.java, copyright (c) 2005 Werner Randelshofer
+// http://www.java2s.com/Open-Source/Java-Document/Swing-Library/jide-common/com/jidesoft/plaf/aqua/BinaryPListParser.java.htm
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace System.Runtime.Serialization.Plists
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Text;
+
+ /// <summary>
+ /// Represents a dictionary in a binary plist.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ internal class BinaryPlistDictionary
+ {
+ /// <summary>
+ /// Initializes a new instance of the BinaryPlistDictionary class.
+ /// </summary>
+ /// <param name="objectTable">A reference to the binary plist's object table.</param>
+ public BinaryPlistDictionary(IList<object> objectTable)
+ : this(objectTable, 0)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the BinaryPlistDictionary class.
+ /// </summary>
+ /// <param name="objectTable">A reference to the binary plist's object table.</param>
+ /// <param name="size">The size of the dictionay.</param>
+ public BinaryPlistDictionary(IList<object> objectTable, int size)
+ {
+ this.KeyReference = new List<int>(size);
+ this.ObjectReference = new List<int>(size);
+ this.ObjectTable = objectTable;
+ }
+
+ /// <summary>
+ /// Gets the dictionary's key reference collection.
+ /// </summary>
+ public IList<int> KeyReference { get; private set; }
+
+ /// <summary>
+ /// Gets the dictionary's object reference collection.
+ /// </summary>
+ public IList<int> ObjectReference { get; private set; }
+
+ /// <summary>
+ /// Gets a reference to the binary plist's object table.
+ /// </summary>
+ public IList<object> ObjectTable { get; private set; }
+
+ /// <summary>
+ /// Gets the key at the specified index as a string.
+ /// </summary>
+ /// <param name="index">The index in the key reference collection to get the key at.</param>
+ /// <returns>The specified key as a string.</returns>
+ public string GetKey(int index)
+ {
+ return this.ObjectTable[this.KeyReference[index]].ToString();
+ }
+
+ /// <summary>
+ /// Gets the object value at the specified index.
+ /// </summary>
+ /// <param name="index">The index in the object reference collection to get the object value at.</param>
+ /// <returns>The specified object value.</returns>
+ public object GetValue(int index)
+ {
+ return this.ObjectTable[this.ObjectReference[index]];
+ }
+
+ /// <summary>
+ /// Converts this instance into a <see cref="Dictionary{Object, Object}"/>.
+ /// </summary>
+ /// <returns>A <see cref="Dictionary{Object, Object}"/> representation this instance.</returns>
+ public Dictionary<object, object> ToDictionary()
+ {
+ Dictionary<object, object> dictionary = new Dictionary<object, object>();
+ int keyRef, objectRef;
+ object objectValue;
+ BinaryPlistArray innerArray;
+ BinaryPlistDictionary innerDict;
+
+ for (int i = 0; i < this.KeyReference.Count; i++)
+ {
+ keyRef = this.KeyReference[i];
+ objectRef = this.ObjectReference[i];
+
+ if (keyRef >= 0 && keyRef < this.ObjectTable.Count && this.ObjectTable[keyRef] != this &&
+ objectRef >= 0 && objectRef < this.ObjectTable.Count && this.ObjectTable[objectRef] != this)
+ {
+ objectValue = this.ObjectTable[objectRef];
+ innerDict = objectValue as BinaryPlistDictionary;
+
+ if (innerDict != null)
+ {
+ objectValue = innerDict.ToDictionary();
+ }
+ else
+ {
+ innerArray = objectValue as BinaryPlistArray;
+
+ if (innerArray != null)
+ {
+ objectValue = innerArray.ToArray();
+ }
+ }
+
+ dictionary[this.ObjectTable[keyRef]] = objectValue;
+ }
+ }
+
+ return dictionary;
+ }
+
+ /// <summary>
+ /// Returns the string representation of this instance.
+ /// </summary>
+ /// <returns>This instance's string representation.</returns>
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder("{");
+ int keyRef, objectRef;
+
+ for (int i = 0; i < this.KeyReference.Count; i++)
+ {
+ if (i > 0)
+ {
+ sb.Append(",");
+ }
+
+ keyRef = this.KeyReference[i];
+ objectRef = this.ObjectReference[i];
+
+ if (keyRef < 0 || keyRef >= this.ObjectTable.Count)
+ {
+ sb.Append("#" + keyRef);
+ }
+ else if (this.ObjectTable[keyRef] == this)
+ {
+ sb.Append("*" + keyRef);
+ }
+ else
+ {
+ sb.Append(this.ObjectTable[keyRef]);
+ }
+
+ sb.Append(":");
+
+ if (objectRef < 0 || objectRef >= this.ObjectTable.Count)
+ {
+ sb.Append("#" + objectRef);
+ }
+ else if (this.ObjectTable[objectRef] == this)
+ {
+ sb.Append("*" + objectRef);
+ }
+ else
+ {
+ sb.Append(this.ObjectTable[objectRef]);
+ }
+ }
+
+ return sb.ToString() + "}";
+ }
+ }
+}
543 Source/System.Runtime.Serialization.Plists/BinaryPlistReader.cs
@@ -0,0 +1,543 @@
+//-----------------------------------------------------------------------
+// <copyright file="BinaryPlistReader.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// Inspired by BinaryPListParser.java, copyright (c) 2005 Werner Randelshofer
+// http://www.java2s.com/Open-Source/Java-Document/Swing-Library/jide-common/com/jidesoft/plaf/aqua/BinaryPListParser.java.htm
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace System.Runtime.Serialization.Plists
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Text;
+
+ /// <summary>
+ /// Performs de-serialization of binary plists.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public sealed class BinaryPlistReader
+ {
+ #region Private Fields
+
+ private List<object> objectTable;
+ private List<int> offsetTable;
+ private int offsetIntSize, objectRefSize, objectCount, topLevelObjectOffset, offsetTableOffset;
+
+ #endregion
+
+ #region Public Instance Methods
+
+ /// <summary>
+ /// Reads a binary plist from the given file path into an <see cref="IDictionary"/>.
+ /// </summary>
+ /// <param name="path">The path of the file to read.</param>
+ /// <returns>The result plist <see cref="IDictionary"/>.</returns>
+ public IDictionary ReadObject(string path)
+ {
+ using (FileStream stream = File.OpenRead(path))
+ {
+ return this.ReadObject(stream);
+ }
+ }
+
+ /// <summary>
+ /// Reads a binary plist from the given stream into an <see cref="IDictionary"/>.
+ /// </summary>
+ /// <param name="stream">The <see cref="Stream"/> to read.</param>
+ /// <returns>The result plist <see cref="IDictionary"/>.</returns>
+ public IDictionary ReadObject(Stream stream)
+ {
+ if (stream == null)
+ {
+ throw new ArgumentNullException("stream", "stream cannot be null.");
+ }
+
+ if (!stream.CanRead || !stream.CanSeek)
+ {
+ throw new ArgumentException("The stream must be a readable and seek-able stream.", "stream");
+ }
+
+ Dictionary<object, object> dictionary = null;
+ this.Reset();
+
+ // Header + trailer = 40.
+ if (stream.Length > 40)
+ {
+ using (BinaryReader reader = new BinaryReader(stream))
+ {
+ // Read the header.
+ stream.Position = 0;
+ int bpli = reader.ReadInt32().ToBigEndianConditional();
+ int version = reader.ReadInt32().ToBigEndianConditional();
+
+ if (bpli != BinaryPlistWriter.HeaderMagicNumber || version != BinaryPlistWriter.HeaderVersionNumber)
+ {
+ throw new ArgumentException("The stream data does not start with required 'bplist00' header.", "stream");
+ }
+
+ // Read the trailer.
+ // The first six bytes of the first eight-byte block are unused, so offset by 26 instead of 32.
+ stream.Position = stream.Length - 26;
+ this.offsetIntSize = (int)reader.ReadByte();
+ this.objectRefSize = (int)reader.ReadByte();
+ this.objectCount = (int)reader.ReadInt64().ToBigEndianConditional();
+ this.topLevelObjectOffset = (int)reader.ReadInt64().ToBigEndianConditional();
+ this.offsetTableOffset = (int)reader.ReadInt64().ToBigEndianConditional();
+ int offsetTableSize = this.offsetIntSize * this.objectCount;
+
+ // Ensure our sanity.
+ if (this.offsetIntSize < 1
+ || this.offsetIntSize > 8
+ || this.objectRefSize < 1
+ || this.objectRefSize > 8
+ || this.offsetTableOffset < 8
+ || this.topLevelObjectOffset >= this.objectCount
+ || offsetTableSize + this.offsetTableOffset + 32 > stream.Length)
+ {
+ throw new ArgumentException("The stream data contains an invalid trailer.", "stream");
+ }
+
+ // Read the offset table and then the object table.
+ this.ReadOffsetTable(reader);
+ this.ReadObjectTable(reader);
+ }
+ }
+
+ BinaryPlistDictionary root = this.objectTable[this.topLevelObjectOffset] as BinaryPlistDictionary;
+
+ if (root != null)
+ {
+ dictionary = root.ToDictionary();
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported root plist object: " + this.objectTable[this.topLevelObjectOffset].GetType() + ". A dictionary must be the root plist object.");
+ }
+
+ return dictionary ?? new Dictionary<object, object>();
+ }
+
+ /// <summary>
+ /// Reads a binary plist from the given file path into a new <see cref="IPlistSerializable"/> object instance.
+ /// </summary>
+ /// <typeparam name="T">The concrete <see cref="IPlistSerializable"/> type to create.</typeparam>
+ /// <param name="path">The path of the file to read.</param>
+ /// <returns>The result <see cref="IPlistSerializable"/> object instance.</returns>
+ public T ReadObject<T>(string path) where T : IPlistSerializable, new()
+ {
+ using (Stream stream = File.OpenRead(path))
+ {
+ return this.ReadObject<T>(path);
+ }
+ }
+
+ /// <summary>
+ /// Reads a binary plist from the given stream into a new <see cref="IPlistSerializable"/> object instance.
+ /// </summary>
+ /// <typeparam name="T">The concrete <see cref="IPlistSerializable"/> type to create.</typeparam>
+ /// <param name="stream">The <see cref="Stream"/> to read.</param>
+ /// <returns>The result <see cref="IPlistSerializable"/> object instance.</returns>
+ public T ReadObject<T>(Stream stream) where T : IPlistSerializable, new()
+ {
+ T obj = new T();
+ obj.FromPlistDictionary(this.ReadObject(stream));
+ return obj;
+ }
+
+ #endregion
+
+ #region Private Static Methods
+
+ /// <summary>
+ /// Reads an ASCII string value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the ASCII string value from.</param>
+ /// <param name="index">The index in the stream the string value starts at.</param>
+ /// <param name="size">The number of bytes that make up the string value.</param>
+ /// <returns>A string value.</returns>
+ private static string ReadAsciiString(BinaryReader reader, long index, int size)
+ {
+ byte[] buffer = ReadData(reader, index, size);
+ return buffer.Length > 0 ? Encoding.ASCII.GetString(buffer) : String.Empty;
+ }
+
+ /// <summary>
+ /// Reads a data value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the data value from.</param>
+ /// <param name="index">The index in the stream the data value starts at.</param>
+ /// <param name="size">The number of bytes that make up the data value.</param>
+ /// <returns>A data value.</returns>
+ private static byte[] ReadData(BinaryReader reader, long index, int size)
+ {
+ reader.BaseStream.Position = index;
+
+ byte[] buffer = new byte[size];
+ int bufferIndex = 0, count;
+
+ while (0 < (count = reader.Read(buffer, bufferIndex, buffer.Length - bufferIndex)))
+ {
+ bufferIndex += count;
+ }
+
+ return buffer;
+ }
+
+ /// <summary>
+ /// Reads a date value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the date value from.</param>
+ /// <param name="index">The index in the stream the date value starts at.</param>
+ /// <param name="size">The number of bytes that make up the date value.</param>
+ /// <returns>A date value.</returns>
+ private static DateTime ReadDate(BinaryReader reader, long index, int size)
+ {
+ return BinaryPlistWriter.ReferenceDate.AddSeconds(ReadReal(reader, index, size)).ToLocalTime();
+ }
+
+ /// <summary>
+ /// Reads an integer value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the integer value from.</param>
+ /// <param name="index">The index in the stream the integer value starts at.</param>
+ /// <param name="size">The number of bytes that make up the integer value.</param>
+ /// <returns>An integer value.</returns>
+ private static long ReadInteger(BinaryReader reader, long index, int size)
+ {
+ byte[] buffer = ReadData(reader, index, size);
+
+ if (buffer.Length > 1 && BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(buffer);
+ }
+
+ switch (size)
+ {
+ case 1:
+ return (long)buffer[0];
+ case 2:
+ return (long)BitConverter.ToInt16(buffer, 0);
+ case 4:
+ return (long)BitConverter.ToInt32(buffer, 0);
+ case 8:
+ return (long)BitConverter.ToInt64(buffer, 0);
+ default:
+ throw new InvalidOperationException("Unsupported variable-length integer size: " + size);
+ }
+ }
+
+ /// <summary>
+ /// Reads a primitive (true, false or null) value from the given reader, starting at the given index.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the primitive value from.</param>
+ /// <param name="index">The index in the stream the value starts at.</param>
+ /// <param name="primitive">Contains the read primitive value upon completion.</param>
+ /// <returns>True if a value was read, false if the value was a fill byte.</returns>
+ private static bool ReadPrimitive(BinaryReader reader, long index, out bool? primitive)
+ {
+ reader.BaseStream.Position = index;
+ byte value = reader.ReadByte();
+
+ switch (value & 0xf)
+ {
+ case 0:
+ primitive = null;
+ return true;
+ case 8:
+ primitive = false;
+ return true;
+ case 9:
+ primitive = true;
+ return true;
+ case 15:
+ // This is a fill byte.
+ primitive = null;
+ return false;
+ default:
+ throw new InvalidOperationException("Illegal primitive: " + value.ToBinaryString());
+ }
+ }
+
+ /// <summary>
+ /// Reads a floating-point value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the floating-point value from.</param>
+ /// <param name="index">The index int he stream the floating-point value starts at.</param>
+ /// <param name="size">The number of bytes that make up the floating-point value.</param>
+ /// <returns>A floating-point value.</returns>
+ private static double ReadReal(BinaryReader reader, long index, int size)
+ {
+ byte[] buffer = ReadData(reader, index, size);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(buffer);
+ }
+
+ switch (size)
+ {
+ case 4:
+ return BitConverter.ToSingle(buffer, 0);
+ case 8:
+ return BitConverter.ToDouble(buffer, 0);
+ default:
+ throw new InvalidOperationException("Unsupported floating point number size: " + size);
+ }
+ }
+
+ /// <summary>
+ /// Reads a Unicode string value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the Unicode string value from.</param>
+ /// <param name="index">The index in the stream the string value starts at.</param>
+ /// <param name="size">The number of characters that make up the string value.</param>
+ /// <returns>A string value.</returns>
+ private static string ReadUnicodeString(BinaryReader reader, long index, int size)
+ {
+ reader.BaseStream.Position = index;
+ size = size * 2;
+
+ byte[] buffer = new byte[size];
+ byte one, two;
+
+ for (int i = 0; i < size; i++)
+ {
+ one = reader.ReadByte();
+ two = reader.ReadByte();
+
+ if (BitConverter.IsLittleEndian)
+ {
+ buffer[i++] = two;
+ buffer[i] = one;
+ }
+ else
+ {
+ buffer[i++] = one;
+ buffer[i] = two;
+ }
+ }
+
+ return Encoding.Unicode.GetString(buffer);
+ }
+
+ /// <summary>
+ /// Reads a unique ID value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the unique ID value from.</param>
+ /// <param name="index">The index in the stream the unique ID value starts at.</param>
+ /// <param name="size">The number of bytes that make up the unique ID value.</param>
+ /// <returns>A unique ID value.</returns>
+ private static IDictionary ReadUniqueId(BinaryReader reader, long index, int size)
+ {
+ // Unique IDs in XML plists are <dict><key>CF$UID</key><integer>value</integer></dict>.
+ // They're used by Cocoa's key-value coder.
+ Dictionary<string, ulong> dict = new Dictionary<string, ulong>();
+ dict["CF$UID"] = (ulong)ReadInteger(reader, index, size);
+ return dict;
+ }
+
+ #endregion
+
+ #region Private Instance Methods
+
+ /// <summary>
+ /// Reads an array value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the array value from.</param>
+ /// <param name="index">The index in the stream the array value starts at.</param>
+ /// <param name="size">The number of items in the array.</param>
+ /// <returns>An array value.</returns>
+ private BinaryPlistArray ReadArray(BinaryReader reader, long index, int size)
+ {
+ BinaryPlistArray array = new BinaryPlistArray(this.objectTable, size);
+
+ for (int i = 0; i < size; i++)
+ {
+ array.ObjectReference.Add((int)ReadInteger(reader, index + (i * this.objectRefSize), this.objectRefSize));
+ }
+
+ return array;
+ }
+
+ /// <summary>
+ /// Reads a dictionary value from the given reader, starting at the given index and of the given size.
+ /// </summary>
+ /// <param name="reader">The <see cref="BinaryReader"/> to read the dictionary value from.</param>
+ /// <param name="index">The index in the stream the dictionary value starts at.</param>
+ /// <param name="size">The number of items in the dictionary.</param>
+ /// <returns>A dictionary value.</returns>
+ private BinaryPlistDictionary ReadDictionary(BinaryReader reader, long index, int size)
+ {
+ BinaryPlistDictionary dictionary = new BinaryPlistDictionary(this.objectTable, size);
+ int skip = size * this.objectRefSize;
+
+ for (int i = 0; i < size; i++)
+ {
+ dictionary.KeyReference.Add((int)ReadInteger(reader, index + (i * this.objectRefSize), this.objectRefSize));
+ dictionary.ObjectReference.Add((int)ReadInteger(reader, skip + index + (i * this.objectRefSize), this.objectRefSize));
+ }
+
+ return dictionary;
+ }
+
+ /// <summary>
+ /// Reads the object table from the given reader.
+ /// </summary>
+ /// <param name="reader">The reader to read the object table from.</param>
+ private void ReadObjectTable(BinaryReader reader)
+ {
+ byte marker;
+ bool? primitive;
+ int size, intSize;
+ long parsedInt;
+
+ for (int i = 0; i < this.objectCount; i++)
+ {
+ reader.BaseStream.Position = this.offsetTable[i];
+ marker = reader.ReadByte();
+
+ // The first half of the byte is the base marker.
+ switch ((marker & 0xf0) >> 4)
+ {
+ case 0:
+ if (ReadPrimitive(reader, reader.BaseStream.Position - 1, out primitive))
+ {
+ this.objectTable.Add(primitive);
+ }
+
+ break;
+ case 1:
+ size = 1 << (marker & 0xf);
+ parsedInt = ReadInteger(reader, reader.BaseStream.Position, size);
+
+ if (size < 4)
+ {
+ this.objectTable.Add((short)parsedInt);
+ }
+ else if (size < 8)
+ {
+ this.objectTable.Add((int)parsedInt);
+ }
+ else
+ {
+ this.objectTable.Add(parsedInt);
+ }
+
+ break;
+ case 2:
+ size = 1 << (marker & 0xf);
+ this.objectTable.Add(ReadReal(reader, reader.BaseStream.Position, size));
+ break;
+ case 3:
+ size = marker & 0xf;
+
+ if (size == 3)
+ {
+ this.objectTable.Add(ReadDate(reader, reader.BaseStream.Position, 8));
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported date size: " + size.ToBinaryString());
+ }
+
+ break;
+ case 4:
+ size = marker & 0xf;
+
+ if (size == 15)
+ {
+ intSize = 1 << (reader.ReadByte() & 0xf);
+ size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize);
+ }
+
+ this.objectTable.Add(ReadData(reader, reader.BaseStream.Position, size));
+ break;
+ case 5:
+ size = marker & 0xf;
+
+ if (size == 15)
+ {
+ intSize = 1 << (reader.ReadByte() & 0xf);
+ size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize);
+ }
+
+ this.objectTable.Add(ReadAsciiString(reader, reader.BaseStream.Position, size));
+ break;
+ case 6:
+ size = marker & 0xf;
+
+ if (size == 15)
+ {
+ intSize = 1 << (reader.ReadByte() & 0xf);
+ size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize);
+ }
+
+ this.objectTable.Add(ReadUnicodeString(reader, reader.BaseStream.Position, size));
+ break;
+ case 8:
+ size = (marker & 0xf) + 1;
+ this.objectTable.Add(ReadUniqueId(reader, reader.BaseStream.Position, size));
+ break;
+ case 10:
+ case 12:
+ size = marker & 0xf;
+
+ if (size == 15)
+ {
+ intSize = 1 << (reader.ReadByte() & 0xf);
+ size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize);
+ }
+
+ this.objectTable.Add(this.ReadArray(reader, reader.BaseStream.Position, size));
+ break;
+ case 13:
+ size = marker & 0xf;
+
+ if (size == 15)
+ {
+ intSize = 1 << (reader.ReadByte() & 0xf);
+ size = (int)ReadInteger(reader, reader.BaseStream.Position, intSize);
+ }
+
+ this.objectTable.Add(this.ReadDictionary(reader, reader.BaseStream.Position, size));
+ break;
+ default:
+ throw new InvalidOperationException("An invalid marker was found while reading the object table: " + marker.ToBinaryString());
+ }
+ }
+ }
+
+ /// <summary>
+ /// Reads the offset table from the given reader.
+ /// </summary>
+ /// <param name="reader">The reader to read the offset table from.</param>
+ private void ReadOffsetTable(BinaryReader reader)
+ {
+ for (int i = 0; i < this.objectCount; i++)
+ {
+ this.offsetTable.Add((int)ReadInteger(reader, this.offsetTableOffset + (i * this.offsetIntSize), this.offsetIntSize));
+ }
+ }
+
+ /// <summary>
+ /// Resets this instance's state.
+ /// </summary>
+ private void Reset()
+ {
+ this.objectRefSize =
+ this.objectCount =
+ this.offsetIntSize =
+ this.offsetTableOffset =
+ this.topLevelObjectOffset = 0;
+
+ this.objectTable = new List<object>();
+ this.offsetTable = new List<int>();
+ }
+
+ #endregion
+ }
+}
682 Source/System.Runtime.Serialization.Plists/BinaryPlistWriter.cs
@@ -0,0 +1,682 @@
+//-----------------------------------------------------------------------
+// <copyright file="BinaryPlistWriter.cs" company="Tasty Codes">
+// Copyright (c) 2011 Chad Burggraf.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace System.Runtime.Serialization.Plists
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Linq;
+ using System.Runtime.Serialization;
+ using System.Runtime.Serialization.Formatters.Binary;
+ using System.Text;
+
+ /// <summary>
+ /// Performs serialization of objects into binary plist format.
+ /// </summary>
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The spelling is correct.")]
+ public sealed class BinaryPlistWriter
+ {
+ #region Internal Fields
+
+ /// <summary>
+ /// Gets the magic number value used in a binary plist header.
+ /// </summary>
+ internal const uint HeaderMagicNumber = 0x62706c69;
+
+ /// <summary>
+ /// Gets the version number value used in a binary plist header.
+ /// </summary>
+ internal const uint HeaderVersionNumber = 0x73743030;
+
+ /// <summary>
+ /// Gets Apple's reference date value.
+ /// </summary>
+ internal static readonly DateTime ReferenceDate = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ #endregion
+
+ #region Private Fields
+
+ private List<object> objectTable;
+ private List<long> offsetTable;
+ private int topLevelObjectOffset, offsetIntSize, objectRefSize, maxCollectionSize;
+
+ #endregion
+
+ #region Public Instance Methods
+
+ /// <summary>
+ /// Writes the specified <see cref="IPlistSerializable"/> object to the given file path as a binary plist.
+ /// </summary>
+ /// <param name="path">The file path to write to.</param>
+ /// <param name="obj">The <see cref="IPlistSerializable"/> object to write.</param>
+ public void WriteObject(string path, IPlistSerializable obj)
+ {
+ using (FileStream stream = File.Create(path))
+ {
+ this.WriteObject(stream, obj);
+ }
+ }
+
+ /// <summary>
+ /// Writes the specified <see cref="IPlistSerializable"/> object to the given stream as a binary plist.
+ /// </summary>
+ /// <param name="stream">The stream to write to.</param>
+ /// <param name="obj">The <see cref="IPlistSerializable"/> object to write.</param>
+ public void WriteObject(Stream stream, IPlistSerializable obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException("obj", "obj cannot be null.");
+ }
+
+ this.WriteObject(stream, obj.ToPlistDictionary());
+ }
+
+ /// <summary>
+ /// Writes the specified <see cref="IDictionary"/> object to the given file path as a binary plist.
+ /// </summary>
+ /// <param name="path">The file path to write to.</param>
+ /// <param name="dictionary">The <see cref="IDictionary"/> object to write.</param>
+ public void WriteObject(string path, IDictionary dictionary)
+ {
+ using (FileStream stream = File.Create(path))
+ {
+ this.WriteObject(stream, dictionary);
+ }
+ }
+
+ /// <summary>
+ /// Writes the specified <see cref="IDictionary"/> object to the given stream as a binary plist.
+ /// </summary>
+ /// <param name="stream">The stream to write to.</param>
+ /// <param name="dictionary">The <see cref="IDictionary"/> object to write.</param>
+ public void WriteObject(Stream stream, IDictionary dictionary)
+ {
+ if (stream == null)
+ {
+ throw new ArgumentNullException("stream", "stream cannot be null.");
+ }
+
+ if (!stream.CanWrite)
+ {
+ throw new ArgumentException("The stream must be writable.", "stream");
+ }
+
+ if (dictionary == null)
+ {
+ throw new ArgumentNullException("dictionary", "dictionary cannot be null.");
+ }
+
+ // Reset the state and then build the object table.
+ this.Reset();
+ this.AddDictionary(dictionary);
+
+ using (BinaryWriter writer = new BinaryWriter(stream))
+ {
+ long baseOffset = stream.Position;
+
+ // Write the header.
+ writer.Write(HeaderMagicNumber.ToBigEndianConditional());
+ writer.Write(HeaderVersionNumber.ToBigEndianConditional());
+
+ // Write the object table.
+ this.topLevelObjectOffset = 8;
+ this.offsetIntSize = ByteSizeForRefCount(this.offsetTable.Count);
+ this.objectRefSize = ByteSizeForRefCount(this.objectTable.Count);
+ this.WriteObjectTable(writer);
+
+ // Write the offset table.
+ long offsetTableOffset = stream.Position - baseOffset;
+
+ foreach (int offset in this.offsetTable)
+ {
+ WriteReferenceInteger(writer, offset, this.offsetIntSize);
+ }
+
+ // Write the trailer.
+ stream.Position += 6;
+ writer.Write((byte)this.offsetIntSize);
+ writer.Write((byte)this.objectRefSize);
+ writer.Write(((long)this.objectTable.Count).ToBigEndianConditional());
+ writer.Write((long)0);
+ writer.Write(offsetTableOffset.ToBigEndianConditional());
+ }
+ }
+
+ #endregion
+
+ #region Private Static Methods
+
+ /// <summary>
+ /// Gets the number of bytes to use for referene values given the provided number of values
+ /// being addressed.
+ /// </summary>
+ /// <param name="count">The number of values being addressed.</param>
+ /// <returns>The number of bytes required.</returns>
+ private static int ByteSizeForRefCount(int count)
+ {
+ int size = 1;
+
+ if (count > 255)
+ {
+ size = 1 << size;
+ }
+
+ if (count > 65535)
+ {
+ size = 1 << size;
+ }
+
+ return size;
+ }
+
+ /// <summary>
+ /// Gets a big-endian byte array that corresponds to the given integer value.
+ /// </summary>
+ /// <param name="value">The integer value to get bytes for.</param>
+ /// <returns>A big-endian byte array.</returns>
+ private static byte[] GetIntegerBytes(long value)
+ {
+ if (value >= 0 && value < 128)
+ {
+ return new byte[] { (byte)value };
+ }
+ else if (value >= Int16.MinValue && value <= Int16.MaxValue)
+ {
+ return BitConverter.GetBytes(((short)value).ToBigEndianConditional());
+ }
+ else if (value >= Int32.MinValue && value <= Int32.MaxValue)
+ {
+ return BitConverter.GetBytes(((int)value).ToBigEndianConditional());
+ }
+ else
+ {
+ return BitConverter.GetBytes(value.ToBigEndianConditional());
+ }
+ }
+
+ /// <summary>
+ /// Writes an object as raw data to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The object to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteData(BinaryWriter writer, object value)
+ {
+ int size = 1, index = 0, count;
+ byte[] buffer = value as byte[];
+
+ if (buffer == null)
+ {
+ using (MemoryStream stream = new MemoryStream())
+ {
+ BinaryFormatter formatter = new BinaryFormatter();
+ formatter.Serialize(stream, value);
+
+ stream.Position = 0;
+ buffer = new byte[stream.Length];
+
+ while (0 < (count = stream.Read(buffer, 0, buffer.Length - index)))
+ {
+ index += count;
+ }
+ }
+ }
+
+ if (buffer.Length < 15)
+ {
+ writer.Write((byte)((byte)0x40 | (byte)buffer.Length));
+ }
+ else
+ {
+ writer.Write((byte)0x4F);
+ size += WriteIntegerWithoutMarker(writer, buffer.Length);
+ }
+
+ writer.Write(buffer, 0, buffer.Length);
+
+ return buffer.Length + size;
+ }
+
+ /// <summary>
+ /// Writes a date to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The date to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteDate(BinaryWriter writer, DateTime value)
+ {
+ byte[] buffer = BitConverter.GetBytes(value.ToUniversalTime().Subtract(ReferenceDate).TotalSeconds);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(buffer);
+ }
+
+ writer.Write((byte)0x33);
+ writer.Write(buffer, 0, buffer.Length);
+
+ return buffer.Length + 1;
+ }
+
+ /// <summary>
+ /// Writes an integer to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The integer to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteInteger(BinaryWriter writer, long value)
+ {
+ byte[] buffer = GetIntegerBytes(value);
+
+ writer.Write((byte)((byte)0x10 | (byte)Math.Log(buffer.Length, 2)));
+ writer.Write(buffer, 0, buffer.Length);
+
+ return buffer.Length + 1;
+ }
+
+ /// <summary>
+ /// Writes an integer value without an object-table marker to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The integer to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteIntegerWithoutMarker(BinaryWriter writer, long value)
+ {
+ byte[] buffer = GetIntegerBytes(value);
+
+ writer.Write((byte)Math.Log(buffer.Length, 2));
+ writer.Write(buffer, 0, buffer.Length);
+
+ return buffer.Length + 1;
+ }
+
+ /// <summary>
+ /// Writes a primitive value to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The primitive to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WritePrimitive(BinaryWriter writer, bool? value)
+ {
+ byte val = 0;
+
+ if (value.HasValue)
+ {
+ val = value.Value ? (byte)0x9 : (byte)0x8;
+ }
+
+ writer.Write(val);
+ return 1;
+ }
+
+ /// <summary>
+ /// Writes a floating-point value to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The floating-point value to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteReal(BinaryWriter writer, double value)
+ {
+ byte[] buffer = BitConverter.GetBytes(value);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(buffer);
+ }
+
+ writer.Write((byte)((byte)0x20 | (byte)Math.Log(buffer.Length, 2)));
+ writer.Write(buffer, 0, buffer.Length);
+
+ return buffer.Length + 1;
+ }
+
+ /// <summary>
+ /// Writes the given value using the number of bytes indicated by the specified size
+ /// to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The value to write.</param>
+ /// <param name="size">The size of the integer to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteReferenceInteger(BinaryWriter writer, long value, int size)
+ {
+ byte[] buffer;
+
+ switch (size)
+ {
+ case 1:
+ buffer = new byte[] { (byte)value };
+ break;
+ case 2:
+ buffer = BitConverter.GetBytes(((short)value).ToBigEndianConditional());
+ break;
+ case 4:
+ buffer = BitConverter.GetBytes(((int)value).ToBigEndianConditional());
+ break;
+ case 8:
+ buffer = BitConverter.GetBytes(value.ToBigEndianConditional());
+ break;
+ default:
+ throw new ArgumentException("The reference size must be one of 2, 4 or 8. The specified reference size was: " + size, "size");
+ }
+
+ writer.Write(buffer, 0, buffer.Length);
+ return buffer.Length;
+ }
+
+ /// <summary>
+ /// Writes a string to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The string to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private static int WriteString(BinaryWriter writer, string value)
+ {
+ bool ascii = value.IsAscii();
+ int size = 1;
+ byte[] buffer;
+
+ if (value.Length < 15)
+ {
+ writer.Write((byte)((byte)(ascii ? 0x50 : 0x60) | (byte)value.Length));
+ }
+ else
+ {
+ writer.Write((byte)(ascii ? 0x5F : 0x6F));
+ size += WriteIntegerWithoutMarker(writer, value.Length);
+ }
+
+ if (ascii)
+ {
+ buffer = Encoding.ASCII.GetBytes(value);
+ }
+ else
+ {
+ buffer = Encoding.Unicode.GetBytes(value);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ byte l = buffer[i];
+ buffer[i] = buffer[++i];
+ buffer[i] = l;
+ }
+ }
+ }
+
+ writer.Write(buffer, 0, buffer.Length);
+ return buffer.Length + size;
+ }
+
+ #endregion
+
+ #region Private Instance Methods
+
+ /// <summary>
+ /// Adds an array to the internal object table.
+ /// </summary>
+ /// <param name="array">The array to add.</param>
+ /// <returns>The index of the added array.</returns>
+ private int AddArray(IEnumerable array)
+ {
+ int index = this.objectTable.Count, itemCount = 0;
+
+ BinaryPlistArray arr = new BinaryPlistArray(this.objectTable);
+ this.objectTable.Add(arr);
+
+ foreach (object value in array)
+ {
+ arr.ObjectReference.Add(this.AddObject(value));
+ itemCount++;
+ }
+
+ if (itemCount > this.maxCollectionSize)
+ {
+ this.maxCollectionSize = itemCount;
+ }
+
+ return index;
+ }
+
+ /// <summary>
+ /// Adds a dictionary to the internal object table.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to add.</param>
+ /// <returns>The index of the added dictionary.</returns>
+ private int AddDictionary(IDictionary dictionary)
+ {
+ int index = this.objectTable.Count;
+
+ BinaryPlistDictionary dict = new BinaryPlistDictionary(this.objectTable, dictionary.Count);
+ this.objectTable.Add(dict);
+
+ foreach (object key in dictionary.Keys)
+ {
+ dict.KeyReference.Add(this.AddObject(key));
+ dict.ObjectReference.Add(this.AddObject(dictionary[key]));
+ }
+
+ if (dictionary.Count > this.maxCollectionSize)
+ {
+ this.maxCollectionSize = dictionary.Count;
+ }
+
+ return index;
+ }
+
+ /// <summary>
+ /// Adds an object to the internal object table.
+ /// </summary>
+ /// <param name="value">The object value to add.</param>
+ /// <returns>The index of the added object.</returns>
+ private int AddObject(object value)
+ {
+ int index = this.objectTable.Count;
+
+ if (value != null)
+ {
+ Type type = value.GetType();
+
+ if (typeof(IPlistSerializable).IsAssignableFrom(type))
+ {
+ index = this.AddDictionary(((IPlistSerializable)value).ToPlistDictionary());
+ }
+ else if (typeof(IDictionary).IsAssignableFrom(type))
+ {
+ index = this.AddDictionary(value as IDictionary);
+ }
+ else if ((typeof(Array).IsAssignableFrom(type) || typeof(ICollection).IsAssignableFrom(type)) && !typeof(byte[]).IsAssignableFrom(type))
+ {
+ index = this.AddArray(value as IEnumerable);
+ }
+ else
+ {
+ this.objectTable.Add(value);
+ }
+ }
+ else
+ {
+ this.objectTable.Add(null);
+ }
+
+ return index;
+ }
+
+ /// <summary>
+ /// Resets this instance's state.
+ /// </summary>
+ private void Reset()
+ {
+ this.topLevelObjectOffset =
+ this.offsetIntSize =
+ this.objectRefSize =
+ this.maxCollectionSize = 0;
+
+ this.objectTable = new List<object>();
+ this.offsetTable = new List<long>();
+ }
+
+ /// <summary>
+ /// Writes an array to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The array to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private int WriteArray(BinaryWriter writer, BinaryPlistArray value)
+ {
+ int size = 1;
+
+ if (value.ObjectReference.Count < 15)
+ {
+ writer.Write((byte)((byte)0xA0 | (byte)value.ObjectReference.Count));
+ }
+ else
+ {
+ writer.Write((byte)0xAF);
+ size += WriteIntegerWithoutMarker(writer, value.ObjectReference.Count);
+ }
+
+ foreach (int objectRef in value.ObjectReference)
+ {
+ WriteReferenceInteger(writer, objectRef, this.objectRefSize);
+ }
+
+ return size + (value.ObjectReference.Count * this.objectRefSize);
+ }
+
+ /// <summary>
+ /// Writes a dictionary to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write to.</param>
+ /// <param name="value">The dictionary to write.</param>
+ /// <returns>The number of bytes written.</returns>
+ private int WriteDictionary(BinaryWriter writer, BinaryPlistDictionary value)
+ {
+ int size = 1, skip = value.KeyReference.Count * this.objectRefSize;
+
+ if (value.KeyReference.Count < 15)
+ {
+ writer.Write((byte)((byte)0xD0 | (byte)value.KeyReference.Count));
+ }
+ else
+ {
+ writer.Write((byte)0xDF);
+ size += WriteIntegerWithoutMarker(writer, value.KeyReference.Count);
+ }
+
+ long startPosition = writer.BaseStream.Position;
+
+ for (int i = 0; i < value.KeyReference.Count; i++)
+ {
+ writer.BaseStream.Position = startPosition + (i * this.objectRefSize);
+ WriteReferenceInteger(writer, value.KeyReference[i], this.objectRefSize);
+ writer.BaseStream.Position = startPosition + skip + (i * this.objectRefSize);
+ WriteReferenceInteger(writer, value.ObjectReference[i], this.objectRefSize);
+ }
+
+ return size + (value.KeyReference.Count * this.objectRefSize * 2);
+ }
+
+ /// <summary>
+ /// Writes the object table to the given <see cref="BinaryWriter"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="BinaryWriter"/> to write the object table to.</param>
+ private void WriteObjectTable(BinaryWriter writer)
+ {
+ int currentOffset = this.topLevelObjectOffset;
+ object obj;
+ Type type;
+
+ for (int i = 0; i < this.objectTable.Count; i++)
+ {
+ obj = this.objectTable[i];
+ this.offsetTable.Add(currentOffset);
+
+ if (obj != null)
+ {
+ type = obj.GetType();
+
+ if (typeof(BinaryPlistDictionary).IsAssignableFrom(type))
+ {
+ currentOffset += this.WriteDictionary(writer, (BinaryPlistDictionary)obj);
+ }
+ else if (typeof(BinaryPlistArray).IsAssignableFrom(type))
+ {
+ currentOffset += this.WriteArray(writer, (BinaryPlistArray)obj);
+ }
+ else if (typeof(bool).IsAssignableFrom(type))
+ {
+ currentOffset += WritePrimitive(writer, (bool)obj);
+ }
+ else if (typeof(long).IsAssignableFrom(type))
+ {
+ currentOffset += WriteInteger(writer, (long)obj);
+ }
+ else if (typeof(int).IsAssignableFrom(type))
+ {
+ currentOffset += WriteInteger(writer, (long)(int)obj);
+ }
+ else if (typeof(uint).IsAssignableFrom(type))
+ {
+ currentOffset += WriteInteger(writer, (long)(uint)obj);
+ }
+ else if (typeof(short).IsAssignableFrom(type))
+ {
+ currentOffset += WriteInteger(writer, (long)(short)obj);
+ }
+ else if (typeof(ushort).IsAssignableFrom(type))
+ {
+ currentOffset += WriteInteger(writer, (long)(ushort)obj);
+ }
+ else if (typeof(byte).IsAssignableFrom(type))
+ {
+ currentOffset += WriteInteger(writer, (long)(byte)obj);
+ }
+ else if (typeof(double).IsAssignableFrom(type))
+ {
+ currentOffset += WriteReal(writer, (double)obj);
+ }
+ else if (typeof(float).IsAssignableFrom(type))
+ {
+ currentOffset += WriteReal(writer, (double)(float)obj);
+ }
+ else if (typeof(decimal).IsAssignableFrom(type))
+ {
+ currentOffset += WriteReal(writer, (double)(decimal)obj);
+ }
+ else if (typeof(DateTime).IsAssignableFrom(type))
+ {
+ currentOffset += WriteDate(writer, (DateTime)obj);
+ }
+ else if (typeof(string).IsAssignableFrom(type))
+ {
+ currentOffset += WriteString(writer, (string)obj);
+ }
+ else if (typeof(ISerializable).IsAssignableFrom(type) || type.IsSerializable)
+ {
+ currentOffset += WriteData(writer, obj);
+ }
+ else
+ {
+ throw new InvalidOperationException("A type was found in the object table that is not serializable. Types that are natively serializable to a binary plist include: null, booleans, integers, floats, dates, strings, arrays and dictionaries. Any other types must be marked with a SerializableAttribute or implement ISerializable.");
+ }
+ }
+ else
+ {
+ currentOffset += WritePrimitive(writer, null);
+ }
+ }
+ }
+
+ #endregion
+ }
+}
331 Source/System.Runtime.Serialization.Plists/EndianConverter.cs
@@ -0,0 +1,331 @@
+//-----------------------------------------------------------------------
+// <copyright file="EndianConverter.cs" company="Tasty Codes">
+// Modifications copyright (c) 2011 Chad Burggraf.
+// Original copyright (c) 2009 Cor Schols
+// Original source: http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/c878e72e-d42e-417d-b4f6-1935ad96d8ae/
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace System.Runtime.Serialization.Plists
+{
+ using System;
+
+ /// <summary>
+ /// Converts the endian-ness of primitive number types.
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class EndianConverter
+ {
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static short SwapEndian(this short value)
+ {
+ return SwapInt16(value);
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static ushort SwapEndian(this ushort value)
+ {
+ return SwapUInt16(value);
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static int SwapEndian(this int value)
+ {
+ return SwapInt32(value);
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static uint SwapEndian(this uint value)
+ {
+ return SwapUInt32(value);
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static long SwapEndian(this long value)
+ {
+ return SwapInt64(value);
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static ulong SwapEndian(this ulong value)
+ {
+ return SwapUInt64(value);
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static short SwapInt16(short value)
+ {
+ return (short)(((value & 0xff) << 8) | ((value >> 8) & 0xff));
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static int SwapInt32(int value)
+ {
+ return (int)(((SwapInt16((short)value) & 0xffff) << 0x10) | (SwapInt16((short)(value >> 0x10)) & 0xffff));
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static long SwapInt64(long value)
+ {
+ return (long)(((SwapInt32((int)value) & 0xffffffffL) << 0x20) | (SwapInt32((int)(value >> 0x20)) & 0xffffffffL));
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static uint SwapUInt32(uint value)
+ {
+ return (uint)(((SwapUInt16((ushort)value) & 0xffff) << 0x10) | (SwapUInt16((ushort)(value >> 0x10)) & 0xffff));
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static ushort SwapUInt16(ushort value)
+ {
+ return (ushort)(((value & 0xff) << 8) | ((value >> 8) & 0xff));
+ }
+
+ /// <summary>
+ /// Swaps the endian-ness of the given value.
+ /// </summary>
+ /// <param name="value">The value to swap the endian-ness of.</param>
+ /// <returns>The resulting value.</returns>
+ public static ulong SwapUInt64(ulong value)
+ {
+ return (ulong)(((SwapUInt32((uint)value) & 0xffffffffL) << 0x20) | (SwapUInt32((uint)(value >> 0x20)) & 0xffffffffL));
+ }
+
+ /// <summary>
+ /// Gets the big-endian value of the given value if the current system is little-endian.
+ /// If the curreng system is big-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static ushort ToBigEndianConditional(this ushort value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value.SwapEndian();
+ }
+
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the big-endian value of the given value if the current system is little-endian.
+ /// If the curreng system is big-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static short ToBigEndianConditional(this short value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value.SwapEndian();
+ }
+
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the big-endian value of the given value if the current system is little-endian.
+ /// If the curreng system is big-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static uint ToBigEndianConditional(this uint value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value.SwapEndian();
+ }
+
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the big-endian value of the given value if the current system is little-endian.
+ /// If the curreng system is big-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static int ToBigEndianConditional(this int value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value.SwapEndian();
+ }
+
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the big-endian value of the given value if the current system is little-endian.
+ /// If the curreng system is big-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static ulong ToBigEndianConditional(this ulong value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value.SwapEndian();
+ }
+
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the big-endian value of the given value if the current system is little-endian.
+ /// If the curreng system is big-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static long ToBigEndianConditional(this long value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value.SwapEndian();
+ }
+
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the little-endian value of the given value if the current system is big-endian.
+ /// If the curreng system is little-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static ushort ToLittleEndianConditional(this ushort value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value;
+ }
+
+ return value.SwapEndian();
+ }
+
+ /// <summary>
+ /// Gets the little-endian value of the given value if the current system is big-endian.
+ /// If the curreng system is little-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static short ToLittleEndianConditional(this short value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value;
+ }
+
+ return value.SwapEndian();
+ }
+
+ /// <summary>
+ /// Gets the little-endian value of the given value if the current system is big-endian.
+ /// If the curreng system is little-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static uint ToLittleEndianConditional(this uint value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value;
+ }
+
+ return value.SwapEndian();
+ }
+
+ /// <summary>
+ /// Gets the little-endian value of the given value if the current system is big-endian.
+ /// If the curreng system is little-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static int ToLittleEndianConditional(this int value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ return value;
+ }
+
+ return value.SwapEndian();
+ }
+
+ /// <summary>
+ /// Gets the little-endian value of the given value if the current system is big-endian.
+ /// If the curreng system is little-endian, returns the value as-is.
+ /// </summary>
+ /// <param name="value">The value to swap if necessary.</param>
+ /// <returns>The resulting value.</returns>
+ public static ulong ToLittleEndianConditional(this ulong value)
+ {
+ if (BitConverter.IsLittleEndian)