From 5fa756d3e7b76d6a3c971c13641f87569c7606ad Mon Sep 17 00:00:00 2001 From: Ram Choudhary Date: Sat, 9 May 2026 22:59:37 +0530 Subject: [PATCH 1/2] feat: Add SODB asset upload support for XVC packages Adds support for uploading SODB (Shader Object Database) binary assets as part of XVC package uploads. This enables 1PP teams to include SODB files alongside their packages. Changes: - Added SodbFile enum value to GamePackageAssetType - Added SodbBinary API type mapping in extensions - Added SodbFilePath property to GameAssets model - Added SODB file validation, file list, and upload in service - Updated JSON schema, template, README, and Operations docs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Operations.md | 1 + README.md | 5 +++-- schemas/PackageUploaderOperationConfigSchema.json | 4 ++++ .../Extensions/GamePackageAssetTypeExtensions.cs | 1 + .../Client/Ingestion/Models/GamePackageAssetType.cs | 1 + src/PackageUploader.ClientApi/Models/GameAssets.cs | 2 ++ src/PackageUploader.ClientApi/PackageUploaderService.cs | 9 +++++++++ templates/UploadXvcPackage.json | 3 ++- 8 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Operations.md b/Operations.md index 013bad67..87535bf9 100644 --- a/Operations.md +++ b/Operations.md @@ -89,6 +89,7 @@ - **subValFilePath**: required - path to the SubVal File - **symbolsFilePath**: optional - path to the Symbols File - **discLayoutFilePath**: optional - path to the Disc Layout File + - **sodbFilePath**: optional - path to the SODB (Shader Object Database) file - **minutesToWaitForProcessing**: optional (default 30) - it will check the package processing status every minute for this long, until it succeeds or fails - **availabilityDate**: optional - if informed it will configure custom availability date for your XVC/MSIXVC packages [Learn more](http://go.microsoft.com/fwlink/?LinkId=825239) - **isEnabled**: optional (default false) - it will enable/disable custom availability date diff --git a/README.md b/README.md index 383fe170..8c919821 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ The following table has important arguments for running Package Uploader. | **[GetProduct](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#GetProduct)** | Gets metadata for the product. This is useful for getting the productId, BigId, and product name that's used in all configuration files. This also gets a list of the BranchFriendlyNames and FlightNames of the product. | | **[GetPackages](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#GetPackages)** | Gets a list of the packages in a branch or flight. | | **[UploadUwpPackage](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#UploadUwpPackage)** | Uploads a UWP game package. | -| **[UploadXvcPackage](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#UploadXvcPackage)** | Uploads an XVC game package and assets, including EKB, SubVal, and layout files. | +| **[UploadXvcPackage](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#UploadXvcPackage)** | Uploads an XVC game package and assets, including EKB, SubVal, layout, and SODB files. | | **[RemovePackages](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#RemovePackages)** | Removes game packages and assets from a branch. We recommend keeping only your 10 most recent packages to ensure optimal performance. | | **[ImportPackages](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#ImportPackages)** | Imports all game packages from a branch to a destination branch. Use this operation to copy your previously uploaded and published packages from one branch to another. | | **[PublishPackages](https://github.com/microsoft/PackageUploader/blob/main/Operations.md#PublishPackages)** | Publishes all game packages from a branch or flight to a destination sandbox or flight. You can set specific availability times in the configuration file. | @@ -295,7 +295,8 @@ Product: { "ekbFilePath": "C:\\Users\\someone\\Desktop\\StubPackage\\Builds\\ TestPublisher.SomeDemoProductName_0.9.1.0_x64__fjtqkg6rpm1hy_Full_33ec8436-5a0e-4f0d-b1ce-3f29c3955039.ekb", "subValFilePath": "C:\\Users\\someone\\Desktop\\StubPackage\\Builds\\Validator_ TestPublisher.SomeDemoProductName_0.9.1.0_x64__fjtqkg6rpm1hy.xml", "symbolsFilePath": "", - "discLayoutFilePath": "" + "discLayoutFilePath": "", + "sodbFilePath": "" }, "minutesToWaitForProcessing": 60, diff --git a/schemas/PackageUploaderOperationConfigSchema.json b/schemas/PackageUploaderOperationConfigSchema.json index 6b0974d0..42eb3c02 100644 --- a/schemas/PackageUploaderOperationConfigSchema.json +++ b/schemas/PackageUploaderOperationConfigSchema.json @@ -338,6 +338,10 @@ "discLayoutFilePath": { "description": "path to the Disc Layout File", "type": "string" + }, + "sodbFilePath": { + "description": "path to the SODB (Shader Object Database) file", + "type": "string" } }, "required": [ diff --git a/src/PackageUploader.ClientApi/Client/Ingestion/Extensions/GamePackageAssetTypeExtensions.cs b/src/PackageUploader.ClientApi/Client/Ingestion/Extensions/GamePackageAssetTypeExtensions.cs index ce1d95b2..d52c028a 100644 --- a/src/PackageUploader.ClientApi/Client/Ingestion/Extensions/GamePackageAssetTypeExtensions.cs +++ b/src/PackageUploader.ClientApi/Client/Ingestion/Extensions/GamePackageAssetTypeExtensions.cs @@ -14,6 +14,7 @@ public static string GetGamePackageAssetType(this GamePackageAssetType gamePacka GamePackageAssetType.SubmissionValidatorLog => "EraSubmissionValidatorLog", GamePackageAssetType.SymbolsZip => "EraSymbolFile", GamePackageAssetType.DiscLayoutFile => "EraSubsetFile", + GamePackageAssetType.SodbFile => "SodbBinary", _ => string.Empty, }; } \ No newline at end of file diff --git a/src/PackageUploader.ClientApi/Client/Ingestion/Models/GamePackageAssetType.cs b/src/PackageUploader.ClientApi/Client/Ingestion/Models/GamePackageAssetType.cs index f2dff58e..6962b758 100644 --- a/src/PackageUploader.ClientApi/Client/Ingestion/Models/GamePackageAssetType.cs +++ b/src/PackageUploader.ClientApi/Client/Ingestion/Models/GamePackageAssetType.cs @@ -9,4 +9,5 @@ public enum GamePackageAssetType SubmissionValidatorLog, SymbolsZip, DiscLayoutFile, + SodbFile, } \ No newline at end of file diff --git a/src/PackageUploader.ClientApi/Models/GameAssets.cs b/src/PackageUploader.ClientApi/Models/GameAssets.cs index 9b03372c..01860bf2 100644 --- a/src/PackageUploader.ClientApi/Models/GameAssets.cs +++ b/src/PackageUploader.ClientApi/Models/GameAssets.cs @@ -16,4 +16,6 @@ public class GameAssets public string SymbolsFilePath { get; set; } public string DiscLayoutFilePath { get; set; } + + public string SodbFilePath { get; set; } } \ No newline at end of file diff --git a/src/PackageUploader.ClientApi/PackageUploaderService.cs b/src/PackageUploader.ClientApi/PackageUploaderService.cs index de8cf34c..3a61643a 100644 --- a/src/PackageUploader.ClientApi/PackageUploaderService.cs +++ b/src/PackageUploader.ClientApi/PackageUploaderService.cs @@ -211,6 +211,10 @@ public async Task UploadGamePackageAsync( { throw new FileNotFoundException("Disc Layout file not found.", gameAssets.DiscLayoutFilePath); } + if (!string.IsNullOrEmpty(gameAssets.SodbFilePath) && !File.Exists(gameAssets.SodbFilePath)) + { + throw new FileNotFoundException("SODB file not found.", gameAssets.SodbFilePath); + } } // Calculate total size of all files to be uploaded @@ -233,6 +237,10 @@ public async Task UploadGamePackageAsync( { files.Add(new FileInfo(gameAssets.DiscLayoutFilePath)); } + if (!string.IsNullOrEmpty(gameAssets.SodbFilePath) && File.Exists(gameAssets.SodbFilePath)) + { + files.Add(new FileInfo(gameAssets.SodbFilePath)); + } } currentProgress.Stage = PackageUploadingProgressStage.ComputingDeltas; progress?.Report(currentProgress); @@ -323,6 +331,7 @@ void ReportProgress(ulong bytesUploaded) await UploadAssetAsync(product, package, gameAssets.SymbolsFilePath, GamePackageAssetType.SymbolsZip, bytesProgress, ct); await UploadAssetAsync(product, package, gameAssets.SubValFilePath, GamePackageAssetType.SubmissionValidatorLog, bytesProgress, ct); await UploadAssetAsync(product, package, gameAssets.DiscLayoutFilePath, GamePackageAssetType.DiscLayoutFile, bytesProgress, ct); + await UploadAssetAsync(product, package, gameAssets.SodbFilePath, GamePackageAssetType.SodbFile, bytesProgress, ct); } // Set progress to 100% when complete diff --git a/templates/UploadXvcPackage.json b/templates/UploadXvcPackage.json index c6cb7187..66c49abb 100644 --- a/templates/UploadXvcPackage.json +++ b/templates/UploadXvcPackage.json @@ -18,7 +18,8 @@ "ekbFilePath": "{{ required }}", "subValFilePath": "{{ required }}", "symbolsFilePath": "{{ optional }}", - "discLayoutFilePath": "{{ optional }}" + "discLayoutFilePath": "{{ optional }}", + "sodbFilePath": "{{ optional }}" }, "minutesToWaitForProcessing": 60, From 4ce5b996bd6ff07ed4ba808063050877a8ca53ec Mon Sep 17 00:00:00 2001 From: Ram Choudhary Date: Thu, 14 May 2026 22:40:40 +0530 Subject: [PATCH 2/2] feat: Restrict SODB asset upload to MSIXVC packages only SODB (Shader Object Database) is only applicable to MSIXVC-format packages. Adds an up-front validation guard in PackageUploaderService.UploadGamePackageAsync that throws InvalidOperationException when sodbFilePath is set but the package is not an .msixvc file. Empty/null sodbFilePath remains a no-op for any package type, so shared templates continue to work for both .xvc and .msixvc uploads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PackageUploaderService.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/PackageUploader.ClientApi/PackageUploaderService.cs b/src/PackageUploader.ClientApi/PackageUploaderService.cs index 3a61643a..87413efc 100644 --- a/src/PackageUploader.ClientApi/PackageUploaderService.cs +++ b/src/PackageUploader.ClientApi/PackageUploaderService.cs @@ -211,9 +211,17 @@ public async Task UploadGamePackageAsync( { throw new FileNotFoundException("Disc Layout file not found.", gameAssets.DiscLayoutFilePath); } - if (!string.IsNullOrEmpty(gameAssets.SodbFilePath) && !File.Exists(gameAssets.SodbFilePath)) + if (!string.IsNullOrEmpty(gameAssets.SodbFilePath)) { - throw new FileNotFoundException("SODB file not found.", gameAssets.SodbFilePath); + if (!packageFilePath.EndsWith(".msixvc", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException( + $"SODB asset is only supported for MSIXVC packages (.msixvc). Package file '{Path.GetFileName(packageFilePath)}' is not an MSIXVC package."); + } + if (!File.Exists(gameAssets.SodbFilePath)) + { + throw new FileNotFoundException("SODB file not found.", gameAssets.SodbFilePath); + } } }