void UExporter::ExportComponentDefinitions(const FExportObjectInnerContext* Context, const TArray<UObject*>& Components, FOutputDevice& Ar, uint32 PortFlags) { PortFlags |= PPF_ExportsNotFullyQualified; if (!(PortFlags & PPF_SeparateDefine)) { // export forward declarations // technically we only need to do this if there are circular references but it doesn't seem worth it // to complicate this code for a minor speed improvement in the text import path for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UObject* Component = Components[ComponentIndex]; FName ComponentName = Component->GetFName(); if (!Component->HasAnyMarks(OBJECTMARK_TagImp) && !Component->HasAnyFlags(RF_TextExportTransient)) { if (Component->HasAnyFlags(RF_ClassDefaultObject) || Component->GetArchetype()->HasAllFlags(RF_ClassDefaultObject)) { Ar.Logf(TEXT("%sBegin Object Class=%s Name=%s ObjName=%s%s"), FCString::Spc(TextIndent), *Component->GetClass()->GetName(), *ComponentName.ToString(), *Component->GetName(), LINE_TERMINATOR); } else { Ar.Logf(TEXT("%sBegin Object Class=%s Name=%s ObjName=%s Archetype=%s'%s'%s"),FCString::Spc(TextIndent),*Component->GetClass()->GetName(), *ComponentName.ToString(), *Component->GetName(), *Component->GetArchetype()->GetClass()->GetName(), *Component->GetArchetype()->GetPathName(), LINE_TERMINATOR); } if (PortFlags & PPF_SeparateDeclare) { ExportObjectInner(Context, Component, Ar, PortFlags, false); } Ar.Logf(TEXT("%sEnd Object%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); } } } if (!(PortFlags & PPF_SeparateDeclare)) { // export property definitions for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UObject* Component = Components[ComponentIndex]; FName ComponentName = Component->GetFName(); if (!Component->HasAnyMarks(OBJECTMARK_TagImp) && !Component->HasAnyFlags(RF_TextExportTransient)) { Ar.Logf(TEXT("%sBegin Object Name=%s%s"), FCString::Spc(TextIndent), *ComponentName.ToString(), LINE_TERMINATOR); uint32 OldPortFlags = PortFlags; if (!(Component->HasAnyFlags(RF_ClassDefaultObject) || Component->GetArchetype()->HasAllFlags(RF_ClassDefaultObject))) { // we created this thing with an archetype (see archetype=, above), so we don't want to list the archetype because it is unqualified and will clash, resetting the archetype pointer to something silly PortFlags |= PPF_NoInternalArcheType; } ExportObjectInner(Context, Component, Ar, PortFlags, false); PortFlags = OldPortFlags; Ar.Logf(TEXT("%sEnd Object%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); Component->Mark(OBJECTMARK_TagImp); } } } }
// This is from FArchiveTraceRoute -This only creates object graph of all objects // This can be used by other classes such as FTraceReferences - trace references of one object FArchiveObjectGraph::FArchiveObjectGraph(bool IncludeTransients, EObjectFlags KeepFlags) : CurrentReferencer(NULL), bIncludeTransients(IncludeTransients), RequiredFlags(KeepFlags) { ArIsObjectReferenceCollector = true; // ALL objects reference their outers...it's just log spam here //ArIgnoreOuterRef = true; TArray<UObject*> RootObjects; // allocate enough memory for all objects ObjectGraph.Empty(GetUObjectArray().GetObjectArrayNum()); RootObjects.Empty(GetUObjectArray().GetObjectArrayNum()); // search for objects that have the right flags and add them to the list of objects that we're going to start with // all other objects need to be tagged so that we can tell whether they've been serialized or not. for( FObjectIterator It; It; ++It ) { UObject* CurrentObject = *It; if ( CurrentObject->HasAnyFlags(RequiredFlags) ) { // make sure it isn't tagged // ASKRON: WHY do we need this? CurrentObject->UnMark(OBJECTMARK_TagExp); RootObjects.Add(CurrentObject); ObjectGraph.Add(CurrentObject, new FObjectGraphNode(CurrentObject)); } else { // ASKRON: WHY do we need this? CurrentObject->Mark(OBJECTMARK_TagExp); } } // Populate the ObjectGraph - this serializes our root set to map out the relationships between all rooted objects GenerateObjectGraph(RootObjects); // we won't be adding any additional objects for the arrays and graphs, so free up any memory not being used. RootObjects.Shrink(); ObjectGraph.Shrink(); // we're done with serialization; clear the tags so that we don't interfere with anything else for( FObjectIterator It; It; ++It ) { It->UnMark(OBJECTMARK_TagExp); } }
/** * Exports the property values for the specified object as text to the output device. * * @param Context Context from which the set of 'inner' objects is extracted. If NULL, an object iterator will be used. * @param Out the output device to send the exported text to * @param ObjectClass the class of the object to dump properties for * @param Object the address of the object to dump properties for * @param Indent number of spaces to prepend to each line of output * @param DiffClass the class to use for comparing property values when delta export is desired. * @param Diff the address of the object to use for determining whether a property value should be exported. If the value in Object matches the corresponding * value in Diff, it is not exported. Specify NULL to export all properties. * @param Parent the UObject corresponding to Object * @param PortFlags flags used for modifying the output and/or behavior of the export */ void ExportProperties ( const FExportObjectInnerContext* Context, FOutputDevice& Out, UClass* ObjectClass, uint8* Object, int32 Indent, UClass* DiffClass, uint8* Diff, UObject* Parent, uint32 PortFlags, UObject* ExportRootScope ) { FString ThisName = TEXT("(none)"); check(ObjectClass != NULL); for( UProperty* Property = ObjectClass->PropertyLink; Property; Property = Property->PropertyLinkNext ) { if (!Property->ShouldPort(PortFlags)) continue; ThisName = Property->GetName(); UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property); UObjectPropertyBase* ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(Property) : NULL; const uint32 ExportFlags = PortFlags | PPF_Delimited; if ( ArrayProperty != NULL ) { // Export dynamic array. UProperty* InnerProp = ArrayProperty->Inner; ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(InnerProp) : NULL; // This is used as the default value in the case of an array property that has // fewer elements than the exported object. uint8* StructDefaults = NULL; UStructProperty* StructProperty = Cast<UStructProperty>(InnerProp); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { void* Arr = Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex); FScriptArrayHelper ArrayHelper(ArrayProperty, Arr); void* DiffArr = NULL; if( DiffClass ) { DiffArr = Property->ContainerPtrToValuePtrForDefaults<void>(DiffClass, Diff, PropertyArrayIndex); } // we won't use this if DiffArr is NULL, but we have to set it up to something FScriptArrayHelper DiffArrayHelper(ArrayProperty, DiffArr); bool bAnyElementDiffered = false; for( int32 DynamicArrayIndex=0; DynamicArrayIndex<ArrayHelper.Num(); DynamicArrayIndex++ ) { FString Value; // compare each element's value manually so that elements which match the NULL value for the array's inner property type // but aren't in the diff array are still exported uint8* SourceData = ArrayHelper.GetRawPtr(DynamicArrayIndex); uint8* DiffData = DiffArr && DynamicArrayIndex < DiffArrayHelper.Num() ? DiffArrayHelper.GetRawPtr(DynamicArrayIndex) : StructDefaults; bool bExportItem = DiffData == NULL || (DiffData != SourceData && !InnerProp->Identical(SourceData, DiffData, ExportFlags)); if ( bExportItem ) { bAnyElementDiffered = true; InnerProp->ExportTextItem(Value, SourceData, DiffData, Parent, ExportFlags, ExportRootScope); if(ExportObjectProp) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(ArrayHelper.GetRawPtr(DynamicArrayIndex)); check(!Obj || Obj->IsValidLowLevel()); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component // Is this an array of components? if ( InnerProp->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex, *Value ); } // if some other element has already been determined to differ from the defaults, then export this item with no data so that // the different array's size is maintained on import (this item will get the default values for that index, if any) // however, if no elements of the array have changed, we still don't want to export anything // so that the array size will also be taken from the defaults, which won't be the case if any element is exported else if (bAnyElementDiffered) { Out.Logf( TEXT("%s%s(%i)=()\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex ); } } } if (StructDefaults) { StructProperty->DestroyValue(StructDefaults); FMemory::Free(StructDefaults); } } else { for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { FString Value; // Export single element. uint8* DiffData = (DiffClass && Property->IsInContainer(DiffClass->GetPropertiesSize())) ? Diff : NULL; if( Property->ExportText_InContainer( PropertyArrayIndex, Value, Object, DiffData, Parent, ExportFlags, ExportRootScope ) ) { if ( ExportObjectProp ) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex)); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component if ( Property->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } if( Property->ArrayDim == 1 ) { Out.Logf( TEXT("%s%s=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), *Value ); } else { Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), PropertyArrayIndex, *Value ); } } } } } // Allows to import/export C++ properties in case the automatic unreal script mesh wouldn't work. Parent->ExportCustomProperties(Out, Indent); }
void UExporter::ExportObjectInner(const FExportObjectInnerContext* Context, UObject* Object, FOutputDevice& Ar, uint32 PortFlags, bool bSkipComponents) { // indent all the text in here TextIndent += 3; FExportObjectInnerContext::InnerList ObjectInners; if ( Context ) { const FExportObjectInnerContext::InnerList* Inners = Context->ObjectToInnerMap.Find( Object ); if ( Inners ) { ObjectInners = *Inners; } } else { for (TObjectIterator<UObject> It; It; ++It) { if ( It->GetOuter() == Object ) { ObjectInners.Add( *It ); } } } TArray<UObject*> Components; if (!bSkipComponents) { // first export the components Object->CollectDefaultSubobjects(Components, false); } if (!(PortFlags & PPF_SeparateDefine)) { for ( int32 ObjIndex = 0 ; ObjIndex < ObjectInners.Num() ; ++ObjIndex ) { // NOTE: We ignore inner objects that have been tagged for death UObject* Obj = ObjectInners[ObjIndex]; if ( !Obj->IsPendingKill() && !Obj->IsDefaultSubobject() && !Obj->HasAnyFlags(RF_TextExportTransient) && FCString::Stricmp(*Obj->GetClass()->GetName(), TEXT("Model")) != 0) { // export the object UExporter::ExportToOutputDevice( Context, Obj, NULL, Ar, (PortFlags & PPF_Copy) ? TEXT("Copy") : TEXT("T3D"), TextIndent, PortFlags | PPF_SeparateDeclare, false, ExportRootScope ); } } if (!bSkipComponents) { ExportComponentDefinitions(Context, Components, Ar, PortFlags | PPF_SeparateDeclare); } } if (!(PortFlags & PPF_SeparateDeclare)) { for ( int32 ObjIndex = 0 ; ObjIndex < ObjectInners.Num() ; ++ObjIndex ) { // NOTE: We ignore inner objects that have been tagged for death UObject* Obj = ObjectInners[ObjIndex]; if ( !Obj->IsPendingKill() && !Obj->IsDefaultSubobject() && !Obj->HasAnyFlags(RF_TextExportTransient) && FCString::Stricmp(*Obj->GetClass()->GetName(), TEXT("Model")) != 0) { // export the object UExporter::ExportToOutputDevice( Context, Obj, NULL, Ar, (PortFlags & PPF_Copy) ? TEXT("Copy") : TEXT("T3D"), TextIndent, PortFlags | PPF_SeparateDefine, false, ExportRootScope ); // don't reexport below in ExportProperties Obj->Mark(OBJECTMARK_TagImp); } } if (!bSkipComponents) { ExportComponentDefinitions(Context, Components, Ar, PortFlags | PPF_SeparateDefine); } // export the object's properties // Note: we use archetype as the object to diff properties against before they exported. When object is created, they should create from archetype // and using this system, it should recover all properties it needs to copy uint8 *CompareObject; if (Object->HasAnyFlags(RF_ClassDefaultObject)) { CompareObject = (uint8*)Object; } else { CompareObject = (uint8*)Object->GetArchetype(); } ExportProperties( Context, Ar, Object->GetClass(), (uint8*)Object, TextIndent, Object->GetClass(), CompareObject, Object, PortFlags, ExportRootScope ); if (!bSkipComponents) { // Export anything extra for the components. Used for instanced foliage. // This is done after the actor properties so these are set when regenerating the extra data objects. ExportComponentExtra( Context, Components, Ar, PortFlags ); } } // remove indent TextIndent -= 3; }