diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 206ae6f71..8b4ec41c7 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -1,9 +1,7 @@ name: NuGet push (tag) -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' +on: + workflow_dispatch: env: DOTNET_CLI_TELEMETRY_OPTOUT: true @@ -11,22 +9,20 @@ env: jobs: build: - runs-on: windows-2019 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 + - name: Setup .NET Core + uses: actions/setup-dotnet@v5 with: - fetch-depth: 50 - lfs: 'true' - # We do not need to fetch tags, as we're already at a tagged build - it should be available automatically + dotnet-version: 10.0.x - - name: Setup .NET Core 7.0 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '7.0.x' + - name: Build + run: dotnet build -c Release - name: Pack - run: dotnet pack -c Release -o ${{ github.workspace }}/build + run: dotnet pack -c Release -o ${{ github.workspace }}/build --no-build - name: NuGet push run: dotnet nuget push *.nupkg --skip-duplicate -k ${{secrets.NUGET_KEY}} -s https://api.nuget.org/v3/index.json diff --git a/.gitignore b/.gitignore index b207fc71c..7d87a18bc 100644 --- a/.gitignore +++ b/.gitignore @@ -386,3 +386,19 @@ artifacts-dotnet-releaser/ # Verify *.received.* *# + +# Build outputs in Library folder +Library/net*/ +Library/netstandard*/ +net10.0/ + +# Build outputs in root +netstandard*/ + +# Build outputs in Tests folder +Tests/net*/ +Tests/netstandard*/ + +# Build outputs in Utilities folder +Utilities/net*/ +Utilities/netstandard*/ diff --git a/Directory.Build.props b/Directory.Build.props index dd0d89387..6429d7843 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,18 +6,19 @@ portable - https://github.com/LTRData/DiscUtils + https://github.com/Devedse/DiscUtils MIT git LTR Data Kenneth Bell;LordMike;Olof Lagerkvist ..\$(Configuration) - 1.0.72 + 1.0.102 true CS1591;CS0649 + false diff --git a/DiscUtils.slnx b/DiscUtils.slnx index 29103d33c..af19f17d9 100644 --- a/DiscUtils.slnx +++ b/DiscUtils.slnx @@ -48,6 +48,7 @@ + diff --git a/Library/Directory.Build.props b/Library/Directory.Build.props index 15793114e..3c0fe7c12 100644 --- a/Library/Directory.Build.props +++ b/Library/Directory.Build.props @@ -5,7 +5,7 @@ netstandard2.0;netstandard2.1;net46;net48;net8.0;net9.0;net10.0 true - LTRData.$(MSBuildProjectName) + Devedse.$(MSBuildProjectName) $(LocalNuGetPath) $(FileVersion) README.md @@ -26,6 +26,8 @@ $(MSBuildThisFileDirectory)../SigningKey.snk false + false + $(BaseIntermediateOutputPath)Generated false @@ -37,4 +39,8 @@ + + + + diff --git a/Library/DiscUtils.Core/DiskImageBuilder.cs b/Library/DiscUtils.Core/DiskImageBuilder.cs index 73663b373..cf12403f9 100644 --- a/Library/DiscUtils.Core/DiskImageBuilder.cs +++ b/Library/DiscUtils.Core/DiskImageBuilder.cs @@ -102,9 +102,18 @@ public static DiskImageBuilder GetBuilder(string type, string variant) /// 'foo', the files 'foo.vmdk' and 'foo-flat.vmdk' could be returned. public abstract IEnumerable Build(string baseName); + // Set this to true to enable AoT compatiblity + public static bool ShouldUseVirtualDiskManagerTypeMap { get; set; } + [MemberNotNull(nameof(_typeMap))] private static void InitializeMaps() { + if (ShouldUseVirtualDiskManagerTypeMap) + { + _typeMap = VirtualDiskManager.TypeMap; + return; + } + var typeMap = new Dictionary(); foreach (var type in typeof(VirtualDisk).Assembly.GetTypes()) diff --git a/Library/DiscUtils.Core/Partitions/PartitionTable.cs b/Library/DiscUtils.Core/Partitions/PartitionTable.cs index dd59c5dfb..9def19505 100644 --- a/Library/DiscUtils.Core/Partitions/PartitionTable.cs +++ b/Library/DiscUtils.Core/Partitions/PartitionTable.cs @@ -54,11 +54,13 @@ public abstract class PartitionTable /// public abstract Geometry? DiskGeometry { get; } + private static List? _factories; + private static List Factories { get { - if (field == null) + if (_factories == null) { var factories = new List(); @@ -66,17 +68,25 @@ private static List Factories { foreach (var attr in type.GetCustomAttributes(false)) { - factories.Add((PartitionTableFactory)Activator.CreateInstance(type)!); + factories.Add((PartitionTableFactory)Activator.CreateInstance(type, true)!); } } - field = factories; + _factories = factories; } - return field; + return _factories; + } + } + + internal static void RegisterPartitionTableFactory(PartitionTableFactory factory) + { + if (_factories == null) + { + _factories = new List(); } - set; + _factories.Add(factory); } /// diff --git a/Library/DiscUtils.Core/VirtualDisk.cs b/Library/DiscUtils.Core/VirtualDisk.cs index ee4cfe228..9ff177862 100644 --- a/Library/DiscUtils.Core/VirtualDisk.cs +++ b/Library/DiscUtils.Core/VirtualDisk.cs @@ -375,12 +375,12 @@ public static VirtualDisk CreateDisk(DiscFileSystem fileSystem, string type, str var uri = PathToUri(path); VirtualDisk result; - if (!VirtualDiskManager.DiskTransports.TryGetValue(uri.Scheme, out var transportType)) + if (!VirtualDiskManager.DiskTransports.TryGetValue(uri.Scheme, out var transportFactory)) { throw new FileNotFoundException($"Unable to parse path '{path}'", path); } - var transport = (VirtualDiskTransport)Activator.CreateInstance(transportType)!; + var transport = transportFactory(); try { @@ -499,12 +499,12 @@ public static VirtualDisk CreateDisk(DiscFileSystem fileSystem, string type, str var uri = PathToUri(path); VirtualDisk? result = null; - if (!VirtualDiskManager.DiskTransports.TryGetValue(uri.Scheme, out var transportType)) + if (!VirtualDiskManager.DiskTransports.TryGetValue(uri.Scheme, out var transportFactory)) { throw new FileNotFoundException($"Unable to parse path '{uri}'", path); } - var transport = (VirtualDiskTransport)Activator.CreateInstance(transportType)!; + var transport = transportFactory(); try { diff --git a/Library/DiscUtils.Core/VirtualDiskManager.cs b/Library/DiscUtils.Core/VirtualDiskManager.cs index b6179de34..a25858960 100644 --- a/Library/DiscUtils.Core/VirtualDiskManager.cs +++ b/Library/DiscUtils.Core/VirtualDiskManager.cs @@ -1,7 +1,7 @@ -using System; +using DiscUtils.Internal; +using System; using System.Collections.Generic; using System.Reflection; -using DiscUtils.Internal; namespace DiscUtils; @@ -14,10 +14,10 @@ static VirtualDiskManager() { ExtensionMap = new Dictionary(StringComparer.OrdinalIgnoreCase); TypeMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - DiskTransports = new Dictionary(StringComparer.OrdinalIgnoreCase); + DiskTransports = new Dictionary>(StringComparer.OrdinalIgnoreCase); } - internal static Dictionary DiskTransports { get; } + internal static Dictionary> DiskTransports { get; } internal static Dictionary ExtensionMap { get; } /// @@ -33,7 +33,46 @@ static VirtualDiskManager() internal static Dictionary TypeMap { get; } /// - /// Locates VirtualDiskFactory factories attributed with VirtualDiskFactoryAttribute, and types marked with VirtualDiskTransportAttribute, that are able to work with Virtual Disk types. + /// Registers a VirtualDiskFactory instance. + /// + /// The factory to register. + public static void RegisterVirtualDiskFactory(VirtualDiskFactory factory) + { + var type = factory.GetType(); + var diskFactoryAttribute = type.GetCustomAttribute(false); + if (diskFactoryAttribute != null) + { + if (!TypeMap.ContainsKey(diskFactoryAttribute.Type)) + { + TypeMap.Add(diskFactoryAttribute.Type, factory); + } + + foreach (var extension in diskFactoryAttribute.FileExtensions) + { + if (!ExtensionMap.ContainsKey(extension)) + { + ExtensionMap.Add(extension, factory); + } + } + } + } + + /// + /// Registers a VirtualDiskTransport factory. + /// + /// The URI scheme. + /// The factory method. + internal static void RegisterVirtualDiskTransport(string scheme, Func factory) + { + if (!DiskTransports.ContainsKey(scheme)) + { + DiskTransports.Add(scheme, factory); + } + } + + /// + /// Locates VirtualDiskFactory factories attributed with VirtualDiskFactoryAttribute, and types marked with VirtualDiskTransportAttribute, + /// that are able to work with Virtual Disk types. /// /// An assembly to scan public static void RegisterVirtualDiskTypes(Assembly assembly) @@ -55,7 +94,7 @@ public static void RegisterVirtualDiskTypes(Assembly assembly) var diskTransportAttribute = type.GetCustomAttribute(false); if (diskTransportAttribute != null) { - DiskTransports.Add(diskTransportAttribute.Scheme, type); + DiskTransports.Add(diskTransportAttribute.Scheme, () => (VirtualDiskTransport)Activator.CreateInstance(type)!); } } } diff --git a/Library/DiscUtils.Core/VolumeManager.cs b/Library/DiscUtils.Core/VolumeManager.cs index 417f09fc3..90bfa2a5b 100644 --- a/Library/DiscUtils.Core/VolumeManager.cs +++ b/Library/DiscUtils.Core/VolumeManager.cs @@ -82,26 +82,45 @@ public VolumeManager(Stream initialDiskContent) private static readonly object _syncObj = new(); + private static ConcurrentBag? _logicalVolumeFactories; + private static ConcurrentBag LogicalVolumeFactories { get { - if (field == null) + if (_logicalVolumeFactories == null) { lock (_syncObj) { - if (field == null) + if (_logicalVolumeFactories == null) { - var factories = new ConcurrentBag(GetLogicalVolumeFactories(_coreAssembly)); - field = factories; + _logicalVolumeFactories = new ConcurrentBag(GetLogicalVolumeFactories(_coreAssembly)); } } } - return field; + return _logicalVolumeFactories; + } + } + + /// + /// Register a new LogicalVolumeFactory instance. + /// + /// The factory to register. + internal static void RegisterLogicalVolumeFactory(LogicalVolumeFactory factory) + { + if (_logicalVolumeFactories == null) + { + lock (_syncObj) + { + if (_logicalVolumeFactories == null) + { + _logicalVolumeFactories = new ConcurrentBag(); + } + } } - set; + _logicalVolumeFactories.Add(factory); } private static IEnumerable GetLogicalVolumeFactories(Assembly assembly) diff --git a/Library/DiscUtils.Registry/Bin.cs b/Library/DiscUtils.Registry/Bin.cs index 12dcc19a6..e81d9b67a 100644 --- a/Library/DiscUtils.Registry/Bin.cs +++ b/Library/DiscUtils.Registry/Bin.cs @@ -152,6 +152,37 @@ public Span ReadRawCellData(int cellIndex, Span maxBytes) { var index = cellIndex - _header.FileOffset; var len = Math.Abs(EndianUtilities.ToInt32LittleEndian(_buffer, index)); + + // Check if this is a "big data" cell (signature "db") + // Big data cells are used for values larger than ~16KB + if (len >= 6 && _buffer[index + 4] == 0x64 && _buffer[index + 5] == 0x62) // "db" + { + // Big data format: + // 0x00: signature "db" (2 bytes) + // 0x02: number of segments (2 bytes) + // 0x04: offset to list of cell indices (4 bytes) + var numSegments = EndianUtilities.ToUInt16LittleEndian(_buffer.AsSpan(index + 6)); + var listOffset = EndianUtilities.ToInt32LittleEndian(_buffer.AsSpan(index + 8)); + + // Read the list of cell indices + var listIndex = listOffset - _header.FileOffset; + var bytesWritten = 0; + + for (var i = 0; i < numSegments && bytesWritten < maxBytes.Length; i++) + { + var segmentCellIndex = EndianUtilities.ToInt32LittleEndian(_buffer.AsSpan(listIndex + i * 4)); + var segmentIndex = segmentCellIndex - _header.FileOffset; + var segmentLen = Math.Abs(EndianUtilities.ToInt32LittleEndian(_buffer, segmentIndex)) - 4; + + var bytesToCopy = Math.Min(segmentLen, maxBytes.Length - bytesWritten); + _buffer.AsSpan(segmentIndex + 4, bytesToCopy).CopyTo(maxBytes.Slice(bytesWritten)); + bytesWritten += bytesToCopy; + } + + return maxBytes.Slice(0, bytesWritten); + } + + // Regular cell data var result = maxBytes.Slice(0, Math.Min(len - 4, maxBytes.Length)); _buffer.AsSpan(index + 4, result.Length).CopyTo(result); return result; diff --git a/Library/DiscUtils.Registry/RegistryKey.cs b/Library/DiscUtils.Registry/RegistryKey.cs index f8448d54e..553e03c9e 100644 --- a/Library/DiscUtils.Registry/RegistryKey.cs +++ b/Library/DiscUtils.Registry/RegistryKey.cs @@ -40,6 +40,8 @@ public enum RegistryValueOptions /// public sealed class RegistryKey { + internal static readonly char[] RegistryPathSeparators = ['\\']; + private readonly KeyNodeCell _cell; private readonly RegistryHive _hive; @@ -530,7 +532,7 @@ public RegistryKey CreateSubKey(string subkey) return this; } - var split = subkey.Split(Utilities.PathSeparators, 2, StringSplitOptions.RemoveEmptyEntries); + var split = subkey.Split(RegistryPathSeparators, 2, StringSplitOptions.RemoveEmptyEntries); var cellIndex = FindSubKeyCell(split[0]); if (cellIndex < 0) @@ -573,7 +575,7 @@ public RegistryKey OpenSubKey(string path) return this; } - var split = path.Split(Utilities.PathSeparators, 2, StringSplitOptions.RemoveEmptyEntries); + var split = path.Split(RegistryPathSeparators, 2, StringSplitOptions.RemoveEmptyEntries); var cellIndex = FindSubKeyCell(split[0]); if (cellIndex < 0) @@ -642,7 +644,7 @@ public bool DeleteSubKey(string subkey, bool throwOnMissingSubKey) throw new ArgumentException("Invalid SubKey", nameof(subkey)); } - var split = subkey.Split(Utilities.PathSeparators, 2, StringSplitOptions.RemoveEmptyEntries); + var split = subkey.Split(RegistryPathSeparators, 2, StringSplitOptions.RemoveEmptyEntries); var subkeyCellIndex = FindSubKeyCell(split[0]); if (subkeyCellIndex < 0) diff --git a/Library/DiscUtils.SourceGenerator/DiscUtils.SourceGenerator.csproj b/Library/DiscUtils.SourceGenerator/DiscUtils.SourceGenerator.csproj new file mode 100644 index 000000000..35a9841d6 --- /dev/null +++ b/Library/DiscUtils.SourceGenerator/DiscUtils.SourceGenerator.csproj @@ -0,0 +1,22 @@ + + + + + netstandard2.0 + + true + Source Generator for DiscUtils + true + DiscUtils;SourceGenerator;Analyzer + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/Library/DiscUtils.SourceGenerator/FactoryGenerator.cs b/Library/DiscUtils.SourceGenerator/FactoryGenerator.cs new file mode 100644 index 000000000..c9b57a250 --- /dev/null +++ b/Library/DiscUtils.SourceGenerator/FactoryGenerator.cs @@ -0,0 +1,199 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace DiscUtils.SourceGenerator +{ + [Generator] + public class FactoryGenerator : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Debugging helper - uncomment to attach debugger + // if (!System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Launch(); + + var classDeclarations = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: IsSyntaxTargetForGeneration, + transform: GetSemanticTargetForGeneration) + .Where(m => m != null); + + var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); + + context.RegisterSourceOutput(compilationAndClasses, (spc, source) => Execute(spc, source.Left, source.Right)); + } + + private bool IsSyntaxTargetForGeneration(SyntaxNode node, CancellationToken cancellationToken) + { + return node is ClassDeclarationSyntax cds && cds.BaseList != null; + } + + private INamedTypeSymbol? GetSemanticTargetForGeneration(GeneratorSyntaxContext context, CancellationToken cancellationToken) + { + var classDeclaration = (ClassDeclarationSyntax)context.Node; + var symbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol; + + if (symbol == null) return null; + + if (InheritsFrom(symbol, "DiscUtils.Vfs.VfsFileSystemFactory") || + InheritsFrom(symbol, "DiscUtils.Internal.VirtualDiskFactory") || + InheritsFrom(symbol, "DiscUtils.Internal.LogicalVolumeFactory") || + InheritsFrom(symbol, "DiscUtils.Internal.VirtualDiskTransport") || + InheritsFrom(symbol, "DiscUtils.Partitions.PartitionTableFactory")) + { + return symbol; + } + + return null; + } + + private bool InheritsFrom(INamedTypeSymbol symbol, string typeName) + { + var current = symbol.BaseType; + while (current != null) + { + if (current.ToDisplayString() == typeName) + { + return true; + } + current = current.BaseType; + } + return false; + } + + private void Execute(SourceProductionContext context, Compilation compilation, System.Collections.Immutable.ImmutableArray classes) + { + try + { + if (classes.IsDefaultOrEmpty && compilation.AssemblyName != "DiscUtils" && compilation.AssemblyName != "LTRData.DiscUtils") + { + return; + } + + var distinctClasses = classes.Where(c => c != null).Distinct(SymbolEqualityComparer.Default).Cast().ToList(); + + // Generate AssemblyRegistration for libraries + if (distinctClasses.Any()) + { + GenerateAssemblyRegistration(context, compilation, distinctClasses); + } + + // If this is the main DiscUtils assembly, generate the aggregator + if (compilation.AssemblyName == "DiscUtils" || compilation.AssemblyName == "LTRData.DiscUtils") + { + GenerateSetupHelper(context, compilation); + } + } + catch (Exception ex) + { + // Generate a file with the error so we can see it + context.AddSource("GeneratorError.g.cs", SourceText.From($"/* Error: {ex.ToString()} */", Encoding.UTF8)); + } + } + + private void GenerateAssemblyRegistration(SourceProductionContext context, Compilation compilation, List classes) + { + var sb = new StringBuilder(); + var assemblyName = compilation.AssemblyName?.Replace(".", "_"); + + sb.AppendLine("using System;"); + sb.AppendLine("using DiscUtils.Core;"); + sb.AppendLine(); + sb.AppendLine($"namespace {compilation.AssemblyName}"); + sb.AppendLine("{"); + sb.AppendLine($" public static class AssemblyRegistration_{assemblyName}"); + sb.AppendLine(" {"); + sb.AppendLine(" public static void Register()"); + sb.AppendLine(" {"); + + foreach (var classSymbol in classes) + { + if (InheritsFrom(classSymbol, "DiscUtils.Vfs.VfsFileSystemFactory")) + { + sb.AppendLine($" DiscUtils.FileSystemManager.RegisterFileSystems(new {classSymbol.ToDisplayString()}());"); + } + else if (InheritsFrom(classSymbol, "DiscUtils.Internal.VirtualDiskFactory")) + { + sb.AppendLine($" DiscUtils.VirtualDiskManager.RegisterVirtualDiskFactory(new {classSymbol.ToDisplayString()}());"); + } + else if (InheritsFrom(classSymbol, "DiscUtils.Internal.LogicalVolumeFactory")) + { + sb.AppendLine($" DiscUtils.VolumeManager.RegisterLogicalVolumeFactory(new {classSymbol.ToDisplayString()}());"); + } + else if (InheritsFrom(classSymbol, "DiscUtils.Partitions.PartitionTableFactory")) + { + sb.AppendLine($" DiscUtils.Partitions.PartitionTable.RegisterPartitionTableFactory(new {classSymbol.ToDisplayString()}());"); + } + else if (InheritsFrom(classSymbol, "DiscUtils.Internal.VirtualDiskTransport")) + { + var attributes = classSymbol.GetAttributes().Where(ad => ad.AttributeClass?.ToDisplayString() == "DiscUtils.Internal.VirtualDiskTransportAttribute"); + foreach(var attr in attributes) + { + if (attr.ConstructorArguments.Length > 0) + { + string scheme = attr.ConstructorArguments[0].Value?.ToString(); + if (!string.IsNullOrEmpty(scheme)) + { + sb.AppendLine($" DiscUtils.VirtualDiskManager.RegisterVirtualDiskTransport(\"{scheme}\", () => new {classSymbol.ToDisplayString()}());"); + } + } + } + } + } + + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine("}"); + + context.AddSource($"AssemblyRegistration_{assemblyName}.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); + } + + private void GenerateSetupHelper(SourceProductionContext context, Compilation compilation) + { + var sb = new StringBuilder(); + sb.AppendLine("using System;"); + sb.AppendLine("using System.Reflection;"); + sb.AppendLine(); + sb.AppendLine("namespace DiscUtils.Setup"); + sb.AppendLine("{"); + sb.AppendLine(" public static class GeneratedSetupHelper"); + sb.AppendLine(" {"); + sb.AppendLine(" public static void RegisterFactories()"); + sb.AppendLine(" {"); + sb.AppendLine(" Console.WriteLine(\"GeneratedSetupHelper.RegisterFactories() called.\");"); + + foreach (var reference in compilation.References) + { + if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assemblySymbol) + { + var assemblyName = assemblySymbol.Name; + if ((assemblyName.StartsWith("DiscUtils.") || assemblyName.StartsWith("LTRData.DiscUtils.")) && !assemblyName.EndsWith("SourceGenerator")) + { + var safeAssemblyName = assemblyName.Replace(".", "_"); + var typeName = $"{assemblyName}.AssemblyRegistration_{safeAssemblyName}"; + + var typeSymbol = compilation.GetTypeByMetadataName(typeName); + + if (typeSymbol != null) + { + sb.AppendLine($" Console.WriteLine(\"Calling {typeName}.Register()\");"); + sb.AppendLine($" {typeName}.Register();"); + } + } + } + } + + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine("}"); + + context.AddSource("GeneratedSetupHelper.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); + } + } +} + diff --git a/Library/DiscUtils/SetupHelper.cs b/Library/DiscUtils/SetupHelper.cs index 9e68e892d..895416adf 100644 --- a/Library/DiscUtils/SetupHelper.cs +++ b/Library/DiscUtils/SetupHelper.cs @@ -6,6 +6,7 @@ using DiscUtils.Fat; using DiscUtils.HfsPlus; using DiscUtils.Iso9660; +using DiscUtils.Lvm; using DiscUtils.Nfs; using DiscUtils.Ntfs; using DiscUtils.OpticalDisk; @@ -53,4 +54,10 @@ public static void SetupComplete() Setup.SetupHelper.RegisterAssembly(typeof(Xva.Disk).Assembly); Setup.SetupHelper.RegisterAssembly(typeof(Lvm.LogicalVolumeManager).Assembly); } + + public static void SetupCompleteAot() + { + DiskImageBuilder.ShouldUseVirtualDiskManagerTypeMap = true; + DiscUtils.Setup.GeneratedSetupHelper.RegisterFactories(); + } } \ No newline at end of file diff --git a/Tests/LibraryTests/Registry/RegistryKeyTest.cs b/Tests/LibraryTests/Registry/RegistryKeyTest.cs index 6f099087e..81165c5bf 100644 --- a/Tests/LibraryTests/Registry/RegistryKeyTest.cs +++ b/Tests/LibraryTests/Registry/RegistryKeyTest.cs @@ -77,6 +77,34 @@ public void SetLargeValue() Assert.Equal(0xAD, readVal[5232]); } + [Fact] + public void SetVeryLargeValue_BigDataCell() + { + // Test big data cells (used for values >~16KB) + // This mimics real-world scenarios like Windows registry ProductPolicy values + var buffer = new byte[80 * 1024]; // 80KB - larger than big data threshold + + // Set some distinctive bytes at various positions + buffer[0] = 0x12; + buffer[100] = 0x34; + buffer[16384] = 0x56; // Past first 16KB boundary + buffer[32768] = 0x78; // Past second 16KB boundary + buffer[buffer.Length - 1] = 0x9A; + + hive.Root.SetValue("verybigvalue", buffer); + + var readVal = (byte[])hive.Root.GetValue("verybigvalue"); + Assert.Equal(buffer.Length, readVal.Length); + Assert.Equal(0x12, readVal[0]); + Assert.Equal(0x34, readVal[100]); + Assert.Equal(0x56, readVal[16384]); + Assert.Equal(0x78, readVal[32768]); + Assert.Equal(0x9A, readVal[buffer.Length - 1]); + + // Verify entire buffer matches + Assert.Equal(buffer, readVal); + } + [Fact] public void SetLongValue() { diff --git a/generate-packages.ps1 b/generate-packages.ps1 new file mode 100644 index 000000000..30b4e2a18 --- /dev/null +++ b/generate-packages.ps1 @@ -0,0 +1,23 @@ +$outputDir = "C:\XGitPrivate\DevePXEBootStuff\DevePXEBootClean\DiscUtils\nupkgs" + +Write-Host "Cleaning output directory: $outputDir" +if (Test-Path -Path $outputDir) { + Get-ChildItem -Path $outputDir -Filter *.nupkg | Remove-Item -Force + Get-ChildItem -Path $outputDir -Filter *.snupkg | Remove-Item -Force +} else { + New-Item -ItemType Directory -Path $outputDir | Out-Null +} + +Write-Host "Cleaning solution..." +dotnet clean --configuration Release + +Write-Host "Restoring..." +dotnet restore + +Write-Host "Building..." +dotnet build --configuration Release + +Write-Host "Packing..." +dotnet pack --configuration Release --no-build --output $outputDir + +Write-Host "Done. Packages are in $outputDir"