go to that location and send the code of “archive.CPP” and “archive.h” in the text
.h is in the same location as CPP .you can save your file as text and send them as an attachment
both files have been added to the original post. I couldn’t add them in .txt files but I added them with the code option.
This project has been working great for 3 months with minimal crashes. I closed the project yesterday after working on it all day with no issues. Today, I went to try to open it and it randomly crashes and shows an error message that I’ve attached below.
I tried verifying and reinstalling the engine but I get the same crash. I have a complete backup from 5 days ago and I have builds from every day since then, but I’m unsure if I can get the editor files back from the built game.
We could resort to the version from 5 days ago, but we’ve accomplished so much since then working around the clock and it would really set us back to go back to that version. I can provide any dump files, just not sure what is needed. Thanks very much in advance, this is a huge setback for us.
archive.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Serialization/Archive.h"
#include "Serialization/ArchiveProxy.h"
#include "Math/UnrealMathUtility.h"
#include "HAL/UnrealMemory.h"
#include "Containers/Array.h"
#include "Containers/UnrealString.h"
#include "UObject/NameTypes.h"
#include "Logging/LogMacros.h"
#include "Misc/Parse.h"
#include "UObject/ObjectVersion.h"
#include "Serialization/NameAsStringProxyArchive.h"
#include "Misc/CommandLine.h"
#include "Internationalization/Text.h"
#include "Stats/StatsMisc.h"
#include "Stats/Stats.h"
#include "Async/AsyncWork.h"
#include "Serialization/CustomVersion.h"
#include "Misc/EngineVersion.h"
#include "Misc/NetworkVersion.h"
#include "Interfaces/ITargetPlatform.h"
#include "Serialization/CompressedChunkInfo.h"
#include "Serialization/ArchiveSerializedPropertyChain.h"
PRAGMA_DISABLE_UNSAFE_TYPECAST_WARNINGS
namespace ArchiveUtil
{
template<typename T>
FArchive& SerializeByteOrderSwapped(FArchive& Ar, T& Value)
{
static_assert(!TIsSigned<T>::Value, "To reduce the number of template instances, cast 'Value' to a uint16&, uint32& or uint64& prior to the call.");
if (Ar.IsLoading())
{
// Read and swap.
Ar.Serialize(&Value, sizeof(T));
Value = ByteSwap(Value);
}
else // Saving
{
// Swap and write.
T SwappedValue = ByteSwap(Value);
Ar.Serialize(&SwappedValue, sizeof(T));
}
return Ar;
}
} // namespace ArchiveUtil
/*-----------------------------------------------------------------------------
FArchive implementation.
-----------------------------------------------------------------------------*/
FArchiveState::FArchiveState()
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
ActiveFPLB = &InlineFPLB;
#endif
SerializedPropertyChain = nullptr;
#if USE_STABLE_LOCALIZATION_KEYS
LocalizationNamespacePtr = nullptr;
#endif // USE_STABLE_LOCALIZATION_KEYS
Reset();
}
FArchiveState::FArchiveState(const FArchiveState& ArchiveToCopy)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
ActiveFPLB = &InlineFPLB;
#endif
#if USE_STABLE_LOCALIZATION_KEYS
LocalizationNamespacePtr = nullptr;
#endif // USE_STABLE_LOCALIZATION_KEYS
CopyTrivialFArchiveStatusMembers(ArchiveToCopy);
SerializedPropertyChain = nullptr;
SetSerializedPropertyChain(ArchiveToCopy.SerializedPropertyChain, ArchiveToCopy.SerializedProperty);
// Don't know why this is set to false, but this is what the original copying code did
ArIsFilterEditorOnly = false;
bCustomVersionsAreReset = ArchiveToCopy.bCustomVersionsAreReset;
if (ArchiveToCopy.CustomVersionContainer)
{
CustomVersionContainer = new FCustomVersionContainer(*ArchiveToCopy.CustomVersionContainer);
}
else
{
CustomVersionContainer = nullptr;
}
}
FArchiveState& FArchiveState::operator=(const FArchiveState& ArchiveToCopy)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
ActiveFPLB = &InlineFPLB;
ActiveFPLB->Reset();
#endif
CopyTrivialFArchiveStatusMembers(ArchiveToCopy);
SetSerializedPropertyChain(ArchiveToCopy.SerializedPropertyChain, ArchiveToCopy.SerializedProperty);
// Don't know why this is set to false, but this is what the original copying code did
ArIsFilterEditorOnly = false;
bCustomVersionsAreReset = ArchiveToCopy.bCustomVersionsAreReset;
if (ArchiveToCopy.CustomVersionContainer)
{
if (!CustomVersionContainer)
{
CustomVersionContainer = new FCustomVersionContainer(*ArchiveToCopy.CustomVersionContainer);
}
else
{
*CustomVersionContainer = *ArchiveToCopy.CustomVersionContainer;
}
}
else if (CustomVersionContainer)
{
delete CustomVersionContainer;
CustomVersionContainer = nullptr;
}
return *this;
}
FArchiveState::~FArchiveState()
{
checkf(NextProxy == nullptr, TEXT("Archive destroyed before its proxies"));
delete CustomVersionContainer;
delete SerializedPropertyChain;
#if USE_STABLE_LOCALIZATION_KEYS
delete LocalizationNamespacePtr;
#endif // USE_STABLE_LOCALIZATION_KEYS
}
void FArchiveState::Reset()
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
ActiveFPLB->Reset();
#endif
ArUE4Ver = GPackageFileUE4Version;
ArLicenseeUE4Ver = GPackageFileLicenseeUE4Version;
ArEngineVer = FEngineVersion::Current();
ArEngineNetVer = FNetworkVersion::GetEngineNetworkProtocolVersion();
ArGameNetVer = FNetworkVersion::GetGameNetworkProtocolVersion();
ArIsLoading = false;
ArIsSaving = false;
ArIsTransacting = false;
ArIsTextFormat = false;
ArWantBinaryPropertySerialization = false;
ArUseUnversionedPropertySerialization = false;
ArForceUnicode = false;
ArIsPersistent = false;
ArIsError = false;
ArIsCriticalError = false;
ArContainsCode = false;
ArContainsMap = false;
ArRequiresLocalizationGather = false;
ArForceByteSwapping = false;
ArSerializingDefaults = false;
ArIgnoreArchetypeRef = false;
ArNoDelta = false;
ArNoIntraPropertyDelta = false;
ArIgnoreOuterRef = false;
ArIgnoreClassGeneratedByRef = false;
ArIgnoreClassRef = false;
ArAllowLazyLoading = false;
ArIsObjectReferenceCollector = false;
ArIsModifyingWeakAndStrongReferences= false;
ArIsCountingMemory = false;
ArPortFlags = 0;
ArShouldSkipBulkData = false;
ArMaxSerializeSize = 0;
ArIsFilterEditorOnly = false;
ArIsSaveGame = false;
ArIsNetArchive = false;
ArCustomPropertyList = nullptr;
ArUseCustomPropertyList = false;
CookingTargetPlatform = nullptr;
SerializedProperty = nullptr;
delete SerializedPropertyChain;
SerializedPropertyChain = nullptr;
#if USE_STABLE_LOCALIZATION_KEYS
SetBaseLocalizationNamespace(FString());
#endif // USE_STABLE_LOCALIZATION_KEYS
#if WITH_EDITOR
ArDebugSerializationFlags = 0;
#endif
// Reset all custom versions to the current registered versions.
ResetCustomVersions();
}
void FArchiveState::CopyTrivialFArchiveStatusMembers(const FArchiveState& ArchiveToCopy)
{
ArUE4Ver = ArchiveToCopy.ArUE4Ver;
ArLicenseeUE4Ver = ArchiveToCopy.ArLicenseeUE4Ver;
ArEngineVer = ArchiveToCopy.ArEngineVer;
ArEngineNetVer = ArchiveToCopy.ArEngineNetVer;
ArGameNetVer = ArchiveToCopy.ArGameNetVer;
ArIsLoading = ArchiveToCopy.ArIsLoading;
ArIsSaving = ArchiveToCopy.ArIsSaving;
ArIsTransacting = ArchiveToCopy.ArIsTransacting;
ArIsTextFormat = ArchiveToCopy.ArIsTextFormat;
ArWantBinaryPropertySerialization = ArchiveToCopy.ArWantBinaryPropertySerialization;
ArUseUnversionedPropertySerialization = ArchiveToCopy.ArUseUnversionedPropertySerialization;
ArForceUnicode = ArchiveToCopy.ArForceUnicode;
ArIsPersistent = ArchiveToCopy.ArIsPersistent;
ArIsError = ArchiveToCopy.ArIsError;
ArIsCriticalError = ArchiveToCopy.ArIsCriticalError;
ArContainsCode = ArchiveToCopy.ArContainsCode;
ArContainsMap = ArchiveToCopy.ArContainsMap;
ArRequiresLocalizationGather = ArchiveToCopy.ArRequiresLocalizationGather;
ArForceByteSwapping = ArchiveToCopy.ArForceByteSwapping;
ArSerializingDefaults = ArchiveToCopy.ArSerializingDefaults;
ArIgnoreArchetypeRef = ArchiveToCopy.ArIgnoreArchetypeRef;
ArNoDelta = ArchiveToCopy.ArNoDelta;
ArNoIntraPropertyDelta = ArchiveToCopy.ArNoIntraPropertyDelta;
ArIgnoreOuterRef = ArchiveToCopy.ArIgnoreOuterRef;
ArIgnoreClassGeneratedByRef = ArchiveToCopy.ArIgnoreClassGeneratedByRef;
ArIgnoreClassRef = ArchiveToCopy.ArIgnoreClassRef;
ArAllowLazyLoading = ArchiveToCopy.ArAllowLazyLoading;
ArIsObjectReferenceCollector = ArchiveToCopy.ArIsObjectReferenceCollector;
ArIsModifyingWeakAndStrongReferences = ArchiveToCopy.ArIsModifyingWeakAndStrongReferences;
ArIsCountingMemory = ArchiveToCopy.ArIsCountingMemory;
ArPortFlags = ArchiveToCopy.ArPortFlags;
ArShouldSkipBulkData = ArchiveToCopy.ArShouldSkipBulkData;
ArMaxSerializeSize = ArchiveToCopy.ArMaxSerializeSize;
ArIsFilterEditorOnly = ArchiveToCopy.ArIsFilterEditorOnly;
ArIsSaveGame = ArchiveToCopy.ArIsSaveGame;
ArIsNetArchive = ArchiveToCopy.ArIsNetArchive;
ArCustomPropertyList = ArchiveToCopy.ArCustomPropertyList;
ArUseCustomPropertyList = ArchiveToCopy.ArUseCustomPropertyList;
CookingTargetPlatform = ArchiveToCopy.CookingTargetPlatform;
SerializedProperty = ArchiveToCopy.SerializedProperty;
#if USE_STABLE_LOCALIZATION_KEYS
SetBaseLocalizationNamespace(ArchiveToCopy.GetBaseLocalizationNamespace());
#endif // USE_STABLE_LOCALIZATION_KEYS
}
void FArchiveState::LinkProxy(FArchiveState& Inner, FArchiveState& Proxy)
{
Proxy.NextProxy = Inner.NextProxy;
Inner.NextProxy = &Proxy;
}
void FArchiveState::UnlinkProxy(FArchiveState& Inner, FArchiveState& Proxy)
{
FArchiveState* Prev = &Inner;
while (Prev->NextProxy != &Proxy)
{
Prev = Prev->NextProxy;
checkf(Prev, TEXT("Proxy link not found - likely lifetime violation"));
}
Prev->NextProxy = Proxy.NextProxy;
Proxy.NextProxy = nullptr;
}
template<typename T>
FORCEINLINE void FArchiveState::ForEachState(T Func)
{
FArchiveState& RootState = GetInnermostState();
Func(RootState);
for (FArchiveState* Proxy = RootState.NextProxy; Proxy; Proxy = Proxy->NextProxy)
{
Func(*Proxy);
}
}
void FArchiveState::SetArchiveState(const FArchiveState& InState)
{
ForEachState([&InState](FArchiveState& State) { State = InState; });
}
void FArchiveState::SetError()
{
ForEachState([](FArchiveState& State) { State.ArIsError = true; });
}
void FArchiveState::SetCriticalError()
{
ForEachState([](FArchiveState& State) { State.ArIsError = State.ArIsCriticalError = true; });
}
void FArchiveState::ClearError()
{
ForEachState([](FArchiveState& State) { State.ArIsError = false; });
}
/**
* Returns the name of the Archive. Useful for getting the name of the package a struct or object
* is in when a loading error occurs.
*
* This is overridden for the specific Archive Types
**/
FString FArchiveState::GetArchiveName() const
{
return TEXT("FArchive");
}
void FArchiveState::GetSerializedPropertyChain(TArray<class FProperty*>& OutProperties) const
{
if (SerializedPropertyChain)
{
const int32 NumProperties = SerializedPropertyChain->GetNumProperties();
OutProperties.Reserve(NumProperties);
for (int32 PropertyIndex = 0; PropertyIndex < NumProperties; ++PropertyIndex)
{
OutProperties.Add(SerializedPropertyChain->GetPropertyFromStack(PropertyIndex));
}
}
}
void FArchiveState::SetSerializedPropertyChain(const FArchiveSerializedPropertyChain* InSerializedPropertyChain, class FProperty* InSerializedPropertyOverride)
{
if (InSerializedPropertyChain && InSerializedPropertyChain->GetNumProperties() > 0)
{
if (!SerializedPropertyChain)
{
SerializedPropertyChain = new FArchiveSerializedPropertyChain();
}
*SerializedPropertyChain = *InSerializedPropertyChain;
}
else
{
delete SerializedPropertyChain;
SerializedPropertyChain = nullptr;
}
if (InSerializedPropertyOverride)
{
SerializedProperty = InSerializedPropertyOverride;
}
else if (SerializedPropertyChain && SerializedPropertyChain->GetNumProperties() > 0)
{
SerializedProperty = SerializedPropertyChain->GetPropertyFromStack(0);
}
else
{
SerializedProperty = nullptr;
}
}
void FArchive::PushSerializedProperty(class FProperty* InProperty, const bool bIsEditorOnlyProperty)
{
if (InProperty)
{
// Push this property into the chain
if (!SerializedPropertyChain)
{
SerializedPropertyChain = new FArchiveSerializedPropertyChain();
}
SerializedPropertyChain->PushProperty(InProperty, bIsEditorOnlyProperty);
// Update the serialized property pointer with the new head
SerializedProperty = InProperty;
}
}
void FArchive::PopSerializedProperty(class FProperty* InProperty, const bool bIsEditorOnlyProperty)
{
if (InProperty)
{
// Pop this property from the chain
check(SerializedPropertyChain);
SerializedPropertyChain->PopProperty(InProperty, bIsEditorOnlyProperty);
// Update the serialized property pointer with the new head
if (SerializedPropertyChain->GetNumProperties() > 0)
{
SerializedProperty = SerializedPropertyChain->GetPropertyFromStack(0);
}
else
{
SerializedProperty = nullptr;
}
}
}
#if WITH_EDITORONLY_DATA
bool FArchiveState::IsEditorOnlyPropertyOnTheStack() const
{
return SerializedPropertyChain && SerializedPropertyChain->HasEditorOnlyProperty();
}
#endif
#if USE_STABLE_LOCALIZATION_KEYS
void FArchiveState::SetBaseLocalizationNamespace(const FString& InLocalizationNamespace)
{
if (InLocalizationNamespace.IsEmpty())
{
delete LocalizationNamespacePtr;
LocalizationNamespacePtr = nullptr;
}
else
{
if (!LocalizationNamespacePtr)
{
LocalizationNamespacePtr = new FString();
}
*LocalizationNamespacePtr = InLocalizationNamespace;
}
}
FString FArchiveState::GetBaseLocalizationNamespace() const
{
return LocalizationNamespacePtr ? *LocalizationNamespacePtr : FString();
}
void FArchiveState::SetLocalizationNamespace(const FString& InLocalizationNamespace)
{
SetBaseLocalizationNamespace(InLocalizationNamespace);
}
FString FArchiveState::GetLocalizationNamespace() const
{
return GetBaseLocalizationNamespace();
}
#endif // USE_STABLE_LOCALIZATION_KEYS
#if WITH_EDITOR
FArchive::FScopeAddDebugData::FScopeAddDebugData(FArchive& InAr, const FName& DebugData) : Ar(InAr)
{
Ar.PushDebugDataString(DebugData);
}
void FArchive::PushDebugDataString(const FName& DebugData)
{
}
#endif
FArchive& FArchive::operator<<( FText& Value )
{
FText::SerializeText(*this, Value);
return *this;
}
FArchive& FArchive::operator<<(struct FLazyObjectPtr& Value)
{
// The base FArchive does not implement this method. Use FArchiveUObject instead.
UE_LOG(LogSerialization, Fatal, TEXT("FArchive does not support FLazyObjectPtr serialization. Use FArchiveUObject instead."));
return *this;
}
FArchive& FArchive::operator<<(struct FSoftObjectPtr& Value)
{
// The base FArchive does not implement this method. Use FArchiveUObject instead.
UE_LOG(LogSerialization, Fatal, TEXT("FArchive does not support FSoftObjectPtr serialization. Use FArchiveUObject instead."));
return *this;
}
FArchive& FArchive::operator<<(struct FSoftObjectPath& Value)
{
// The base FArchive does not implement this method. Use FArchiveUObject instead.
UE_LOG(LogSerialization, Fatal, TEXT("FArchive does not support FSoftObjectPath serialization. Use FArchiveUObject instead."));
return *this;
}
FArchive& FArchive::operator<<(struct FWeakObjectPtr& Value)
{
// The base FArchive does not implement this method. Use FArchiveUObject instead.
UE_LOG(LogSerialization, Fatal, TEXT("FArchive does not support FWeakObjectPtr serialization. Use FArchiveUObject instead."));
return *this;
}
#if WITH_EDITOR
void FArchive::SerializeBool( bool& D )
{
// Serialize bool as if it were UBOOL (legacy, 32 bit int).
uint32 OldUBoolValue;
#if DEVIRTUALIZE_FLinkerLoad_Serialize
const uint8 * RESTRICT Src = this->ActiveFPLB->StartFastPathLoadBuffer;
if (Src + sizeof(uint32) <= this->ActiveFPLB->EndFastPathLoadBuffer)
{
OldUBoolValue = FPlatformMemory::ReadUnaligned<uint32>(Src);
this->ActiveFPLB->StartFastPathLoadBuffer += 4;
}
else
#endif
{
OldUBoolValue = D ? 1 : 0;
this->Serialize(&OldUBoolValue, sizeof(OldUBoolValue));
}
if (OldUBoolValue > 1)
{
UE_LOG(LogSerialization, Error, TEXT("Invalid boolean encountered while reading archive %s - stream is most likely corrupted."), *GetArchiveName());
this->SetError();
}
D = !!OldUBoolValue;
}
#endif
const FCustomVersionContainer& FArchiveState::GetCustomVersions() const
{
if (!CustomVersionContainer)
{
CustomVersionContainer = new FCustomVersionContainer;
}
if (bCustomVersionsAreReset)
{
bCustomVersionsAreReset = false;
// If the archive is for reading then we want to use currently registered custom versions, otherwise we expect
// serialization code to use UsingCustomVersion to populate the container.
if (this->IsLoading())
{
*CustomVersionContainer = FCurrentCustomVersions::GetAll();
}
else
{
CustomVersionContainer->Empty();
}
}
return *CustomVersionContainer;
}
void FArchiveState::SetCustomVersions(const FCustomVersionContainer& NewVersions)
{
if (!CustomVersionContainer)
{
CustomVersionContainer = new FCustomVersionContainer(NewVersions);
}
else
{
*CustomVersionContainer = NewVersions;
}
bCustomVersionsAreReset = false;
}
void FArchiveState::ResetCustomVersions()
{
bCustomVersionsAreReset = true;
}
void FArchive::UsingCustomVersion(const FGuid& Key)
{
// If we're loading, we want to use the version that the archive was serialized with, not register a new one.
if (IsLoading())
{
return;
}
FCustomVersion RegisteredVersion = FCurrentCustomVersions::Get(Key).GetValue();
const_cast<FCustomVersionContainer&>(GetCustomVersions()).SetVersion(Key, RegisteredVersion.Version, RegisteredVersion.GetFriendlyName());
}
int32 FArchiveState::CustomVer(const FGuid& Key) const
{
auto* CustomVersion = GetCustomVersions().GetVersion(Key);
// If this fails, you have forgotten to make an Ar.UsingCustomVersion call
// before serializing your custom version-dependent object.
check(IsLoading() || CustomVersion);
return CustomVersion ? CustomVersion->Version : -1;
}
void FArchiveState::SetCustomVersion(const FGuid& Key, int32 Version, FName FriendlyName)
{
const_cast<FCustomVersionContainer&>(GetCustomVersions()).SetVersion(Key, Version, FriendlyName);
}
FString FArchiveProxy::GetArchiveName() const
{
return InnerArchive.GetArchiveName();
}
#if USE_STABLE_LOCALIZATION_KEYS
void FArchiveProxy::SetLocalizationNamespace(const FString& InLocalizationNamespace)
{
InnerArchive.SetLocalizationNamespace(InLocalizationNamespace);
}
FString FArchiveProxy::GetLocalizationNamespace() const
{
return InnerArchive.GetLocalizationNamespace();
}
#endif // USE_STABLE_LOCALIZATION_KEYS
/**
* Serialize the given FName as an FString
*/
FArchive& FNameAsStringProxyArchive::operator<<( class FName& N )
{
if (IsLoading())
{
FString LoadedString;
InnerArchive << LoadedString;
N = FName(*LoadedString);
}
else
{
FString SavedString(N.ToString());
InnerArchive << SavedString;
}
return *this;
}
/** Accumulative time spent in IsSaving portion of SerializeCompressed. */
CORE_API double GArchiveSerializedCompressedSavingTime = 0;
// MT compression disabled on console due to memory impact and lack of beneficial usage case.
#define WITH_MULTI_THREADED_COMPRESSION (WITH_EDITORONLY_DATA)
#if WITH_MULTI_THREADED_COMPRESSION
// Helper structure to keep information about async chunks that are in-flight.
class FAsyncCompressionChunk : public FNonAbandonableTask
{
public:
/** Pointer to source (uncompressed) memory. */
void* UncompressedBuffer;
/** Pointer to destination (compressed) memory. */
void* CompressedBuffer;
/** Compressed size in bytes as passed to/ returned from compressor. */
int32 CompressedSize;
/** Uncompressed size in bytes as passed to compressor. */
int32 UncompressedSize;
/** Target platform for compressed data */
int32 BitWindow;
/** Format to compress with */
FName CompressionFormat;
/** Flags to control compression */
ECompressionFlags Flags;
/**
* Constructor, zeros everything
*/
FAsyncCompressionChunk()
: UncompressedBuffer(0)
, CompressedBuffer(0)
, CompressedSize(0)
, UncompressedSize(0)
, BitWindow(DEFAULT_ZLIB_BIT_WINDOW)
, CompressionFormat(NAME_Zlib)
, Flags(COMPRESS_NoFlags)
{
}
/**
* Performs the async compression
*/
void DoWork()
{
// upgrade old flag method
if ((Flags & COMPRESS_DeprecatedFormatFlagsMask) != 0)
{
UE_LOG(LogSerialization, Warning, TEXT("Old style compression flags are being used with FAsyncCompressionChunk, please update any code using this!"));
CompressionFormat = FCompression::GetCompressionFormatFromDeprecatedFlags(Flags);
}
// Compress from memory to memory.
verify( FCompression::CompressMemory(CompressionFormat, CompressedBuffer, CompressedSize, UncompressedBuffer, UncompressedSize, Flags, BitWindow) );
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FAsyncCompressionChunk, STATGROUP_ThreadPoolAsyncTasks);
}
};
#endif // WITH_MULTI_THREADED_COMPRESSION
void FArchive::SerializeCompressed(void* V, int64 Length, FName CompressionFormat, ECompressionFlags Flags, bool bTreatBufferAsFileReader)
{
if( IsLoading() )
{
if (CompressionFormat == NAME_Zlib && FPlatformProperties::GetZlibReplacementFormat() != nullptr)
{
// use this platform's replacement format in case it's not zlib
CompressionFormat = FPlatformProperties::GetZlibReplacementFormat();
}
// Serialize package file tag used to determine endianess.
FCompressedChunkInfo PackageFileTag;
PackageFileTag.CompressedSize = 0;
PackageFileTag.UncompressedSize = 0;
*this << PackageFileTag;
bool bWasByteSwapped = PackageFileTag.CompressedSize != PACKAGE_FILE_TAG;
// Read in base summary.
FCompressedChunkInfo Summary;
*this << Summary;
bool bHeaderWasValid = true;
if (bWasByteSwapped)
{
bHeaderWasValid = PackageFileTag.CompressedSize == PACKAGE_FILE_TAG_SWAPPED;
if (bHeaderWasValid)
{
Summary.CompressedSize = BYTESWAP_ORDER64(Summary.CompressedSize);
Summary.UncompressedSize = BYTESWAP_ORDER64(Summary.UncompressedSize);
PackageFileTag.UncompressedSize = BYTESWAP_ORDER64(PackageFileTag.UncompressedSize);
}
}
else
{
bHeaderWasValid = PackageFileTag.CompressedSize == PACKAGE_FILE_TAG; //-V547
}
if (!bHeaderWasValid)
{
UE_LOG(LogSerialization, Log, TEXT("ArchiveName: %s"), *GetArchiveName());
UE_LOG(LogSerialization, Log, TEXT("Archive UE4 Version: %d"), UE4Ver());
UE_LOG(LogSerialization, Log, TEXT("Archive Licensee Version: %d"), LicenseeUE4Ver());
UE_LOG(LogSerialization, Log, TEXT("Position: %lld"), Tell());
UE_LOG(LogSerialization, Log, TEXT("Read Size: %lld"), Length);
UE_LOG(LogSerialization, Fatal, TEXT("BulkData compressed header read error. This package may be corrupt!"));
}
// Handle change in compression chunk size in backward compatible way.
int64 LoadingCompressionChunkSize = PackageFileTag.UncompressedSize;
if (LoadingCompressionChunkSize == PACKAGE_FILE_TAG)
{
LoadingCompressionChunkSize = LOADING_COMPRESSION_CHUNK_SIZE;
}
// Figure out how many chunks there are going to be based on uncompressed size and compression chunk size.
int64 TotalChunkCount = (Summary.UncompressedSize + LoadingCompressionChunkSize - 1) / LoadingCompressionChunkSize;
// Allocate compression chunk infos and serialize them, keeping track of max size of compression chunks used.
FCompressedChunkInfo* CompressionChunks = new FCompressedChunkInfo[TotalChunkCount];
int64 MaxCompressedSize = 0;
for( int32 ChunkIndex=0; ChunkIndex<TotalChunkCount; ChunkIndex++ )
{
*this << CompressionChunks[ChunkIndex];
if (bWasByteSwapped)
{
CompressionChunks[ChunkIndex].CompressedSize = BYTESWAP_ORDER64( CompressionChunks[ChunkIndex].CompressedSize );
CompressionChunks[ChunkIndex].UncompressedSize = BYTESWAP_ORDER64( CompressionChunks[ChunkIndex].UncompressedSize );
}
MaxCompressedSize = FMath::Max( CompressionChunks[ChunkIndex].CompressedSize, MaxCompressedSize );
}
// Set up destination pointer and allocate memory for compressed chunk[s] (one at a time).
uint8* Dest = (uint8*) V;
void* CompressedBuffer = FMemory::Malloc( MaxCompressedSize );
// Iterate over all chunks, serialize them into memory and decompress them directly into the destination pointer
for( int64 ChunkIndex=0; ChunkIndex<TotalChunkCount; ChunkIndex++ )
{
const FCompressedChunkInfo& Chunk = CompressionChunks[ChunkIndex];
// Read compressed data.
Serialize( CompressedBuffer, Chunk.CompressedSize );
// Decompress into dest pointer directly.
bool bUncompressMemorySucceeded = FCompression::UncompressMemory( CompressionFormat, Dest, Chunk.UncompressedSize, CompressedBuffer, Chunk.CompressedSize, COMPRESS_NoFlags);
verifyf(bUncompressMemorySucceeded, TEXT("Failed to uncompress data in %s. Check log for details."), *GetArchiveName()); // And advance it by read amount.
Dest += Chunk.UncompressedSize;
}
// Free up allocated memory.
FMemory::Free( CompressedBuffer );
delete [] CompressionChunks;
}
else if( IsSaving() )
{
SCOPE_SECONDS_COUNTER(GArchiveSerializedCompressedSavingTime);
check( Length > 0 );
// if there's a cooking target, and it wants to replace Zlib compression with another format, use it. When loading,
// the platform will replace Zlib with that format above
if (CompressionFormat == NAME_Zlib && CookingTargetPlatform != nullptr)
{
// use the replacement format
CompressionFormat = CookingTargetPlatform->GetZlibReplacementFormat();
}
// Serialize package file tag used to determine endianess in LoadCompressedData.
FCompressedChunkInfo PackageFileTag;
PackageFileTag.CompressedSize = PACKAGE_FILE_TAG;
PackageFileTag.UncompressedSize = GSavingCompressionChunkSize;
*this << PackageFileTag;
// Figure out how many chunks there are going to be based on uncompressed size and compression chunk size.
int64 TotalChunkCount = (Length + GSavingCompressionChunkSize - 1) / GSavingCompressionChunkSize + 1;
// Keep track of current position so we can later seek back and overwrite stub compression chunk infos.
int64 StartPosition = Tell();
// Allocate compression chunk infos and serialize them so we can later overwrite the data.
FCompressedChunkInfo* CompressionChunks = new FCompressedChunkInfo[TotalChunkCount];
for( int64 ChunkIndex=0; ChunkIndex<TotalChunkCount; ChunkIndex++ )
{
*this << CompressionChunks[ChunkIndex];
}
// The uncompressd size is equal to the passed in length.
CompressionChunks[0].UncompressedSize = Length;
// Zero initialize compressed size so we can update it during chunk compression.
CompressionChunks[0].CompressedSize = 0;
#if WITH_MULTI_THREADED_COMPRESSION
#define MAX_COMPRESSION_JOBS (16)
// Don't scale more than 16x to avoid going overboard wrt temporary memory.
FAsyncTask<FAsyncCompressionChunk> AsyncChunks[MAX_COMPRESSION_JOBS];
// used to keep track of which job is the next one we need to retire
int32 AsyncChunkIndex[MAX_COMPRESSION_JOBS]={0};
static uint32 GNumUnusedThreads_SerializeCompressed = -1;
if (GNumUnusedThreads_SerializeCompressed == (uint32)-1)
{
// one-time initialization
GNumUnusedThreads_SerializeCompressed = 1;
// if we should use all available cores then we want to compress with all
if( FParse::Param(FCommandLine::Get(), TEXT("USEALLAVAILABLECORES")) == true )
{
GNumUnusedThreads_SerializeCompressed = 0;
}
}
// Maximum number of concurrent async tasks we're going to kick off. This is based on the number of processors
// available in the system.
int32 MaxConcurrentAsyncChunks = FMath::Clamp<int32>( FPlatformMisc::NumberOfCores() - GNumUnusedThreads_SerializeCompressed, 1, MAX_COMPRESSION_JOBS );
if (FParse::Param(FCommandLine::Get(), TEXT("MTCHILD")))
{
// throttle this back when doing MT cooks
MaxConcurrentAsyncChunks = FMath::Min<int32>( MaxConcurrentAsyncChunks,4 );
}
// Number of chunks left to finalize.
int64 NumChunksLeftToFinalize = (Length + GSavingCompressionChunkSize - 1) / GSavingCompressionChunkSize;
// Number of chunks left to kick off
int64 NumChunksLeftToKickOff = NumChunksLeftToFinalize;
// Start at index 1 as first chunk info is summary.
int64 CurrentChunkIndex = 1;
// Start at index 1 as first chunk info is summary.
int64 RetireChunkIndex = 1;
// Number of bytes remaining to kick off compression for.
int64 BytesRemainingToKickOff = Length;
// Pointer to src data if buffer is memory pointer, NULL if it's a FArchive.
uint8* SrcBuffer = bTreatBufferAsFileReader ? NULL : (uint8*)V;
check(!bTreatBufferAsFileReader || ((FArchive*)V)->IsLoading());
check(NumChunksLeftToFinalize);
// Loop while there is work left to do based on whether we have finalized all chunks yet.
while( NumChunksLeftToFinalize )
{
// If true we are waiting for async tasks to complete and should wait to complete some
// if there are no async tasks finishing this iteration.
bool bNeedToWaitForAsyncTask = false;
// Try to kick off async tasks if there are chunks left to kick off.
if( NumChunksLeftToKickOff )
{
// Find free index based on looking at uncompressed size. We can't use the thread counter
// for this as that might be a chunk ready for finalization.
int32 FreeIndex = INDEX_NONE;
for( int32 i=0; i<MaxConcurrentAsyncChunks; i++ )
{
if( !AsyncChunkIndex[i] )
{
FreeIndex = i;
check(AsyncChunks[FreeIndex].IsIdle()); // this is not supposed to be in use
break;
}
}
// Kick off async compression task if we found a chunk for it.
if( FreeIndex != INDEX_NONE )
{
FAsyncCompressionChunk& NewChunk = AsyncChunks[FreeIndex].GetTask();
// 2 times the uncompressed size should be more than enough; the compressed data shouldn't be that much larger
NewChunk.CompressedSize = 2 * GSavingCompressionChunkSize;
// Allocate compressed buffer placeholder on first use.
if( NewChunk.CompressedBuffer == NULL )
{
NewChunk.CompressedBuffer = FMemory::Malloc( NewChunk.CompressedSize );
}
// By default everything is chunked up into GSavingCompressionChunkSize chunks.
NewChunk.UncompressedSize = FMath::Min( BytesRemainingToKickOff, (int64)GSavingCompressionChunkSize );
check(NewChunk.UncompressedSize>0);
// Need to serialize source data if passed in pointer is an FArchive.
if( bTreatBufferAsFileReader )
{
// Allocate memory on first use. We allocate the maximum amount to allow reuse.
if( !NewChunk.UncompressedBuffer )
{
NewChunk.UncompressedBuffer = FMemory::Malloc(GSavingCompressionChunkSize);
}
((FArchive*)V)->Serialize(NewChunk.UncompressedBuffer, NewChunk.UncompressedSize);
}
// Advance src pointer by amount to be compressed.
else
{
NewChunk.UncompressedBuffer = SrcBuffer;
SrcBuffer += NewChunk.UncompressedSize;
}
// Update status variables for tracking how much work is left, what to do next.
BytesRemainingToKickOff -= NewChunk.UncompressedSize;
AsyncChunkIndex[FreeIndex] = CurrentChunkIndex++;
NewChunk.Flags = Flags;
NewChunk.CompressionFormat = CompressionFormat;
NumChunksLeftToKickOff--;
AsyncChunks[FreeIndex].StartBackgroundTask();
}
// No chunks were available to use, complete some
else
{
bNeedToWaitForAsyncTask = true;
}
}
// Wait for the oldest task to finish instead of spinning
if (NumChunksLeftToKickOff == 0)
{
bNeedToWaitForAsyncTask = true;
}
// Index of oldest chunk, needed as we need to serialize in order.
int32 OldestAsyncChunkIndex = INDEX_NONE;
for( int32 i=0; i<MaxConcurrentAsyncChunks; i++ )
{
check(AsyncChunkIndex[i] == 0 || AsyncChunkIndex[i] >= RetireChunkIndex);
check(AsyncChunkIndex[i] < RetireChunkIndex + MaxConcurrentAsyncChunks);
if (AsyncChunkIndex[i] == RetireChunkIndex)
{
OldestAsyncChunkIndex = i;
}
}
check(OldestAsyncChunkIndex != INDEX_NONE); // the retire chunk better be outstanding
bool ChunkReady;
if (bNeedToWaitForAsyncTask)
{
// This guarantees that the async work has finished, doing it on this thread if it hasn't been started
AsyncChunks[OldestAsyncChunkIndex].EnsureCompletion();
ChunkReady = true;
}
else
{
ChunkReady = AsyncChunks[OldestAsyncChunkIndex].IsDone();
}
if (ChunkReady)
{
FAsyncCompressionChunk& DoneChunk = AsyncChunks[OldestAsyncChunkIndex].GetTask();
// Serialize the data via archive.
Serialize( DoneChunk.CompressedBuffer, DoneChunk.CompressedSize );
// Update associated chunk.
int64 CompressionChunkIndex = RetireChunkIndex++;
check(CompressionChunkIndex<TotalChunkCount);
CompressionChunks[CompressionChunkIndex].CompressedSize = DoneChunk.CompressedSize;
CompressionChunks[CompressionChunkIndex].UncompressedSize = DoneChunk.UncompressedSize;
// Keep track of total compressed size, stored in first chunk.
CompressionChunks[0].CompressedSize += DoneChunk.CompressedSize;
// Clean up chunk. Src and dst buffer are not touched as the contain allocations we keep till the end.
AsyncChunkIndex[OldestAsyncChunkIndex] = 0;
DoneChunk.CompressedSize = 0;
DoneChunk.UncompressedSize = 0;
// Finalized one :)
NumChunksLeftToFinalize--;
bNeedToWaitForAsyncTask = false;
}
}
// Free intermediate buffer storage.
for( int32 i=0; i<MaxConcurrentAsyncChunks; i++ )
{
// Free temporary compressed buffer storage.
FMemory::Free( AsyncChunks[i].GetTask().CompressedBuffer );
AsyncChunks[i].GetTask().CompressedBuffer = NULL;
// Free temporary uncompressed buffer storage if data was serialized in.
if( bTreatBufferAsFileReader )
{
FMemory::Free( AsyncChunks[i].GetTask().UncompressedBuffer );
AsyncChunks[i].GetTask().UncompressedBuffer = NULL;
}
}
#else
// Set up source pointer amount of data to copy (in bytes)
uint8* Src;
// allocate memory to read into
if (bTreatBufferAsFileReader)
{
Src = (uint8*)FMemory::Malloc(GSavingCompressionChunkSize);
check(((FArchive*)V)->IsLoading());
}
else
{
Src = (uint8*) V;
}
int64 BytesRemaining = Length;
// Start at index 1 as first chunk info is summary.
int32 CurrentChunkIndex = 1;
// 2 times the uncompressed size should be more than enough; the compressed data shouldn't be that much larger
int64 CompressedBufferSize = 2 * GSavingCompressionChunkSize;
void* CompressedBuffer = FMemory::Malloc( CompressedBufferSize );
while( BytesRemaining > 0 )
{
int64 BytesToCompress = FMath::Min( BytesRemaining, (int64)GSavingCompressionChunkSize );
int64 CompressedSize = CompressedBufferSize;
// read in the next chunk from the reader
if (bTreatBufferAsFileReader)
{
((FArchive*)V)->Serialize(Src, BytesToCompress);
}
check(CompressedSize < INT_MAX);
int32 CompressedSizeInt = (int32)CompressedSize;
verify( FCompression::CompressMemory( CompressionFormat, CompressedBuffer, CompressedSizeInt, Src, BytesToCompress, Flags ) );
CompressedSize = CompressedSizeInt;
// move to next chunk if not reading from file
if (!bTreatBufferAsFileReader)
{
Src += BytesToCompress;
}
Serialize( CompressedBuffer, CompressedSize );
// Keep track of total compressed size, stored in first chunk.
CompressionChunks[0].CompressedSize += CompressedSize;
// Update current chunk.
check(CurrentChunkIndex<TotalChunkCount);
CompressionChunks[CurrentChunkIndex].CompressedSize = CompressedSize;
CompressionChunks[CurrentChunkIndex].UncompressedSize = BytesToCompress;
CurrentChunkIndex++;
BytesRemaining -= GSavingCompressionChunkSize;
}
// free the buffer we read into
if (bTreatBufferAsFileReader)
{
FMemory::Free(Src);
}
// Free allocated memory.
FMemory::Free( CompressedBuffer );
#endif
// Overrwrite chunk infos by seeking to the beginning, serializing the data and then
// seeking back to the end.
auto EndPosition = Tell();
// Seek to the beginning.
Seek( StartPosition );
// Serialize chunk infos.
for( int32 ChunkIndex=0; ChunkIndex<TotalChunkCount; ChunkIndex++ )
{
*this << CompressionChunks[ChunkIndex];
}
// Seek back to end.
Seek( EndPosition );
// Free intermediate data.
delete [] CompressionChunks;
}
}
void FArchive::ByteSwap(void* V, int32 Length)
{
uint8* Ptr = (uint8*)V;
int32 Top = Length - 1;
int32 Bottom = 0;
while (Bottom < Top)
{
Swap(Ptr[Top--], Ptr[Bottom++]);
}
}
FArchive& FArchive::SerializeByteOrderSwapped(void* V, int32 Length)
{
if (IsLoading())
{
Serialize(V, Length); // Read.
ByteSwap(V, Length); // Swap.
}
else // Writing
{
ByteSwap(V, Length); // Swap V.
Serialize(V, Length); // Write V.
ByteSwap(V, Length); // Swap V back to its original byte order to prevent caller from observing V swapped.
}
return *this;
}
FArchive& FArchive::SerializeByteOrderSwapped(uint16& Value)
{
return ArchiveUtil::SerializeByteOrderSwapped(*this, Value);
}
FArchive& FArchive::SerializeByteOrderSwapped(uint32& Value)
{
return ArchiveUtil::SerializeByteOrderSwapped(*this, Value);
}
FArchive& FArchive::SerializeByteOrderSwapped(uint64& Value)
{
return ArchiveUtil::SerializeByteOrderSwapped(*this, Value);
}
void FArchive::SerializeIntPacked(uint32& Value)
{
if (IsLoading())
{
Value = 0;
uint8 cnt = 0;
uint8 more = 1;
while(more)
{
uint8 NextByte;
Serialize(&NextByte, 1); // Read next byte
more = NextByte & 1; // Check 1 bit to see if theres more after this
NextByte = NextByte >> 1; // Shift to get actual 7 bit value
Value += NextByte << (7 * cnt++); // Add to total value
}
}
else
{
uint8 PackedBytes[5];
int32 PackedByteCount = 0;
uint32 Remaining = Value;
while(true)
{
uint8 nextByte = Remaining & 0x7f; // Get next 7 bits to write
Remaining = Remaining >> 7; // Update remaining
nextByte = nextByte << 1; // Make room for 'more' bit
if( Remaining > 0)
{
nextByte |= 1; // set more bit
PackedBytes[PackedByteCount++] = nextByte;
}
else
{
PackedBytes[PackedByteCount++] = nextByte;
break;
}
}
Serialize(PackedBytes, PackedByteCount); // Actually serialize the bytes we made
}
}
void FArchive::LogfImpl(const TCHAR* Fmt, ...)
{
// We need to use malloc here directly as GMalloc might not be safe, e.g. if called from within GMalloc!
int32 BufferSize = 1024;
TCHAR* Buffer = NULL;
int32 Result = -1;
while(Result == -1)
{
FMemory::SystemFree(Buffer);
Buffer = (TCHAR*) FMemory::SystemMalloc( BufferSize * sizeof(TCHAR) );
GET_VARARGS_RESULT( Buffer, BufferSize, BufferSize-1, Fmt, Fmt, Result );
BufferSize *= 2;
};
Buffer[Result] = 0;
// Convert to ANSI and serialize as ANSI char.
for( int32 i=0; i<Result; i++ )
{
ANSICHAR Char = CharCast<ANSICHAR>( Buffer[i] );
Serialize( &Char, 1 );
}
// Write out line terminator.
for( int32 i=0; LINE_TERMINATOR[i]; i++ )
{
ANSICHAR Char = LINE_TERMINATOR[i];
Serialize( &Char, 1 );
}
// Free temporary buffers.
FMemory::SystemFree( Buffer );
}
void FArchiveState::SetUE4Ver(int32 InVer)
{
ArUE4Ver = InVer;
}
void FArchiveState::SetLicenseeUE4Ver(int32 InVer)
{
ArLicenseeUE4Ver = InVer;
}
void FArchiveState::SetEngineVer(const FEngineVersionBase& InVer)
{
ArEngineVer = InVer;
}
void FArchiveState::SetEngineNetVer(const uint32 InEngineNetVer)
{
ArEngineNetVer = InEngineNetVer;
}
void FArchiveState::SetGameNetVer(const uint32 InGameNetVer)
{
ArGameNetVer = InGameNetVer;
}
void FArchiveState::SetIsLoading(bool bInIsLoading)
{
ArIsLoading = bInIsLoading;
}
void FArchiveState::SetIsSaving(bool bInIsSaving)
{
ArIsSaving = bInIsSaving;
}
void FArchiveState::SetIsTransacting(bool bInIsTransacting)
{
ArIsTransacting = bInIsTransacting;
}
void FArchiveState::SetIsTextFormat(bool bInIsTextFormat)
{
ArIsTextFormat = bInIsTextFormat;
}
void FArchiveState::SetWantBinaryPropertySerialization(bool bInWantBinaryPropertySerialization)
{
ArWantBinaryPropertySerialization = bInWantBinaryPropertySerialization;
}
void FArchiveState::SetUseUnversionedPropertySerialization(bool bInUseUnversioned)
{
ArUseUnversionedPropertySerialization = bInUseUnversioned;
}
void FArchiveState::SetForceUnicode(bool bInForceUnicode)
{
ArForceUnicode = bInForceUnicode;
}
void FArchiveState::SetIsPersistent(bool bInIsPersistent)
{
ArIsPersistent = bInIsPersistent;
}
static_assert(sizeof(FArchive) == sizeof(FArchiveState), "New FArchive members should be added to FArchiveState instead");
PRAGMA_ENABLE_UNSAFE_TYPECAST_WARNINGS
archive.h
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Misc/VarArgs.h"
#include "Misc/AssertionMacros.h"
#include "Templates/EnableIf.h"
#include "Templates/IsEnumClass.h"
#include "Templates/Function.h"
#include "HAL/PlatformProperties.h"
#include "Misc/CompressionFlags.h"
#include "Misc/EngineVersionBase.h"
#include "Internationalization/TextNamespaceFwd.h"
#include "Templates/IsValidVariadicFunctionArg.h"
#include "Templates/AndOrNot.h"
#include "Templates/IsArrayOrRefOfType.h"
#include "Templates/IsSigned.h"
class FArchive;
class FCustomVersionContainer;
class FLinker;
class FName;
class FString;
class FText;
class ITargetPlatform;
class UObject;
class FProperty;
struct FUntypedBulkData;
struct FArchiveSerializedPropertyChain;
template<class TEnum> class TEnumAsByte;
typedef TFunction<bool (double RemainingTime)> FExternalReadCallback;
struct FUObjectSerializeContext;
class FField;
enum class EFileRegionType : uint8;
// Temporary while we shake out the EDL at boot
#define USE_EVENT_DRIVEN_ASYNC_LOAD_AT_BOOT_TIME (1)
#if USE_EVENT_DRIVEN_ASYNC_LOAD_AT_BOOT_TIME
#define EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME (1)
#else
#define EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME (!GIsInitialLoad)
#endif
#define DEVIRTUALIZE_FLinkerLoad_Serialize (!WITH_EDITORONLY_DATA)
// Helper macro to make serializing a bitpacked boolean in an archive easier
#define FArchive_Serialize_BitfieldBool(ARCHIVE, BITFIELD_BOOL) { bool TEMP_BITFIELD_BOOL = BITFIELD_BOOL; ARCHIVE << TEMP_BITFIELD_BOOL; BITFIELD_BOOL = TEMP_BITFIELD_BOOL; }
struct CORE_API FArchiveState
{
private:
// Only FArchive is allowed to instantiate this, by inheritance
friend class FArchive;
FArchiveState();
/** Copy constructor. */
FArchiveState(const FArchiveState&);
/**
* Copy assignment operator.
*
* @param ArchiveToCopy The archive to copy from.
*/
FArchiveState& operator=(const FArchiveState& ArchiveToCopy);
virtual ~FArchiveState() = 0;
protected:
static void LinkProxy(FArchiveState& Inner, FArchiveState& Proxy);
static void UnlinkProxy(FArchiveState& Inner, FArchiveState& Proxy);
public:
/**
* Returns lowest level archive state, proxy archives will override this.
*/
virtual FArchiveState& GetInnermostState()
{
return *this;
}
/**
* Modifies current archive state, can be used to override flags.
*/
void SetArchiveState(const FArchiveState& InState);
/**
* Sets ArIsError to true. Also sets error in the proxy archiver if one is wrapping this.
*/
void SetError();
/**
* Sets ArIsError to false, this does not clear any CriticalErrors
*/
void ClearError();
/**
* Sets the archiver IsCriticalError and IsError to true. Also sets CriticalError in the proxy archiver if one is wrapping this.
*/
void SetCriticalError();
/**
* Called to get the computed size from a size-detecting archive after it has finished serializing.
*/
virtual void CountBytes(SIZE_T InNum, SIZE_T InMax) { }
/**
* Returns the name of the Archive. Useful for getting the name of the package a struct or object
* is in when a loading error occurs.
*
* This is overridden for the specific Archive Types
*/
virtual FString GetArchiveName() const;
/**
* If this archive is a FLinkerLoad or FLinkerSave, returns a pointer to the ULinker portion.
*
* @return The linker, or nullptr if the archive is not a linker.
*/
virtual FLinker* GetLinker()
{
return nullptr;
}
/**
* Returns the current location within the backing data storage, which can possibly be passed to Seek later to restore a read/write location.
* If this returns -1, there is no backing data storage and Seek will not function.
*/
virtual int64 Tell()
{
return INDEX_NONE;
}
/** Returns total size of the backing data storage. */
virtual int64 TotalSize()
{
return INDEX_NONE;
}
/** Returns true if the current location within the backing data storage is at the end, always returns false if there is no storage. */
virtual bool AtEnd()
{
int64 Pos = Tell();
return ((Pos != INDEX_NONE) && (Pos >= TotalSize()));
}
/** Returns true if data larger than 1 byte should be swapped to deal with endian mismatches. */
FORCEINLINE bool IsByteSwapping()
{
#if PLATFORM_LITTLE_ENDIAN
bool SwapBytes = ArForceByteSwapping;
#else
bool SwapBytes = this->IsPersistent();
#endif
return SwapBytes;
}
/** Sets a flag indicating that this archive contains native or generated code. */
void ThisContainsCode()
{
ArContainsCode = true;
}
/** Sets a flag indicating that this archive contains a ULevel or UWorld object. */
void ThisContainsMap()
{
ArContainsMap = true;
}
/** Sets a flag indicating that this archive contains data required to be gathered for localization. */
void ThisRequiresLocalizationGather()
{
ArRequiresLocalizationGather = true;
}
/**
* Called to retrieve the archetype from the event driven loader.
* If this returns null, then call GetArchetype yourself.
*/
virtual UObject* GetArchetypeFromLoader(const UObject* Obj)
{
return nullptr;
}
/** Returns the global engine serialization version used for this archive. */
FORCEINLINE int32 UE4Ver() const
{
return ArUE4Ver;
}
/** Returns the licensee-specific version used for this archive, will be 0 by default. */
FORCEINLINE int32 LicenseeUE4Ver() const
{
return ArLicenseeUE4Ver;
}
/** Returns the compiled engine version used for this archive. */
FORCEINLINE FEngineVersionBase EngineVer() const
{
return ArEngineVer;
}
/** Returns the engine-global network protocol version for this archive. */
FORCEINLINE uint32 EngineNetVer() const
{
return ArEngineNetVer;
}
/** Returns the game-specific network protocol version for this archive. */
FORCEINLINE uint32 GameNetVer() const
{
return ArGameNetVer;
}
/**
* Queries a custom version from the archive. If the archive is being used to write, the custom version must have already been registered.
*
* @param Key The guid of the custom version to query.
* @return The version number, or 0 if the custom tag isn't stored in the archive.
*/
int32 CustomVer(const struct FGuid& Key) const;
/** Returns true if this archive is for loading data. */
FORCEINLINE bool IsLoading() const
{
return ArIsLoading;
}
/** Returns true if this archive is for saving data, this can also be a pre-save preparation archive. */
FORCEINLINE bool IsSaving() const
{
return ArIsSaving;
}
/** Returns true if this archive is transacting, which is used to keep track of changes to objects for things like the editor undo system. */
FORCEINLINE bool IsTransacting() const
{
if (FPlatformProperties::HasEditorOnlyData())
{
return ArIsTransacting;
}
else
{
return false;
}
}
/**
* Returns true if this archive serializes to a structured text format.
* Text format archives should use high level constructs from FStructuredArchive for delimiting data rather than manually seeking through the file.
*/
FORCEINLINE bool IsTextFormat() const
{
return (ArIsTextFormat && WITH_TEXT_ARCHIVE_SUPPORT);
}
/** Returns true if this archive wants properties to be serialized in binary form instead of safer but slower tagged form. */
FORCEINLINE bool WantBinaryPropertySerialization() const
{
return ArWantBinaryPropertySerialization;
}
/**
* Returns true if tagged property serialization should be replaced by faster unversioned serialization.
* This assumes writer and reader share the same property definitions.
*/
FORCEINLINE bool UseUnversionedPropertySerialization() const
{
return ArUseUnversionedPropertySerialization;
}
/** Returns true if this archive wants to always save strings in UTF16 format even if they are ANSI characters. */
FORCEINLINE bool IsForcingUnicode() const
{
return ArForceUnicode;
}
/**
* Returns true if this archive is saving or loading data destined for persistent storage and should skip transient data.
* This is also true for some intermediate archives for tasks like duplication that are eventually destined for persistent storage.
*/
FORCEINLINE bool IsPersistent() const
{
return ArIsPersistent;
}
/** Returns true if this archive contains errors, which means that further serialization is generally not safe. */
FORCEINLINE bool IsError() const
{
return ArIsError;
}
FORCEINLINE bool GetError() const
{
return ArIsError;
}
/** Returns true if this archive contains critical errors that cannot be recovered from. */
FORCEINLINE bool IsCriticalError() const
{
return ArIsCriticalError;
}
/** Returns true if this archive contains native or generated code. */
FORCEINLINE bool ContainsCode() const
{
return ArContainsCode;
}
/** Returns true if this archive contains a ULevel or UWorld object. */
FORCEINLINE bool ContainsMap() const
{
return ArContainsMap;
}
/** Returns true if this archive contains data required to be gathered for localization. */
FORCEINLINE bool RequiresLocalizationGather() const
{
return ArRequiresLocalizationGather;
}
/** Returns true if this archive should always swap bytes, ignoring endian rules. */
FORCEINLINE bool ForceByteSwapping() const
{
return ArForceByteSwapping;
}
/** Returns true if this archive is currently serializing class/struct default values. */
FORCEINLINE bool IsSerializingDefaults() const
{
return (ArSerializingDefaults > 0) ? true : false;
}
/** Returns true if this archive should ignore archetype references for structs and classes. */
FORCEINLINE bool IsIgnoringArchetypeRef() const
{
return ArIgnoreArchetypeRef;
}
/** Returns true if this archive should handle delta serialization for properties. */
FORCEINLINE bool DoDelta() const
{
return !ArNoDelta;
}
/** Returns true if this archive should perform delta serialization within properties (e.g. TMaps and TSets). */
FORCEINLINE bool DoIntraPropertyDelta() const
{
return !ArNoIntraPropertyDelta;
}
/** Returns true if this archive should ignore the Outer reference in UObject. */
FORCEINLINE bool IsIgnoringOuterRef() const
{
return ArIgnoreOuterRef;
}
/** Returns true if this archive should ignore the ClassGeneratedBy reference in UClass. */
FORCEINLINE bool IsIgnoringClassGeneratedByRef() const
{
return ArIgnoreClassGeneratedByRef;
}
/** Returns true if this archive should ignore the Class reference in UObject. */
FORCEINLINE bool IsIgnoringClassRef() const
{
return ArIgnoreClassRef;
}
/** Returns true if this archive sould allow lazy loading of bulk / secondary data. */
FORCEINLINE bool IsAllowingLazyLoading() const
{
return ArAllowLazyLoading;
}
/**
* Returns true if this archive is only looking for UObject references.
* This can be false for reference collectors looking for more general references.
*/
FORCEINLINE bool IsObjectReferenceCollector() const
{
return ArIsObjectReferenceCollector;
}
/** Returns true if this archive should modify/search weak object references as well as strong ones. */
FORCEINLINE bool IsModifyingWeakAndStrongReferences() const
{
return ArIsModifyingWeakAndStrongReferences;
}
/** Returns true if this archive is counting memory, normally CountBytes is called to get the size. */
FORCEINLINE bool IsCountingMemory() const
{
return ArIsCountingMemory;
}
/** Returns this archive's property serialization modifier flags. */
FORCEINLINE uint32 GetPortFlags() const
{
return ArPortFlags;
}
/** Checks to see if any of the passed in property serialization modifier flags are set. */
FORCEINLINE bool HasAnyPortFlags(uint32 Flags) const
{
return ((ArPortFlags & Flags) != 0);
}
/** Checks to see if all of the passed in property serialization modifier flags are set. */
FORCEINLINE bool HasAllPortFlags(uint32 Flags) const
{
return ((ArPortFlags & Flags) == Flags);
}
/** Returns the editor-only debug serialization flags. */
FORCEINLINE uint32 GetDebugSerializationFlags() const
{
#if WITH_EDITOR
return ArDebugSerializationFlags;
#else
return 0;
#endif
}
/** Returns true if this archive should ignore bulk data. */
FORCEINLINE bool ShouldSkipBulkData() const
{
return ArShouldSkipBulkData;
}
/** Returns the maximum size of data that this archive is allowed to serialize. */
FORCEINLINE int64 GetMaxSerializeSize() const
{
return ArMaxSerializeSize;
}
/**
* Gets the custom version numbers for this archive.
* These are used to check for system or game-specific version numbers.
*
* @return The container of custom versions in the archive.
*/
virtual const FCustomVersionContainer& GetCustomVersions() const;
/**
* Sets the custom version numbers for this archive.
*
* @param CustomVersionContainer - The container of custom versions to copy into the archive.
*/
virtual void SetCustomVersions(const FCustomVersionContainer& CustomVersionContainer);
/** Resets the custom version numbers for this archive. */
virtual void ResetCustomVersions();
/**
* Sets a specific custom version
*
* @param Key - The guid of the custom version to query.
* @param Version - The version number to set key to
* @param FriendlyName - Friendly name corresponding to the key
*/
void SetCustomVersion(const struct FGuid& Key, int32 Version, FName FriendlyName);
/**
* Toggle byte order swapping. This is needed in rare cases when we already know that the data
* swapping has already occurred or if we know that it will be handled later.
*
* @param Enabled set to true to enable byte order swapping
*/
void SetByteSwapping(bool Enabled)
{
ArForceByteSwapping = Enabled;
}
/**
* Sets the archive's property serialization modifier flags
*
* @param InPortFlags the new flags to use for property serialization
*/
void SetPortFlags(uint32 InPortFlags)
{
ArPortFlags = InPortFlags;
}
/**
* Sets the archives custom serialization modifier flags (nothing to do with PortFlags or Custom versions)
*
* @param InCustomFlags the new flags to use for custom serialization
*/
void SetDebugSerializationFlags(uint32 InCustomFlags)
{
#if WITH_EDITOR
ArDebugSerializationFlags = InCustomFlags;
#endif
}
/**
* Indicates whether this archive is filtering editor-only on save or contains data that had editor-only content stripped.
*
* @return true if the archive filters editor-only content, false otherwise.
*/
bool IsFilterEditorOnly() const
{
return ArIsFilterEditorOnly;
}
/**
* Sets a flag indicating that this archive needs to filter editor-only content.
*
* @param InFilterEditorOnly Whether to filter editor-only content.
*/
virtual void SetFilterEditorOnly(bool InFilterEditorOnly)
{
ArIsFilterEditorOnly = InFilterEditorOnly;
}
/**
* Indicates whether this archive is saving or loading game state
*
* @note This is intended for game-specific archives and is not true for any of the build in save methods
* @return true if the archive is dealing with save games, false otherwise.
*/
bool IsSaveGame() const
{
return ArIsSaveGame;
}
/**
* Whether or not this archive is serializing data being sent/received by the netcode
*/
FORCEINLINE bool IsNetArchive() const
{
return ArIsNetArchive;
}
/**
* Checks whether the archive is used for cooking.
*
* @return true if the archive is used for cooking, false otherwise.
*/
FORCEINLINE bool IsCooking() const
{
check(!CookingTargetPlatform || (!IsLoading() && !IsTransacting() && IsSaving()));
return !!CookingTargetPlatform;
}
/**
* Returns the cooking target platform.
*
* @return Target platform.
*/
FORCEINLINE const ITargetPlatform* CookingTarget() const
{
return CookingTargetPlatform;
}
/**
* Sets the cooking target platform.
*
* @param InCookingTarget The target platform to set.
*/
FORCEINLINE void SetCookingTarget(const ITargetPlatform* InCookingTarget)
{
CookingTargetPlatform = InCookingTarget;
}
/**
* Checks whether the archive is used to resolve out-of-date enum indexes
* If function returns true, the archive should be called only for objects containing user defined enum
*
* @return true if the archive is used to resolve out-of-date enum indexes
*/
virtual bool UseToResolveEnumerators() const
{
return false;
}
/**
* Checks whether the archive wants to skip the property independent of the other flags
*/
virtual bool ShouldSkipProperty(const FProperty* InProperty) const
{
return false;
}
/**
* Overrides the property that is currently being serialized
* @note: You likely want to call PushSerializedProperty/PopSerializedProperty instead
*
* @param InProperty Pointer to the property that is currently being serialized
*/
virtual void SetSerializedProperty(FProperty* InProperty)
{
SerializedProperty = InProperty;
}
/**
* Gets the property that is currently being serialized
*
* @return Pointer to the property that is currently being serialized
*/
FORCEINLINE class FProperty* GetSerializedProperty() const
{
return SerializedProperty;
}
/**
* Gets the chain of properties that are currently being serialized
* @note This populates the array in stack order, so the 0th entry in the array is the top of the stack of properties
*/
void GetSerializedPropertyChain(TArray<class FProperty*>& OutProperties) const;
/**
* Get the raw serialized property chain for this archive
* @note Accessing this directly can avoid an array allocation depending on your use-case
*/
FORCEINLINE const FArchiveSerializedPropertyChain* GetSerializedPropertyChain() const
{
return SerializedPropertyChain;
}
/**
* Set the raw serialized property chain for this archive, optionally overriding the serialized property too (or null to use the head of the property chain)
*/
virtual void SetSerializedPropertyChain(const FArchiveSerializedPropertyChain* InSerializedPropertyChain, class FProperty* InSerializedPropertyOverride = nullptr);
#if WITH_EDITORONLY_DATA
/** Returns true if the stack of currently serialized properties contains an editor-only property */
virtual bool IsEditorOnlyPropertyOnTheStack() const;
#endif
/** Sets the current UObject serialization context for this archive. */
virtual void SetSerializeContext(FUObjectSerializeContext* InLoadContext) {}
/** Gets the current UObject serialization context for this archive. */
virtual FUObjectSerializeContext* GetSerializeContext() { return nullptr; }
#if USE_STABLE_LOCALIZATION_KEYS
/**
* Set the localization namespace that this archive should use when serializing text properties.
* This is typically the namespace used by the package being serialized (if serializing a package, or an object within a package).
*/
virtual void SetLocalizationNamespace(const FString& InLocalizationNamespace);
/**
* Get the localization namespace that this archive should use when serializing text properties.
* This is typically the namespace used by the package being serialized (if serializing a package, or an object within a package).
*/
virtual FString GetLocalizationNamespace() const;
#endif // USE_STABLE_LOCALIZATION_KEYS
/** Resets all of the base archive members. */
virtual void Reset();
public:
#if DEVIRTUALIZE_FLinkerLoad_Serialize
/* These are used for fastpath inline serializers */
struct FFastPathLoadBuffer
{
const uint8* StartFastPathLoadBuffer;
const uint8* EndFastPathLoadBuffer;
const uint8* OriginalFastPathLoadBuffer;
FORCEINLINE FFastPathLoadBuffer()
{
Reset();
}
FORCEINLINE void Reset()
{
StartFastPathLoadBuffer = nullptr;
EndFastPathLoadBuffer = nullptr;
OriginalFastPathLoadBuffer = nullptr;
}
};
//@todoio FArchive is really a horrible class and the way it is proxied by FLinkerLoad is double terrible. It makes the fast path really hacky and slower than it would need to be.
FFastPathLoadBuffer* ActiveFPLB;
FFastPathLoadBuffer InlineFPLB;
#endif
// These will be private in FArchive
protected:
/** Copies all of the members except CustomVersionContainer */
void CopyTrivialFArchiveStatusMembers(const FArchiveState& ArchiveStatusToCopy);
/** Whether this archive is for loading data. */
uint8 ArIsLoading : 1;
/** Whether this archive is for saving data. */
uint8 ArIsSaving : 1;
/** Whether archive is transacting, which is used to keep track of changes to objects for things like the editor undo system. */
uint8 ArIsTransacting : 1;
/** Whether this archive serializes to a text format. Text format archives should use high level constructs from FStructuredArchive for delimiting data rather than manually seeking through the file. */
uint8 ArIsTextFormat : 1;
/** Whether this archive wants properties to be serialized in binary form instead of tagged. */
uint8 ArWantBinaryPropertySerialization : 1;
/** Whether tagged property serialization is replaced by faster unversioned serialization. This assumes writer and reader share the same property definitions. */
uint8 ArUseUnversionedPropertySerialization : 1;
/** Whether this archive wants to always save strings in UTF16 format even if they are ANSI characters */
uint8 ArForceUnicode : 1;
/** Whether this archive saves to persistent storage. This is also true for some intermediate archives like DuplicateObject that are expected to go to persistent storage but may be discarded */
uint8 ArIsPersistent : 1;
private:
/** Whether this archive contains errors, which means that further serialization is generally not safe */
uint8 ArIsError : 1;
/** Whether this archive contains critical errors that cannot be recovered from */
uint8 ArIsCriticalError : 1;
public:
/** Quickly tell if an archive contains script code. */
uint8 ArContainsCode : 1;
/** Used to determine whether FArchive contains a level or world. */
uint8 ArContainsMap : 1;
/** Used to determine whether FArchive contains data required to be gathered for localization. */
uint8 ArRequiresLocalizationGather : 1;
/** Whether we should forcefully swap bytes. */
uint8 ArForceByteSwapping : 1;
/** If true, we will not serialize archetype references for structs and classes. */
uint8 ArIgnoreArchetypeRef : 1;
/** If true, do not perform delta serialization of properties. */
uint8 ArNoDelta : 1;
/** If true, do not perform delta serialization within properties (e.g. TMaps and TSets). */
uint8 ArNoIntraPropertyDelta : 1;
/** If true, we will not serialize the Outer reference in UObject. */
uint8 ArIgnoreOuterRef : 1;
/** If true, we will not serialize ClassGeneratedBy reference in UClass. */
uint8 ArIgnoreClassGeneratedByRef : 1;
/** If true, UObject::Serialize will skip serialization of the Class property. */
uint8 ArIgnoreClassRef : 1;
/** Whether to allow lazy loading of bulk/secondary data. */
uint8 ArAllowLazyLoading : 1;
/** Whether this archive only cares about serializing object references. */
uint8 ArIsObjectReferenceCollector : 1;
/** Whether a reference collector is modifying the references and wants both weak and strong ones */
uint8 ArIsModifyingWeakAndStrongReferences : 1;
/** Whether this archive is counting memory. */
uint8 ArIsCountingMemory : 1;
/** Whether bulk data serialization should be skipped or not. */
uint8 ArShouldSkipBulkData : 1;
/** Whether editor only properties are being filtered from the archive (or has been filtered). */
uint8 ArIsFilterEditorOnly : 1;
/** Whether this archive is saving/loading game state */
uint8 ArIsSaveGame : 1;
/** Whether or not this archive is sending/receiving network data */
uint8 ArIsNetArchive : 1;
/** Set TRUE to use the custom property list attribute for serialization. */
uint8 ArUseCustomPropertyList : 1;
/** Whether we are currently serializing defaults. > 0 means yes, <= 0 means no. */
int32 ArSerializingDefaults;
/** Modifier flags that be used when serializing UProperties */
uint32 ArPortFlags;
/** Max size of data that this archive is allowed to serialize. */
int64 ArMaxSerializeSize;
/**
* Sets whether this archive is for loading data.
*
* @param bInIsLoading true if this archive is for loading, false otherwise.
*/
virtual void SetIsLoading(bool bInIsLoading);
/**
* Sets whether this archive is for saving data.
*
* @param bInIsSaving true if this archive is for saving, false otherwise.
*/
virtual void SetIsSaving(bool bInIsSaving);
/**
* Sets whether this archive is for transacting.
*
* @param bInIsTransacting true if this archive is for transacting, false otherwise.
*/
virtual void SetIsTransacting(bool bInIsTransacting);
/**
* Sets whether this archive is in text format.
*
* @param bInIsTextFormat true if this archive is in text format, false otherwise.
*/
virtual void SetIsTextFormat(bool bInIsTextFormat);
/**
* Sets whether this archive wants binary property serialization.
*
* @param bInWantBinaryPropertySerialization true if this archive wants binary serialization, false otherwise.
*/
virtual void SetWantBinaryPropertySerialization(bool bInWantBinaryPropertySerialization);
/** Sets whether tagged property serialization should be replaced by faster unversioned serialization. This assumes writer and reader share the same property definitions. */
virtual void SetUseUnversionedPropertySerialization(bool bInUseUnversioned);
/**
* Sets whether this archive wants to force saving as Unicode.
* This is needed when we need to make sure ANSI strings are saved as Unicode.
*
* @param bInForceUnicode true if this archive wants to force saving as Unicode, false otherwise.
*/
virtual void SetForceUnicode(bool bInForceUnicode);
/**
* Sets whether this archive is to persistent storage.
*
* @param bInIsPersistent true if this archive is to persistent storage, false otherwise.
*/
virtual void SetIsPersistent(bool bInIsPersistent);
/**
* Sets the archive version number. Used by the code that makes sure that FLinkerLoad's
* internal archive versions match the file reader it creates.
*
* @param UE4Ver new version number
*/
virtual void SetUE4Ver(int32 InVer);
/**
* Sets the archive licensee version number. Used by the code that makes sure that FLinkerLoad's
* internal archive versions match the file reader it creates.
*
* @param Ver new version number
*/
virtual void SetLicenseeUE4Ver(int32 InVer);
/**
* Sets the archive engine version. Used by the code that makes sure that FLinkerLoad's
* internal archive versions match the file reader it creates.
*
* @param InVer new version number
*/
virtual void SetEngineVer(const FEngineVersionBase& InVer);
/**
* Sets the archive engine network version.
*/
virtual void SetEngineNetVer(const uint32 InEngineNetVer);
/**
* Sets the archive game network version.
*/
virtual void SetGameNetVer(const uint32 InGameNetVer);
// These will be private in FArchive
protected:
/** Holds the archive version. */
int32 ArUE4Ver;
/** Holds the archive version for licensees. */
int32 ArLicenseeUE4Ver;
/** Holds the engine version. */
FEngineVersionBase ArEngineVer;
/** Holds the engine network protocol version. */
uint32 ArEngineNetVer;
/** Holds the game network protocol version. */
uint32 ArGameNetVer;
/**
* All the custom versions stored in the archive.
* Stored as a pointer to a heap-allocated object because of a 3-way dependency between TArray, FCustomVersionContainer and FArchive, which is too much work to change right now.
* Keeping it as a heap-allocated object also helps with performance in some cases as we don't need to construct it for archives that don't care about custom versions.
*/
mutable FCustomVersionContainer* CustomVersionContainer = nullptr;
public:
/** Custom property list attribute. If the flag below is set, only these properties will be iterated during serialization. If NULL, then no properties will be iterated. */
const struct FCustomPropertyListNode* ArCustomPropertyList;
#if WITH_EDITOR
/** Custom serialization modifier flags can be used for anything */
uint32 ArDebugSerializationFlags;
#endif
// These will be private in FArchive
protected:
/** Holds the cooking target platform. */
const ITargetPlatform* CookingTargetPlatform;
/** Holds the pointer to the property that is currently being serialized */
FProperty* SerializedProperty;
/** Holds the chain of properties that are currently being serialized */
FArchiveSerializedPropertyChain* SerializedPropertyChain;
#if USE_STABLE_LOCALIZATION_KEYS
/**
* The localization namespace that this archive should use when serializing text properties.
* This is typically the namespace used by the package being serialized (if serializing a package, or an object within a package).
* Stored as a pointer to a heap-allocated string because of a dependency between TArray (thus FString) and FArchive; null should be treated as an empty string.
*/
FString* LocalizationNamespacePtr;
/** See GetLocalizationNamespace */
FString GetBaseLocalizationNamespace() const;
/** See SetLocalizationNamespace */
void SetBaseLocalizationNamespace(const FString& InLocalizationNamespace);
#endif // USE_STABLE_LOCALIZATION_KEYS
/**
* Indicates if the custom versions container is in a 'reset' state. This will be used to defer the choice about how to
* populate the container until it is needed, where the read/write state will be known.
*/
mutable bool bCustomVersionsAreReset;
private:
/** Linked list to all proxies */
FArchiveState* NextProxy = nullptr;
template<typename T> void ForEachState(T Func);
};
/**
* TCheckedObjPtr
*
* Wrapper for UObject pointers, which checks that the base class is accurate, upon serializing (to prevent illegal casting)
*/
template<class T> class TCheckedObjPtr
{
friend class FArchive;
public:
TCheckedObjPtr()
: Object(nullptr)
, bError(false)
{
}
TCheckedObjPtr(T* InObject)
: Object(InObject)
, bError(false)
{
}
/**
* Assigns a value to the object pointer
*
* @param InObject The value to assign to the pointer
*/
FORCEINLINE TCheckedObjPtr& operator = (T* InObject)
{
Object = InObject;
return *this;
}
/**
* Returns the object pointer, for accessing members of the object
*
* @return Returns the object pointer
*/
FORCEINLINE T* operator -> () const
{
return Object;
}
/**
* Retrieves a writable/serializable reference to the pointer
*
* @return Returns a reference to the pointer
*/
FORCEINLINE T*& Get()
{
return Object;
}
/**
* Whether or not the pointer is valid/non-null
*
* @return Whether or not the pointer is valid
*/
FORCEINLINE bool IsValid() const
{
return Object != nullptr;
}
/**
* Whether or not there was an error during the previous serialization.
* This occurs if an object was successfully serialized, but with the wrong base class
* (which net serialization may have to recover from, if there was supposed to be data serialized along with the object)
*
* @return Whether or not there was an error
*/
FORCEINLINE bool IsError() const
{
return bError;
}
private:
/** The object pointer */
T* Object;
/** Whether or not there was an error upon serializing */
bool bError;
};
/**
* Base class for archives that can be used for loading, saving, and garbage
* collecting in a byte order neutral way.
*/
class CORE_API FArchive : private FArchiveState
{
public:
FArchive() = default;
FArchive(const FArchive&) = default;
FArchive& operator=(const FArchive& ArchiveToCopy) = default;
~FArchive() = default;
protected:
using FArchiveState::LinkProxy;
using FArchiveState::UnlinkProxy;
public:
/**
* Serializes an FName value from or into this archive.
*
* This operator can be implemented by sub-classes that wish to serialize FName instances.
*
* @param Value The value to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(FName& Value)
{
return *this;
}
/**
* Serializes an FText value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
virtual FArchive& operator<<(FText& Value);
/**
* Serializes an UObject value from or into this archive.
*
* This operator can be implemented by sub-classes that wish to serialize UObject instances.
*
* @param Value The value to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(UObject*& Value)
{
return *this;
}
/**
* Serializes a Field value from or into this archive.
*
* This operator can be implemented by sub-classes that wish to serialize UObject instances.
*
* @param Value The value to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(FField*& Value)
{
return *this;
}
/**
* Serializes a UObject wrapped in a TCheckedObjPtr container, using the above operator,
* and verifies the serialized object is derived from the correct base class, to prevent illegal casting.
*
* @param Value The value to serialize.
* @return This instance.
*/
template<class T> FORCEINLINE FArchive& operator<<(TCheckedObjPtr<T>& Value)
{
Value.bError = false;
if (IsSaving())
{
UObject* SerializeObj = nullptr;
if (Value.IsValid())
{
if (Value.Get()->IsA(T::StaticClass()))
{
SerializeObj = Value.Get();
}
else
{
Value.bError = true;
}
}
*this << SerializeObj;
}
else
{
*this << Value.Get();
if (IsLoading() && Value.IsValid() && !Value.Get()->IsA(T::StaticClass()))
{
Value.bError = true;
Value = nullptr;
}
}
return *this;
}
/**
* Serializes a lazy object pointer value from or into this archive.
*
* Most of the time, FLazyObjectPtrs are serialized as UObject*, but some archives need to override this.
*
* @param Value The value to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(struct FLazyObjectPtr& Value);
/**
* Serializes asset pointer from or into this archive.
*
* Most of the time, FSoftObjectPtr are serialized as UObject *, but some archives need to override this.
*
* @param Value The asset pointer to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(struct FSoftObjectPtr& Value);
/**
* Serializes soft object paths from or into this archive.
*
* @param Value Soft object path to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(struct FSoftObjectPath& Value);
/**
* Serializes FWeakObjectPtr value from or into this archive.
*
* This operator can be implemented by sub-classes that wish to serialize FWeakObjectPtr instances.
*
* @param Value The value to serialize.
* @return This instance.
*/
virtual FArchive& operator<<(struct FWeakObjectPtr& Value);
/**
* Inform the archive that a blueprint would like to force finalization, normally
* this is triggered by CDO load, but if there's no CDO we force finalization.
*/
virtual void ForceBlueprintFinalization() {}
public:
/**
* Serializes an ANSICHAR value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, ANSICHAR& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.Serialize(&Value, 1);
}
return Ar;
}
/**
* Serializes a WIDECHAR value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, WIDECHAR& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(&Value, sizeof(Value));
}
return Ar;
}
/**
* Serializes an unsigned 8-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, uint8& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.Serialize(&Value, 1);
}
return Ar;
}
/**
* Serializes an enumeration value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
template<class TEnum>
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, TEnumAsByte<TEnum>& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.Serialize(&Value, 1);
}
return Ar;
}
/**
* Serializes a signed 8-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, int8& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.Serialize(&Value, 1);
}
return Ar;
}
/**
* Serializes an unsigned 16-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, uint16& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(Value);
}
return Ar;
}
/**
* Serializes a signed 16-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, int16& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(reinterpret_cast<uint16&>(Value));
}
return Ar;
}
/**
* Serializes an unsigned 32-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, uint32& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(Value);
}
return Ar;
}
/**
* Serializes a Boolean value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
#if WITH_EDITOR
protected:
virtual void SerializeBool( bool& D );
public:
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, bool& D)
{
Ar.SerializeBool(D);
return Ar;
}
#else
FORCEINLINE friend FArchive& operator<<( FArchive& Ar, bool& D )
{
// Serialize bool as if it were UBOOL (legacy, 32 bit int).
#if DEVIRTUALIZE_FLinkerLoad_Serialize
const uint8 * RESTRICT Src = Ar.ActiveFPLB->StartFastPathLoadBuffer;
if (Src + sizeof(uint32) <= Ar.ActiveFPLB->EndFastPathLoadBuffer)
{
D = !!FPlatformMemory::ReadUnaligned<uint32>(Src);
Ar.ActiveFPLB->StartFastPathLoadBuffer += 4;
}
else
#endif
{
uint32 OldUBoolValue = D ? 1 : 0;
Ar.Serialize(&OldUBoolValue, sizeof(OldUBoolValue));
D = !!OldUBoolValue;
}
return Ar;
}
#endif
/**
* Serializes a signed 32-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, int32& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(reinterpret_cast<uint32&>(Value));
}
return Ar;
}
#if PLATFORM_COMPILER_DISTINGUISHES_INT_AND_LONG
/**
* Serializes a long integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, long& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(reinterpret_cast<unsigned long&>(Value));
}
return Ar;
}
#endif
/**
* Serializes a single precision floating point value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<( FArchive& Ar, float& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
static_assert(sizeof(float) == sizeof(uint32), "Expected float to be 4 bytes to swap as uint32");
Ar.ByteOrderSerialize(reinterpret_cast<uint32&>(Value));
}
return Ar;
}
/**
* Serializes a double precision floating point value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, double& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
static_assert(sizeof(double) == sizeof(uint64), "Expected double to be 8 bytes to swap as uint64");
Ar.ByteOrderSerialize(reinterpret_cast<uint64&>(Value));
}
return Ar;
}
/**
* Serializes a unsigned 64-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
FORCEINLINE friend FArchive& operator<<(FArchive &Ar, uint64& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(Value);
}
return Ar;
}
/**
* Serializes a signed 64-bit integer value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
/*FORCEINLINE*/friend FArchive& operator<<(FArchive& Ar, int64& Value)
{
#if DEVIRTUALIZE_FLinkerLoad_Serialize
if (!Ar.FastPathLoad<sizeof(Value)>(&Value))
#endif
{
Ar.ByteOrderSerialize(reinterpret_cast<uint64&>(Value));
}
return Ar;
}
/**
* Serializes enum classes as their underlying type.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
template <
typename EnumType,
typename = typename TEnableIf<TIsEnumClass<EnumType>::Value>::Type
>
FORCEINLINE friend FArchive& operator<<(FArchive& Ar, EnumType& Value)
{
return Ar << (__underlying_type(EnumType)&)Value;
}
/**
* Serializes an FIntRect value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
friend FArchive& operator<<(FArchive& Ar, struct FIntRect& Value);
/**
* Serializes an FString value from or into an archive.
*
* @param Ar The archive to serialize from or to.
* @param Value The value to serialize.
*/
friend CORE_API FArchive& operator<<(FArchive& Ar, FString& Value);
public:
virtual void Serialize(void* V, int64 Length) { }
virtual void SerializeBits(void* V, int64 LengthBits)
{
Serialize(V, (LengthBits + 7) / 8);
if (IsLoading() && (LengthBits % 8) != 0)
{
((uint8*)V)[LengthBits / 8] &= ((1 << (LengthBits & 7)) - 1);
}
}
virtual void SerializeInt(uint32& Value, uint32 Max)
{
ByteOrderSerialize(Value);
}
/** Packs int value into bytes of 7 bits with 8th bit for 'more' */
virtual void SerializeIntPacked(uint32& Value);
/** Tells the archive to attempt to preload the specified object so data can be loaded out of it. */
virtual void Preload(UObject* Object) { }
/** Returns the low level archive state for this archive. */
FArchiveState& GetArchiveState()
{
return ImplicitConv<FArchiveState&>(*this);
}
const FArchiveState& GetArchiveState() const
{
return ImplicitConv<const FArchiveState&>(*this);
}
using FArchiveState::SetArchiveState;
using FArchiveState::SetError;
using FArchiveState::ClearError;
using FArchiveState::SetCriticalError;
using FArchiveState::GetInnermostState;
using FArchiveState::CountBytes;
using FArchiveState::GetArchiveName;
using FArchiveState::GetLinker;
using FArchiveState::Tell;
using FArchiveState::TotalSize;
using FArchiveState::AtEnd;
/** Attempts to set the current offset into backing data storage, this will do nothing if there is no storage. */
virtual void Seek(int64 InPos) { }
/**
* Attaches/ associates the passed in bulk data object with the linker.
*
* @param Owner UObject owning the bulk data
* @param BulkData Bulk data object to associate
*/
virtual void AttachBulkData(UObject* Owner, FUntypedBulkData* BulkData) { }
/**
* Detaches the passed in bulk data object from the linker.
*
* @param BulkData Bulk data object to detach
* @param bEnsureBulkDataIsLoaded Whether to ensure that the bulk data is loaded before detaching
*/
virtual void DetachBulkData(FUntypedBulkData* BulkData, bool bEnsureBulkDataIsLoaded) { }
/**
* Determine if the given archive is a valid "child" of this archive. In general, this means "is exactly the same" but
* this function allows a derived archive to support "child" or "internal" archives which are different objects that proxy the
* original one in some way.
*
* @param BulkData Bulk data object to detach
* @param bEnsureBulkDataIsLoaded Whether to ensure that the bulk data is loaded before detaching
*/
virtual bool IsProxyOf(FArchive* InOther) const
{
return InOther == this;
}
/**
* Hint the archive that the region starting at passed in offset and spanning the passed in size
* is going to be read soon and should be precached.
*
* The function returns whether the precache operation has completed or not which is an important
* hint for code knowing that it deals with potential async I/O. The archive is free to either not
* implement this function or only partially precache so it is required that given sufficient time
* the function will return true. Archives not based on async I/O should always return true.
*
* This function will not change the current archive position.
*
* @param PrecacheOffset Offset at which to begin precaching.
* @param PrecacheSize Number of bytes to precache
* @return false if precache operation is still pending, true otherwise
*/
virtual bool Precache(int64 PrecacheOffset, int64 PrecacheSize)
{
return true;
}
/**
* Flushes cache and frees internal data.
*/
virtual void FlushCache() { }
/**
* Sets mapping from offsets/ sizes that are going to be used for seeking and serialization to what
* is actually stored on disk. If the archive supports dealing with compression in this way it is
* going to return true.
*
* @param CompressedChunks Pointer to array containing information about [un]compressed chunks
* @param CompressionFlags Flags determining compression format associated with mapping
*
* @return true if archive supports translating offsets & uncompressing on read, false otherwise
*/
virtual bool SetCompressionMap(TArray<struct FCompressedChunk>* CompressedChunks, ECompressionFlags CompressionFlags)
{
return false;
}
/**
* Attempts to finish writing any buffered data to disk/permanent storage.
*/
virtual void Flush() { }
/**
* Attempts to close and finalize any handles used for backing data storage, returns true if it succeeded.
*/
virtual bool Close()
{
return !IsError();
}
/**
* Serializes and compresses/ uncompresses data. This is a shared helper function for compression
* support. The data is saved in a way compatible with FIOSystem::LoadCompressedData.
*
* @param V Data pointer to serialize data from/ to
* @param Length Length of source data if we're saving, unused otherwise
* @param Flags Flags to control what method to use for [de]compression and optionally control memory vs speed when compressing
* @param bTreatBufferAsFileReader true if V is actually an FArchive, which is used when saving to read data - helps to avoid single huge allocations of source data
* @param bUsePlatformBitWindow use a platform specific bitwindow setting
*/
void SerializeCompressed(void* V, int64 Length, FName CompressionFormat, ECompressionFlags Flags=COMPRESS_NoFlags, bool bTreatBufferAsFileReader=false);
using FArchiveState::IsByteSwapping;
/** Used to do byte swapping on small items. This does not happen usually, so we don't want it inline. */
void ByteSwap(void* V, int32 Length);
/** Serialize data of Length bytes, taking into account byte swapping if needed. */
FORCEINLINE FArchive& ByteOrderSerialize(void* V, int32 Length)
{
if (!IsByteSwapping()) // Most likely case (hot path)
{
Serialize(V, Length);
return *this;
}
return SerializeByteOrderSwapped(V, Length); // Slowest and unlikely path (should not be inlined)
}
using FArchiveState::ThisContainsCode;
using FArchiveState::ThisContainsMap;
using FArchiveState::ThisRequiresLocalizationGather;
/** Sets a flag indicating that this archive is currently serializing class/struct defaults. */
void StartSerializingDefaults()
{
ArSerializingDefaults++;
}
/** Indicate that this archive is no longer serializing class/struct defaults. */
void StopSerializingDefaults()
{
ArSerializingDefaults--;
}
/**
* Called when an object begins serializing property data using script serialization.
*/
virtual void MarkScriptSerializationStart(const UObject* Obj) { }
/**
* Called when an object stops serializing property data using script serialization.
*/
virtual void MarkScriptSerializationEnd(const UObject* Obj) { }
/**
* Called to register a reference to a specific name value, of type TypeObject (UEnum or UStruct normally). Const so it can be called from PostSerialize
*/
virtual void MarkSearchableName(const UObject* TypeObject, const FName& ValueName) const { }
using FArchiveState::GetArchetypeFromLoader;
private:
void VARARGS LogfImpl(const TCHAR* Fmt, ...);
public:
// Logf implementation for convenience.
template <typename FmtType, typename... Types>
void Logf(const FmtType& Fmt, Types... Args)
{
static_assert(TIsArrayOrRefOfType<FmtType, TCHAR>::Value, "Formatting string must be a TCHAR array.");
static_assert(TAnd<TIsValidVariadicFunctionArg<Types>...>::Value, "Invalid argument(s) passed to FArchive::Logf");
LogfImpl(Fmt, Args...);
}
using FArchiveState::UE4Ver;
using FArchiveState::LicenseeUE4Ver;
using FArchiveState::EngineVer;
using FArchiveState::EngineNetVer;
using FArchiveState::GameNetVer;
/**
* Registers the custom version to the archive. This is used to inform the archive that custom version information is about to be stored.
* There is no effect when the archive is being loaded from.
*
* @param Guid The guid of the custom version. This must have previously been registered with FCustomVersionRegistration.
*/
virtual void UsingCustomVersion(const struct FGuid& Guid);
using FArchiveState::CustomVer;
/**
* Returns a pointer to an archive that represents the same data that the current archive covers, but that can be cached and reused later
* In the case of standard archives, this function will just return a pointer to itself. If the archive is actually a temporary proxy to
* another archive, and has a shorter lifecycle than the source archive, it should return either a pointer to the underlying archive, or
* if the data becomes inaccessible when the proxy object disappears (as is the case with text format archives) then nullptr
*/
virtual FArchive* GetCacheableArchive()
{
return this;
}
using FArchiveState::IsLoading;
using FArchiveState::IsSaving;
using FArchiveState::IsTransacting;
using FArchiveState::IsTextFormat;
using FArchiveState::WantBinaryPropertySerialization;
using FArchiveState::UseUnversionedPropertySerialization;
using FArchiveState::IsForcingUnicode;
using FArchiveState::IsPersistent;
using FArchiveState::GetError;
using FArchiveState::IsError;
using FArchiveState::IsCriticalError;
using FArchiveState::ContainsCode;
using FArchiveState::ContainsMap;
using FArchiveState::RequiresLocalizationGather;
using FArchiveState::ForceByteSwapping;
using FArchiveState::IsSerializingDefaults;
using FArchiveState::IsIgnoringArchetypeRef;
using FArchiveState::DoDelta;
using FArchiveState::DoIntraPropertyDelta;
using FArchiveState::IsIgnoringOuterRef;
using FArchiveState::IsIgnoringClassGeneratedByRef;
using FArchiveState::IsIgnoringClassRef;
using FArchiveState::IsAllowingLazyLoading;
using FArchiveState::IsObjectReferenceCollector;
using FArchiveState::IsModifyingWeakAndStrongReferences;
using FArchiveState::IsCountingMemory;
using FArchiveState::GetPortFlags;
using FArchiveState::HasAnyPortFlags;
using FArchiveState::HasAllPortFlags;
using FArchiveState::GetDebugSerializationFlags;
using FArchiveState::ShouldSkipBulkData;
using FArchiveState::GetMaxSerializeSize;
using FArchiveState::GetCustomVersions;
using FArchiveState::SetCustomVersions;
using FArchiveState::ResetCustomVersions;
using FArchiveState::SetCustomVersion;
using FArchiveState::SetByteSwapping;
using FArchiveState::SetPortFlags;
using FArchiveState::SetDebugSerializationFlags;
using FArchiveState::IsFilterEditorOnly;
using FArchiveState::SetFilterEditorOnly;
using FArchiveState::IsSaveGame;
using FArchiveState::IsNetArchive;
using FArchiveState::IsCooking;
using FArchiveState::CookingTarget;
using FArchiveState::SetCookingTarget;
using FArchiveState::UseToResolveEnumerators;
using FArchiveState::ShouldSkipProperty;
using FArchiveState::SetSerializedProperty;
using FArchiveState::GetSerializedProperty;
using FArchiveState::GetSerializedPropertyChain;
using FArchiveState::SetSerializedPropertyChain;
/**
* Push a property that is currently being serialized onto the property stack
*
* @param InProperty Pointer to the property that is currently being serialized
* @param bIsEditorOnlyProperty True if the property is editor only (call FProperty::IsEditorOnlyProperty to work this out, as the archive can't since it can't access CoreUObject types)
*/
virtual void PushSerializedProperty(class FProperty* InProperty, const bool bIsEditorOnlyProperty);
/**
* Pop a property that was previously being serialized off the property stack
*
* @param InProperty Pointer to the property that was previously being serialized
* @param bIsEditorOnlyProperty True if the property is editor only (call FProperty::IsEditorOnlyProperty to work this out, as the archive can't since it can't access CoreUObject types)
*/
virtual void PopSerializedProperty(class FProperty* InProperty, const bool bIsEditorOnlyProperty);
#if WITH_EDITORONLY_DATA
using FArchiveState::IsEditorOnlyPropertyOnTheStack;
#endif
using FArchiveState::SetSerializeContext;
using FArchiveState::GetSerializeContext;
/**
* Adds external read dependency
*
* @return true if dependency has been added, false if Archive does not support them
*/
virtual bool AttachExternalReadDependency(FExternalReadCallback& ReadCallback) { return false; };
#if USE_STABLE_LOCALIZATION_KEYS
using FArchiveState::SetLocalizationNamespace;
using FArchiveState::GetLocalizationNamespace;
#endif // USE_STABLE_LOCALIZATION_KEYS
/** Resets all of the base archive members. */
using FArchiveState::Reset;
#if DEVIRTUALIZE_FLinkerLoad_Serialize
private:
template<SIZE_T Size>
FORCEINLINE bool FastPathLoad(void* InDest)
{
const uint8* RESTRICT Src = ActiveFPLB->StartFastPathLoadBuffer;
if (Src + Size <= ActiveFPLB->EndFastPathLoadBuffer)
{
if (Size == 2)
{
uint16 * RESTRICT Dest = (uint16 * RESTRICT)InDest;
*Dest = FPlatformMemory::ReadUnaligned<uint16>(Src);
}
else if (Size == 4)
{
uint32 * RESTRICT Dest = (uint32 * RESTRICT)InDest;
*Dest = FPlatformMemory::ReadUnaligned<uint32>(Src);
}
else if (Size == 8)
{
uint64 * RESTRICT Dest = (uint64 * RESTRICT)InDest;
*Dest = FPlatformMemory::ReadUnaligned<uint64>(Src);
}
else
{
uint8 * RESTRICT Dest = (uint8 * RESTRICT)InDest;
for (SIZE_T Index = 0; Index < Size; Index++)
{
Dest[Index] = Src[Index];
}
}
ActiveFPLB->StartFastPathLoadBuffer += Size;
return true;
}
return false;
}
public:
//@todoio FArchive is really a horrible class and the way it is proxied by FLinkerLoad is double terrible. It makes the fast path really hacky and slower than it would need to be.
using FArchiveState::ActiveFPLB;
using FArchiveState::InlineFPLB;
#else
template<SIZE_T Size>
FORCEINLINE bool FastPathLoad(void* InDest)
{
return false;
}
#endif
private:
// Used internally only to control the amount of generated code/type under control.
template<typename T>
FArchive& ByteOrderSerialize(T& Value)
{
static_assert(!TIsSigned<T>::Value, "To reduce the number of template instances, cast 'Value' to a uint16&, uint32& or uint64& prior to the call or use ByteOrderSerialize(void*, int32).");
if (!IsByteSwapping()) // Most likely case (hot path)
{
Serialize(&Value, sizeof(T));
return *this;
}
return SerializeByteOrderSwapped(Value); // Slowest and unlikely path (but fastest than SerializeByteOrderSwapped(void*, int32)).
}
// Not inlined to keep ByteOrderSerialize(), small and fast.
FArchive& SerializeByteOrderSwapped(void* V, int32 Length);
FArchive& SerializeByteOrderSwapped(uint16& Value);
FArchive& SerializeByteOrderSwapped(uint32& Value);
FArchive& SerializeByteOrderSwapped(uint64& Value);
#if PLATFORM_COMPILER_DISTINGUISHES_INT_AND_LONG
FArchive& SerializeByteOrderSwapped(unsigned long& Value)
{
static_assert(sizeof(unsigned long) == sizeof(uint64), "Wrong unsigned long size assuption.");
return SerializeByteOrderSwapped(reinterpret_cast<uint64&>(Value));
}
#endif
private:
using FArchiveState::ArIsLoading;
using FArchiveState::ArIsSaving;
using FArchiveState::ArIsTransacting;
using FArchiveState::ArIsTextFormat;
using FArchiveState::ArWantBinaryPropertySerialization;
using FArchiveState::ArUseUnversionedPropertySerialization;
using FArchiveState::ArForceUnicode;
using FArchiveState::ArIsPersistent;
public:
using FArchiveState::ArIsError;
using FArchiveState::ArIsCriticalError;
using FArchiveState::ArContainsCode;
using FArchiveState::ArContainsMap;
using FArchiveState::ArRequiresLocalizationGather;
using FArchiveState::ArForceByteSwapping;
using FArchiveState::ArIgnoreArchetypeRef;
using FArchiveState::ArNoDelta;
using FArchiveState::ArNoIntraPropertyDelta;
using FArchiveState::ArIgnoreOuterRef;
using FArchiveState::ArIgnoreClassGeneratedByRef;
using FArchiveState::ArIgnoreClassRef;
using FArchiveState::ArAllowLazyLoading;
using FArchiveState::ArIsObjectReferenceCollector;
using FArchiveState::ArIsModifyingWeakAndStrongReferences;
using FArchiveState::ArIsCountingMemory;
using FArchiveState::ArShouldSkipBulkData;
using FArchiveState::ArIsFilterEditorOnly;
using FArchiveState::ArIsSaveGame;
using FArchiveState::ArIsNetArchive;
using FArchiveState::ArUseCustomPropertyList;
using FArchiveState::ArSerializingDefaults;
using FArchiveState::ArPortFlags;
using FArchiveState::ArMaxSerializeSize;
public:
using FArchiveState::SetIsLoading;
using FArchiveState::SetIsSaving;
using FArchiveState::SetIsTransacting;
using FArchiveState::SetIsTextFormat;
using FArchiveState::SetWantBinaryPropertySerialization;
using FArchiveState::SetUseUnversionedPropertySerialization;
using FArchiveState::SetForceUnicode;
using FArchiveState::SetIsPersistent;
using FArchiveState::SetUE4Ver;
using FArchiveState::SetLicenseeUE4Ver;
using FArchiveState::SetEngineVer;
using FArchiveState::SetEngineNetVer;
using FArchiveState::SetGameNetVer;
private:
using FArchiveState::ArUE4Ver;
using FArchiveState::ArLicenseeUE4Ver;
using FArchiveState::ArEngineVer;
using FArchiveState::ArEngineNetVer;
using FArchiveState::ArGameNetVer;
using FArchiveState::CustomVersionContainer;
public:
/** Custom property list attribute. If the flag below is set, only these properties will be iterated during serialization. If NULL, then no properties will be iterated. */
using FArchiveState::ArCustomPropertyList;
class FScopeSetDebugSerializationFlags
{
private:
#if WITH_EDITOR
uint32 PreviousFlags;
FArchive& Ar;
#endif
public:
/**
* Initializes an object which will set flags for the scope of this code
*
* @param NewFlags new flags to set
* @param Remove should we add these flags or remove them default is to add
*/
#if WITH_EDITOR
FScopeSetDebugSerializationFlags(FArchive& InAr, uint32 NewFlags, bool Remove = false)
: Ar(InAr)
{
PreviousFlags = Ar.GetDebugSerializationFlags();
if (Remove)
{
Ar.SetDebugSerializationFlags( PreviousFlags & ~NewFlags);
}
else
{
Ar.SetDebugSerializationFlags( PreviousFlags | NewFlags);
}
}
~FScopeSetDebugSerializationFlags()
{
Ar.SetDebugSerializationFlags( PreviousFlags);
}
#else
FScopeSetDebugSerializationFlags(FArchive& InAr, uint32 NewFlags, bool Remove = false)
{}
~FScopeSetDebugSerializationFlags()
{}
#endif
};
#if WITH_EDITOR
/** Custom serialization modifier flags can be used for anything */
using FArchiveState::ArDebugSerializationFlags;
/** Debug stack storage if you want to add data to the archive for usage further down the serialization stack this should be used in conjunction with the FScopeAddDebugData struct */
virtual void PushDebugDataString(const FName& DebugData);
virtual void PopDebugDataString() { }
class FScopeAddDebugData
{
private:
FArchive& Ar;
public:
CORE_API FScopeAddDebugData(FArchive& InAr, const FName& DebugData);
~FScopeAddDebugData()
{
Ar.PopDebugDataString();
}
};
#endif
/** Called whilst cooking to provide file region hints to the cooker. */
virtual void PushFileRegionType(EFileRegionType Type) { }
virtual void PopFileRegionType() { }
private:
/** Holds the cooking target platform. */
using FArchiveState::CookingTargetPlatform;
/** Holds the pointer to the property that is currently being serialized */
using FArchiveState::SerializedProperty;
/** Holds the chain of properties that are currently being serialized */
using FArchiveState::SerializedPropertyChain;
#if USE_STABLE_LOCALIZATION_KEYS
/**
* The localization namespace that this archive should use when serializing text properties.
* This is typically the namespace used by the package being serialized (if serializing a package, or an object within a package).
* Stored as a pointer to a heap-allocated string because of a dependency between TArray (thus FString) and FArchive; null should be treated as an empty string.
*/
using FArchiveState::LocalizationNamespacePtr;
#endif // USE_STABLE_LOCALIZATION_KEYS
/**
* Indicates if the custom versions container is in a 'reset' state. This will be used to defer the choice about how to
* populate the container until it is needed, where the read/write state will be known.
*/
using FArchiveState::bCustomVersionsAreReset;
};
/**
* Template for archive constructors.
*/
template<class T> T Arctor(FArchive& Ar)
{
T Tmp;
Ar << Tmp;
return Tmp;
}
I can post more, but here’s the block where the error came. I guess due to it not having a valid header?
if (!bHeaderWasValid)
{
UE_LOG(LogSerialization, Log, TEXT(“ArchiveName: %s”), *GetArchiveName());
UE_LOG(LogSerialization, Log, TEXT(“Archive UE4 Version: %d”), UE4Ver());
UE_LOG(LogSerialization, Log, TEXT(“Archive Licensee Version: %d”), LicenseeUE4Ver());
UE_LOG(LogSerialization, Log, TEXT(“Position: %lld”), Tell());
UE_LOG(LogSerialization, Log, TEXT(“Read Size: %lld”), Length);
UE_LOG(LogSerialization, Fatal, TEXT(“BulkData compressed header read error. This package may be corrupt!”));
}