Documentation of the Satisfactory save game file structure.
- Introduction
- General file structure
- Decompressed binary data
- Objects
- Properties
- Structs
- Type and Object Reference
- Basic types
- Containers
- Unreal Objects
- Satisfactory Objects
- FObjectReferenceDisc
- FFGDynamicStruct
- FWorldPartitionValidationData
- FWPGridValidationData
- FPerStreamingLevelSaveData
- FPersistentAndRuntimeSaveData
- FObjectBaseSaveHeader
- FObjectSaveHeader
- FActorSaveHeader
- FUnresolvedWorldSaveData
- FInventoryItem
- FConveyorBeltItem
- FConveyorBeltItems
- FConveyorChainSplineSegment
- FSplinePointData
- FRuntimeBuildableInstanceData
- FUniqueNetIdRepl
- FVehiclePhysicsData
- FTopLevelAssetPath
- FBlueprintItemAmount
- Blueprints
Satisfactory is a game from Coffee Stain Studios based on the Unreal Engine.
Many data structures in the save game are based on the Unreal serialization system.
Satisfactory uses a few data structures specific to the game.
Most of them can be found in the C++ headers, distributed within the CommunityResources
folder in the game's installation directory.
This documentation tries to use the original Unreal/Satisfactory type names as closely as possible. The structure of all types and objects used in this document is referenced at the end, see Type and Object Reference.
All binary data is encoded little-endian in the save.
Document Version: Satisfactory 1.0
The save file starts with a save header, followed by chunks of zlib compressed data. Each chunk starts with a chunk header followed by the binary chunk data.
+--------------+
| Save Header |
+--------------+
| Chunk Header |
+--------------+
| Chunk Data |
+--------------+
| Chunk Header |
+--------------+
| Chunk Data |
+--------------+
| ... |
+--------------+
The save header has the following structure:
+----------+-----------------------+
| int32 | SaveHeaderVersion |
| int32 | SaveVersion |
| int32 | BuildVersion |
| FString | MapName |
| FString | MapOptions |
| FString | SessionName |
| int32 | PlayDurationSeconds |
| int64 | SaveDateTime |
| int8 | SessionVisibility |
| int32 | EditorObjectVersion |
| FString | ModMetadata |
| bool | IsModdedSave |
| FString | SaveIdentifier |
| bool | IsPartitionedWorld |
| FMD5Hash | SaveDataHash |
| bool | IsCreativeModeEnabled |
+----------+-----------------------+
This is the save header as of Update 8 and 1.0.
In the past, the header was shorter, but additional values were added with updates.
Each time this struct is extended, the SaveHeaderVersion
value increases, the current value is 13
.
Internally, an enum Type
is used for this number, see FGSaveManagerInterface.h
distributed with the game files.
The variable names are taken from the struct FSaveHeader
in FGSaveManagerInterface.h
.
SaveDateTime
is the serialization of an FDateTime object.
Ticks since 0001-01-01 00:00, where 1 tick is 100 nanoseconds. Satisfactory seems to use the UTC time zone.
Source Reference:
FGSaveManagerInterface.h
-struct FSaveHeader
The save data is stored in a binary structure (see below). This binary structure is divided into chunks, which are then individually compressed with zlib. The division into chunks is done purely on size and has nothing to do with the serialized content within this binary structure. In the save data, each chunk is prefixed by a header, followed by the compressed binary data.
The chunk header has the following structure:
+---------------+---------------------------+----------------------------------------------+
| int32 | PACKAGE_FILE_TAG | always `0x9E2A83C1` |
| int32 | archive header | 0x00000000: v1 header, 0x22222222: v2 header |
| int64 | max chunk size | always `131072` |
| if v2 header: | | |
| uint8 | CompressorNum | compression algorithm, 3: zlib |
| int64 | compressed size summary | compressed buffer size |
| int64 | uncompressed size summary | uncompressed buffer size |
| int64 | compressed size | compressed buffer size |
| int64 | uncompressed size | uncompressed buffer size |
+---------------+---------------------------+----------------------------------------------+
PACKAGE_FILE_TAG
is a constant magic number.
archive header
was introduced with the Unreal 5 upgrade and marks if CompressorNum exists.
max chunk size
is the maximum size used for (uncompressed) chunks.
This is a hardcoded constant from Unreal.
CompressorNum
is the index of the used compression algorithm, which currently seems to be always zlib (value = 3).
compressed size
refers to the size of the compressed chunk data within the save file.
uncompressed size
is the size of the chunk data after decompression with zlib.
All chunks, except the last one, use max chunk size
as uncompressed size.
The sizes are stored twice with identical values (see below).
After decompression, all uncompressed chunk buffers are merged into a single large binary object, defined in the next section.
Unreal internal details:
Unreal can serialize an archive to a compressed structure. Unreal uses the struct FCompressedChunkInfo to store chunk header information. It is a pair of two int64 sizes. A compressed archive starts with an FCompressedChunkInfo containing the PACKAGE_FILE_TAG and COMPRESSION_CHUNK_SIZE. Next is an FCompressedChunkInfo with the summary compressed and uncompressed size of all data in this archive. That is followed by a series of FCompressedChunkInfo with the current chunks' size and the actual compressed chunk data. There are as many chunks until the summary size is reached. But instead of using a single archive for the binary blob, Satisfactory uses the FArchiveLoadCompressedProxy to store the big binary structure. Here, the data is not stored in a single archive but multiple archives containing just a single chunk. Probably, this structure has the advantage of decompressing the large buffer internally step by step and not all at once. Nevertheless, this explains why the buffer size is stored twice in the chunk header.
With Update 6 and again Update 8, the format changed quite a bit; only the new format will be described here. The new format stores data for each level separately, where a level refers to a part of the game map.
The binary data layout follows the following format:
+-------------------------------------------+------------------------------------------------------+
| int64 | total size of binary data (not including this value) |
| FWorldPartitionValidationData | SaveGameValidationData |
| TMap<FString, FPerStreamingLevelSaveData> | mPerLevelDataMap |
| FPersistentAndRuntimeSaveData | mPersistentAndRuntimeData |
| FUnresolvedWorldSaveData | mUnresolvedWorldSaveData |
+-------------------------------------------+------------------------------------------------------+
Structs are defined in FGSaveSession.h
.
FString in mPerLevelDataMap
is the levelName
.
FPerStreamingLevelSaveData
and FPersistentAndRuntimeSaveData
contain binary buffers named TOCBlob64
and DataBlob64
, described in detail below.
Within these, all objects of a level are stored.
The result is a list of all objects TArray<UObject*> objects
.
However, information about each object is separated.
TOCBlob64
stores metainformation about each object, such as the class name.
DataBlob64
contains the internal data of each object, i.e., a list of properties.
Each object has a class name in the form /Game/FactoryGame/Buildable/Building/Foundation/Build_Foundation_8x4_01.Build_Foundation_8x4_01_C
and an additional instance name.
This allows for the interpretation of a save file in a hierarchical way, similar to a file system.
The size of each object varies. Therefore, the data must be parsed sequentially. Different objects are just written one by one after each other. The number of objects in the TOCBlob and DataBlob are the same (first object in TOCBlob corresponds to first object in DataBlob, ...).
+-----------------------------------------------------+---------------------------+
| int32 | numObjects |
| for i = 1 to numObjects: | |
| bool | isActor |
| if isActor: | |
| FActorSaveHeader | objectHeader |
| else: | |
| FObjectSaveHeader | objectHeader |
| if remaining data: | |
| if within FPerStreamingLevelSaveData: | |
| TArray<FObjectReferenceDisc> | DestroyedActors |
| if within FPersistentAndRuntimeSaveData: | |
| TMap<FString, TArray<FObjectReferenceDisc>> | LevelToDestroyedActorsMap |
+-----------------------------------------------------+---------------------------+
DestroyedActors
is not always present.
It needs to be determined if the TOCBlob buffer has already been completely read after parsing the object headers.
Internally, the object headers are stored as
FGenericObjectSaveHeader
. The serialization code can be found inFWPSaveDataMigrationContext.h
in the game headers.
+--------------------------+-------------------------------------+
| int32 | numObjects |
| for i = 1 to numObjects: | |
| int32 | SaveVersion |
| bool | ShouldMigrateObjectRefsToPersistent |
| TArray<uint8> | Data | binary object data
+--------------------------+-------------------------------------+
The size of the Data
array can be used to skip over the current object.
This allows only partially reading data of some objects or skipping of unknown objects.
Internally, this data is stored in the
FObjectSaveData
struct. The serialization code can be found inFWPSaveDataMigrationContext.h
in the game headers.
Each object corresponds to an instance of a class representing any type of object.
The state of each instance is serialized using the Unreal serialization system.
All objects use a common UObject
class as base, but each class can add additional binary save data.
However, most classes use the Unreal reflection system to store their properties.
Each UObject contains a List of Properties
that stores all properties that utilize that reflection system.
There are only a few classes that add custom binary data.
Parsing the List of Properties
is probably the most complex part of save parsing.
Therefore, the details are in a separate section below.
The size of the class-specific binary data is the remaining buffer, of which the total size is given by the containing TArray.
The second most commonly used class is AActor
.
If isActor
is true in the TOCBlob, the object is always an AActor
instance or a class derived from AActor
.
Unreal internal details:
The UObject class has a virtual methodSerialize
, that can be overwritten by child classes to serialize data. Each class that overwrites theSerialize
method usually calls the parentSerialize
method, so child classes usually only add additional data.
Note: The actual classes in the game source files may have additional classes in their inheritance tree. Here, only classes adding data to the serialization are mentioned.
+--------------------+------------+
| List of Properties | Properties |
| bool | HasGuid | (only observed false values so far)
| if HasGuid: | |
| FGuid | Guid |
+--------------------+------------+
+------------------------------+----------------+
| FObjectReferenceDisc | Owner |
| TArray<FObjectReferenceDisc> | Components |
| UObject | -> see UObject |
+------------------------------+----------------+
+--------------------+---------------+
| AActor | -> see AActor |
| FConveyorBeltItems | mItems |
+--------------------+---------------+
The following classes are instances of this:
/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk1/Build_ConveyorBeltMk1.Build_ConveyorBeltMk1_C
/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk2/Build_ConveyorBeltMk2.Build_ConveyorBeltMk2_C
/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk3/Build_ConveyorBeltMk3.Build_ConveyorBeltMk3_C
/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk4/Build_ConveyorBeltMk4.Build_ConveyorBeltMk4_C
/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk5/Build_ConveyorBeltMk5.Build_ConveyorBeltMk5_C
/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk6/Build_ConveyorBeltMk6.Build_ConveyorBeltMk6_C
/Game/FactoryGame/Buildable/Factory/ConveyorLiftMk1/Build_ConveyorLiftMk1.Build_ConveyorLiftMk1_C
/Game/FactoryGame/Buildable/Factory/ConveyorLiftMk2/Build_ConveyorLiftMk2.Build_ConveyorLiftMk2_C
/Game/FactoryGame/Buildable/Factory/ConveyorLiftMk3/Build_ConveyorLiftMk3.Build_ConveyorLiftMk3_C
/Game/FactoryGame/Buildable/Factory/ConveyorLiftMk4/Build_ConveyorLiftMk4.Build_ConveyorLiftMk4_C
/Game/FactoryGame/Buildable/Factory/ConveyorLiftMk5/Build_ConveyorLiftMk5.Build_ConveyorLiftMk5_C
/Game/FactoryGame/Buildable/Factory/ConveyorLiftMk6/Build_ConveyorLiftMk6.Build_ConveyorLiftMk6_C
+-------------------------------------+----------------------+
| AActor | -> see AActor |
| FObjectReferenceDisc | mFirstConveyor |
| FObjectReferenceDisc | mLastConveyor |
| TArray<FConveyorChainSplineSegment> | mChainSplineSegments |
| float | mTotalLength |
| int32 | mNumItems |
| int32 | mLeadItemIndex |
| int32 | mTailItemIndex |
| TArray<FConveyorBeltItem> | mConveyorChainItems |
+-------------------------------------+----------------------+
The following classes are instances of this:
/Script/FactoryGame.FGConveyorChainActor
/Script/FactoryGame.FGConveyorChainActor_RepSizeHuge
/Script/FactoryGame.FGConveyorChainActor_RepSizeLarge
/Script/FactoryGame.FGConveyorChainActor_RepSizeMedium
/Script/FactoryGame.FGConveyorChainActor_RepSizeNoCull
+-------------------------+---------------+
| AActor | -> see AActor |
| FObjectReferenceDisc[2] | mConnections | (fixed-sized array of length two)
+-------------------------+---------------+
The following classes are instances of this:
/Game/FactoryGame/Buildable/Factory/PowerLine/Build_PowerLine.Build_PowerLine_C
/Game/FactoryGame/Events/Christmas/Buildings/PowerLineLights/Build_XmassLightsLine.Build_XmassLightsLine_C
+-----------------------------------+---------------+
| AActor | -> see AActor |
| TMap<int32, FObjectReferenceDisc> | mCircuits |
+-----------------------------------+---------------+
The following classes are instances of this:
/Game/FactoryGame/-Shared/Blueprint/BP_CircuitSubsystem.BP_CircuitSubsystem_C
+-------------------------------------------------------------------+--------------------------------+
| AActor | -> see AActor |
| TMap<FObjectReferenceDisc, TArray<FRuntimeBuildableInstanceData>> | mBuildableClassToInstanceArray |
+-------------------------------------------------------------------+--------------------------------+
The following classes are instances of this:
/Script/FactoryGame.FGLightweightBuildableSubsystem
+------------------------------+------------------------+
| AActor | -> see AActor |
| TArray<FObjectReferenceDisc> | rawPlayerStatePointers |
+------------------------------+------------------------+
The following classes are instances of this:
/Game/FactoryGame/-Shared/Blueprint/BP_GameMode.BP_GameMode_C
+------------------------------+------------------------+
| AActor | -> see AActor |
| TArray<FObjectReferenceDisc> | rawPlayerStatePointers |
+------------------------------+------------------------+
The following classes are instances of this:
/Game/FactoryGame/-Shared/Blueprint/BP_GameState.BP_GameState_C
+------------------+---------------+
| AActor | -> see AActor |
| FUniqueNetIdRepl | Id |
+------------------+---------------+
The following classes are instances of this:
/Game/FactoryGame/Character/Player/BP_PlayerState.BP_PlayerState_C
+-----------------------------+--------------------+
| AActor | -> see AActor |
| TArray<FVehiclePhysicsData> | mStoredPhysicsData |
+-----------------------------+--------------------+
The following classes are instances of this:
/Game/FactoryGame/Buildable/Vehicle/Cyberwagon/Testa_BP_WB.Testa_BP_WB_C
/Game/FactoryGame/Buildable/Vehicle/Explorer/BP_Explorer.BP_Explorer_C
/Game/FactoryGame/Buildable/Vehicle/Golfcart/BP_Golfcart.BP_Golfcart_C
/Game/FactoryGame/Buildable/Vehicle/Golfcart/BP_GolfcartGold.BP_GolfcartGold_C
/Game/FactoryGame/Buildable/Vehicle/Tractor/BP_Tractor.BP_Tractor_C
/Game/FactoryGame/Buildable/Vehicle/Truck/BP_Truck.BP_Truck_C
+----------------------+----------------------+
| AFGVehicle | -> see AFGVehicle |
| FObjectReferenceDisc | mCoupledVehicleFront |
| FObjectReferenceDisc | mCoupledVehicleBack |
+----------------------+----------------------+
The following classes are instances of this:
/Game/FactoryGame/Buildable/Vehicle/Train/Locomotive/BP_Locomotive.BP_Locomotive_C
/Game/FactoryGame/Buildable/Vehicle/Train/Wagon/BP_FreightWagon.BP_FreightWagon_C
+-----------------------+-------------------+
| AFGVehicle | -> see AFGVehicle |
| bool | has_mActiveAction |
| if has_mActiveAction: | |
| FDroneAction | mActiveAction |
| TArray<FDroneAction> | mActionQueue |
+-----------------------+-------------------+
FDroneAction is defined as:
+--------------------+------------------+
| FName | actionStructName |
| List of Properties | action |
+--------------------+------------------+
The following classes are instances of this:
/Game/FactoryGame/Buildable/Factory/DroneStation/BP_DroneTransport.BP_DroneTransport_C
+--------------------------------+-----------------------+
| UObject | -> see UObject |
| TArray<FSimpleMemberReference> | UCSModifiedProperties | (only observed empty array so far)
+--------------------------------+-----------------------+
Note: Only save games with an empty
UCSModifiedProperties
array has been observed so far. Therefore, the typeFSimpleMemberReference
is unused. Data can be parsed as int32 with zero check.
The following classes are instances of this:
/Script/FactoryGame.FGDroneMovementComponent
/Script/FactoryGame.FGFactoryConnectionComponent
/Script/FactoryGame.FGFactoryLegsComponent
/Script/FactoryGame.FGHealthComponent
/Script/FactoryGame.FGInventoryComponent
/Script/FactoryGame.FGInventoryComponentEquipment
/Script/FactoryGame.FGInventoryComponentTrash
/Script/FactoryGame.FGPipeConnectionComponent
/Script/FactoryGame.FGPipeConnectionComponentHyper
/Script/FactoryGame.FGPipeConnectionFactory
/Script/FactoryGame.FGPowerConnectionComponent
/Script/FactoryGame.FGPowerInfoComponent
/Script/FactoryGame.FGRailroadTrackConnectionComponent
/Script/FactoryGame.FGShoppingListComponent
/Script/FactoryGame.FGTrainPlatformConnection
Properties are stored one by one in a list, where the end of a list is always defined by a property with the name "None". All Properties have a common header named PropertyTag and data, which differs for each property type.
+---------------+-----------------+
| PropertyTag | Property 1 |
|---------------| |
| Property data | |
+---------------+-----------------+
| PropertyTag | Property 2 |
|---------------| |
| Property data | |
+---------------+-----------------+
| ... |
+---------------+-----------------+
| PropertyTag | Property "None" |
+---------------+-----------------+
The common header (a struct named PropertyTag
, see
PropertyTag.h
PropertyTag.cpp)
has the following format:
+----------------------------------+-----------------+
| FName | Name |
| if Name != "None": | |
| FName | Type |
| int32 | Size |
| int32 | ArrayIndex |
| if Type == "StructProperty": | |
| FName | StructName |
| GUID | StructGuid |
| if Type == "BoolProperty": | |
| int8 | BoolVal |
| if Type == "ByteProperty": | |
| FName | EnumName |
| if Type == "EnumProperty": | |
| FName | EnumName |
| if Type == "ArrayProperty": | |
| FName | InnerType |
| if Type == "SetProperty": | |
| FName | InnerType |
| if Type == "MapProperty": | |
| FName | InnerType |
| FName | ValueType |
| uint8 | HasPropertyGuid |
| if HasPropertyGuid: | |
| GUID | PropertyGuid |
+----------------------------------+-----------------+
BoolProperty has no additional data.
The value is stored in the BoolVal
field of the PropertyTag.
The type of the value is stored in Tag.EnumName
.
+----------------------------+-----------+
| if Tag.EnumName == "None": | |
| int8 | valueByte |
| else: | |
| FName | valueName |
+----------------------------+-----------+
The type of the enum is stored in Tag.EnumName
.
+-------+-------+
| FName | value |
+-------+-------+
+-------+-------+
| FName | value |
+-------+-------+
+----------------------+-------+
| FObjectReferenceDisc | value |
+----------------------+-------+
TODO
+---------+-------+
| FString | value |
+---------+-------+
+-------+-------+
| FText | value |
+-------+-------+
+-------+-------+
| int32 | value |
+-------+-------+
+------+-------+
| int8 | value |
+------+-------+
+-------+-------+
| int64 | value |
+-------+-------+
+--------+-------+
| uint32 | value |
+--------+-------+
+--------+-------+
| uint64 | value |
+--------+-------+
+-------+-------+
| float | value |
+-------+-------+
+--------+-------+
| double | value |
+--------+-------+
The type of the array is defined by Tag.InnerType
.
For most types, the format of the data is:
+-----------+--------+
| TArray<T> | values |
+-----------+--------+
With using the following types:
InnerType | T |
---|---|
BoolProperty |
int8 |
ByteProperty |
int8 |
EnumProperty |
FName |
FloatProperty |
float |
Int8Property |
int8 |
Int64Property |
int64 |
IntProperty |
int32 |
InterfaceProperty |
FObjectReferenceDisc |
NameProperty |
FName |
ObjectProperty |
FObjectReferenceDisc |
SoftObjectProperty |
FSoftObjectPath |
StrProperty |
FString |
The type StructProperty
has a custom format:
+---------------------+-------------+
| int32 | count |
| PropertyTag | innerTag | (always type StructProperty)
| for i = 1 to count: | |
| Struct_T | struct data |
+---------------------+-------------+
The type of the struct data is stored in innerTag.StructName
.
For more information about structs, see Structs.
The type of the map key is in Tag.InnerType
and the type of the values Tag.ValueType
.
The layout of the data is:
+-------------------------+-----------------+
| int32 | NumKeysToRemove |
| if NumKeysToRemove > 0: | |
| ... (unknown) | | (not observed in save games)
| TMap<Key_T, Value_T> | map data |
+-------------------------+-----------------+
With using the following types:
InnerType / ValueType | T |
---|---|
ByteProperty |
int8 |
EnumProperty |
FName |
FloatProperty |
float |
Int64Property |
int64 |
IntProperty |
int32 |
NameProperty |
FName |
ObjectProperty |
FObjectReferenceDisc |
In addition, StructProperty
is used as type.
But maps have the problem that no information about which struct type is used is being serialized to the save game.
It is only possible to workaround this by manually creating a list of struct types using the parent class name and property name.
So far, the following types have been observed:
Parent Class | Name | Key | Value | Struct Type | Notes |
---|---|---|---|---|---|
/Game/FactoryGame/Buildable/Factory/TruckStation/Build_TruckStation.Build_TruckStation_C |
mDockingVehicleStatistics |
x | DockingVehicleStatistics |
||
/Game/FactoryGame/Events/BP_EventSubsystem.BP_EventSubsystem_C |
mStoredCalendarData |
x | CalendarData |
||
/Game/FactoryGame/Events/BP_EventSubsystem.BP_EventSubsystem_C |
mCalendarData |
x | CalendarData |
||
/Game/FactoryGame/Events/BP_EventSubsystem.BP_EventSubsystem_C |
mCalendarsOpenedByPlayers |
x | PlayerStateSetWrapper |
||
/Script/FactoryGame.FGFoliageRemovalSubsystem |
mSaveData |
x | IntVector |
||
/Script/FactoryGame.FGFoliageRemovalSubsystem |
mSaveData |
x | FoliageRemovalSaveDataPerCell |
||
/Script/FactoryGame.FGFoliageRemovalSubsystem |
mUnresolvedSaveData |
x | IntVector |
||
/Script/FactoryGame.FGFoliageRemovalSubsystem |
mUnresolvedSaveData |
x | FoliageRemovalUnresolvedSaveDataPerCell |
||
/Script/FactoryGame.FGStatisticsSubsystem |
mItemsManuallyCraftedCount |
x | MappedItemAmount |
||
/Script/FactoryGame.FGStatisticsSubsystem |
mItemsPickedUp |
x | MappedItemAmount |
||
/Script/FactoryGame.FGStatisticsSubsystem |
mActorsBuiltCount |
x | ActorBuiltData |
||
FoliageRemovalSaveDataPerCell |
SaveDataMap |
x | FoliageRemovalSaveDataForFoliageType |
||
FoliageRemovalUnresolvedSaveDataPerCell |
SaveDataMap |
x | FoliageRemovalSaveDataForFoliageType |
||
LBBalancerData |
mIndexMapping |
x | LBBalancerIndexing |
Mod LoadBalancers |
For more information about structs, see Structs.
The type of the set is defined by Tag.InnerType
.
The layout of the data is:
+-----------------------------+---------------------+
| int32 | NumElementsToRemove |
| if NumElementsToRemove > 0: | |
| ... (unknown) | | (not observed in save games)
| TArray<T> | values |
+-----------------------------+---------------------+
With using the following types:
InnerType | T |
---|---|
ObjectProperty |
FObjectReferenceDisc |
UInt32Property |
uint32 |
In addition, StructProperty
is used as type.
Similar to maps, sets have the problem that no information about which struct type is used is being serialized to the save game.
It is only possible to workaround this by manually creating a list of struct types using the parent class name and property name.
So far, the following types have been observed:
Parent Class | Name | Struct Type |
---|---|---|
/Script/FactoryGame.FGFoliageRemoval |
mRemovalLocations |
Vector |
/Script/FactoryGame.FGScannableSubsystem |
mDestroyedPickups |
Guid |
/Script/FactoryGame.FGScannableSubsystem |
mLootedDropPods |
Guid |
For more information about structs, see Structs.
The type of the struct is stored in Tag.StructName
.
See Structs for the data layout of individual structs.
Structs are typically objects containing multiple variables and could be interpreted as their own type. From a save game serialization perspective, they could be sorted into two groups. The first group uses the Unreal reflection system to store their values (Property Structs), and the others use their own binary serialization (Binary Structs).
Property structs are stored using the Unreal reflection system.
The format is exactly the same as a List of Properties
.
The following struct names were observed to be property structs:
ActorBuiltData
,
ActorTickFunction
,
BlueprintCategoryRecord
,
BlueprintRecord
,
BlueprintSubCategoryRecord
,
BodyInstance
,
BoomBoxPlayerState
,
BoxSphereBounds
,
CalendarData
,
CollisionResponse
,
CustomizationDescToRecipeData
,
DockingVehicleStatistics
,
DroneDockingStateInfo
,
DroneTripInformation
,
FactoryCustomizationColorSlot
,
FactoryCustomizationData
,
FactoryTickFunction
,
FeetOffset
,
FGCachedConnectedWire
,
FGDroneFuelRuntimeData
,
FGPortalCachedFactoryTickData
,
FloatInterval
,
FloatRange
,
FloatRangeBound
,
FoliageRemovalSaveDataForFoliageType
,
FoliageRemovalSaveDataPerCell
,
FoliageRemovalUnresolvedSaveDataPerCell
,
FoundationSideSelectionFlags
,
GlobalColorPreset
,
HardDriveData
,
HeadlightParams
,
HighlightedMarkerPair
,
Hotbar
,
InstanceData
,
InventoryStack
,
InventoryToRespawnWith
,
ItemAmount
,
ItemFoundData
,
KAggregateGeom
,
KConvexElem
,
LBBalancerData
(Mod LoadBalancers),
LightSourceControlData
,
MapMarker
,
MappedItemAmount
,
MaterialCachedExpressionData
,
MaterialInstanceBasePropertyOverrides
,
MaterialParameterInfo
,
MeshUVChannelInfo
,
MessageData
,
MiniGameResult
,
ParticleMap
,
PersistentGlobalIconId
,
PhaseCost
,
PlayerCustomizationData
,
PlayerRules
,
PlayerStateSetWrapper
,
PointerToUberGraphFrame
,
PrefabIconElementSaveData
,
PrefabTextElementSaveData
,
ProjectAssemblyLaunchSequenceValue
,
RecipeAmountStruct
,
RemovedInstance
,
RemovedInstanceArray
,
ResearchData
,
ResearchTime
,
ResourceSinkHistory
,
ResponseChannel
,
ScalarParameterValue
,
ScannableObjectData
,
ScannableResourcePair
,
SchematicCost
,
ShoppingListClassEntry
,
ShoppingListSettings
,
SpawnData
,
SplinePointData
,
SplitterSortRule
,
StaticMaterial
,
StaticParameterSet
,
StaticSwitchParameter
,
StreamingTextureBuildInfo
,
StringPair
,
SubCategoryMaterialDefault
,
SwatchGroupData
,
TextureParameterValue
,
TimerHandle
,
TimeTableStop
,
TireTrackDecalDetails
,
TopLevelAssetPath
,
TrainDockingRuleSet
,
TrainSimulationData
,
Transform
,
Vector_NetQuantize
,
WeightmapLayerAllocationInfo
,
WireInstance
,
WorldPartitionRuntimeCellDebugInfo
As property structs seem more common than binary structs, save game parsers may just try to parse unknown structs as property structs.
The following structs are binary structs with the type described in the table:
Struct Name | Type | Description | Notes |
---|---|---|---|
Box |
FBox |
FVector Min FVector Max uint8 IsValid |
|
ClientIdentityInfo |
FClientIdentityInfo |
FString OfflineId TMap<uint8, TArray<uint8>> AccountIds |
|
Color |
FColor |
uint8 B uint8 G uint8 R uint8 A |
|
FluidBox |
FFluidBox |
float Value |
|
Guid |
FGuid |
FGuid | |
IntPoint |
FIntPoint |
int32 X int32 Y |
|
IntVector |
FIntVector |
int32 X int32 Y int32 Z |
|
InventoryItem |
FInventoryItem |
FInventoryItem | |
LBBalancerIndexing |
FLBBalancerIndexing |
int32 mNormalIndex int32 mOverflowIndex int32 mFilterIndex |
(Mod LoadBalancers) |
LinearColor |
FLinearColor |
float R float G float B float A |
|
Quat |
FQuat |
double X double Y double Z double W |
|
RailroadTrackPosition |
FRailroadTrackPosition |
FObjectReferenceDisc Track float Offset float Forward |
|
Rotator |
FRotator |
double Pitch double Yaw double Roll |
|
SoftClassPath |
FSoftObjectPath |
TODO | |
Vector2D |
FVector2D |
double X double Y |
|
Vector4 |
FVector4 |
double X double Y double Z double W |
|
Vector |
FVector |
double X double Y double Z |
Integer types are all named by their size in bits, e.g. int8, int32, int64, uint8, uint32, uint64, etc. Floating point numbers are either defined as float (32-bit) or double (64-bit). Bools are stored as uint32 in the save game.
TArray<T>
is defined in the following way:
+--------+-------+
| int32 | num |
| T[num] | items |
+--------+-------+
TArray is a template, and the size of a single item is defined by the underlying type.
In addition, variants templating the size exist, i.e., using int64 TArray<T, int64>
:
+--------+-------+
| int64 | num |
| T[num] | items |
+--------+-------+
TMap<Key_T, Value_T>:
+--------------------+-------+
| int32 | size |
| for i = 1 to size: | |
| Key_T | key |
| Value_T | value |
+--------------------+-------+
TMap is a template, and the size of a single key and value items is defined by the underlying type.
FString
is an Unreal class for strings.
It has the following binary structure:
+--------+---------------+
| int32 | string length |
| char[] | data |
+--------+---------------+
length = 0: data is empty.
length > 0: data is a `char` array with the size `length`, representing a null-terminated ASCII string.
length < 0: data is a `char16` array with the size `-length`, representing a null-terminated UTF-16 string.
FName is serialized as FString
in the save game.
+-----------------------------------+---------------------------+
| uint32 | Flags |
| int8 (ETextHistoryType) | HistoryType |
| if historyType == -1: | | (ETextHistoryType::None)
| bool | HasCultureInvariantString |
| if HasCultureInvariantString: | |
| FString | TextData |
| if historyType == 0: | | (ETextHistoryType::Base)
| FString | Namespace |
| FString | Key |
| FString | SourceString |
| if historyType == ?: | |
| ... TODO | | (not observed in save games)
+-----------------------------------+---------------------------+
+--------+---+
| uint32 | A |
| uint32 | B |
| uint32 | C |
| uint32 | D |
+--------+---+
+---------------+----------+
| bool | bIsValid |
| if bIsValid: | |
| uint8[16] | Bytes |
+---------------+----------+
Another common type used within the save data is FObjectReferenceDisc
, defined in the following way:
+---------+------------+
| FString | level name |
| FString | path name |
+---------+------------+
- Satisfactory internal struct, header file:
FGObjectReference.h
+--------------------------+------------------+
| bool | bHasValidStruct |
| if bHasValidStruct: | |
| FObjectReferenceDisc | ScriptStruct |
| int32 | savedPayloadSize | (size of StructInstance)
| List of Properties | StructInstance |
+--------------------------+------------------+
Unreal archives internally can select binary, native, or tagged property serialization for the content of StructInstance, see https://github.com/EpicGames/UnrealEngine/blob/5.3.2-release/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp#L2761. Here, it is assumed that the game will always use tagged properties.
+------------------------------------+-------+
| TMap<FName, FWPGridValidationData> | Grids |
+------------------------------------+-------+
+-----------------------+------------+
| int32 | CellSize |
| uint32 | GridHash |
| TMap<FName, uint32_t> | CellHashes |
+-----------------------+------------+
+------------------------------+-----------------+
| TArray<uint8, int64> | TOCBlob64 |
| TArray<uint8, int64> | DataBlob64 |
| TArray<FObjectReferenceDisc> | DestroyedActors |
+------------------------------+-----------------+
+---------------------------------------------+---------------------------+
| TArray<uint8, int64> | TOCBlob64 |
| TArray<uint8, int64> | DataBlob64 |
| TMap<FString, TArray<FObjectReferenceDisc>> | LevelToDestroyedActorsMap |
+---------------------------------------------+---------------------------+
+----------------------+-----------+
| FString | ClassName |
| FObjectReferenceDisc | Reference |
+----------------------+-----------+
Defined in FGActorSaveHeaderTypes.h
.
+-----------------------+---------------+
| FObjectBaseSaveHeader | BaseHeader |
| FString | OuterPathName |
+-----------------------+---------------+
Defined in FGActorSaveHeaderTypes.h
.
+-----------------------+------------------+
| FObjectBaseSaveHeader | ObjectHeader |
| bool | NeedTransform |
| FTransform3f | Transform |
| bool | WasPlacedInLevel |
+-----------------------+------------------+
Defined in FGActorSaveHeaderTypes.h
.
FTransform3f
is defined as:
+----------+-------------+-------------------------------------------+
| float[4] | Rotation | quaternion describing object rotation |
| float[3] | Translation | world position of object (in centi meter) |
| float[3] | Scale3D | object scale |
+----------+-------------+-------------------------------------------+
+------------------------------+-----------------+
| TArray<FObjectReferenceDisc> | DestroyedActors |
+------------------------------+-----------------+
+--------------------------+----------------------+
| FObjectReferenceDisc | ItemClass |
| if SaveVersion >= 43: | | (see SaveVersion from parent object)
| FFGDynamicStruct | ItemState |
| else: | |
| FObjectReferenceDisc | LegacyItemStateActor |
+--------------------------+----------------------+
The internal type for ItemClass is
TSubclassOf<class UFGItemDescriptor>
, which is serialized asFObjectReferenceDisc
.
+----------------+--------+
| FInventoryItem | Item |
| float | Offset |
+----------------+--------+
+---------------------------+-------+
| TArray<FConveyorBeltItem> | Items |
+---------------------------+-------+
+--------------------------+-------------------+
| FObjectReferenceDisc | ChainActor |
| FObjectReferenceDisc | ConveyorBase |
| TArray<FSplinePointData> | SplinePointData |
| float | OffsetAtStart |
| float | StartsAtLength |
| float | EndsAtLength |
| int32 | FirstItemIndex |
| int32 | LastItemIndex |
| int32 | IndexInChainArray |
+--------------------------+-------------------+
+---------+---------------+
| FVector | Location |
| FVector | ArriveTangent |
| FVector | LeaveTangent |
+---------+---------------+
+----------------------+----------------------------------------------------+
| FTransform | Transform |
| FObjectReferenceDisc | CustomizationData.SwatchDesc |
| FObjectReferenceDisc | CustomizationData.MaterialDesc |
| FObjectReferenceDisc | CustomizationData.PatternDesc |
| FObjectReferenceDisc | CustomizationData.SkinDesc |
| FLinearColor | CustomizationData.OverrideColorData.PrimaryColor |
| FLinearColor | CustomizationData.OverrideColorData.SecondaryColor |
| FObjectReferenceDisc | CustomizationData.OverrideColorData.PaintFinish |
| uint8 | CustomizationData.PatternRotation |
| FObjectReferenceDisc | BuiltWithRecipe |
| FObjectReferenceDisc | BlueprintProxy |
+----------------------+----------------------------------------------------+
FTransform
is defined as:
+-----------+-------------+-------------------------------------------+
| double[4] | Rotation | quaternion describing object rotation |
| double[3] | Translation | world position of object (in centi meter) |
| double[3] | Scale3D | object scale |
+-----------+-------------+-------------------------------------------+
+------------------------------------------+--------------------+
| uint8 | EncodingFlags |
| if EncodingFlags & 1: | |
| if !EncodingFlags & 2: | |
| if EncodingFlags >> 3 == 30: | |
| uint8 | OnlineServicesType |
| TArray<uint8> | ReplicationData |
| else: | |
| if EncodingFlags >> 3 == 31: | |
| FString | TypeString |
| if EncodingFlags >> 3 != 0: | |
| uint8 | EncodedSize |
| uint8[EncodedSize] | TempBytes |
| else: | |
| if EncodingFlags >> 3 == 31: | |
| FString | TypeString |
| if EncodingFlags >> 3 != 0: | |
| FString | Contents |
+------------------------------------------+--------------------+
+---------+----------------------+
| FName | BoneName |
| FVector | BodyState.Position |
| FQuat | BodyState.Quaternion |
| FVector | BodyState.AngVel |
| FVector | BodyState.LinVel |
| uint8 | BodyState.Flags |
+---------+----------------------+
+-------+-------------+
| FName | PackageName |
| FName | AssetName |
+-------+-------------+
+----------------------+-----------+
| FObjectReferenceDisc | ItemClass |
| int32 | Amount |
+----------------------+-----------+
Blueprints consist of two files, a config file (*.sbpcfg
) and the blueprint file itself (*.sbp
).
+------------------------+----------------------+
| int32 | ConfigVersion |
| FString | BlueprintDescription |
| int32 | IconID.IconID |
| FLinearColor | Color |
| if ConfigVersion >= 3: | |
| FTopLevelAssetPath | IconID.IconLibrary |
+------------------------+----------------------+
This documentation is only valid if ConfigVersion
equals 2
or 3
!
IconID
is a struct named FPersistentGlobalIconId
, but serialized element wise dependent on ConfigVersion
.
The data except
ConfigVersion
is internally stored as structFBlueprintRecord
.
The blueprint file is very similar to the General file structure of the save file format. There is a blueprint header followed by binary data, which is stored using the same compressed chunks as in save games above.
+------------------------------+---------------+
| int32 | HeaderVersion |
| int32 | SaveVersion |
| int32 | BuildVersion |
| FIntVector | Dimensions |
| TArray<FBlueprintItemAmount> | Cost |
| TArray<FObjectReferenceDisc> | RecipeRefs |
+------------------------------+---------------+
This documentation is only valid if HeaderVersion
equals 2
!
The data except
HeaderVersion
is internally stored as structFBlueprintHeader
.
The blueprint binary data is similar to the structure of save games, but slightly different. Here, the format is described very shortly with focusing on differences. More detailed information can be found in the Decompressed binary data section above.
The binary data layout follows the following format:
+----------------------+------------------------------------------------------+
| int32 | total size of binary data (not including this value) |
| TArray<uint8, int32> | TOCData |
| TArray<uint8, int32> | BlobData |
+----------------------+------------------------------------------------------+
Objects within a blueprint are stored in similar TOC/Data blob like objects in a save game level.
+-----------------------------------------------------+---------------------------+
| int32 | numObjects |
| for i = 1 to numObjects: | |
| bool | isActor |
| if isActor: | |
| FActorSaveHeader | objectHeader |
| else: | |
| FObjectSaveHeader | objectHeader |
+-----------------------------------------------------+---------------------------+
+--------------------------+-------------------------------------+
| int32 | numObjects |
| for i = 1 to numObjects: | |
| TArray<uint8> | Data |
+--------------------------+-------------------------------------+
For parsing of Data
, see Objects.