// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License. #include "Libraries/DirectiveUtilEditorAssetLibrary.h" #include "Subsystems/EditorAssetSubsystem.h" #include "Algo/Transform.h" #include "AssetRegistry/IAssetRegistry.h" #include "AssetRegistry/ARFilter.h" #include "AssetToolsModule.h" #include "IAssetTools.h" #include "Misc/AssetRegistryInterface.h" #include "Modules/ModuleManager.h" #include "UObject/ObjectRedirector.h" namespace { void WaitForAssetRegistry() { if (IAssetRegistry* AssetRegistry = IAssetRegistry::Get()) { if (AssetRegistry->IsLoadingAssets()) { AssetRegistry->WaitForCompletion(); } } } } TArray UDirectiveUtilEditorAssetLibrary::GetAssetDataListFromDirectory( const FString& DirectoryPath, const bool bRecursive) { WaitForAssetRegistry(); TArray AssetDataList; UEditorAssetSubsystem* EditorAssetSubsystem = GEditor ? GEditor->GetEditorSubsystem() : nullptr; if (!EditorAssetSubsystem) { return AssetDataList; } const TArray AssetPaths = EditorAssetSubsystem->ListAssets(DirectoryPath, bRecursive, false); Algo::Transform(AssetPaths, AssetDataList, [EditorAssetSubsystem](const FString& AssetPath) { return EditorAssetSubsystem->FindAssetData(AssetPath); }); AssetDataList.RemoveAll([](const FAssetData& AssetData) { return !AssetData.IsValid(); }); return AssetDataList; } TMap UDirectiveUtilEditorAssetLibrary::FindDuplicateAssets( const TArray& DirectoryPaths, const bool bRecursive) { TArray CombinedAssetDataList; for (const FString& DirectoryPath : DirectoryPaths) { const TArray AssetDataList = GetAssetDataListFromDirectory(DirectoryPath, bRecursive); CombinedAssetDataList.Append(AssetDataList); } TMap DuplicateAssetsMap; for (const FAssetData& AssetData : CombinedAssetDataList) { if (!AssetData.IsValid()) { continue; } const FString AssetName = AssetData.AssetName.ToString(); const FString AssetClass = AssetData.AssetClassPath.ToString(); const FString AssetPath = AssetData.GetSoftObjectPath().ToString(); const FDirectiveUtilAssetKey AssetKey(AssetName, AssetClass); if (FDirectiveUtilDuplicateAssetData* ExistingData = DuplicateAssetsMap.Find(AssetKey)) { ExistingData->DuplicateAssetPaths.AddUnique(AssetPath); } else { FDirectiveUtilDuplicateAssetData DuplicateAssetData; DuplicateAssetData.AssetName = AssetName; DuplicateAssetData.AssetClass = AssetClass; DuplicateAssetData.DuplicateAssetPaths.Add(AssetPath); DuplicateAssetsMap.Emplace(AssetKey, MoveTemp(DuplicateAssetData)); } } TArray KeysToRemove; for (const auto& Pair : DuplicateAssetsMap) { if (Pair.Value.DuplicateAssetPaths.Num() <= 1) { KeysToRemove.Add(Pair.Key); } } for (const FDirectiveUtilAssetKey& Key : KeysToRemove) { DuplicateAssetsMap.Remove(Key); } return DuplicateAssetsMap; } EDirectiveUtilSuccessStatus UDirectiveUtilEditorAssetLibrary::FixUpRedirectorsInPaths(const TArray& DirectoryPaths, int32& OutRedirectorsProcessed) { OutRedirectorsProcessed = 0; IAssetRegistry* AssetRegistry = IAssetRegistry::Get(); if (!AssetRegistry) { return EDirectiveUtilSuccessStatus::Failure; } if (AssetRegistry->IsLoadingAssets()) { AssetRegistry->WaitForCompletion(); } FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); IAssetTools& AssetTools = AssetToolsModule.Get(); if (AssetTools.IsFixupReferencersInProgress()) { return EDirectiveUtilSuccessStatus::Failure; } FARFilter Filter; Filter.bRecursiveClasses = false; Filter.ClassPaths.Add(UObjectRedirector::StaticClass()->GetClassPathName()); if (DirectoryPaths.Num() > 0) { Filter.bRecursivePaths = true; for (const FString& DirectoryPath : DirectoryPaths) { Filter.PackagePaths.Add(FName(*DirectoryPath)); } } TArray RedirectorAssets; if (!AssetRegistry->GetAssets(Filter, RedirectorAssets)) { return EDirectiveUtilSuccessStatus::Failure; } TArray Redirectors; Redirectors.Reserve(RedirectorAssets.Num()); for (const FAssetData& RedirectorData : RedirectorAssets) { if (UObjectRedirector* Redirector = Cast(RedirectorData.GetAsset())) { Redirectors.Add(Redirector); } } if (Redirectors.Num() == 0) { return EDirectiveUtilSuccessStatus::Success; } AssetTools.FixupReferencers(Redirectors, false, ERedirectFixupMode::DeleteFixedUpRedirectors); OutRedirectorsProcessed = Redirectors.Num(); return EDirectiveUtilSuccessStatus::Success; } TArray UDirectiveUtilEditorAssetLibrary::GetAssetsByClass( UClass* AssetClass, const FString& PackagePath, const bool bRecursiveClasses, const bool bRecursivePaths, EDirectiveUtilSuccessStatus& OutStatus) { WaitForAssetRegistry(); TArray Result; OutStatus = EDirectiveUtilSuccessStatus::Failure; if (!IsValid(AssetClass)) { return Result; } IAssetRegistry* AssetRegistry = IAssetRegistry::Get(); if (!AssetRegistry) { return Result; } FARFilter Filter; Filter.ClassPaths.Add(AssetClass->GetClassPathName()); Filter.bRecursiveClasses = bRecursiveClasses; if (!PackagePath.IsEmpty()) { Filter.PackagePaths.Add(FName(*PackagePath)); Filter.bRecursivePaths = bRecursivePaths; } if (AssetRegistry->GetAssets(Filter, Result)) { OutStatus = EDirectiveUtilSuccessStatus::Success; } return Result; } TArray UDirectiveUtilEditorAssetLibrary::GetAssetDependencies(const FAssetData& Asset, const bool bHardDependenciesOnly, EDirectiveUtilSuccessStatus& OutStatus) { using namespace UE::AssetRegistry; WaitForAssetRegistry(); TArray Result; OutStatus = EDirectiveUtilSuccessStatus::Failure; IAssetRegistry* AssetRegistry = IAssetRegistry::Get(); if (!AssetRegistry || !Asset.IsValid()) { return Result; } TArray Dependencies; const FDependencyQuery Query = bHardDependenciesOnly ? FDependencyQuery(EDependencyQuery::Hard) : FDependencyQuery(); if (AssetRegistry->GetDependencies(Asset.PackageName, Dependencies, EDependencyCategory::Package, Query)) { Result.Reserve(Dependencies.Num()); for (const FName& Dependency : Dependencies) { Result.Add(Dependency.ToString()); } OutStatus = EDirectiveUtilSuccessStatus::Success; } return Result; } TArray UDirectiveUtilEditorAssetLibrary::GetAssetReferencers(const FAssetData& Asset, const bool bHardReferencesOnly, EDirectiveUtilSuccessStatus& OutStatus) { using namespace UE::AssetRegistry; WaitForAssetRegistry(); TArray Result; OutStatus = EDirectiveUtilSuccessStatus::Failure; IAssetRegistry* AssetRegistry = IAssetRegistry::Get(); if (!AssetRegistry || !Asset.IsValid()) { return Result; } TArray Referencers; const FDependencyQuery Query = bHardReferencesOnly ? FDependencyQuery(EDependencyQuery::Hard) : FDependencyQuery(); if (AssetRegistry->GetReferencers(Asset.PackageName, Referencers, EDependencyCategory::Package, Query)) { Result.Reserve(Referencers.Num()); for (const FName& Referencer : Referencers) { Result.Add(Referencer.ToString()); } OutStatus = EDirectiveUtilSuccessStatus::Success; } return Result; } FString UDirectiveUtilEditorAssetLibrary::GetDefaultAssetNameForClass(UClass* AssetClass, EDirectiveUtilSuccessStatus& OutStatus) { OutStatus = EDirectiveUtilSuccessStatus::Failure; if (!IsValid(AssetClass)) { return FString(); } FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); IAssetTools& AssetTools = AssetToolsModule.Get(); #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 const TOptional ResolvedName = AssetTools.GetDefaultAssetNameForClass(AssetClass, nullptr, nullptr); #else const TOptional ResolvedName = AssetTools.GetDefaultAssetNameForClass(AssetClass); #endif if (ResolvedName.IsSet() && !ResolvedName.GetValue().IsEmpty()) { OutStatus = EDirectiveUtilSuccessStatus::Success; return ResolvedName.GetValue(); } return FString(); }