diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/FileSystem/Associations/Microsoft/WindowsImage.xml b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/FileSystem/Associations/Microsoft/WindowsImage.xml index 266fd033..a598b6aa 100644 --- a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/FileSystem/Associations/Microsoft/WindowsImage.xml +++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/FileSystem/Associations/Microsoft/WindowsImage.xml @@ -6,6 +6,7 @@ *.wim + *.swm diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj index a5558fcc..dc493c3c 100644 --- a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj +++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj @@ -117,6 +117,7 @@ + diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMArchiveFlags.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMArchiveFlags.cs new file mode 100644 index 00000000..81aa9bf5 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMArchiveFlags.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + [Flags()] + public enum WIMArchiveFlags : uint + { + None = 0x00000000, + Reserved = 0x00000001, + /// + /// Resources within the WIM (both file and metadata) are compressed. + /// + Compressed = 0x00000002, + /// + /// The contents of this WIM should not be changed. + /// + ReadOnly = 0x00000004, + /// + /// Resource data specified by the images within this WIM may be contained in another WIM. + /// + Spanned = 0x00000008, + /// + /// This WIM contains file resources only. It does not contain any file metadata. + /// + ResourceOnly = 0x00000010, + /// + /// This WIM contains file metadata only. + /// + MetadataOnly = 0x00000020, + /// + /// Limits one writer to the WIM file when opened with the WIM_FLAG_SHARE_WRITE mode. This flag is primarily used in the Windows Deployment Services (WDS) scenario. + /// + WriteInProgress = 0x00000040, + ReparsePointFixup = 0x00000080, + CompressReserved = 0x00010000, + /// + /// When is set, resources within the wim are compressed using XPRESS compression. + /// + CompressedXpress = 0x00020000, + /// + /// When is set, resources within the wim are compressed using LZX compression. + /// + CompressedLZX = 0x00040000 + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMArchiveHeader.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMArchiveHeader.cs new file mode 100644 index 00000000..2cc6994e --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMArchiveHeader.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + public struct WIMArchiveHeader + { + /// + /// Signature that identifies the file as a .wim file. Value is set to “MSWIM\0\0”. + /// + public string magic; + /// + /// Size of the WIM header in bytes. + /// + public uint cbSize; + /// + /// The current version of the .wim file. This number will increase if the format of the .wim file changes. + /// + public uint dwVersion; + public WIMArchiveFlags dwFlags; + /// + /// Size of the compressed .wim file in bytes. + /// + public uint dwCompressionSize; + /// + /// A unique identifier for this WIM archive. + /// + public Guid gWIMGuid; + /// + /// The part number of the current .wim file in a spanned set. This value is 1, unless the data of the .wim file was split into multiple parts (.swm). + /// + public ushort usPartNumber; + /// + /// The total number of .wim file parts in a spanned set. + /// + public ushort usTotalParts; + /// + /// The number of images contained in the .wim file. + /// + public uint dwImageCount; + public WIMResourceHeaderDiskShort rhOffsetTable; + public WIMResourceHeaderDiskShort rhXmlData; + public WIMResourceHeaderDiskShort rhBootMetadata; + public uint dwBootIndex; + public WIMResourceHeaderDiskShort rhIntegrity; + public byte[/*60*/] bUnused; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDataFormat.cs index 365f6804..1acc9e7c 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDataFormat.cs +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDataFormat.cs @@ -25,56 +25,212 @@ namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage if (fsom == null) throw new ObjectModelNotSupportedException(); Reader reader = base.Accessor.Reader; - string magic = reader.ReadFixedLengthString(8); - if (magic != "MSWIM\0\0\0") throw new InvalidDataFormatException("File does not begin with 'MSWIM', 0x00, 0x00, 0x00"); + WIMArchiveHeader header = ReadWIMArchiveHeader(reader); - uint offsetToFirstDataBlock = reader.ReadUInt32(); + reader.Seek((long)header.rhOffsetTable.liOffset, SeekOrigin.Begin); + byte[] tblOffsetTable = reader.ReadBytes(header.rhOffsetTable.ullSize); - uint unknown2 = reader.ReadUInt32(); - uint unknown3 = reader.ReadUInt32(); - uint unknown4 = reader.ReadUInt32(); - - Guid guid = reader.ReadGuid(); - ushort unknown5a = reader.ReadUInt16(); - ushort unknown5b = reader.ReadUInt16(); - uint unknown6 = reader.ReadUInt32(); - uint unknown7 = reader.ReadUInt32(); - uint unknown8 = reader.ReadUInt32(); - uint unknown9 = reader.ReadUInt32(); - uint unknown10 = reader.ReadUInt32(); - uint unknown11 = reader.ReadUInt32(); - uint unknown12 = reader.ReadUInt32(); - uint unknown13 = reader.ReadUInt32(); - uint unknown14 = reader.ReadUInt32(); - - ulong xmlDataOffset = reader.ReadUInt64(); - ulong xmlDataLength = reader.ReadUInt64(); - - - - // file record + Reader offsetTableReader = new Reader(new Accessors.MemoryAccessor(tblOffsetTable)); + List entries = new List(); + while (!offsetTableReader.EndOfStream) { - // 20 byte guid??? - Guid fileGuid = reader.ReadGuid(); - uint fileGuid2 = reader.ReadUInt32(); - - ulong unknownB1 = reader.ReadUInt64(); - ulong unknownB2 = reader.ReadUInt64(); - ushort fileNameLength = reader.ReadUInt16(); - string fileName = reader.ReadFixedLengthString(fileNameLength, Encoding.UTF16LittleEndian); + WIMOffsetTableEntry entry = ReadWIMOffsetTableEntry(offsetTableReader); + entries.Add(entry); } - // file data mapping record + WIMOffsetTableEntry lastEntry = entries[entries.Count - 1]; + reader.Seek((long)lastEntry.liOffset, SeekOrigin.Begin); + + int padRest = (int)((long)lastEntry.liOffset % 8); + + Dictionary direntries = new Dictionary(); + + #region SECURITYBLOCK_DISK { - uint unknownZ1 = reader.ReadUInt32(); - - ulong fileLength1 = reader.ReadUInt64(); // maybe compressed/uncompressed length? - ulong fileOffset = reader.ReadUInt64(); - ulong fileLength = reader.ReadUInt64(); - - Guid fileGuid = reader.ReadGuid(); - uint fileGuid2 = reader.ReadUInt32(); + uint dwTotalLength = reader.ReadUInt32(); + uint dwNumEntries = reader.ReadUInt32(); + ulong[] liEntryLength = reader.ReadUInt64Array((int)dwNumEntries); } + #endregion + #region DIRENTRY + { + WIMDirectoryEntry entry = default(WIMDirectoryEntry); + do + { + entry = ReadWIMDirectoryEntry(reader); + + if (!entry.Equals(default(WIMDirectoryEntry))) + { + direntries.Add(entry.bHash, entry); + reader.Align(8, padRest); + } + else + { + reader.Align(8); + } + } + while (!entry.Equals(default(WIMDirectoryEntry))); + + + Dictionary offsetDictionary = new Dictionary(); + + foreach (WIMOffsetTableEntry ent in entries) + { + foreach (KeyValuePair kvp in direntries) + { + if (kvp.Key.Match(ent.bHash)) + { + offsetDictionary.Add(kvp.Value, ent); + } + } + } + + foreach (KeyValuePair kvp in offsetDictionary) + { + File file = fsom.AddFile(kvp.Key.FileName); + file.Name = kvp.Key.FileName; + file.Size = (long)kvp.Value.liOriginalSize; + file.Properties.Add("offset", (long)kvp.Value.liOffset); + file.Properties.Add("length", (long)kvp.Value.liOriginalSize); + file.Properties.Add("reader", reader); + file.DataRequest += file_DataRequest; + } + } + #endregion + + #region Load XML data - we don't actually use this anywhere... yet + { + reader.Seek((long)header.rhXmlData.liOffset, SeekOrigin.Begin); + string xmlData = reader.ReadFixedLengthString((long)header.rhXmlData.ullSize, Encoding.UTF16LittleEndian); + + UniversalEditor.ObjectModels.Markup.MarkupObjectModel mom = new ObjectModels.Markup.MarkupObjectModel(); + UniversalEditor.DataFormats.Markup.XML.XMLDataFormat xdf = new Markup.XML.XMLDataFormat(); + Document.Load(mom, xdf, new Accessors.StringAccessor(xmlData)); + + fsom.CustomProperties.Add(typeof(UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage.WIMDataFormat), "XMLDescriptor", mom); + } + #endregion + } + + private void file_DataRequest(object sender, DataRequestEventArgs e) + { + File file = (sender as File); + Reader reader = (Reader)file.Properties["reader"]; + long offset = (long)file.Properties["offset"]; + long length = (long)file.Properties["length"]; + + reader.Seek(offset, SeekOrigin.Begin); + byte[] compressedData = reader.ReadBytes(length); + byte[] uncompressedData = compressedData; + e.Data = uncompressedData; + } + + private WIMDirectoryEntry ReadWIMDirectoryEntry(Reader lastEntryReader) + { + WIMDirectoryEntry item = new WIMDirectoryEntry(); + item.liLength = lastEntryReader.ReadUInt64(); + if (item.liLength == 0) return default(WIMDirectoryEntry); + + item.dwAttributes = lastEntryReader.ReadUInt32(); + item.dwSecurityId = lastEntryReader.ReadInt32(); + item.liSubdirOffset = lastEntryReader.ReadUInt64(); + item.liUnused1 = lastEntryReader.ReadUInt64(); + item.liUnused2 = lastEntryReader.ReadUInt64(); + item.liCreationTime = lastEntryReader.ReadUInt64(); + item.liLastAccessTime = lastEntryReader.ReadUInt64(); + item.liLastWriteTime = lastEntryReader.ReadUInt64(); + item.bHash = lastEntryReader.ReadBytes(20); + item.dwReparseTag = lastEntryReader.ReadUInt32(); + item.dwReparseReserved = lastEntryReader.ReadUInt32(); + + if ((item.dwAttributes & 16) == 16) + { + item.liHardLink = lastEntryReader.ReadUInt64(); + } + else + { + item.liHardLink = lastEntryReader.ReadUInt32(); + } + + ushort wStreamCount = lastEntryReader.ReadUInt16(); + item.wStreams = new WIMStreamEntry[wStreamCount]; + + item.wShortNameLength = lastEntryReader.ReadUInt16(); + ushort wFileNameLength = lastEntryReader.ReadUInt16(); + item.FileName = lastEntryReader.ReadFixedLengthString(wFileNameLength, Encoding.UTF16LittleEndian); + for (ushort i = 0; i < wStreamCount; i++) + { + item.wStreams[i] = ReadWIMStreamEntry(lastEntryReader); + } + + ulong realLength = (ulong)((8 * 10) + 20 + 4 + wFileNameLength); + if (item.liLength != realLength) + { + + } + return item; + } + private WIMStreamEntry ReadWIMStreamEntry(Reader reader) + { + WIMStreamEntry item = new WIMStreamEntry(); + item.liLength = reader.ReadUInt64(); + item.Unused1 = reader.ReadUInt64(); + item.bHash = reader.ReadBytes(20); + item.wStreamNameLength = reader.ReadUInt16(); + item.StreamName = reader.ReadFixedLengthString(item.wStreamNameLength, Encoding.UTF16LittleEndian); + return item; + } + private WIMOffsetTableEntry ReadWIMOffsetTableEntry(Reader reader) + { + WIMResourceHeaderDiskShort reshdr = ReadWIMResourceHeaderDiskShort(reader); + WIMOffsetTableEntry wim = new WIMOffsetTableEntry(); + wim.flags = reshdr.flags; + wim.liOffset = reshdr.liOffset; + wim.liOriginalSize = reshdr.liOriginalSize; + wim.ullSize = reshdr.ullSize; + + wim.usPartNumber = reader.ReadUInt16(); + wim.dwRefCount = reader.ReadUInt32(); + wim.bHash = reader.ReadBytes(20); + return wim; + } + private WIMArchiveHeader ReadWIMArchiveHeader(Reader reader) + { + WIMArchiveHeader header = new WIMArchiveHeader(); + + header.magic = reader.ReadFixedLengthString(8); + if (header.magic != "MSWIM\0\0\0") throw new InvalidDataFormatException("File does not begin with 'MSWIM', 0x00, 0x00, 0x00"); + + header.cbSize = reader.ReadUInt32(); + header.dwVersion = reader.ReadUInt32(); + header.dwFlags = (WIMArchiveFlags)reader.ReadUInt32(); + header.dwCompressionSize = reader.ReadUInt32(); + header.gWIMGuid = reader.ReadGuid(); + header.usPartNumber = reader.ReadUInt16(); + header.usTotalParts = reader.ReadUInt16(); + header.dwImageCount = reader.ReadUInt32(); + header.rhOffsetTable = ReadWIMResourceHeaderDiskShort(reader); + header.rhXmlData = ReadWIMResourceHeaderDiskShort(reader); + header.rhBootMetadata = ReadWIMResourceHeaderDiskShort(reader); + header.dwBootIndex = reader.ReadUInt32(); + header.rhIntegrity = ReadWIMResourceHeaderDiskShort(reader); + header.bUnused = reader.ReadBytes(60); + + return header; + } + private WIMResourceHeaderDiskShort ReadWIMResourceHeaderDiskShort(Reader reader) + { + WIMResourceHeaderDiskShort item = new WIMResourceHeaderDiskShort(); + long sizeAndFlags = reader.ReadInt64(); + ulong size = (ulong)(sizeAndFlags << 8); + size = (ulong)(size >> 8); + byte flags = (byte)(sizeAndFlags >> 56); + + item.ullSize = size; + item.flags = (WIMResourceHeaderDiskFlags)flags; + item.liOffset = reader.ReadUInt64(); + item.liOriginalSize = reader.ReadUInt64(); + return item; } protected override void SaveInternal(ObjectModel objectModel) diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDirectoryEntry.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDirectoryEntry.cs new file mode 100644 index 00000000..2d88f945 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMDirectoryEntry.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + public struct WIMDirectoryEntry + { + public ulong liLength; + public uint dwAttributes; + public int dwSecurityId; + public ulong liSubdirOffset; + public ulong liUnused1; + public ulong liUnused2; + public ulong liCreationTime; + public ulong liLastAccessTime; + public ulong liLastWriteTime; + public byte[/*20*/] bHash; + public uint dwReparseTag; + public uint dwReparseReserved; + public ulong liHardLink; + public ushort wShortNameLength; + public string FileName; + public WIMStreamEntry[] wStreams; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMOffsetTableEntry.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMOffsetTableEntry.cs new file mode 100644 index 00000000..5bcb63e4 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMOffsetTableEntry.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + public class WIMOffsetTableEntry : WIMResourceHeaderDiskShort + { + public ushort usPartNumber; + public uint dwRefCount; + public byte[/*20*/] bHash; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDisk.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDisk.cs new file mode 100644 index 00000000..5c9139e3 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDisk.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + public class WIMResourceHeaderDisk + { + public ulong ullSize; + public WIMResourceHeaderDiskFlags flags; + public ulong liOffset; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDiskFlags.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDiskFlags.cs new file mode 100644 index 00000000..7d0fc282 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDiskFlags.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + [Flags()] + public enum WIMResourceHeaderDiskFlags + { + None = 0x00, + Free = 0x01, + Metadata = 0x02, + Compressed = 0x04, + Spanned = 0x08 + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDiskShort.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDiskShort.cs new file mode 100644 index 00000000..21cf509d --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMResourceHeaderDiskShort.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + public class WIMResourceHeaderDiskShort : WIMResourceHeaderDisk + { + public ulong liOriginalSize; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMStreamEntry.cs b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMStreamEntry.cs new file mode 100644 index 00000000..fbe60703 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/DataFormats/FileSystem/Microsoft/WindowsImage/WIMStreamEntry.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Microsoft.WindowsImage +{ + public struct WIMStreamEntry + { + public ulong liLength; + public ulong Unused1; + public byte[/*20*/] bHash; + public ushort wStreamNameLength; + public string StreamName; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj index 192a5c03..659e206f 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj +++ b/CSharp/Plugins/UniversalEditor.Plugins.Microsoft/UniversalEditor.Plugins.Microsoft.csproj @@ -50,6 +50,15 @@ + + + + + + + + +