//------------------------------------------------------------------------------ bool FStructScriptLoader::ShouldDeferScriptSerialization(FArchive& Ar) { #if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING if (Ar.IsLoading() && Ar.IsPersistent()) { if (ULinkerLoad* Linker = Cast<ULinkerLoad>(Ar.GetLinker())) { return ((Linker->LoadFlags & LOAD_DeferDependencyLoads) != 0); } } #endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING return false; }
void UStructProperty::Serialize( FArchive& Ar ) { Super::Serialize( Ar ); static UScriptStruct* FallbackStruct = GetFallbackStruct(); if (Ar.IsPersistent() && Ar.GetLinker() && Ar.IsLoading() && !Struct) { // It's necessary to solve circular dependency problems, when serializing the Struct causes linking of the Property. Struct = FallbackStruct; } Ar << Struct; #if WITH_EDITOR if (Ar.IsPersistent() && Ar.GetLinker()) { if (!Struct && Ar.IsLoading()) { UE_LOG(LogProperty, Error, TEXT("UStructProperty::Serialize Loading: Property '%s'. Unknown structure."), *GetFullName()); Struct = FallbackStruct; } else if ((FallbackStruct == Struct) && Ar.IsSaving()) { UE_LOG(LogProperty, Error, TEXT("UStructProperty::Serialize Saving: Property '%s'. FallbackStruct structure."), *GetFullName()); } } #endif // WITH_EDITOR if (Struct) { PreloadInnerStructMembers(this); } else { ensure(true); } }
/** * Serialize function used to serialize this bulk data structure. * * @param Ar Archive to serialize with * @param Owner Object owning the bulk data * @param Idx Index of bulk data item being serialized */ void FUntypedBulkData::Serialize( FArchive& Ar, UObject* Owner, int32 Idx ) { check( LockStatus == LOCKSTATUS_Unlocked ); if(Ar.IsTransacting()) { // Special case for transacting bulk data arrays. // constructing the object during load will save it to the transaction buffer. If it tries to load the bulk data now it will try to break it. bool bActuallySave = Ar.IsSaving() && (!Owner || !Owner->HasAnyFlags(RF_NeedLoad)); Ar << bActuallySave; if (bActuallySave) { if(Ar.IsLoading()) { // Flags for bulk data. Ar << BulkDataFlags; // Number of elements in array. Ar << ElementCount; // Allocate bulk data. check(bShouldFreeOnEmpty); BulkData = FMemory::Realloc( BulkData, GetBulkDataSize() ); // Deserialize bulk data. SerializeBulkData( Ar, BulkData ); } else if(Ar.IsSaving()) { // Flags for bulk data. Ar << BulkDataFlags; // Number of elements in array. Ar << ElementCount; // Don't attempt to load or serialize BulkData if the current size is 0. // This could be a newly constructed BulkData that has not yet been loaded, // and allocating 0 bytes now will cause a crash when we load. if (GetBulkDataSize() > 0) { // Make sure bulk data is loaded. MakeSureBulkDataIsLoaded(); // Serialize bulk data. SerializeBulkData(Ar, BulkData); } } } } else if( Ar.IsPersistent() && !Ar.IsObjectReferenceCollector() && !Ar.ShouldSkipBulkData() ) { #if TRACK_BULKDATA_USE FThreadSafeBulkDataToObjectMap::Get().Add( this, Owner ); #endif // Offset where the bulkdata flags are stored int64 SavedBulkDataFlagsPos = Ar.Tell(); Ar << BulkDataFlags; // Number of elements in array. Ar << ElementCount; // We're loading from the persistent archive. if( Ar.IsLoading() ) { Filename = TEXT(""); // @todo when Landscape (and others?) only Lock/Unlock once, we can enable this if (false) // FPlatformProperties::RequiresCookedData()) { // Bulk data that is being serialized via seekfree loading is single use only. This allows us // to free the memory as e.g. the bulk data won't be attached to an archive in the case of // seek free loading. BulkDataFlags |= BULKDATA_SingleUse; } // Size on disk, which in the case of compression is != GetBulkDataSize() Ar << BulkDataSizeOnDisk; Ar << BulkDataOffsetInFile; // fix up the file offset if (Owner != NULL && Owner->GetLinker()) { BulkDataOffsetInFile += Owner->GetLinker()->Summary.BulkDataStartOffset; } // determine whether the payload is stored inline or at the end of the file bool bPayloadInline = !(BulkDataFlags&BULKDATA_PayloadAtEndOfFile); // check( (bPayloadInline && BulkDataOffsetInFile == Ar.Tell()) || // (!bPayloadInline && BulkDataOffsetInFile > Ar.Tell())); // We're allowing defered serialization. if( Ar.IsAllowingLazyLoading() && Owner != NULL) { Linker = Owner->GetLinker(); #if WITH_EDITOR check(Linker); Ar.AttachBulkData( Owner, this ); AttachedAr = &Ar; #else check(Linker.IsValid()); Filename = Linker->Filename; #endif // WITH_EDITOR // only skip over payload, if it's stored inline if (bPayloadInline) { Ar.Seek( Ar.Tell() + BulkDataSizeOnDisk ); } } // Serialize the bulk data right away. else { // memory for bulk data can come from preallocated GPU-accessible resource memory or default to system memory BulkData = GetBulkDataResourceMemory(Owner,Idx); if( !BulkData ) { BulkData = FMemory::Realloc( BulkData, GetBulkDataSize() ); } if (bPayloadInline) { // if the payload is stored inline, just serialize it SerializeBulkData( Ar, BulkData ); } else { // if the payload is NOT stored inline ... // store the current file offset int64 CurOffset = Ar.Tell(); // seek to the location in the file where the payload is stored Ar.Seek(BulkDataOffsetInFile); // serialize the payload SerializeBulkData( Ar, BulkData ); // seek to the location we came from Ar.Seek(CurOffset); } } } // We're saving to the persistent archive. else if( Ar.IsSaving() ) { // check if we save the package compressed UPackage* Pkg = Owner ? dynamic_cast<UPackage*>(Owner->GetOutermost()) : nullptr; if (Pkg && !!(Pkg->PackageFlags & PKG_StoreCompressed) ) { ECompressionFlags BaseCompressionMethod = COMPRESS_Default; if (Ar.IsCooking()) { BaseCompressionMethod = Ar.CookingTarget()->GetBaseCompressionMethod(); } StoreCompressedOnDisk(BaseCompressionMethod); } // Remove single element serialization requirement before saving out bulk data flags. BulkDataFlags &= ~BULKDATA_ForceSingleElementSerialization; // Make sure bulk data is loaded. MakeSureBulkDataIsLoaded(); // Only serialize status information if wanted. int64 SavedBulkDataSizeOnDiskPos = INDEX_NONE; int64 SavedBulkDataOffsetInFilePos = INDEX_NONE; // Keep track of position we are going to serialize placeholder BulkDataSizeOnDisk. SavedBulkDataSizeOnDiskPos = Ar.Tell(); BulkDataSizeOnDisk = INDEX_NONE; // And serialize the placeholder which is going to be overwritten later. Ar << BulkDataSizeOnDisk; // Keep track of position we are going to serialize placeholder BulkDataOffsetInFile. SavedBulkDataOffsetInFilePos = Ar.Tell(); BulkDataOffsetInFile = INDEX_NONE; // And serialize the placeholder which is going to be overwritten later. Ar << BulkDataOffsetInFile; // try to get the linkersave object ULinkerSave* LinkerSave = dynamic_cast<ULinkerSave*>(Ar.GetLinker()); // determine whether we are going to store the payload inline or not. bool bStoreInline = !!(BulkDataFlags&BULKDATA_ForceInlinePayload) || LinkerSave == NULL; if (!bStoreInline) { // set the flag indicating where the payload is stored BulkDataFlags |= BULKDATA_PayloadAtEndOfFile; // with no LinkerSave we have to store the data inline check(LinkerSave != NULL); // add the bulkdata storage info object to the linkersave int32 Index = LinkerSave->BulkDataToAppend.AddZeroed(1); ULinkerSave::FBulkDataStorageInfo& BulkStore = LinkerSave->BulkDataToAppend[Index]; BulkStore.BulkDataOffsetInFilePos = SavedBulkDataOffsetInFilePos; BulkStore.BulkDataSizeOnDiskPos = SavedBulkDataSizeOnDiskPos; BulkStore.BulkData = this; // Serialize bulk data into the storage info BulkDataSizeOnDisk = -1; } else { // set the flag indicating where the payload is stored BulkDataFlags &= ~BULKDATA_PayloadAtEndOfFile; int64 SavedBulkDataStartPos = Ar.Tell(); // Serialize bulk data. SerializeBulkData( Ar, BulkData ); // store the payload endpos int64 SavedBulkDataEndPos = Ar.Tell(); checkf(SavedBulkDataStartPos >= 0 && SavedBulkDataEndPos >= 0, TEXT("Bad archive positions for bulkdata. StartPos=%d EndPos=%d"), SavedBulkDataStartPos, SavedBulkDataEndPos); BulkDataSizeOnDisk = SavedBulkDataEndPos - SavedBulkDataStartPos; BulkDataOffsetInFile = SavedBulkDataStartPos; } // store current file offset before seeking back int64 CurrentFileOffset = Ar.Tell(); // Seek back and overwrite the flags Ar.Seek(SavedBulkDataFlagsPos); Ar << BulkDataFlags; // Seek back and overwrite placeholder for BulkDataSizeOnDisk Ar.Seek( SavedBulkDataSizeOnDiskPos ); Ar << BulkDataSizeOnDisk; // Seek back and overwrite placeholder for BulkDataOffsetInFile Ar.Seek( SavedBulkDataOffsetInFilePos ); Ar << BulkDataOffsetInFile; // Seek to the end of written data so we don't clobber any data in subsequent write // operations Ar.Seek(CurrentFileOffset); } } }
//------------------------------------------------------------------------------ bool FStructScriptLoader::LoadStructWithScript(UStruct* DestScriptContainer, FArchive& Ar, bool bAllowDeferredSerialization) { if (!Ar.IsLoading() || !IsPrimed() || GIsDuplicatingClassForReinstancing) { return false; } bool const bIsLinkerLoader = Ar.IsPersistent() && (Ar.GetLinker() != nullptr); int32 const ScriptEndOffset = ScriptSerializationOffset + SerializedScriptSize; // to help us move development forward (and not have to support ancient // script code), we define a minimum script version bool bSkipScriptSerialization = (Ar.UE4Ver() < VER_MIN_SCRIPTVM_UE4) || (Ar.LicenseeUE4Ver() < VER_MIN_SCRIPTVM_LICENSEEUE4); #if WITH_EDITOR static const FBoolConfigValueHelper SkipByteCodeHelper(TEXT("StructSerialization"), TEXT("SkipByteCodeSerialization")); // in editor builds, we're going to regenerate the bytecode anyways, so it // is a waste of cycles to try and serialize it in bSkipScriptSerialization |= (bool)SkipByteCodeHelper; #endif // WITH_EDITOR bSkipScriptSerialization &= bIsLinkerLoader; // to keep consistent with old UStruct::Serialize() functionality if (bSkipScriptSerialization) { int32 TrackedBufferSize = BytecodeBufferSize; BytecodeBufferSize = 0; // temporarily clear so that ClearScriptCode() doesn't leave Class->Script with anything allocated ClearScriptCode(DestScriptContainer); BytecodeBufferSize = TrackedBufferSize; // we have to at least move the archiver forward, so it is positioned // where it expects to be (as if we read in the script) Ar.Seek(ScriptEndOffset); return false; } bAllowDeferredSerialization &= bIsLinkerLoader; if (bAllowDeferredSerialization && ShouldDeferScriptSerialization(Ar)) { ULinkerLoad* Linker = CastChecked<ULinkerLoad>(Ar.GetLinker()); FDeferredScriptTracker::Get().AddDeferredScriptObject(Linker, DestScriptContainer, *this); // we have to at least move the archiver forward, so it is positioned // where it expects to be (as if we read in the script) Ar.Seek(ScriptEndOffset); return false; } Ar.Seek(ScriptSerializationOffset); if (bIsLinkerLoader) { ULinkerLoad* LinkerLoad = CastChecked<ULinkerLoad>(Ar.GetLinker()); TArray<uint8> ShaScriptBuffer; ShaScriptBuffer.AddUninitialized(SerializedScriptSize); Ar.Serialize(ShaScriptBuffer.GetData(), SerializedScriptSize); ensure(ScriptEndOffset == Ar.Tell()); LinkerLoad->UpdateScriptSHAKey(ShaScriptBuffer); Ar.Seek(ScriptSerializationOffset); } DestScriptContainer->Script.Empty(BytecodeBufferSize); DestScriptContainer->Script.AddUninitialized(BytecodeBufferSize); int32 BytecodeIndex = 0; while (BytecodeIndex < BytecodeBufferSize) { DestScriptContainer->SerializeExpr(BytecodeIndex, Ar); } ensure(ScriptEndOffset == Ar.Tell()); checkf(BytecodeIndex == BytecodeBufferSize, TEXT("'%s' script expression-count mismatch; Expected: %i, Got: %i"), *DestScriptContainer->GetName(), BytecodeBufferSize, BytecodeIndex); if (!GUObjectArray.IsDisregardForGC(DestScriptContainer)) { DestScriptContainer->ScriptObjectReferences.Empty(); FArchiveScriptReferenceCollector ObjRefCollector(DestScriptContainer->ScriptObjectReferences); BytecodeIndex = 0; while (BytecodeIndex < BytecodeBufferSize) { DestScriptContainer->SerializeExpr(BytecodeIndex, ObjRefCollector); } } // success! (we filled the target with serialized script code) return true; }