GamePassStorage 0.1.1

dotnet add package GamePassStorage --version 0.1.1
                    
NuGet\Install-Package GamePassStorage -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GamePassStorage" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GamePassStorage" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="GamePassStorage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GamePassStorage --version 0.1.1
                    
#r "nuget: GamePassStorage, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GamePassStorage@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GamePassStorage&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=GamePassStorage&version=0.1.1
                    
Install as a Cake Tool

GamePassStorage

A game-agnostic .NET library for reading and writing Xbox Connected Storage ("wgs") save folders, the format Game Pass (Microsoft Store / Xbox app) PC titles use for their saves.

It handles the container layer only: containers.index, container.N manifests (one blob or several named blobs per container) and GUID-named blobs, ETag and sync-state rules, snapshots, orphan discovery, write ordering and atomic file replacement. What is inside a blob (compression, bundles, save classes) belongs to the game and lives in a game adapter: one ships for Abiotic Factor, more can load as plugins, and a generic model works for every other title. The library has no package dependencies and no game code.

What it can do

  • Read and write containers and their blobs byte-for-byte, with multi-blob containers: read all blobs by name, replace one blob while the others stay byte-identical, create a multi-blob container.
  • Find stores on the machine (%LOCALAPPDATA%\Packages\...\wgs\... and XboxGames\GameSave\wgs), filtered by package family name.
  • Delete a container: never-uploaded ones are removed, ones the cloud knows become a Deleted tombstone that keeps its ETag.
  • Restore a whole-folder backup, with a safety copy of the current store first.
  • Export every container's blobs to a folder with a manifest, and import a folder back.
  • Gate writes structurally and by process (refuse while the game runs), composable.
  • Inspect what a blob holds through a game adapter, or generically (size, SHA-256, sniffing).
  • Snapshot and compare a store around a cloud sync; diagnose and explicitly repair.

It was extracted from Abiotic Editor, which uses it for Abiotic Factor's Game Pass saves. That is the only title verified so far; see Supported titles.

Install

dotnet add package GamePassStorage          # the library
dotnet add package GamePassStorage.Adapters.AbioticFactor   # optional: the Abiotic Factor adapter
dotnet tool install -g GamePassStorage.Tool # the `wgs` command-line tool (adapters built in)

Command-line tool

wgs list      <store> [--json]                  # containers, states, sizes, ETags
wgs diagnose  <store> [--json]                  # health report; exits 1 when writes are blocked
wgs extract   <store> <container> <out-file>    # copy a blob out
wgs backup    <store> <destination>             # whole-folder copy
wgs snapshot  <store> -o before.json            # SHA-256 fingerprint of every container
wgs compare   before.json after.json            # what a cloud sync changed
wgs put       <store> <container> <blob> --backup <dir> [--dry-run]
wgs find      [--package <text>] [--exact] [--json]   # find wgs stores on this machine
wgs blobs     <store> <container> [--json]            # blobs inside a container
wgs delete    <store> <container> --backup <dir> [--dry-run]
wgs restore   <store> <backup-folder> --backup <dir> [--dry-run]
wgs adapters  [--json]                                # game adapters in use
wgs inspect   <store> [<container>] [--json]          # what containers hold (adapter or generic)
wgs export    <store> <out-folder>                    # every container's blobs + manifest
wgs import    <store> <folder> --backup <dir> [--dry-run]

put, delete, restore and import write. Each refuses without a backup folder (or --dry-run), takes the backup first, and goes through the same write gate and concurrent-change check as the library. --refuse-if-running <name> adds a process check. list and diagnose say which game adapter matched the store.

Game adapters

wgs ships with the adapters in this repository (today Abiotic Factor) and loads more from --adapters <dir> or the WGS_ADAPTERS_DIR environment variable. When two adapters match, the more specific one wins: an exact package family name beats a looser match. --no-builtin-adapters forces the generic model. Plugins are ordinary .NET code and run with full trust: load only assemblies you built or trust.

wgs adapters
wgs inspect <store>                 # world bundle table of contents, decoded settings ini, ...
wgs inspect <store> --no-builtin-adapters   # the generic view: sizes, SHA-256, sniffing

Library quick start

using GamePassStorage;

var opened = WgsStore.TryOpen(@"C:\Users\me\AppData\Local\Packages\<family>\SystemAppData\wgs\<xuid>_<scid>");
if (!opened.Succeeded) { Console.WriteLine(opened.Message); return; }
var store = opened.Store!;

foreach (var container in store.Containers)
    Console.WriteLine($"{container.Name}  {container.State}  {container.BlobSize} bytes");

