Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<Company>R3dByt3</Company>
<Copyright>R3dByt3</Copyright>
<Authors>R3dByt3</Authors>
<Authors>R3dByt3, ChrisonSimtian</Authors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>12.0</LangVersion>
Expand All @@ -11,7 +11,7 @@
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>

<VersionPrefix>3.2.3</VersionPrefix>
<VersionPrefix>4.0.0</VersionPrefix>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix)-$(VersionSuffix)</Version>
<Version Condition=" '$(VersionSuffix)' == '' ">$(VersionPrefix)</Version>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using SatisfactorySaveNet.Abstracts.Model;
using System.IO;

namespace SatisfactorySaveNet.Abstracts;

public interface IFSaveObjectVersionDataSerializer
{
public FSaveObjectVersionData Deserialize(BinaryReader reader);
}
11 changes: 8 additions & 3 deletions SatisfactorySaveNet.Abstracts/IObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
using SatisfactorySaveNet.Abstracts.Model;
using SatisfactorySaveNet.Abstracts.Model;
using System.IO;

namespace SatisfactorySaveNet.Abstracts;

public interface IObjectSerializer
{
public ComponentObject Deserialize(BinaryReader reader, Header header, ComponentObject componentObject);
}
/// <summary>
/// Deserializes a single object body. <paramref name="saveVersion"/> is the level's
/// SaveCustomVersion (or the header's SaveVersion for the persistent level) — used to
/// gate post-body fields introduced at SaveCustomVersion 53+.
/// </summary>
public ComponentObject Deserialize(BinaryReader reader, Header header, ComponentObject componentObject, int? saveVersion = null);
}
10 changes: 8 additions & 2 deletions SatisfactorySaveNet.Abstracts/IPropertySerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ namespace SatisfactorySaveNet.Abstracts;

public interface IPropertySerializer
{
public IEnumerable<Property> DeserializeProperties(BinaryReader reader, Header? header = null, string? type = null, long? expectedPosition = null);
public Property? DeserializeProperty(BinaryReader reader, Header? header = null, string? type = null);
/// <summary>
/// Deserializes the property list for one object. <paramref name="saveVersion"/> is
/// the per-object SaveCustomVersion captured from the data blob — used to gate the
/// v1.2+ FPropertyTag-complete-type-name format and the leading serializationControl byte.
/// </summary>
public IEnumerable<Property> DeserializeProperties(BinaryReader reader, Header? header = null, string? type = null, long? expectedPosition = null, int? saveVersion = null);

public Property? DeserializeProperty(BinaryReader reader, Header? header = null, string? type = null, int? saveVersion = null);
}
6 changes: 6 additions & 0 deletions SatisfactorySaveNet.Abstracts/Model/ComponentObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ public class ComponentObject
public ExtraData? ExtraData { get; set; }
public int? EntitySaveVersion { get; set; }
public uint? Flags { get; set; }

/// <summary>
/// Per-object versioning struct serialized after the body at
/// <c>SaveCustomVersion.SerializeDataPackageVersionAndCustomVersions</c> (53). Null when absent.
/// </summary>
public FSaveObjectVersionData? ObjectVersionData { get; set; }
}
15 changes: 15 additions & 0 deletions SatisfactorySaveNet.Abstracts/Model/FPropertyTagNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;

namespace SatisfactorySaveNet.Abstracts.Model;

/// <summary>
/// Recursive type-tree node used by the v1.2+ FPropertyTag.IsCompletePropertyTagType
/// format. Encodes the property's type name plus any nested type info (e.g.
/// <c>ArrayProperty(StructProperty(InventoryStack))</c>) that previously lived in
/// per-type tag-extension fields.
/// </summary>
public class FPropertyTagNode
{
public string Name { get; set; } = string.Empty;
public ICollection<FPropertyTagNode> Children { get; set; } = [];
}
44 changes: 44 additions & 0 deletions SatisfactorySaveNet.Abstracts/Model/FSaveObjectVersionData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;

namespace SatisfactorySaveNet.Abstracts.Model;

/// <summary>
/// Per-object or per-level save versioning struct introduced at
/// <c>SaveCustomVersion.SerializeDataPackageVersionAndCustomVersions</c> (custom version 53).
/// Mirrors the Unreal <c>FSaveObjectVersionData</c> binary layout.
/// </summary>
public class FSaveObjectVersionData
{
public uint SaveObjectVersionDataVersion { get; set; }
public required FPackageFileVersion PackageFileVersion { get; set; }
public int LicenceVersion { get; set; }
public required FEngineVersion EngineVersion { get; set; }
public required FCustomVersionContainer CustomVersionContainer { get; set; }
}

public class FPackageFileVersion
{
public int Ue4Version { get; set; }
public int Ue5Version { get; set; }
}

