| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | namespace WixToolset.Core.Burn.Bundles |
| 4 | { |
| 5 | using System; |
| 6 | using System.Collections.Generic; |
| 7 | using System.Diagnostics; |
| 8 | using System.IO; |
| 9 | using WixToolset.Data; |
| 10 | using WixToolset.Extensibility.Services; |
| 11 | |
| 12 | /// <summary> |
| 13 | /// Common functionality for Burn PE Writer & Reader for the WiX toolset. |
| 14 | /// </summary> |
| 15 | /// <remarks>This class encapsulates common functionality related to |
| 16 | /// bundled/chained setup packages.</remarks> |
| 17 | /// <example> |
| 18 | /// </example> |
| 19 | internal abstract class BurnCommon : IDisposable |
| 20 | { |
| 21 | public const string BurnV3Namespace = "http://schemas.microsoft.com/wix/2008/Burn"; |
| 22 | public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn"; |
| 23 | public const string BurnUXContainerEmbeddedIdFormat = "u{0}"; |
| 24 | public const string BurnAuthoredContainerEmbeddedIdFormat = "a{0}"; |
| 25 | |
| 26 | public const string BADataFileName = "BootstrapperApplicationData.xml"; |
| 27 | |
| 28 | public const string BootstrapperExtensionDataFileName = "BootstrapperExtensionData.xml"; |
| 29 | |
| 30 | // See WinNT.h for details about the PE format, including the |
| 31 | // structure and offsets for IMAGE_DOS_HEADER, IMAGE_NT_HEADERS32, |
| 32 | // IMAGE_FILE_HEADER, etc. |
| 33 | protected const uint IMAGE_DOS_HEADER_SIZE = 64; |
| 34 | protected const uint IMAGE_DOS_HEADER_OFFSET_MAGIC = 0; |
| 35 | protected const uint IMAGE_DOS_HEADER_OFFSET_NTHEADER = 60; |
| 36 | |
| 37 | protected const uint IMAGE_NT_HEADER_SIZE = 24; // signature DWORD (4) + IMAGE_FILE_HEADER (20) |
| 38 | protected const uint IMAGE_NT_HEADER_OFFSET_SIGNATURE = 0; |
| 39 | protected const uint IMAGE_NT_HEADER_OFFSET_MACHINE = 4; |
| 40 | protected const uint IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6; |
| 41 | protected const uint IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20; |
| 42 | |
| 43 | protected const uint IMAGE_OPTIONAL_OFFSET_CHECKSUM = 4 * 16; // checksum is 16 DWORDs into IMAGE_OPTIONAL_HEADER which is right after the IMAGE_NT_HEADER. |
| 44 | protected const uint IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE = (IMAGE_DATA_DIRECTORY_SIZE * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); |
| 45 | |
| 46 | protected const uint IMAGE_SECTION_HEADER_SIZE = 40; |
| 47 | protected const uint IMAGE_SECTION_HEADER_OFFSET_NAME = 0; |
| 48 | protected const uint IMAGE_SECTION_HEADER_OFFSET_VIRTUALSIZE = 8; |
| 49 | protected const uint IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA = 16; |
| 50 | protected const uint IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA = 20; |
| 51 | |
| 52 | protected const uint IMAGE_DATA_DIRECTORY_SIZE = 8; // struct of two DWORDs. |
| 53 | protected const uint IMAGE_DIRECTORY_ENTRY_SECURITY = 4; |
| 54 | protected const uint IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; |
| 55 | |
| 56 | protected const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; |
| 57 | protected const uint IMAGE_NT_SIGNATURE = 0x00004550; |
| 58 | protected const ulong IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword. |
| 59 | |
| 60 | public const ushort IMAGE_FILE_MACHINE_AM33 = 0x1D3; |
| 61 | public const ushort IMAGE_FILE_MACHINE_AMD64 = 0x8664; |
| 62 | public const ushort IMAGE_FILE_MACHINE_ARM = 0x1C0; |
| 63 | public const ushort IMAGE_FILE_MACHINE_ARM64 = 0xAA64; |
| 64 | public const ushort IMAGE_FILE_MACHINE_ARMNT = 0x1C4; |
| 65 | public const ushort IMAGE_FILE_MACHINE_EBC = 0xEBC; |
| 66 | public const ushort IMAGE_FILE_MACHINE_I386 = 0x14C; |
| 67 | public const ushort IMAGE_FILE_MACHINE_IA64 = 0x200; |
| 68 | public const ushort IMAGE_FILE_MACHINE_LOONGARCH32 = 0x6232; |
| 69 | public const ushort IMAGE_FILE_MACHINE_LOONGARCH64 = 0x6264; |
| 70 | public const ushort IMAGE_FILE_MACHINE_M32R = 0x9041; |
| 71 | public const ushort IMAGE_FILE_MACHINE_MIPS16 = 0x266; |
| 72 | public const ushort IMAGE_FILE_MACHINE_MIPSFPU = 0x366; |
| 73 | public const ushort IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466; |
| 74 | public const ushort IMAGE_FILE_MACHINE_POWERPC = 0x1F0; |
| 75 | public const ushort IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1; |
| 76 | public const ushort IMAGE_FILE_MACHINE_R4000 = 0x166; |
| 77 | public const ushort IMAGE_FILE_MACHINE_RISCV32 = 0x5032; |
| 78 | public const ushort IMAGE_FILE_MACHINE_RISCV64 = 0x5064; |
| 79 | public const ushort IMAGE_FILE_MACHINE_RISCV128 = 0x5128; |
| 80 | public const ushort IMAGE_FILE_MACHINE_SH3 = 0x1A2; |
| 81 | public const ushort IMAGE_FILE_MACHINE_SH3DSP = 0x1A3; |
| 82 | public const ushort IMAGE_FILE_MACHINE_SH4 = 0x1A6; |
| 83 | public const ushort IMAGE_FILE_MACHINE_SH5 = 0x1A8; |
| 84 | public const ushort IMAGE_FILE_MACHINE_THUMB = 0x1C2; |
| 85 | public const ushort IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169; |
| 86 | |
| 87 | // The ".wixburn" section contains: |
| 88 | // 0- 3: magic number |
| 89 | // 4- 7: version |
| 90 | // 8-23: bundle GUID |
| 91 | // 24-27: engine (stub) size |
| 92 | // 28-31: original checksum |
| 93 | // 32-35: original signature offset |
| 94 | // 36-39: original signature size |
| 95 | // 40-43: container type (1 = CAB) |
| 96 | // 44-47: container count |
| 97 | // 48-51: byte count of manifest + UX container |
| 98 | // 52-512: byte count of attached containers (4 bytes for each container) |
| 99 | protected const uint BURN_SECTION_OFFSET_MAGIC = 0; |
| 100 | protected const uint BURN_SECTION_OFFSET_VERSION = 4; |
| 101 | protected const uint BURN_SECTION_OFFSET_BUNDLEGUID = 8; |
| 102 | protected const uint BURN_SECTION_OFFSET_STUBSIZE = 24; |
| 103 | protected const uint BURN_SECTION_OFFSET_ORIGINALCHECKSUM = 28; |
| 104 | protected const uint BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET = 32; |
| 105 | protected const uint BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE = 36; |
| 106 | protected const uint BURN_SECTION_OFFSET_FORMAT = 40; |
| 107 | protected const uint BURN_SECTION_OFFSET_COUNT = 44; |
| 108 | protected const uint BURN_SECTION_OFFSET_UXSIZE = 48; |
| 109 | protected const uint BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 = 52; |
| 110 | protected const uint BURN_SECTION_MIN_SIZE = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0; |
| 111 | |
| 112 | // Keep in sync with burn\engine\inc\engine.h |
| 113 | protected const uint BURN_SECTION_MAGIC = 0x00f14300; |
| 114 | protected const uint BURN_SECTION_VERSION = 0x00000002; |
| 115 | public const uint BURN_PROTOCOL_VERSION = 0x00000001; |
| 116 | |
| 117 | protected string fileExe; |
| 118 | protected uint peOffset = UInt32.MaxValue; |
| 119 | protected ushort sections = UInt16.MaxValue; |
| 120 | protected uint firstSectionOffset = UInt32.MaxValue; |
| 121 | protected uint checksumOffset; |
| 122 | protected uint certificateTableSignatureOffset; |
| 123 | protected uint certificateTableSignatureSize; |
| 124 | protected uint wixburnDataOffset = UInt32.MaxValue; |
| 125 | protected uint wixburnRawDataSize; |
| 126 | protected uint wixburnMaxContainers; |
| 127 | |
| 128 | // TODO: does this enum exist in another form somewhere? |
| 129 | /// <summary> |
| 130 | /// The types of attached containers that BurnWriter supports. |
| 131 | /// </summary> |
| 132 | public enum Container |
| 133 | { |
| 134 | Nothing = 0, |
| 135 | UX, |
| 136 | Attached, |
| 137 | } |
| 138 | |
| 139 | /// <summary> |
| 140 | /// Creates a BurnCommon for re-writing a PE file. |
| 141 | /// </summary> |
| 142 | /// <param name="messaging"></param> |
| 143 | /// <param name="fileExe">File to modify in-place.</param> |
| 144 | public BurnCommon(IMessaging messaging, string fileExe) |
| 145 | { |
| 146 | this.Messaging = messaging; |
| 147 | this.fileExe = fileExe; |
| 148 | this.AttachedContainers = new List<ContainerSlot>(); |
| 149 | } |
| 150 | |
| 151 | public bool Invalid { get; protected set; } |
| 152 | public ushort MachineType { get; private set; } |
| 153 | public uint Checksum { get; protected set; } |
| 154 | public uint SignatureOffset { get; protected set; } |
| 155 | public uint SignatureSize { get; protected set; } |
| 156 | public uint Version { get; protected set; } |
| 157 | public Guid BundleId { get; protected set; } |
| 158 | public uint StubSize { get; protected set; } |
| 159 | public uint OriginalChecksum { get; protected set; } |
| 160 | public uint OriginalSignatureOffset { get; protected set; } |
| 161 | public uint OriginalSignatureSize { get; protected set; } |
| 162 | public uint EngineSize { get; protected set; } |
| 163 | public uint UXAddress { get { return this.StubSize; } } |
| 164 | public List<ContainerSlot> AttachedContainers { get; protected set; } |
| 165 | |
| 166 | protected IMessaging Messaging { get; } |
| 167 | |
| 168 | public void Dispose() |
| 169 | { |
| 170 | this.Dispose(true); |
| 171 | |
| 172 | GC.SuppressFinalize(this); |
| 173 | } |
| 174 | |
| 175 | /// <summary> |
| 176 | /// Copies one stream to another. |
| 177 | /// </summary> |
| 178 | /// <param name="input">Input stream.</param> |
| 179 | /// <param name="output">Output stream.</param> |
| 180 | /// <param name="size">Optional count of bytes to copy. 0 indicates whole input stream from current should be copied.</param> |
| 181 | protected static int CopyStream(Stream input, Stream output, int size) |
| 182 | { |
| 183 | var bytes = new byte[4096]; |
| 184 | var total = 0; |
| 185 | do |
| 186 | { |
| 187 | var read = Math.Min(bytes.Length, size - total); |
| 188 | read = input.Read(bytes, 0, read); |
| 189 | if (0 == read) |
| 190 | { |
| 191 | break; |
| 192 | } |
| 193 | |
| 194 | output.Write(bytes, 0, read); |
| 195 | total += read; |
| 196 | } while (0 == size || total < size); |
| 197 | |
| 198 | return total; |
| 199 | } |
| 200 | |
| 201 | /// <summary> |
| 202 | /// Initialize the common information about a Burn engine. |
| 203 | /// </summary> |
| 204 | /// <param name="reader">Binary reader open against a Burn engine.</param> |
| 205 | protected void Initialize(BinaryReader reader) |
| 206 | { |
| 207 | this.Invalid = !this.TryInitialize(reader); |
| 208 | } |
| 209 | |
| 210 | private bool TryInitialize(BinaryReader reader) |
| 211 | { |
| 212 | if (!this.GetWixburnSectionInfo(reader)) |
| 213 | { |
| 214 | return false; |
| 215 | } |
| 216 | |
| 217 | reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin); |
| 218 | var bytes = reader.ReadBytes((int)this.wixburnRawDataSize); |
| 219 | |
| 220 | var uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); |
| 221 | if (BURN_SECTION_MAGIC != uint32) |
| 222 | { |
| 223 | this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); |
| 224 | return false; |
| 225 | } |
| 226 | |
| 227 | this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION); |
| 228 | if (BURN_SECTION_VERSION != this.Version) |
| 229 | { |
| 230 | this.Messaging.Write(BurnBackendErrors.IncompatibleWixBurnSection(this.fileExe, this.Version)); |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_FORMAT); // We only know how to deal with CABs right now |
| 235 | if (1 != uint32) |
| 236 | { |
| 237 | this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); |
| 238 | return false; |
| 239 | } |
| 240 | |
| 241 | this.BundleId = BurnCommon.ReadGuid(bytes, BURN_SECTION_OFFSET_BUNDLEGUID); |
| 242 | this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE); |
| 243 | this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM); |
| 244 | this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); |
| 245 | this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE); |
| 246 | |
| 247 | var containerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT); |
| 248 | uint uxSize = 0; |
| 249 | if (this.wixburnMaxContainers < containerCount) |
| 250 | { |
| 251 | this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); |
| 252 | return false; |
| 253 | } |
| 254 | else if (containerCount > 0) |
| 255 | { |
| 256 | this.AttachedContainers.Clear(); |
| 257 | for (uint i = 0; i < containerCount; ++i) |
| 258 | { |
| 259 | uint sizeOffset = BURN_SECTION_OFFSET_UXSIZE + (i * 4); |
| 260 | var size = BurnCommon.ReadUInt32(bytes, sizeOffset); |
| 261 | this.AttachedContainers.Add(new ContainerSlot(size)); |
| 262 | } |
| 263 | uxSize = this.AttachedContainers[0].Size; |
| 264 | } |
| 265 | |
| 266 | // If there is an original signature use that to determine the engine size. |
| 267 | if (0 < this.OriginalSignatureOffset) |
| 268 | { |
| 269 | this.EngineSize = this.OriginalSignatureOffset + this.OriginalSignatureSize; |
| 270 | } |
| 271 | else if (0 < this.SignatureOffset && 2 > containerCount) // if there is a signature and no attached containers, use the current signature. |
| 272 | { |
| 273 | this.EngineSize = this.SignatureOffset + this.SignatureSize; |
| 274 | } |
| 275 | else // just use the stub and UX container as the size of the engine. |
| 276 | { |
| 277 | this.EngineSize = this.UXAddress + uxSize; |
| 278 | } |
| 279 | |
| 280 | return true; |
| 281 | } |
| 282 | |
| 283 | protected virtual void Dispose(bool disposing) |
| 284 | { |
| 285 | } |
| 286 | |
| 287 | /// <summary> |
| 288 | /// Finds the ".wixburn" section in the current exe. |
| 289 | /// </summary> |
| 290 | /// <returns>true if the ".wixburn" section is successfully found; false otherwise</returns> |
| 291 | private bool GetWixburnSectionInfo(BinaryReader reader) |
| 292 | { |
| 293 | if (UInt32.MaxValue == this.wixburnDataOffset) |
| 294 | { |
| 295 | if (!this.EnsureNTHeader(reader)) |
| 296 | { |
| 297 | return false; |
| 298 | } |
| 299 | |
| 300 | var wixburnSectionOffset = UInt32.MaxValue; |
| 301 | var bytes = new byte[IMAGE_SECTION_HEADER_SIZE]; |
| 302 | |
| 303 | reader.BaseStream.Seek(this.firstSectionOffset, SeekOrigin.Begin); |
| 304 | for (ushort sectionIndex = 0; sectionIndex < this.sections; ++sectionIndex) |
| 305 | { |
| 306 | reader.Read(bytes, 0, bytes.Length); |
| 307 | |
| 308 | if (IMAGE_SECTION_WIXBURN_NAME == BurnCommon.ReadUInt64(bytes, IMAGE_SECTION_HEADER_OFFSET_NAME)) |
| 309 | { |
| 310 | wixburnSectionOffset = this.firstSectionOffset + (IMAGE_SECTION_HEADER_SIZE * sectionIndex); |
| 311 | break; |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | if (UInt32.MaxValue == wixburnSectionOffset) |
| 316 | { |
| 317 | this.Messaging.Write(ErrorMessages.StubMissingWixburnSection(this.fileExe)); |
| 318 | return false; |
| 319 | } |
| 320 | |
| 321 | this.wixburnRawDataSize = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA); |
| 322 | |
| 323 | // we need 52 bytes for the manifest header, which is always going to fit in |
| 324 | // the smallest alignment (512 bytes), but just to be paranoid... |
| 325 | if (BURN_SECTION_MIN_SIZE > this.wixburnRawDataSize) |
| 326 | { |
| 327 | this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe)); |
| 328 | return false; |
| 329 | } |
| 330 | |
| 331 | this.wixburnMaxContainers = (this.wixburnRawDataSize - BURN_SECTION_OFFSET_UXSIZE) / sizeof(uint); |
| 332 | this.wixburnDataOffset = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA); |
| 333 | } |
| 334 | |
| 335 | return true; |
| 336 | } |
| 337 | |
| 338 | /// <summary> |
| 339 | /// Checks for a valid Windows PE signature (IMAGE_NT_SIGNATURE) in the current exe. |
| 340 | /// </summary> |
| 341 | /// <returns>true if the exe is a Windows executable; false otherwise</returns> |
| 342 | private bool EnsureNTHeader(BinaryReader reader) |
| 343 | { |
| 344 | if (UInt32.MaxValue == this.firstSectionOffset) |
| 345 | { |
| 346 | if (!this.EnsureDosHeader(reader)) |
| 347 | { |
| 348 | return false; |
| 349 | } |
| 350 | |
| 351 | reader.BaseStream.Seek(this.peOffset, SeekOrigin.Begin); |
| 352 | var bytes = reader.ReadBytes((int)IMAGE_NT_HEADER_SIZE); |
| 353 | |
| 354 | // Verify the NT signature... |
| 355 | if (IMAGE_NT_SIGNATURE != BurnCommon.ReadUInt32(bytes, IMAGE_NT_HEADER_OFFSET_SIGNATURE)) |
| 356 | { |
| 357 | this.Messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe)); |
| 358 | return false; |
| 359 | } |
| 360 | |
| 361 | this.MachineType = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_MACHINE); |
| 362 | |
| 363 | var sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER); |
| 364 | |
| 365 | this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS); |
| 366 | this.firstSectionOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader; |
| 367 | |
| 368 | this.checksumOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + IMAGE_OPTIONAL_OFFSET_CHECKSUM; |
| 369 | this.certificateTableSignatureOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE; |
| 370 | this.certificateTableSignatureSize = this.certificateTableSignatureOffset + 4; // size is in the DWORD after the offset. |
| 371 | |
| 372 | bytes = reader.ReadBytes(sizeOptionalHeader); |
| 373 | this.Checksum = BurnCommon.ReadUInt32(bytes, IMAGE_OPTIONAL_OFFSET_CHECKSUM); |
| 374 | this.SignatureOffset = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE); |
| 375 | this.SignatureSize = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE + 4); |
| 376 | } |
| 377 | |
| 378 | return true; |
| 379 | } |
| 380 | |
| 381 | /// <summary> |
| 382 | /// Checks for a valid DOS header in the current exe. |
| 383 | /// </summary> |
| 384 | /// <returns>true if the exe starts with a DOS stub; false otherwise</returns> |
| 385 | private bool EnsureDosHeader(BinaryReader reader) |
| 386 | { |
| 387 | if (UInt32.MaxValue == this.peOffset) |
| 388 | { |
| 389 | var bytes = reader.ReadBytes((int)IMAGE_DOS_HEADER_SIZE); |
| 390 | |
| 391 | // Verify the DOS 'MZ' signature. |
| 392 | if (IMAGE_DOS_SIGNATURE != BurnCommon.ReadUInt16(bytes, IMAGE_DOS_HEADER_OFFSET_MAGIC)) |
| 393 | { |
| 394 | this.Messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe)); |
| 395 | return false; |
| 396 | } |
| 397 | |
| 398 | this.peOffset = BurnCommon.ReadUInt32(bytes, IMAGE_DOS_HEADER_OFFSET_NTHEADER); |
| 399 | } |
| 400 | |
| 401 | return true; |
| 402 | } |
| 403 | |
| 404 | internal static Guid ReadGuid(byte[] bytes, uint offset) |
| 405 | { |
| 406 | var guidBytes = new byte[16]; |
| 407 | for (var i = 0; i < 16; ++i) |
| 408 | { |
| 409 | guidBytes[i] = bytes[offset + i]; |
| 410 | } |
| 411 | |
| 412 | return new Guid(guidBytes); |
| 413 | } |
| 414 | |
| 415 | /// <summary> |
| 416 | /// Reads a UInt16 value in little-endian format from an offset in an array of bytes. |
| 417 | /// </summary> |
| 418 | /// <param name="bytes">Array from which to read.</param> |
| 419 | /// <param name="offset">Beginning offset from which to read.</param> |
| 420 | /// <returns>value at offset</returns> |
| 421 | internal static ushort ReadUInt16(byte[] bytes, uint offset) |
| 422 | { |
| 423 | Debug.Assert(offset + 2 <= bytes.Length); |
| 424 | return (ushort)(bytes[offset] + (bytes[offset + 1] << 8)); |
| 425 | } |
| 426 | |
| 427 | /// <summary> |
| 428 | /// Reads a UInt32 value in little-endian format from an offset in an array of bytes. |
| 429 | /// </summary> |
| 430 | /// <param name="bytes">Array from which to read.</param> |
| 431 | /// <param name="offset">Beginning offset from which to read.</param> |
| 432 | /// <returns>value at offset</returns> |
| 433 | internal static uint ReadUInt32(byte[] bytes, uint offset) |
| 434 | { |
| 435 | Debug.Assert(offset + 4 <= bytes.Length); |
| 436 | return BurnCommon.ReadUInt16(bytes, offset) + ((uint)BurnCommon.ReadUInt16(bytes, offset + 2) << 16); |
| 437 | } |
| 438 | |
| 439 | /// <summary> |
| 440 | /// Reads a UInt64 value in little-endian format from an offset in an array of bytes. |
| 441 | /// </summary> |
| 442 | /// <param name="bytes">Array from which to read.</param> |
| 443 | /// <param name="offset">Beginning offset from which to read.</param> |
| 444 | /// <returns>value at offset</returns> |
| 445 | internal static ulong ReadUInt64(byte[] bytes, uint offset) |
| 446 | { |
| 447 | Debug.Assert(offset + 8 <= bytes.Length); |
| 448 | return BurnCommon.ReadUInt32(bytes, offset) + ((ulong)BurnCommon.ReadUInt32(bytes, offset + 4) << 32); |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | internal struct ContainerSlot |
| 453 | { |
| 454 | public ContainerSlot(uint size) |
| 455 | { |
| 456 | this.Size = size; |
| 457 | } |
| 458 | |
| 459 | public uint Size { get; set; } |
| 460 | } |
| 461 | } |