Skip to content

Save File Parsing

Chrison Simtian edited this page May 17, 2026 · 1 revision

Save File Parsing

The planner ingests live factory state directly from your Satisfactory .sav file. Parsing happens in-process via a forked copy of SatisfactorySaveNet; the parsed actor graph is then projected into game-agnostic ERP.Domain entities by Satisfactory.Save.

The fork

Upstream Our fork
Repo R3dByt3/SatisfactorySaveNet ChrisonSimtian/SatisfactorySaveNet
Distribution NuGet.org GitHub Packages (auth required, even for public)
Vendored at vendor/SatisfactorySaveNet/ (update=none, optional)
Consumed as PackageReference Version="4.1.3" from src/Satisfactory/Save/Satisfactory.Save.csproj

The fork is the source of truth until upstream catches up; see ADR-0014.

What the fork adds on top of upstream

Area Change Tracking
Save format v1.2 (SaveVersion 60) TOC / Data Blob structure ported; complete-tag flow at SerializeDataPackageVersionAndCustomVersions = 53 upstream issue #33
v1.2 deep-parse ObjectProperty, ArrayProperty<ObjectProperty>, StrProperty, scalars, fixed-size structs, simple maps surfaced on RawProperty upstream PR pending
v1.2 ArrayProperty<StructProperty> New RawProperty.ArrayStructValues slot — unlocks pipe mSplineData (Array<Struct<FSplinePointData>>) PR #8
Chain-actor v1.2 fallback Graceful recovery when FGConveyorChainActor ExtraData diverges from the synthesized v1.2 shape PR #7
Continuous publish Every push to the fork's main republishes a new version fork issue #103

GitHub Packages auth always requires a token (even for public packages). The build picks it up from $GITHUB_TOKEN; nuget.config is wired to the fork's feed with packageSourceMapping so the right source is used per package.

Working on the fork

# Populate the submodule (it's not on the build path by default)
git submodule update --init --checkout vendor/SatisfactorySaveNet

cd vendor/SatisfactorySaveNet
# Make changes, run the fork's tests:
DOTNET_ROLL_FORWARD=LatestMajor dotnet test SatisfactorySaveNet.Tests/SatisfactorySaveNet.Tests.csproj -c Release

Every push to the fork's main triggers a continuous publish — the new version is available on GitHub Packages within ~1m of the merge. Bump the app's PackageReference in src/Satisfactory/Save/Satisfactory.Save.csproj once it lands.

App-side wiring

Satisfactory.Save wraps the parser into domain entities:

  • SaveFileReader.Read(savePath) — entry point. Returns LiveFactoryState (metadata + lists of resource nodes, miners, buildings, belts, pipelines, generators, warnings).
  • BuildingIdentifiers — type-path → game-agnostic BuildingId, MinerTier, BeltTier, PipelineTier, GeneratorKind.
  • PropertyExtensions — typed accessors over the fork's RawProperty (TryGetFloat, TryGetObjectPath, TryGetString, TryGetInt, TryGetArrayStructValues).
  • KnownResourceNodes / KnownFlora — embedded JSON lookups for resource node identity and purity (the save doesn't tell us which iron node we're parsing — coordinates do).

Recently added: pipe polylines (v0.4)

Pipes carry their route in mSplineData (Array<Struct<FSplinePointData>>). Until the fork landed the v1.2 Array<Struct> deep-parse, the planner only had each pipe's origin and had to render them as points. Now SaveFileReader.ExtractPipePolyline walks the spline data and Pipeline.Polyline carries the full LineString, matching how conveyor belts already worked via FGConveyorChainActor.

Pipes without spline data (legacy saves, isolated actors) keep Polyline = null and fall back to point-only rendering.

Troubleshooting

  • 401 Unauthorized from GitHub Packages during restore$GITHUB_TOKEN is missing or lacks read:packages. Run gh auth refresh -h github.com -s read:packages and re-export.
  • "Unsupported body version: " — the parser ran but the save body isn't BodyV8. v1.2 saves should always be BodyV8; older saves are not supported.
  • Empty belt polylinesFGConveyorChainActor's ExtraData diverged from the synthesized v1.2 shape; the fork falls back gracefully but you'll see point-only rendering. See fork PR #7.
  • Unknown actor warnings — mod-added actors not in the fork's class registry. Safe to ignore unless they're load-bearing for your factory.

Clone this wiki locally