void UUserDefinedStruct::Serialize(FArchive& Ar) { Super::Serialize( Ar ); if (Ar.IsLoading() && (EUserDefinedStructureStatus::UDSS_UpToDate == Status)) { // We need to force the editor data to be preload in case anyone needs to extract variable // information at editor time about the user structure. if ( EditorData != nullptr ) { Ar.Preload(EditorData); if (!(Ar.GetPortFlags() & PPF_Duplicate)) { FStructureEditorUtils::RecreateDefaultInstanceInEditorData(this); } } const FStructureEditorUtils::EStructureError Result = FStructureEditorUtils::IsStructureValid(this, NULL, &ErrorMessage); if (FStructureEditorUtils::EStructureError::Ok != Result) { Status = EUserDefinedStructureStatus::UDSS_Error; UE_LOG(LogClass, Log, TEXT("UUserDefinedStruct.Serialize '%s' validation: %s"), *GetName(), *ErrorMessage); } } }
void UByteProperty::Serialize( FArchive& Ar ) { Super::Serialize( Ar ); Ar << Enum; if (Enum != NULL) { Ar.Preload(Enum); } }
/*----------------------------------------------------------------------------- UByteProperty. -----------------------------------------------------------------------------*/ void UByteProperty::SerializeItem( FArchive& Ar, void* Value, void const* Defaults ) const { if(Enum && Ar.UseToResolveEnumerators()) { const int32 ResolvedIndex = Enum->ResolveEnumerator(Ar, *(uint8*)Value); *(uint8*)Value = static_cast<uint8>(ResolvedIndex); return; } // Serialize enum values by name unless we're not saving or loading OR for backwards compatibility const bool bUseBinarySerialization = (Enum == NULL) || (!Ar.IsLoading() && !Ar.IsSaving()); if( bUseBinarySerialization ) { Super::SerializeItem(Ar, Value, Defaults); } // Loading else if (Ar.IsLoading()) { FName EnumValueName; Ar << EnumValueName; // Make sure enum is properly populated if( Enum->HasAnyFlags(RF_NeedLoad) ) { Ar.Preload(Enum); } // There's no guarantee EnumValueName is still present in Enum, in which case Value will be set to the enum's max value. // On save, it will then be serialized as NAME_None. int32 EnumIndex = Enum->FindEnumIndex(EnumValueName); if (EnumIndex == INDEX_NONE) { *(uint8*)Value = Enum->GetMaxEnumValue(); } else { *(uint8*)Value = Enum->GetValueByIndex(EnumIndex); } } // Saving else { FName EnumValueName; uint8 ByteValue = *(uint8*)Value; // subtract 1 because the last entry in the enum's Names array // is the _MAX entry if ( Enum->IsValidEnumValue(ByteValue) ) { EnumValueName = Enum->GetNameByValue(ByteValue); } else { EnumValueName = NAME_None; } Ar << EnumValueName; } }
void UArrayProperty::LinkInternal(FArchive& Ar) { ULinkerLoad* MyLinker = GetLinker(); if( MyLinker ) { MyLinker->Preload(this); } Ar.Preload(Inner); Inner->Link(Ar); Super::LinkInternal(Ar); }
void UStructProperty::SerializeItem( FArchive& Ar, void* Value, int32 MaxReadBytes, void const* Defaults ) const { const bool bUseBinarySerialization = UseBinarySerialization(Ar); const bool bUseNativeSerialization = UseNativeSerialization(); // Preload struct before serialization tracking to not double count time. if ( bUseBinarySerialization || bUseNativeSerialization) { Ar.Preload( Struct ); } bool bItemSerialized = false; if (bUseNativeSerialization) { UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps(); check(CppStructOps); // else should not have STRUCT_SerializeNative check(!Struct->InheritedCppStructOps()); // else should not have STRUCT_SerializeNative bItemSerialized = CppStructOps->Serialize(Ar, Value); } if (!bItemSerialized) { if( bUseBinarySerialization ) { // Struct is already preloaded above. if ( !Ar.IsPersistent() && Ar.GetPortFlags() != 0 && !Struct->ShouldSerializeAtomically(Ar) ) { Struct->SerializeBinEx( Ar, Value, Defaults, Struct ); } else { Struct->SerializeBin( Ar, Value, MaxReadBytes ); } } else { Struct->SerializeTaggedProperties( Ar, (uint8*)Value, Struct, (uint8*)Defaults ); } } if (Struct->StructFlags & STRUCT_PostSerializeNative) { UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps(); check(CppStructOps); // else should not have STRUCT_PostSerializeNative check(!Struct->InheritedCppStructOps()); // else should not have STRUCT_PostSerializeNative CppStructOps->PostSerialize(Ar, Value); } }
void UArrayProperty::SerializeItem( FArchive& Ar, void* Value, void const* Defaults ) const { checkSlow(Inner); // Ensure that the Inner itself has been loaded before calling SerializeItem() on it Ar.Preload(Inner); FScriptArrayHelper ArrayHelper(this, Value); int32 n = ArrayHelper.Num(); Ar << n; if( Ar.IsLoading() ) { ArrayHelper.EmptyAndAddValues(n); } ArrayHelper.CountBytes( Ar ); for( int32 i=0; i<n; i++ ) { Inner->SerializeItem( Ar, ArrayHelper.GetRawPtr(i) ); } }
void UStructProperty::LinkInternal(FArchive& Ar) { // We potentially have to preload the property itself here, if we were the inner of an array property if(HasAnyFlags(RF_NeedLoad)) { GetLinker()->Preload(this); } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (Struct == NULL) { UE_LOG(LogProperty, Error, TEXT("Struct type unknown for property '%s'; perhaps the USTRUCT() was renamed or deleted?"), *GetFullName()); } #endif // Preload is required here in order to load the value of Struct->PropertiesSize Ar.Preload(Struct); if ( !ensure(Struct) ) { Struct = GetFallbackStruct(); } PreloadInnerStructMembers(this); ElementSize = Align(Struct->PropertiesSize, Struct->GetMinAlignment()); if (Struct->StructFlags & STRUCT_IsPlainOldData) // if there is nothing to construct or the struct is known to be memcpy-able, then allow memcpy { PropertyFlags |= CPF_IsPlainOldData; } if (Struct->StructFlags & STRUCT_NoDestructor) { PropertyFlags |= CPF_NoDestructor; } if (Struct->StructFlags & STRUCT_ZeroConstructor) { PropertyFlags |= CPF_ZeroConstructor; } }
void UArrayProperty::SerializeItem( FArchive& Ar, void* Value, void const* Defaults ) const { checkSlow(Inner); // Ensure that the Inner itself has been loaded before calling SerializeItem() on it Ar.Preload(Inner); FScriptArrayHelper ArrayHelper(this, Value); int32 n = ArrayHelper.Num(); Ar << n; if( Ar.IsLoading() ) { // If using a custom property list, don't empty the array on load. Not all indices may have been serialized, so we need to preserve existing values at those slots. if (Ar.ArUseCustomPropertyList) { const int32 OldNum = ArrayHelper.Num(); if (n > OldNum) { ArrayHelper.AddValues(n - OldNum); } else if (n < OldNum) { ArrayHelper.RemoveValues(n, OldNum - n); } } else { ArrayHelper.EmptyAndAddValues(n); } } ArrayHelper.CountBytes( Ar ); // Serialize a PropertyTag for the inner property of this array, allows us to validate the inner struct to see if it has changed FPropertyTag InnerTag(Ar, Inner, 0, (uint8*)Value, (uint8*)Defaults); if (Ar.UE4Ver() >= VER_UE4_INNER_ARRAY_TAG_INFO && InnerTag.Type == NAME_StructProperty) { if (Ar.IsSaving()) { Ar << InnerTag; } else if (Ar.IsLoading()) { Ar << InnerTag; auto CanSerializeFromStructWithDifferentName = [](const FArchive& InAr, const FPropertyTag& PropertyTag, const UStructProperty* StructProperty) { return PropertyTag.StructGuid.IsValid() && StructProperty && StructProperty->Struct && (PropertyTag.StructGuid == StructProperty->Struct->GetCustomGuid()); }; // Check if the Inner property can successfully serialize, the type may have changed UStructProperty* StructProperty = CastChecked<UStructProperty>(Inner); // if check redirector to make sure if the name has changed FName* NewName = FLinkerLoad::StructNameRedirects.Find(InnerTag.StructName); FName StructName = CastChecked<UStructProperty>(StructProperty)->Struct->GetFName(); if (NewName != nullptr && *NewName == StructName) { InnerTag.StructName = *NewName; } if (InnerTag.StructName != StructProperty->Struct->GetFName() && !CanSerializeFromStructWithDifferentName(Ar, InnerTag, StructProperty)) { UE_LOG(LogClass, Warning, TEXT("Property %s of %s has a struct type mismatch (tag %s != prop %s) in package: %s. If that struct got renamed, add an entry to ActiveStructRedirects."), *InnerTag.Name.ToString(), *GetName(), *InnerTag.StructName.ToString(), *CastChecked<UStructProperty>(Inner)->Struct->GetName(), *Ar.GetArchiveName()); #if WITH_EDITOR // Ensure the structure is initialized for (int32 i = 0; i < n; i++) { StructProperty->Struct->InitializeDefaultValue(ArrayHelper.GetRawPtr(i)); } #endif // WITH_EDITOR // Skip the property const int64 StartOfProperty = Ar.Tell(); const int64 RemainingSize = InnerTag.Size - (Ar.Tell() - StartOfProperty); uint8 B; for (int64 i = 0; i < RemainingSize; i++) { Ar << B; } return; } } } // need to know how much data this call to SerializeItem consumes, so mark where we are int32 DataOffset = Ar.Tell(); // If we're using a custom property list, first serialize any explicit indices int32 i = 0; bool bSerializeRemainingItems = true; bool bUsingCustomPropertyList = Ar.ArUseCustomPropertyList; if (bUsingCustomPropertyList && Ar.ArCustomPropertyList != nullptr) { // Initially we only serialize indices that are explicitly specified (in order) bSerializeRemainingItems = false; const FCustomPropertyListNode* CustomPropertyList = Ar.ArCustomPropertyList; const FCustomPropertyListNode* PropertyNode = CustomPropertyList; while (PropertyNode && i < n && !bSerializeRemainingItems) { if (PropertyNode->Property != Inner) { // A null property value signals that we should serialize the remaining array values in full starting at this index if (PropertyNode->Property == nullptr) { i = PropertyNode->ArrayIndex; } bSerializeRemainingItems = true; } else { // Set a temporary node to represent the item FCustomPropertyListNode ItemNode = *PropertyNode; ItemNode.ArrayIndex = 0; ItemNode.PropertyListNext = nullptr; Ar.ArCustomPropertyList = &ItemNode; // Serialize the item at this array index i = PropertyNode->ArrayIndex; Inner->SerializeItem(Ar, ArrayHelper.GetRawPtr(i)); PropertyNode = PropertyNode->PropertyListNext; // Restore the current property list Ar.ArCustomPropertyList = CustomPropertyList; } } } if (bSerializeRemainingItems) { // Temporarily suspend the custom property list (as we need these items to be serialized in full) Ar.ArUseCustomPropertyList = false; // Serialize each item until we get to the end of the array while (i < n) { Inner->SerializeItem(Ar, ArrayHelper.GetRawPtr(i++)); } // Restore use of the custom property list (if it was previously enabled) Ar.ArUseCustomPropertyList = bUsingCustomPropertyList; } if (Ar.UE4Ver() >= VER_UE4_INNER_ARRAY_TAG_INFO && Ar.IsSaving() && InnerTag.Type == NAME_StructProperty) { // set the tag's size InnerTag.Size = Ar.Tell() - DataOffset; if (InnerTag.Size > 0) { // mark our current location DataOffset = Ar.Tell(); // go back and re-serialize the size now that we know it Ar.Seek(InnerTag.SizeOffset); Ar << InnerTag.Size; // return to the current location Ar.Seek(DataOffset); } } }