diff --git a/src/PackageUploader.UI.Test/ViewModel/ExtractIdInformationFromValidatorLogTest.cs b/src/PackageUploader.UI.Test/ViewModel/ExtractIdInformationFromValidatorLogTest.cs index f7a40536..7640de49 100644 --- a/src/PackageUploader.UI.Test/ViewModel/ExtractIdInformationFromValidatorLogTest.cs +++ b/src/PackageUploader.UI.Test/ViewModel/ExtractIdInformationFromValidatorLogTest.cs @@ -28,7 +28,7 @@ public TestableValidatorLogViewModel( UploadingProgressPercentageProvider uploadingProgressPercentageProvider, ErrorModelProvider errorModelProvider, string xmlContent) - : base(packageModelProvider, uploaderService, windowService, uploadingProgressPercentageProvider, errorModelProvider) + : base(packageModelProvider, uploaderService, windowService, uploadingProgressPercentageProvider, errorModelProvider, new PathConfigurationProvider()) { _xmlContent = xmlContent; TestSubValFilePath = Path.GetTempFileName(); diff --git a/src/PackageUploader.UI.Test/ViewModel/GenerateUploaderConfigTest.cs b/src/PackageUploader.UI.Test/ViewModel/GenerateUploaderConfigTest.cs index 43122ae0..abb3587f 100644 --- a/src/PackageUploader.UI.Test/ViewModel/GenerateUploaderConfigTest.cs +++ b/src/PackageUploader.UI.Test/ViewModel/GenerateUploaderConfigTest.cs @@ -30,7 +30,7 @@ public TestableUploaderConfigViewModel( IWindowService windowService, UploadingProgressPercentageProvider uploadingProgressPercentageProvider, ErrorModelProvider errorModelProvider) - : base(packageModelProvider, uploaderService, windowService, uploadingProgressPercentageProvider, errorModelProvider) + : base(packageModelProvider, uploaderService, windowService, uploadingProgressPercentageProvider, errorModelProvider, new PathConfigurationProvider()) { } diff --git a/src/PackageUploader.UI.Test/ViewModel/PackageUploadViewModelTest.cs b/src/PackageUploader.UI.Test/ViewModel/PackageUploadViewModelTest.cs index 63d7dbcd..215b2f20 100644 --- a/src/PackageUploader.UI.Test/ViewModel/PackageUploadViewModelTest.cs +++ b/src/PackageUploader.UI.Test/ViewModel/PackageUploadViewModelTest.cs @@ -63,7 +63,8 @@ public void Setup() _mockPackageUploaderService.Object, _mockWindowService.Object, _uploadingProgressPercentageProvider, - _errorModelProvider + _errorModelProvider, + new PathConfigurationProvider() ); } @@ -87,8 +88,9 @@ public void Test_BranchOrFlightDisplayName() _mockPackageUploaderService.Object, _mockWindowService.Object, _uploadingProgressPercentageProvider, - _errorModelProvider - ); + _errorModelProvider, + new PathConfigurationProvider() + ); viewModel2.BranchOrFlightDisplayName = "Test"; Assert.AreEqual("Test", viewModel2.BranchOrFlightDisplayName); } @@ -124,7 +126,8 @@ public void Test_BranchAndFlightNames() _mockPackageUploaderService.Object, _mockWindowService.Object, _uploadingProgressPercentageProvider, - _errorModelProvider + _errorModelProvider, + new PathConfigurationProvider() ); viewModel2.BranchAndFlightNames = names; // tests the former value is successfully retrieved @@ -146,7 +149,8 @@ public void Test_MarketGroupNames() _mockPackageUploaderService.Object, _mockWindowService.Object, _uploadingProgressPercentageProvider, - _errorModelProvider + _errorModelProvider, + new PathConfigurationProvider() ); viewModel2.MarketGroupNames = names; // tests the former value is successfully retrieved diff --git a/src/PackageUploader.UI.Test/ViewModel/TestablePackageUploadViewModel.cs b/src/PackageUploader.UI.Test/ViewModel/TestablePackageUploadViewModel.cs index 1216de53..3bb82cc5 100644 --- a/src/PackageUploader.UI.Test/ViewModel/TestablePackageUploadViewModel.cs +++ b/src/PackageUploader.UI.Test/ViewModel/TestablePackageUploadViewModel.cs @@ -43,7 +43,7 @@ public TestablePackageUploadViewModel( IWindowService windowService, UploadingProgressPercentageProvider uploadingProgressPercentageProvider, ErrorModelProvider errorModelProvider) - : base(packageModelProvider, uploaderService, windowService, uploadingProgressPercentageProvider, errorModelProvider) + : base(packageModelProvider, uploaderService, windowService, uploadingProgressPercentageProvider, errorModelProvider, new PathConfigurationProvider()) { } diff --git a/src/PackageUploader.UI/Model/Xvc/XvcFile.cs b/src/PackageUploader.UI/Model/Xvc/XvcFile.cs index a7602574..39f9882e 100644 --- a/src/PackageUploader.UI/Model/Xvc/XvcFile.cs +++ b/src/PackageUploader.UI/Model/Xvc/XvcFile.cs @@ -60,6 +60,84 @@ private static UInt32 NumberOfHashPagesForLevel(UInt64 dataPages, Int32 level) return (UInt32)((dataPages + divisor - 1) / divisor); } + private static readonly byte[] ZipLocalFileSignature = [0x50, 0x4B, 0x03, 0x04]; + private static readonly byte[] ZipEocdSignature = [0x50, 0x4B, 0x05, 0x06]; + private const int FirstReadSize = 4096; + private const int LastReadSize = 65 * 1024; + private const int MinFileNameOffset = 30; + + /// + /// Detects whether a .msixvc file is in MSIXVC2 format by checking for ZIP signatures. + /// MSIXVC2 packages are ZIP-based and contain standard ZIP headers, while MSIXVC1 + /// packages use a proprietary binary format without ZIP signatures. + /// + public static bool IsLikelyMsixvc2Package(string packagePath) + { + try + { + if (!packagePath.EndsWith(".msixvc", StringComparison.OrdinalIgnoreCase)) + return false; + + var fileInfo = new FileInfo(packagePath); + if (!fileInfo.Exists || fileInfo.Length < FirstReadSize) + return false; + + using var stream = File.OpenRead(packagePath); + + // Check first bytes for ZIP local file header at offset 0 + var firstBuffer = new byte[Math.Min(FirstReadSize, fileInfo.Length)]; + stream.ReadExactly(firstBuffer, 0, firstBuffer.Length); + + if (firstBuffer.Length >= MinFileNameOffset && + firstBuffer[0] == ZipLocalFileSignature[0] && + firstBuffer[1] == ZipLocalFileSignature[1] && + firstBuffer[2] == ZipLocalFileSignature[2] && + firstBuffer[3] == ZipLocalFileSignature[3]) + { + return true; + } + + // Check last 65KB for ZIP End of Central Directory signature + long lastChunkStart = Math.Max(0, fileInfo.Length - LastReadSize); + int lastChunkSize = (int)(fileInfo.Length - lastChunkStart); + var lastBuffer = new byte[lastChunkSize]; + stream.Position = lastChunkStart; + stream.ReadExactly(lastBuffer, 0, lastChunkSize); + + if (LastIndexOfSignature(lastBuffer, ZipEocdSignature) >= 0) + return true; + + return false; + } + catch (Exception) + { + return false; + } + } + + private static int LastIndexOfSignature(byte[] buffer, byte[] signature) + { + if (buffer.Length < signature.Length) + return -1; + + for (int i = buffer.Length - signature.Length; i >= 0; i--) + { + bool match = true; + for (int j = 0; j < signature.Length; j++) + { + if (buffer[i + j] != signature[j]) + { + match = false; + break; + } + } + if (match) + return i; + } + + return -1; + } + public static void GetBuildAndKeyId(string packagePath, out Guid buildId, out Guid keyId) { using FileStream stream = File.OpenRead(packagePath); diff --git a/src/PackageUploader.UI/Resources/Images/PackagePlaceholder.png b/src/PackageUploader.UI/Resources/Images/PackagePlaceholder.png new file mode 100644 index 00000000..aa12d3e2 Binary files /dev/null and b/src/PackageUploader.UI/Resources/Images/PackagePlaceholder.png differ diff --git a/src/PackageUploader.UI/View/PackageUploadView.xaml b/src/PackageUploader.UI/View/PackageUploadView.xaml index c307d248..be089404 100644 --- a/src/PackageUploader.UI/View/PackageUploadView.xaml +++ b/src/PackageUploader.UI/View/PackageUploadView.xaml @@ -178,22 +178,31 @@ Height="32" Margin="0,0,0,10"/> - - - - + + + @@ -496,7 +505,12 @@ + Foreground="{DynamicResource SecondaryTextBrush}" + Visibility="{Binding IsMsixvc2Package, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}"/> + @@ -511,11 +525,15 @@ - + + + + Foreground="{DynamicResource SecondaryTextBrush}" + Visibility="{Binding IsMsixvc2Package, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}"/> + + TextWrapping="Wrap" + Visibility="{Binding IsMsixvc2Package, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}"/> + + Foreground="{DynamicResource SecondaryTextBrush}" + Visibility="{Binding IsMsixvc2Package, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}"/> + @@ -563,7 +596,10 @@ Visibility="{Binding IsCompactMode, Converter={StaticResource BooleanToVisibilityConverter}}"> - + + @@ -574,8 +610,8 @@ - -