AkAssetMigrationHelper.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #include "AkAssetMigrationHelper.h"
  16. #include "AkAssetDatabase.h"
  17. #include "AkAudioEvent.h"
  18. #include "AkAuxBus.h"
  19. #include "AkDeprecated.h"
  20. #include "AkAudioBank.h"
  21. #include "AkWaapiClient.h"
  22. #include "AkWaapiUtils.h"
  23. #include "AkMigrationWidgets.h"
  24. #include "AkCustomVersion.h"
  25. #include "AkUnrealEditorHelper.h"
  26. #include "AkUnrealHelper.h"
  27. #include "WwiseDefines.h"
  28. #include "IAudiokineticTools.h"
  29. #include "AssetRegistry/AssetRegistryModule.h"
  30. #include "AssetTools/Public/AssetToolsModule.h"
  31. #include "GenericPlatform/GenericPlatformFile.h"
  32. #if UE_5_0_OR_LATER
  33. #include "HAL/PlatformFileManager.h"
  34. #else
  35. #include "HAL/PlatformFilemanager.h"
  36. #endif
  37. #include "UnrealEd/Public/ObjectTools.h"
  38. #include "UnrealEd/Public/FileHelpers.h"
  39. #include "Misc/FileHelper.h"
  40. #include "IDesktopPlatform.h"
  41. #include "DesktopPlatformModule.h"
  42. #include "Interfaces/IMainFrameModule.h"
  43. #include "Mathematics/APConversion.h"
  44. #include "Misc/App.h"
  45. #define LOCTEXT_NAMESPACE "AkAudio"
  46. namespace AkAssetMigration
  47. {
  48. void PromptMigration(const FMigrationContext& MigrationOptions, FMigrationOperations& OutMigrationOperations)
  49. {
  50. if (!FApp::CanEverRender())
  51. {
  52. OutMigrationOperations.bCancelled =true;
  53. return;
  54. }
  55. TSharedPtr<SWindow> Dialog = SNew(SWindow)
  56. .Title(LOCTEXT("BankMigrationDialog", "Wwise Integration Migration"))
  57. .SupportsMaximize(false)
  58. .SupportsMinimize(false)
  59. .FocusWhenFirstShown(true)
  60. .HasCloseButton(false)
  61. .SizingRule(ESizingRule::Autosized);
  62. TSharedPtr<SMigrationWidget> MigrationWidget;
  63. Dialog->SetContent(
  64. SAssignNew(MigrationWidget, SMigrationWidget)
  65. .Dialog(Dialog)
  66. .ShowBankTransfer(MigrationOptions.bBanksInProject)
  67. .ShowDeprecatedAssetCleanup(MigrationOptions.bDeprecatedAssetsInProject)
  68. .ShowAssetMigration(MigrationOptions.bAssetsNeedMigration)
  69. .ShowProjectMigration(MigrationOptions.bProjectSettingsNotUpToDate)
  70. .NumDeprecatedAssets(MigrationOptions.NumDeprecatedAssetsInProject)
  71. );
  72. FSlateApplication::Get().AddModalWindow(Dialog.ToSharedRef(), nullptr);
  73. if (MigrationOptions.bBanksInProject)
  74. {
  75. OutMigrationOperations.BankTransferMethod = MigrationWidget->BankTransferWidget->BankTransferMethod;
  76. OutMigrationOperations.bDoBankCleanup = MigrationWidget->BankTransferWidget->DeleteSoundBanksCheckBox->IsChecked();
  77. OutMigrationOperations.bTransferAutoload = MigrationWidget->BankTransferWidget->TransferAutoLoadCheckBox->IsChecked();
  78. OutMigrationOperations.DefinitionFilePath = MigrationWidget->BankTransferWidget->SoundBankDefinitionFilePath;
  79. }
  80. if (MigrationOptions.bDeprecatedAssetsInProject)
  81. {
  82. OutMigrationOperations.bDoDeprecatedAssetCleanup = MigrationWidget->DeprecatedAssetCleanupWidget->DeleteAssetsCheckBox->IsChecked();
  83. }
  84. if (MigrationOptions.bAssetsNeedMigration)
  85. {
  86. OutMigrationOperations.bDoAssetMigration = MigrationWidget->AssetMigrationWidget->MigrateAssetsCheckBox->IsChecked();
  87. }
  88. if (MigrationOptions.bProjectSettingsNotUpToDate)
  89. {
  90. OutMigrationOperations.bDoProjectUpdate = MigrationWidget->ProjectMigrationWidget->AutoMigrateCheckbox->IsChecked();
  91. OutMigrationOperations.GeneratedSoundBankDirectory = MigrationWidget->ProjectMigrationWidget->GeneratedSoundBanksFolderPickerWidget->GetDirectory();
  92. }
  93. OutMigrationOperations.bCancelled = MigrationWidget->bCancel;
  94. }
  95. bool PromptFailedBankTransfer(const FString& ErrorMessage)
  96. {
  97. if (!FApp::CanEverRender())
  98. {
  99. return false;
  100. }
  101. TSharedPtr<SWindow> Dialog = SNew(SWindow)
  102. .Title(LOCTEXT("BankTransfer", "Bank Transfer Failure"))
  103. .SupportsMaximize(false)
  104. .SupportsMinimize(false)
  105. .FocusWhenFirstShown(true)
  106. .HasCloseButton(false)
  107. .SizingRule(ESizingRule::Autosized);
  108. TSharedPtr<SBankMigrationFailureWidget> FailedBankTransferWidget;
  109. Dialog->SetContent(
  110. SAssignNew(FailedBankTransferWidget, SBankMigrationFailureWidget)
  111. .Dialog(Dialog)
  112. .ErrorMessage(FText::Format( LOCTEXT("BankTransferErrorMessage", "{0}"), FText::FromString(ErrorMessage)))
  113. );
  114. FSlateApplication::Get().AddModalWindow(Dialog.ToSharedRef(), nullptr);
  115. return !FailedBankTransferWidget->bCancel;
  116. }
  117. FString FormatWaapiErrorMessage(const TArray<FBankTransferError>& ErrorMessages)
  118. {
  119. FString CombinedErrors;
  120. for (auto BankErrors : ErrorMessages)
  121. {
  122. if (BankErrors.bHasBankEntry)
  123. {
  124. CombinedErrors += "SoundBank: " + BankErrors.BankEntry.BankAssetData.AssetName.ToString() + "\n";
  125. }
  126. CombinedErrors += "Error: " + BankErrors.ErrorMessage + "\n";
  127. if (BankErrors.bHasBankEntry)
  128. {
  129. CombinedErrors += "Wwise Assets in SoundBank : \n";
  130. for (auto LinkedAsset : BankErrors.BankEntry.LinkedEvents)
  131. {
  132. CombinedErrors += FString::Format(TEXT("GUID: {0} - Name: {1}\n"), { LinkedAsset.WwiseGuid.ToString(), LinkedAsset.AssetName });
  133. }
  134. for (auto LinkedAsset : BankErrors.BankEntry.LinkedAuxBusses)
  135. {
  136. CombinedErrors += FString::Format(TEXT("GUID: {0} - Name: {1}\n"), { LinkedAsset.WwiseGuid.ToString(), LinkedAsset.AssetName });
  137. }
  138. }
  139. }
  140. return CombinedErrors;
  141. }
  142. void FindDeprecatedAssets(TArray<FAssetData>& OutDeprecatedAssets)
  143. {
  144. OutDeprecatedAssets.Empty();
  145. FARFilter Filter;
  146. #if UE_5_1_OR_LATER
  147. Filter.ClassPaths.Add(UAkMediaAsset::StaticClass()->GetClassPathName());
  148. Filter.ClassPaths.Add(UAkLocalizedMediaAsset::StaticClass()->GetClassPathName());
  149. Filter.ClassPaths.Add(UAkExternalMediaAsset::StaticClass()->GetClassPathName());
  150. Filter.ClassPaths.Add(UAkFolder::StaticClass()->GetClassPathName());
  151. Filter.ClassPaths.Add(UAkAssetPlatformData::StaticClass()->GetClassPathName());
  152. #else
  153. Filter.ClassNames.Add(UAkMediaAsset::StaticClass()->GetFName());
  154. Filter.ClassNames.Add(UAkLocalizedMediaAsset::StaticClass()->GetFName());
  155. Filter.ClassNames.Add(UAkExternalMediaAsset::StaticClass()->GetFName());
  156. Filter.ClassNames.Add(UAkFolder::StaticClass()->GetFName());
  157. Filter.ClassNames.Add(UAkAssetPlatformData::StaticClass()->GetFName());
  158. #endif
  159. auto& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
  160. AssetRegistryModule.Get().GetAssets(Filter, OutDeprecatedAssets);
  161. }
  162. bool DeleteAssets(const TArray<FAssetData>& InAssetsToDelete)
  163. {
  164. /*
  165. *Deleting an Asset that is referenced somewhere needs a confirmation which is impossible using a commandlet.
  166. *Calling ForceDelete on the UObjects of the Assets will ignore the confirmation and delete the assets.
  167. */
  168. if(!FApp::CanEverRender())
  169. {
  170. TArray<UObject*> Objects;
  171. for(auto Asset : InAssetsToDelete)
  172. {
  173. Objects.Add(Asset.GetAsset());
  174. }
  175. int DeletedObjects = ObjectTools::ForceDeleteObjects(Objects, false);
  176. return DeletedObjects == Objects.Num();
  177. }
  178. else
  179. {
  180. int DeletedObjects = ObjectTools::DeleteAssets(InAssetsToDelete, true);
  181. return DeletedObjects == InAssetsToDelete.Num();
  182. }
  183. }
  184. bool DeleteDeprecatedAssets(const TArray<FAssetData>& InAssetsToDelete)
  185. {
  186. bool bSuccess = true;
  187. bSuccess &= DeleteAssets(InAssetsToDelete);
  188. const FString MediaFolderpath = FPaths::Combine(AkUnrealEditorHelper::GetLegacySoundBankDirectory(), AkUnrealHelper::MediaFolderName);
  189. IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
  190. PlatformFile.DeleteDirectoryRecursively(*MediaFolderpath);
  191. const FString LocalizedFolderPath = FPaths::Combine(AkUnrealEditorHelper::GetLegacySoundBankDirectory(), AkUnrealEditorHelper::LocalizedFolderName);
  192. PlatformFile.DeleteDirectoryRecursively(*LocalizedFolderPath);
  193. return bSuccess;
  194. }
  195. void FindWwiseAssetsInProject(TArray<FAssetData>& OutWwiseAssets)
  196. {
  197. FARFilter Filter;
  198. Filter.bRecursiveClasses = true;
  199. #if UE_5_1_OR_LATER
  200. Filter.ClassPaths.Add(UAkAudioType::StaticClass()->GetClassPathName());
  201. //We want to delete these asset types during cleanup so no need to dirty them
  202. Filter.RecursiveClassPathsExclusionSet.Add(UAkAudioBank::StaticClass()->GetClassPathName());
  203. Filter.RecursiveClassPathsExclusionSet.Add(UAkFolder::StaticClass()->GetClassPathName());
  204. #else
  205. Filter.ClassNames.Add(UAkAudioType::StaticClass()->GetFName());
  206. Filter.RecursiveClassesExclusionSet.Add(UAkAudioBank::StaticClass()->GetFName());
  207. Filter.RecursiveClassesExclusionSet.Add(UAkFolder::StaticClass()->GetFName());
  208. #endif
  209. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
  210. AssetRegistryModule.Get().GetAssets(Filter, OutWwiseAssets);
  211. }
  212. bool MigrateWwiseAssets(const TArray<FAssetData> & WwiseAssets, bool bShouldSplitSwitchContainerMedia)
  213. {
  214. TArray<UPackage*> WwisePackagesToSave;
  215. EWwiseEventSwitchContainerLoading SwitchContainerShouldLoad = bShouldSplitSwitchContainerMedia
  216. ? EWwiseEventSwitchContainerLoading::LoadOnReference
  217. : EWwiseEventSwitchContainerLoading::AlwaysLoad;
  218. for (auto& Asset : WwiseAssets)
  219. {
  220. UAkAudioType* AssetPtr = Cast<UAkAudioType>(Asset.GetAsset());
  221. if (AssetPtr)
  222. {
  223. AssetPtr->MigrateWwiseObjectInfo();
  224. if (AssetPtr->GetClass() == UAkAudioEvent::StaticClass())
  225. {
  226. UAkAudioEvent* Event = Cast<UAkAudioEvent>(AssetPtr);
  227. Event->EventInfo.SwitchContainerLoading = SwitchContainerShouldLoad;
  228. }
  229. if (!AssetPtr->MarkPackageDirty())
  230. {
  231. UE_LOG(LogAudiokineticTools, Warning, TEXT("Could not dirty asset %s during migration, will still try to save it."), *AssetPtr->GetFullName());
  232. }
  233. WwisePackagesToSave.Add(AssetPtr->GetPackage());
  234. }
  235. else
  236. {
  237. UE_LOG(LogAudiokineticTools, Error, TEXT("Could not get asset '%s' in order to migrate it."), *Asset.AssetName.ToString());
  238. }
  239. }
  240. if (WwisePackagesToSave.Num() > 0)
  241. {
  242. if (!UEditorLoadingAndSavingUtils::SavePackages(WwisePackagesToSave, false))
  243. {
  244. UE_LOG(LogAudiokineticTools, Warning, TEXT("MigrateWwiseAssets: Could not save assets during asset migration. Please save them manually, or run the migration operation again."));
  245. return false;
  246. }
  247. }
  248. return true;
  249. }
  250. bool MigrateAudioBanks(const FMigrationOperations& MigrationOperations, const bool bCleanupAssets, const bool bWasUsingEBP, const bool bTransferAutoLoad, const FString DefinitionFile)
  251. {
  252. TSet<FAssetData> FailedBanks;
  253. TMap<FString, FBankEntry> BanksToTransfer;
  254. FillBanksToTransfer(BanksToTransfer);
  255. const bool bIncludeMedia = !bWasUsingEBP;
  256. bool bContinueMigration = true;
  257. bool bTransferSuccess = true;
  258. FString ErrorMessage;
  259. if (MigrationOperations.BankTransferMethod == EBankTransferMode::WAAPI)
  260. {
  261. TArray<FBankTransferError> ErrorMessages = TransferUserBanksWaapi( BanksToTransfer, FailedBanks, bIncludeMedia);
  262. ErrorMessage = FormatWaapiErrorMessage(ErrorMessages);
  263. bTransferSuccess = FailedBanks.Num() == 0;
  264. }
  265. else if (MigrationOperations.BankTransferMethod == EBankTransferMode::DefinitionFile)
  266. {
  267. auto Result = WriteBankDefinitionFile( BanksToTransfer, bIncludeMedia, DefinitionFile);
  268. bTransferSuccess = Result == Success;
  269. if (!bTransferSuccess)
  270. {
  271. switch (Result)
  272. {
  273. case OpenFailure:
  274. ErrorMessage = FString::Format(TEXT("Failed to open bank definition file for write '{0}'."), {DefinitionFile});
  275. break;
  276. case WriteFailure:
  277. ErrorMessage = FString::Format(TEXT("Failed to write to bank definition file '{0}'."), {DefinitionFile});
  278. break;
  279. default:
  280. break;
  281. }
  282. }
  283. }
  284. if (!bTransferSuccess)
  285. {
  286. if (!FApp::CanEverRender())
  287. {
  288. bContinueMigration = MigrationOperations.bIgnoreBankTransferErrors;
  289. }
  290. else //Migrating in Unreal Editor
  291. {
  292. bContinueMigration = PromptFailedBankTransfer(ErrorMessage);
  293. }
  294. }
  295. if (!bContinueMigration)
  296. {
  297. UE_LOG(LogAudiokineticTools, Error, TEXT("MigrateAudioBanks : Errors encountered while transferring SoundBanks: \n%s"), *ErrorMessage);
  298. UE_LOG(LogAudiokineticTools, Error, TEXT("MigrateAudioBanks : Migration aborted due to SoundBank transfer errors."));
  299. return false;
  300. }
  301. if (!ErrorMessage.IsEmpty())
  302. {
  303. UE_LOG(LogAudiokineticTools, Warning, TEXT("MigrateAudioBanks : Errors encountered while transferring SoundBanks: \n%s"), *ErrorMessage);
  304. UE_LOG(LogAudiokineticTools, Display, TEXT("MigrateAudioBanks : Migration will continue and ignore SoundBank transfer errors."));
  305. }
  306. if (bTransferAutoLoad)
  307. {
  308. UE_LOG(LogAudiokineticTools, Display, TEXT("MigrateAudioBanks: Transferring AutoLoad setting from AkAudioBank assets to Event and Aux Bus assets."));
  309. for (auto& Bank : BanksToTransfer)
  310. {
  311. UAkAudioBank* BankAsset = Cast<UAkAudioBank>(Bank.Value.BankAssetData.GetAsset());
  312. if (!BankAsset)
  313. {
  314. #if UE_5_1_OR_LATER
  315. UE_LOG(LogAudiokineticTools, Warning, TEXT("MigrateAudioBanks: Could not load UAkAudioBank Asset '%s'. AutoLoad property will not be transferred. "),
  316. *Bank.Value.BankAssetData.GetObjectPathString());
  317. #else
  318. UE_LOG(LogAudiokineticTools, Warning, TEXT("MigrateAudioBanks: Could not load UAkAudioBank Asset '%s'. AutoLoad property will not be transferred. "),
  319. *Bank.Value.BankAssetData.ObjectPath.ToString());
  320. #endif
  321. continue;
  322. }
  323. if (!BankAsset->AutoLoad_DEPRECATED)
  324. {
  325. TArray<FLinkedAssetEntry>& LinkedAuxBusses = Bank.Value.LinkedAuxBusses;
  326. TArray<FLinkedAssetEntry>& LinkedEvents = Bank.Value.LinkedEvents;
  327. for (auto& LinkedAuxBus : LinkedAuxBusses)
  328. {
  329. bool MigrateSuccess = false;
  330. auto FoundAssetData = AkAssetDatabase::Get().FindAssetByObjectPath(LinkedAuxBus.AssetPath);
  331. if (FoundAssetData.IsValid())
  332. {
  333. if (auto* BusAsset = Cast<UAkAuxBus>(FoundAssetData.GetAsset()))
  334. {
  335. BusAsset->bAutoLoad = false;
  336. MigrateSuccess = BusAsset->MarkPackageDirty();
  337. }
  338. }
  339. if(!MigrateSuccess)
  340. {
  341. UE_LOG(LogAudiokineticTools, Warning, TEXT("MigrateAudioBanks: Could not successfully disable AutoLoad on AuxBus asset %s during migration. It will be automatically loaded."), *LinkedAuxBus.AssetPath);
  342. }
  343. }
  344. for (auto& LinkedEvent : LinkedEvents)
  345. {
  346. bool MigrateSuccess = false;
  347. auto FoundAssetData = AkAssetDatabase::Get().FindAssetByObjectPath(LinkedEvent.AssetPath);
  348. if (FoundAssetData.IsValid())
  349. {
  350. if (auto* EventAsset = Cast<UAkAudioEvent>(FoundAssetData.GetAsset()))
  351. {
  352. EventAsset->bAutoLoad = false;
  353. MigrateSuccess = EventAsset->MarkPackageDirty();
  354. }
  355. }
  356. if (!MigrateSuccess)
  357. {
  358. UE_LOG(LogAudiokineticTools, Warning, TEXT("MigrateAudioBanks: Could not successfully disable AutoLoad on Event asset %s during migration. It will be automatically loaded."), *LinkedEvent.AssetPath);
  359. }
  360. }
  361. }
  362. }
  363. }
  364. if (bCleanupAssets)
  365. {
  366. UE_LOG(LogAudiokineticTools, Display, TEXT("MigrateAudioBanks: Deleting deprecated AkAudioBank assets."));
  367. TSet<FAssetData> ProjectBanks;
  368. for (auto Bank : BanksToTransfer)
  369. {
  370. ProjectBanks.Add(Bank.Value.BankAssetData);
  371. }
  372. TArray<FAssetData> BanksToDelete = ProjectBanks.Array();
  373. DeleteAssets(BanksToDelete);
  374. }
  375. return true;
  376. }
  377. void FillBanksToTransfer(TMap<FString, FBankEntry>& BanksToTransfer)
  378. {
  379. auto& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
  380. auto& AssetRegistry = AssetRegistryModule.Get();
  381. TArray<FAssetData> Banks;
  382. #if UE_5_1_OR_LATER
  383. AssetRegistry.GetAssetsByClass(UAkAudioBank::StaticClass()->GetClassPathName(), Banks);
  384. #else
  385. AssetRegistry.GetAssetsByClass(UAkAudioBank::StaticClass()->GetFName(), Banks);
  386. #endif
  387. for (FAssetData& BankData : Banks)
  388. {
  389. FString BankName = BankData.AssetName.ToString();
  390. BanksToTransfer.Add(BankName, { BankData, {}, {} });
  391. }
  392. TArray< FAssetData> Events;
  393. #if UE_5_1_OR_LATER
  394. AssetRegistry.GetAssetsByClass(UAkAudioEvent::StaticClass()->GetClassPathName(), Events);
  395. #else
  396. AssetRegistry.GetAssetsByClass(UAkAudioEvent::StaticClass()->GetFName(), Events);
  397. #endif
  398. for (FAssetData EventData : Events)
  399. {
  400. if (UAkAudioEvent* Event = Cast<UAkAudioEvent>(EventData.GetAsset()))
  401. {
  402. if (Event->RequiredBank_DEPRECATED != nullptr)
  403. {
  404. FString BankName = Event->RequiredBank_DEPRECATED->GetName();
  405. FLinkedAssetEntry EventEntry = { Event->GetName(), Event->EventInfo.WwiseGuid, Event->GetPathName() };
  406. if (BanksToTransfer.Contains(BankName))
  407. {
  408. BanksToTransfer[BankName].LinkedEvents.Add(EventEntry);
  409. }
  410. else
  411. {
  412. FAssetData BankAssetData = FAssetData(Event->RequiredBank_DEPRECATED);
  413. BanksToTransfer.Add(BankName, { BankAssetData, {EventEntry}, {} });
  414. }
  415. }
  416. }
  417. }
  418. TArray< FAssetData> AuxBusses;
  419. #if UE_5_1_OR_LATER
  420. AssetRegistry.GetAssetsByClass(UAkAuxBus::StaticClass()->GetClassPathName(), AuxBusses);
  421. #else
  422. AssetRegistry.GetAssetsByClass(UAkAuxBus::StaticClass()->GetFName(), AuxBusses);
  423. #endif
  424. for (FAssetData AuxBusData : AuxBusses)
  425. {
  426. if (UAkAuxBus* AuxBus = Cast<UAkAuxBus>(AuxBusData.GetAsset()))
  427. {
  428. if (AuxBus->RequiredBank_DEPRECATED != nullptr)
  429. {
  430. FString BankName = AuxBus->RequiredBank_DEPRECATED->GetName();
  431. FLinkedAssetEntry AuxBusEntry = { AuxBus->GetName(), AuxBus->AuxBusInfo.WwiseGuid, AuxBus->GetPathName() };
  432. if (BanksToTransfer.Contains(BankName))
  433. {
  434. BanksToTransfer[BankName].LinkedAuxBusses.Add(AuxBusEntry);
  435. }
  436. else
  437. {
  438. FAssetData BankAssetData = FAssetData(AuxBus->RequiredBank_DEPRECATED);
  439. BanksToTransfer.Add(BankName, { BankAssetData, {}, {AuxBusEntry} });
  440. }
  441. }
  442. }
  443. }
  444. UE_LOG(LogAudiokineticTools, Display, TEXT("MigrateAudioBanks: Found %d SoundBank assets in project."), BanksToTransfer.Num());
  445. }
  446. TArray<FBankTransferError> TransferUserBanksWaapi(const TMap<FString, FBankEntry>& InBanksToTransfer, TSet<FAssetData>& OutFailedBanks, const bool bIncludeMedia)
  447. {
  448. UE_LOG(LogAudiokineticTools, Display, TEXT("MigrateAudioBanks: Transfering SoundBanks with WAAPI."));
  449. TArray<FBankTransferError> ErrorMessages;
  450. for (TPair<FString, FBankEntry > BankEntry : InBanksToTransfer)
  451. {
  452. FGuid BankID;
  453. if (!CreateBankWaapi(BankEntry.Key, BankEntry.Value, BankID, ErrorMessages))
  454. {
  455. OutFailedBanks.Add(BankEntry.Value.BankAssetData);
  456. continue;
  457. }
  458. if (!SetBankIncludesWaapi(BankEntry.Value, BankID, bIncludeMedia, ErrorMessages))
  459. {
  460. OutFailedBanks.Add(BankEntry.Value.BankAssetData);
  461. }
  462. }
  463. SaveProjectWaapi(ErrorMessages);
  464. return ErrorMessages;
  465. }
  466. EDefinitionFileCreationResult WriteBankDefinitionFile(const TMap<FString, FBankEntry>& InBanksToTransfer, const bool bIncludeMedia, const FString DefinitionFilePath)
  467. {
  468. UE_LOG(LogAudiokineticTools, Display, TEXT("MigrateAudioBanks: Writing SoundBanks Definition File to '%s'."), *DefinitionFilePath);
  469. // open file to start writing
  470. IPlatformFile* PlatformFile = &FPlatformFileManager::Get().GetPlatformFile();;
  471. FString FileLocation = IFileManager::Get().ConvertToRelativePath(*DefinitionFilePath);
  472. TUniquePtr<IFileHandle> FileWriter = TUniquePtr<IFileHandle>(PlatformFile->OpenWrite(*FileLocation));
  473. if (!FileWriter.IsValid())
  474. {
  475. return OpenFailure;
  476. }
  477. bool bWriteSuccess = true;
  478. for (TPair<FString, FBankEntry > BankEntry : InBanksToTransfer)
  479. {
  480. bWriteSuccess &= WriteBankDefinition(BankEntry.Value, FileWriter, bIncludeMedia);
  481. }
  482. if (!bWriteSuccess)
  483. {
  484. return WriteFailure;
  485. }
  486. FileWriter->Flush();
  487. return Success;
  488. }
  489. bool WriteBankDefinition(const FBankEntry& BankEntry, TUniquePtr<IFileHandle>& FileWriter, const bool bIncludeMedia)
  490. {
  491. bool bWriteSuccess = true;
  492. FString MediaString = bIncludeMedia? "\tMedia": "";
  493. for (FLinkedAssetEntry Event : BankEntry.LinkedEvents)
  494. {
  495. auto Line = BankEntry.BankAssetData.AssetName.ToString() + TEXT("\t\"") + Event.AssetName + TEXT("\"") + TEXT("\tEvent") + MediaString + TEXT("\tStructure") + LINE_TERMINATOR;
  496. FTCHARToUTF8 Utf8Formatted(*Line);
  497. bWriteSuccess &= FileWriter->Write(reinterpret_cast<const uint8*>(Utf8Formatted.Get()), Utf8Formatted.Length());
  498. }
  499. for (FLinkedAssetEntry Bus : BankEntry.LinkedAuxBusses)
  500. {
  501. auto Line = BankEntry.BankAssetData.AssetName.ToString() + TEXT("\t-AuxBus\t\"") + Bus.AssetName + TEXT("\"") + MediaString + TEXT("\tStructure") + LINE_TERMINATOR;
  502. FTCHARToUTF8 Utf8Formatted(*Line);
  503. bWriteSuccess &= FileWriter->Write(reinterpret_cast<const uint8*>(Utf8Formatted.Get()), Utf8Formatted.Length());
  504. }
  505. return bWriteSuccess;
  506. }
  507. bool CreateBankWaapi(const FString& BankName, const FBankEntry& BankEntry, FGuid& OutBankGuid, TArray<FBankTransferError>& ErrorMessages)
  508. {
  509. #if AK_SUPPORT_WAAPI
  510. FAkWaapiClient* WaapiClient = FAkWaapiClient::Get();
  511. if (!WaapiClient)
  512. {
  513. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to create SoundBank for <%s>. \nCould not get WAAPI Client."), *BankEntry.BankAssetData.PackageName.ToString());
  514. ErrorMessages.Add({"Could not get WAAPI Client", true, BankEntry});
  515. return false;
  516. }
  517. else if (!WaapiClient->IsConnected())
  518. {
  519. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to create SoundBank for <%s>. \nWAAPI Client not connected."), *BankEntry.BankAssetData.PackageName.ToString());
  520. ErrorMessages.Add({"WAAPI Client not connected", true, BankEntry});
  521. return false;
  522. }
  523. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  524. Args->SetStringField(WwiseWaapiHelper::PARENT, TEXT("\\SoundBanks\\Default Work Unit"));
  525. Args->SetStringField(WwiseWaapiHelper::ON_NAME_CONFLICT, WwiseWaapiHelper::RENAME);
  526. Args->SetStringField(WwiseWaapiHelper::TYPE, WwiseWaapiHelper::SOUNDBANK_TYPE);
  527. Args->SetStringField(WwiseWaapiHelper::NAME, BankName);
  528. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  529. TSharedPtr<FJsonObject> Result;
  530. FString IdString;
  531. if (!WaapiClient->Call(ak::wwise::core::object::create, Args, Options, Result))
  532. {
  533. FString ErrorMessage;
  534. if (Result.IsValid())
  535. {
  536. Result->TryGetStringField(TEXT("message"), ErrorMessage);
  537. }
  538. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to create SoundBank for <%s>.\nMessage : <%s>."), *BankEntry.BankAssetData.PackageName.ToString(), *ErrorMessage);
  539. ErrorMessages.Add({ErrorMessage, true, BankEntry});
  540. return false;
  541. }
  542. if (Result.IsValid() && !Result->TryGetStringField(WwiseWaapiHelper::ID, IdString))
  543. {
  544. FString ErrorMessage;
  545. if (Result.IsValid())
  546. {
  547. Result->TryGetStringField(TEXT("message"), ErrorMessage);
  548. }
  549. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to create SoundBank for <%s>.\nMessage : <%s>."), *BankEntry.BankAssetData.PackageName.ToString(), *ErrorMessage);
  550. ErrorMessages.Add({ErrorMessage, true, BankEntry});
  551. return false; // error parsing Json
  552. }
  553. FGuid::ParseExact(IdString, EGuidFormats::DigitsWithHyphensInBraces, OutBankGuid);
  554. return true;
  555. #else
  556. UE_LOG(LogAudiokineticTools, Error, TEXT("SetBankIncludesWaapi: WAAPI not supported"));
  557. ErrorMessages.Add({TEXT("WAAPI not supported"), false, {}});
  558. return false;
  559. #endif
  560. }
  561. bool SetBankIncludesWaapi(const FBankEntry& BankEntry, const FGuid& BankId, const bool bIncludeMedia, TArray<FBankTransferError>& ErrorMessages)
  562. {
  563. #if AK_SUPPORT_WAAPI
  564. FAkWaapiClient* WaapiClient = FAkWaapiClient::Get();
  565. if (!WaapiClient)
  566. {
  567. UE_LOG(LogAudiokineticTools, Warning, TEXT("WAAPI command to add Wwise objects to SoundBank '%s' failed. \nCould not get WAAPI Client."), *BankEntry.BankAssetData.PackageName.ToString());
  568. ErrorMessages.Add({"Could not get WAAPI Client", true, BankEntry});
  569. return false;
  570. }
  571. else if (!WaapiClient->IsConnected())
  572. {
  573. UE_LOG(LogAudiokineticTools, Warning, TEXT("WAAPI command to add Wwise objects to SoundBank '%s' failed. \nWAAPI Client not connected."), *BankEntry.BankAssetData.PackageName.ToString());
  574. ErrorMessages.Add({"WAAPI Client not connected", true, BankEntry});
  575. return false;
  576. }
  577. TSet<FString> IncludeIds;
  578. for (FLinkedAssetEntry Event : BankEntry.LinkedEvents)
  579. {
  580. if (Event.WwiseGuid.IsValid())
  581. {
  582. IncludeIds.Add(Event.WwiseGuid.ToString(EGuidFormats::DigitsWithHyphensInBraces));
  583. }
  584. else
  585. {
  586. IncludeIds.Add(TEXT("Event:") + Event.AssetName);
  587. }
  588. }
  589. for (FLinkedAssetEntry AuxBus : BankEntry.LinkedAuxBusses)
  590. {
  591. if (AuxBus.WwiseGuid.IsValid())
  592. {
  593. IncludeIds.Add(AuxBus.WwiseGuid.ToString(EGuidFormats::DigitsWithHyphensInBraces));
  594. }
  595. else
  596. {
  597. IncludeIds.Add(TEXT("AuxBus:") + AuxBus.AssetName);
  598. }
  599. }
  600. if (IncludeIds.Num() < 0)
  601. {
  602. return true;
  603. }
  604. TArray<TSharedPtr<FJsonValue>> Filters;
  605. Filters.Add(MakeShared< FJsonValueString>(TEXT("events")));
  606. Filters.Add(MakeShared< FJsonValueString>(TEXT("structures")));
  607. if (bIncludeMedia)
  608. {
  609. Filters.Add(MakeShared< FJsonValueString>(TEXT("media")));
  610. }
  611. TArray<TSharedPtr<FJsonValue>> IncludeIdJson;
  612. for (const FString IncludedId : IncludeIds)
  613. {
  614. TSharedPtr<FJsonObject> IncludedObject = MakeShared< FJsonObject>();
  615. IncludedObject->SetStringField(WwiseWaapiHelper::OBJECT, IncludedId);
  616. IncludedObject->SetArrayField(WwiseWaapiHelper::FILTER, Filters);
  617. IncludeIdJson.Add(MakeShared< FJsonValueObject>(IncludedObject));
  618. }
  619. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  620. Args->SetStringField(WwiseWaapiHelper::SOUNDBANK_FIELD, BankId.ToString(EGuidFormats::DigitsWithHyphensInBraces));
  621. Args->SetStringField(WwiseWaapiHelper::OPERATION, TEXT("add"));
  622. Args->SetArrayField(WwiseWaapiHelper::INCLUSIONS, IncludeIdJson);
  623. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  624. TSharedPtr<FJsonObject> Result;
  625. if (!WaapiClient->Call(ak::wwise::core::soundbank::setInclusions, Args, Options, Result))
  626. {
  627. FString ErrorMessage;
  628. if (Result.IsValid())
  629. {
  630. Result->TryGetStringField(TEXT("message"), ErrorMessage);
  631. }
  632. UE_LOG(LogAudiokineticTools, Warning, TEXT("WAAPI command to add Wwise objects to SoundBank '%s' failed. \nError Message : <%s>."), *BankEntry.BankAssetData.PackageName.ToString(), *ErrorMessage);
  633. ErrorMessages.Add({ErrorMessage, true, BankEntry});
  634. return false;
  635. }
  636. return true;
  637. #else
  638. UE_LOG(LogAudiokineticTools, Error, TEXT("SetBankIncludesWaapi: WAAPI not supported"));
  639. ErrorMessages.Add({TEXT("WAAPI not supported"), false, {}});
  640. return false;
  641. #endif
  642. }
  643. bool SaveProjectWaapi(TArray<FBankTransferError>& ErrorMessages)
  644. {
  645. #if AK_SUPPORT_WAAPI
  646. FAkWaapiClient* WaapiClient = FAkWaapiClient::Get();
  647. if (!WaapiClient)
  648. {
  649. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to save Wwise project.\n Could not get Waapi Client."));
  650. ErrorMessages.Add({TEXT("Failed to save Wwise project over WAAPI. Please save the project manually."), false, {}});
  651. return false;
  652. }
  653. else if (!WaapiClient->IsConnected())
  654. {
  655. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to save Wwise project.\n Waapi Client not connected."));
  656. ErrorMessages.Add({TEXT("Failed to save Wwise project over WAAPI. Please save the project manually."), false, {}});
  657. return false;
  658. }
  659. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  660. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  661. TSharedPtr<FJsonObject> Result;
  662. FString IdString;
  663. if (!WaapiClient->Call(ak::wwise::core::project::save, Args, Options, Result))
  664. {
  665. FString ErrorMessage;
  666. if (Result.IsValid())
  667. {
  668. Result->TryGetStringField(TEXT("message"), ErrorMessage);
  669. }
  670. UE_LOG(LogAudiokineticTools, Warning, TEXT("Failed to save Wwise project.\nMessage : <%s>."), *ErrorMessage);
  671. ErrorMessages.Add({TEXT("Failed to save Wwise project over WAAPI. Please save the project manually."), false, {}});
  672. ErrorMessages.Add({ErrorMessage, false, {}});
  673. return false;
  674. }
  675. return true;
  676. #else
  677. UE_LOG(LogAudiokineticTools, Error, TEXT("SetBankIncludesWaapi: WAAPI not supported"));
  678. ErrorMessages.Add({TEXT("WAAPI not supported"), false, {}});
  679. return false;
  680. #endif
  681. }
  682. bool MigrateProjectSettings(FString& ProjectContent, const bool bWasUsingEBP, const bool bUseGeneratedSubFolders, const FString& GeneratedSoundBanksFolder )
  683. {
  684. //migrate split media per id
  685. TArray<PropertyToChange> PropertiesToAdd;
  686. if (bWasUsingEBP)
  687. {
  688. PropertiesToAdd.Add({ TEXT("AutoSoundBankEnabled"), TEXT("True"), TEXT("<Property Name=\"AutoSoundBankEnabled\" Type=\"bool\" Value=\"True\"/>") });
  689. }
  690. if (bUseGeneratedSubFolders)
  691. {
  692. PropertiesToAdd.Add({ TEXT("MediaAutoBankSubFolders"), TEXT("True"), TEXT("<Property Name=\"MediaAutoBankSubFolders\" Type=\"bool\" Value=\"True\"/>") });
  693. }
  694. static const TArray<FString> LogCentralItemsToRemove =
  695. {
  696. TEXT("<IgnoreItem MessageId=\"MediaDuplicated\"/>"),
  697. TEXT("<IgnoreItem MessageId=\"MediaNotFound\"/>")
  698. };
  699. bool bModified = false;
  700. if (PropertiesToAdd.Num() >0)
  701. {
  702. bModified = InsertProperties(PropertiesToAdd, ProjectContent);
  703. }
  704. for (const FString& LogItemToRemove : LogCentralItemsToRemove)
  705. {
  706. if (ProjectContent.Contains(LogItemToRemove))
  707. {
  708. ProjectContent.ReplaceInline(*LogItemToRemove, TEXT(""));
  709. bModified = true;
  710. }
  711. }
  712. return bModified;
  713. }
  714. bool SetStandardSettings(FString& ProjectContent)
  715. {
  716. static const TArray<PropertyToChange> PropertiesToAdd = {
  717. { TEXT("GenerateMultipleBanks"), TEXT("True"), TEXT("<Property Name=\"GenerateMultipleBanks\" Type=\"bool\" Value=\"True\"/>") },
  718. { TEXT("GenerateSoundBankJSON"), TEXT("True"), TEXT("<Property Name=\"GenerateSoundBankJSON\" Type=\"bool\" Value=\"True\"/>") },
  719. { TEXT("SoundBankGenerateEstimatedDuration"), TEXT("True"), TEXT("<Property Name=\"SoundBankGenerateEstimatedDuration\" Type=\"bool\" Value=\"True\"/>") },
  720. { TEXT("SoundBankGenerateMaxAttenuationInfo"), TEXT("True"), TEXT("<Property Name=\"SoundBankGenerateMaxAttenuationInfo\" Type=\"bool\" Value=\"True\"/>") },
  721. { TEXT("SoundBankGeneratePrintGUID"), TEXT("True"), TEXT("<Property Name=\"SoundBankGeneratePrintGUID\" Type=\"bool\" Value=\"True\"/>") },
  722. { TEXT("SoundBankGeneratePrintPath"), TEXT("True"), TEXT("<Property Name=\"SoundBankGeneratePrintPath\" Type=\"bool\" Value=\"True\"/>") },
  723. { TEXT("CopyLooseStreamedMedia"), TEXT("True"), TEXT("<Property Name=\"CopyLooseStreamedMedia\" Type=\"bool\" Value=\"True\"/>") },
  724. { TEXT("RemoveUnusedGeneratedFiles"), TEXT("True"), TEXT("<Property Name=\"RemoveUnusedGeneratedFiles\" Type=\"bool\" Value=\"True\"/>") },
  725. };
  726. return InsertProperties(PropertiesToAdd, ProjectContent);
  727. }
  728. bool InsertProperties(const TArray<PropertyToChange>& PropertiesToChange, FString& ProjectContent)
  729. {
  730. static const auto PropertyListStart = TEXT("<PropertyList>");
  731. static const FString EndTag = TEXT(">");
  732. static const TCHAR EmptyElementEndChar = '/';
  733. static const FString ValueTag = TEXT("<Value>");
  734. static const FString EndValueTag = TEXT("</Value>");
  735. static const FString ValueAttribute = TEXT("Value=\"");
  736. static const FString EndValueAttribute = TEXT("\"");
  737. bool bModified = false;
  738. int32 PropertyListPosition = ProjectContent.Find(PropertyListStart);
  739. if (PropertyListPosition != -1)
  740. {
  741. int32 InsertPosition = PropertyListPosition + FCString::Strlen(PropertyListStart);
  742. for (PropertyToChange ItemToAdd : PropertiesToChange)
  743. {
  744. auto idx = ProjectContent.Find(ItemToAdd.Name);
  745. if (idx == -1)
  746. {
  747. ProjectContent.InsertAt(InsertPosition, FString::Printf(TEXT("\n\t\t\t\t%s"), *ItemToAdd.Xml));
  748. bModified = true;
  749. }
  750. else
  751. {
  752. FString ValueText;
  753. FString EndValueText;
  754. int32 EndTagIdx = ProjectContent.Find(EndTag, ESearchCase::IgnoreCase, ESearchDir::FromStart, idx);
  755. if (ProjectContent[EndTagIdx - 1] == EmptyElementEndChar)
  756. {
  757. // The property is an empty element, the value will be in an attribute
  758. ValueText = ValueAttribute;
  759. EndValueText = EndValueAttribute;
  760. }
  761. else
  762. {
  763. // We are in a ValueList
  764. ValueText = ValueTag;
  765. EndValueText = EndValueTag;
  766. }
  767. int32 ValueIdx = ProjectContent.Find(ValueText, ESearchCase::IgnoreCase, ESearchDir::FromStart, idx);
  768. int32 EndValueIdx = ProjectContent.Find(EndValueText, ESearchCase::IgnoreCase, ESearchDir::FromStart, ValueIdx);
  769. if (ValueIdx != -1 && ValueIdx > idx && ValueIdx < EndValueIdx)
  770. {
  771. ValueIdx += ValueText.Len();
  772. auto ValueEndIdx = ProjectContent.Find(EndValueText, ESearchCase::IgnoreCase, ESearchDir::FromStart, ValueIdx);
  773. if (ValueEndIdx != -1)
  774. {
  775. FString value = ProjectContent.Mid(ValueIdx, ValueEndIdx - ValueIdx);
  776. if (value != ItemToAdd.Value)
  777. {
  778. ProjectContent.RemoveAt(ValueIdx, ValueEndIdx - ValueIdx, false);
  779. ProjectContent.InsertAt(ValueIdx, ItemToAdd.Value);
  780. bModified = true;
  781. }
  782. }
  783. }
  784. else
  785. {
  786. UE_LOG(LogAudiokineticTools, Warning, TEXT("Could not change value for %s in Wwise project. Some features might not work properly."), *ItemToAdd.Name);
  787. }
  788. }
  789. }
  790. }
  791. return bModified;
  792. }
  793. }
  794. #undef LOCTEXT_NAMESPACE