// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundFrontendDocumentVersioning.h" #if WITH_EDITORONLY_DATA #include "Algo/Transform.h" #include "CoreGlobals.h" #include "Interfaces/MetasoundFrontendInterface.h" #include "Interfaces/MetasoundFrontendInterfaceRegistry.h" #include "MetasoundAccessPtr.h" #include "MetasoundDocumentInterface.h" #include "MetasoundFrontendDocumentBuilder.h" #include "MetasoundFrontendDocumentController.h" #include "MetasoundFrontendRegistries.h" #include "MetasoundFrontendSearchEngine.h" #include "MetasoundFrontendTransform.h" #include "MetasoundLog.h" #include "MetasoundTrace.h" #include "Misc/App.h" namespace Metasound::Frontend { #define METASOUND_VERSIONING_LOG(Verbosity, Format, ...) if (DocumentTransform::GetVersioningLoggingEnabled()) { UE_LOG(LogMetaSound, Verbosity, Format, ##__VA_ARGS__); } namespace VersioningPrivate { class FMigratePagePropertiesTransform : public FMetaSoundFrontendDocumentBuilder::IPropertyVersionTransform { public: virtual ~FMigratePagePropertiesTransform() = default; bool Transform(FMetaSoundFrontendDocumentBuilder& OutBuilder) const override { using namespace Metasound; bool bUpdated = false; auto MigrateInterfaceInputDefaults = [&](FMetasoundFrontendClassInterface& OutInterface) { for (FMetasoundFrontendClassInput& Input : OutInterface.Inputs) { PRAGMA_DISABLE_DEPRECATION_WARNINGS if (Input.DefaultLiteral.IsValid()) { Input.AddDefault(Frontend::DefaultPageID) = MoveTemp(Input.DefaultLiteral); Input.DefaultLiteral = FMetasoundFrontendLiteral::GetInvalid(); bUpdated = true; } PRAGMA_ENABLE_DEPRECATION_WARNINGS } }; FMetasoundFrontendDocument& Document = GetDocumentUnsafe(OutBuilder); // For all class definitions we are going to access the default interface instead of inspecting the // interface override. This is safe here because the class interface override did not exist in this // version of the document. checkf(Document.Metadata.Version.Number <= FMetasoundFrontendVersionNumber(1, 14), TEXT("Migration of page properties needs to happen before the introduction of node configuration to the document")); FMetasoundFrontendGraphClass& GraphClass = Document.RootGraph; MigrateInterfaceInputDefaults(GraphClass.GetDefaultInterface()); for (FMetasoundFrontendClass& Dependency : Document.Dependencies) { MigrateInterfaceInputDefaults(Dependency.GetDefaultInterface()); } struct FMigratePageGraphs : public FMetasoundFrontendGraphClass::IPropertyVersionTransform { public: virtual ~FMigratePageGraphs() = default; virtual bool Transform(FMetasoundFrontendGraphClass& OutClass) const override { PRAGMA_DISABLE_DEPRECATION_WARNINGS TArray& Pages = GetPagesUnsafe(OutClass); if (Pages.IsEmpty()) { Pages.Add(MoveTemp(OutClass.Graph)); return true; } PRAGMA_ENABLE_DEPRECATION_WARNINGS return false; } }; bUpdated |= FMigratePageGraphs().Transform(GraphClass); return bUpdated; } }; class FVersionDocumentInterfacesTransform : public FMetaSoundFrontendDocumentBuilder::IPropertyVersionTransform { public: virtual ~FVersionDocumentInterfacesTransform() = default; bool Transform(FMetaSoundFrontendDocumentBuilder& OutBuilder) const override { FMetasoundFrontendDocument& Document = GetDocumentUnsafe(OutBuilder); if (Document.RequiresInterfaceVersioning()) { Document.VersionInterfaces(); return true; } return false; } }; class FVersionDocumentTransform { public: virtual ~FVersionDocumentTransform() = default; protected: virtual FMetasoundFrontendVersionNumber GetTargetVersion() const = 0; virtual void TransformInternal(FDocumentHandle) const { checkNoEntry(); } virtual void TransformInternal(FMetasoundFrontendDocument& OutDocument) const { FDocumentAccessPtr DocAccessPtr = MakeAccessPtr(OutDocument.AccessPoint, OutDocument); return TransformInternal(FDocumentController::CreateDocumentHandle(DocAccessPtr)); } virtual void TransformInternal(FMetaSoundFrontendDocumentBuilder&) const { } public: bool Transform(FDocumentHandle InDocument) const { if (FMetasoundFrontendDocumentMetadata* Metadata = InDocument->GetMetadata()) { const FMetasoundFrontendVersionNumber TargetVersion = GetTargetVersion(); if (Metadata->Version.Number < TargetVersion) { TransformInternal(InDocument); Metadata->Version.Number = TargetVersion; return true; } } return false; } virtual bool Transform(FMetaSoundFrontendDocumentBuilder& OutDocumentBuilder) const { const FMetasoundFrontendDocumentMetadata& Metadata = OutDocumentBuilder.GetConstDocumentChecked().Metadata; const FMetasoundFrontendVersionNumber TargetVersion = GetTargetVersion(); if (Metadata.Version.Number < TargetVersion) { TransformInternal(OutDocumentBuilder); OutDocumentBuilder.SetVersionNumber(TargetVersion); return true; } return false; } }; /** Versions document from 1.0 to 1.1. */ class FVersionDocument_1_1 : public FVersionDocumentTransform { FName Name; const FString& Path; public: virtual ~FVersionDocument_1_1() = default; FVersionDocument_1_1(FName InName, const FString& InPath) : Name(InName) , Path(InPath) { } FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 1 }; } void TransformInternal(FDocumentHandle InDocument) const override { #if WITH_EDITOR FGraphHandle GraphHandle = InDocument->GetRootGraph(); TArray FrontendNodes = GraphHandle->GetNodes(); // Before literals could be stored on node inputs directly, they were stored // by creating hidden input nodes. Update the doc by finding all hidden input // nodes, placing the literal value of the input node directly on the // downstream node's input. Then delete the hidden input node. for (FNodeHandle& NodeHandle : FrontendNodes) { const bool bIsHiddenNode = NodeHandle->GetNodeStyle().Display.Visibility == EMetasoundFrontendNodeStyleDisplayVisibility::Hidden; const bool bIsInputNode = EMetasoundFrontendClassType::Input == NodeHandle->GetClassMetadata().GetType(); const bool bIsHiddenInputNode = bIsHiddenNode && bIsInputNode; if (bIsHiddenInputNode) { // Get literal value from input node. const FGuid VertexID = GraphHandle->GetVertexIDForInputVertex(NodeHandle->GetNodeName()); const FMetasoundFrontendLiteral DefaultLiteral = GraphHandle->GetDefaultInput(VertexID); // Apply literal value to downstream node's inputs. TArray OutputHandles = NodeHandle->GetOutputs(); if (ensure(OutputHandles.Num() == 1)) { FOutputHandle OutputHandle = OutputHandles[0]; TArray Inputs = OutputHandle->GetConnectedInputs(); OutputHandle->Disconnect(); for (FInputHandle& Input : Inputs) { if (const FMetasoundFrontendLiteral* Literal = Input->GetClassDefaultLiteral()) { if (!Literal->IsEqual(DefaultLiteral)) { Input->SetLiteral(DefaultLiteral); } } else { Input->SetLiteral(DefaultLiteral); } } } GraphHandle->RemoveNode(*NodeHandle); } } #else METASOUND_VERSIONING_LOG(Error, TEXT("Asset '%s' at '%s' must be saved with editor enabled in order to version document to target version '%s'."), *Name.ToString(), *Path, *GetTargetVersion().ToString()); #endif // !WITH_EDITOR } }; /** Versions document from 1.1 to 1.2. */ class FVersionDocument_1_2 : public FVersionDocumentTransform { private: const FName Name; const FString& Path; public: FVersionDocument_1_2(const FName InName, const FString& InPath) : Name(InName) , Path(InPath) { } virtual ~FVersionDocument_1_2() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 2 }; } void TransformInternal(FDocumentHandle InDocument) const override { #if WITH_EDITOR const FMetasoundFrontendGraphClass& GraphClass = InDocument->GetRootGraphClass(); FMetasoundFrontendClassMetadata Metadata = GraphClass.Metadata; Metadata.SetClassName({ "GraphAsset", Name, *Path }); Metadata.SetDisplayName(FText::FromString(Name.ToString())); InDocument->GetRootGraph()->SetGraphMetadata(Metadata); #else METASOUND_VERSIONING_LOG(Error, TEXT("Asset '%s' at '%s' must be saved with editor enabled in order to version document to target version '%s'."), *Name.ToString(), *Path, *GetTargetVersion().ToString()); #endif // !WITH_EDITOR } }; /** Versions document from 1.2 to 1.3. */ class FVersionDocument_1_3 : public FVersionDocumentTransform { public: virtual ~FVersionDocument_1_3() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return {1, 3}; } void TransformInternal(FDocumentHandle InDocument) const override { const FMetasoundFrontendGraphClass& GraphClass = InDocument->GetRootGraphClass(); FMetasoundFrontendClassMetadata Metadata = GraphClass.Metadata; Metadata.SetClassName(FMetasoundFrontendClassName { FName(), *FGuid::NewGuid().ToString(), FName() }); InDocument->GetRootGraph()->SetGraphMetadata(Metadata); } }; /** Versions document from 1.3 to 1.4. */ class FVersionDocument_1_4 : public FVersionDocumentTransform { public: virtual ~FVersionDocument_1_4() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return {1, 4}; } void TransformInternal(FDocumentHandle InDocument) const override { FMetasoundFrontendDocumentMetadata* Metadata = InDocument->GetMetadata(); check(Metadata); check(Metadata->Version.Number.Major == 1); check(Metadata->Version.Number.Minor == 3); const TSet& Interfaces = InDocument->GetInterfaceVersions(); // Version 1.3 did not have an "InterfaceVersion" property on the // document, so any document that is being updated should start off // with an "Invalid" interface version. if (ensure(Interfaces.IsEmpty())) { // At the time when version 1.4 of the document was introduced, // these were the only available interfaces. static const FMetasoundFrontendVersion PreexistingInterfaceVersions[] = { FMetasoundFrontendVersion{"MetaSound", {1, 0}}, FMetasoundFrontendVersion{"MonoSource", {1, 0}}, FMetasoundFrontendVersion{"StereoSource", {1, 0}}, FMetasoundFrontendVersion{"MonoSource", {1, 1}}, FMetasoundFrontendVersion{"StereoSource", {1, 1}} }; static const int32 NumPreexistingInterfaceVersions = sizeof(PreexistingInterfaceVersions) / sizeof(PreexistingInterfaceVersions[0]); TArray CandidateInterfaces; IInterfaceRegistry& InterfaceRegistry = IInterfaceRegistry::Get(); for (int32 i = 0; i < NumPreexistingInterfaceVersions; i++) { FMetasoundFrontendInterface Interface; if (InterfaceRegistry.FindInterface(GetInterfaceRegistryKey(PreexistingInterfaceVersions[i]), Interface)) { CandidateInterfaces.Add(Interface); } } const FMetasoundFrontendGraphClass& RootGraph = InDocument->GetRootGraphClass(); const TArray& Dependencies = InDocument->GetDependencies(); const TArray& Subgraphs = InDocument->GetSubgraphs(); if (const FMetasoundFrontendInterface* Interface = FindMostSimilarInterfaceSupportingEnvironment(RootGraph, Dependencies, Subgraphs, CandidateInterfaces)) { METASOUND_VERSIONING_LOG(Display, TEXT("Assigned interface [InterfaceVersion:%s] to document [RootGraphClassName:%s]"), *Interface->Metadata.Version.ToString(), *RootGraph.Metadata.GetClassName().ToString()); InDocument->AddInterfaceVersion(Interface->Metadata.Version); } else { METASOUND_VERSIONING_LOG(Warning, TEXT("Failed to find interface for document [RootGraphClassName:%s]"), *RootGraph.Metadata.GetClassName().ToString()); } } } }; /** Versions document from 1.4 to 1.5. */ class FVersionDocument_1_5 : public FVersionDocumentTransform { FName Name; const FString& Path; public: FVersionDocument_1_5(FName InName, const FString& InPath) : Name(InName) , Path(InPath) { } virtual ~FVersionDocument_1_5() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 5 }; } void TransformInternal(FDocumentHandle InDocument) const override { #if WITH_EDITOR const FMetasoundFrontendClassMetadata& Metadata = InDocument->GetRootGraphClass().Metadata; const FText NewAssetName = FText::FromString(Name.ToString()); if (Metadata.GetDisplayName().CompareTo(NewAssetName) != 0) { FMetasoundFrontendClassMetadata NewMetadata = Metadata; NewMetadata.SetDisplayName(NewAssetName); InDocument->GetRootGraph()->SetGraphMetadata(NewMetadata); } #else METASOUND_VERSIONING_LOG(Error, TEXT("Asset '%s' at '%s' must be saved with editor enabled in order to version document to target version '%s'."), *Name.ToString(), *Path, *GetTargetVersion().ToString()); #endif // !WITH_EDITOR } }; /** Versions document from 1.5 to 1.6. */ class FVersionDocument_1_6 : public FVersionDocumentTransform { public: FVersionDocument_1_6() = default; virtual ~FVersionDocument_1_6() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 6 }; } void TransformInternal(FDocumentHandle InDocument) const override { const FGuid NewAssetClassID = FGuid::NewGuid(); FMetasoundFrontendGraphClass Class = InDocument->GetRootGraphClass(); Class.Metadata.SetClassName(FMetasoundFrontendClassName({ }, FName(*NewAssetClassID.ToString()), { })); } }; /** Versions document from 1.6 to 1.7. */ class FVersionDocument_1_7 : public FVersionDocumentTransform { FName Name; const FString& Path; public: FVersionDocument_1_7(FName InName, const FString& InPath) : Name(InName) , Path(InPath) { } virtual ~FVersionDocument_1_7() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 7 }; } void TransformInternal(FDocumentHandle InDocument) const override { #if WITH_EDITOR auto RenameTransform = [](FNodeHandle NodeHandle) { // Required nodes are all (at the point of this transform) providing // unique names and customized display names (ex. 'Audio' for both mono & // L/R output, On Play, & 'On Finished'), so do not replace them by nulling // out the guid as a name and using the converted FName of the FText DisplayName. if (!NodeHandle->IsInterfaceMember()) { const FName NewNodeName = *NodeHandle->GetDisplayName().ToString(); NodeHandle->IterateInputs([&](FInputHandle InputHandle) { InputHandle->SetName(NewNodeName); }); NodeHandle->IterateOutputs([&](FOutputHandle OutputHandle) { OutputHandle->SetName(NewNodeName); }); NodeHandle->SetDisplayName(FText()); NodeHandle->SetNodeName(NewNodeName); } }; InDocument->GetRootGraph()->IterateNodes(RenameTransform, EMetasoundFrontendClassType::Input); InDocument->GetRootGraph()->IterateNodes(RenameTransform, EMetasoundFrontendClassType::Output); #else METASOUND_VERSIONING_LOG(Error, TEXT("Asset '%s' at '%s' must be saved with editor enabled in order to version document to target version '%s'."), *Name.ToString(), *Path, *GetTargetVersion().ToString()); #endif // !WITH_EDITOR } }; /** Versions document from 1.7 to 1.8. */ class FVersionDocument_1_8 : public FVersionDocumentTransform { FName Name; const FString& Path; public: FVersionDocument_1_8(FName InName, const FString& InPath) : Name(InName) , Path(InPath) { } virtual ~FVersionDocument_1_8() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 8 }; } void TransformInternal(FDocumentHandle InDocument) const override { #if WITH_EDITOR // For all class definitions we are going to access the default interface instead of inspecting the // interface override. This is safe here because the class interface override did not exist in this // version of the document. checkf(InDocument->GetMetadata()->Version.Number <= FMetasoundFrontendVersionNumber(1, 14), TEXT("Migration of page properties needs to happen before the introduction of node configuration to the document")); // Do not serialize MetaData text for dependencies as // CacheRegistryData dynamically provides this. InDocument->IterateDependencies([](FMetasoundFrontendClass& Dependency) { constexpr bool bSerializeText = false; Dependency.Metadata.SetSerializeText(bSerializeText); for (FMetasoundFrontendClassInput& Input : Dependency.GetDefaultInterface().Inputs) { Input.Metadata.SetSerializeText(false); } for (FMetasoundFrontendClassOutput& Output : Dependency.GetDefaultInterface().Outputs) { Output.Metadata.SetSerializeText(false); } }); const TSet& InterfaceVersions = InDocument->GetInterfaceVersions(); using FNameDataTypePair = TPair; TSet InterfaceInputs; TSet InterfaceOutputs; for (const FMetasoundFrontendVersion& Version : InterfaceVersions) { FInterfaceRegistryKey RegistryKey = GetInterfaceRegistryKey(Version); const IInterfaceRegistryEntry* Entry = IInterfaceRegistry::Get().FindInterfaceRegistryEntry(RegistryKey); if (ensure(Entry)) { const FMetasoundFrontendInterface& Interface = Entry->GetInterface(); Algo::Transform(Interface.Inputs, InterfaceInputs, [](const FMetasoundFrontendClassInput& Input) { return FNameDataTypePair(Input.Name, Input.TypeName); }); Algo::Transform(Interface.Outputs, InterfaceOutputs, [](const FMetasoundFrontendClassOutput& Output) { return FNameDataTypePair(Output.Name, Output.TypeName); }); } } // Only serialize MetaData text for inputs owned by the graph (not by interfaces) FMetasoundFrontendGraphClass RootGraphClass = InDocument->GetRootGraphClass(); for (FMetasoundFrontendClassInput& Input : RootGraphClass.GetDefaultInterface().Inputs) { const bool bSerializeText = !InterfaceInputs.Contains(FNameDataTypePair(Input.Name, Input.TypeName)); Input.Metadata.SetSerializeText(bSerializeText); } // Only serialize MetaData text for outputs owned by the graph (not by interfaces) for (FMetasoundFrontendClassOutput& Output : RootGraphClass.GetDefaultInterface().Outputs) { const bool bSerializeText = !InterfaceOutputs.Contains(FNameDataTypePair(Output.Name, Output.TypeName)); Output.Metadata.SetSerializeText(bSerializeText); } InDocument->SetRootGraphClass(MoveTemp(RootGraphClass)); #else METASOUND_VERSIONING_LOG(Error, TEXT("Asset '%s' at '%s' must be saved with editor enabled in order to version document to target version '%s'."), *Name.ToString(), *Path, *GetTargetVersion().ToString()); #endif // !WITH_EDITOR } }; /** Versions document from 1.8 to 1.9. */ class FVersionDocument_1_9 : public FVersionDocumentTransform { FName Name; const FString& Path; public: FVersionDocument_1_9(FName InName, const FString& InPath) : Name(InName) , Path(InPath) { } virtual ~FVersionDocument_1_9() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 9 }; } void TransformInternal(FDocumentHandle InDocument) const override { #if WITH_EDITOR // Display name text is no longer copied at this versioning point for assets // from the asset's FName to avoid FText warnings regarding generation from // an FString. It also avoids desync if asset gets moved. FMetasoundFrontendGraphClass RootGraphClass = InDocument->GetRootGraphClass(); RootGraphClass.Metadata.SetDisplayName(FText()); InDocument->SetRootGraphClass(MoveTemp(RootGraphClass)); #else METASOUND_VERSIONING_LOG(Error, TEXT("Asset '%s' at '%s' must be saved with editor enabled in order to version document to target version '%s'."), *Name.ToString(), *Path, *GetTargetVersion().ToString()); #endif // !WITH_EDITOR } }; /** Versions document from 1.9 to 1.10. */ class FVersionDocument_1_10 : public FVersionDocumentTransform { public: virtual ~FVersionDocument_1_10() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 10 }; } void TransformInternal(FDocumentHandle InDocument) const override { FMetasoundFrontendGraphClass Class = InDocument->GetRootGraphClass(); FMetasoundFrontendGraphClassPresetOptions PresetOptions = Class.PresetOptions; Class.PresetOptions.bIsPreset = Class.Metadata.GetAndClearAutoUpdateManagesInterface_Deprecated(); InDocument->SetRootGraphClass(MoveTemp(Class)); } }; /** Versions document from 1.10 to 1.11. */ class FVersionDocument_1_11 : public FVersionDocumentTransform { public: virtual ~FVersionDocument_1_11() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 11 }; } void TransformInternal(FDocumentHandle InDocument) const override { // Clear object literals on inputs that are connected // to prevent referencing assets that are not used in the graph InDocument->GetRootGraph()->IterateNodes([](FNodeHandle NodeHandle) { TArray NodeInputs = NodeHandle->GetInputs(); for (FInputHandle NodeInput : NodeInputs) { NodeInput->ClearConnectedObjectLiterals(); } }); } }; /** Versions document from 1.11 to 1.12. */ class FVersionDocument_1_12 : public FVersionDocumentTransform { const FName Name; const FSoftObjectPath* Path = nullptr; public: FVersionDocument_1_12(FName InName, const FSoftObjectPath& InAssetPath) : Name(InName) , Path(&InAssetPath) { } virtual ~FVersionDocument_1_12() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 12 }; } void TransformInternal(FMetaSoundFrontendDocumentBuilder& OutBuilder) const override { using namespace VersioningPrivate; if (IsRunningCookCommandlet()) { if (DocumentTransform::GetVersioningLoggingEnabled()) { METASOUND_VERSIONING_LOG(Display, TEXT("Resave recommended: Asset '%s' at '%s' skipped migrated editor data/creation of input template nodes during cook to target document version '%s'."), *Name.ToString(), *Path->ToString(), *GetTargetVersion().ToString()); } } else { FMigratePagePropertiesTransform().Transform(OutBuilder); OutBuilder.GetMetasoundAsset().MigrateEditorGraph(OutBuilder); METASOUND_VERSIONING_LOG(Display, TEXT("Resave recommended: Asset '%s' at '%s' successfully migrated editor data in target document version '%s'."), *Name.ToString(), *Path->ToString(), *GetTargetVersion().ToString()); } } }; /** Versions document from 1.12 to 1.13. */ class FVersionDocument_1_13 : public FVersionDocumentTransform { public: virtual ~FVersionDocument_1_13() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 13 }; } void TransformInternal(FMetaSoundFrontendDocumentBuilder& OutBuilder) const override { FMigratePagePropertiesTransform().Transform(OutBuilder); } }; /** Versions document from 1.13 to 1.14. */ class FVersionDocument_1_14 : public FVersionDocumentTransform { public: virtual ~FVersionDocument_1_14() = default; FMetasoundFrontendVersionNumber GetTargetVersion() const override { return { 1, 14 }; } void TransformInternal(FMetaSoundFrontendDocumentBuilder& OutBuilder) const override { // Between 1.13 and 1.14, it was possible to add multiple default input page values // due to missing versioning logic. This fixes that issue if any data was serialized // to a MetaSound Asset by removing any extraneous default data (early values in the // array were stale). FMetasoundFrontendDocument& Document = const_cast(OutBuilder.GetConstDocumentChecked()); // For all class definitions we are going to access the default interface instead of inspecting the // interface override. This is safe here because the class interface override did not exist in this // version of the document. checkf(Document.Metadata.Version.Number <= FMetasoundFrontendVersionNumber(1, 14), TEXT("Migration of page properties needs to happen before the introduction of node configuration to the document")); for (FMetasoundFrontendClassInput& Input : Document.RootGraph.GetDefaultInterface().Inputs) { int32 PageIDIndex = INDEX_NONE; TArray& Defaults = const_cast&>(Input.GetDefaults()); for (int32 Index = 0; Index < Defaults.Num(); ++Index) { FMetasoundFrontendClassInputDefault& Default = Defaults[Index]; const bool bIsDefault = Default.PageID == Frontend::DefaultPageID; if (bIsDefault) { if (PageIDIndex == INDEX_NONE) { PageIDIndex = Index; } else { Defaults.RemoveAt(PageIDIndex); break; } } } } // Safeguards against prior fix-up corrupting any cached data if (IDocumentBuilderRegistry* BuilderRegistry = IDocumentBuilderRegistry::Get()) { BuilderRegistry->ReloadBuilder(Document.RootGraph.Metadata.GetClassName()); } } }; bool VersionBuilderDocument(FMetaSoundFrontendDocumentBuilder& Builder) { UObject& DocObject = Builder.CastDocumentObjectChecked(); const FName Name = DocObject.GetFName(); const FString Path = DocObject.GetPathName(); bool bWasUpdated = false; bWasUpdated |= FVersionDocument_1_12(Name, Path).Transform(Builder); bWasUpdated |= FVersionDocument_1_13().Transform(Builder); bWasUpdated |= FVersionDocument_1_14().Transform(Builder); return bWasUpdated; } } // namespace VersioningPrivate bool VersionDocument(FMetaSoundFrontendDocumentBuilder& Builder) { using namespace VersioningPrivate; bool bWasUpdated = false; UObject& MetaSoundAsset = Builder.CastDocumentObjectChecked(); const FName Name(*MetaSoundAsset.GetName()); const FString Path = MetaSoundAsset.GetPathName(); // Copied as value will be mutated with each applicable transform below const FMetasoundFrontendVersionNumber InitVersionNumber = Builder.GetConstDocumentChecked().Metadata.Version.Number; // Old manual property transform that was applied prior to versioning schema being added. // Only runs if internal logic finds necessary. bWasUpdated = FVersionDocumentInterfacesTransform().Transform(Builder); if (InitVersionNumber < GetMaxDocumentVersion()) { // Controller (Soft Deprecated) Transforms if (InitVersionNumber.Major == 1 && InitVersionNumber.Minor < 12) { // Page Graph migration must be completed for graph accessor back // compat prior to all controller versioning, so just do it here. FMigratePagePropertiesTransform().Transform(Builder); FDocumentHandle DocHandle = Builder.GetMetasoundAsset().GetDocumentHandle(); bWasUpdated |= FVersionDocument_1_1(Name, Path).Transform(DocHandle); bWasUpdated |= FVersionDocument_1_2(Name, Path).Transform(DocHandle); bWasUpdated |= FVersionDocument_1_3().Transform(DocHandle); bWasUpdated |= FVersionDocument_1_4().Transform(DocHandle); bWasUpdated |= FVersionDocument_1_5(Name, Path).Transform(DocHandle); bWasUpdated |= FVersionDocument_1_6().Transform(DocHandle); bWasUpdated |= FVersionDocument_1_7(Name, Path).Transform(DocHandle); bWasUpdated |= FVersionDocument_1_8(Name, Path).Transform(DocHandle); bWasUpdated |= FVersionDocument_1_9(Name, Path).Transform(DocHandle); bWasUpdated |= FVersionDocument_1_10().Transform(DocHandle); bWasUpdated |= FVersionDocument_1_11().Transform(DocHandle); // No longer supported, new versions should go in VersioningPrivate::VersionBuilderDocument } bWasUpdated |= VersionBuilderDocument(Builder); if (bWasUpdated) { const FMetasoundFrontendVersionNumber& NewVersionNumber = Builder.GetConstDocumentChecked().Metadata.Version.Number; METASOUND_VERSIONING_LOG(Verbose, TEXT("MetaSound at '%s' Document Versioned: '%s' --> '%s'"), *Path, *InitVersionNumber.ToString(), *NewVersionNumber.ToString()); } } return bWasUpdated; } #undef METASOUND_VERSIONING_LOG } // namespace Metasound::Frontend #endif // WITH_EDITORONLY_DATA