var save = store.Find("MySave")!;
var read = store.TryReadBlob(save);            // Ok, MissingBlob, SyncInFlight, UnsupportedLayout, ...
if (read.Succeeded)
{
    var edited = EditMyGameSave(read.Blob!);   // your game-specific code
    store.CopyStoreTo(@"D:\backups\wgs-before-edit"); // whole-folder backup
    var commit = store.TryWriteBlob(save, edited); // Ok, Refused, LockConflict, ConcurrentChange, Failed
    Console.WriteLine(commit.Status);
}

Extension points

All services are injected through WgsStoreOptions; every member has a real default.

Interface Purpose
IWgsFileSystem File access. Swap in an in-memory implementation for tests or fault injection.
IWgsClock Time source for the index and entry FILETIMEs.
IWgsLog Receives what the store does.
IWgsBlobInspector Game adapter: recognises a blob's payload, to label orphaned data and suggest a container name.
IWgsWriteGate Decides whether a write may proceed. WgsWriteGates.Structural (the default) refuses unresolved cloud conflicts and undefined container states. WgsWriteGates.RefuseWhileRunning("MyGame*") refuses while a process runs, and WgsWriteGates.Combine(...) composes gates.
IWgsGameAdapter A game adapter: matches a package family, classifies containers, describes blob contents, supplies an inspector, gate and codec. Resolved by WgsGameAdapterRegistry; GenericWgsAdapter is the always-last fallback.
IWgsProcessLister Lists running processes, so a process gate is testable.

Safety rules the library enforces

  • ETags are only ever echoed, never minted. A container with an ETag becomes Modified on write; one without stays Created.
  • The index FILETIME strictly advances on every write, and FullyUploaded is cleared. An unresolved-conflict flag is never cleared locally.
  • A write goes blob, then manifest, then index, each through an atomic replace, so a crash leaves the previous generation fully described.
  • A store changed on disk after it was opened is refused (ConcurrentChange) before anything is written.
  • Multi-blob writes replace only the blobs they name; untouched blobs keep their files and ids, and a write that would misstate an untouched blob is refused.
  • Deleting never invents state: a never-uploaded container is removed, a cloud-known one becomes a tombstone with its ETag untouched. Restore and import go through the same gate, stamp the index strictly newer, and never apply an ETag from a file.
  • Repair is an explicit operation (ContainersNeedingRepair, RepairRecoveredManifests); reading never repairs silently.

Local file access does not give control over Xbox cloud sync. Close the game (and ideally go offline) before writing, and let the title upload the change on its next launch.

Format reference

docs/wgs-format.md documents the byte layouts, state and sync-flag meanings, read and write rules, sources, and what is still unverified.

Supported titles

Title Read Write Evidence
Abiotic Factor Yes Yes Shipped adapter (GamePassStorage.Adapters.AbioticFactor, built into wgs); real sanitized stores and in-game use through Abiotic Editor
Any other title Unverified Unverified Works through the generic model; needs real sanitized fixtures before a support claim

The in-memory tests exercise the API boundary with synthetic stores; they are not evidence that a particular game's layout is supported. Multi-blob manifests are implemented from public sources (XGP-save-extractor, libNOM.io, XblContainerReader, GPSaveConverter) but there is no real multi-blob fixture yet. WgsStore reports layouts it does not understand as UnsupportedLayout rather than guessing.

Adding a game

A game adapter is a project src/GamePassStorage.Adapters.<Game> that references only the library and implements IWgsGameAdapter. The Abiotic Factor adapter is the template. See Game adapters (plugins) and Contributing for the project layout, registration in the tool's built-in set, tests and the fixtures policy.

Building

Requires the .NET 10 SDK.

dotnet build GamePassStorage.slnx
dotnet test  GamePassStorage.slnx

Releasing

Pushing a version tag tests on Windows and Linux, publishes all three packages to nuget.org (GamePassStorage, GamePassStorage.Adapters.AbioticFactor and GamePassStorage.Tool), and creates a GitHub release:

git tag v0.1.0
git push origin v0.1.0

The workflow (.github/workflows/publish.yml) uses NuGet trusted publishing (OIDC), so no API key is stored. nuget.org needs a trusted publishing policy for this repository, publish.yml and the production environment; set the NUGET_USER repository variable if the nuget.org account name differs from the repository owner. It can also be run by hand from the Actions tab with a version number.

License

Apache-2.0. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on GamePassStorage:

Package Downloads
GamePassStorage.Adapters.AbioticFactor

Game adapter for Abiotic Factor's Game Pass saves: classifies containers, reads a world bundle's table of contents without decompressing it, decodes the settings ini, names orphaned worlds and refuses writes while the game runs.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 35 10/3/2026
0.1.0 41 10/3/2026