trumank / uesave-rs

Rust library to read and write Unreal Engine save files
MIT License
274 stars 54 forks source link

update SoftObject to include added string #34

Closed c01dc0ffee closed 4 months ago

c01dc0ffee commented 8 months ago

SoftObjectProperty contains an extra string value in the Abiotic Factor demo's save files. This change allows uesave to parse ABF saves, but will break existing compatibility.

c01dc0ffee commented 8 months ago

Abiotic Factor uses UE 5.3.2:

{
  "header": {
    "magic": 1396790855,
    "save_game_version": 3,
    "package_version": {
      "New": [
        522,
        1009
      ]
    },
    "engine_version_major": 5,
    "engine_version_minor": 3,
    "engine_version_patch": 2,
    "engine_version_build": 0,
    "engine_version": "UE5",
...

I'm not sure if this change comes from 5.3 or if it's exclusive to Abiotic Factor, but I'll try to keep this updated if I find out.

Egaliterrier commented 5 months ago

Thanks so much for this. Still can't parse WorldSave_*.sav files though. Except Worldsave_MetaData.sav. The others fail with:

offset 1653: StructType for ".SimpleDoorMap.Value" unspecified, assuming Struct(None)
Error: at offset 1970: unknown property type: "DoorState_16_FC20B6E3483FF18E4FBDF19E39E880E9"

WorldSave_Facility.zip

trumank commented 5 months ago

@Egaliterrier I've updated the SoftObjectPath struct to have the same change (I believe one is simply a wrapper of the other) which fixes that save.

I took the liberty of finding the serialization code in the UE source and it looks like the format did indeed change, so I'll add some version checks and merge this at some point.


void FSoftObjectPath::SerializePathWithoutFixup(FArchive& Ar)
{
    if (Ar.IsLoading() && Ar.UEVer() < VER_UE4_ADDED_SOFT_OBJECT_PATH)
    {
        FString Path;
        Ar << Path;

        if (Ar.UEVer() < VER_UE4_KEEP_ONLY_PACKAGE_NAMES_IN_STRING_ASSET_REFERENCES_MAP)
        {
            Path = FPackageName::GetNormalizedObjectPath(Path);
        }

        SetPath(MoveTemp(Path));
    }
    else if (Ar.IsLoading() && Ar.UEVer() < EUnrealEngineObjectUE5Version::FSOFTOBJECTPATH_REMOVE_ASSET_PATH_FNAMES)
    {
        FName AssetPathName;
        Ar << AssetPathName;
        AssetPath = WriteToString<FName::StringBufferSize>(AssetPathName).ToView();

        Ar << SubPathString;
    }
    else
    {
        Ar << AssetPath;
        Ar << SubPathString;
    }
}