diff --git a/Ffu2Vhdx.sln b/Ffu2Vhdx.sln index 460fc7a..3924f63 100644 --- a/Ffu2Vhdx.sln +++ b/Ffu2Vhdx.sln @@ -4,10 +4,6 @@ VisualStudioVersion = 17.7.33927.210 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ffu2Vhdx", "Ffu2Vhdx\Ffu2Vhdx.csproj", "{6BB88918-A58C-4510-A99F-AA22C7D5BD55}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageCommon", "ImageCommon\ImageCommon.csproj", "{8CA492CB-6F12-4862-991C-B28468172730}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageStorageServiceManaged", "ImageStorageServiceManaged\ImageStorageServiceManaged.csproj", "{5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Core", "DiscUtils\DiscUtils.Core\DiscUtils.Core.csproj", "{A76FBCBD-08EB-413D-8DD7-8264D4E50304}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Streams", "DiscUtils\DiscUtils.Streams\DiscUtils.Streams.csproj", "{37E19DC2-07BE-4E1E-8878-7A7BA28F5D45}" @@ -16,9 +12,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Vhdx", "DiscUtils EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DiscUtils", "DiscUtils", "{A431EF08-BBEF-4228-A12D-88149C3844F6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowsPhone", "WindowsPhone", "{6B6B2266-D083-4F82-9A0A-4A2D8D8F3F16}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FfuStream", "FfuStream\FfuStream.csproj", "{64DDFC5B-0C2F-4426-9DEE-1164292C0F25}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Img2Ffu", "Img2Ffu\Img2Ffu.csproj", "{97E58534-9D04-4E73-9243-50AA143A1DA6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -54,46 +48,6 @@ Global {6BB88918-A58C-4510-A99F-AA22C7D5BD55}.Release|x64.Build.0 = Release|x64 {6BB88918-A58C-4510-A99F-AA22C7D5BD55}.Release|x86.ActiveCfg = Release|x86 {6BB88918-A58C-4510-A99F-AA22C7D5BD55}.Release|x86.Build.0 = Release|x86 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|ARM32.ActiveCfg = Debug|ARM32 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|ARM32.Build.0 = Debug|ARM32 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|ARM64.Build.0 = Debug|ARM64 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|x64.ActiveCfg = Debug|x64 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|x64.Build.0 = Debug|x64 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|x86.ActiveCfg = Debug|x86 - {8CA492CB-6F12-4862-991C-B28468172730}.Debug|x86.Build.0 = Debug|x86 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CA492CB-6F12-4862-991C-B28468172730}.Release|Any CPU.Build.0 = Release|Any CPU - {8CA492CB-6F12-4862-991C-B28468172730}.Release|ARM32.ActiveCfg = Release|ARM32 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|ARM32.Build.0 = Release|ARM32 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|ARM64.ActiveCfg = Release|ARM64 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|ARM64.Build.0 = Release|ARM64 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|x64.ActiveCfg = Release|x64 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|x64.Build.0 = Release|x64 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|x86.ActiveCfg = Release|x86 - {8CA492CB-6F12-4862-991C-B28468172730}.Release|x86.Build.0 = Release|x86 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|ARM32.ActiveCfg = Debug|ARM32 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|ARM32.Build.0 = Debug|ARM32 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|ARM64.Build.0 = Debug|ARM64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|x64.ActiveCfg = Debug|x64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|x64.Build.0 = Debug|x64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|x86.ActiveCfg = Debug|x86 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Debug|x86.Build.0 = Debug|x86 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|Any CPU.Build.0 = Release|Any CPU - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|ARM32.ActiveCfg = Release|ARM32 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|ARM32.Build.0 = Release|ARM32 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|ARM64.ActiveCfg = Release|ARM64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|ARM64.Build.0 = Release|ARM64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|x64.ActiveCfg = Release|x64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|x64.Build.0 = Release|x64 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|x86.ActiveCfg = Release|x86 - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16}.Release|x86.Build.0 = Release|x86 {A76FBCBD-08EB-413D-8DD7-8264D4E50304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A76FBCBD-08EB-413D-8DD7-8264D4E50304}.Debug|Any CPU.Build.0 = Debug|Any CPU {A76FBCBD-08EB-413D-8DD7-8264D4E50304}.Debug|ARM32.ActiveCfg = Debug|ARM32 @@ -154,33 +108,31 @@ Global {77D1F20E-4CD6-4810-9C5C-EF6CA3A9BC53}.Release|x64.Build.0 = Release|x64 {77D1F20E-4CD6-4810-9C5C-EF6CA3A9BC53}.Release|x86.ActiveCfg = Release|x86 {77D1F20E-4CD6-4810-9C5C-EF6CA3A9BC53}.Release|x86.Build.0 = Release|x86 - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|Any CPU.Build.0 = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|ARM32.ActiveCfg = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|ARM32.Build.0 = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|ARM64.Build.0 = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|x64.ActiveCfg = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|x64.Build.0 = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|x86.ActiveCfg = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Debug|x86.Build.0 = Debug|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|Any CPU.ActiveCfg = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|Any CPU.Build.0 = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|ARM32.ActiveCfg = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|ARM32.Build.0 = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|ARM64.ActiveCfg = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|ARM64.Build.0 = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|x64.ActiveCfg = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|x64.Build.0 = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|x86.ActiveCfg = Release|Any CPU - {64DDFC5B-0C2F-4426-9DEE-1164292C0F25}.Release|x86.Build.0 = Release|Any CPU + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|ARM32.ActiveCfg = Debug|ARM32 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|ARM32.Build.0 = Debug|ARM32 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|ARM64.Build.0 = Debug|ARM64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|x64.ActiveCfg = Debug|x64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|x64.Build.0 = Debug|x64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|x86.ActiveCfg = Debug|x86 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Debug|x86.Build.0 = Debug|x86 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|Any CPU.Build.0 = Release|Any CPU + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|ARM32.ActiveCfg = Release|ARM32 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|ARM32.Build.0 = Release|ARM32 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|ARM64.ActiveCfg = Release|ARM64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|ARM64.Build.0 = Release|ARM64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|x64.ActiveCfg = Release|x64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|x64.Build.0 = Release|x64 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|x86.ActiveCfg = Release|x86 + {97E58534-9D04-4E73-9243-50AA143A1DA6}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {8CA492CB-6F12-4862-991C-B28468172730} = {6B6B2266-D083-4F82-9A0A-4A2D8D8F3F16} - {5C2B4F9B-479D-4301-AEB9-33B83B6F5B16} = {6B6B2266-D083-4F82-9A0A-4A2D8D8F3F16} {A76FBCBD-08EB-413D-8DD7-8264D4E50304} = {A431EF08-BBEF-4228-A12D-88149C3844F6} {37E19DC2-07BE-4E1E-8878-7A7BA28F5D45} = {A431EF08-BBEF-4228-A12D-88149C3844F6} {77D1F20E-4CD6-4810-9C5C-EF6CA3A9BC53} = {A431EF08-BBEF-4228-A12D-88149C3844F6} diff --git a/Ffu2Vhdx/Ffu2Vhdx.csproj b/Ffu2Vhdx/Ffu2Vhdx.csproj index 92964ca..664bfa6 100644 --- a/Ffu2Vhdx/Ffu2Vhdx.csproj +++ b/Ffu2Vhdx/Ffu2Vhdx.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0-windows enable enable x64;AnyCPU;ARM32;ARM64;x86 @@ -10,7 +10,7 @@ - + diff --git a/Ffu2Vhdx/Program.cs b/Ffu2Vhdx/Program.cs index 48660dc..a41ca49 100644 --- a/Ffu2Vhdx/Program.cs +++ b/Ffu2Vhdx/Program.cs @@ -1,7 +1,8 @@ using DiscUtils; using DiscUtils.Streams; using DiscUtils.Vhdx; -using FfuStream; +using Img2Ffu.Reader; +using Img2Ffu.Reader.Data; namespace Ffu2Vhdx { @@ -102,12 +103,9 @@ namespace Ffu2Vhdx { DiscUtils.Setup.SetupHelper.RegisterAssembly(typeof(Disk).Assembly); - using FileStream ffuStream = File.OpenRead(ffuPath); - FfuFile ffuFile = new(ffuStream); - - for (int i = 0; i < ffuFile.StoreCount; i++) + for (int i = 0; i < FullFlashUpdateReaderStream.GetStoreCount(ffuPath); i++) { - using FfuStoreStream store = ffuFile.OpenStore(i); + using FullFlashUpdateReaderStream store = new(ffuPath, (ulong)i); string friendlyDevicePath = FormatDevicePath(store.DevicePath); string vhdfile = Path.Combine(outputDirectory, $"Store{i}_{ReplaceInvalidChars(store.DevicePath)}.vhdx"); @@ -117,18 +115,16 @@ namespace Ffu2Vhdx Console.WriteLine($"Device Path: {friendlyDevicePath}"); Console.WriteLine($"Destination: {vhdfile}"); - long SectorSize = ffuFile.GetSectorSize(i); - using Stream fs = new FileStream(vhdfile, FileMode.CreateNew, FileAccess.ReadWrite); - using VirtualDisk outDisk = Disk.InitializeDynamic(fs, Ownership.None, store.Length, logicalSectorSize: (int)SectorSize); + using VirtualDisk outDisk = Disk.InitializeDynamic(fs, Ownership.None, store.Length, logicalSectorSize: store.SectorSize); StreamPump pump = new() { InputStream = store, OutputStream = outDisk.Content, SparseCopy = true, - SparseChunkSize = (int)SectorSize, - BufferSize = (int)SectorSize * 1024 + SparseChunkSize = store.SectorSize, + BufferSize = store.SectorSize * 1024 }; long totalBytes = store.Length; diff --git a/FfuStream/FfuFile.cs b/FfuStream/FfuFile.cs deleted file mode 100644 index afea06f..0000000 --- a/FfuStream/FfuFile.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Microsoft.WindowsPhone.Imaging; - -namespace FfuStream -{ - public class FfuFile - { - private readonly FullFlashUpdateImage ffuImage; - - private readonly PayloadReader payloadReader; - - public FfuFile(Stream stream) - { - ffuImage = new(stream); - Stream imageStream = ffuImage.GetImageStream(); - payloadReader = new(imageStream); - - if (payloadReader.Payloads.Count() != ffuImage.StoreCount) - { - throw new ImageStorageException("Store counts in metadata and store header do not match."); - } - } - - public int StoreCount => ffuImage.StoreCount; - - public void WriteStoreToStream(int StoreIndex, Stream outputStream) - { - if (StoreIndex >= StoreCount || StoreIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(StoreIndex)); - } - - FullFlashUpdateStore fullFlashUpdateStore = ffuImage.Stores[StoreIndex]; - StorePayload storePayload = payloadReader.Payloads[StoreIndex]; - - payloadReader.WriteToStream(outputStream, storePayload, fullFlashUpdateStore.MinSectorCount, fullFlashUpdateStore.SectorSize); - } - - public FfuStoreStream OpenStore(int StoreIndex) - { - if (StoreIndex >= StoreCount || StoreIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(StoreIndex)); - } - - FullFlashUpdateStore fullFlashUpdateStore = ffuImage.Stores[StoreIndex]; - StorePayload storePayload = payloadReader.Payloads[StoreIndex]; - - return new FfuStoreStream(payloadReader, fullFlashUpdateStore, storePayload); - } - - public uint GetMinSectorCount(int StoreIndex) - { - if (StoreIndex >= StoreCount || StoreIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(StoreIndex)); - } - - FullFlashUpdateStore fullFlashUpdateStore = ffuImage.Stores[StoreIndex]; - return fullFlashUpdateStore.MinSectorCount; - } - - public uint GetSectorSize(int StoreIndex) - { - if (StoreIndex >= StoreCount || StoreIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(StoreIndex)); - } - - FullFlashUpdateStore fullFlashUpdateStore = ffuImage.Stores[StoreIndex]; - return fullFlashUpdateStore.SectorSize; - } - } -} \ No newline at end of file diff --git a/FfuStream/FfuStoreStream.cs b/FfuStream/FfuStoreStream.cs deleted file mode 100644 index f75335c..0000000 --- a/FfuStream/FfuStoreStream.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Microsoft.WindowsPhone.Imaging; - -namespace FfuStream -{ - public partial class FfuStoreStream : Stream - { - private readonly PayloadReader payloadReader; - private readonly FullFlashUpdateStore fullFlashUpdateStore; - private readonly StorePayload storePayload; - private long position = 0; - - public FfuStoreStream(PayloadReader payloadReader, FullFlashUpdateStore fullFlashUpdateStore, StorePayload storePayload) - { - this.payloadReader = payloadReader; - this.fullFlashUpdateStore = fullFlashUpdateStore; - this.storePayload = storePayload; - } - - public string DevicePath => fullFlashUpdateStore.DevicePath; - - public override bool CanRead => true; - - public override bool CanSeek => true; - - public override bool CanWrite => false; - - public override long Length => ((long)fullFlashUpdateStore.MinSectorCount * fullFlashUpdateStore.SectorSize); - - public override long Position - { - get => position; - set => position = value; - } - - public override void Flush() - { - // Nothing to do! - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (position + count > Length) - { - count = (int)Length - (int)position; - } - - payloadReader.WriteToStream(storePayload, fullFlashUpdateStore.MinSectorCount, fullFlashUpdateStore.SectorSize, position, buffer, offset, count); - - position += count; - - return count; - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (offset > Length) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - switch (origin) - { - case SeekOrigin.Begin: - { - if (offset < 0) - { - throw new IOException(); - } - - position = offset; - break; - } - case SeekOrigin.Current: - { - int tempPosition = (int)position + (int)offset; - if (tempPosition < 0) - { - throw new IOException(); - } - - position = tempPosition; - break; - } - case SeekOrigin.End: - { - int tempPosition = (int)Length + (int)offset; - if (tempPosition < 0) - { - throw new IOException(); - } - - position = tempPosition; - break; - } - default: - { - throw new ArgumentException(nameof(origin)); - } - } - - return position; - } - - public override void SetLength(long value) - { - // Nothing to do! - } - - public override void Write(byte[] buffer, int offset, int count) - { - // Nothing to do! - } - } -} \ No newline at end of file diff --git a/FfuStream/FfuStream.csproj b/FfuStream/FfuStream.csproj deleted file mode 100644 index 6d7f6b6..0000000 --- a/FfuStream/FfuStream.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net7.0 - enable - enable - - - - - - - - diff --git a/ImageCommon/CodeSite.cs b/ImageCommon/CodeSite.cs deleted file mode 100644 index 1581ebd..0000000 --- a/ImageCommon/CodeSite.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Microsoft.WindowsPhone.Imaging -{ - public enum CodeSite - { - Unknown = -1, - Security, - ImageCommon, - Imaging, - Packages, - FeatureManifests, - StorageService, - CommitImage, - UpdateAPI, - DeviceLayoutValidation, - CompDBCommon, - WcosImagePrep, - LCUPub - } -} diff --git a/ImageCommon/ErrorCategory.cs b/ImageCommon/ErrorCategory.cs deleted file mode 100644 index 5bf6aee..0000000 --- a/ImageCommon/ErrorCategory.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Microsoft.WindowsPhone.Imaging -{ - public enum ErrorCategory - { - Unknown = -1, - BadCommandLineParams, - IncorrectUserInput, - MissingArtifact, - BadContent, - UnexpectedState, - XmlSerialization - } -} diff --git a/ImageCommon/FullFlashUpdateHeaders.cs b/ImageCommon/FullFlashUpdateHeaders.cs deleted file mode 100644 index e7cb131..0000000 --- a/ImageCommon/FullFlashUpdateHeaders.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.WindowsPhone.Imaging -{ - public static class FullFlashUpdateHeaders - { - public static byte[] GetSecurityHeaderSignature() - { - return Encoding.ASCII.GetBytes("SignedImage "); - } - - public static byte[] GetImageHeaderSignature() - { - return Encoding.ASCII.GetBytes("ImageFlash "); - } - - public static uint SecurityHeaderSize => (uint)(Marshal.SizeOf(default(SecurityHeader)) + GetSecurityHeaderSignature().Length); - - public static uint ImageHeaderSize => (uint)(FullFlashUpdateImage.ImageHeaderSize + GetImageHeaderSignature().Length); - - public static uint ImageHeaderExSize => (uint)(FullFlashUpdateImage.ImageHeaderExSize + GetImageHeaderSignature().Length); - } -} diff --git a/ImageCommon/FullFlashUpdateImage.cs b/ImageCommon/FullFlashUpdateImage.cs deleted file mode 100644 index 1d33482..0000000 --- a/ImageCommon/FullFlashUpdateImage.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class FullFlashUpdateImage - { - private readonly Stream imageStream; - - public FullFlashUpdateImage(Stream imageStream) - { - this.imageStream = imageStream; - - _ffuStoragePools = new List(); - _ffuStores = new List(); - - using BinaryReader binaryReader = new(imageStream, Encoding.UTF8, true); - uint num = binaryReader.ReadUInt32(); - byte[] signature = binaryReader.ReadBytes(12); - if (num != FullFlashUpdateHeaders.SecurityHeaderSize || !SecurityHeader.ValidateSignature(signature)) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.BadContent, "Unable to load image because the security header is invalid."); - } - _securityHeader.ByteCount = num; - _securityHeader.ChunkSize = binaryReader.ReadUInt32(); - _securityHeader.HashAlgorithmID = binaryReader.ReadUInt32(); - _securityHeader.CatalogSize = binaryReader.ReadUInt32(); - _securityHeader.HashTableSize = binaryReader.ReadUInt32(); - CatalogData = binaryReader.ReadBytes((int)_securityHeader.CatalogSize); - HashTableData = binaryReader.ReadBytes((int)_securityHeader.HashTableSize); - _ = binaryReader.ReadBytes((int)SecurityPadding); - num = binaryReader.ReadUInt32(); - signature = binaryReader.ReadBytes(12); - if ((num != FullFlashUpdateHeaders.ImageHeaderSize && num != FullFlashUpdateHeaders.ImageHeaderExSize) || !ImageHeader.ValidateSignature(signature)) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.BadContent, "Unable to load image because the image header is invalid."); - } - _imageHeader.ByteCount = num; - _imageHeader.ManifestLength = binaryReader.ReadUInt32(); - _imageHeader.ChunkSize = binaryReader.ReadUInt32(); - _imageHeader.NumberOfDeviceTargetingIds = num == FullFlashUpdateHeaders.ImageHeaderSize ? 0U : binaryReader.ReadUInt32(); - Manifest = new FullFlashUpdateManifest(this, binaryReader.ReadBytes((int)_imageHeader.ManifestLength)); - Manifest.Validate("2.0"); - if (_imageHeader.ChunkSize > 0U) - { - _ = binaryReader.ReadBytes((int)CalculateAlignment((uint)imageStream.Position, _imageHeader.ChunkSize * 1024U)); - } - _payloadOffset = imageStream.Position; - } - - public Stream GetImageStream() - { - imageStream.Position = _payloadOffset; - return imageStream; - } - - internal void AddStoragePool(ManifestCategory A_1) - { - if (A_1 == null) - { - throw new ArgumentNullException("category"); - } - _ffuStoragePools.Add(new FullFlashUpdateStoragePool(this, A_1)); - } - - internal void AddStore(ManifestCategory A_1) - { - if (A_1 == null) - { - throw new ArgumentNullException("category"); - } - _ffuStores.Add(new FullFlashUpdateStore(this, null, A_1)); - } - - public static int ImageHeaderSize => Marshal.SizeOf(new ImageHeader()); - - public static int ImageHeaderExSize => Marshal.SizeOf(new ImageHeaderEx()); - - public byte[] CatalogData - { - get; set; - } - - public byte[] HashTableData - { - get; set; - } - - public List StoragePools => new(_ffuStoragePools); - - public List Stores => new(_ffuStores); - - public int StoreCount => Stores.Count; - - public FullFlashUpdateManifest Manifest - { - get; private set; - } - - internal uint SecurityPadding - { - get - { - uint num = 1024U; - if (_imageHeader.ChunkSize != 0U) - { - num *= _imageHeader.ChunkSize; - } - else - { - if (_securityHeader.ChunkSize == 0U) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.BadContent, "Neither the of the headers have been initialized with a chunk size."); - } - num *= _securityHeader.ChunkSize; - } - return CalculateAlignment(FullFlashUpdateHeaders.SecurityHeaderSize + (uint)(CatalogData != null ? CatalogData.Length : 0) + (uint)(HashTableData != null ? HashTableData.Length : 0), num); - } - } - - private uint CalculateAlignment(uint A_1, uint A_2) - { - uint result = 0U; - uint num = A_1 % A_2; - if (num > 0U) - { - result = A_2 - num; - } - return result; - } - - public static readonly uint PartitionTypeMbr = 0U; - - public static readonly uint PartitionTypeGpt = 1U; - - private readonly List _ffuStoragePools; - - private readonly List _ffuStores; - - private readonly long _payloadOffset; - - private readonly ImageHeaderEx _imageHeader = new(); - - private SecurityHeader _securityHeader; - } -} diff --git a/ImageCommon/FullFlashUpdateManifest.cs b/ImageCommon/FullFlashUpdateManifest.cs deleted file mode 100644 index 783f473..0000000 --- a/ImageCommon/FullFlashUpdateManifest.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class FullFlashUpdateManifest - { - internal FullFlashUpdateManifest(FullFlashUpdateImage A_1) - { - _ffuImage = A_1; - } - - internal FullFlashUpdateManifest(FullFlashUpdateImage A_1, byte[] A_2) : this(A_1) - { - using MemoryStream memoryStream = new(A_2); - using StreamReader streamReader = new(memoryStream, Encoding.ASCII); - Regex regex = new("^\\s*\\[(?[^\\]]+)\\]\\s*$"); - Regex regex2 = new("^\\s*(?[^=\\s]+)\\s*=\\s*(?.*)(\\s*$)"); - Match match = null; - ManifestCategory manifestCategory = null; - while (!streamReader.EndOfStream) - { - string text = streamReader.ReadLine(); - if (regex.IsMatch(text)) - { - match = null; - string value = regex.Match(text).Groups["category"].Value; - ProcessCategory(manifestCategory); - if (string.Compare(value, "StoragePool", StringComparison.Ordinal) == 0) - { - manifestCategory = new ManifestCategory("StoragePool", "StoragePool"); - } - else - { - manifestCategory = string.Compare(value, "StoragePoolStore", StringComparison.Ordinal) == 0 - ? new ManifestCategory("StoragePoolStore", "StoragePoolStore") - : string.Compare(value, "StoragePoolPartition", StringComparison.Ordinal) == 0 - ? new ManifestCategory("StoragePoolPartition", "StoragePoolPartition") - : string.Compare(value, "Store", StringComparison.Ordinal) == 0 - ? new ManifestCategory("Store", "Store") - : string.Compare(value, "Partition", StringComparison.Ordinal) == 0 - ? new ManifestCategory("Partition", "Partition") - : AddCategory(value, value); - } - } - else if (manifestCategory != null && regex2.IsMatch(text)) - { - match = null; - Match match2 = regex2.Match(text); - manifestCategory[match2.Groups["key"].Value] = match2.Groups["value"].Value; - if (match2.Groups["key"].ToString() == "Description") - { - match = match2; - } - } - else if (match != null) - { - ManifestCategory manifestCategory2 = manifestCategory; - string value2 = match.Groups["key"].Value; - manifestCategory2[value2] = manifestCategory2[value2] + "\r\n" + text; - } - } - ProcessCategory(manifestCategory); - } - - internal ManifestCategory AddCategory(string A_1, string A_2) - { - if (this[A_1] != null) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, "Cannot add duplicate categories to a manifest."); - } - ManifestCategory manifestCategory = new(A_1, A_2); - _ = _categories.Add(manifestCategory); - return manifestCategory; - } - - internal ManifestCategory this[string A_1] - { - get - { - foreach (object obj in _categories) - { - ManifestCategory manifestCategory = (ManifestCategory)obj; - if (string.Compare(manifestCategory.Name, A_1, StringComparison.Ordinal) == 0) - { - return manifestCategory; - } - } - return null; - } - } - - internal void Validate(string A_1) - { - if (this["FullFlash"] == null) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.BadContent, "Missing 'FullFlash' or 'Image' category in the manifest"); - } - string text = this["FullFlash"]["Version"]; - if (text == null) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.BadContent, "Missing 'Version' name/value pair in the 'FullFlash' category."); - } - if (!text.Equals(A_1, StringComparison.OrdinalIgnoreCase)) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.BadContent, string.Concat(new string[] - { - "'Version' value (", - text, - ") does not match current version of ", - A_1, - "." - })); - } - } - - private void ProcessCategory(ManifestCategory A_1) - { - if (A_1 != null) - { - if (string.CompareOrdinal(A_1.Name, "StoragePool") == 0) - { - _ffuImage.AddStoragePool(A_1); - return; - } - if (string.CompareOrdinal(A_1.Name, "StoragePoolStore") == 0) - { - _ffuImage.StoragePools.Last().AddStore(A_1); - return; - } - if (string.CompareOrdinal(A_1.Name, "StoragePoolPartition") == 0) - { - _ffuImage.StoragePools.Last().Stores.Last().AddPartition(A_1); - return; - } - if (string.CompareOrdinal(A_1.Name, "Store") == 0) - { - _ffuImage.AddStore(A_1); - return; - } - if (string.CompareOrdinal(A_1.Name, "Partition") == 0) - { - _ffuImage.Stores.Last().AddPartition(A_1); - } - } - } - - private readonly ArrayList _categories = new(20); - - private readonly FullFlashUpdateImage _ffuImage; - } -} diff --git a/ImageCommon/FullFlashUpdatePartition.cs b/ImageCommon/FullFlashUpdatePartition.cs deleted file mode 100644 index 563574e..0000000 --- a/ImageCommon/FullFlashUpdatePartition.cs +++ /dev/null @@ -1,137 +0,0 @@ -namespace Microsoft.WindowsPhone.Imaging -{ - public class FullFlashUpdatePartition - { - public FullFlashUpdatePartition() - { - } - - public FullFlashUpdatePartition(FullFlashUpdateStore A_1, InputPartition A_2) : this() - { - Store = A_1; - SectorsInUse = 0U; - TotalSectors = A_2.TotalSectors; - PartitionType = A_2.Type; - PartitionId = A_2.Id; - Name = A_2.Name; - UseAllSpace = A_2.UseAllSpace; - FileSystem = A_2.FileSystem; - Bootable = A_2.Bootable; - ReadOnly = A_2.ReadOnly; - Hidden = A_2.Hidden; - AttachDriveLetter = A_2.AttachDriveLetter; - ServicePartition = A_2.ServicePartition; - PrimaryPartition = A_2.PrimaryPartition; - RequiredToFlash = A_2.RequiredToFlash; - ByteAlignment = A_2.ByteAlignment; - ClusterSize = A_2.ClusterSize; - OffsetInSectors = A_2.OffsetInSectors; - PrepareFveMetadata = A_2.PrepareFveMetadata; - } - - public override string ToString() - { - return Name; - } - - private FullFlashUpdateStore Store - { - get; set; - } - - public string Name - { - get; set; - } - - public uint TotalSectors - { - get; set; - } - - public string PartitionType - { - get; set; - } - - public string PartitionId - { - get; set; - } - - public bool Bootable - { - get; set; - } - - public bool ReadOnly - { - get; set; - } - - public bool Hidden - { - get; set; - } - - public bool AttachDriveLetter - { - get; set; - } - - public bool ServicePartition - { - get; set; - } - - public string PrimaryPartition - { - get; set; - } - - public string FileSystem - { - get; set; - } - - public uint ByteAlignment - { - get; set; - } - - public uint ClusterSize - { - get; set; - } - - public uint SectorsInUse - { - get; set; - } - - public bool UseAllSpace - { - get; set; - } - - public bool RequiredToFlash - { - get; set; - } - - public uint SectorAlignment - { - get; set; - } - - public ulong OffsetInSectors - { - get; set; - } - - public bool PrepareFveMetadata - { - get; set; - } - } -} diff --git a/ImageCommon/FullFlashUpdateStoragePool.cs b/ImageCommon/FullFlashUpdateStoragePool.cs deleted file mode 100644 index 3b5af51..0000000 --- a/ImageCommon/FullFlashUpdateStoragePool.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class FullFlashUpdateStoragePool - { - private FullFlashUpdateStoragePool() - { - _ffuStores = new List(); - } - - public FullFlashUpdateStoragePool(FullFlashUpdateImage A_1, ManifestCategory A_2) : this() - { - Image = A_1; - Name = A_2.GetString("Name"); - } - - public FullFlashUpdateImage Image - { - get; private set; - } - - private string Name - { - get; set; - } - - public List Stores => new(_ffuStores); - - internal void AddStore(ManifestCategory A_1) - { - if (A_1 == null) - { - throw new ArgumentNullException("category"); - } - _ffuStores.Add(new FullFlashUpdateStore(Image, this, A_1)); - } - - private readonly List _ffuStores; - } -} diff --git a/ImageCommon/FullFlashUpdateStore.cs b/ImageCommon/FullFlashUpdateStore.cs deleted file mode 100644 index cb57927..0000000 --- a/ImageCommon/FullFlashUpdateStore.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class FullFlashUpdateStore : IDisposable - { - public FullFlashUpdateStore() - { - _ffuPartitions = new List(); - } - - public FullFlashUpdateStore(FullFlashUpdateImage A_1, FullFlashUpdateStoragePool A_2, ManifestCategory A_3) : this() - { - Initialize(A_1, A_2, A_3.GetString("StoreId"), A_3.GetString("StoreType"), A_3.GetBool("IsSpace"), A_3.GetBool("IsMainOSStore", true), A_3.GetString("Provisioning"), A_3.GetString("DevicePath"), A_3.GetBool("OnlyAllocateDefinedGptEntries"), A_3.GetUInt32("MinSectorCount"), A_3.GetUInt32("SectorSize")); - } - - private void Initialize(FullFlashUpdateImage A_1, FullFlashUpdateStoragePool A_2, string A_3, string A_4, bool A_5, bool A_6, string A_7, string A_8, bool A_9, uint A_10, uint A_11) - { - Image = A_1; - StoragePool = A_2; - Id = A_3; - Type = A_4; - IsSpace = A_5; - IsMainOSStore = A_6; - Provisioning = A_7; - DevicePath = A_8; - OnlyAllocateDefinedGptEntries = A_9; - MinSectorCount = A_10; - SectorSize = A_11; - } - - ~FullFlashUpdateStore() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool A_1) - { - if (_alreadyDisposed) - { - return; - } - if (A_1) - { - Partitions = null; - } - _alreadyDisposed = true; - } - - private FullFlashUpdateImage Image - { - get; set; - } - - private FullFlashUpdateStoragePool StoragePool - { - get; set; - } - - private string Id - { - get; set; - } - - private string Type - { - get; set; - } - - public bool IsMainOSStore - { - get; private set; - } - - private string Provisioning - { - get; set; - } - - public string DevicePath - { - get; private set; - } - - private bool OnlyAllocateDefinedGptEntries - { - get; set; - } - - public uint MinSectorCount - { - get; set; - } - - public uint SectorSize - { - get; set; - } - - public List Partitions - { - get => new(_ffuPartitions); - private set => _ffuPartitions = value; - } - - public FullFlashUpdatePartition this[string A_1] - { - get - { - foreach (FullFlashUpdatePartition fullFlashUpdatePartition in Partitions) - { - if (string.CompareOrdinal(fullFlashUpdatePartition.Name, A_1) == 0) - { - return fullFlashUpdatePartition; - } - } - return null; - } - } - - public bool IsSpace - { - get; private set; - } - - internal void AddPartition(FullFlashUpdatePartition A_1) - { - if (this[A_1.Name] != null) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, "Two partitions in a store have the same name (" + A_1.Name + ")."); - } - if (!IsSpace && IsMainOSStore) - { - if (MinSectorCount != 0U && A_1.TotalSectors > MinSectorCount) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, "The partition " + A_1.Name + " is too large for the store."); - } - if (A_1.UseAllSpace) - { - using (List.Enumerator enumerator = Partitions.GetEnumerator()) - { - while (enumerator.MoveNext()) - { - if (enumerator.Current.UseAllSpace) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, "Two partitions in the same store have the UseAllSpace flag set."); - } - } - goto IL_FA; - } - } - if (A_1.SectorsInUse > A_1.TotalSectors) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, string.Format("The partition data is invalid. There are more used sectors ({0}) than total sectors ({1}) for partition:{2}", A_1.SectorsInUse, A_1.TotalSectors, A_1.Name)); - } - IL_FA: - if (MinSectorCount != 0U) - { - if (A_1.UseAllSpace) - { - _sectorsUsed += 1U; - } - else - { - _sectorsUsed += A_1.TotalSectors; - } - if (_sectorsUsed > MinSectorCount) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, string.Format("Partition ({0}) on the Store does not fit. SectorsUsed = {1} > MinSectorCount = {2}", A_1.Name, _sectorsUsed, MinSectorCount)); - } - } - } - _ffuPartitions.Add(A_1); - } - - internal void AddPartition(ManifestCategory A_1) - { - uint sectorsInUse = 0U; - uint num = 0U; - bool flag = false; - if (A_1 == null) - { - throw new ArgumentNullException("category"); - } - if (IsMainOSStore) - { - sectorsInUse = A_1.GetUInt32("UsedSectors"); - num = A_1.GetUInt32("TotalSectors"); - flag = A_1.GetBool("UseAllSpace"); - if (!flag && num == 0U) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, "The partition category for partition " + A_1.GetString("Name") + " must contain either a 'TotalSectors' or 'UseAllSpace' key/value pair."); - } - if (flag && num > 0U) - { - throw new ImageCommonException(CodeSite.StorageService, ErrorCategory.IncorrectUserInput, "The partition category for partition " + A_1.GetString("Name") + " cannot contain both a 'TotalSectors' and a 'UseAllSpace' key/value pair."); - } - } - AddPartition(new FullFlashUpdatePartition(this, new InputPartition - { - Name = A_1.GetString("Name"), - Type = A_1.GetString("Type"), - Id = A_1.GetString("Id"), - ReadOnly = A_1.GetBool("ReadOnly"), - AttachDriveLetter = A_1.GetBool("AttachDriveLetter"), - ServicePartition = A_1.GetBool("ServicePartition"), - Hidden = A_1.GetBool("Hidden"), - Bootable = A_1.GetBool("Bootable"), - TotalSectors = num, - UseAllSpace = flag, - FileSystem = A_1.GetString("FileSystem"), - PrimaryPartition = A_1.GetString("Primary"), - RequiredToFlash = A_1.GetBool("RequiredToFlash"), - ByteAlignment = A_1.GetUInt32("ByteAlignment"), - ClusterSize = A_1.GetUInt32("ClusterSize"), - OffsetInSectors = A_1.GetUInt64("OffsetInSectors"), - PrepareFveMetadata = A_1.GetBool("PrepareFveMetadata") - }) - { - SectorsInUse = sectorsInUse, - SectorAlignment = A_1.GetUInt32("SectorAlignment") - }); - } - - private List _ffuPartitions; - - private uint _sectorsUsed; - - private bool _alreadyDisposed; - } -} diff --git a/ImageCommon/ImageCommon.csproj b/ImageCommon/ImageCommon.csproj deleted file mode 100644 index 98d70ce..0000000 --- a/ImageCommon/ImageCommon.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - net7.0 - Library - Microsoft Corporation - Copyright %28c%29 Microsoft Corporation. All rights reserved. - Microsoft %28R%29 Windows %28R%29 Operating System - 0.0.0.0 - x64;AnyCPU;ARM32;ARM64;x86 - - \ No newline at end of file diff --git a/ImageCommon/ImageCommonException.cs b/ImageCommon/ImageCommonException.cs deleted file mode 100644 index 62af000..0000000 --- a/ImageCommon/ImageCommonException.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Text; - -namespace Microsoft.WindowsPhone.Imaging -{ - [Serializable] - public class ImageCommonException : Exception - { - public CodeSite CodeSite - { - get; set; - } = CodeSite.Unknown; - - public ErrorCategory ErrorCategory - { - get; set; - } = ErrorCategory.Unknown; - - public ImageCommonException(CodeSite A_1, ErrorCategory A_2, string A_3) : base(A_3) - { - CodeSite = A_1; - ErrorCategory = A_2; - } - - public override string ToString() - { - StringBuilder stringBuilder = new(); - _ = stringBuilder.AppendLine(Message); - if (InnerException != null) - { - _ = stringBuilder.Append(InnerException.ToString()); - } - return stringBuilder.ToString(); - } - } -} diff --git a/ImageCommon/ImageHeader.cs b/ImageCommon/ImageHeader.cs deleted file mode 100644 index 8f23e68..0000000 --- a/ImageCommon/ImageHeader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.InteropServices; - -namespace Microsoft.WindowsPhone.Imaging -{ - [StructLayout(LayoutKind.Sequential)] - public class ImageHeader - { - public static bool ValidateSignature(byte[] A_0) - { - return A_0.SequenceEqual(FullFlashUpdateHeaders.GetImageHeaderSignature()); - } - - [MarshalAs(UnmanagedType.U4)] - public uint ByteCount; - - [MarshalAs(UnmanagedType.U4)] - public uint ManifestLength; - - [MarshalAs(UnmanagedType.U4)] - public uint ChunkSize; - } -} diff --git a/ImageCommon/ImageHeaderEx.cs b/ImageCommon/ImageHeaderEx.cs deleted file mode 100644 index 8d51265..0000000 --- a/ImageCommon/ImageHeaderEx.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Microsoft.WindowsPhone.Imaging -{ - [StructLayout(LayoutKind.Sequential)] - public class ImageHeaderEx : ImageHeader - { - [MarshalAs(UnmanagedType.U4)] - public uint NumberOfDeviceTargetingIds; - } -} diff --git a/ImageCommon/InputPartition.cs b/ImageCommon/InputPartition.cs deleted file mode 100644 index 67ef2e2..0000000 --- a/ImageCommon/InputPartition.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Xml.Serialization; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class InputPartition - { - public string Name - { - get; set; - } - - public string Type - { - get; set; - } - - public string Id - { - get; set; - } - - public bool ReadOnly - { - get; set; - } - - public bool AttachDriveLetter - { - get; set; - } - - public bool Hidden - { - get; set; - } - - public bool ServicePartition - { - get; set; - } - - public bool Bootable - { - get; set; - } - - public uint TotalSectors - { - get; set; - } - - public bool UseAllSpace - { - get; set; - } - - public string FileSystem - { - get; set; - } - - public string PrimaryPartition - { - get => string.IsNullOrEmpty(_primaryPartition) ? Name : _primaryPartition; - set => _primaryPartition = value; - } - - public bool RequiredToFlash - { - get; set; - } - - [XmlIgnore] - public uint ByteAlignment - { - get; set; - } - - [XmlIgnore] - public uint ClusterSize - { - get; set; - } - - [XmlIgnore] - public ulong OffsetInSectors - { - get; set; - } - - public bool PrepareFveMetadata - { - get; set; - } - - private string _primaryPartition; - } -} diff --git a/ImageCommon/ManifestCategory.cs b/ImageCommon/ManifestCategory.cs deleted file mode 100644 index 838b07d..0000000 --- a/ImageCommon/ManifestCategory.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Collections; -using System.Globalization; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class ManifestCategory - { - public string this[string A_1] - { - get => (string)_keyValues[A_1]; - set - { - if (_keyValues.ContainsKey(A_1)) - { - _keyValues[A_1] = value; - return; - } - if (A_1.Length > _maxKeySize) - { - _maxKeySize = A_1.Length; - } - _keyValues.Add(A_1, value); - } - } - - public string Category - { - get; set; - } - - public string Name - { - get; private set; - } - - public ManifestCategory(string A_1) - { - Name = A_1; - } - - public ManifestCategory(string A_1, string A_2) : this(A_1) - { - Category = A_2; - } - - internal bool GetBool(string A_1) - { - return GetBool(A_1, false); - } - - internal bool GetBool(string A_1, bool A_2) - { - return _keyValues.ContainsKey(A_1) ? bool.Parse(this[A_1]) : A_2; - } - - internal uint GetUInt32(string A_1) - { - return GetUInt32(A_1, 0U); - } - - internal uint GetUInt32(string A_1, uint A_2) - { - return _keyValues.ContainsKey(A_1) ? uint.Parse(this[A_1], CultureInfo.InvariantCulture) : A_2; - } - - internal ulong GetUInt64(string A_1) - { - return GetUInt64(A_1, 0UL); - } - - internal ulong GetUInt64(string A_1, ulong A_2) - { - return _keyValues.ContainsKey(A_1) ? ulong.Parse(this[A_1], CultureInfo.InvariantCulture) : A_2; - } - - internal string GetString(string A_1) - { - return GetString(A_1, null); - } - - internal string GetString(string A_1, string A_2) - { - return _keyValues.ContainsKey(A_1) ? this[A_1] : A_2; - } - - private int _maxKeySize; - - private readonly Hashtable _keyValues = new(); - } -} diff --git a/ImageCommon/SecurityHeader.cs b/ImageCommon/SecurityHeader.cs deleted file mode 100644 index 6a1fd89..0000000 --- a/ImageCommon/SecurityHeader.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Microsoft.WindowsPhone.Imaging -{ - public struct SecurityHeader - { - public static bool ValidateSignature(byte[] A_0) - { - byte[] securityHeaderSignature = FullFlashUpdateHeaders.GetSecurityHeaderSignature(); - for (int i = 0; i < securityHeaderSignature.Length; i++) - { - if (A_0[i] != securityHeaderSignature[i]) - { - return false; - } - } - return true; - } - - public uint ByteCount - { - get; set; - } - - public uint ChunkSize - { - readonly get; set; - } - - public uint HashAlgorithmID - { - get; set; - } - - public uint CatalogSize - { - readonly get; set; - } - - public uint HashTableSize - { - readonly get; set; - } - } -} diff --git a/ImageStorageServiceManaged/DataBlockEntry.cs b/ImageStorageServiceManaged/DataBlockEntry.cs deleted file mode 100644 index 6a95544..0000000 --- a/ImageStorageServiceManaged/DataBlockEntry.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class DataBlockEntry - { - public uint BytesPerBlock - { - get; private set; - } - - public DataBlockEntry(uint A_1) - { - BytesPerBlock = A_1; - DataSource = new DataBlockSource(); - } - - public List BlockLocationsOnDisk { get; } = new List(); - - public DataBlockSource DataSource - { - get; set; - } - - public void ReadEntryFromStream(BinaryReader A_1, uint A_2) - { - int num = A_1.ReadInt32(); - if (A_1.ReadUInt32() != 1U) - { - throw new ImageStorageException("More than one block per data block entry is not currently supported."); - } - for (int i = 0; i < num; i++) - { - DiskLocation diskLocation = new(); - diskLocation.Read(A_1); - BlockLocationsOnDisk.Add(diskLocation); - } - DataBlockSource dataSource = new() - { - Source = DataBlockSource.DataSource.Disk, - StorageOffset = A_2 * BytesPerBlock - }; - DataSource = dataSource; - } - } -} diff --git a/ImageStorageServiceManaged/DataBlockSource.cs b/ImageStorageServiceManaged/DataBlockSource.cs deleted file mode 100644 index 502fb6e..0000000 --- a/ImageStorageServiceManaged/DataBlockSource.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Microsoft.WindowsPhone.Imaging -{ - public class DataBlockSource - { - public DataSource Source - { - get; set; - } - - public ulong StorageOffset - { - get; set; - } - - public enum DataSource - { - Zero, - Disk, - Memory - } - } -} diff --git a/ImageStorageServiceManaged/DiskLocation.cs b/ImageStorageServiceManaged/DiskLocation.cs deleted file mode 100644 index cb32d6f..0000000 --- a/ImageStorageServiceManaged/DiskLocation.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.IO; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class DiskLocation - { - public DiskLocation() - { - BlockIndex = 0U; - AccessMethod = DiskLocation.DiskAccessMethod.DiskBegin; - } - - public DiskAccessMethod AccessMethod - { - get; set; - } - - public uint BlockIndex - { - get; set; - } - - public void Read(BinaryReader A_1) - { - AccessMethod = (DiskAccessMethod)A_1.ReadUInt32(); - BlockIndex = A_1.ReadUInt32(); - } - - public enum DiskAccessMethod : uint - { - DiskBegin, - DiskEnd = 2U - } - } -} diff --git a/ImageStorageServiceManaged/FullFlashUpdateType.cs b/ImageStorageServiceManaged/FullFlashUpdateType.cs deleted file mode 100644 index 27606e9..0000000 --- a/ImageStorageServiceManaged/FullFlashUpdateType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Microsoft.WindowsPhone.Imaging -{ - public enum FullFlashUpdateType : uint - { - FullUpdate, - PartialUpdate - } -} diff --git a/ImageStorageServiceManaged/ImageStorageException.cs b/ImageStorageServiceManaged/ImageStorageException.cs deleted file mode 100644 index b0f4174..0000000 --- a/ImageStorageServiceManaged/ImageStorageException.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Microsoft.WindowsPhone.Imaging -{ - [Serializable] - public class ImageStorageException : Exception - { - public ImageStorageException(string A_1) : base(A_1) - { - } - - public override string ToString() - { - string text = Message; - if (base.InnerException != null) - { - text += base.InnerException.ToString(); - } - return text; - } - } -} diff --git a/ImageStorageServiceManaged/ImageStorageServiceManaged.csproj b/ImageStorageServiceManaged/ImageStorageServiceManaged.csproj deleted file mode 100644 index 677429c..0000000 --- a/ImageStorageServiceManaged/ImageStorageServiceManaged.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net7.0 - Library - Microsoft.WindowsPhone.Imaging - Microsoft Corporation - Copyright %28c%29 Microsoft Corporation. All rights reserved. - Microsoft %28R%29 Windows %28R%29 Operating System - 0.0.0.0 - x64;AnyCPU;ARM32;ARM64;x86 - - - - - \ No newline at end of file diff --git a/ImageStorageServiceManaged/ImageStoreHeader.cs b/ImageStorageServiceManaged/ImageStoreHeader.cs deleted file mode 100644 index 4bbf5c3..0000000 --- a/ImageStorageServiceManaged/ImageStoreHeader.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System; -using System.IO; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class ImageStoreHeader - { - [StructVersion(Version = 1)] - public FullFlashUpdateType UpdateType - { - get; set; - } - - [StructVersion(Version = 1)] - public ushort MajorVersion - { - get; set; - } - - [StructVersion(Version = 1)] - public ushort MinorVersion - { - get; set; - } - - [StructVersion(Version = 1)] - public ushort FullFlashMajorVersion - { - get; set; - } - - [StructVersion(Version = 1)] - public ushort FullFlashMinorVersion - { - get; set; - } - - [StructVersion(Version = 1)] - public byte[] PlatformIdentifier - { - set => _platformId = value; - } - - [StructVersion(Version = 1)] - public uint BytesPerBlock - { - get; set; - } - - [StructVersion(Version = 1)] - public uint StoreDataEntryCount - { - get; set; - } - - [StructVersion(Version = 1)] - public uint StoreDataSizeInBytes - { - get; set; - } - - [StructVersion(Version = 1)] - public uint ValidationEntryCount - { - get; set; - } - - [StructVersion(Version = 1)] - public uint ValidationDataSizeInBytes - { - get; set; - } - - [StructVersion(Version = 1)] - public uint InitialPartitionTableBlockIndex - { - get; set; - } - - [StructVersion(Version = 1)] - public uint InitialPartitionTableBlockCount - { - get; set; - } - - [StructVersion(Version = 1)] - public uint FlashOnlyPartitionTableBlockIndex - { - get; set; - } - - [StructVersion(Version = 1)] - public uint FlashOnlyPartitionTableBlockCount - { - get; set; - } - - [StructVersion(Version = 1)] - public uint FinalPartitionTableBlockIndex - { - get; set; - } - - [StructVersion(Version = 1)] - public uint FinalPartitionTableBlockCount - { - get; set; - } - - [StructVersion(Version = 2)] - public ushort NumberOfStores - { - get => MajorVersion < 2 ? throw new NotImplementedException("NumberOfStores") : _numberOfStores; - set => _numberOfStores = value; - } - - [StructVersion(Version = 2)] - public ushort StoreIndex - { - set => _storeIndex = value; - } - - [StructVersion(Version = 2)] - public ulong StorePayloadSize - { - set => _storePayloadSize = value; - } - - [StructVersion(Version = 2)] - public ushort DevicePathLength - { - get => MajorVersion < 2 ? throw new NotImplementedException("DevicePathLength") : _devicePathLength; - set => _devicePathLength = value; - } - - [StructVersion(Version = 2)] - public byte[] DevicePath - { - set => _devicePath = value; - } - - public static ImageStoreHeader ReadFromStream(Stream A_0) - { - BinaryReader binaryReader = new(A_0); - ImageStoreHeader imageStoreHeader = new() - { - UpdateType = (FullFlashUpdateType)binaryReader.ReadUInt32(), - MajorVersion = binaryReader.ReadUInt16(), - MinorVersion = binaryReader.ReadUInt16(), - FullFlashMajorVersion = binaryReader.ReadUInt16(), - FullFlashMinorVersion = binaryReader.ReadUInt16(), - PlatformIdentifier = binaryReader.ReadBytes(ImageStoreHeader.PlatformIdSizeInBytes), - BytesPerBlock = binaryReader.ReadUInt32(), - StoreDataEntryCount = binaryReader.ReadUInt32(), - StoreDataSizeInBytes = binaryReader.ReadUInt32(), - ValidationEntryCount = binaryReader.ReadUInt32(), - ValidationDataSizeInBytes = binaryReader.ReadUInt32(), - InitialPartitionTableBlockIndex = binaryReader.ReadUInt32(), - InitialPartitionTableBlockCount = binaryReader.ReadUInt32(), - FlashOnlyPartitionTableBlockIndex = binaryReader.ReadUInt32(), - FlashOnlyPartitionTableBlockCount = binaryReader.ReadUInt32(), - FinalPartitionTableBlockIndex = binaryReader.ReadUInt32(), - FinalPartitionTableBlockCount = binaryReader.ReadUInt32() - }; - if (imageStoreHeader.MajorVersion >= 2) - { - imageStoreHeader.NumberOfStores = binaryReader.ReadUInt16(); - imageStoreHeader.StoreIndex = binaryReader.ReadUInt16(); - imageStoreHeader.StorePayloadSize = binaryReader.ReadUInt64(); - imageStoreHeader.DevicePathLength = binaryReader.ReadUInt16(); - imageStoreHeader.DevicePath = binaryReader.ReadBytes(imageStoreHeader.DevicePathLength * 2); - } - return imageStoreHeader; - } - - public static readonly int PlatformIdSizeInBytes = 192; - - private byte[] _platformId = new byte[ImageStoreHeader.PlatformIdSizeInBytes]; - - private ushort _numberOfStores = 1; - - private ushort _storeIndex = 1; - - private ulong _storePayloadSize; - - private ushort _devicePathLength; - - private byte[] _devicePath; - } -} diff --git a/ImageStorageServiceManaged/PayloadReader.cs b/ImageStorageServiceManaged/PayloadReader.cs deleted file mode 100644 index 16affb1..0000000 --- a/ImageStorageServiceManaged/PayloadReader.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class PayloadReader - { - public PayloadReader(Stream fileStream) - { - _payloadStream = fileStream; - _payloadOffsets = new List(); - - int numberOfStores = 1; - - for (int i = 1; i <= numberOfStores; i++) - { - StorePayload storePayload = new(false, true); - storePayload.ReadMetadataFromStream(fileStream); - - long position = 0; - if (_payloadStream.Position % (long)(ulong)storePayload.StoreHeader.BytesPerBlock != 0) - { - position = (long)(ulong)(storePayload.StoreHeader.BytesPerBlock - (uint)(_payloadStream.Position % (long)(ulong)storePayload.StoreHeader.BytesPerBlock)); - } - - _payloadStream.Position += position; - - _payloadOffsets.Add(new PayloadOffset - { - Payload = storePayload - }); - - if (storePayload.StoreHeader.MajorVersion >= 2) - { - numberOfStores = storePayload.StoreHeader.NumberOfStores; - } - } - - long streamPosition = _payloadStream.Position; - - for (int j = 0; j < numberOfStores; j++) - { - PayloadOffset payloadOffset = _payloadOffsets[j]; - payloadOffset.Offset = streamPosition; - ImageStoreHeader storeHeader = payloadOffset.Payload.StoreHeader; - streamPosition += (long)(ulong)(storeHeader.BytesPerBlock * storeHeader.StoreDataEntryCount); - } - } - - public void WriteToStream(Stream outputStream, StorePayload storePayload, ulong sectorCount, uint sectorSize) - { - uint bytesPerBlock = storePayload.StoreHeader.BytesPerBlock; - long totalSize = (long)(sectorCount * sectorSize); - - PayloadOffset payloadOffset = FindPayloadOffset(storePayload); - if (payloadOffset == null) - { - throw new ImageStorageException("Unable to find store payload."); - } - - _payloadStream.Position = payloadOffset.Offset; - - for (StorePayload.BlockPhase blockPhase = StorePayload.BlockPhase.Phase1; blockPhase != StorePayload.BlockPhase.Invalid; blockPhase++) - { - foreach (DataBlockEntry dataBlockEntry in storePayload.GetPhaseEntries(blockPhase)) - { - byte[] buffer = new byte[bytesPerBlock]; - _ = _payloadStream.Read(buffer, 0, (int)bytesPerBlock); - - for (int i = 0; i < dataBlockEntry.BlockLocationsOnDisk.Count; i++) - { - long offset = (long)(dataBlockEntry.BlockLocationsOnDisk[i].BlockIndex * (ulong)bytesPerBlock); - if (dataBlockEntry.BlockLocationsOnDisk[i].AccessMethod == DiskLocation.DiskAccessMethod.DiskEnd) - { - offset = totalSize - offset - (long)(ulong)bytesPerBlock; - } - - _ = outputStream.Seek(offset, SeekOrigin.Begin); - outputStream.Write(buffer, 0, (int)bytesPerBlock); - } - } - } - } - - private static bool WriteDetourOk(long inputPosition, int inputCount, long position, int count) - { - long begin = Math.Max(inputPosition, position); - long end = Math.Min(inputPosition + inputCount, position + count); - - return end > begin; - } - - private static int WriteDetour(long inputPosition, byte[] inputBuffer, int inputCount, long position, byte[] buffer, int offset, int count) - { - long begin = Math.Max(inputPosition, position); - long end = Math.Min(inputPosition + inputCount, position + count); - - if (end > begin) - { - int index = (int)(begin - position) + offset; - int inputIndex = (int)(begin - inputPosition); - - using var strm = new MemoryStream(buffer); - strm.Seek(index, SeekOrigin.Begin); - strm.Write(inputBuffer, inputIndex, (int)(end - begin)); - - return (int)(end - begin); - } - - return 0; - } - - public void WriteToStream(StorePayload storePayload, ulong sectorCount, uint sectorSize, long position, byte[] buffer, int offset, int count) - { - uint bytesPerBlock = storePayload.StoreHeader.BytesPerBlock; - long totalSize = (long)(sectorCount * sectorSize); - - PayloadOffset payloadOffset = FindPayloadOffset(storePayload); - if (payloadOffset == null) - { - throw new ImageStorageException("Unable to find store payload."); - } - - _payloadStream.Position = payloadOffset.Offset; - - int blocksRead = 0; - - for (StorePayload.BlockPhase blockPhase = StorePayload.BlockPhase.Phase1; blockPhase != StorePayload.BlockPhase.Invalid; blockPhase++) - { - int readCount = 0; - - foreach (DataBlockEntry dataBlockEntry in storePayload.GetPhaseEntries(blockPhase)) - { - if (readCount == count) - { - blocksRead++; - continue; - } - - bool blockMatches = false; - byte[] ibuffer = new byte[bytesPerBlock]; - - for (int i = 0; i < dataBlockEntry.BlockLocationsOnDisk.Count; i++) - { - long ioffset = (long)(dataBlockEntry.BlockLocationsOnDisk[i].BlockIndex * (ulong)bytesPerBlock); - if (dataBlockEntry.BlockLocationsOnDisk[i].AccessMethod == DiskLocation.DiskAccessMethod.DiskEnd) - { - ioffset = totalSize - ioffset - (long)(ulong)bytesPerBlock; - } - - if (blockMatches) - { - readCount += WriteDetour(ioffset, ibuffer, (int)bytesPerBlock, position, buffer, offset, count); - if (readCount == count) - { - break; - } - } - else - { - blockMatches = WriteDetourOk(ioffset, (int)bytesPerBlock, position, count); - if (blockMatches) - { - _payloadStream.Position = payloadOffset.Offset + (bytesPerBlock * blocksRead); - _payloadStream.Read(ibuffer, 0, (int)bytesPerBlock); - blocksRead++; - - readCount += WriteDetour(ioffset, ibuffer, (int)bytesPerBlock, position, buffer, offset, count); - if (readCount == count) - { - break; - } - } - } - } - - if (!blockMatches) - { - blocksRead++; - } - } - } - } - - public ReadOnlyCollection Payloads - { - get - { - List list = new(); - - foreach (PayloadOffset payloadOffset in _payloadOffsets) - { - list.Add(payloadOffset.Payload); - } - - return list.AsReadOnly(); - } - } - - private PayloadOffset FindPayloadOffset(StorePayload storePayload) - { - for (int i = 0; i < _payloadOffsets.Count; i++) - { - PayloadOffset payloadOffset = _payloadOffsets[i]; - - if (payloadOffset.Payload == storePayload) - { - return payloadOffset; - } - } - - return null; - } - - private readonly List _payloadOffsets; - - private readonly Stream _payloadStream; - - private class PayloadOffset - { - public StorePayload Payload - { - get; set; - } - - public long Offset - { - get; set; - } - } - } -} \ No newline at end of file diff --git a/ImageStorageServiceManaged/StorePayload.cs b/ImageStorageServiceManaged/StorePayload.cs deleted file mode 100644 index 7e295eb..0000000 --- a/ImageStorageServiceManaged/StorePayload.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Microsoft.WindowsPhone.Imaging -{ - public class StorePayload - { - public ImageStoreHeader StoreHeader - { - get; set; - } - - public List Phase1DataEntries - { - get; set; - } - - public List Phase2DataEntries - { - get; set; - } - - public List Phase3DataEntries - { - get; set; - } - - public StorePayload(bool A_1, bool A_2) - { - _recovery = A_1; - _useLegacyBehavior = A_2; - StoreHeader = new ImageStoreHeader(); - Phase1DataEntries = new List(); - Phase2DataEntries = new List(); - Phase3DataEntries = new List(); - } - - public List GetPhaseEntries(BlockPhase A_1) - { - List result = null; - switch (A_1) - { - case StorePayload.BlockPhase.Phase1: - result = Phase1DataEntries; - break; - case StorePayload.BlockPhase.Phase2: - result = Phase2DataEntries; - break; - case StorePayload.BlockPhase.Phase3: - result = Phase3DataEntries; - break; - } - return result; - } - - public void ReadMetadataFromStream(Stream A_1) - { - StoreHeader = ImageStoreHeader.ReadFromStream(A_1); - uint num = StoreHeader.InitialPartitionTableBlockIndex + StoreHeader.InitialPartitionTableBlockCount; - uint num2 = StoreHeader.FlashOnlyPartitionTableBlockIndex + StoreHeader.FlashOnlyPartitionTableBlockCount; - uint num3 = StoreHeader.FinalPartitionTableBlockIndex + StoreHeader.FinalPartitionTableBlockCount; - BinaryReader reader = new(A_1); - uint num4; - for (num4 = 0U; num4 < num; num4 += 1U) - { - DataBlockEntry dataBlockEntry = new(StoreHeader.BytesPerBlock); - dataBlockEntry.ReadEntryFromStream(reader, num4); - Phase1DataEntries.Add(dataBlockEntry); - } - while (num4 < num2) - { - DataBlockEntry dataBlockEntry2 = new(StoreHeader.BytesPerBlock); - dataBlockEntry2.ReadEntryFromStream(reader, num4); - Phase2DataEntries.Add(dataBlockEntry2); - num4 += 1U; - } - while (num4 < num3) - { - DataBlockEntry dataBlockEntry3 = new(StoreHeader.BytesPerBlock); - dataBlockEntry3.ReadEntryFromStream(reader, num4); - Phase3DataEntries.Add(dataBlockEntry3); - num4 += 1U; - } - } - - private readonly bool _recovery; - - private readonly bool _useLegacyBehavior = true; - - public enum BlockPhase - { - Phase1, - Phase2, - Phase3, - Invalid - } - } -} diff --git a/ImageStorageServiceManaged/StructVersionAttribute.cs b/ImageStorageServiceManaged/StructVersionAttribute.cs deleted file mode 100644 index 14ba93c..0000000 --- a/ImageStorageServiceManaged/StructVersionAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Microsoft.WindowsPhone.Imaging -{ - [AttributeUsage(AttributeTargets.Property)] - internal class StructVersionAttribute : Attribute - { - public ushort Version - { - get; set; - } - } -} diff --git a/Img2Ffu/Img2Ffu.csproj b/Img2Ffu/Img2Ffu.csproj new file mode 100644 index 0000000..af2de46 --- /dev/null +++ b/Img2Ffu/Img2Ffu.csproj @@ -0,0 +1,8 @@ + + + net8.0-windows + AnyCPU;ARM32;ARM64;x86;x64 + enable + true + + \ No newline at end of file diff --git a/Img2Ffu/Reader/Compression/WindowsNativeCompression.cs b/Img2Ffu/Reader/Compression/WindowsNativeCompression.cs new file mode 100644 index 0000000..7f64b91 --- /dev/null +++ b/Img2Ffu/Reader/Compression/WindowsNativeCompression.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Compression +{ + public class WindowsNativeCompression + { + [DllImport("ntdll.dll")] + private static extern uint RtlGetCompressionWorkSpaceSize(ushort CompressionFormatAndEngine, out uint CompressBufferWorkSpaceSize, out uint CompressFragmentWorkSpaceSize); + + [DllImport("ntdll.dll")] + private static extern uint RtlDecompressBufferEx(ushort CompressionFormat, byte[] UncompressedBuffer, int UncompressedBufferSize, byte[] CompressedBuffer, int CompressedBufferSize, out int FinalUncompressedSize, byte[] WorkSpace); + + [DllImport("ntdll.dll")] + private static extern uint RtlCompressBuffer(ushort CompressionFormatAndEngine, byte[] UncompressedBuffer, int UncompressedBufferSize, byte[] CompressedBuffer, int CompressedBufferSize, int UncompressedChunkSize, out int FinalCompressedSize, byte[] WorkSpace); + + public static byte[] Decompress(WindowsNativeCompressionAlgorithm CompressionFormat, byte[] CompressedBuffer, int UncompressedBufferSize) + { + uint ntStatus = RtlGetCompressionWorkSpaceSize((ushort)CompressionFormat, out uint compressBufferWorkSpaceSize, out _); + if (ntStatus != 0) + { + throw new Exception($"Cannot get decompress workspace size! NTSTATUS=0x{ntStatus:X8}"); + } + + byte[] UncompressedBuffer = new byte[UncompressedBufferSize]; + byte[] workSpace = new byte[compressBufferWorkSpaceSize]; + + + ntStatus = RtlDecompressBufferEx((ushort)CompressionFormat, UncompressedBuffer, UncompressedBuffer.Length, CompressedBuffer, CompressedBuffer.Length, out _, workSpace); + return ntStatus != 0 ? throw new Exception($"Cannot decompress buffer! NTSTATUS=0x{ntStatus:X8}") : UncompressedBuffer; + } + + public static byte[] Compress(WindowsNativeCompressionAlgorithm CompressionFormat, byte[] UncompressedBuffer, int CompressedBufferSize) + { + uint ntStatus = RtlGetCompressionWorkSpaceSize((ushort)CompressionFormat, out uint compressBufferWorkSpaceSize, out _); + if (ntStatus != 0) + { + throw new Exception($"Cannot get compression workspace size! NTSTATUS=0x{ntStatus:X8}"); + } + + byte[] CompressedBuffer = new byte[CompressedBufferSize]; + byte[] workSpace = new byte[compressBufferWorkSpaceSize]; + + + ntStatus = RtlCompressBuffer((ushort)CompressionFormat, UncompressedBuffer, UncompressedBuffer.Length, CompressedBuffer, CompressedBuffer.Length, 4096, out _, workSpace); + return ntStatus != 0 ? throw new Exception($"Cannot compress buffer! NTSTATUS=0x{ntStatus:X8}") : CompressedBuffer; + } + } +} diff --git a/Img2Ffu/Reader/Compression/WindowsNativeCompressionAlgorithm.cs b/Img2Ffu/Reader/Compression/WindowsNativeCompressionAlgorithm.cs new file mode 100644 index 0000000..3d5ff20 --- /dev/null +++ b/Img2Ffu/Reader/Compression/WindowsNativeCompressionAlgorithm.cs @@ -0,0 +1,17 @@ +using System; + +namespace Img2Ffu.Reader.Compression +{ + [Flags] + public enum WindowsNativeCompressionAlgorithm + { + COMPRESSION_FORMAT_NONE = 0x0000, + COMPRESSION_ENGINE_STANDARD = 0x0000, + COMPRESSION_FORMAT_DEFAULT = 0x0001, + COMPRESSION_FORMAT_LZNT1 = 0x0002, + COMPRESSION_FORMAT_XPRESS = 0x0003, + COMPRESSION_FORMAT_XPRESS_HUFF = 0x0004, + COMPRESSION_ENGINE_MAXIMUM = 0x0100, + COMPRESSION_ENGINE_HIBER = 0x0200 + } +} diff --git a/Img2Ffu/Reader/Data/ImageFlash.cs b/Img2Ffu/Reader/Data/ImageFlash.cs new file mode 100644 index 0000000..376f603 --- /dev/null +++ b/Img2Ffu/Reader/Data/ImageFlash.cs @@ -0,0 +1,202 @@ +using Img2Ffu.Reader.Compression; +using Img2Ffu.Reader.Enums; +using Img2Ffu.Reader.Structs; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Img2Ffu.Reader.Data +{ + public class ImageFlash + { + public ImageHeader ImageHeader; + public string ManifestData = ""; + public byte[] Padding = []; + public readonly List Stores = []; + + private readonly long DataBlocksPosition; + private readonly long InitialStreamPosition; + + + public ImageFlash(Stream stream) + { + InitialStreamPosition = stream.Position; + ImageHeader = stream.ReadStructure(); + + if (ImageHeader.Signature != "ImageFlash ") + { + throw new InvalidDataException("Invalid Image Header Signature!"); + } + + byte[] manifestDataBuffer = new byte[ImageHeader.ManifestLength]; + _ = stream.Read(manifestDataBuffer, 0, (int)ImageHeader.ManifestLength); + ASCIIEncoding asciiEncoding = new(); + ManifestData = asciiEncoding.GetString(manifestDataBuffer); + + long Position = stream.Position; + if (Position % (ImageHeader.ChunkSize * 1024) > 0) + { + long paddingSize = (ImageHeader.ChunkSize * 1024) - (Position % (ImageHeader.ChunkSize * 1024)); + Padding = new byte[paddingSize]; + _ = stream.Read(Padding, 0, (int)paddingSize); + } + + Store store = new(stream); + Stores.Add(store); + + if (store.StoreHeader.MajorVersion == 2 && store.StoreHeader.FullFlashMajorVersion == 2) + { + for (uint i = 2; i <= store.StoreHeaderV2.NumberOfStores; i++) + { + Stores.Add(new Store(stream)); + } + } + + DataBlocksPosition = stream.Position; + } + + + public ulong GetImageBlockCount() + { + ulong imageBlockCount = (ulong)(DataBlocksPosition - InitialStreamPosition) / (ImageHeader.ChunkSize * 1024); + ulong storeBlockCount = GetDataBlockCount(); + return imageBlockCount + storeBlockCount; + } + + public byte[] GetImageBlock(Stream Stream, ulong dataBlockIndex) + { + ulong imageBlockCount = (ulong)(DataBlocksPosition - InitialStreamPosition) / (ImageHeader.ChunkSize * 1024); + + // The data block is within the image headers + if (dataBlockIndex < imageBlockCount) + { + byte[] dataBlock = new byte[(ImageHeader.ChunkSize * 1024)]; + ulong dataBlockPosition = (ulong)InitialStreamPosition + (dataBlockIndex * (ImageHeader.ChunkSize * 1024)); + + _ = Stream.Seek((long)dataBlockPosition, SeekOrigin.Begin); + _ = Stream.Read(dataBlock, 0, dataBlock.Length); + + return dataBlock; + } + // The data block is within the store data blocks, those may be compressed + else + { + return GetDataBlock(Stream, dataBlockIndex - imageBlockCount); + } + } + + + public ulong GetDataBlockCount() + { + ulong dataBlockCount = 0; + foreach (Store store in Stores) + { + dataBlockCount += (ulong)store.WriteDescriptors.LongCount(); + } + return dataBlockCount; + } + + public byte[] GetDataBlock(Stream Stream, ulong dataBlockIndex) + { + ulong dataBlockIndexOffset = 0; + for (ulong storeIndex = 0; storeIndex < (ulong)Stores.Count; storeIndex++) + { + ulong storeDataBlockCount = GetStoreDataBlockCount(storeIndex); + if (dataBlockIndexOffset + storeDataBlockCount > dataBlockIndex) + { + return GetStoreDataBlock(Stream, storeIndex, dataBlockIndex - dataBlockIndexOffset); + } + + dataBlockIndexOffset += storeDataBlockCount; + } + + throw new Exception("Invalid data block index value"); + } + + + public ulong GetStoreDataBlockCount(ulong storeIndex) + { + return (ulong)Stores[(int)storeIndex].WriteDescriptors.LongCount(); + } + + public byte[] GetStoreDataBlock(Stream Stream, ulong storeIndex, ulong dataBlockIndex) + { + ulong dataBlockPosition = (ulong)DataBlocksPosition; + for (ulong s = 0; s < storeIndex; s++) + { + Store store = Stores[(int)s]; + if (store.CompressionAlgorithm != 0) + { + foreach (WriteDescriptor writeDescriptor in store.WriteDescriptors) + { + dataBlockPosition += writeDescriptor.DataSize; + } + } + else + { + dataBlockPosition += store.StoreHeader.WriteDescriptorCount * store.StoreHeader.BlockSize; + } + } + + Store currentStore = Stores[(int)storeIndex]; + byte[] dataBlock = new byte[currentStore.StoreHeader.BlockSize]; + + CompressionAlgorithm compressionAlgorithm = (CompressionAlgorithm)currentStore.CompressionAlgorithm; + + if (compressionAlgorithm != CompressionAlgorithm.None) + { + for (ulong i = 0; i < dataBlockIndex; i++) + { + WriteDescriptor writeDescriptor = currentStore.WriteDescriptors[(int)i]; + dataBlockPosition += writeDescriptor.DataSize; + } + + WriteDescriptor currentWriteDescriptor = currentStore.WriteDescriptors[(int)dataBlockIndex]; + uint compressedDataBlockSize = currentWriteDescriptor.DataSize; + byte[] compressedDataBlock = new byte[compressedDataBlockSize]; + + _ = Stream.Seek((long)dataBlockPosition, SeekOrigin.Begin); + _ = Stream.Read(compressedDataBlock, 0, compressedDataBlock.Length); + + switch ((CompressionAlgorithm)currentStore.CompressionAlgorithm) + { + case CompressionAlgorithm.Default: + { + dataBlock = WindowsNativeCompression.Decompress(WindowsNativeCompressionAlgorithm.COMPRESSION_FORMAT_XPRESS, compressedDataBlock, (int)currentStore.StoreHeader.BlockSize); + break; + } + case CompressionAlgorithm.LZNT1: + { + dataBlock = WindowsNativeCompression.Decompress(WindowsNativeCompressionAlgorithm.COMPRESSION_FORMAT_LZNT1, compressedDataBlock, (int)currentStore.StoreHeader.BlockSize); + break; + } + case CompressionAlgorithm.XPRESS: + { + dataBlock = WindowsNativeCompression.Decompress(WindowsNativeCompressionAlgorithm.COMPRESSION_FORMAT_XPRESS, compressedDataBlock, (int)currentStore.StoreHeader.BlockSize); + break; + } + case CompressionAlgorithm.XPRESS_HUFF: + { + dataBlock = WindowsNativeCompression.Decompress(WindowsNativeCompressionAlgorithm.COMPRESSION_FORMAT_XPRESS_HUFF, compressedDataBlock, (int)currentStore.StoreHeader.BlockSize); + break; + } + default: + { + throw new NotImplementedException("The compression algorithm this data block uses is not currently implemented."); + } + } + } + else + { + dataBlockPosition += dataBlockIndex * currentStore.StoreHeader.BlockSize; + + _ = Stream.Seek((long)dataBlockPosition, SeekOrigin.Begin); + _ = Stream.Read(dataBlock, 0, dataBlock.Length); + } + + return dataBlock; + } + } +} diff --git a/Img2Ffu/Reader/Data/SignedImage.cs b/Img2Ffu/Reader/Data/SignedImage.cs new file mode 100644 index 0000000..6b9995b --- /dev/null +++ b/Img2Ffu/Reader/Data/SignedImage.cs @@ -0,0 +1,135 @@ +using Img2Ffu.Reader.Structs; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Img2Ffu.Reader.Data +{ + public class SignedImage + { + public SecurityHeader SecurityHeader; + public byte[] Catalog; + public byte[] TableOfHashes; + public byte[] Padding = []; + public ImageFlash Image; + + public ulong HeaderSize + { + get; + } + public int ChunkSize => (int)(SecurityHeader.ChunkSizeInKB * 1024); + public ulong TotalChunkCount => Image.GetImageBlockCount(); + + public readonly List BlockHashes = []; + public X509Certificate? Certificate; + + private readonly Stream Stream; + + public SignedImage(Stream stream) + { + Stream = stream; + + SecurityHeader = stream.ReadStructure(); + + if (SecurityHeader.Signature != "SignedImage ") + { + throw new InvalidDataException("Invalid Security Header Signature!"); + } + + Catalog = new byte[SecurityHeader.CatalogSize]; + _ = stream.Read(Catalog, 0, (int)SecurityHeader.CatalogSize); + + TableOfHashes = new byte[SecurityHeader.HashTableSize]; + _ = stream.Read(TableOfHashes, 0, (int)SecurityHeader.HashTableSize); + + long Position = stream.Position; + uint sizeOfBlock = SecurityHeader.ChunkSizeInKB * 1024; + if (Position % sizeOfBlock > 0) + { + long paddingSize = sizeOfBlock - (Position % sizeOfBlock); + Padding = new byte[paddingSize]; + _ = stream.Read(Padding, 0, (int)paddingSize); + } + + try + { + Certificate = new X509Certificate(Catalog); + } + catch { } + + Image = new ImageFlash(stream); + + HeaderSize = (ulong)stream.Position; + + ParseBlockHashes(); + } + + private void ParseBlockHashes() + { + BlockHashes.Clear(); + + ulong numberOfBlocksToVerify = Image.GetImageBlockCount(); + ulong sizeOfHash = (ulong)TableOfHashes.LongLength / numberOfBlocksToVerify; + + for (int i = 0; i < TableOfHashes.Length; i += (int)sizeOfHash) + { + BlockHashes.Add(TableOfHashes[i..(i + (int)sizeOfHash)]); + } + } + + private uint GetImageHeaderPosition() + { + uint sizeOfBlock = SecurityHeader.ChunkSizeInKB * 1024; + uint ImageHeaderPosition = SecurityHeader.Size + SecurityHeader.CatalogSize + SecurityHeader.HashTableSize; + if (ImageHeaderPosition % sizeOfBlock > 0) + { + uint paddingSize = sizeOfBlock - (ImageHeaderPosition % sizeOfBlock); + ImageHeaderPosition += paddingSize; + } + return ImageHeaderPosition; + } + + public void VerifyFFU() + { + ulong numberOfBlocksToVerify = Image.GetImageBlockCount(); + + long oldPosition = Stream.Position; + + using BinaryReader binaryReader = new(Stream, Encoding.ASCII, true); + using BinaryReader hashTableBinaryReader = new(new MemoryStream(TableOfHashes)); + + for (ulong i = 0; i < numberOfBlocksToVerify; i++) + { + Console.Title = $"{i + 1}/{numberOfBlocksToVerify}"; + + if (SecurityHeader.AlgorithmId == 0x0000800c) // SHA256 Algorithm ID + { + byte[] block = Image.GetImageBlock(Stream, i); + byte[] hash = SHA256.HashData(block); + byte[] hashTableHash = BlockHashes.ElementAt((int)i); + + if (!StructuralComparisons.StructuralEqualityComparer.Equals(hash, hashTableHash)) + { + _ = Stream.Seek(oldPosition, SeekOrigin.Begin); + throw new InvalidDataException($"The FFU image contains a mismatched hash value at chunk {i}. " + + $"Expected: {BitConverter.ToString(hashTableHash).Replace("-", "")} " + + $"Computed: {BitConverter.ToString(hash).Replace("-", "")}"); + } + } + else + { + throw new InvalidDataException($"Unknown Hash algorithm id: {SecurityHeader.AlgorithmId}"); + } + } + + Console.Title = $"{numberOfBlocksToVerify}/{numberOfBlocksToVerify}"; + + _ = Stream.Seek(oldPosition, SeekOrigin.Begin); + } + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Data/Store.cs b/Img2Ffu/Reader/Data/Store.cs new file mode 100644 index 0000000..c1ff293 --- /dev/null +++ b/Img2Ffu/Reader/Data/Store.cs @@ -0,0 +1,95 @@ +using Img2Ffu.Reader.Enums; +using Img2Ffu.Reader.Structs; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Img2Ffu.Reader.Data +{ + public class Store + { + public StoreHeader StoreHeader; + public uint CompressionAlgorithm; + public StoreHeaderV2 StoreHeaderV2; + public string DevicePath = ""; // Device path has no NUL at then end (V2 only) + public readonly List ValidationDescriptor = []; + public readonly List WriteDescriptors = []; + public readonly List PlatformIDs = []; + public byte[] Padding = []; + public readonly FFUVersion FFUVersion; + + public Store(Stream stream) + { + StoreHeader = stream.ReadStructure(); + + int lastFinding = 0; + for (int i = 0; i < StoreHeader.PlatformId.Length; i++) + { + if (StoreHeader.PlatformId[i] == '\0') + { + if (i == lastFinding) + { + break; + } + + ASCIIEncoding asciiEnconding = new(); + string PlatformID = asciiEnconding.GetString(StoreHeader.PlatformId[lastFinding..i]); + PlatformIDs.Add(PlatformID); + lastFinding = i + 1; + } + } + + FFUVersion = GetFFUVersion(); + + switch (FFUVersion) + { + case FFUVersion.V2: + { + StoreHeaderV2 = stream.ReadStructure(); + byte[] stringBytes = new byte[StoreHeaderV2.DevicePathLength * 2]; + _ = stream.Read(stringBytes, 0, stringBytes.Length); + UnicodeEncoding unicodeEncoding = new(); + DevicePath = unicodeEncoding.GetString(stringBytes); + break; + } + case FFUVersion.V1_COMPRESSED: + { + using BinaryReader binaryReader = new(stream, Encoding.ASCII, true); + CompressionAlgorithm = binaryReader.ReadUInt32(); + break; + } + } + + for (uint i = 0; i < StoreHeader.ValidateDescriptorCount; i++) + { + ValidationDescriptor.Add(new ValidationDescriptor(stream)); + } + + for (uint i = 0; i < StoreHeader.WriteDescriptorCount; i++) + { + WriteDescriptors.Add(new WriteDescriptor(stream, FFUVersion)); + } + + long Position = stream.Position; + if (Position % StoreHeader.BlockSize > 0) + { + long paddingSize = StoreHeader.BlockSize - (Position % StoreHeader.BlockSize); + Padding = new byte[paddingSize]; + _ = stream.Read(Padding, 0, (int)paddingSize); + } + } + + public override string ToString() + { + return $"{{StoreHeader: {StoreHeader}, StoreHeaderV2: {StoreHeaderV2}, DevicePath: {DevicePath}}}"; + } + + private FFUVersion GetFFUVersion() => (StoreHeader.MajorVersion, StoreHeader.FullFlashMajorVersion) switch + { + (1, 2) => FFUVersion.V1, + (1, 3) => FFUVersion.V1_COMPRESSED, + (2, 2) => FFUVersion.V2, + _ => throw new InvalidDataException($"Unsupported FFU Store Format! MajorVersion: {StoreHeader.MajorVersion} FullFlashMajorVersion: {StoreHeader.FullFlashMajorVersion}"), + }; + } +} diff --git a/Img2Ffu/Reader/Data/ValidationDescriptor.cs b/Img2Ffu/Reader/Data/ValidationDescriptor.cs new file mode 100644 index 0000000..ef583d8 --- /dev/null +++ b/Img2Ffu/Reader/Data/ValidationDescriptor.cs @@ -0,0 +1,44 @@ +using Img2Ffu.Reader.Structs; +using System.Collections.Generic; +using System.IO; + +namespace Img2Ffu.Reader.Data +{ + public class ValidationDescriptor + { + public ValidationEntry ValidationEntry; + public byte[] ValidationBytes; + + public ValidationDescriptor(Stream stream) + { + ValidationEntry = stream.ReadStructure(); + + ValidationBytes = new byte[ValidationEntry.dwByteCount]; + _ = stream.Read(ValidationBytes, 0, (int)ValidationEntry.dwByteCount); + } + + public ValidationDescriptor(ValidationEntry validationEntry, byte[] validationBytes) + { + ValidationEntry = validationEntry; + ValidationBytes = validationBytes; + ValidationEntry.dwByteCount = (uint)validationBytes.LongLength; + } + + public override string ToString() + { + return $"{{ValidationEntry: {ValidationEntry}}}"; + } + + public byte[] GetBytes() + { + List bytes = []; + + ValidationEntry.dwByteCount = (uint)ValidationBytes.Length; + + bytes.AddRange(ValidationEntry.GetBytes()); + bytes.AddRange(ValidationBytes); + + return [.. bytes]; + } + } +} diff --git a/Img2Ffu/Reader/Data/WriteDescriptor.cs b/Img2Ffu/Reader/Data/WriteDescriptor.cs new file mode 100644 index 0000000..1f51ad0 --- /dev/null +++ b/Img2Ffu/Reader/Data/WriteDescriptor.cs @@ -0,0 +1,79 @@ +using Img2Ffu.Reader.Enums; +using Img2Ffu.Reader.Structs; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Img2Ffu.Reader.Data +{ + public class WriteDescriptor + { + public BlockDataEntry BlockDataEntry; + public uint DataSize; + + public readonly List DiskLocations = []; + private readonly bool HasDataSizeField; + + public WriteDescriptor(Stream stream, FFUVersion ffuVersion) + { + HasDataSizeField = ffuVersion == FFUVersion.V1_COMPRESSED; + + BlockDataEntry = stream.ReadStructure(); + + if (HasDataSizeField) + { + using BinaryReader binaryReader = new(stream, Encoding.ASCII, true); + DataSize = binaryReader.ReadUInt32(); + } + + for (uint i = 0; i < BlockDataEntry.LocationCount; i++) + { + DiskLocations.Add(stream.ReadStructure()); + } + } + + public WriteDescriptor(BlockDataEntry blockDataEntry, List diskLocations, uint compressedDataBlockSize) + { + HasDataSizeField = true; + + BlockDataEntry = blockDataEntry; + DiskLocations = diskLocations; + DataSize = compressedDataBlockSize; + } + + public WriteDescriptor(BlockDataEntry blockDataEntry, List diskLocations, FFUVersion ffuVersion) + { + HasDataSizeField = false; + + BlockDataEntry = blockDataEntry; + DiskLocations = diskLocations; + } + + public override string ToString() + { + return $"{{BlockDataEntry: {BlockDataEntry}}}"; + } + + public byte[] GetBytes() + { + List bytes = []; + + BlockDataEntry.LocationCount = (uint)DiskLocations.Count; + + bytes.AddRange(BlockDataEntry.GetBytes()); + + if (HasDataSizeField) + { + bytes.AddRange(BitConverter.GetBytes(DataSize)); + } + + foreach (DiskLocation diskLocation in DiskLocations) + { + bytes.AddRange(diskLocation.GetBytes()); + } + + return [.. bytes]; + } + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Enums/CompressionAlgorithm.cs b/Img2Ffu/Reader/Enums/CompressionAlgorithm.cs new file mode 100644 index 0000000..93423ac --- /dev/null +++ b/Img2Ffu/Reader/Enums/CompressionAlgorithm.cs @@ -0,0 +1,11 @@ +namespace Img2Ffu.Reader.Enums +{ + public enum CompressionAlgorithm + { + None = 0, + Default = 1, + LZNT1 = 2, + XPRESS = 3, + XPRESS_HUFF = 4 + } +} diff --git a/Img2Ffu/Reader/Enums/DiskAccessMethod.cs b/Img2Ffu/Reader/Enums/DiskAccessMethod.cs new file mode 100644 index 0000000..5fbf7c6 --- /dev/null +++ b/Img2Ffu/Reader/Enums/DiskAccessMethod.cs @@ -0,0 +1,8 @@ +namespace Img2Ffu.Reader.Enums +{ + public enum DiskAccessMethod + { + DISK_BEGIN = 0, + DISK_END = 2 + } +} diff --git a/Img2Ffu/Reader/Enums/FFUVersion.cs b/Img2Ffu/Reader/Enums/FFUVersion.cs new file mode 100644 index 0000000..6ce140b --- /dev/null +++ b/Img2Ffu/Reader/Enums/FFUVersion.cs @@ -0,0 +1,9 @@ +namespace Img2Ffu.Reader.Enums +{ + public enum FFUVersion + { + V1, + V1_COMPRESSED, + V2 + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/FullFlashUpdateReaderStream.cs b/Img2Ffu/Reader/FullFlashUpdateReaderStream.cs new file mode 100644 index 0000000..cd56034 --- /dev/null +++ b/Img2Ffu/Reader/FullFlashUpdateReaderStream.cs @@ -0,0 +1,315 @@ +using Img2Ffu.Reader.Data; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Img2Ffu.Reader +{ + public class FullFlashUpdateReaderStream : Stream + { + private Stream ffuFileStream; + private SignedImage signedImage; + private ImageFlash image; + private Store store; + private ulong storeIndex; + private long length; + private int sectorSize; + private int minSectorCount; + + private long currentPosition = 0; + + public int SectorSize => sectorSize; + public int MinSectorCount => minSectorCount; + + public string DevicePath => store.DevicePath; + + public FullFlashUpdateReaderStream(string FFUFilePath, ulong storeIndex) + { + ffuFileStream = File.OpenRead(FFUFilePath); + signedImage = new SignedImage(ffuFileStream); + image = signedImage.Image; + this.storeIndex = storeIndex; + store = image.Stores[(int)storeIndex]; + length = GetStoreLength(); + + try + { + (int minSectorCount, int sectorSize)[] manifestStoreInformation = ExtractImageManifestStoreInformation(signedImage); + + (minSectorCount, sectorSize) = manifestStoreInformation[storeIndex]; + + if (sectorSize == 0) + { + sectorSize = signedImage.ChunkSize; + } + + length = (long)((ulong)minSectorCount * (ulong)sectorSize); + } + catch { } + } + + public static int GetStoreCount(string FFUFilePath) + { + using FileStream ffuStream = File.Open(FFUFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); + SignedImage ffuFile = new(ffuStream); + return ffuFile.Image.Stores.Count; + } + + private static (int minSectorCount, int minSectorSize)[] ExtractImageManifestStoreInformation(SignedImage ffuFile) + { + List<(int minSectorCount, int minSectorSize)> manifestStoreInformation = []; + + int currentMinSectorCount = 0; + int currentSectorSize = 0; + bool isInStoreSection = false; + + foreach (string line in ffuFile.Image.ManifestData.Split("\n")) + { + if (line.StartsWith('[') && line.Contains(']')) + { + string sectionName = line.Split('[')[1].Split(']')[0]; + + if (isInStoreSection) + { + manifestStoreInformation.Add((currentMinSectorCount, currentSectorSize)); + } + + isInStoreSection = sectionName.Equals("Store", StringComparison.InvariantCultureIgnoreCase); + } + else if (isInStoreSection) + { + if (line.StartsWith("MinSectorCount", StringComparison.InvariantCultureIgnoreCase)) + { + bool success = int.TryParse(line.Split("=")[1].Trim(), out int tempCurrentMinSectorCount); + if (success) + { + currentMinSectorCount = tempCurrentMinSectorCount; + } + else + { + throw new InvalidDataException(line); + } + } + else if (line.StartsWith("SectorSize", StringComparison.InvariantCultureIgnoreCase)) + { + bool success = int.TryParse(line.Split("=")[1].Trim(), out int tempCurrentMinSectorSize); + if (success) + { + currentSectorSize = tempCurrentMinSectorSize; + } + else + { + throw new InvalidDataException(line); + } + } + } + } + + if (isInStoreSection) + { + manifestStoreInformation.Add((currentMinSectorCount, currentSectorSize)); + } + + return [.. manifestStoreInformation]; + } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + public override long Length => length; + + private long GetStoreLength() + { + long blockMaxStart = 0; + long blockMaxEnd = 0; + + foreach (WriteDescriptor writeDescriptor in store.WriteDescriptors) + { + foreach (Structs.DiskLocation diskLocation in writeDescriptor.DiskLocations) + { + switch (diskLocation.DiskAccessMethod) + { + case 0: + { + if (diskLocation.BlockIndex > blockMaxStart) + { + blockMaxStart = diskLocation.BlockIndex; + } + break; + } + case 2: + { + if (diskLocation.BlockIndex > blockMaxEnd) + { + blockMaxEnd = diskLocation.BlockIndex; + } + break; + } + } + } + } + + long totalBlocks = blockMaxStart + blockMaxEnd + 2; + + return totalBlocks * store.StoreHeader.BlockSize; + } + + private long GetBlockDataIndex(long realBlockOffset) + { + // We do not want to immediately return because FFU files may first blank out sectors and then write post mortem + long matchedIndex = -1; // Invalid + + for (int i = 0; i < store.WriteDescriptors.Count; i++) + { + WriteDescriptor writeDescriptor = store.WriteDescriptors[i]; + foreach (Structs.DiskLocation diskLocation in writeDescriptor.DiskLocations) + { + switch (diskLocation.DiskAccessMethod) + { + case 0: + { + long blockOffset = diskLocation.BlockIndex; + if (blockOffset == realBlockOffset) + { + matchedIndex = i; + } + break; + } + case 2: + { + long blockOffset = (Length / store.StoreHeader.BlockSize) - 1 - diskLocation.BlockIndex; + if (blockOffset == realBlockOffset) + { + matchedIndex = i; + } + break; + } + } + } + } + + return matchedIndex; + } + + public override long Position + { + get => currentPosition; + set => currentPosition = value; + } + + public override void Flush() + { + // Nothing to do here + } + + public override int Read(byte[] buffer, int offset, int count) + { + int readBytes = count; + + if (Position + readBytes > Length) + { + readBytes = (int)(Length - Position); + } + + byte[] readBuffer = new byte[readBytes]; + Array.Fill(readBuffer, 0); + + // Read the buffer from the FFU file. + // First we have to figure out where do we land here. + + long overflowBlockStartByteCount = Position % store.StoreHeader.BlockSize; + long overflowBlockEndByteCount = (Position + readBytes) % store.StoreHeader.BlockSize; + + long totalBlockCount = Length / store.StoreHeader.BlockSize; + + long startBlockIndex = (Position - overflowBlockStartByteCount) / store.StoreHeader.BlockSize; + long endBlockIndex = (Position + readBytes + (store.StoreHeader.BlockSize - overflowBlockEndByteCount)) / store.StoreHeader.BlockSize; + + byte[] allReadBlocks = new byte[(endBlockIndex - startBlockIndex + 1) * store.StoreHeader.BlockSize]; + + for (long currentBlock = startBlockIndex; currentBlock < endBlockIndex; currentBlock++) + { + long virtualBlockIndex = GetBlockDataIndex(currentBlock); + if (virtualBlockIndex != -1) + { + byte[] block = image.GetStoreDataBlock(ffuFileStream, storeIndex, (ulong)virtualBlockIndex); + Array.Copy(block, 0, allReadBlocks, (int)((currentBlock - startBlockIndex) * store.StoreHeader.BlockSize), store.StoreHeader.BlockSize); + } + } + + Array.Copy(allReadBlocks, overflowBlockStartByteCount, buffer, offset, readBytes); + + Position += readBytes; + return readBytes; + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (offset > Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + switch (origin) + { + case SeekOrigin.Begin: + { + if (offset < 0) + { + throw new IOException(); + } + + Position = offset; + break; + } + case SeekOrigin.Current: + { + int tempPosition = (int)Position + (int)offset; + if (tempPosition < 0) + { + throw new IOException(); + } + + Position = tempPosition; + break; + } + case SeekOrigin.End: + { + int tempPosition = (int)Length + (int)offset; + if (tempPosition < 0) + { + throw new IOException(); + } + + Position = tempPosition; + break; + } + default: + { + throw new ArgumentException(nameof(origin)); + } + } + + return Position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + ffuFileStream.Dispose(); + } + } +} diff --git a/Img2Ffu/Reader/StructExtensions.cs b/Img2Ffu/Reader/StructExtensions.cs new file mode 100644 index 0000000..dfebd5e --- /dev/null +++ b/Img2Ffu/Reader/StructExtensions.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader +{ + public static class StructExtensions + { + public static T GetStruct(this byte[] bytes) where T : struct + { + int requiredSize = Marshal.SizeOf(typeof(T)); + if (bytes.Length < requiredSize) + { + throw new Exception($"Invalid buffer size for passed in structure. Expected: {requiredSize} Provided: {bytes.Length}"); + } + + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + T structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + handle.Free(); + return structure; + } + + public static byte[] GetBytes(this T structure) where T : struct + { + byte[] bytes = new byte[Marshal.SizeOf(typeof(T))]; + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + Marshal.StructureToPtr(structure, handle.AddrOfPinnedObject(), false); + handle.Free(); + return bytes; + } + + public static T Read(this BinaryReader reader) where T : struct + { + byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); + return bytes.GetStruct(); + } + + public static void Write(this BinaryWriter writer, T structure) where T : struct + { + byte[] bytes = structure.GetBytes(); + writer.Write(bytes); + } + + public static T ReadStructure(this Stream stream) where T : struct + { + using BinaryReader binaryReader = new(stream, System.Text.Encoding.ASCII, true); + return binaryReader.Read(); + } + + public static void WriteStructure(this Stream stream, T structure) where T : struct + { + using BinaryWriter binaryWriter = new(stream, System.Text.Encoding.ASCII, true); + binaryWriter.Write(structure); + } + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Structs/BlockDataEntry.cs b/Img2Ffu/Reader/Structs/BlockDataEntry.cs new file mode 100644 index 0000000..ae79ca5 --- /dev/null +++ b/Img2Ffu/Reader/Structs/BlockDataEntry.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct BlockDataEntry + { + public uint LocationCount; + public uint BlockCount; + + public override readonly string ToString() + { + return $"{{LocationCount: {LocationCount}, BlockCount: {BlockCount}}}"; + } + } +} diff --git a/Img2Ffu/Reader/Structs/DiskLocation.cs b/Img2Ffu/Reader/Structs/DiskLocation.cs new file mode 100644 index 0000000..958ae11 --- /dev/null +++ b/Img2Ffu/Reader/Structs/DiskLocation.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct DiskLocation + { + public uint DiskAccessMethod; + public uint BlockIndex; + + public override readonly string ToString() + { + return $"{{DiskAccessMethod: {DiskAccessMethod}, BlockIndex: {BlockIndex}}}"; + } + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Structs/ImageHeader.cs b/Img2Ffu/Reader/Structs/ImageHeader.cs new file mode 100644 index 0000000..7f9dc26 --- /dev/null +++ b/Img2Ffu/Reader/Structs/ImageHeader.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct ImageHeader + { + public uint Size; // sizeof(ImageHeader) + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] + public string Signature; // "ImageFlash " + public uint ManifestLength; // in bytes + public uint ChunkSize; // Used only during image generation. + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Structs/SecurityHeader.cs b/Img2Ffu/Reader/Structs/SecurityHeader.cs new file mode 100644 index 0000000..c15572c --- /dev/null +++ b/Img2Ffu/Reader/Structs/SecurityHeader.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct SecurityHeader + { + public uint Size; // size of struct, overall + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] + public string Signature; // "SignedImage " + public uint ChunkSizeInKB; // size of a hashed chunk within the image + public uint AlgorithmId; // algorithm used to hash + public uint CatalogSize; // size of catalog to validate + public uint HashTableSize; // size of hash table + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Structs/StoreHeader.cs b/Img2Ffu/Reader/Structs/StoreHeader.cs new file mode 100644 index 0000000..66b8c3e --- /dev/null +++ b/Img2Ffu/Reader/Structs/StoreHeader.cs @@ -0,0 +1,30 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct StoreHeader + { + public uint UpdateType; // indicates partial or full flash + public ushort MajorVersion, MinorVersion; // used to validate struct + public ushort FullFlashMajorVersion, FullFlashMinorVersion; // FFU version, i.e. the image format + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)] + public byte[] PlatformId; // string which indicates what device this FFU is intended to be written to + public uint BlockSize; // size of an image block in bytes – the device’s actual sector size may differ + public uint WriteDescriptorCount; // number of write descriptors to iterate through + public uint WriteDescriptorLength; // total size of all the write descriptors, in bytes (included so they can be read out up front and interpreted later) + public uint ValidateDescriptorCount; // number of validation descriptors to check + public uint ValidateDescriptorLength; // total size of all the validation descriptors, in bytes + public uint InitialTableIndex; // block index in the payload of the initial (invalid) GPT + public uint InitialTableCount; // count of blocks for the initial GPT, i.e. the GPT spans blockArray[idx..(idx + count -1)] + public uint FlashOnlyTableIndex; // first block index in the payload of the flash-only GPT (included so safe flashing can be accomplished) + public uint FlashOnlyTableCount; // count of blocks in the flash-only GPT + public uint FinalTableIndex; // index in the table of the real GPT + public uint FinalTableCount; // number of blocks in the real GPT + + public override readonly string ToString() + { + return $"{{UpdateType: {UpdateType}, MajorVersion: {MajorVersion}, MinorVersion: {MinorVersion}}}"; + } + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Structs/StoreHeaderV2.cs b/Img2Ffu/Reader/Structs/StoreHeaderV2.cs new file mode 100644 index 0000000..15fe042 --- /dev/null +++ b/Img2Ffu/Reader/Structs/StoreHeaderV2.cs @@ -0,0 +1,18 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct StoreHeaderV2 + { + public ushort NumberOfStores; // Total number of stores (V2 only) + public ushort StoreIndex; // Current store index, 1-based (V2 only) + public ulong StorePayloadSize; // Payload data only, excludes padding (V2 only) + public ushort DevicePathLength; // Length of the device path (V2 only) + + public override readonly string ToString() + { + return $"{{NumberOfStores: {NumberOfStores}, StoreIndex: {StoreIndex}, StorePayloadSize: {StorePayloadSize}, DevicePathLength: {DevicePathLength}}}"; + } + } +} \ No newline at end of file diff --git a/Img2Ffu/Reader/Structs/ValidationEntry.cs b/Img2Ffu/Reader/Structs/ValidationEntry.cs new file mode 100644 index 0000000..ab67289 --- /dev/null +++ b/Img2Ffu/Reader/Structs/ValidationEntry.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Img2Ffu.Reader.Structs +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ValidationEntry + { + public uint dwSectorIndex; + public uint dwSectorOffset; + public uint dwByteCount; + } +} \ No newline at end of file