public class FEngineVersion
{
public ushort Major { get; set; }
public ushort Minor { get; set; }
public ushort Patch { get; set; }
public uint Changelist { get; set; }
public string Branch { get; set; } = string.Empty;
}

public class FCustomVersionContainer
{
public ICollection<FCustomVersion> Versions { get; set; } = [];
}

public class FCustomVersion
{
public Guid Guid { get; set; }
public int Version { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,12 @@ public enum PropertyConstraint
Struct,
Text,
UInt32,
UInt64
UInt64,

/// <summary>
/// New v1.2+ property whose tag was parsed via FPropertyTag's complete-type-name
/// format but whose value bytes are not yet deeply deserialized. Stream-aligned
/// (binary size is consumed) but the value is opaque.
/// </summary>
Raw
}
80 changes: 80 additions & 0 deletions SatisfactorySaveNet.Abstracts/Model/Properties/RawProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;

namespace SatisfactorySaveNet.Abstracts.Model.Properties;

/// <summary>
/// A v1.2+ property whose tag (name, type tree, binary size, flags, optional GUID) was
/// parsed. For a handful of common property types (ObjectProperty, ArrayProperty whose
/// element is ObjectProperty, StrProperty / NameProperty) the value is also parsed so
/// callers can resolve recipes, mined resources, fuels, etc. without re-running the
/// parse. Other property types leave the value bytes unread (but stream-aligned).
/// </summary>
public class RawProperty : Property
{
public override PropertyConstraint PropertyValueType => PropertyConstraint.Raw;

public required string Type { get; set; }
public FPropertyTagNode? TypeNode { get; set; }
public int BinarySize { get; set; }
public byte Flags { get; set; }
public Guid? PropertyGuid { get; set; }

// ---- scalars ----

public int? IntValue { get; set; } // IntProperty
public uint? UIntValue { get; set; } // UInt32Property
public long? LongValue { get; set; } // Int64Property
public ulong? ULongValue { get; set; } // UInt64Property
public sbyte? SByteValue { get; set; } // Int8Property
public float? FloatValue { get; set; } // FloatProperty
public double? DoubleValue { get; set; } // DoubleProperty
/// <summary>BoolProperty value — at v1.2 it lives in tag flag bit 0x10, not in the value bytes.</summary>
public bool? BoolValue { get; set; }

/// <summary>Parsed value when <see cref="Type"/> == "ObjectProperty".</summary>
public ObjectReferenceValue? ObjectValue { get; set; }

/// <summary>Parsed values when <see cref="Type"/> == "ArrayProperty" and the
/// element type is "ObjectProperty".</summary>
public IReadOnlyList<ObjectReferenceValue>? ArrayObjectValues { get; set; }

/// <summary>Parsed string when <see cref="Type"/> == "StrProperty" or "NameProperty".</summary>
public string? StringValue { get; set; }

/// <summary>StructProperty value for the fixed-size raw struct types we recognise
/// (Vector/Rotator/Quat/Box/Color/LinearColor/Guid/IntPoint/DateTime/TimerHandle/
/// SlateBrush/FluidBox/RailroadTrackPosition). Unknown / property-list-shaped
/// structs leave this null and the value bytes are skipped via the binary-size fence.</summary>
public StructValue? StructValue { get; set; }

/// <summary>MapProperty entries when every key and value type is a fixed-size primitive
/// or framed string. Maps with composite key/value types leave this null and the
/// value bytes are skipped via the binary-size fence.</summary>
public IReadOnlyList<MapEntryValue>? MapEntries { get; set; }
}

/// <summary>
/// StructProperty value. <see cref="Value"/> is one of:
/// <see cref="double"/>[] (Vector/Rotator/Vector2D/Quat/Vector4)
/// <see cref="float"/>[] (LinearColor)
/// <see cref="BoxValue"/>
/// <see cref="byte"/>[] (Color = 4 bytes BGRA, Guid = 16 bytes)
/// <see cref="long"/> (IntPoint, DateTime)
/// <see cref="float"/> (FluidBox)
/// <see cref="string"/> (TimerHandle, SlateBrush)
/// <see cref="RailroadTrackPositionValue"/>
/// null (unrecognised type; bytes were skipped via the fence).
/// </summary>
public sealed record StructValue(string TypeName, object? Value);

public readonly record struct BoxValue(double[] Min, double[] Max, bool IsValid);
public readonly record struct RailroadTrackPositionValue(string Root, string InstanceName, float Offset, float Forward);

/// <summary>MapProperty entry. Key/Value runtime types depend on the map's declared
/// key/value types — int / long / string / ObjectReferenceValue / byte / bool / float / double.</summary>
public readonly record struct MapEntryValue(object Key, object Value);

/// <summary>An Unreal <c>FObjectReference</c> — a (levelName, pathName) pair.</summary>
public readonly record struct ObjectReferenceValue(string LevelName, string PathName);

Loading