Custom asset reimport menu options

I’ve got a custom asset, which I’ve made a custom factory for. The actual file is just text, so I load it, do some processing and save whatever data I need in the asset. I’ve also implemented an FReimportHandler so that the asset can be re-imported.

The factory is like so:

// Copyright 2018 David Colson. All Rights Reserved.

#include "StoryAssetFactory.h"

#include "HAL/PlatformFilemanager.h"
#include "Misc/Paths.h"
#include "Interfaces/IPluginManager.h"
#include "InkEditor.h"
#include "StoryAsset.h"
#include "InkCompiler.h"
#include "Misc/FileHelper.h"
#include "EditorFramework/AssetImportData.h"


////////////////////////////////////////////////////////
UStoryAssetFactory::UStoryAssetFactory()
{
	Formats.Add(FString(TEXT("ink;")) + NSLOCTEXT("UTextAssetFactory", "FormatTxt", "Ink Story File").ToString());
	Formats.Add(FString(TEXT("json;")) + NSLOCTEXT("UTextAssetFactory", "FormatTxt", "Compiled Ink Story File").ToString());
	SupportedClass = UStoryAsset::StaticClass();
	bCreateNew = false;
	bEditorImport = true;
	
}

////////////////////////////////////////////////////////
bool UStoryAssetFactory::FactoryCanImport(const FString & Filename)
{
	if (Filename.EndsWith("ink.json") || Filename.EndsWith("ink"))
	{
		return true;
	}
	return false;
}

////////////////////////////////////////////////////////
UObject * UStoryAssetFactory::FactoryCreateFile(UClass * InClass, UObject * InParent, FName InName, EObjectFlags Flags, const FString & Filename, const TCHAR * Parms, FFeedbackContext * Warn, bool & bOutOperationCanceled)
{
	FString FileExtension = FPaths::GetExtension(Filename);

	GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPreImport(this, InClass, InParent, InName, *FileExtension);

	UStoryAsset* NewStory = nullptr;
	FString FileContents;


	if (Filename.EndsWith("ink.json"))
	{
		// This is precompiled, just save the contents
		if (FFileHelper::LoadFileToString(FileContents, *Filename))
		{
			FString Assetname = InName.ToString();
			NewStory = NewObject<UStoryAsset>(InParent, InClass, FName(*(Assetname.LeftChop(4))), Flags);
			NewStory->CompiledStory = FileContents;
			NewStory->AssetImportData->Update(Filename);
		}

		bOutOperationCanceled = false;

		GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, NewStory);
		return NewStory;
	}
	else if (Filename.EndsWith("ink"))
	{

		if (FFileHelper::LoadFileToString(FileContents, *Filename))
		{
			// Make new inkcompiler object
			UInkCompiler* compiler = UInkCompiler::NewInkCompiler(FileContents, Filename);
			FString compiledStory = compiler->CompileToJson();


			// Run the function
			NewStory = NewObject<UStoryAsset>(InParent, InClass, InName, Flags);
			NewStory->CompiledStory = compiledStory;
			NewStory->AssetImportData->Update(Filename);

			bOutOperationCanceled = false;

			// profit
			GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, NewStory);
			return NewStory;
		}
	}

	GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, nullptr);
	return nullptr;
}

bool UStoryAssetFactory::CanReimport(UObject* Obj, TArray<FString>& OutFilenames)
{
	UStoryAsset* StoryAsset = Cast<UStoryAsset>(Obj);
	if (StoryAsset && StoryAsset->AssetImportData)
	{
		StoryAsset->AssetImportData->ExtractFilenames(OutFilenames);
		return true;
	}
	return false;
}

void UStoryAssetFactory::SetReimportPaths(UObject* Obj, const TArray<FString>& NewReimportPaths)
{
	UStoryAsset* StoryAsset = Cast<UStoryAsset>(Obj);
	if (StoryAsset && ensure(NewReimportPaths.Num() == 1))
	{
		StoryAsset->AssetImportData->UpdateFilenameOnly(NewReimportPaths[0]);
	}
}

EReimportResult::Type UStoryAssetFactory::Reimport(UObject* Obj)
{
	UStoryAsset* StoryAsset = Cast<UStoryAsset>(Obj);
	if (StoryAsset != nullptr)
	{
		if (StoryAsset->AssetImportData)
		{
			bool bOperationCancelled = false;
			if (ImportObject(StoryAsset->GetClass(), StoryAsset->GetOuter(), *StoryAsset->GetName(), RF_Public | RF_Standalone, StoryAsset->AssetImportData->GetFirstFilename(), nullptr, bOperationCancelled) != nullptr)
			{
				return EReimportResult::Succeeded;
			}
		}
	}

	return EReimportResult::Failed;
}

int32 UStoryAssetFactory::GetPriority() const
{
	return ImportPriority;
}

The issue is, if you drop the source file into the Content folder of your game. It will auto-reimport correctly. But if you import a file not inside the content folder, it won’t reimport. And the context menu options in the Content Browser are missing for reimporting:

How can I get it to have the reimport options that you’d find on a texture for example?

1 Like