From 2b27e713163df8508fbfec5d1c312bcf2a92cd52 Mon Sep 17 00:00:00 2001 From: Elshiekh Ahmed Date: Mon, 13 Apr 2026 15:25:03 -0700 Subject: [PATCH 1/6] "Upload a package" legacy path recognizes MSIXVC2 .msixvc package and displays message indicating its recognition --- src/PackageUploader.UI/Model/Xvc/XvcFile.cs | 25 +++++++++++++++++++ .../View/PackageUploadView.xaml | 17 ++++++++++--- .../ViewModel/PackageUploadViewModel.cs | 24 +++++++++++++++++- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/PackageUploader.UI/Model/Xvc/XvcFile.cs b/src/PackageUploader.UI/Model/Xvc/XvcFile.cs index a7602574..7915ff93 100644 --- a/src/PackageUploader.UI/Model/Xvc/XvcFile.cs +++ b/src/PackageUploader.UI/Model/Xvc/XvcFile.cs @@ -60,6 +60,31 @@ private static UInt32 NumberOfHashPagesForLevel(UInt64 dataPages, Int32 level) return (UInt32)((dataPages + divisor - 1) / divisor); } + /// + /// Detects whether a .msixvc file was created by makepkg2 (MSIXVC2 format). + /// Attempts full XVC header parsing. If it fails with an ArgumentException + /// from the GUID constructor, the binary layout differs from MSIXVC v1. + /// + public static bool IsLikelyMsixvc2Package(string packagePath) + { + try + { + // If full parsing succeeds, this is a valid MSIXVC v1 package. + GetBuildAndKeyId(packagePath, out _, out _); + return false; + } + catch (ArgumentException) + { + // "Byte array for Guid must be exactly 16 bytes": MSIXVC2 binary layout + return true; + } + catch (Exception) + { + // IO errors, corrupt files, etc. (not indicative of MSIXVC2) + return false; + } + } + public static void GetBuildAndKeyId(string packagePath, out Guid buildId, out Guid keyId) { using FileStream stream = File.OpenRead(packagePath); diff --git a/src/PackageUploader.UI/View/PackageUploadView.xaml b/src/PackageUploader.UI/View/PackageUploadView.xaml index c307d248..b69a0530 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"/> - - - - + + + diff --git a/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs index fe777238..7ecdf371 100644 --- a/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs +++ b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs @@ -370,6 +370,13 @@ public string PackageErrorMessage set => SetProperty(ref _packageErrorMessage, value); } + private string _msixvc2InfoMessage = string.Empty; + public string Msixvc2InfoMessage + { + get => _msixvc2InfoMessage; + set => SetProperty(ref _msixvc2InfoMessage, value); + } + private string _branchOrFlightErrorMessage = string.Empty; public string BranchOrFlightErrorMessage { @@ -511,7 +518,14 @@ private void ProcessSelectedPackage() { return; } - + + // Detect MSIXVC2 packages (created by makepkg2) before attempting legacy extraction + if (XvcFile.IsLikelyMsixvc2Package(PackageFilePath)) + { + Msixvc2InfoMessage = "MSIXVC2 package detected. Upload is supported and will use the makepkg2 upload tool."; + return; + } + try { // Extract package information @@ -519,6 +533,13 @@ private void ProcessSelectedPackage() } catch (Exception ex) { + // Fallback: if extraction fails with a GUID parsing error, it may be an MSIXVC2 package + if (ex is ArgumentException || ex.InnerException is ArgumentException) + { + Msixvc2InfoMessage = "MSIXVC2 package detected. Upload is supported and will use the makepkg2 upload tool."; + return; + } + PackageErrorMessage = $"{Resources.Strings.PackageUpload.ErrorProcessingPackageErrMsg} {ex.Message}"; // "Error processing package. {ex.Message}"; } } @@ -534,6 +555,7 @@ private void ResetPackage() MarketGroupName = string.Empty; PackageErrorMessage = string.Empty; + Msixvc2InfoMessage = string.Empty; ResetProductInfo(); } From 898124b7f64f9205e6a67e117709279e595d56d6 Mon Sep 17 00:00:00 2001 From: Elshiekh Ahmed Date: Mon, 13 Apr 2026 15:44:55 -0700 Subject: [PATCH 2/6] Utilizing zip header to detect MSIXVC2 .msixvc package within legacy upload path and displaying message if detected informing user of MSIXVC2 package and that Upload is still supported --- src/PackageUploader.UI/Model/Xvc/XvcFile.cs | 75 ++++++++++++++++--- .../ViewModel/PackageUploadViewModel.cs | 7 -- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/PackageUploader.UI/Model/Xvc/XvcFile.cs b/src/PackageUploader.UI/Model/Xvc/XvcFile.cs index 7915ff93..39f9882e 100644 --- a/src/PackageUploader.UI/Model/Xvc/XvcFile.cs +++ b/src/PackageUploader.UI/Model/Xvc/XvcFile.cs @@ -60,31 +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 was created by makepkg2 (MSIXVC2 format). - /// Attempts full XVC header parsing. If it fails with an ArgumentException - /// from the GUID constructor, the binary layout differs from MSIXVC v1. + /// 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 full parsing succeeds, this is a valid MSIXVC v1 package. - GetBuildAndKeyId(packagePath, out _, out _); + 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 (ArgumentException) - { - // "Byte array for Guid must be exactly 16 bytes": MSIXVC2 binary layout - return true; - } catch (Exception) { - // IO errors, corrupt files, etc. (not indicative of MSIXVC2) 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/ViewModel/PackageUploadViewModel.cs b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs index 7ecdf371..f5450028 100644 --- a/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs +++ b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs @@ -533,13 +533,6 @@ private void ProcessSelectedPackage() } catch (Exception ex) { - // Fallback: if extraction fails with a GUID parsing error, it may be an MSIXVC2 package - if (ex is ArgumentException || ex.InnerException is ArgumentException) - { - Msixvc2InfoMessage = "MSIXVC2 package detected. Upload is supported and will use the makepkg2 upload tool."; - return; - } - PackageErrorMessage = $"{Resources.Strings.PackageUpload.ErrorProcessingPackageErrMsg} {ex.Message}"; // "Error processing package. {ex.Message}"; } } From 558837d037eeec7146a370936ca44927d0358337 Mon Sep 17 00:00:00 2001 From: Elshiekh Ahmed Date: Mon, 13 Apr 2026 15:55:48 -0700 Subject: [PATCH 3/6] Preview for MSIXVC2 .msixvc package when selected via "Upload Package" legacy upload path --- .../View/PackageUploadView.xaml | 39 ++++++-- .../ViewModel/PackageUploadViewModel.cs | 89 +++++++++++++++++++ 2 files changed, 122 insertions(+), 6 deletions(-) diff --git a/src/PackageUploader.UI/View/PackageUploadView.xaml b/src/PackageUploader.UI/View/PackageUploadView.xaml index b69a0530..dc01b8a7 100644 --- a/src/PackageUploader.UI/View/PackageUploadView.xaml +++ b/src/PackageUploader.UI/View/PackageUploadView.xaml @@ -505,7 +505,12 @@ + Foreground="{DynamicResource SecondaryTextBrush}" + Visibility="{Binding IsMsixvc2Package, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}"/> + @@ -520,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}"/> + @@ -572,7 +596,10 @@ Visibility="{Binding IsCompactMode, Converter={StaticResource BooleanToVisibilityConverter}}"> - + + diff --git a/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs index f5450028..90762e95 100644 --- a/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs +++ b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics; +using System.IO.Compression; using System.Windows.Input; using System.Xml; using PackageUploader.ClientApi; @@ -377,6 +378,26 @@ public string Msixvc2InfoMessage set => SetProperty(ref _msixvc2InfoMessage, value); } + private bool _isMsixvc2Package = false; + public bool IsMsixvc2Package + { + get => _isMsixvc2Package; + set => SetProperty(ref _isMsixvc2Package, value); + } + + public string PackageIdentityName + { + get => Package.PackageIdentityName; + set + { + if (Package.PackageIdentityName != value) + { + Package.PackageIdentityName = value; + OnPropertyChanged(); + } + } + } + private string _branchOrFlightErrorMessage = string.Empty; public string BranchOrFlightErrorMessage { @@ -522,7 +543,16 @@ private void ProcessSelectedPackage() // Detect MSIXVC2 packages (created by makepkg2) before attempting legacy extraction if (XvcFile.IsLikelyMsixvc2Package(PackageFilePath)) { + IsMsixvc2Package = true; Msixvc2InfoMessage = "MSIXVC2 package detected. Upload is supported and will use the makepkg2 upload tool."; + try + { + ExtractMsixvc2PackageInformation(PackageFilePath); + } + catch (Exception ex) + { + PackageErrorMessage = $"{Resources.Strings.PackageUpload.ErrorExtractingInfoErrMsg} {ex.Message}"; + } return; } @@ -549,6 +579,8 @@ private void ResetPackage() PackageErrorMessage = string.Empty; Msixvc2InfoMessage = string.Empty; + IsMsixvc2Package = false; + PackageIdentityName = string.Empty; ResetProductInfo(); } @@ -668,6 +700,63 @@ private void ExtractPackageInformation(string packagePath) } } + private void ExtractMsixvc2PackageInformation(string packagePath) + { + long fileLength = GetFileSize(packagePath); + double bytesInMB = 1024.0 * 1024.0; + double bytesInGB = bytesInMB * 1024.0; + PackageSize = fileLength > bytesInGB + ? string.Format("{0:0.##} GB", fileLength / bytesInGB) + : string.Format("{0:0.##} MB", fileLength / bytesInMB); + + string? tempConfigPath = null; + try + { + using var archive = ZipFile.OpenRead(packagePath); + var configEntry = archive.Entries.FirstOrDefault(e => + e.Name.Equals("MicrosoftGame.config", StringComparison.OrdinalIgnoreCase)); + + if (configEntry == null) + { + PackageErrorMessage = "MicrosoftGame.config not found in MSIXVC2 package."; + return; + } + + tempConfigPath = Path.GetTempFileName(); + configEntry.ExtractToFile(tempConfigPath, overwrite: true); + + var gameConfig = new PartialGameConfigModel(tempConfigPath); + + PackageIdentityName = gameConfig.Identity.Name ?? string.Empty; + + try + { + PackageType = gameConfig.GetDeviceFamily(); + } + catch + { + // Device family not available + } + + if (!string.IsNullOrEmpty(gameConfig.StoreId)) + { + BigId = gameConfig.StoreId; + IsPackageMissingStoreId = false; + } + else + { + BigId = string.Empty; + IsPackageMissingStoreId = true; + PackageErrorMessage = Resources.Strings.PackageUpload.PackageHasNoBigIdConfigureMsftGameCfgErrMsg; + } + } + finally + { + if (tempConfigPath != null && File.Exists(tempConfigPath)) + File.Delete(tempConfigPath); + } + } + // Virtual methods to make the class more testable /// From b44eeaae4274012495a12cb271c960be00dd642c Mon Sep 17 00:00:00 2001 From: Elshiekh Ahmed Date: Mon, 13 Apr 2026 16:15:54 -0700 Subject: [PATCH 4/6] Unable to extract product image from packed MSIXVC2 .msixvc, so will set a place holder image anytime MSIXVC2 .msixvc package is detected from legacy Upload path --- .../Resources/Images/PackagePlaceholder.png | Bin 0 -> 672 bytes .../ViewModel/PackageUploadViewModel.cs | 1 + 2 files changed, 1 insertion(+) create mode 100644 src/PackageUploader.UI/Resources/Images/PackagePlaceholder.png diff --git a/src/PackageUploader.UI/Resources/Images/PackagePlaceholder.png b/src/PackageUploader.UI/Resources/Images/PackagePlaceholder.png new file mode 100644 index 0000000000000000000000000000000000000000..aa12d3e2a3da9a422cf72009e568f1d4b858b432 GIT binary patch literal 672 zcmeAS@N?(olHy`uVBq!ia0vp^(?FPm2}o{Eezui?fhpb7#WAE}&f8m#d3Ov17#!`- z{IB=AH!+&e(^2vQBC5epE=dSA?{asbX__obJ{!k6aXT>w^WHHRWf9$=o zyLbMsmroyOpZ+g#=BDZpe_Jmdy7EVgL;v=x46%pr9?t#v zn^U|_%IU`>aow6n{c&9d*?;EsUwpu6yqRNtz2Kp4{`WoaYrdDyYyPn5<8luFza}jo zPKooLdtW24KK4^i?|p9bwLies+{yvXO@ldpr1qYa1;kd$){Md%3k-u!>EWAB8 TGG|=_raT5uS3j3^P6 Date: Mon, 13 Apr 2026 16:41:44 -0700 Subject: [PATCH 5/6] When uploading an MSIXVC2 .msixvc package through Upload a Package legacy path, reroutes to makepkg2 upload as well as its appropriate progress screen --- ...xtractIdInformationFromValidatorLogTest.cs | 2 +- .../ViewModel/GenerateUploaderConfigTest.cs | 2 +- .../ViewModel/PackageUploadViewModelTest.cs | 14 ++-- .../TestablePackageUploadViewModel.cs | 2 +- .../ViewModel/PackageUploadViewModel.cs | 78 ++++++++++++++++++- 5 files changed, 89 insertions(+), 9 deletions(-) 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/ViewModel/PackageUploadViewModel.cs b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs index 9c94c93e..4b8da008 100644 --- a/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs +++ b/src/PackageUploader.UI/ViewModel/PackageUploadViewModel.cs @@ -33,6 +33,7 @@ public partial class PackageUploadViewModel : BaseViewModel private readonly IWindowService _windowService; public readonly UploadingProgressPercentageProvider _uploadingProgressPercentageProvider; private readonly ErrorModelProvider _errorModelProvider; + private readonly PathConfigurationProvider _pathConfigurationService; private GameProduct? _gameProduct = null; private IReadOnlyCollection? _branchesAndFlights = null; @@ -439,13 +440,15 @@ public PackageUploadViewModel(PackageModelProvider packageModelService, IPackageUploaderService uploaderService, IWindowService windowService, UploadingProgressPercentageProvider uploadingProgressPercentageProvider, - ErrorModelProvider errorModelProvider) + ErrorModelProvider errorModelProvider, + PathConfigurationProvider pathConfigurationService) { _packageModelService = packageModelService; _uploaderService = uploaderService; _windowService = windowService; _uploadingProgressPercentageProvider = uploadingProgressPercentageProvider; _errorModelProvider = errorModelProvider; + _pathConfigurationService = pathConfigurationService; // Initialize commands with RelayCommand UploadPackageCommand = new RelayCommand(UploadPackageProcessAsync, () => IsUploadReady()); @@ -923,6 +926,13 @@ private async void GetProductInfoAsync() private async void UploadPackageProcessAsync() { + // For MSIXVC2 packages, reroute to makepkg2 upload + if (IsMsixvc2Package) + { + StartMsixvc2Upload(); + return; + } + System.Windows.Application.Current.Dispatcher.Invoke(() => { _windowService.NavigateTo(typeof(PackageUploadingView)); @@ -1065,6 +1075,72 @@ private void OnCancelButton() _windowService.NavigateTo(typeof(MainPageView)); } + /// + /// Reroutes MSIXVC2 .msixvc package upload to makepkg2 upload tool. + /// Sets PackageModel properties and navigates to the MSIXVC2 uploading progress screen. + /// + private void StartMsixvc2Upload() + { + string makePkg2Path = _pathConfigurationService.MakePkg2Path; + if (string.IsNullOrEmpty(makePkg2Path) || !File.Exists(makePkg2Path)) + { + SetErrorAndGoToErrorPage("makepkg2 Not Found", + "makepkg2.exe was not found. Please install the Microsoft.Xbox.Packaging.Tools.makepkg2 NuGet package."); + return; + } + + string uploadArgs = BuildMsixvc2UploadArguments(); + + IGamePackageBranch? branchOrFlight = GetBranchOrFlightFromUISelection(); + + Package.BigId = BigId; + Package.PackageType = PackageType; + Package.PackagePreviewImage = PackagePreviewImage; + Package.PackageName = ProductName; + Package.Destination = BranchOrFlightDisplayName; + Package.Market = MarketGroupName; + Package.PackageIdentityName = PackageIdentityName; + Package.FolderSize = PackageSize; + Package.UploadArguments = uploadArgs; + Package.MakePkg2Path = makePkg2Path; + if (branchOrFlight != null) + { + Package.BranchId = branchOrFlight.CurrentDraftInstanceId; + } + + _windowService.NavigateTo(typeof(Msixvc2UploadingView)); + } + + internal string BuildMsixvc2UploadArguments() + { + string packageDir = Path.GetDirectoryName(PackageFilePath) ?? string.Empty; + var args = $"upload /pd \"{packageDir}\""; + + if (BranchOrFlightDisplayName.StartsWith("Branch: ")) + { + string branchName = BranchOrFlightDisplayName[(BranchOrFlightDisplayName.IndexOf(':') + 2)..]; + args += $" /branch \"{branchName}\""; + } + else if (BranchOrFlightDisplayName.StartsWith("Flight: ")) + { + string flightName = BranchOrFlightDisplayName[(BranchOrFlightDisplayName.IndexOf(':') + 2)..]; + args += $" /flight \"{flightName}\""; + } + + if (!string.IsNullOrEmpty(MarketGroupName)) + { + args += $" /market \"{MarketGroupName}\""; + } + + if (!string.IsNullOrEmpty(BigId) && BigId != "None") + { + args += $" /storeid \"{BigId}\""; + } + + args += " /auth CacheableBrowser"; + return args; + } + private void SetErrorAndGoToErrorPage(string errorTitle, string errorDescription) { _errorModelProvider.Error.MainMessage = errorTitle; From 4cc19cddf25678b8fe4db8baa9a856a3aa2926cb Mon Sep 17 00:00:00 2001 From: Elshiekh Ahmed Date: Tue, 14 Apr 2026 11:20:01 -0700 Subject: [PATCH 6/6] If makepkg2 tool not installed, when MSIXVC2 .msixvc package detected for "Upload a package" legacy path, "Upload" button will be grayed out and error message appears --- .../View/PackageUploadView.xaml | 16 +++++++--- .../ViewModel/PackageUploadViewModel.cs | 30 ++++++++++++++++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/PackageUploader.UI/View/PackageUploadView.xaml b/src/PackageUploader.UI/View/PackageUploadView.xaml index dc01b8a7..be089404 100644 --- a/src/PackageUploader.UI/View/PackageUploadView.xaml +++ b/src/PackageUploader.UI/View/PackageUploadView.xaml @@ -610,8 +610,8 @@ - -