Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save rdeforest/1fdde20fa631c548d4fa600aaa074472 to your computer and use it in GitHub Desktop.
Save rdeforest/1fdde20fa631c548d4fa600aaa074472 to your computer and use it in GitHub Desktop.
diff --git a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveComponent.cpp b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveComponent.cpp
index ab15f5280115..c4d559254147 100644
--- a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveComponent.cpp
+++ b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveComponent.cpp
@@ -664,9 +664,16 @@ void URamaSaveComponent::LoadOwnerVariables(UWorld* World, FArchive &Ar)
if(Property)
{
uint8* InstanceValuePtr = Property->ContainerPtrToValuePtr<uint8>(ActorOwner); //this = object instance that has this property!
-
+
//Serialize Instance!
- Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ // RdF 2023-05-18: UE v5.x float -> double work-around, see RamaSaveSystem/docs/v5-float-bug.md
+ if (EndPosToSkip - Ar.Tell() == 4 && CastField<FDoubleProperty>(Property)) {
+ float floatValue = 0.0f;
+ Ar << floatValue;
+ TPropertyTypeFundamentals<double>::SetPropertyValue(InstanceValuePtr, (double) floatValue);
+ } else {
+ Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ }
}
else
{
@@ -985,9 +992,16 @@ void URamaSaveComponent::LoadSelfAndSubclassVariables(FArchive &Ar)
if(Property)
{
uint8* InstanceValuePtr = Property->ContainerPtrToValuePtr<uint8>(this); //this = object instance that has this property!
-
+
//Serialize Instance!
- Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ // RdF 2023-05-18: UE v5.x float -> double work-around, see RamaSaveSystem/docs
+ if (EndPosToSkip - Ar.Tell() == 4 && CastField<FDoubleProperty>(Property)) {
+ float floatValue = 0.0f;
+ Ar << floatValue;
+ TPropertyTypeFundamentals<double>::SetPropertyValue(InstanceValuePtr, (double) floatValue);
+ } else {
+ Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ }
}
else
{
@@ -1289,9 +1303,16 @@ void URamaSaveComponent::LoadSubComponentVariables(AActor* ActorOwner, UWorld* W
UE_LOG(RamaSave,Warning,TEXT("Property found in component and loaded from disk! %s %s %s"), *GetClass()->GetName(), *EachComp->GetName(), *PropertyNameString);
}
uint8* InstanceValuePtr = Property->ContainerPtrToValuePtr<uint8>(EachComp); //this = object instance that has this property!
-
+
//Serialize Instance!
- Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ // RdF 2023-05-18: UE v5.x float -> double work-around, see RamaSaveSystem/docs/v5-float-bug.md
+ if (EndPosToSkip - Ar.Tell() == 4 && CastField<FDoubleProperty>(Property)) {
+ float floatValue = 0.0f;
+ Ar << floatValue;
+ TPropertyTypeFundamentals<double>::SetPropertyValue(InstanceValuePtr, (double) floatValue);
+ } else {
+ Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ }
Found = true;
//~~~~
@@ -1380,7 +1401,14 @@ void URamaSaveComponent::LoadSubComponentVariables(AActor* ActorOwner, UWorld* W
uint8* InstanceValuePtr = Property->ContainerPtrToValuePtr<uint8>(FoundComponent); //this = object instance that has this property!
//#SC_7 //Serialize Instance!
- Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(), InstanceValuePtr);
+ // RdF 2023-05-18: UE v5.x float -> double work-around, see RamaSaveSystem/docs/v5-float-bug.md
+ if (EndPosToSkip - Ar.Tell() == 4 && CastField<FDoubleProperty>(Property)) {
+ float floatValue = 0.0f;
+ Ar << floatValue;
+ TPropertyTypeFundamentals<double>::SetPropertyValue(InstanceValuePtr, (double) floatValue);
+ } else {
+ Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(), InstanceValuePtr);
+ }
}
else
diff --git a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveEngine.cpp b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveEngine.cpp
index c7ec60df6e16..4d0c6d7ce9da 100644
--- a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveEngine.cpp
+++ b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveEngine.cpp
@@ -7,7 +7,9 @@
#include "Serialization/Archive.h"
#include "Compression/CompressionUtil.h"
-
+
+#include "Runtime/Core/Public/Serialization/CompressedChunkInfo.h"
+
//////////////////////////////////////////////////////////////////////////
// RamaSaveEngine
@@ -1022,7 +1024,14 @@ void ARamaSaveEngine::Phase2()
// Read engine and UE version information
FPackageFileVersion SavedUEVersion;
- MemoryReader << SavedUEVersion;
+ // RdF 2023-05-18: UE v5.x UE version field change work-around, see RamaSaveSystem/docs/v5-workarounds.md
+ if (IsUE4CompressedFile(LoadParams.FileName)) {
+ int32 SavedUEVersionNum;
+ MemoryReader << SavedUEVersionNum;
+ SavedUEVersion = FPackageFileVersion::CreateUE4Version(SavedUEVersionNum);
+ } else {
+ MemoryReader << SavedUEVersion;
+ }
FEngineVersion SavedEngineVersion;
MemoryReader << SavedEngineVersion;
@@ -1229,6 +1238,22 @@ void ARamaSaveEngine::SkipStaticData(FArchive& Ar)
Ar.Seek(StaticDataSkipPos);
}
+// RdF 2023-05-18: UE v5.x UE version field change work-around, see RamaSaveSystem/docs/v5-workarounds.md
+bool ARamaSaveEngine::IsUE4CompressedFile(const FString& FullFilePath)
+{
+ FArrayReader PossiblyCompressedData;
+ if (!FFileHelper::LoadFileToArray(PossiblyCompressedData, *FullFilePath)) {
+ return false;
+ }
+
+ FCompressedChunkInfo PackageFileTag;
+ PackageFileTag.CompressedSize = 0;
+ PackageFileTag.UncompressedSize = 0;
+ PossiblyCompressedData << PackageFileTag;
+
+ return PackageFileTag.CompressedSize == PACKAGE_FILE_TAG;
+}
+
URamaSaveObject* ARamaSaveEngine::LoadStaticData(bool& FileIOSuccess, FString FileName)
{
URamaSaveSystemSettings* Settings = URamaSaveSystemSettings::Get();
@@ -1270,8 +1295,15 @@ URamaSaveObject* ARamaSaveEngine::LoadStaticData(bool& FileIOSuccess, FString F
//! #2
// Read engine and UE version information
- FPackageFileVersion SavedUEVersion;
- MemoryReader << SavedUEVersion;
+ FPackageFileVersion SavedUEVersion;
+ // RdF 2023-05-18: UE v5.x UE version field change work-around, see RamaSaveSystem/docs/v5-workarounds.md
+ if (IsUE4CompressedFile(FileName)) {
+ int32 SavedUEVersionNum;
+ MemoryReader << SavedUEVersionNum;
+ SavedUEVersion = FPackageFileVersion::CreateUE4Version(SavedUEVersionNum);
+ } else {
+ MemoryReader << SavedUEVersion;
+ }
FEngineVersion SavedEngineVersion;
MemoryReader << SavedEngineVersion;
@@ -1370,11 +1402,18 @@ URamaSaveObject* ARamaSaveEngine::LoadStaticData(bool& FileIOSuccess, FString F
FProperty* Property = FindFProperty<FProperty>( RSO->GetClass(), *PropertyNameString );
if(Property)
- {
+ {
uint8* InstanceValuePtr = Property->ContainerPtrToValuePtr<uint8>(RSO);
-
+
//Serialize Instance!
- Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ // RdF 2023-05-18: UE v5.x float -> double work-around, see RamaSaveSystem/docs/v5-workarounds.md
+ if (EndPosToSkip - Ar.Tell() == 4 && CastField<FDoubleProperty>(Property)) {
+ float floatValue = 0.0f;
+ Ar << floatValue;
+ TPropertyTypeFundamentals<double>::SetPropertyValue(InstanceValuePtr, (double) floatValue);
+ } else {
+ Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ }
if(Settings->LogSavingAndLoadingOfEachStaticDataProperty)
{
diff --git a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveUtility.cpp b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveUtility.cpp
index 9a6f39d92ba5..f762bd9effda 100644
--- a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveUtility.cpp
+++ b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Private/RamaSaveUtility.cpp
@@ -154,7 +154,9 @@ static bool IsCompressedFile(const FString& FullFilePath)
PackageFileTag.UncompressedSize = 0;
PossiblyCompressedData << PackageFileTag;
- return PackageFileTag.CompressedSize == ARCHIVE_V2_HEADER_TAG;
+ // RdF 2023-05-18: UE file tag work-around, see RamaSaveSystem/docs/v5-workarounds.md
+ return PackageFileTag.CompressedSize == ARCHIVE_V2_HEADER_TAG
+ || PackageFileTag.CompressedSize == PACKAGE_FILE_TAG;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Public/RamaSaveEngine.h b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Public/RamaSaveEngine.h
index 6062964fc69e..ae4e4aea84d3 100644
--- a/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Public/RamaSaveEngine.h
+++ b/Engine/Plugins/Marketplace/RamaSaveSystem/Source/RamaSaveSystem/Public/RamaSaveEngine.h
@@ -183,6 +183,8 @@ public:
static URamaSaveObject* LoadStaticData(bool& FileIOSuccess, FString FileName);
+ static bool IsUE4CompressedFile(const FString& FullFilePath);
+
public:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
diff --git a/Engine/Plugins/Marketplace/RamaSaveSystem/docs/v5-workarounds.md b/Engine/Plugins/Marketplace/RamaSaveSystem/docs/v5-workarounds.md
new file mode 100644
index 000000000000..7b7810275524
--- /dev/null
+++ b/Engine/Plugins/Marketplace/RamaSaveSystem/docs/v5-workarounds.md
@@ -0,0 +1,108 @@
+# What is this
+
+Epic made three changes to UnrealEngine in version 5.0 which broke
+RamaSaveSystem's ability to read a gave save created in v4.x. This
+file documents those changes and how we addressed them.
+
+Some of these changes were discussed on the [RamaSaveSystem support
+thread](https://forums.unrealengine.com/t/plugin-discussion-rama-save-system-plugin-thread/758345/13?u=rama).
+
+## The archive header tag change
+
+### The change
+
+In v4 files the first 64 bits are a magic number (called
+`PACKAGE_FILE_TAG` in source). The first 32 bits are `0x9E2A83C1`, and
+the last 32 bits are zeros. In v5 Unreal changed the zeros to twos
+(`0x22222222`) and they check for byte order swapping. A file which
+starts with `0x9E2A83C1` may be either v4 or v5, but if it's v5 then
+the next 64 bits are `0x22222222`.
+
+See `FArchive::SerializeCompressedNew` in Core/Private/Archive.cpp for
+how Epic handles all the different combinations of version and byte
+order.
+
+Rama copied part of `SerializeCompressedNew` to handle v5 files but
+didn't copy the parts that would account for older files.
+
+### The Fix
+
+To allow RamaSaveSystem to read files with either the old OR the new
+magic number, we added another condition to the tag test:
+
+```C++
+return PackageFileTag.CompressedSize == ARCHIVE_V2_HEADER_TAG
+ || (0xFFFFFFFF && PackageFileTag.CompressedSize) == PACKAGE_FILE_TAG;
+```
+
+A better long-term fix would probably involve calling some method in
+Archive.cpp instead of copying its code. This fix is just to unblock
+our own project.
+
+## The archive version information change
+
+UE v5 also changed the format of the version information in the
+archive payload (compressed or not). In v4 it was an `int32`. In v5
+it's a `FPackageFileVersion`, which is a structure of two `int32`
+fields: the original UE4 version and a new UE5 version. We hope
+version six won't add another field to this structure.
+
+To account for the change in the size of the field we have to know
+when we're in a v4 archive and not treat it like a v5 archive. To this
+end we added a function which checks the beginning of the file for
+`PACKAGE_FILE_TAG`, similar to what we discussed in the previous
+change. When we're about to read the UE version from an archive we
+use this function to determine how to handle it.
+
+This change, too, could probably be handled more gracefully. Also
+there may be other locations in the RamaSaveSystem source which need
+to have the fix applied.
+
+## The Float change
+
+### The change
+
+In version 5.0 of UnrealEngine, Epic switched to persisting all floats
+as doubles, as noted
+[here](https://www.unrealdirective.com/tip/float-is-now-double). In
+specific projects this transition was handled the first time the
+project was loaded in a v5 editor.
+
+In RamaSaveSystem version 2.0, during de-serialization, the type of a
+value was derived from what was in the relevant blueprint. This meant
+that floats saved in UE 4 would be read in UE5 as doubles, which are
+64 bits instead of 32. This caused the index into the serialized data
+to be off starting after the first float.
+
+### The fix
+
+To detect when a value is a float, we check to see if the blueprint
+thinks it's a double but has a size of 32 bits.
+
+If it is, we declare a new local float, read its value in from the
+archive and store the value in the destination slot as a double. For
+all other value types we use the original method of reading values.
+
+```c++
+ if (EndPosToSkip - Ar.Tell() == 4 && CastField<FDoubleProperty>(Property)) {
+ float floatValue = 0.0f;
+ Ar << floatValue;
+ TPropertyTypeFundamentals<double>::SetPropertyValue(InstanceValuePtr, (double) floatValue);
+ } else {
+ Property->SerializeItem(FStructuredArchiveFromArchive(Ar).GetSlot(),InstanceValuePtr);
+ }
+```
+
+This change had to be made in five places, all of which are marked by
+a comment starting with
+"`RdF 2023-05-18: UE v5.x float -> double work-around`".
+
+# Who are we
+
+We are, since February of 2022, the developers working on
+[Hold Your Own](https://store.steampowered.com/app/717790/Hold_Your_Own/).
+We can be reached on [Discord](https://discord.gg/bCJZUN7R).
+
+The lead developer is Zuleica. Robert is the second developer and
+author of this patch, with help from other Rama users. You know who
+you are! Thank you!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment