Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Commandlets/ImportLocalizedDialogueCommandlet.cpp
2025-05-18 13:04:45 +08:00

534 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Commandlets/ImportLocalizedDialogueCommandlet.h"
#include "Modules/ModuleManager.h"
#include "Misc/PackageName.h"
#include "AssetRegistry/AssetData.h"
#include "Sound/SoundWave.h"
#include "Misc/Paths.h"
#include "Misc/App.h"
#include "UObject/MetaData.h"
#include "EditorFramework/AssetImportData.h"
#include "Sound/DialogueWave.h"
#include "Utils.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Interfaces/ITargetPlatform.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "AudioEditorModule.h"
#include "AudioCompressionSettingsUtils.h"
DEFINE_LOG_CATEGORY_STATIC(LogImportLocalizedDialogueCommandlet, Log, All);
namespace
{
const FString GeneratedByCommandletMetaDataKey = TEXT("GeneratedByCommandlet");
const FString GeneratedByCommandletMetaDataValue = TEXT("ImportLocalizedDialogueCommandlet");
}
UImportLocalizedDialogueCommandlet::UImportLocalizedDialogueCommandlet(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
int32 UImportLocalizedDialogueCommandlet::Main(const FString& Params)
{
// Parse command line
TArray<FString> Tokens;
TArray<FString> Switches;
TMap<FString, FString> ParamVals;
UCommandlet::ParseCommandLine(*Params, Tokens, Switches, ParamVals);
// Set config path
FString ConfigPath;
{
const FString* ConfigPathParamVal = ParamVals.Find(FString(TEXT("Config")));
if (!ConfigPathParamVal)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No config specified."));
return -1;
}
ConfigPath = *ConfigPathParamVal;
}
// Set config section
FString SectionName;
{
const FString* SectionNameParamVal = ParamVals.Find(FString(TEXT("Section")));
if (!SectionNameParamVal)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No config section specified."));
return -1;
}
SectionName = *SectionNameParamVal;
}
// Source path to the root folder that manifest/archive files live in
FString SourcePath;
if (!GetPathFromConfig(*SectionName, TEXT("SourcePath"), SourcePath, ConfigPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No source path specified."));
return -1;
}
// Get the native culture
FString NativeCulture;
if (!GetStringFromConfig(*SectionName, TEXT("NativeCulture"), NativeCulture, ConfigPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No native culture specified."));
return -1;
}
// Get cultures to generate
TArray<FString> CulturesToGenerate;
if (GetStringArrayFromConfig(*SectionName, TEXT("CulturesToGenerate"), CulturesToGenerate, ConfigPath) == 0)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No cultures specified for import."));
return -1;
}
// Get the manifest name
FString ManifestName;
if (!GetStringFromConfig(*SectionName, TEXT("ManifestName"), ManifestName, ConfigPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No manifest name specified."));
return -1;
}
// Get the archive name
FString ArchiveName;
if (!GetStringFromConfig(*SectionName, TEXT("ArchiveName"), ArchiveName, ConfigPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No archive name specified."));
return -1;
}
// Should we import the native audio as source audio?
bool bImportNativeAsSource = false;
if (!GetBoolFromConfig(*SectionName, TEXT("bImportNativeAsSource"), bImportNativeAsSource, ConfigPath))
{
bImportNativeAsSource = false;
}
// Source path to the raw audio files that we're going to import
FString RawAudioPath;
if (!GetPathFromConfig(*SectionName, TEXT("RawAudioPath"), RawAudioPath, ConfigPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No raw audio path specified."));
return -1;
}
if (!FPaths::DirectoryExists(RawAudioPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Invalid raw audio path specified: %s."), *RawAudioPath);
return -1;
}
// Folder in which to place automatically imported sound wave assets
FString ImportedDialogueFolder;
if (!GetStringFromConfig(*SectionName, TEXT("ImportedDialogueFolder"), ImportedDialogueFolder, ConfigPath))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("No imported dialogue folder specified."));
return -1;
}
if (ImportedDialogueFolder.IsEmpty())
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Imported dialogue folder cannot be empty."));
return -1;
}
// Load the manifest and all archives
FLocTextHelper LocTextHelper(SourcePath, ManifestName, ArchiveName, NativeCulture, CulturesToGenerate, GatherManifestHelper->GetLocFileNotifies(), GatherManifestHelper->GetPlatformSplitMode());
LocTextHelper.SetCopyrightNotice(GatherManifestHelper->GetCopyrightNotice());
{
FText LoadError;
if (!LocTextHelper.LoadAll(ELocTextHelperLoadFlags::LoadOrCreate, &LoadError))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("%s"), *LoadError.ToString());
return false;
}
}
const FString RootAssetPath = FApp::HasProjectName() ? TEXT("/Game") : TEXT("/Engine");
const FString RootContentDir = FApp::HasProjectName() ? FPaths::ProjectContentDir() : FPaths::EngineContentDir();
// Prepare the asset registry
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
AssetRegistry.SearchAllAssets(true);
// We want all the non-localized project specific dialogue waves
TArray<FAssetData> AssetDataArrayForDialogueWaves;
if (!FLocalizedAssetUtil::GetAssetsByPathAndClass(AssetRegistry, *RootAssetPath, UDialogueWave::StaticClass()->GetClassPathName(), /*bIncludeLocalizedAssets*/false, AssetDataArrayForDialogueWaves))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Unable to get dialogue wave asset data from asset registry."));
return -1;
}
// Build up the culture specific import info
TMap<FString, FCultureImportInfo> CultureImportInfoMap;
for (const FString& CultureName : CulturesToGenerate)
{
FCultureImportInfo& CultureImportInfo = CultureImportInfoMap.Add(CultureName);
CultureImportInfo.Name = CultureName;
CultureImportInfo.AudioPath = RawAudioPath / CultureName;
CultureImportInfo.ArchiveFileName = SourcePath / CultureName / ArchiveName;
CultureImportInfo.LocalizedRootContentPath = RootContentDir / TEXT("L10N") / CultureName;
CultureImportInfo.LocalizedRootPackagePath = RootAssetPath / TEXT("L10N") / CultureName;
CultureImportInfo.LocalizedImportedDialoguePackagePath = CultureImportInfo.LocalizedRootPackagePath / ImportedDialogueFolder;
CultureImportInfo.bIsNativeCulture = CultureName == NativeCulture;
}
// Find all of the existing localized dialogue and sound waves - we'll keep track of which ones we process so we can delete any that are no longer needed
TArray<FAssetData> LocalizedAssetsToPotentiallyDelete;
{
TArray<FName> LocalizedDialogueWavePathsToSearch;
TArray<FName> LocalizedSoundWavePathsToSearch;
// We always add the source imported dialogue folder to ensure we clean it up correctly if we change the "import native as source" option
// This is also why we always add the native culture, even though only one will be in use at any one time
LocalizedSoundWavePathsToSearch.Add(*(RootAssetPath / ImportedDialogueFolder));
for (const auto& CultureImportInfoPair : CultureImportInfoMap)
{
const FCultureImportInfo& CultureImportInfo = CultureImportInfoPair.Value;
LocalizedDialogueWavePathsToSearch.Add(*CultureImportInfo.LocalizedRootPackagePath);
LocalizedSoundWavePathsToSearch.Add(*CultureImportInfo.LocalizedImportedDialoguePackagePath);
}
FLocalizedAssetUtil::GetAssetsByPathAndClass(AssetRegistry, LocalizedDialogueWavePathsToSearch, UDialogueWave::StaticClass()->GetClassPathName(), /*bIncludeLocalizedAssets*/true, LocalizedAssetsToPotentiallyDelete);
FLocalizedAssetUtil::GetAssetsByPathAndClass(AssetRegistry, LocalizedSoundWavePathsToSearch, USoundWave::StaticClass()->GetClassPathName(), /*bIncludeLocalizedAssets*/true, LocalizedAssetsToPotentiallyDelete);
}
// We're going to walk every context from every dialogue wave asset looking to see whether there's new audio to import for each culture we generate for
// We filter these dialogue waves against the current manifest so that we only attempt to update assets that we gather text from
for (const FAssetData& AssetData : AssetDataArrayForDialogueWaves)
{
// Verify that the found asset is a dialogue wave
if (AssetData.GetClass() != UDialogueWave::StaticClass())
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Asset registry found asset '%s', but the asset with this name is not actually a dialogue wave."), *AssetData.AssetName.ToString());
continue;
}
// Get the dialogue wave
UDialogueWave* const DialogueWave = Cast<UDialogueWave>(AssetData.GetAsset());
// Verify that the dialogue wave was loaded
if (!DialogueWave)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Asset registry found asset '%s', but the dialogue wave could not be accessed."), *AssetData.AssetName.ToString());
continue;
}
FString _Unused_DialogueWaveRoot, DialogueWaveSubPath, _Unused_DialogueWaveAssetName;
if (!FPackageName::SplitLongPackageName(AssetData.PackageName.ToString(), _Unused_DialogueWaveRoot, DialogueWaveSubPath, _Unused_DialogueWaveAssetName))
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Failed to split dialogue wave package name '%s'."), *AssetData.PackageName.ToString());
continue;
}
// If we're importing native as source, then we import using a special culture import info
if (bImportNativeAsSource && CultureImportInfoMap.Contains(NativeCulture))
{
FCultureImportInfo SourceCultureImportInfo = CultureImportInfoMap.FindRef(NativeCulture);
SourceCultureImportInfo.LocalizedRootContentPath = RootContentDir;
SourceCultureImportInfo.LocalizedRootPackagePath = RootAssetPath;
SourceCultureImportInfo.LocalizedImportedDialoguePackagePath = SourceCultureImportInfo.LocalizedRootPackagePath / ImportedDialogueFolder;
ImportDialogueForCulture(LocTextHelper, DialogueWave, DialogueWaveSubPath, SourceCultureImportInfo, /*bImportAsSource*/true);
}
// Iterate over each context looking for new audio to import
for (const auto& CultureImportInfoPair : CultureImportInfoMap)
{
const FCultureImportInfo& CultureImportInfo = CultureImportInfoPair.Value;
// Skip the native culture if importing native as source, as we'll have imported it above
if (bImportNativeAsSource && CultureImportInfo.bIsNativeCulture)
{
continue;
}
ImportDialogueForCulture(LocTextHelper, DialogueWave, DialogueWaveSubPath, CultureImportInfo, /*bImportAsSource*/false);
}
}
// Remove any left over assets that we no longer need
for (const FAssetData& LocalizedAssetData : LocalizedAssetsToPotentiallyDelete)
{
// Has this asset already keen marked as "keep"?
if (AssetsToKeep.Contains(LocalizedAssetData.GetSoftObjectPath()))
{
continue;
}
// Check the package meta-data to make sure that we only delete packages that we own
UObject* LocalizedAsset = LocalizedAssetData.GetAsset();
const FString AssetGeneratedByCommandletMetaDataValue = LocalizedAsset->GetPackage()->GetMetaData().GetValue(LocalizedAsset, *GeneratedByCommandletMetaDataKey);
if (AssetGeneratedByCommandletMetaDataValue != GeneratedByCommandletMetaDataValue)
{
continue;
}
FLocalizedAssetSCCUtil::DeleteAssetWithSCC(SourceControlInfo, LocalizedAsset);
}
return 0;
}
bool UImportLocalizedDialogueCommandlet::ImportDialogueForCulture(FLocTextHelper& InLocTextHelper, UDialogueWave* const DialogueWave, const FString& DialogueWaveSubPath, const FCultureImportInfo& InCultureImportInfo, const bool bImportAsSource)
{
UDialogueWave* LocalizedDialogueWave = nullptr;
FString LocalizedDialogueWaveFileName;
if (bImportAsSource)
{
LocalizedDialogueWave = DialogueWave;
LocalizedDialogueWaveFileName = FPackageName::LongPackageNameToFilename(DialogueWave->GetOutermost()->GetPathName(), FPackageName::GetAssetPackageExtension());
}
else
{
LocalizedDialogueWaveFileName = (InCultureImportInfo.LocalizedRootContentPath / DialogueWaveSubPath / DialogueWave->GetName()) + FPackageName::GetAssetPackageExtension();
// Clone the source dialogue wave into the localized folder, replacing any existing asset to ensure that we're up-to-date with the source data
{
if (!FLocalizedAssetSCCUtil::SaveAssetWithSCC(SourceControlInfo, DialogueWave, LocalizedDialogueWaveFileName))
{
return false;
}
// Load up the newly saved asset
const FString LocalizedDialogueWavePackagePath = (InCultureImportInfo.LocalizedRootPackagePath / DialogueWaveSubPath / TEXT("")) + DialogueWave->GetName();
const FString LocalizedDialogueWaveAssetPath = FString::Printf(TEXT("%s.%s"), *LocalizedDialogueWavePackagePath, *DialogueWave->GetName());
LocalizedDialogueWave = LoadObject<UDialogueWave>(nullptr, *LocalizedDialogueWaveAssetPath);
}
if (!LocalizedDialogueWave)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Failed to create a localized dialogue wave '%s' for culture '%s'. No dialogue will be imported for this culture."), *DialogueWave->GetName(), *InCultureImportInfo.Name);
return false;
}
// Mark this localized dialogue wave as used so it doesn't get deleted later
AssetsToKeep.Add(FSoftObjectPath(LocalizedDialogueWave));
}
// First pass, handle any contexts that have an exact mapping to their audio file
TArray<FDialogueContextMapping*> ContextMappingsWithMissingAudio;
for (FDialogueContextMapping& ContextMapping : LocalizedDialogueWave->ContextMappings)
{
const FString ContextLocalizationKey = LocalizedDialogueWave->GetContextLocalizationKey(ContextMapping);
// Check that this entry exists in the manifest file, as we want to skip over dialogue that we aren't gathering
TSharedPtr<FManifestEntry> ContextManifestEntry = InLocTextHelper.FindSourceText(FDialogueConstants::DialogueNamespace, ContextLocalizationKey, &LocalizedDialogueWave->SpokenText);
if (!ContextManifestEntry.IsValid())
{
// We're skipping this context entry due to our manifest, but we don't want the sound wave it's using to be deleted
if (ContextMapping.SoundWave)
{
AssetsToKeep.Add(FSoftObjectPath(ContextMapping.SoundWave));
}
UE_LOG(LogImportLocalizedDialogueCommandlet, Log, TEXT("No internationalization manifest entry was found for context '%s' in culture '%s'. This context will be skipped."), *ContextLocalizationKey, *InCultureImportInfo.Name);
continue;
}
const FString ContextAudioFilename = InCultureImportInfo.AudioPath / LocalizedDialogueWave->GetContextRecordedAudioFilename(ContextMapping);
if (!FPaths::FileExists(ContextAudioFilename))
{
// No specific audio file exists for this context, however that means we may use a different audio file if we have another context with the same speaker (to share sound waves where possible)
// Flag this context as needing a second pass
ContextMappingsWithMissingAudio.Add(&ContextMapping);
continue;
}
// Import the WAV file as a sound wave asset, potentially overwriting any existing asset
// The WAV file will only be imported if it has been changed since the last time it was imported
USoundWave* const SoundWave = ConditionalImportSoundWave(InCultureImportInfo.LocalizedImportedDialoguePackagePath / ContextLocalizationKey, ContextLocalizationKey, ContextAudioFilename);
if (SoundWave)
{
// Set this context to use the newly imported sound wave
ContextMapping.SoundWave = SoundWave;
}
// This sound wave is in use, so shouldn't be deleted
if (ContextMapping.SoundWave)
{
AssetsToKeep.Add(FSoftObjectPath(ContextMapping.SoundWave));
}
}
// Second pass, handle any contexts that should share sound data with another context
for (FDialogueContextMapping* ContextMappingPtr : ContextMappingsWithMissingAudio)
{
auto GetTranslatedTextForContext = [&](const FDialogueContextMapping& InContextMapping) -> FString
{
const FString ContextLocalizationKey = LocalizedDialogueWave->GetContextLocalizationKey(*ContextMappingPtr);
// Find the manifest entry for our context
TSharedPtr<FManifestEntry> ContextManifestEntry = InLocTextHelper.FindSourceText(FDialogueConstants::DialogueNamespace, ContextLocalizationKey, &LocalizedDialogueWave->SpokenText);
if (!ContextManifestEntry.IsValid())
{
return FString();
}
// Find the correct entry for our context
const FManifestContext* ContextManifestEntryContext = ContextManifestEntry->FindContextByKey(ContextLocalizationKey);
check(ContextManifestEntryContext); // This should never fail as we pass in the key to FindSourceText
// Get the localized text to export
FLocItem ExportedSource;
FLocItem ExportedTranslation;
InLocTextHelper.GetExportText(InCultureImportInfo.Name, FDialogueConstants::DialogueNamespace, ContextManifestEntryContext->Key, ContextManifestEntryContext->KeyMetadataObj, ELocTextExportSourceMethod::NativeText, ContextManifestEntry->Source, ExportedSource, ExportedTranslation);
return ExportedTranslation.Text;
};
// Find the correct localized dialogue for this context
const FString ContextLocalizedDialogue = GetTranslatedTextForContext(*ContextMappingPtr);
if (ContextLocalizedDialogue.IsEmpty())
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Warning, TEXT("No dialogue was imported for context '%s' in culture '%s' as it has an empty translation."), *LocalizedDialogueWave->GetContextLocalizationKey(*ContextMappingPtr), *InCultureImportInfo.Name);
}
else
{
// Try and find another context using the same speaking voice and localized dialogue that does have audio to import - we'll share its sound wave
const FDialogueContextMapping* FoundContextMapping = LocalizedDialogueWave->ContextMappings.FindByPredicate([&](const FDialogueContextMapping& PotentialContextMapping) -> bool
{
// Can't match ourself
if (ContextMappingPtr == &PotentialContextMapping)
{
return false;
}
// Need to be using the same speaking voice
if (ContextMappingPtr->Context.Speaker != PotentialContextMapping.Context.Speaker)
{
return false;
}
// Need to be saying the same dialogue
const FString PotentialContextLocalizedDialogue = GetTranslatedTextForContext(PotentialContextMapping);
if (!ContextLocalizedDialogue.Equals(PotentialContextLocalizedDialogue, ESearchCase::CaseSensitive))
{
return false;
}
// Needs to actually have a valid audio file to import
const FString PotentialContextAudioFilename = InCultureImportInfo.AudioPath / LocalizedDialogueWave->GetContextRecordedAudioFilename(PotentialContextMapping);
return FPaths::FileExists(PotentialContextAudioFilename);
});
if (FoundContextMapping)
{
// Set this context to use the same sound wave as the found context
ContextMappingPtr->SoundWave = FoundContextMapping->SoundWave;
}
else
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Warning, TEXT("No dialogue was imported for context '%s' in culture '%s' as no suitable audio file could be found to import."), *LocalizedDialogueWave->GetContextLocalizationKey(*ContextMappingPtr), *InCultureImportInfo.Name);
}
}
// This sound wave is in use, so shouldn't be deleted
if (ContextMappingPtr->SoundWave)
{
AssetsToKeep.Add(FSoftObjectPath(ContextMappingPtr->SoundWave));
}
}
LocalizedDialogueWave->MarkPackageDirty();
// Add meta-data stating that this asset is owned by this commandlet
LocalizedDialogueWave->GetPackage()->GetMetaData().SetValue(LocalizedDialogueWave, *GeneratedByCommandletMetaDataKey, *GeneratedByCommandletMetaDataValue);
return FLocalizedAssetSCCUtil::SaveAssetWithSCC(SourceControlInfo, LocalizedDialogueWave, LocalizedDialogueWaveFileName);
}
USoundWave* UImportLocalizedDialogueCommandlet::ConditionalImportSoundWave(const FString& InSoundWavePackageName, const FString& InSoundWaveAssetName, const FString& InWavFilename) const
{
FString PackageFileName;
if (!FPackageName::TryConvertLongPackageNameToFilename(InSoundWavePackageName, PackageFileName, FPackageName::GetAssetPackageExtension()) || !FPaths::FileExists(PackageFileName))
{
// No existing asset, we need to perform the import
return ImportSoundWave(InSoundWavePackageName, InSoundWaveAssetName, InWavFilename);
}
USoundWave* const ExistingSoundWave = LoadObject<USoundWave>(nullptr, *FString::Printf(TEXT("%s.%s"), *InSoundWavePackageName, *InSoundWaveAssetName));
if (!ExistingSoundWave)
{
// No existing asset, we need to perform the import
return ImportSoundWave(InSoundWavePackageName, InSoundWaveAssetName, InWavFilename);
}
// Find the import data that matches the file we're going to import
FMD5Hash OldFileHash;
{
const FString WavLeafname = FPaths::GetCleanFilename(InWavFilename);
const FAssetImportInfo::FSourceFile* const FoundSourceFile = ExistingSoundWave->AssetImportData->SourceData.SourceFiles.FindByPredicate([&](const FAssetImportInfo::FSourceFile& InSourceFile) -> bool
{
const FString SourceFileLeafname = FPaths::GetCleanFilename(InSourceFile.RelativeFilename);
return SourceFileLeafname == WavLeafname;
});
if (FoundSourceFile)
{
OldFileHash = FoundSourceFile->FileHash;
}
}
// We only need to import the sound wave if the file hash has changed, or the source hash is invalid
const FMD5Hash CurrentFileHash = FMD5Hash::HashFile(*InWavFilename);
if (!OldFileHash.IsValid() || CurrentFileHash != OldFileHash)
{
return ImportSoundWave(InSoundWavePackageName, InSoundWaveAssetName, InWavFilename);
}
return ExistingSoundWave;
}
USoundWave* UImportLocalizedDialogueCommandlet::ImportSoundWave(const FString& InSoundWavePackageName, const FString& InSoundWaveAssetName, const FString& InWavFilename) const
{
// Find or create the package to host the sound wave
UPackage* const SoundWavePackage = CreatePackage( *InSoundWavePackageName);
if (!SoundWavePackage)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Failed to create a sound wave package '%s'."), *InSoundWavePackageName);
return nullptr;
}
// Make sure the destination package is loaded
SoundWavePackage->FullyLoad();
IAudioEditorModule* AudioEditorModule = &FModuleManager::LoadModuleChecked<IAudioEditorModule>("AudioEditor");
USoundWave* const SoundWave = AudioEditorModule->ImportSoundWave(SoundWavePackage, *InSoundWaveAssetName, *InWavFilename);
if (!SoundWave)
{
UE_LOG(LogImportLocalizedDialogueCommandlet, Error, TEXT("Failed to import the sound wave asset '%s.%s' from '%s'"), *InSoundWavePackageName, *InSoundWaveAssetName, *InWavFilename);
return nullptr;
}
// Compress to whatever formats the active target platforms want prior to saving the asset
{
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
if (TPM)
{
const TArray<ITargetPlatform*>& Platforms = TPM->GetActiveTargetPlatforms();
for (ITargetPlatform* Platform : Platforms)
{
SoundWave->GetCompressedData(Platform->GetWaveFormat(SoundWave), FPlatformCompressionUtilities::GetCookOverrides(*Platform->IniPlatformName()));
}
}
}
// Add meta-data stating that this asset is owned by this commandlet
SoundWavePackage->GetMetaData().SetValue(SoundWave, *GeneratedByCommandletMetaDataKey, *GeneratedByCommandletMetaDataValue);
// Write out the updated sound wave asset
if (!FLocalizedAssetSCCUtil::SavePackageWithSCC(SourceControlInfo, SoundWavePackage))
{
return nullptr;
}
return SoundWave;
}