Last active
May 19, 2023 18:11
-
-
Save rdeforest/1fdde20fa631c548d4fa600aaa074472 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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