diff --git a/Libraries/UniversalEditor.Essential/DataFormats/Markup/XML/.#XMLDataFormat.cs b/Libraries/UniversalEditor.Essential/DataFormats/Markup/XML/.#XMLDataFormat.cs
deleted file mode 100644
index e69de29b..00000000
diff --git a/Plugins/UniversalEditor.Plugins.Genealogy/DataFormats/FamilyTreeMaker/Windows/FTWDataFormat.cs b/Plugins/UniversalEditor.Plugins.Genealogy/DataFormats/FamilyTreeMaker/Windows/FTWDataFormat.cs
index fd05cd8e..4ee62a18 100644
--- a/Plugins/UniversalEditor.Plugins.Genealogy/DataFormats/FamilyTreeMaker/Windows/FTWDataFormat.cs
+++ b/Plugins/UniversalEditor.Plugins.Genealogy/DataFormats/FamilyTreeMaker/Windows/FTWDataFormat.cs
@@ -32,8 +32,8 @@ namespace UniversalEditor.Plugins.Genealogy.DataFormats.FamilyTreeMaker.Windows
CompoundDocumentObjectModel fsom = (objectModels.Pop () as CompoundDocumentObjectModel);
- File IND_DB = fsom.Files["IND.DB"];
- File INDGROUPS = fsom.Files["QEDIT0.DB"];
+ File IND_DB = fsom.Folders["Root Entry"].Files["IND.DB"];
+ File INDGROUPS = fsom.Folders["Root Entry"].Files["QEDIT0.DB"];
if (IND_DB == null)
throw new InvalidDataFormatException("IND.DB not found");
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft.VisualStudio/DataFormats/PropertyList/Microsoft/VisualStudio/SUODataFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft.VisualStudio/DataFormats/PropertyList/Microsoft/VisualStudio/SUODataFormat.cs
index 34c6302d..d4e8adc0 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft.VisualStudio/DataFormats/PropertyList/Microsoft/VisualStudio/SUODataFormat.cs
+++ b/Plugins/UniversalEditor.Plugins.Microsoft.VisualStudio/DataFormats/PropertyList/Microsoft/VisualStudio/SUODataFormat.cs
@@ -20,6 +20,8 @@
// along with this program. If not, see .
using System.Collections.Generic;
+using MBS.Framework;
+using MBS.Framework.Settings;
using UniversalEditor.DataFormats.CompoundDocument;
using UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument;
using UniversalEditor.ObjectModels.CompoundDocument;
@@ -38,7 +40,7 @@ namespace UniversalEditor.DataFormats.PropertyList.Microsoft.VisualStudio
{
if (_dfr == null)
{
- _dfr = new DataFormatReference(GetType());
+ _dfr = new DataFormatReference(GetType(), base.MakeReferenceInternal());
_dfr.Capabilities.Add(typeof(PropertyListObjectModel), DataFormatCapabilities.All);
}
return _dfr;
@@ -55,8 +57,7 @@ namespace UniversalEditor.DataFormats.PropertyList.Microsoft.VisualStudio
CompoundDocumentObjectModel fsom = (objectModels.Pop() as CompoundDocumentObjectModel);
PropertyListObjectModel plom = (objectModels.Pop() as PropertyListObjectModel);
- for (int i = 0; i < fsom.Files.Count; i++ )
- fsom.Files[i].Save(@"C:\Temp\SUO\" + fsom.Files[i].Name);
+
}
protected override void BeforeSaveInternal(Stack objectModels)
{
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/Associations/CompoundDocument.uexml b/Plugins/UniversalEditor.Plugins.Microsoft/Associations/CompoundDocument.uexml
index 7e66e1fd..f8ab10fa 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/Associations/CompoundDocument.uexml
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/Associations/CompoundDocument.uexml
@@ -1,6 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -8,11 +21,22 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/Associations/Text/Formatted/DOC.uexml b/Plugins/UniversalEditor.Plugins.Microsoft/Associations/Text/Formatted/DOC.uexml
index ec7636bc..a71c003f 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/Associations/Text/Formatted/DOC.uexml
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/Associations/Text/Formatted/DOC.uexml
@@ -5,6 +5,9 @@
+
+ *.doc
+
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/CompoundDocumentDataFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/CompoundDocumentDataFormat.cs
index 7a398111..2dadb2cb 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/CompoundDocumentDataFormat.cs
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/CompoundDocumentDataFormat.cs
@@ -19,12 +19,118 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
using System;
+using System.Collections.Generic;
+using MBS.Framework;
+using MBS.Framework.Settings;
+using UniversalEditor.Accessors;
using UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument;
+using UniversalEditor.IO;
+using UniversalEditor.ObjectModels.CompoundDocument;
+using UniversalEditor.ObjectModels.FileSystem;
+using UniversalEditor.ObjectModels.PropertyList;
namespace UniversalEditor.DataFormats.CompoundDocument
{
public class CompoundDocumentDataFormat : CompoundDocumentBaseDataFormat
{
+ private static DataFormatReference _dfr = null;
+ protected override DataFormatReference MakeReferenceInternal()
+ {
+ if (_dfr == null)
+ {
+ _dfr = new DataFormatReference(GetType());
+
+ SettingsGroup sgSummaryInfo = new SettingsGroup(new string[] { "General", "Summary" }, new Setting[]
+ {
+ new TextSetting("Title", "_Title"),
+ new TextSetting("Subject", "_Subject"),
+ new TextSetting("Author", "_Author"),
+ new TextSetting("Keywords", "_Keywords"),
+ new TextSetting("Comments", "_Comments"),
+ new TextSetting("AppName", "App name")
+ });
+ _dfr.ExportOptions.SettingsGroups.Add(sgSummaryInfo);
+ }
+ return _dfr;
+ }
+
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ CompoundDocumentObjectModel cdom = (objectModels.Pop() as CompoundDocumentObjectModel);
+
+ File _CompObj = cdom.Folders["Root Entry"].Files["\u0001CompObj"];
+ if (_CompObj != null)
+ {
+ byte[] _CompObj_data = _CompObj.GetData();
+ MemoryAccessor ma = new MemoryAccessor(_CompObj_data);
+
+ int reserved1 = ma.Reader.ReadInt32();
+ int version = ma.Reader.ReadInt32();
+ byte[] reserved2 = ma.Reader.ReadBytes(20);
+
+ cdom.UserType = ReadString32(ma.Reader);
+ object ansiClipboardFormat = ReadClipboardFormatOrAnsiString(ma.Reader);
+ if (ansiClipboardFormat is uint)
+ {
+ cdom.ClipboardFormat = CompoundDocumentClipboardFormat.FromStandard((uint)ansiClipboardFormat);
+ }
+ else if (ansiClipboardFormat is string)
+ {
+ cdom.ClipboardFormat = CompoundDocumentClipboardFormat.FromString((string)ansiClipboardFormat);
+ }
+
+ cdom.AssociationTypeId = ReadString32(ma.Reader);
+ uint unicodeMarker = ma.Reader.ReadUInt32();
+ if (unicodeMarker != 0x71B239F4)
+ {
+
+ }
+ }
+ else
+ {
+ throw new InvalidDataFormatException();
+ }
+
+ File _SummaryInformation = cdom.Folders["Root Entry"].Files["\u0005SummaryInformation"];
+ if (_SummaryInformation != null)
+ {
+ byte[] data = _SummaryInformation.GetData();
+ MemoryAccessor ma = new MemoryAccessor(data);
+
+ PropertyListObjectModel plom = new PropertyListObjectModel();
+ Document.Load(plom, new SummaryInformation.SummaryInformationDataFormat(), ma);
+ }
+
+ // we need to remember to push it back onto the stack
+ objectModels.Push(cdom);
+ }
+
+ private object ReadClipboardFormatOrAnsiString(Reader reader)
+ {
+ uint markerOrLength = reader.ReadUInt32();
+ if (markerOrLength == 0)
+ {
+ return null;
+ }
+ else if (markerOrLength == 0xFFFFFFFF || markerOrLength == 0xFFFFFFFE)
+ {
+ uint format = reader.ReadUInt32();
+ return format;
+ }
+ else
+ {
+ return reader.ReadFixedLengthString(markerOrLength).TrimNull();
+ }
+ }
+
+ private string ReadString32(Reader reader)
+ {
+ uint length = reader.ReadUInt32();
+ string value = reader.ReadFixedLengthString(length);
+ return value.TrimNull();
+ }
}
}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertyInfo.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertyInfo.cs
new file mode 100644
index 00000000..f3c119a4
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertyInfo.cs
@@ -0,0 +1,29 @@
+//
+// PropertyInfo.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
+{
+ public struct PropertyInfo
+ {
+ public uint Identifier { get; set; }
+ public uint Offset { get; set; }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertySetInfo.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertySetInfo.cs
new file mode 100644
index 00000000..cd349fd5
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertySetInfo.cs
@@ -0,0 +1,29 @@
+//
+// CompoundDocumentPropertySetInfo.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
+{
+ public struct PropertySetInfo
+ {
+ public Guid Guid { get; set; }
+ public uint Offset { get; set; }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertySetPropertyType.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertySetPropertyType.cs
new file mode 100644
index 00000000..78c25019
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/PropertySetPropertyType.cs
@@ -0,0 +1,169 @@
+//
+// PropertySetPropertyType.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
+{
+ public enum PropertySetPropertyType : ushort
+ {
+ ///
+ /// Type is undefined, and the minimum property set version is 0.
+ ///
+ Empty = 0x0000,
+ ///
+ /// Type is null, and the minimum property set version is 0.
+ ///
+ Null = 0x0001,
+ ///
+ /// Type is 16-bit signed integer, and the minimum property set version is 0.
+ ///
+ I2 = 0x0002,
+ ///
+ /// Type is 32-bit signed integer, and the minimum property set version is 0.
+ ///
+ I4 = 0x0003,
+ ///
+ /// Type is 4-byte (single-precision) IEEE floating-point number, and the
+ /// minimum property set version is 0.
+ ///
+ R4 = 0x0004,
+ ///
+ /// Type is 8-byte (double-precision) IEEE floating-point number, and the
+ /// minimum property set version is 0.
+ ///
+ R8 = 0x0005,
+ ///
+ /// Type is CURRENCY, and the minimum property set version is 0.
+ ///
+ Currency = 0x0006,
+ ///
+ /// Type is DATE (OLE Automation), and the minimum property set version is 0.
+ ///
+ Date = 0x0007,
+ ///
+ /// Type is CodePageString, and the minimum property set version is 0.
+ ///
+ BStr = 0x0008,
+ ///
+ /// Type is HRESULT, and the minimum property set version is 0.
+ ///
+ Error = 0x000A,
+ ///
+ /// Type is VARIANT_BOOL, and the minimum property set version is 0.
+ ///
+ Bool = 0x000B,
+ ///
+ /// Type is DECIMAL, and the minimum property set version is 0.
+ ///
+ Decimal = 0x000E,
+ ///
+ /// Type is 1-byte signed integer, and the minimum property set version is 1.
+ ///
+ I1 = 0x0010,
+ ///
+ /// Type is 1-byte unsigned integer, and the minimum property set version is 0.
+ ///
+ UI1 = 0x0011,
+ ///
+ /// Type is 2-byte unsigned integer, and the minimum property set version is 0.
+ ///
+ UI2 = 0x0012,
+ ///
+ /// Type is 4-byte unsigned integer, and the minimum property set version is 0.
+ ///
+ UI4 = 0x0013,
+ ///
+ /// Type is 8-byte signed integer, and the minimum property set version is 0.
+ ///
+ I8 = 0x0014,
+ ///
+ /// Type is 8-byte unsigned integer, and the minimum property set version is 0.
+ ///
+ UI8 = 0x0015,
+ ///
+ /// Type is 4-byte signed integer, and the minimum property set version is 1.
+ ///
+ Int = 0x0016,
+ ///
+ /// Type is 4-byte unsigned integer, and the minimum property set version is 1.
+ ///
+ UInt = 0x0017,
+ ///
+ /// Type is CodePageString, and the minimum property set version is 0.
+ ///
+ LPStr = 0x001E,
+ ///
+ /// Type is UnicodeString, and the minimum property set version is 0.
+ ///
+ LPWStr = 0x001F,
+ ///
+ /// Type is FILETIME, and the minimum property set version is 0.
+ ///
+ FileTime = 0x0040,
+ ///
+ /// Type is binary large object (BLOB), and the minimum property set version is 0.
+ ///
+ Blob = 0x0041,
+ ///
+ /// Type is Stream, and the minimum property set version is 0. VT_STREAM is not
+ /// allowed in a simple property set.
+ ///
+ Stream = 0x0042,
+ ///
+ /// Type is Storage, and the minimum property set version is 0. VT_STORAGE is not
+ /// allowed in a simple property set.
+ ///
+ Storage = 0x0043,
+ ///
+ /// Type is Stream representing an Object in an application-specific manner, and the
+ /// minimum property set version is 0. VT_STREAMED_Object is not allowed in a simple
+ /// property set.
+ ///
+ StreamedObject = 0x0044,
+ ///
+ /// Type is Storage representing an Object in an application-specific manner, and the
+ /// minimum property set version is 0. VT_STORED_Object is not allowed in a simple
+ /// property set.
+ ///
+ StoredObject = 0x0045,
+ ///
+ /// Type is BLOB representing an object in an application-specific manner. The minimum
+ /// property set version is 0.
+ ///
+ BlobObject = 0x0046,
+ ///
+ /// Type is PropertyIdentifier, and the minimum property set version is 0.
+ ///
+ PropertyIdentifier = 0x0047,
+ ///
+ /// Type is CLSID, and the minimum property set version is 0.
+ ///
+ CLSID = 0x0048,
+ ///
+ /// Type is Stream with application-specific version GUID (VersionedStream). The
+ /// minimum property set version is 0. VT_VERSIONED_STREAM is not allowed in a
+ /// simple property set.
+ ///
+ VersionedStream = 0x0049,
+
+ Vector = 0x1000,
+ Array = 0x2000
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/SummaryInformationDataFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/SummaryInformationDataFormat.cs
new file mode 100644
index 00000000..353f3120
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/CompoundDocument/SummaryInformation/SummaryInformationDataFormat.cs
@@ -0,0 +1,560 @@
+//
+// SummaryInformationDataFormat.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MBS.Framework;
+using UniversalEditor.IO;
+using UniversalEditor.ObjectModels.PropertyList;
+
+namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
+{
+ public class SummaryInformationDataFormat : DataFormat
+ {
+ private static DataFormatReference _dfr = null;
+ protected override DataFormatReference MakeReferenceInternal()
+ {
+ if (_dfr == null)
+ {
+ _dfr = base.MakeReferenceInternal();
+ _dfr.Capabilities.Add(typeof(PropertyListObjectModel), DataFormatCapabilities.All);
+ }
+ return _dfr;
+ }
+
+ public Endianness Endianness { get; set; } = Endianness.LittleEndian;
+ public ushort FormatVersion { get; set; } = 0;
+ public uint SystemIdentifier { get; set; } = 0x00020006;
+ public Guid ClassIdentifier { get; set; } = Guid.Empty;
+
+ protected override void LoadInternal(ref ObjectModel objectModel)
+ {
+ PropertyListObjectModel plom = (objectModel as PropertyListObjectModel);
+ if (plom == null)
+ {
+ throw new ObjectModelNotSupportedException();
+ }
+
+ Reader reader = Accessor.Reader;
+ ushort byteOrder = reader.ReadUInt16();
+ if (byteOrder == 0xFFFE)
+ {
+ Endianness = Endianness.LittleEndian;
+ }
+ else if (byteOrder == 0xFEFF)
+ {
+ Endianness = Endianness.BigEndian;
+ }
+ else
+ {
+ throw new InvalidDataFormatException("file must begin with a valid big-endian or little-endian byte order mark (BOM)");
+ }
+
+ FormatVersion = reader.ReadUInt16();
+ if (FormatVersion != 0)
+ {
+ throw new InvalidDataFormatException(String.Format("unsupported format version {0}", FormatVersion));
+ }
+
+ SystemIdentifier = reader.ReadUInt32();
+
+ ClassIdentifier = reader.ReadGuid();
+ uint propertySetCount = reader.ReadUInt32();
+
+ PropertySetInfo[] propertySetInfos = new PropertySetInfo[propertySetCount];
+ Group[] propertySetGroups = new Group[propertySetCount];
+ for (uint i = 0; i < propertySetCount; i++)
+ {
+ PropertySetInfo propertySetInfo = new PropertySetInfo();
+ propertySetInfo.Guid = reader.ReadGuid();
+ propertySetInfo.Offset = reader.ReadUInt32();
+ propertySetInfos[i] = propertySetInfo;
+
+ propertySetGroups[i] = new Group(propertySetInfo.Guid.ToString("B"));
+ propertySetGroups[i].SetExtraData("guid", propertySetInfo.Guid);
+ plom.Items.Add(propertySetGroups[i]);
+ }
+
+ for (int i = 0; i < propertySetInfos.Length; i++)
+ {
+ PropertySetInfo propertySetInfo = propertySetInfos[i];
+
+ // we should be at offset 48, if propertySetCount == 1
+ uint propertySetSize = reader.ReadUInt32();
+ uint propertySetPropertyCount = reader.ReadUInt32();
+
+ PropertyInfo[] pis = new PropertyInfo[propertySetPropertyCount];
+ for (uint j = 0; j < propertySetPropertyCount; j++)
+ {
+ PropertyInfo pi = new PropertyInfo();
+ pi.Identifier = reader.ReadUInt32();
+ pi.Offset = reader.ReadUInt32();
+ pis[j] = pi;
+
+ Property property = new Property(pi.Identifier.ToString());
+ property.SetExtraData("info", pi);
+ propertySetGroups[i].Items.Add(property);
+ }
+ }
+
+ for (int i = 0; i < propertySetInfos.Length; i++)
+ {
+ IEnumerable properties = propertySetGroups[i].Items.OfType();
+ foreach (Property property in properties)
+ {
+ PropertyInfo pi = property.GetExtraData("info");
+
+ Accessor.Seek(propertySetInfos[i].Offset + pi.Offset, SeekOrigin.Begin);
+
+ object value = ReadTypedValue(Accessor.Reader);
+ property.Value = value;
+ }
+ }
+ }
+
+ public static object ReadTypedValue(Reader reader)
+ {
+ PropertySetPropertyType propertyType = (PropertySetPropertyType)reader.ReadUInt16();
+ ushort padding = reader.ReadUInt16();
+ return ReadTypedValue(reader, propertyType);
+ }
+ public static object ReadTypedValue(Reader reader, PropertySetPropertyType propertyType)
+ {
+ object value = null;
+ switch (propertyType)
+ {
+ case PropertySetPropertyType.Empty:
+ {
+ break;
+ }
+ case PropertySetPropertyType.Null:
+ {
+ break;
+ }
+ case PropertySetPropertyType.I2:
+ {
+ value = reader.ReadInt16();
+ break;
+ }
+ case PropertySetPropertyType.I4:
+ {
+ value = reader.ReadInt32();
+ break;
+ }
+ case PropertySetPropertyType.R4:
+ {
+ value = reader.ReadSingle();
+ break;
+ }
+ case PropertySetPropertyType.R8:
+ {
+ value = reader.ReadDouble();
+ break;
+ }
+ case PropertySetPropertyType.Currency:
+ {
+ value = reader.ReadUInt64();
+ break;
+ }
+ case PropertySetPropertyType.Date:
+ {
+ value = DateTime.FromOADate(reader.ReadDouble());
+ break;
+ }
+ case PropertySetPropertyType.BStr:
+ {
+ value = ReadCodePageString(reader);
+ break;
+ }
+ case PropertySetPropertyType.Error:
+ {
+ value = reader.ReadUInt32();
+ break;
+ }
+ case PropertySetPropertyType.Bool:
+ {
+ value = reader.ReadBoolean();
+ break;
+ }
+ case PropertySetPropertyType.Decimal:
+ {
+ value = reader.ReadDecimal();
+ break;
+ }
+ case PropertySetPropertyType.I1:
+ {
+ value = reader.ReadSByte();
+ break;
+ }
+ case PropertySetPropertyType.UI1:
+ {
+ value = reader.ReadByte();
+ break;
+ }
+ case PropertySetPropertyType.UI2:
+ {
+ value = reader.ReadUInt16();
+ break;
+ }
+ case PropertySetPropertyType.UI4:
+ {
+ value = reader.ReadUInt32();
+ break;
+ }
+ case PropertySetPropertyType.I8:
+ {
+ value = reader.ReadInt64();
+ break;
+ }
+ case PropertySetPropertyType.UI8:
+ {
+ value = reader.ReadUInt64();
+ break;
+ }
+ case PropertySetPropertyType.Int:
+ {
+ value = reader.ReadInt32();
+ break;
+ }
+ case PropertySetPropertyType.UInt:
+ {
+ value = reader.ReadUInt32();
+ break;
+ }
+ case PropertySetPropertyType.LPStr:
+ {
+ value = ReadCodePageString(reader);
+ break;
+ }
+ case PropertySetPropertyType.LPWStr:
+ {
+ value = ReadUnicodeString(reader);
+ break;
+ }
+ case PropertySetPropertyType.FileTime:
+ {
+ uint lowDateTime = reader.ReadUInt32();
+ uint highDateTime = reader.ReadUInt32();
+ value = null;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ reader.Align(4);
+ return value;
+ }
+ public static void WriteTypedValue(Writer writer, object value)
+ {
+ if (value is byte)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.UI1);
+ }
+ else if (value is short)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.I2);
+ }
+ else if (value is int)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.I4);
+ }
+ else if (value is long)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.I8);
+ }
+ else if (value is sbyte)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.I1);
+ }
+ else if (value is ushort)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.UI2);
+ }
+ else if (value is uint)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.UI4);
+ }
+ else if (value is ulong)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.UI8);
+ }
+ else if (value is string)
+ {
+ WriteTypedValue(writer, value, PropertySetPropertyType.LPWStr);
+ }
+ else
+ {
+
+ }
+ }
+ public static void WriteTypedValue(Writer writer, object value, PropertySetPropertyType propertyType)
+ {
+ writer.WriteUInt32((uint)propertyType);
+ switch (propertyType)
+ {
+ case PropertySetPropertyType.Empty:
+ {
+ break;
+ }
+ case PropertySetPropertyType.Null:
+ {
+ break;
+ }
+ case PropertySetPropertyType.I2:
+ {
+ writer.WriteInt16((short)value);
+ break;
+ }
+ case PropertySetPropertyType.I4:
+ {
+ writer.WriteInt32((int)value);
+ break;
+ }
+ case PropertySetPropertyType.R4:
+ {
+ writer.WriteSingle((float)value);
+ break;
+ }
+ case PropertySetPropertyType.R8:
+ {
+ writer.WriteDouble((double)value);
+ break;
+ }
+ case PropertySetPropertyType.Currency:
+ {
+ writer.WriteUInt64((ulong)value);
+ break;
+ }
+ case PropertySetPropertyType.Date:
+ {
+ writer.WriteDouble(((DateTime)value).ToOADate());
+ break;
+ }
+ case PropertySetPropertyType.BStr:
+ {
+ WriteCodePageString(writer, (string)value);
+ break;
+ }
+ case PropertySetPropertyType.Error:
+ {
+ writer.WriteUInt32((uint)value);
+ break;
+ }
+ case PropertySetPropertyType.Bool:
+ {
+ writer.WriteBoolean((bool)value);
+ break;
+ }
+ case PropertySetPropertyType.Decimal:
+ {
+ writer.WriteDecimal((decimal)value);
+ break;
+ }
+ case PropertySetPropertyType.I1:
+ {
+ writer.WriteSByte((sbyte)value);
+ break;
+ }
+ case PropertySetPropertyType.UI1:
+ {
+ writer.WriteByte((byte)value);
+ break;
+ }
+ case PropertySetPropertyType.UI2:
+ {
+ writer.WriteUInt16((ushort)value);
+ break;
+ }
+ case PropertySetPropertyType.UI4:
+ {
+ writer.WriteUInt32((uint)value);
+ break;
+ }
+ case PropertySetPropertyType.I8:
+ {
+ writer.WriteInt64((long)value);
+ break;
+ }
+ case PropertySetPropertyType.UI8:
+ {
+ writer.WriteUInt64((ulong)value);
+ break;
+ }
+ case PropertySetPropertyType.Int:
+ {
+ writer.WriteInt32((int)value);
+ break;
+ }
+ case PropertySetPropertyType.UInt:
+ {
+ writer.WriteUInt32((uint)value);
+ break;
+ }
+ case PropertySetPropertyType.LPStr:
+ {
+ WriteCodePageString(writer, (string)value);
+ break;
+ }
+ case PropertySetPropertyType.LPWStr:
+ {
+ WriteUnicodeString(writer, (string)value);
+ break;
+ }
+ case PropertySetPropertyType.FileTime:
+ {
+ uint lowDateTime = 0;
+ uint highDateTime = 0;
+ writer.WriteUInt32(lowDateTime);
+ writer.WriteUInt32(highDateTime);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ writer.Align(4);
+ }
+
+ public static string ReadUnicodeString(Reader reader)
+ {
+ uint length = reader.ReadUInt32();
+ return reader.ReadFixedLengthString(length * 2, Encoding.UTF16LittleEndian);
+ }
+ public static void WriteUnicodeString(Writer writer, string value)
+ {
+ uint length = (uint)value.Length;
+ writer.WriteUInt32(length);
+ writer.WriteFixedLengthString(value, Encoding.UTF16LittleEndian);
+ }
+
+ public static string ReadCodePageString(Reader reader)
+ {
+ uint length = reader.ReadUInt32();
+ return reader.ReadFixedLengthString(length);
+ }
+ public static void WriteCodePageString(Writer writer, string value)
+ {
+ uint length = (uint)value.Length;
+ writer.WriteUInt32(length);
+ writer.WriteFixedLengthString(value);
+ }
+
+ protected override void SaveInternal(ObjectModel objectModel)
+ {
+ PropertyListObjectModel plom = (objectModel as PropertyListObjectModel);
+ if (plom == null)
+ {
+ throw new ObjectModelNotSupportedException();
+ }
+
+ switch (Endianness)
+ {
+ case Endianness.LittleEndian:
+ {
+ Accessor.Writer.WriteUInt16(0xFFFE);
+ break;
+ }
+ case Endianness.BigEndian:
+ {
+ Accessor.Writer.WriteUInt16(0xFEFF);
+ break;
+ }
+ default:
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ Accessor.Writer.WriteUInt16(FormatVersion);
+ Accessor.Writer.WriteUInt32(SystemIdentifier);
+
+ Accessor.Writer.WriteGuid(ClassIdentifier);
+
+ IEnumerable groups = plom.Items.OfType();
+ Accessor.Writer.WriteUInt32((uint)groups.Count());
+ uint offset = (uint)(28 + (20 * groups.Count()));
+
+ foreach (Group group in groups)
+ {
+ Accessor.Writer.WriteGuid(group.GetExtraData("guid"));
+ Accessor.Writer.WriteUInt32(offset);
+
+ IEnumerable properties = group.Items.OfType();
+ offset += (uint)(8 + (8 * properties.Count()));
+ }
+
+ offset -= (uint) Accessor.Position;
+
+ foreach (Group group in groups)
+ {
+ IEnumerable properties = group.Items.OfType();
+
+ uint propertySetSize = (uint)(8 + (8 * properties.Count()));
+ foreach (Property property in properties)
+ {
+ propertySetSize += CalculatePropertySize(property);
+ }
+ Accessor.Writer.WriteUInt32(propertySetSize);
+ Accessor.Writer.WriteUInt32((uint)properties.Count());
+
+ foreach (Property property in properties)
+ {
+ uint identifier = 0;
+ if (!UInt32.TryParse(property.Name, out identifier))
+ {
+ identifier = 0;
+ }
+ Accessor.Writer.WriteUInt32(identifier);
+
+ Accessor.Writer.WriteUInt32(offset);
+ offset += CalculatePropertySize(property);
+ }
+ }
+
+ foreach (Group group in groups)
+ {
+ IEnumerable properties = group.Items.OfType();
+
+ foreach (Property property in properties)
+ {
+ WriteTypedValue(Accessor.Writer, property.Value);
+ }
+ }
+ }
+
+ private uint CalculatePropertySize(Property property)
+ {
+ uint size = 4;
+ if (property.Value != null)
+ {
+ if (property.Value is string)
+ {
+ size += (uint)(4 + ((string)property.Value).Length).Align(4);
+ }
+ size += (uint)property.Value.SizeOf().Align(4);
+ }
+ return size;
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentBaseDataFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentBaseDataFormat.cs
index a5b30759..2f498ee6 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentBaseDataFormat.cs
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentBaseDataFormat.cs
@@ -41,77 +41,32 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
{
_dfr = new DataFormatReference(GetType());
_dfr.Capabilities.Add(typeof(FileSystemObjectModel), DataFormatCapabilities.All);
- _dfr.ExportOptions.SettingsGroups[0].Settings.Add(new RangeSetting(nameof(SectorSize), "_Sector size (in bytes)", 512, 128));
- _dfr.ExportOptions.SettingsGroups[0].Settings.Add(new RangeSetting(nameof(ShortSectorSize), "S_hort sector size (in bytes)", 64));
- _dfr.ExportOptions.SettingsGroups[0].Settings.Add(new RangeSetting(nameof(MinimumStandardStreamSize), "_Minimum standard stream size (in bytes)", 4096, 4096));
+ _dfr.ExportOptions.SettingsGroups[0].Settings.Add(new RangeSetting(nameof(SectorSize), "_Sector size (in bytes)", 512, 128));
+ _dfr.ExportOptions.SettingsGroups[0].Settings.Add(new RangeSetting(nameof(ShortSectorSize), "S_hort sector size (in bytes)", 64));
+ _dfr.ExportOptions.SettingsGroups[0].Settings.Add(new RangeSetting(nameof(MinimumStandardStreamSize), "_Minimum standard stream size (in bytes)", 4096, 4096));
_dfr.Sources.Add("http://www.openoffice.org/sc/compdocfileformat.pdf");
}
return _dfr;
}
- private Guid mvarUniqueIdentifier = Guid.Empty;
- public Guid UniqueIdentifier { get { return mvarUniqueIdentifier; } set { mvarUniqueIdentifier = value; } }
-
- private Version mvarFormatVersion = new Version(3, 62);
- public Version FormatVersion { get { return mvarFormatVersion; } set { mvarFormatVersion = value; } }
-
- private Endianness mvarEndianness = Endianness.LittleEndian;
- public Endianness Endianness { get { return mvarEndianness; } set { mvarEndianness = value; } }
-
- private uint mvarSectorSize = 512;
- public uint SectorSize { get { return mvarSectorSize; } set { mvarSectorSize = value; } }
-
- private uint mvarShortSectorSize = 64;
- public uint ShortSectorSize { get { return mvarShortSectorSize; } set { mvarShortSectorSize = value; } }
-
- ///
- /// Total number of sectors used for the sector allocation table
- ///
- private uint mvarSectorAllocationTableSize = 0;
- ///
- /// Sector ID of the first sector of the directory stream
- ///
- private uint mvarDirectoryStreamFirstSectorID = 0;
-
- private uint mvarMinimumStandardStreamSize = 4096;
- ///
- /// Minimum size of a standard stream (in bytes). Minimum allowed and most-used size is
- /// 4096 bytes. Streams with an actual size smaller than (and not equal to) this value
- /// are stored as short-streams.
- ///
- public uint MinimumStandardStreamSize { get { return mvarMinimumStandardStreamSize; } set { mvarMinimumStandardStreamSize = value; } }
-
- ///
- /// Sector ID of the first sector of the short-sector allocation table (or
- /// if not extant).
- ///
- private int mvarShortSectorAllocationTableFirstSectorID = 0;
- ///
- /// Total number of sectors used for the short-sector allocation table.
- ///
- private int mvarShortSectorAllocationTableSize = 0;
- ///
- /// Sector ID of the first sector of the master sector allocation table (or
- /// if no additional sectors
- /// used).
- ///
- private int mvarMasterSectorAllocationTableFirstSectorID = 0;
- ///
- /// Total number of sectors used for the master sector allocation table.
- ///
- private int mvarMasterSectorAllocationTableSize = 0;
+ public Guid UniqueIdentifier { get; set; } = Guid.Empty;
+ public Version FormatVersion { get; set; } = new Version(3, 62);
+ public Endianness Endianness { get; set; } = Endianness.LittleEndian;
+ public uint SectorSize { get; set; } = 512;
+ public uint ShortSectorSize { get; set; } = 64;
+ public uint MinimumStandardStreamSize { get; set; } = 4096;
private int mvarShortSectorFirstSectorID = 0;
private int GetSectorPositionFromSectorID(int sectorID)
{
if (sectorID < 0) return 0;
- return (int)(512 + (sectorID * mvarSectorSize));
+ return (int)(512 + (sectorID * SectorSize));
}
private int GetShortSectorPositionFromSectorID(int sectorID)
{
if (sectorID < 0) return 0;
- return (int)(sectorID * mvarShortSectorSize);
+ return (int)(sectorID * ShortSectorSize);
}
private byte[] mvarShortSectorContainerStreamData = null;
@@ -124,6 +79,8 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
private List shortSectorAllocationTableSectors = new List();
private int[] sectorAllocationTable = new int[0];
+ private Folder rootEntry = null;
+
protected override void LoadInternal(ref ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
@@ -131,65 +88,67 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
Reader reader = base.Accessor.Reader;
- // The header is always located at the beginning of the file, and its size is
- // exactly 512 bytes. This implies that the first sector (0) always starts at
- // file offset 512.
- byte[] signature = reader.ReadBytes(8);
- if (!signature.Match(VALID_SIGNATURE))
+ CompoundDocumentHeader header = ReadCompoundDocumentHeader(reader);
+ if (!header.Signature.Match(VALID_SIGNATURE))
{
throw new InvalidDataFormatException("File does not begin with { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }");
}
- mvarUniqueIdentifier = reader.ReadGuid();
+ UniqueIdentifier = header.UniqueIdentifier;
- ushort MinorVersion = reader.ReadUInt16();
- ushort MajorVersion = reader.ReadUInt16();
- mvarFormatVersion = new Version(MajorVersion, MinorVersion);
-
- byte[] ByteOrderIdentifier = reader.ReadBytes(2);
- if (ByteOrderIdentifier[0] == 0xFE && ByteOrderIdentifier[1] == 0xFF)
+ FormatVersion = new Version(header.MajorVersion, header.MinorVersion);
+ if (FormatVersion.Major == 0x0003 || FormatVersion.Major == 0x0004)
{
- mvarEndianness = Endianness.LittleEndian;
+ if (FormatVersion.Minor != 0x003E)
+ {
+ // sanity check
+ }
}
- else if (ByteOrderIdentifier[0] == 0xFF && ByteOrderIdentifier[1] == 0xFE)
+
+ if (header.ByteOrderIdentifier[0] == 0xFE && header.ByteOrderIdentifier[1] == 0xFF)
{
- mvarEndianness = Endianness.BigEndian;
+ Endianness = Endianness.LittleEndian;
+ }
+ else if (header.ByteOrderIdentifier[0] == 0xFF && header.ByteOrderIdentifier[1] == 0xFE)
+ {
+ Endianness = Endianness.BigEndian;
}
else
{
- throw new InvalidDataFormatException("Invalid value for byte order (" + ByteOrderIdentifier[0].ToString("X").PadLeft(2, '0') + ", " + ByteOrderIdentifier[1].ToString("X").PadLeft(2, '0') + ")");
+ throw new InvalidDataFormatException("Invalid value for byte order (" + header.ByteOrderIdentifier[0].ToString("X").PadLeft(2, '0') + ", " + header.ByteOrderIdentifier[1].ToString("X").PadLeft(2, '0') + ")");
}
- ushort uSectorSize = reader.ReadUInt16();
- mvarSectorSize = (uint)(Math.Pow(2, uSectorSize));
+ SectorSize = (uint)(Math.Pow(2, header.SectorSize));
- ushort uShortSectorSize = reader.ReadUInt16();
- mvarShortSectorSize = (uint)(Math.Pow(2, uShortSectorSize));
+ // If Major Version is 3, the Sector Shift MUST be 0x0009, specifying a sector size of 512 bytes.
+ // If Major Version is 4, the Sector Shift MUST be 0x000C, specifying a sector size of 4096 bytes.
+
+ ShortSectorSize = (uint)(Math.Pow(2, header.ShortSectorSize));
+
+ // This field MUST be set to 0x0006. This field specifies the sector size of
+ // the Mini Stream as a power of 2.The sector size of the Mini Stream MUST be 64 bytes.
if (ShortSectorSize > SectorSize) throw new InvalidDataFormatException("Short sector size (" + ShortSectorSize.ToString() + ") exceeds sector size (" + SectorSize.ToString() + ")");
- byte[] unused1 = reader.ReadBytes(10);
+ if (FormatVersion.Major == 3)
+ {
+ if (header.DirectorySectorCount != 0)
+ {
+ // If Major Version is 3, the Number of Directory Sectors MUST be zero. This field is not
+ // supported for version 3 compound files.
+ }
+ }
- mvarSectorAllocationTableSize = reader.ReadUInt32();
- mvarDirectoryStreamFirstSectorID = reader.ReadUInt32();
- uint unused2 = reader.ReadUInt32();
- mvarMinimumStandardStreamSize = reader.ReadUInt32();
-
- mvarShortSectorAllocationTableFirstSectorID = reader.ReadInt32();
- mvarShortSectorAllocationTableSize = reader.ReadInt32();
-
- // SecID of first sector of the master sector allocation table, or –2 (End Of Chain) if no additional sectors used
- mvarMasterSectorAllocationTableFirstSectorID = reader.ReadInt32();
- // Total number of sectors used for the master sector allocation table
- mvarMasterSectorAllocationTableSize = reader.ReadInt32();
+ MinimumStandardStreamSize = header.MinimumStandardStreamSize;
#region Read Master Sector Allocation Table
// First part of the master sector allocation table, containing 109 SecIDs
- int[] masterSectorAllocationTable = reader.ReadInt32Array(109);
// TODO: test this! when MSAT contains more than 109 SecIDs
- int countForMSAT = (int)((double)mvarSectorSize / 4);
- int nextSectorForMSAT = mvarMasterSectorAllocationTableFirstSectorID;
- int nextPositionForMSAT = masterSectorAllocationTable.Length;
+ int countForMSAT = (int)((double)SectorSize / 4);
+ int nextSectorForMSAT = header.MasterSectorAllocationTableFirstSectorID;
+ int nextPositionForMSAT = header.MasterSectorAllocationTable.Length;
+
+ int[] masterSectorAllocationTable = header.MasterSectorAllocationTable;
while (nextSectorForMSAT != (int)CompoundDocumentKnownSectorID.EndOfChain)
{
@@ -201,7 +160,7 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
}
#endregion
#region Read Sector Allocation Table
- sectorAllocationTable = new int[(int)(mvarSectorSize / 4)];
+ sectorAllocationTable = new int[(int)(SectorSize / 4)];
for (int i = 0; i < masterSectorAllocationTable.Length; i++)
{
if (masterSectorAllocationTable[i] == -1)
@@ -211,21 +170,21 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
// last SecID is the special End Of Chain SecID with the value –2( ➜ 3.1).
SeekToSector(masterSectorAllocationTable[i]);
- int[] sectorAllocationTablePart = reader.ReadInt32Array((int)(mvarSectorSize / 4));
+ int[] sectorAllocationTablePart = reader.ReadInt32Array((int)(SectorSize / 4));
Array.Resize(ref sectorAllocationTable, sectorAllocationTable.Length + sectorAllocationTablePart.Length);
- Array.Copy(sectorAllocationTablePart, 0, sectorAllocationTable, (int)(i * (mvarSectorSize / 4)), sectorAllocationTablePart.Length);
+ Array.Copy(sectorAllocationTablePart, 0, sectorAllocationTable, (int)(i * (SectorSize / 4)), sectorAllocationTablePart.Length);
}
#endregion
// read directory entries - each entry is 128 bytes
- byte[] directoryData = ReadDirectoryData(reader);
+ byte[] directoryData = ReadDirectoryData(reader, header);
#region Read Short Sector Allocation Table
shortSectorAllocationTableSectors = new List();
- if (mvarShortSectorAllocationTableFirstSectorID >= 0)
+ if (header.ShortSectorAllocationTableFirstSectorID >= 0)
{
- int sector = mvarShortSectorAllocationTableFirstSectorID;
+ int sector = header.ShortSectorAllocationTableFirstSectorID;
while (sector != -2)
{
shortSectorAllocationTableSectors.Add(sector);
@@ -240,61 +199,29 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
}
}
- shortSectorAllocationTable = new int[(mvarSectorSize / 4) * shortSectorAllocationTableSectors.Count];
- byte[] shortSectorAllocationTableData = new byte[mvarSectorSize * shortSectorAllocationTableSectors.Count];
+ shortSectorAllocationTable = new int[(SectorSize / 4) * shortSectorAllocationTableSectors.Count];
+ byte[] shortSectorAllocationTableData = new byte[SectorSize * shortSectorAllocationTableSectors.Count];
for (int i = 0; i < shortSectorAllocationTableSectors.Count; i++)
{
SeekToSector(shortSectorAllocationTableSectors[i]);
- int[] tablePart = reader.ReadInt32Array((int)(mvarSectorSize / 4));
+ int[] tablePart = reader.ReadInt32Array((int)(SectorSize / 4));
Array.Copy(tablePart, 0, shortSectorAllocationTable, (i * tablePart.Length), tablePart.Length);
}
#endregion
#region Read Sector Directory Entries
Accessors.MemoryAccessor maDirectory = new Accessors.MemoryAccessor(directoryData);
Reader directoryReader = new Reader(maDirectory);
+
+ List listHeaders = new List();
while (!directoryReader.EndOfStream)
{
- // The first directory entry always represents the root storage entry
- byte[] storageNameBytes = directoryReader.ReadBytes(64);
- ushort storageNameLength = directoryReader.ReadUInt16();
+ CompoundDocumentStorageHeader sh = ReadStorageHeader(directoryReader);
- byte[] storageNameValidBytes = new byte[storageNameLength];
- Array.Copy(storageNameBytes, 0, storageNameValidBytes, 0, storageNameValidBytes.Length);
-
- string storageName = System.Text.Encoding.Unicode.GetString(storageNameValidBytes);
- storageName = storageName.TrimNull();
- // if (storageName.Length != storageNameLength) throw new InvalidDataFormatException("Sanity check: storage name length is not actual length of storage name");
-
- CompoundDocumentStorageType storageType = (CompoundDocumentStorageType)directoryReader.ReadByte();
- byte storageNodeColor = directoryReader.ReadByte();
-
- int leftChildNodeDirectoryID = directoryReader.ReadInt32();
- int rightChildNodeDirectoryID = directoryReader.ReadInt32();
- // directory ID of the root node entry of the red-black tree of all members of the root storage
- int rootNodeEntryDirectoryID = directoryReader.ReadInt32();
-
- Guid uniqueIdentifier = directoryReader.ReadGuid();
- uint flags = directoryReader.ReadUInt32();
- long creationTimestamp = directoryReader.ReadInt64();
- long lastModificationTimestamp = directoryReader.ReadInt64();
-
- // SecID of first sector or short-sector, if this entry refers to a stream
- // SecID of first sector of the short-stream container stream, if this is the root storage entry
- // 0 otherwise
- int firstSectorOfStream = directoryReader.ReadInt32();
-
- // Total stream size in bytes, if this entry refers to a stream,
- // total size of the short-stream container stream, if this is the root storage entry
- // 0 otherwise
- int streamLength = directoryReader.ReadInt32();
-
- int unused3 = directoryReader.ReadInt32();
-
- if (storageType == CompoundDocumentStorageType.RootStorage)
+ if (sh.StorageType == CompoundDocumentStorageType.RootStorage)
{
// this is the root storage entry
- mvarShortSectorFirstSectorID = firstSectorOfStream;
+ mvarShortSectorFirstSectorID = sh.FirstSectorIndex;
#region Read Short Stream Container Stream
List shortStreamContainerStreamSectors = new List();
@@ -313,42 +240,46 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
}
}
}
- byte[] shortStreamContainerStreamData = new byte[shortStreamContainerStreamSectors.Count * mvarSectorSize];
+ byte[] shortStreamContainerStreamData = new byte[shortStreamContainerStreamSectors.Count * SectorSize];
int i = 0;
foreach (int sector in shortStreamContainerStreamSectors)
{
int wpos = GetSectorPositionFromSectorID(sector);
reader.Seek(wpos, SeekOrigin.Begin);
- byte[] sectorData = reader.ReadBytes(mvarSectorSize);
+ byte[] sectorData = reader.ReadBytes(SectorSize);
Array.Copy(sectorData, 0, shortStreamContainerStreamData, i, sectorData.Length);
i += sectorData.Length;
}
mvarShortSectorContainerStreamData = shortStreamContainerStreamData;
#endregion
+
+ rootEntry = new Folder();
+ rootEntry.Name = sh.Name; // MUST be "Root Entry", according to spec, but we want to be versatile
+ fsom.Folders.Add(rootEntry);
}
- else if (storageType == CompoundDocumentStorageType.UserStorage)
+ else if (sh.StorageType == CompoundDocumentStorageType.UserStorage)
{
}
- else if (storageType == CompoundDocumentStorageType.UserStream)
+ else if (sh.StorageType == CompoundDocumentStorageType.UserStream)
{
File file = new File();
- file.Name = storageName;
- file.Size = streamLength;
+ file.Name = sh.Name;
+ file.Size = sh.Length;
file.Properties.Add("reader", reader);
- file.Properties.Add("firstSector", firstSectorOfStream);
- file.Properties.Add("length", streamLength);
+ file.Properties.Add("firstSector", sh.FirstSectorIndex);
+ file.Properties.Add("length", sh.Length);
file.DataRequest += file_DataRequest;
- fsom.Files.Add(file);
+ rootEntry.Files.Add(file);
- Console.WriteLine("{3} {0} {1} {2}", file.Name, firstSectorOfStream, streamLength, storageType.ToString());
+ Console.WriteLine("{3} {0} {1} {2}", file.Name, sh.FirstSectorIndex, sh.Length, sh.StorageType.ToString());
}
- else if (storageType == CompoundDocumentStorageType.Empty)
+ else if (sh.StorageType == CompoundDocumentStorageType.Empty)
{
- Console.WriteLine(storageName + " is empty; should we included it in file list?");
+ Console.WriteLine(String.Format("{0} is empty; should we included it in file list?", sh.Name));
}
else
{
- throw new NotImplementedException(storageType.ToString() + " not implemented");
+ throw new NotImplementedException(String.Format("storage type {0} not implemented", sh.StorageType));
}
}
#endregion
@@ -356,6 +287,131 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
WriteLog(fsom);
}
+ private CompoundDocumentHeader ReadCompoundDocumentHeader(Reader reader)
+ {
+ CompoundDocumentHeader header = new CompoundDocumentHeader();
+
+ // The header is always located at the beginning of the file, and its size is
+ // exactly 512 bytes. This implies that the first sector (0) always starts at
+ // file offset 512.
+ header.Signature = reader.ReadBytes(8);
+ header.UniqueIdentifier = reader.ReadGuid();
+ header.MinorVersion = reader.ReadUInt16();
+ header.MajorVersion = reader.ReadUInt16();
+ header.ByteOrderIdentifier = reader.ReadBytes(2);
+ header.SectorSize = reader.ReadUInt16();
+ header.ShortSectorSize = reader.ReadUInt16();
+ header.Unused1 = reader.ReadBytes(6);
+ header.DirectorySectorCount = reader.ReadUInt32();
+
+ header.SectorAllocationTableSize = reader.ReadUInt32();
+ header.DirectoryStreamFirstSectorID = reader.ReadUInt32();
+ header.TransactionSignatureNumber = reader.ReadUInt32();
+ header.MinimumStandardStreamSize = reader.ReadUInt32();
+
+ header.ShortSectorAllocationTableFirstSectorID = reader.ReadInt32();
+ header.ShortSectorAllocationTableSize = reader.ReadInt32();
+
+ // SecID of first sector of the master sector allocation table, or –2 (End Of Chain) if no additional sectors used
+ header.MasterSectorAllocationTableFirstSectorID = reader.ReadInt32();
+ // Total number of sectors used for the master sector allocation table
+ header.MasterSectorAllocationTableSize = reader.ReadInt32();
+
+ header.MasterSectorAllocationTable = reader.ReadInt32Array(109);
+
+ return header;
+ }
+
+ private CompoundDocumentStorageHeader ReadStorageHeader(Reader directoryReader)
+ {
+ CompoundDocumentStorageHeader sh = new CompoundDocumentStorageHeader();
+
+ // The first directory entry always represents the root storage entry
+ byte[] storageNameBytes = directoryReader.ReadBytes(64);
+ ushort storageNameLength = directoryReader.ReadUInt16();
+
+ byte[] storageNameValidBytes = new byte[storageNameLength];
+ Array.Copy(storageNameBytes, 0, storageNameValidBytes, 0, storageNameValidBytes.Length);
+
+ string storageName = System.Text.Encoding.Unicode.GetString(storageNameValidBytes);
+ storageName = storageName.TrimNull();
+ // if (storageName.Length != storageNameLength) throw new InvalidDataFormatException("Sanity check: storage name length is not actual length of storage name");
+ sh.Name = storageName;
+
+ sh.StorageType = (CompoundDocumentStorageType)directoryReader.ReadByte();
+ sh.NodeColor = (CompoundDocumentStorageColor) directoryReader.ReadByte();
+
+ sh.LeftChildNodeDirectoryID = directoryReader.ReadInt32();
+ sh.RightChildNodeDirectoryID = directoryReader.ReadInt32();
+ // directory ID of the root node entry of the red-black tree of all members of the root storage
+ sh.RootNodeEntryDirectoryID = directoryReader.ReadInt32();
+
+ sh.UniqueIdentifier = directoryReader.ReadGuid();
+ sh.Flags = (CompoundDocumentStorageFlags) directoryReader.ReadUInt32();
+ sh.CreationTimestamp = ReadDateTime(directoryReader);
+ sh.ModificationTimestamp = ReadDateTime(directoryReader);
+
+ // SecID of first sector or short-sector, if this entry refers to a stream
+ // SecID of first sector of the short-stream container stream, if this is the root storage entry
+ // 0 otherwise
+ sh.FirstSectorIndex = directoryReader.ReadInt32();
+
+ // Total stream size in bytes, if this entry refers to a stream,
+ // total size of the short-stream container stream, if this is the root storage entry
+ // 0 otherwise
+ sh.Length = directoryReader.ReadInt32();
+
+ sh.Unused3 = directoryReader.ReadInt32();
+ return sh;
+ }
+ private void WriteStorageHeader(Writer directoryWriter, CompoundDocumentStorageHeader sh)
+ {
+ // The first directory entry always represents the root storage entry
+ byte[] storageNameValidBytes = System.Text.Encoding.Unicode.GetBytes(sh.Name);
+ byte[] storageNameBytes = new byte[64];
+ Array.Copy(storageNameValidBytes, 0, storageNameBytes, 0, Math.Min(storageNameValidBytes.Length, storageNameBytes.Length));
+
+ directoryWriter.WriteBytes(storageNameBytes);
+ directoryWriter.WriteUInt16((ushort)sh.Name.Length);
+
+ directoryWriter.WriteByte((byte)sh.StorageType);
+ directoryWriter.WriteByte((byte)sh.NodeColor);
+
+ directoryWriter.WriteInt32(sh.LeftChildNodeDirectoryID);
+ directoryWriter.WriteInt32(sh.RightChildNodeDirectoryID);
+ // directory ID of the root node entry of the red-black tree of all members of the root storage
+ directoryWriter.WriteInt32(sh.RootNodeEntryDirectoryID);
+
+ directoryWriter.WriteGuid(sh.UniqueIdentifier);
+ directoryWriter.WriteUInt32((uint)sh.Flags);
+ WriteDateTime(directoryWriter, sh.CreationTimestamp);
+ WriteDateTime(directoryWriter, sh.ModificationTimestamp);
+
+ // SecID of first sector or short-sector, if this entry refers to a stream
+ // SecID of first sector of the short-stream container stream, if this is the root storage entry
+ // 0 otherwise
+ directoryWriter.WriteInt32(sh.FirstSectorIndex);
+
+ // Total stream size in bytes, if this entry refers to a stream,
+ // total size of the short-stream container stream, if this is the root storage entry
+ // 0 otherwise
+ directoryWriter.WriteInt32(sh.Length);
+
+ directoryWriter.WriteInt32(sh.Unused3);
+ }
+
+ private void WriteDateTime(Writer directoryWriter, DateTime creationTimestamp)
+ {
+ long value = 0;
+ directoryWriter.WriteInt64(value);
+ }
+
+ private DateTime ReadDateTime(Reader reader)
+ {
+ long value = reader.ReadInt64();
+ return DateTime.Now;
+ }
+
private void WriteLog(FileSystemObjectModel fsom)
{
if (LogPath == null) return;
@@ -369,10 +425,10 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
}
}
- private byte[] ReadDirectoryData(Reader reader)
+ private byte[] ReadDirectoryData(Reader reader, CompoundDocumentHeader header)
{
List directorySectors = new List();
- int currentSector = (int)mvarDirectoryStreamFirstSectorID;
+ int currentSector = (int)header.DirectoryStreamFirstSectorID;
while (currentSector != -2)
{
directorySectors.Add(currentSector);
@@ -386,12 +442,12 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
}
}
- byte[] directoryData = new byte[mvarSectorSize * directorySectors.Count];
+ byte[] directoryData = new byte[SectorSize * directorySectors.Count];
for (int i = 0; i < directorySectors.Count; i++)
{
SeekToSector(directorySectors[i]);
- byte[] sectorData = reader.ReadBytes(mvarSectorSize);
- Array.Copy(sectorData, 0, directoryData, (i * mvarSectorSize), mvarSectorSize);
+ byte[] sectorData = reader.ReadBytes(SectorSize);
+ Array.Copy(sectorData, 0, directoryData, (i * SectorSize), SectorSize);
}
return directoryData;
}
@@ -412,7 +468,7 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
List sectors = new List();
byte[] sectorData = null;
- if (streamLength < mvarMinimumStandardStreamSize)
+ if (streamLength < MinimumStandardStreamSize)
{
// use the short-sector allocation table
int sector = firstSectorOfStream;
@@ -434,6 +490,8 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
{
Array.Copy(mvarShortSectorContainerStreamData, (sectors[i] * ShortSectorSize), sectorData, i * ShortSectorSize, ShortSectorSize);
}
+
+ Array.Resize(ref sectorData, streamLength);
}
else
{
@@ -528,50 +586,100 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
Writer writer = base.Accessor.Writer;
- // The header is always located at the beginning of the file, and its size is
- // exactly 512 bytes. This implies that the first sector (0) always starts at
- // file offset 512.
- writer.WriteBytes(VALID_SIGNATURE);
- writer.WriteGuid(UniqueIdentifier);
-
- writer.WriteUInt16((ushort)FormatVersion.Minor);
- writer.WriteUInt16((ushort)FormatVersion.Major);
-
+ CompoundDocumentHeader header = new CompoundDocumentHeader();
+ header.Signature = VALID_SIGNATURE;
+ header.UniqueIdentifier = UniqueIdentifier;
+ header.MinorVersion = (ushort)FormatVersion.Minor;
+ header.MajorVersion = (ushort)FormatVersion.Major;
switch (Endianness)
{
- case Endianness.LittleEndian:
+ case Endianness.LittleEndian:
{
- writer.WriteBytes(new byte[] { 0xFE, 0xFF });
+ header.ByteOrderIdentifier = new byte[] { 0xFE, 0xFF };
break;
}
- case Endianness.BigEndian:
+ case Endianness.BigEndian:
{
- writer.WriteBytes(new byte[] { 0xFF, 0xFE });
+ header.ByteOrderIdentifier = new byte[] { 0xFF, 0xFE };
break;
}
}
+ WriteCompoundDocumentHeader(writer, header);
+
+
+ if (fsom.Folders.Count != 1)
+ {
+ throw new ObjectModelNotSupportedException("underlying File System must contain exactly ONE root folder, which should be named 'Root Entry'");
+ }
+ if (fsom.Folders[0].Name != "Root Entry")
+ {
+ // we should probably warn the user that this is not kosher, but we will happily write it
+ }
+
+ // FIXME: this is NOT correct
+ SeekToSector(16);
+
+ CompoundDocumentStorageHeader shRootEntry = new CompoundDocumentStorageHeader();
+ shRootEntry.Name = fsom.Folders[0].Name;
+ WriteStorageHeader(writer, shRootEntry);
+
+ foreach (File f in fsom.Folders[0].Files)
+ {
+ CompoundDocumentStorageHeader sh = new CompoundDocumentStorageHeader();
+ sh.Name = f.Name;
+ sh.StorageType = CompoundDocumentStorageType.UserStream;
+ sh.NodeColor = CompoundDocumentStorageColor.Red;
+ sh.LeftChildNodeDirectoryID = 0;
+ sh.RightChildNodeDirectoryID = 0;
+ sh.RootNodeEntryDirectoryID = 0;
+ sh.UniqueIdentifier = Guid.Empty;
+ sh.Flags = CompoundDocumentStorageFlags.None;
+ sh.CreationTimestamp = DateTime.Now;
+ sh.ModificationTimestamp = f.ModificationTimestamp;
+ sh.FirstSectorIndex = 0;
+ sh.Length = (int)f.Size;
+ sh.Unused3 = 0;
+
+ WriteStorageHeader(writer, sh);
+ }
+ }
+
+ private void WriteCompoundDocumentHeader(Writer writer, CompoundDocumentHeader header)
+ {
+ // The header is always located at the beginning of the file, and its size is
+ // exactly 512 bytes. This implies that the first sector (0) always starts at
+ // file offset 512.
+ writer.WriteBytes(header.Signature);
+ writer.WriteGuid(header.UniqueIdentifier);
+
+ writer.WriteUInt16(header.MinorVersion);
+ writer.WriteUInt16(header.MajorVersion);
+ writer.WriteBytes(header.ByteOrderIdentifier);
+
if (ShortSectorSize > SectorSize) throw new InvalidDataFormatException("Short sector size (" + ShortSectorSize.ToString() + ") exceeds sector size (" + SectorSize.ToString() + ")");
// get the 2-root of SectorSize
- ushort uSectorSize = (ushort)(Math.Log10(SectorSize) / Math.Log10(2));
- writer.WriteUInt16(uSectorSize);
+ header.SectorSize = (ushort)(Math.Log10(SectorSize) / Math.Log10(2));
+ writer.WriteUInt16(header.SectorSize);
// get the 2-root of SectorSize
- ushort uShortSectorSize = (ushort)(Math.Log10(ShortSectorSize) / Math.Log10(2));
- writer.WriteUInt16(uShortSectorSize);
+ header.ShortSectorSize = (ushort)(Math.Log10(ShortSectorSize) / Math.Log10(2));
+ writer.WriteUInt16(header.ShortSectorSize);
- writer.WriteBytes(new byte[10]); // unused?
+ header.Unused1 = new byte[6];
+ writer.WriteBytes(header.Unused1); // unused?
- writer.WriteUInt32(mvarSectorAllocationTableSize);
- writer.WriteUInt32(mvarDirectoryStreamFirstSectorID);
- writer.WriteUInt32(0);
- writer.WriteUInt32(mvarMinimumStandardStreamSize);
+ writer.WriteUInt32(header.DirectorySectorCount);
+ writer.WriteUInt32(header.SectorAllocationTableSize);
+ writer.WriteUInt32(header.DirectoryStreamFirstSectorID);
+ writer.WriteUInt32(header.TransactionSignatureNumber);
+ writer.WriteUInt32(header.MinimumStandardStreamSize);
- writer.WriteInt32(mvarShortSectorAllocationTableFirstSectorID);
- writer.WriteInt32(mvarShortSectorAllocationTableSize);
- writer.WriteInt32(mvarMasterSectorAllocationTableFirstSectorID);
- writer.WriteInt32(mvarMasterSectorAllocationTableSize);
+ writer.WriteInt32(header.ShortSectorAllocationTableFirstSectorID);
+ writer.WriteInt32(header.ShortSectorAllocationTableSize);
+ writer.WriteInt32(header.MasterSectorAllocationTableFirstSectorID);
+ writer.WriteInt32(header.MasterSectorAllocationTableSize);
#region Read Master Sector Allocation Table
// First part of the master sector allocation table, containing 109 SecIDs
@@ -579,8 +687,8 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
writer.WriteInt32Array(masterSectorAllocationTable);
// TODO: test this! when MSAT contains more than 109 SecIDs
- int countForMSAT = (int)((double)mvarSectorSize / 4);
- int nextSectorForMSAT = mvarMasterSectorAllocationTableFirstSectorID;
+ int countForMSAT = (int)((double)SectorSize / 4);
+ int nextSectorForMSAT = header.MasterSectorAllocationTableFirstSectorID;
int nextPositionForMSAT = masterSectorAllocationTable.Length;
#endregion
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentHeader.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentHeader.cs
new file mode 100644
index 00000000..a3f6f13a
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentHeader.cs
@@ -0,0 +1,69 @@
+//
+// CompoundDocumentHeader.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
+{
+ public struct CompoundDocumentHeader
+ {
+ public byte[] Signature { get; set; }
+ public Guid UniqueIdentifier { get; set; }
+ public ushort MinorVersion { get; set; }
+ public ushort MajorVersion { get; set; }
+ public byte[] ByteOrderIdentifier { get; set; }
+ public ushort SectorSize { get; set; }
+ public ushort ShortSectorSize { get; set; }
+ public byte[] Unused1 { get; set; }
+ public uint DirectorySectorCount { get; set; }
+ ///
+ /// Total number of sectors used for the sector allocation table
+ ///
+ public uint SectorAllocationTableSize { get; set; }
+ ///
+ /// Sector ID of the first sector of the directory stream
+ ///
+ public uint DirectoryStreamFirstSectorID { get; set; }
+ public uint TransactionSignatureNumber { get; set; }
+ ///
+ /// Minimum size of a standard stream (in bytes). Minimum allowed and most-used size is
+ /// 4096 bytes. Streams with an actual size smaller than (and not equal to) this value
+ /// are stored as short-streams.
+ ///
+ public uint MinimumStandardStreamSize { get; set; } // = 4096;
+ ///
+ /// Sector ID of the first sector of the short-sector allocation table (or
+ /// if not extant).
+ ///
+ /// The short sector allocation table first sector identifier.
+ public int ShortSectorAllocationTableFirstSectorID { get; set; }
+ public int ShortSectorAllocationTableSize { get; set; }
+ ///
+ /// Sector ID of the first sector of the master sector allocation table (or
+ /// if no additional sectors
+ /// used).
+ ///
+ public int MasterSectorAllocationTableFirstSectorID { get; set; }
+ ///
+ /// Total number of sectors used for the master sector allocation table.
+ ///
+ public int MasterSectorAllocationTableSize { get; set; }
+ public int[] MasterSectorAllocationTable { get; set; }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageColor.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageColor.cs
new file mode 100644
index 00000000..b7edcb06
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageColor.cs
@@ -0,0 +1,29 @@
+//
+// CompoundDocumentStorageColor.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
+{
+ public enum CompoundDocumentStorageColor : byte
+ {
+ Red = 0x00,
+ Black = 0x01
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageFlags.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageFlags.cs
new file mode 100644
index 00000000..809f1fcd
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageFlags.cs
@@ -0,0 +1,29 @@
+//
+// CompoundDocumentStorageFlags.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
+{
+ [Flags]
+ public enum CompoundDocumentStorageFlags
+ {
+ None = 0
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageHeader.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageHeader.cs
new file mode 100644
index 00000000..613d4b97
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/CompoundDocument/CompoundDocumentStorageHeader.cs
@@ -0,0 +1,40 @@
+//
+// CompoundDocumentStorageHeader.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
+{
+ public struct CompoundDocumentStorageHeader
+ {
+ public string Name { get; set; }
+ public CompoundDocumentStorageType StorageType { get; set; }
+ public CompoundDocumentStorageColor NodeColor { get; set; }
+ public int LeftChildNodeDirectoryID { get; set; }
+ public int RightChildNodeDirectoryID { get; set; }
+ public int RootNodeEntryDirectoryID { get; set; }
+ public Guid UniqueIdentifier { get; set; }
+ public CompoundDocumentStorageFlags Flags { get; set; }
+ public DateTime CreationTimestamp { get; set; }
+ public DateTime ModificationTimestamp { get; set; }
+ public int FirstSectorIndex { get; set; }
+ public int Length { get; set; }
+ public int Unused3 { get; set; }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Office/MicrosoftOfficeDocumentDataFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Office/MicrosoftOfficeDocumentDataFormat.cs
new file mode 100644
index 00000000..7d6aee1a
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Office/MicrosoftOfficeDocumentDataFormat.cs
@@ -0,0 +1,128 @@
+//
+// MicrosoftOfficeDocumentDataFormat.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using System.Collections.Generic;
+using UniversalEditor.Accessors;
+using UniversalEditor.DataFormats.CompoundDocument;
+using UniversalEditor.ObjectModels.CompoundDocument;
+using UniversalEditor.ObjectModels.FileSystem;
+
+namespace UniversalEditor.DataFormats.Office
+{
+ public abstract class MicrosoftOfficeDocumentDataFormat : CompoundDocumentDataFormat
+ {
+ protected abstract string MainDocumentStreamName { get; }
+ protected abstract ushort MainDocumentIdentifier { get; }
+
+ protected override void BeforeSaveInternal(Stack objectModels)
+ {
+ CompoundDocumentObjectModel fsom = new CompoundDocumentObjectModel();
+
+ Folder rootEntry = fsom.Folders.Add("Root Entry");
+
+ MemoryAccessor ma = new MemoryAccessor();
+ ma.Writer.WriteUInt16(MainDocumentIdentifier);
+
+ ushort fib_nFib = 0;
+ ma.Writer.WriteUInt16(fib_nFib);
+
+ ushort fib_unused1 = 0;
+ ma.Writer.WriteUInt16(fib_unused1);
+
+ ushort fib_lid = 0;
+ ma.Writer.WriteUInt16(fib_lid);
+
+ ushort fib_pnNext = 0;
+ ma.Writer.WriteUInt16(fib_pnNext);
+
+ ushort fib_flags = 0;
+ ma.Writer.WriteUInt16(fib_flags);
+
+ ushort fib_nFibBack = 0;
+ ma.Writer.WriteUInt16(fib_nFibBack);
+
+ uint fib_lKey = 0;
+ ma.Writer.WriteUInt32(fib_lKey);
+
+ byte fib_envr = 0;
+ ma.Writer.WriteByte(fib_envr);
+
+ byte fib_flags2 = 0;
+ ma.Writer.WriteByte(fib_flags2);
+
+ ushort fib_reserved3 = 0;
+ ma.Writer.WriteUInt16(fib_reserved3);
+
+ ushort fib_reserved4 = 0;
+ ma.Writer.WriteUInt16(fib_reserved4);
+
+ uint fib_reserved5 = 0;
+ ma.Writer.WriteUInt32(fib_reserved5);
+
+ uint fib_reserved6 = 0;
+ ma.Writer.WriteUInt32(fib_reserved6);
+
+ ma.Close();
+
+ File mainDocument = rootEntry.Files.Add(MainDocumentStreamName, ma.ToArray());
+
+ objectModels.Push(fsom);
+
+ base.BeforeSaveInternal(objectModels);
+ }
+
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ CompoundDocumentObjectModel fsom = objectModels.Pop() as CompoundDocumentObjectModel;
+
+ File mainDocument = fsom.Folders["Root Entry"].Files[MainDocumentStreamName];
+ if (mainDocument == null)
+ {
+ throw new InvalidDataFormatException(String.Format("compound document file does not contain a '{0}' stream", MainDocumentStreamName));
+ }
+
+ MemoryAccessor ma = new MemoryAccessor(mainDocument.GetData());
+
+ ushort fib_wIdent = ma.Reader.ReadUInt16();
+ if (fib_wIdent != MainDocumentIdentifier)
+ {
+ throw new InvalidDataFormatException(String.Format("file information block does not contain document signature 0x{0}", MainDocumentIdentifier.ToString("x")));
+ }
+ ushort fib_nFib = ma.Reader.ReadUInt16();
+ ushort fib_unused1 = ma.Reader.ReadUInt16();
+ ushort fib_lid = ma.Reader.ReadUInt16();
+ ushort fib_pnNext = ma.Reader.ReadUInt16();
+ ushort fib_flags = ma.Reader.ReadUInt16();
+ ushort fib_nFibBack = ma.Reader.ReadUInt16();
+ uint fib_lKey = ma.Reader.ReadUInt32();
+ byte fib_envr = ma.Reader.ReadByte();
+ byte fib_flags2 = ma.Reader.ReadByte();
+ ushort fib_reserved3 = ma.Reader.ReadUInt16();
+ ushort fib_reserved4 = ma.Reader.ReadUInt16();
+ uint fib_reserved5 = ma.Reader.ReadUInt32();
+ uint fib_reserved6 = ma.Reader.ReadUInt32();
+
+ objectModels.Push(fsom);
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Text/Formatted/DOC/DOCDataFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Text/Formatted/DOC/DOCDataFormat.cs
index 2892d8c8..9d67c93a 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Text/Formatted/DOC/DOCDataFormat.cs
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/Text/Formatted/DOC/DOCDataFormat.cs
@@ -19,11 +19,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+using System;
using System.Collections.Generic;
+using UniversalEditor.Accessors;
using UniversalEditor.DataFormats.CompoundDocument;
using UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument;
+using UniversalEditor.DataFormats.Office;
using UniversalEditor.ObjectModels.CompoundDocument;
using UniversalEditor.ObjectModels.FileSystem;
+using UniversalEditor.ObjectModels.Office;
using UniversalEditor.ObjectModels.Text.Formatted;
namespace UniversalEditor.DataFormats.Text.Formatted.DOC
@@ -31,7 +35,7 @@ namespace UniversalEditor.DataFormats.Text.Formatted.DOC
///
/// Provides a to manipulate Microsoft Office Word DOC files.
///
- public class DOCDataFormat : CompoundDocumentDataFormat
+ public class DOCDataFormat : MicrosoftOfficeDocumentDataFormat
{
private static DataFormatReference _dfr;
protected override DataFormatReference MakeReferenceInternal()
@@ -44,18 +48,26 @@ namespace UniversalEditor.DataFormats.Text.Formatted.DOC
return _dfr;
}
+ protected override string MainDocumentStreamName => "WordDocument";
+ protected override ushort MainDocumentIdentifier => 0xA5EC;
+
protected override void BeforeLoadInternal(Stack objectModels)
{
+ objectModels.Push(new MicrosoftOfficeDocumentObjectModel());
base.BeforeLoadInternal(objectModels);
- objectModels.Push(new CompoundDocumentObjectModel());
}
protected override void AfterLoadInternal(Stack objectModels)
{
base.AfterLoadInternal(objectModels);
- CompoundDocumentObjectModel fsom = (objectModels.Pop() as CompoundDocumentObjectModel);
+ MicrosoftOfficeDocumentObjectModel fsom = (objectModels.Pop() as MicrosoftOfficeDocumentObjectModel);
FormattedTextObjectModel ftom = (objectModels.Pop() as FormattedTextObjectModel);
+ if (fsom.AssociationTypeId != "Word.Document.8")
+ {
+ throw new InvalidDataFormatException(String.Format("association type '{0}' invalid for Microsoft Word 97-2003 data format", fsom.AssociationTypeId));
+ }
+
}
protected override void BeforeSaveInternal(Stack objectModels)
{
@@ -63,7 +75,6 @@ namespace UniversalEditor.DataFormats.Text.Formatted.DOC
CompoundDocumentObjectModel fsom = new CompoundDocumentObjectModel();
-
objectModels.Push(fsom);
base.BeforeSaveInternal(objectModels);
}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentClipboardFormat.cs b/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentClipboardFormat.cs
new file mode 100644
index 00000000..d4b5c871
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentClipboardFormat.cs
@@ -0,0 +1,63 @@
+//
+// CompoundDocumentClipboardFormat.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.ObjectModels.CompoundDocument
+{
+ public struct CompoundDocumentClipboardFormat
+ {
+ private string _Str;
+ private uint _Int;
+
+ public static readonly CompoundDocumentClipboardFormat Empty;
+
+ private CompoundDocumentClipboardFormat(uint _int)
+ {
+ _Int = _int;
+ _Str = null;
+ }
+ private CompoundDocumentClipboardFormat(string _str)
+ {
+ _Str = _str;
+ _Int = 0;
+ }
+
+ public bool IsStandard { get { return _Str == null && _Int != 0; } }
+ public bool IsEmpty { get { return _Str == null && _Int == 0; } }
+
+ public override string ToString()
+ {
+ if (_Str != null)
+ {
+ return _Str;
+ }
+ return _Int.ToString();
+ }
+
+ public static CompoundDocumentClipboardFormat FromStandard(uint index)
+ {
+ return new CompoundDocumentClipboardFormat(index);
+ }
+ public static CompoundDocumentClipboardFormat FromString(string format)
+ {
+ return new CompoundDocumentClipboardFormat(format);
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentObjectModel.cs b/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentObjectModel.cs
index 249076c3..ed56e283 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentObjectModel.cs
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/CompoundDocument/CompoundDocumentObjectModel.cs
@@ -25,8 +25,20 @@ namespace UniversalEditor.ObjectModels.CompoundDocument
{
public class CompoundDocumentObjectModel : FileSystemObjectModel
{
- public CompoundDocumentObjectModel()
+ private static ObjectModelReference _omr = null;
+
+ public string UserType { get; set; }
+ public CompoundDocumentClipboardFormat ClipboardFormat { get; set; }
+ public string AssociationTypeId { get; set; }
+
+ protected override ObjectModelReference MakeReferenceInternal()
{
+ if (_omr == null)
+ {
+ _omr = new ObjectModelReference(GetType());
+ _omr.Path = new string[] { "Microsoft", "OLE", "OLE2 Compound Document" };
+ }
+ return _omr;
}
}
}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/Office/MicrosoftOfficeDocumentObjectModel.cs b/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/Office/MicrosoftOfficeDocumentObjectModel.cs
new file mode 100644
index 00000000..06d13164
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/ObjectModels/Office/MicrosoftOfficeDocumentObjectModel.cs
@@ -0,0 +1,32 @@
+//
+// MicrosoftOfficeDocumentObjectModel.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using UniversalEditor.ObjectModels.CompoundDocument;
+
+namespace UniversalEditor.ObjectModels.Office
+{
+ public class MicrosoftOfficeDocumentObjectModel : CompoundDocumentObjectModel
+ {
+ public MicrosoftOfficeDocumentObjectModel()
+ {
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj b/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj
index 9c976a90..cabd06b2 100644
--- a/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj
+++ b/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj
@@ -149,6 +149,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -207,6 +218,9 @@
+
+
+