263 lines
8.0 KiB
C++
263 lines
8.0 KiB
C++
// Copyright Steve Streeting 2022
|
|
// Released under the MIT license https://opensource.org/license/MIT/
|
|
#include "SUDSScriptFactory.h"
|
|
|
|
#include "PackageTools.h"
|
|
#include "SUDSEditorSettings.h"
|
|
#include "SUDSEditorVoiceOverTools.h"
|
|
#include "SUDSMessageLogger.h"
|
|
#include "SUDSScript.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "EditorFramework/AssetImportData.h"
|
|
#include "Internationalization/StringTable.h"
|
|
#include "Editor.h"
|
|
#include "ObjectTools.h"
|
|
#include "Internationalization/StringTableCore.h"
|
|
|
|
|
|
USUDSScriptFactory::USUDSScriptFactory()
|
|
{
|
|
SupportedClass = USUDSScript::StaticClass();
|
|
bCreateNew = false;
|
|
bEditorImport = true;
|
|
bText = true;
|
|
Formats.Add(TEXT("sud;SUDS Script File"));
|
|
}
|
|
|
|
UObject* USUDSScriptFactory::FactoryCreateText(UClass* InClass,
|
|
UObject* InParent,
|
|
FName InName,
|
|
EObjectFlags Flags,
|
|
UObject* Context,
|
|
const TCHAR* Type,
|
|
const TCHAR*& Buffer,
|
|
const TCHAR* BufferEnd,
|
|
FFeedbackContext* Warn)
|
|
{
|
|
Flags |= RF_Transactional;
|
|
FSUDSMessageLogger::ClearMessages();
|
|
FSUDSMessageLogger Logger;
|
|
|
|
USUDSScript* Result = nullptr;
|
|
|
|
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPreImport(this, InClass, InParent, InName, Type);
|
|
|
|
const FString FactoryCurrentFilename = UFactory::GetCurrentFilename();
|
|
FString CurrentSourcePath;
|
|
FString FilenameNoExtension;
|
|
FString UnusedExtension;
|
|
FPaths::Split(FactoryCurrentFilename, CurrentSourcePath, FilenameNoExtension, UnusedExtension);
|
|
const FString LongPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName());
|
|
|
|
const FString NameForErrors(InName.ToString());
|
|
|
|
// Now parse this using utility
|
|
if(Importer.ImportFromBuffer(Buffer, BufferEnd - Buffer, NameForErrors, &Logger, false))
|
|
{
|
|
|
|
// Populate with data
|
|
Result = NewObject<USUDSScript>(InParent, InName, Flags);
|
|
UStringTable* StringTable = CreateStringTable(InParent, InName, Result, Flags, &Logger);
|
|
Importer.PopulateAsset(Result, StringTable);
|
|
|
|
// Register source info
|
|
const FMD5Hash Hash = FSUDSScriptImporter::CalculateHash(Buffer, BufferEnd - Buffer);
|
|
Result->AssetImportData->Update(FactoryCurrentFilename, Hash);
|
|
|
|
// VO assets at import time?
|
|
if (ShouldGenerateVoiceAssets(LongPackagePath))
|
|
{
|
|
FSUDSEditorVoiceOverTools::GenerateAssets(Result, Flags, &Logger);
|
|
}
|
|
|
|
}
|
|
|
|
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
bool USUDSScriptFactory::ShouldGenerateVoiceAssets(const FString& PackagePath) const
|
|
{
|
|
if (auto Settings = GetDefault<USUDSEditorSettings>())
|
|
{
|
|
return Settings->ShouldGenerateVoiceAssets(PackagePath);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UStringTable* USUDSScriptFactory::CreateStringTable(UObject* ScriptParent, FName InName, USUDSScript* Script, EObjectFlags Flags, FSUDSMessageLogger* Logger)
|
|
{
|
|
auto Settings = GetDefault<USUDSEditorSettings>();
|
|
const bool bCreateSeparatePackage = Settings && Settings->bCreateStringTablesAsSeparatePackages;
|
|
|
|
const FName StringTableName = FName(InName.ToString() + "Strings");
|
|
UStringTable* Table = nullptr;
|
|
auto Registry = &FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName).Get();
|
|
const FString PackageDir = FPackageName::GetLongPackagePath(Script->GetOuter()->GetOutermost()->GetPathName());
|
|
const FString PackageName = UPackageTools::SanitizePackageName(PackageDir / StringTableName.ToString());
|
|
|
|
if (bCreateSeparatePackage)
|
|
{
|
|
|
|
// Create string table as its own package (.uasset)
|
|
|
|
{
|
|
// Destroy any string table packed together with the script
|
|
const FString ScriptPackageName = Script->GetPackage()->GetPathName();
|
|
TArray<FAssetData> ScriptAssets;
|
|
Registry->GetAssetsByPackageName(*ScriptPackageName, ScriptAssets);
|
|
TArray<UObject*> StringTableAssets;
|
|
for (auto& Asset : ScriptAssets)
|
|
{
|
|
if (Asset.GetClass() == UStringTable::StaticClass())
|
|
{
|
|
StringTableAssets.Add(Asset.GetAsset());
|
|
}
|
|
}
|
|
if (StringTableAssets.Num() > 0)
|
|
{
|
|
ObjectTools::ForceDeleteObjects(StringTableAssets, false );
|
|
}
|
|
}
|
|
|
|
|
|
if (FPackageName::DoesPackageExist(*PackageName))
|
|
{
|
|
// Package already exists, so try and import over the top of it, if it doesn't already have a source file path
|
|
TArray<FAssetData> Assets;
|
|
if (Registry->GetAssetsByPackageName(*PackageName, Assets))
|
|
{
|
|
if (Assets.Num() > 0)
|
|
{
|
|
if (Assets[0].GetAsset()->IsA(UStringTable::StaticClass()))
|
|
{
|
|
Table = Cast<UStringTable>(Assets[0].GetAsset());
|
|
Table->GetMutableStringTable()->ClearSourceStrings();
|
|
Table->MarkPackageDirty();
|
|
}
|
|
else
|
|
{
|
|
Logger->Logf(ELogVerbosity::Error,
|
|
TEXT(
|
|
"Asset %s already exists but is not a String Table! Cannot replace, please move aside and re-import script."),
|
|
*PackageName);
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Table)
|
|
{
|
|
// String Table didn't exist (although package might have)
|
|
// It's safe to call CreatePackage either way, it'll return the existing one if needed
|
|
UPackage* Package = CreatePackage(*PackageName);
|
|
if (!ensure(Package))
|
|
{
|
|
Logger->Logf(ELogVerbosity::Error,
|
|
TEXT("Failed to create/retrieve package for string table %s"),
|
|
*PackageName);
|
|
}
|
|
else
|
|
{
|
|
//Logger->Logf(ELogVerbosity::Display, TEXT("Creating string table %s"), *PackageName);
|
|
|
|
// This constructor registers the string table with FStringTableRegistry
|
|
Table = NewObject<UStringTable>(Package, StringTableName, Flags);
|
|
Package->FullyLoad();
|
|
Table->MarkPackageDirty();
|
|
FAssetRegistryModule::AssetCreated(Table);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Destroy separate strings package if it exists (could flip this option off after importing)
|
|
if (FPackageName::DoesPackageExist(*PackageName))
|
|
{
|
|
TArray<FAssetData> Assets;
|
|
if (Registry->GetAssetsByPackageName(*PackageName, Assets))
|
|
{
|
|
if (Assets.Num() > 0)
|
|
{
|
|
// Note: we need to force deletion because old string table will be referenced
|
|
// Unfortunately there is no ObjectTools::ForceDeleteAssets, only the inner
|
|
// ForceDeleteObjects, so we need to do it ourselves
|
|
ForceDeleteAssets(Assets);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default route, create string table inside script package
|
|
Table = NewObject<UStringTable>(ScriptParent, StringTableName, Flags);
|
|
|
|
}
|
|
|
|
return Table;
|
|
|
|
}
|
|
|
|
void USUDSScriptFactory::ForceDeleteAssets(const TArray<FAssetData>& AssetsToDelete)
|
|
{
|
|
// Copied from ObjectTools::DeleteAssets, except that we're using ForceDeleteObjects so we
|
|
// don't have to display any confusing messages about moving the string table into the shared asset
|
|
|
|
TArray<TWeakObjectPtr<UPackage>> PackageFilesToDelete;
|
|
TArray<UObject*> ObjectsToDelete;
|
|
for ( int i = 0; i < AssetsToDelete.Num(); i++ )
|
|
{
|
|
const FAssetData& AssetData = AssetsToDelete[i];
|
|
UObject *ObjectToDelete = AssetData.GetAsset({ ULevel::LoadAllExternalObjectsTag });
|
|
// Assets can be loaded even when their underlying type/class no longer exists...
|
|
if ( ObjectToDelete!=nullptr )
|
|
{
|
|
ObjectsToDelete.Add( ObjectToDelete );
|
|
}
|
|
else if ( AssetData.IsUAsset() )
|
|
{
|
|
// ... In this cases there is no underlying asset or type so remove the package itself directly after confirming it's valid to do so.
|
|
FString PackageFilename;
|
|
if( !FPackageName::DoesPackageExist( AssetData.PackageName.ToString(), &PackageFilename ) )
|
|
{
|
|
// Could not determine filename for package so we can not delete
|
|
continue;
|
|
}
|
|
|
|
UPackage* Package = FindPackage(nullptr, *AssetData.PackageName.ToString());
|
|
if ( Package )
|
|
{
|
|
PackageFilesToDelete.Add(Package);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 NumObjectsToDelete = ObjectsToDelete.Num();
|
|
if ( NumObjectsToDelete > 0 )
|
|
{
|
|
ObjectTools::ForceDeleteObjects( ObjectsToDelete, false );
|
|
}
|
|
|
|
const int32 NumPackagesToDelete = PackageFilesToDelete.Num();
|
|
if (NumPackagesToDelete > 0)
|
|
{
|
|
TArray<UPackage*> PackagePointers;
|
|
for ( const auto& PkgIt : PackageFilesToDelete )
|
|
{
|
|
UPackage* Package = PkgIt.Get();
|
|
if ( Package )
|
|
{
|
|
PackagePointers.Add(Package);
|
|
}
|
|
}
|
|
|
|
if ( PackagePointers.Num() > 0 )
|
|
{
|
|
const bool bPerformReferenceCheck = true;
|
|
ObjectTools::CleanupAfterSuccessfulDelete(PackagePointers, bPerformReferenceCheck);
|
|
}
|
|
}
|
|
}
|
|
|