AkAssetMigrationHelper.cpp 34 KB

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