From 1009303088d214a7570109ec54d20a1159582376 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 24 May 2011 21:40:30 +0200 Subject: [PATCH 1/2] sort BamlRecordType; clean up some code; replace Hashtable with Dictionary --- .../ILSpy.BamlDecompiler.csproj | 4 + .../BAML format.txt | 22 ++++ .../BamlRecordType.cs | 117 +++++++++--------- .../XmlBamlReader.cs | 59 +++++---- 4 files changed, 121 insertions(+), 81 deletions(-) create mode 100644 ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 6f4ffcb22..6514c632b 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -14,6 +14,7 @@ False 4 false + OnBuildSuccess x86 @@ -130,5 +131,8 @@ False + + + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt new file mode 100644 index 000000000..8cd23cb29 --- /dev/null +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt @@ -0,0 +1,22 @@ +BAML format description + +A BAML stream consists of a header and sequential records that contain the data. + +The Header + +The header is 28 bytes long. The first four bytes are a little endian integer containing the length of the preamble in bytes. The preamble is the UTF-16 string "MSBAML", followed by three integers with the value 0x60000. + +Record format: + +In general a record consists of a type byte and its content. Some records also have a field, following the type byte, containing the remaining length of the record, encoded as 7-bit encoded integer. + +Record Types: + +DocumentStart (= 0x01): + +The document start (after the type byte) is 6 Bytes long. Usually 00 FF FF FF FF 00. These bytes can be safely ignored. + +AssemblyInfo (= 0xC1): + +The length field is followed by a 2 byte long ID. The ID is followed by a string containing the assembly name. + diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs index 50ca01ddb..dd53eff6c 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs @@ -9,64 +9,63 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { internal enum BamlRecordType : byte { - AssemblyInfo = 0x1c, - AttributeInfo = 0x1f, - ClrEvent = 0x13, - Comment = 0x17, - ConnectionId = 0x2d, - ConstructorParametersEnd = 0x2b, - ConstructorParametersStart = 0x2a, - ConstructorParameterType = 0x2c, - ContentProperty = 0x2e, - DefAttribute = 0x19, - DefAttributeKeyString = 0x26, - DefAttributeKeyType = 0x27, - DeferableContentStart = 0x25, - DefTag = 0x18, - DocumentEnd = 2, - DocumentStart = 1, - ElementEnd = 4, - ElementStart = 3, - EndAttributes = 0x1a, - KeyElementEnd = 0x29, - KeyElementStart = 40, - LastRecordType = 0x39, - LineNumberAndPosition = 0x35, - LinePosition = 0x36, - LiteralContent = 15, - NamedElementStart = 0x2f, - OptimizedStaticResource = 0x37, - PIMapping = 0x1b, - PresentationOptionsAttribute = 0x34, - ProcessingInstruction = 0x16, - Property = 5, - PropertyArrayEnd = 10, - PropertyArrayStart = 9, - PropertyComplexEnd = 8, - PropertyComplexStart = 7, - PropertyCustom = 6, - PropertyDictionaryEnd = 14, - PropertyDictionaryStart = 13, - PropertyListEnd = 12, - PropertyListStart = 11, - PropertyStringReference = 0x21, - PropertyTypeReference = 0x22, - PropertyWithConverter = 0x24, - PropertyWithExtension = 0x23, - PropertyWithStaticResourceId = 0x38, - RoutedEvent = 0x12, - StaticResourceEnd = 0x31, - StaticResourceId = 50, - StaticResourceStart = 0x30, - StringInfo = 0x20, - Text = 0x10, - TextWithConverter = 0x11, - TextWithId = 0x33, - TypeInfo = 0x1d, - TypeSerializerInfo = 30, - Unknown = 0, - XmlAttribute = 0x15, - XmlnsProperty = 20 + Unknown, + DocumentStart, + DocumentEnd, + ElementStart, + ElementEnd, + Property, + PropertyCustom, + PropertyComplexStart, + PropertyComplexEnd, + PropertyArrayStart, + PropertyArrayEnd, + PropertyListStart, + PropertyListEnd, + PropertyDictionaryStart, + PropertyDictionaryEnd, + LiteralContent, + Text, + TextWithConverter, + RoutedEvent, + ClrEvent, + XmlnsProperty, + XmlAttribute, + ProcessingInstruction, + Comment, + DefTag, + DefAttribute, + EndAttributes, + PIMapping, + AssemblyInfo, + TypeInfo, + TypeSerializerInfo, + AttributeInfo, + StringInfo, + PropertyStringReference, + PropertyTypeReference, + PropertyWithExtension, + PropertyWithConverter, + DeferableContentStart, + DefAttributeKeyString, + DefAttributeKeyType, + KeyElementStart, + KeyElementEnd, + ConstructorParametersStart, + ConstructorParametersEnd, + ConstructorParameterType, + ConnectionId, + ContentProperty, + NamedElementStart, + StaticResourceStart, + StaticResourceEnd, + StaticResourceId, + TextWithId, + PresentationOptionsAttribute, + LineNumberAndPosition, + LinePosition, + OptimizedStaticResource, + PropertyWithStaticResourceId, + LastRecordType } - } diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs index 615c02d4d..9fc75c5b1 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs @@ -21,10 +21,10 @@ namespace Ricciolo.StylesExplorer.MarkupReflection #region Variables private BamlBinaryReader reader; - private Hashtable assemblyTable = new Hashtable(); - private Hashtable stringTable = new Hashtable(); - private Hashtable typeTable = new Hashtable(); - private Hashtable propertyTable = new Hashtable(); + private Dictionary assemblyTable = new Dictionary(); + private Dictionary stringTable = new Dictionary(); + private Dictionary typeTable = new Dictionary(); + private Dictionary propertyTable = new Dictionary(); private readonly ITypeResolver _resolver; @@ -247,9 +247,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { do { - currentType = (BamlRecordType)reader.ReadByte(); - //Debug.WriteLine(currentType); - if (currentType == BamlRecordType.DocumentEnd) break; + ReadRecordType(); + if (currentType == BamlRecordType.DocumentEnd) + break; long position = reader.BaseStream.Position; @@ -257,9 +257,8 @@ namespace Ricciolo.StylesExplorer.MarkupReflection ProcessNext(); if (bytesToSkip > 0) - { + // jump to the end of the record reader.BaseStream.Position = position + bytesToSkip; - } } //while (currentType != BamlRecordType.DocumentEnd); while (nodes.Count == 0 || (currentType != BamlRecordType.ElementEnd) || complexPropertyOpened > 0); @@ -277,6 +276,23 @@ namespace Ricciolo.StylesExplorer.MarkupReflection } } + void ReadRecordType() + { + byte type = reader.ReadByte(); + if (type < 0) + currentType = BamlRecordType.DocumentEnd; + else + currentType = (BamlRecordType)type; + + if (currentType.ToString().EndsWith("End")) + Debug.Unindent(); + + Debug.WriteLine(currentType); + + if (currentType.ToString().StartsWith("Start")) + Debug.Indent(); + } + private bool SetNextNode() { while (nodes.Count > 0) @@ -305,12 +321,8 @@ namespace Ricciolo.StylesExplorer.MarkupReflection switch (currentType) { case BamlRecordType.DocumentStart: - { - reader.ReadBoolean(); - reader.ReadInt32(); - reader.ReadBoolean(); - break; - } + reader.ReadBytes(6); + break; case BamlRecordType.DocumentEnd: break; case BamlRecordType.ElementStart: @@ -586,7 +598,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection PropertyDeclaration declaration; if (identifier >= 0) { - declaration = (PropertyDeclaration)this.propertyTable[identifier]; + declaration = this.propertyTable[identifier]; } else { @@ -603,7 +615,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { if (identifier >= 0) { - PropertyDeclaration declaration = (PropertyDeclaration)this.propertyTable[identifier]; + PropertyDeclaration declaration = this.propertyTable[identifier]; return declaration; } else @@ -906,7 +918,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection pd = new PropertyDeclaration("Name", XamlTypeDeclaration); break; default: - string recordName = (string)this.stringTable[identifier]; + string recordName = this.stringTable[identifier]; if (recordName != "Key") throw new NotSupportedException(recordName); pd = new PropertyDeclaration(recordName, XamlTypeDeclaration); @@ -926,7 +938,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection int position = reader.ReadInt32(); //bool shared = reader.ReadBoolean(); //bool sharedSet = reader.ReadBoolean(); - string text = (string)this.stringTable[num]; + string text = this.stringTable[num]; if (text == null) throw new NotSupportedException(); @@ -963,7 +975,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection string[] textArray = new string[(uint)reader.ReadInt16()]; for (int i = 0; i < textArray.Length; i++) { - textArray[i] = (string)this.assemblyTable[reader.ReadInt16()]; + textArray[i] = this.assemblyTable[reader.ReadInt16()]; } XmlNamespaceCollection namespaces = elements.Peek().Namespaces; @@ -1045,6 +1057,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection private void ReadPropertyComplexEnd() { + if (!(elements.Peek() is XmlBamlPropertyElement)) + throw new InvalidCastException(); + XmlBamlPropertyElement propertyElement = (XmlBamlPropertyElement) elements.Peek(); CloseElement(); @@ -1546,7 +1561,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { TypeDeclaration declaration; if (identifier >= 0) - declaration = (TypeDeclaration)this.typeTable[identifier]; + declaration = this.typeTable[identifier]; else declaration = KnownInfo.KnownTypeTable[-identifier]; @@ -1558,7 +1573,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection internal string GetAssembly(short identifier) { - return this.assemblyTable[identifier].ToString(); + return this.assemblyTable[identifier]; } private XmlBamlNode CurrentNode From 02e6cf6dd8672ba801b4d18487023dee37e502da Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 25 May 2011 13:51:46 +0200 Subject: [PATCH 2/2] implement support for TextWithId records and add some safety checks --- .../XmlBamlReader.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs index 9fc75c5b1..a56c9afa5 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs @@ -419,6 +419,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection case BamlRecordType.TextWithConverter: this.ReadTextWithConverter(); break; + case BamlRecordType.TextWithId: + this.ReadTextWithId(); + break; case BamlRecordType.PropertyWithStaticResourceId: this.ReadPropertyWithStaticResourceIdentifier(); break; @@ -447,9 +450,17 @@ namespace Ricciolo.StylesExplorer.MarkupReflection this.ReadPresentationOptionsAttribute(); break; default: + throw new NotImplementedException("UnsupportedNode: " + currentType); break; } } + + void ReadTextWithId() + { + short textId = reader.ReadInt16(); + string text = stringTable[textId]; + nodes.Enqueue(new XmlBamlText(text)); + } private void ComputeBytesToSkip() { @@ -462,6 +473,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection case BamlRecordType.Property: case BamlRecordType.PropertyCustom: case BamlRecordType.Text: + case BamlRecordType.TextWithId: case BamlRecordType.TextWithConverter: case BamlRecordType.XmlnsProperty: case BamlRecordType.DefAttribute: @@ -783,8 +795,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection case IntegerCollectionType.Integer: for (int k = 0; k < capacity; k++) { - int num7 = reader.ReadInt32(); - ints.Add(num7); + ints.Add(reader.ReadInt32()); } return ints; @@ -1172,7 +1183,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection private void ReadElementStart() { short identifier = reader.ReadInt16(); - reader.ReadByte(); + sbyte flags = reader.ReadSByte(); + if (flags < 0 || flags > 3) + throw new NotImplementedException(); TypeDeclaration declaration = GetTypeDeclaration(identifier); XmlBamlElement element;