improve support for OLE2 compound document base format and some subformats
This commit is contained in:
parent
b5a0487158
commit
04a46da177
@ -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");
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<ObjectModel> objectModels)
|
||||
{
|
||||
|
||||
@ -1,6 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UniversalEditor Version="4.0">
|
||||
<Associations>
|
||||
<Association>
|
||||
<!-- This association exists only to allow us to choose this DataFormat to view its structure as a FileSystemObjectModel. -->
|
||||
<Filters>
|
||||
<Filter Title="Microsoft OLE2 Compound Document file system">
|
||||
</Filter>
|
||||
</Filters>
|
||||
<ObjectModels>
|
||||
<ObjectModel TypeName="UniversalEditor.ObjectModels.FileSystem.FileSystemObjectModel" />
|
||||
</ObjectModels>
|
||||
<DataFormats>
|
||||
<DataFormat TypeName="UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument.CompoundDocumentBaseDataFormat" />
|
||||
</DataFormats>
|
||||
</Association>
|
||||
<Association>
|
||||
<!-- This association exists only to allow us to choose this DataFormat to view its structure as a FileSystemObjectModel. -->
|
||||
<Filters>
|
||||
@ -8,11 +21,22 @@
|
||||
</Filter>
|
||||
</Filters>
|
||||
<ObjectModels>
|
||||
<ObjectModel TypeName="UniversalEditor.ObjectModels.FileSystem.FileSystemObjectModel" />
|
||||
<ObjectModel TypeName="UniversalEditor.ObjectModels.CompoundDocument.CompoundDocumentObjectModel" />
|
||||
</ObjectModels>
|
||||
<DataFormats>
|
||||
<DataFormat TypeName="UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument.CompoundDocumentDataFormat" />
|
||||
<DataFormat TypeName="UniversalEditor.DataFormats.CompoundDocument.CompoundDocumentDataFormat" />
|
||||
</DataFormats>
|
||||
</Association>
|
||||
<Association>
|
||||
<Filters>
|
||||
<Filter Title="Microsoft OLE2 SummaryInformation">
|
||||
</Filter>
|
||||
</Filters>
|
||||
<ObjectModels>
|
||||
<ObjectModel TypeName="UniversalEditor.ObjectModels.PropertyList.PropertyListObjectModel" />
|
||||
</ObjectModels>
|
||||
<DataFormats>
|
||||
<DataFormat TypeName="UniversalEditor.DataFormats.CompoundDocument.SummaryInformation.SummaryInformationDataFormat" />
|
||||
</DataFormats>
|
||||
</Associations>
|
||||
</UniversalEditor>
|
||||
|
||||
@ -5,6 +5,9 @@
|
||||
<!-- This association exists only to allow us to choose this DataFormat to view its structure as a FileSystemObjectModel. -->
|
||||
<Filters>
|
||||
<Filter Title="Microsoft Word 97-2003 Document">
|
||||
<FileNameFilters>
|
||||
<FileNameFilter>*.doc</FileNameFilter>
|
||||
</FileNameFilters>
|
||||
</Filter>
|
||||
</Filters>
|
||||
<ObjectModels>
|
||||
|
||||
@ -19,12 +19,118 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
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<ObjectModel> 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// PropertyInfo.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
|
||||
{
|
||||
public struct PropertyInfo
|
||||
{
|
||||
public uint Identifier { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// CompoundDocumentPropertySetInfo.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
|
||||
{
|
||||
public struct PropertySetInfo
|
||||
{
|
||||
public Guid Guid { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,169 @@
|
||||
//
|
||||
// PropertySetPropertyType.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.CompoundDocument.SummaryInformation
|
||||
{
|
||||
public enum PropertySetPropertyType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Type is undefined, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Empty = 0x0000,
|
||||
/// <summary>
|
||||
/// Type is null, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Null = 0x0001,
|
||||
/// <summary>
|
||||
/// Type is 16-bit signed integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
I2 = 0x0002,
|
||||
/// <summary>
|
||||
/// Type is 32-bit signed integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
I4 = 0x0003,
|
||||
/// <summary>
|
||||
/// Type is 4-byte (single-precision) IEEE floating-point number, and the
|
||||
/// minimum property set version is 0.
|
||||
/// </summary>
|
||||
R4 = 0x0004,
|
||||
/// <summary>
|
||||
/// Type is 8-byte (double-precision) IEEE floating-point number, and the
|
||||
/// minimum property set version is 0.
|
||||
/// </summary>
|
||||
R8 = 0x0005,
|
||||
/// <summary>
|
||||
/// Type is CURRENCY, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Currency = 0x0006,
|
||||
/// <summary>
|
||||
/// Type is DATE (OLE Automation), and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Date = 0x0007,
|
||||
/// <summary>
|
||||
/// Type is CodePageString, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
BStr = 0x0008,
|
||||
/// <summary>
|
||||
/// Type is HRESULT, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Error = 0x000A,
|
||||
/// <summary>
|
||||
/// Type is VARIANT_BOOL, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Bool = 0x000B,
|
||||
/// <summary>
|
||||
/// Type is DECIMAL, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Decimal = 0x000E,
|
||||
/// <summary>
|
||||
/// Type is 1-byte signed integer, and the minimum property set version is 1.
|
||||
/// </summary>
|
||||
I1 = 0x0010,
|
||||
/// <summary>
|
||||
/// Type is 1-byte unsigned integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
UI1 = 0x0011,
|
||||
/// <summary>
|
||||
/// Type is 2-byte unsigned integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
UI2 = 0x0012,
|
||||
/// <summary>
|
||||
/// Type is 4-byte unsigned integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
UI4 = 0x0013,
|
||||
/// <summary>
|
||||
/// Type is 8-byte signed integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
I8 = 0x0014,
|
||||
/// <summary>
|
||||
/// Type is 8-byte unsigned integer, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
UI8 = 0x0015,
|
||||
/// <summary>
|
||||
/// Type is 4-byte signed integer, and the minimum property set version is 1.
|
||||
/// </summary>
|
||||
Int = 0x0016,
|
||||
/// <summary>
|
||||
/// Type is 4-byte unsigned integer, and the minimum property set version is 1.
|
||||
/// </summary>
|
||||
UInt = 0x0017,
|
||||
/// <summary>
|
||||
/// Type is CodePageString, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
LPStr = 0x001E,
|
||||
/// <summary>
|
||||
/// Type is UnicodeString, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
LPWStr = 0x001F,
|
||||
/// <summary>
|
||||
/// Type is FILETIME, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
FileTime = 0x0040,
|
||||
/// <summary>
|
||||
/// Type is binary large object (BLOB), and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
Blob = 0x0041,
|
||||
/// <summary>
|
||||
/// Type is Stream, and the minimum property set version is 0. VT_STREAM is not
|
||||
/// allowed in a simple property set.
|
||||
/// </summary>
|
||||
Stream = 0x0042,
|
||||
/// <summary>
|
||||
/// Type is Storage, and the minimum property set version is 0. VT_STORAGE is not
|
||||
/// allowed in a simple property set.
|
||||
/// </summary>
|
||||
Storage = 0x0043,
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
StreamedObject = 0x0044,
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
StoredObject = 0x0045,
|
||||
/// <summary>
|
||||
/// Type is BLOB representing an object in an application-specific manner. The minimum
|
||||
/// property set version is 0.
|
||||
/// </summary>
|
||||
BlobObject = 0x0046,
|
||||
/// <summary>
|
||||
/// Type is PropertyIdentifier, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
PropertyIdentifier = 0x0047,
|
||||
/// <summary>
|
||||
/// Type is CLSID, and the minimum property set version is 0.
|
||||
/// </summary>
|
||||
CLSID = 0x0048,
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
VersionedStream = 0x0049,
|
||||
|
||||
Vector = 0x1000,
|
||||
Array = 0x2000
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,560 @@
|
||||
//
|
||||
// SummaryInformationDataFormat.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
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>("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<PropertyInfo>("info", pi);
|
||||
propertySetGroups[i].Items.Add(property);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < propertySetInfos.Length; i++)
|
||||
{
|
||||
IEnumerable<Property> properties = propertySetGroups[i].Items.OfType<Property>();
|
||||
foreach (Property property in properties)
|
||||
{
|
||||
PropertyInfo pi = property.GetExtraData<PropertyInfo>("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<Group> groups = plom.Items.OfType<Group>();
|
||||
Accessor.Writer.WriteUInt32((uint)groups.Count());
|
||||
uint offset = (uint)(28 + (20 * groups.Count()));
|
||||
|
||||
foreach (Group group in groups)
|
||||
{
|
||||
Accessor.Writer.WriteGuid(group.GetExtraData<Guid>("guid"));
|
||||
Accessor.Writer.WriteUInt32(offset);
|
||||
|
||||
IEnumerable<Property> properties = group.Items.OfType<Property>();
|
||||
offset += (uint)(8 + (8 * properties.Count()));
|
||||
}
|
||||
|
||||
offset -= (uint) Accessor.Position;
|
||||
|
||||
foreach (Group group in groups)
|
||||
{
|
||||
IEnumerable<Property> properties = group.Items.OfType<Property>();
|
||||
|
||||
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<Property> properties = group.Items.OfType<Property>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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; } }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of sectors used for the sector allocation table
|
||||
/// </summary>
|
||||
private uint mvarSectorAllocationTableSize = 0;
|
||||
/// <summary>
|
||||
/// Sector ID of the first sector of the directory stream
|
||||
/// </summary>
|
||||
private uint mvarDirectoryStreamFirstSectorID = 0;
|
||||
|
||||
private uint mvarMinimumStandardStreamSize = 4096;
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public uint MinimumStandardStreamSize { get { return mvarMinimumStandardStreamSize; } set { mvarMinimumStandardStreamSize = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Sector ID of the first sector of the short-sector allocation table (or
|
||||
/// <see cref="CompoundDocumentKnownSectorID.EndOfChain" /> if not extant).
|
||||
/// </summary>
|
||||
private int mvarShortSectorAllocationTableFirstSectorID = 0;
|
||||
/// <summary>
|
||||
/// Total number of sectors used for the short-sector allocation table.
|
||||
/// </summary>
|
||||
private int mvarShortSectorAllocationTableSize = 0;
|
||||
/// <summary>
|
||||
/// Sector ID of the first sector of the master sector allocation table (or
|
||||
/// <see cref="CompoundDocumentKnownSectorID.EndOfChain" /> if no additional sectors
|
||||
/// used).
|
||||
/// </summary>
|
||||
private int mvarMasterSectorAllocationTableFirstSectorID = 0;
|
||||
/// <summary>
|
||||
/// Total number of sectors used for the master sector allocation table.
|
||||
/// </summary>
|
||||
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<int> shortSectorAllocationTableSectors = new List<int>();
|
||||
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<int>(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<int>();
|
||||
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<CompoundDocumentStorageHeader> listHeaders = new List<CompoundDocumentStorageHeader>();
|
||||
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<int> shortStreamContainerStreamSectors = new List<int>();
|
||||
@ -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<int> directorySectors = new List<int>();
|
||||
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<int> sectors = new List<int>();
|
||||
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
|
||||
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
//
|
||||
// CompoundDocumentHeader.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
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; }
|
||||
/// <summary>
|
||||
/// Total number of sectors used for the sector allocation table
|
||||
/// </summary>
|
||||
public uint SectorAllocationTableSize { get; set; }
|
||||
/// <summary>
|
||||
/// Sector ID of the first sector of the directory stream
|
||||
/// </summary>
|
||||
public uint DirectoryStreamFirstSectorID { get; set; }
|
||||
public uint TransactionSignatureNumber { get; set; }
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public uint MinimumStandardStreamSize { get; set; } // = 4096;
|
||||
/// <summary>
|
||||
/// Sector ID of the first sector of the short-sector allocation table (or
|
||||
/// <see cref="CompoundDocumentKnownSectorID.EndOfChain" /> if not extant).
|
||||
/// </summary>
|
||||
/// <value>The short sector allocation table first sector identifier.</value>
|
||||
public int ShortSectorAllocationTableFirstSectorID { get; set; }
|
||||
public int ShortSectorAllocationTableSize { get; set; }
|
||||
/// <summary>
|
||||
/// Sector ID of the first sector of the master sector allocation table (or
|
||||
/// <see cref="CompoundDocumentKnownSectorID.EndOfChain" /> if no additional sectors
|
||||
/// used).
|
||||
/// </summary>
|
||||
public int MasterSectorAllocationTableFirstSectorID { get; set; }
|
||||
/// <summary>
|
||||
/// Total number of sectors used for the master sector allocation table.
|
||||
/// </summary>
|
||||
public int MasterSectorAllocationTableSize { get; set; }
|
||||
public int[] MasterSectorAllocationTable { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// CompoundDocumentStorageColor.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
|
||||
{
|
||||
public enum CompoundDocumentStorageColor : byte
|
||||
{
|
||||
Red = 0x00,
|
||||
Black = 0x01
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// CompoundDocumentStorageFlags.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.FileSystem.Microsoft.CompoundDocument
|
||||
{
|
||||
[Flags]
|
||||
public enum CompoundDocumentStorageFlags
|
||||
{
|
||||
None = 0
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
//
|
||||
// CompoundDocumentStorageHeader.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
//
|
||||
// MicrosoftOfficeDocumentDataFormat.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
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<ObjectModel> 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<ObjectModel> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,11 +19,15 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
/// <summary>
|
||||
/// Provides a <see cref="DataFormat" /> to manipulate Microsoft Office Word DOC files.
|
||||
/// </summary>
|
||||
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<ObjectModel> objectModels)
|
||||
{
|
||||
objectModels.Push(new MicrosoftOfficeDocumentObjectModel());
|
||||
base.BeforeLoadInternal(objectModels);
|
||||
objectModels.Push(new CompoundDocumentObjectModel());
|
||||
}
|
||||
protected override void AfterLoadInternal(Stack<ObjectModel> 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<ObjectModel> objectModels)
|
||||
{
|
||||
@ -63,7 +75,6 @@ namespace UniversalEditor.DataFormats.Text.Formatted.DOC
|
||||
CompoundDocumentObjectModel fsom = new CompoundDocumentObjectModel();
|
||||
|
||||
|
||||
|
||||
objectModels.Push(fsom);
|
||||
base.BeforeSaveInternal(objectModels);
|
||||
}
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
//
|
||||
// CompoundDocumentClipboardFormat.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
//
|
||||
// MicrosoftOfficeDocumentObjectModel.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
using UniversalEditor.ObjectModels.CompoundDocument;
|
||||
|
||||
namespace UniversalEditor.ObjectModels.Office
|
||||
{
|
||||
public class MicrosoftOfficeDocumentObjectModel : CompoundDocumentObjectModel
|
||||
{
|
||||
public MicrosoftOfficeDocumentObjectModel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,6 +149,17 @@
|
||||
<Compile Include="DataFormats\FileSystem\Microsoft\MSCompressed\Internal\SZDDComp.cs" />
|
||||
<Compile Include="DataFormats\CompoundDocument\CompoundDocumentDataFormat.cs" />
|
||||
<Compile Include="ObjectModels\CompoundDocument\CompoundDocumentObjectModel.cs" />
|
||||
<Compile Include="ObjectModels\CompoundDocument\CompoundDocumentClipboardFormat.cs" />
|
||||
<Compile Include="DataFormats\FileSystem\Microsoft\CompoundDocument\CompoundDocumentStorageHeader.cs" />
|
||||
<Compile Include="DataFormats\FileSystem\Microsoft\CompoundDocument\CompoundDocumentStorageFlags.cs" />
|
||||
<Compile Include="ObjectModels\Office\MicrosoftOfficeDocumentObjectModel.cs" />
|
||||
<Compile Include="DataFormats\Office\MicrosoftOfficeDocumentDataFormat.cs" />
|
||||
<Compile Include="DataFormats\FileSystem\Microsoft\CompoundDocument\CompoundDocumentStorageColor.cs" />
|
||||
<Compile Include="DataFormats\FileSystem\Microsoft\CompoundDocument\CompoundDocumentHeader.cs" />
|
||||
<Compile Include="DataFormats\CompoundDocument\SummaryInformation\SummaryInformationDataFormat.cs" />
|
||||
<Compile Include="DataFormats\CompoundDocument\SummaryInformation\PropertySetInfo.cs" />
|
||||
<Compile Include="DataFormats\CompoundDocument\SummaryInformation\PropertyInfo.cs" />
|
||||
<Compile Include="DataFormats\CompoundDocument\SummaryInformation\PropertySetPropertyType.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Libraries\UniversalEditor.Compression\UniversalEditor.Compression.csproj">
|
||||
@ -207,6 +218,9 @@
|
||||
<Folder Include="Associations\Text\Formatted\" />
|
||||
<Folder Include="DataFormats\CompoundDocument\" />
|
||||
<Folder Include="ObjectModels\CompoundDocument\" />
|
||||
<Folder Include="ObjectModels\Office\" />
|
||||
<Folder Include="DataFormats\Office\" />
|
||||
<Folder Include="DataFormats\CompoundDocument\SummaryInformation\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Associations\Help\WinHelp.uexml" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user