WwiseReconcile.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  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 "Wwise/WwiseReconcile.h"
  16. #include "Wwise/Stats/Reconcile.h"
  17. #include "AkAcousticTexture.h"
  18. #include "AkAudioEvent.h"
  19. #include "AkAudioType.h"
  20. #include "AkAuxBus.h"
  21. #include "AkEffectShareSet.h"
  22. #include "AkInitBank.h"
  23. #include "AkRtpc.h"
  24. #include "AkStateValue.h"
  25. #include "AkSwitchValue.h"
  26. #include "AkTrigger.h"
  27. #include "AkUnrealAssetDataHelper.h"
  28. #include "WwiseUnrealHelper.h"
  29. #include "AssetToolsModule.h"
  30. #include "AssetViewUtils.h"
  31. #include "AssetRegistry/AssetData.h"
  32. #include "AssetRegistry/AssetRegistryModule.h"
  33. #include "FileHelpers.h"
  34. #include "ObjectTools.h"
  35. #include "Misc/App.h"
  36. #include "Misc/EngineBuildSettings.h"
  37. #include "Misc/ScopedSlowTask.h"
  38. FWwiseReconcile* FWwiseReconcile::Instance;
  39. #define LOCTEXT_NAMESPACE "AkAudio"
  40. UClass* FWwiseReconcile::GetUClassFromWwiseRefType(EWwiseRefType RefType)
  41. {
  42. switch (RefType)
  43. {
  44. case EWwiseRefType::Event:
  45. return UAkAudioEvent::StaticClass();
  46. case EWwiseRefType::AuxBus:
  47. return UAkAuxBus::StaticClass();
  48. case EWwiseRefType::AcousticTexture:
  49. return UAkAcousticTexture::StaticClass();
  50. case EWwiseRefType::State:
  51. return UAkStateValue::StaticClass();
  52. case EWwiseRefType::Switch:
  53. return UAkSwitchValue::StaticClass();
  54. case EWwiseRefType::GameParameter:
  55. return UAkRtpc::StaticClass();
  56. case EWwiseRefType::Trigger:
  57. return UAkTrigger::StaticClass();
  58. case EWwiseRefType::PluginShareSet:
  59. return UAkEffectShareSet::StaticClass();
  60. case EWwiseRefType::None:
  61. return nullptr;
  62. default:
  63. return nullptr;
  64. }
  65. }
  66. void FWwiseReconcile::GetAllWwiseRefs()
  67. {
  68. GuidToWwiseRef.Empty();
  69. auto ProjectDatabase = FWwiseProjectDatabase::Get();
  70. if (UNLIKELY(!ProjectDatabase))
  71. {
  72. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not load project database"));
  73. return;
  74. }
  75. FWwiseDataStructureScopeLock DataStructure(*ProjectDatabase);
  76. // Check to make sure there are no issues getting data for the CurrentPlatform
  77. if (DataStructure.GetSoundBanks().Num() == 0)
  78. {
  79. FName PlatformName = DataStructure.GetCurrentPlatform().GetPlatformName();
  80. UE_LOG(LogWwiseReconcile, Error, TEXT("No data loaded from Wwise project database for the curent platform %s"), *PlatformName.ToString());
  81. return;
  82. }
  83. if (DataStructure.GetCurrentPlatformData()->Guids.Num() == 0)
  84. {
  85. FName PlatformName = DataStructure.GetCurrentPlatform().GetPlatformName();
  86. UE_LOG(LogWwiseReconcile, Error, TEXT("No data loaded from Wwise project database for the curent platform %s"), *PlatformName.ToString());
  87. return;
  88. }
  89. for (const auto& WwiseRef : DataStructure.GetCurrentPlatformData()->Guids)
  90. {
  91. if (WwiseRef.Value.GetType() != EWwiseRefType::SoundBank)
  92. {
  93. GuidToWwiseRef.Add(WwiseRef.Key.Guid, { &WwiseRef.Value });
  94. }
  95. }
  96. }
  97. void FWwiseReconcile::GetAllAssets(TArray<FWwiseReconcileItem>& ReconcileItems)
  98. {
  99. GetAllWwiseRefs();
  100. auto AssetToolsModule = &FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
  101. if (!AssetToolsModule)
  102. {
  103. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not load the AssetTools Module"));
  104. return;
  105. }
  106. auto AssetRegistryModule = &FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
  107. if (!AssetRegistryModule)
  108. {
  109. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not load the AssetRegistry Module"));
  110. return;
  111. }
  112. TArray<FAssetData> Assets;
  113. #if UE_5_1_OR_LATER
  114. AssetRegistryModule->Get().GetAssetsByClass(UAkAudioType::StaticClass()->GetClassPathName(), Assets, true);
  115. #else
  116. AssetRegistryModule->Get().GetAssetsByClass(UAkAudioType::StaticClass()->GetFName(), Assets, true);
  117. #endif
  118. ReconcileItems.Empty();
  119. for (const FAssetData& AssetData : Assets)
  120. {
  121. // Exclude the Init bank
  122. if (AkUnrealAssetDataHelper::AssetOfType<UAkInitBank>(AssetData))
  123. {
  124. continue;
  125. }
  126. auto Value = AssetData.TagsAndValues.FindTag(FName("WwiseGuid"));
  127. FWwiseReconcileItem Item;
  128. Item.Asset = AssetData;
  129. if (Value.IsSet())
  130. {
  131. Item.ItemId = FGuid(Value.GetValue());
  132. if (auto WwiseRef = GuidToWwiseRef.Find(Item.ItemId))
  133. {
  134. WwiseRef->bAssetExists = true;
  135. Item.WwiseAnyRef = { *WwiseRef };
  136. }
  137. }
  138. ReconcileItems.Add(Item);
  139. }
  140. for (auto WwiseRef : GuidToWwiseRef)
  141. {
  142. if (!WwiseRef.Value.bAssetExists)
  143. {
  144. FWwiseReconcileItem Item;
  145. Item.WwiseAnyRef = { WwiseRef.Value };
  146. Item.ItemId = WwiseRef.Key;
  147. ReconcileItems.Add(Item);
  148. }
  149. }
  150. }
  151. void FWwiseReconcile::GetAssetChanges(TArray<FWwiseReconcileItem>& ReconcileItems,
  152. EWwiseReconcileOperationFlags OperationFlags)
  153. {
  154. AssetsToCreate.Empty();
  155. AssetsToDelete.Empty();
  156. AssetsToRename.Empty();
  157. AssetsToUpdate.Empty();
  158. for (int i = 0; i < ReconcileItems.Num();)
  159. {
  160. auto& ReconcileItem = ReconcileItems[i];
  161. if(!ReconcileItem.WwiseAnyRef.WwiseAnyRef || !ReconcileItem.WwiseAnyRef.WwiseAnyRef->IsValid())
  162. {
  163. if (EnumHasAnyFlags(OperationFlags, EWwiseReconcileOperationFlags::Delete))
  164. {
  165. if (ReconcileItem.Asset.IsValid())
  166. {
  167. ReconcileItem.OperationRequired |= EWwiseReconcileOperationFlags::Delete;
  168. AssetsToDelete.Add(ReconcileItem.Asset);
  169. }
  170. else
  171. {
  172. ReconcileItems.RemoveAt(i);
  173. continue;
  174. }
  175. }
  176. i++;
  177. continue;
  178. }
  179. const FWwiseAnyRef* WwiseRefValue = ReconcileItem.WwiseAnyRef.WwiseAnyRef;
  180. EWwiseRefType RefType = WwiseRefValue->GetType();
  181. UClass* RefClass = GetUClassFromWwiseRefType(RefType);
  182. FAssetData Asset = ReconcileItem.Asset;
  183. if (!RefClass)
  184. {
  185. i++;
  186. continue;
  187. }
  188. if (!Asset.IsValid())
  189. {
  190. if (EnumHasAnyFlags(OperationFlags, EWwiseReconcileOperationFlags::Create))
  191. {
  192. ReconcileItem.OperationRequired |= EWwiseReconcileOperationFlags::Create;
  193. AssetsToCreate.Add(ReconcileItem.WwiseAnyRef);
  194. }
  195. }
  196. else if (EnumHasAnyFlags(OperationFlags, EWwiseReconcileOperationFlags::UpdateExisting))
  197. {
  198. if (RefClass && Asset.IsValid())
  199. {
  200. FName AssetName = AkUnrealAssetDataHelper::GetAssetDefaultName(WwiseRefValue);
  201. if(IsAssetOutOfDate(Asset, *WwiseRefValue))
  202. {
  203. ReconcileItem.OperationRequired |= EWwiseReconcileOperationFlags::UpdateExisting;
  204. AssetsToUpdate.Add(ReconcileItem.Asset);
  205. }
  206. if (Asset.AssetName != AssetName)
  207. {
  208. ReconcileItem.OperationRequired |= EWwiseReconcileOperationFlags::RenameExisting;
  209. AssetsToRename.Add(ReconcileItem.Asset);
  210. }
  211. }
  212. }
  213. if(ReconcileItem.OperationRequired == EWwiseReconcileOperationFlags::None)
  214. {
  215. ReconcileItems.RemoveAt(i);
  216. }
  217. else
  218. {
  219. i++;
  220. }
  221. }
  222. }
  223. bool FWwiseReconcile::ReconcileAssets(EWwiseReconcileOperationFlags OperationFlags)
  224. {
  225. FScopedSlowTask ReconcileTask(GetNumberOfAssets(), LOCTEXT("ReconcileTask", "Reconciling Wwise Assets"));
  226. if(FApp::CanEverRender())
  227. {
  228. ReconcileTask.MakeDialog(true);
  229. }
  230. auto ProjectDatabase = FWwiseProjectDatabase::Get();
  231. if (UNLIKELY(!ProjectDatabase))
  232. {
  233. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not load project database"));
  234. return false;
  235. }
  236. //Locking the Project Database to prevent changes during reconcile.
  237. FWwiseDataStructureScopeLock DataStructure(*ProjectDatabase);
  238. if(GuidToWwiseRef.Num() == 0)
  239. {
  240. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not find SoundBanks information. Make sure your SoundBanks are generated."));
  241. return false;
  242. }
  243. bool bSucceeded = true;
  244. int NumberOfOperationsCompleted = 0;
  245. if (AssetsToDelete.Num() != 0)
  246. {
  247. int NumberOfAssetsDeleted = DeleteAssets(ReconcileTask);
  248. NumberOfOperationsCompleted += NumberOfAssetsDeleted;
  249. if (NumberOfAssetsDeleted > 0 && !ReconcileTask.ShouldCancel())
  250. {
  251. UE_LOG(LogWwiseReconcile, Display, TEXT("Deleted %i assets out of %i."), NumberOfAssetsDeleted, AssetsToDelete.Num());
  252. }
  253. else if(!ReconcileTask.ShouldCancel())
  254. {
  255. UE_LOG(LogWwiseReconcile, Warning, TEXT("Failed to delete outdated AkAudioType assets"));
  256. bSucceeded = false;
  257. }
  258. }
  259. if (AssetsToUpdate.Num() != 0)
  260. {
  261. int NumberOfAssetsUpdated = UpdateExistingAssets(ReconcileTask).Num();
  262. NumberOfOperationsCompleted += NumberOfAssetsUpdated;
  263. if(NumberOfAssetsUpdated > 0 && !ReconcileTask.ShouldCancel())
  264. {
  265. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Updated %i assets out of %i."), NumberOfAssetsUpdated, AssetsToUpdate.Num());
  266. }
  267. else if(!ReconcileTask.ShouldCancel())
  268. {
  269. UE_LOG(LogWwiseReconcile, Warning, TEXT("Failed to consolidate existing AkAudioType assets"));
  270. bSucceeded = false;
  271. }
  272. }
  273. if (AssetsToRename.Num() != 0)
  274. {
  275. if(RenameExistingAssets(ReconcileTask) &&!ReconcileTask.ShouldCancel())
  276. {
  277. NumberOfOperationsCompleted += AssetsToRename.Num();
  278. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Updated %i assets out of %i."), AssetsToRename.Num(), AssetsToRename.Num());
  279. }
  280. else if(!ReconcileTask.ShouldCancel())
  281. {
  282. UE_LOG(LogWwiseReconcile, Warning, TEXT("Failed to rename existing AkAudioType assets"));
  283. bSucceeded = false;
  284. }
  285. }
  286. if (AssetsToCreate.Num() != 0)
  287. {
  288. int NumberOfAssetsCreated = CreateAssets(ReconcileTask).Num();
  289. NumberOfOperationsCompleted += NumberOfAssetsCreated;
  290. if (NumberOfAssetsCreated > 0 && !ReconcileTask.ShouldCancel())
  291. {
  292. UE_LOG(LogWwiseReconcile, Display, TEXT("Created %i assets out of %i."), NumberOfAssetsCreated, AssetsToCreate.Num());
  293. }
  294. else if(!ReconcileTask.ShouldCancel())
  295. {
  296. UE_LOG(LogWwiseReconcile, Warning, TEXT("No New AkAudioType assets created"));
  297. bSucceeded = false;
  298. }
  299. }
  300. if(ReconcileTask.ShouldCancel())
  301. {
  302. UE_LOG(LogWwiseReconcile, Log, TEXT("Reconcile Operation was cancelled by user."));
  303. }
  304. else
  305. {
  306. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Successfully did %i operations out of %i."), NumberOfOperationsCompleted, GetNumberOfAssets());
  307. }
  308. return bSucceeded;
  309. }
  310. TArray<FAssetData> FWwiseReconcile::UpdateExistingAssets(FScopedSlowTask& SlowTask)
  311. {
  312. check(IsInGameThread());
  313. TArray<UPackage*> PackagesToSave;
  314. TArray<FAssetData> UpdatedAssets;
  315. for (const auto& AssetData : AssetsToUpdate)
  316. {
  317. if(SlowTask.ShouldCancel())
  318. {
  319. return UpdatedAssets;
  320. }
  321. auto AkAudioAsset = Cast<UAkAudioType>(AssetData.GetAsset());
  322. if (!LIKELY(AkAudioAsset))
  323. {
  324. UE_LOG(LogWwiseReconcile, Error, TEXT("Failed to update Wwise asset %s."), *AssetData.AssetName.ToString());
  325. continue;
  326. }
  327. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Updating Wwise asset %s."), *AssetData.AssetName.ToString());
  328. AkAudioAsset->FillInfo();
  329. FAssetData NewAssetData = FAssetData(AkAudioAsset);
  330. PackagesToSave.Add(NewAssetData.GetPackage());
  331. UpdatedAssets.Add(NewAssetData);
  332. AkAudioAsset->MarkPackageDirty();
  333. SlowTask.EnterProgressFrame();
  334. }
  335. if (!UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, false))
  336. {
  337. UE_LOG(LogWwiseReconcile, Error, TEXT("Failed to save updated Wwise assets."));
  338. return {};
  339. }
  340. return UpdatedAssets;
  341. }
  342. void FWwiseReconcile::ConvertWwiseItemTypeToReconcileItem(const TArray<TSharedPtr<FWwiseTreeItem>>& InWwiseItems,
  343. TArray<FWwiseReconcileItem>& OutReconcileItems, EWwiseReconcileOperationFlags OperationFlags, bool bFirstLevel)
  344. {
  345. if(bFirstLevel)
  346. {
  347. GetAllWwiseRefs();
  348. }
  349. for(const auto& WwiseTreeItem : InWwiseItems)
  350. {
  351. if(WwiseTreeItem->IsFolder())
  352. {
  353. ConvertWwiseItemTypeToReconcileItem(WwiseTreeItem->GetChildren(), OutReconcileItems, OperationFlags, false);
  354. if(!WwiseTreeItem->ShouldDisplayInfo())
  355. {
  356. continue;
  357. }
  358. }
  359. auto WwiseRef = GuidToWwiseRef.Find(WwiseTreeItem->ItemId);
  360. if(WwiseTreeItem->UEAssetExists())
  361. {
  362. for(auto Asset : WwiseTreeItem->Assets)
  363. {
  364. FWwiseReconcileItem ReconcileItem;
  365. ReconcileItem.ItemId = WwiseTreeItem->ItemId;
  366. ReconcileItem.Asset = Asset;
  367. if (WwiseRef)
  368. {
  369. ReconcileItem.WwiseAnyRef = *WwiseRef;
  370. }
  371. OutReconcileItems.AddUnique(ReconcileItem);
  372. }
  373. }
  374. else
  375. {
  376. FWwiseReconcileItem ReconcileItem;
  377. ReconcileItem.ItemId = WwiseTreeItem->ItemId;
  378. if(WwiseRef)
  379. {
  380. ReconcileItem.WwiseAnyRef = *WwiseRef;
  381. }
  382. OutReconcileItems.AddUnique(ReconcileItem);
  383. }
  384. }
  385. if (bFirstLevel)
  386. {
  387. GetAssetChanges(OutReconcileItems, OperationFlags);
  388. }
  389. }
  390. bool FWwiseReconcile::RenameExistingAssets(FScopedSlowTask& SlowTask)
  391. {
  392. #if !UE_5_0_OR_LATER
  393. if(IsRunningCommandlet())
  394. {
  395. UE_LOG(LogWwiseReconcile, Error, TEXT("Renaming through the commandlet is only supported with Unreal Engine 5. Use the Wwise Browser to rename assets instead."));
  396. SlowTask.EnterProgressFrame((float)AssetsToRename.Num() / GetNumberOfAssets());
  397. return false;
  398. }
  399. #endif
  400. TArray<FAssetRenameData> AssetsToRenameData;
  401. for (const auto& AssetData : AssetsToRename)
  402. {
  403. if(SlowTask.ShouldCancel())
  404. {
  405. return false;
  406. }
  407. auto AkAudioAsset = Cast<UAkAudioType>(AssetData.GetAsset());
  408. if (!LIKELY(AkAudioAsset))
  409. {
  410. UE_LOG(LogWwiseReconcile, Error, TEXT("Failed to rename Wwise asset %s."), *AssetData.AssetName.ToString());
  411. continue;
  412. }
  413. FName NewAssetName = AkAudioAsset->GetAssetDefaultName();
  414. FString NewAssetPath = AssetData.PackagePath.ToString() / NewAssetName.ToString() + "." + NewAssetName.ToString();
  415. int MaxPath = 0;
  416. //Windows Max Path Length can be 32767 while the max path length for cooking remains 260.
  417. #if PLATFORM_WINDOWS
  418. MaxPath = MAX_PATH;
  419. #else
  420. MaxPath = FPlatformMisc::GetMaxPathLength();
  421. #endif
  422. if (NewAssetPath.Len() > MaxPath || NewAssetPath.Len() >= NAME_SIZE)
  423. {
  424. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not rename asset '%s' to '%s', path exceeds Platform Max Path Length (%i). Please import this asset manually."), *AssetData.AssetName.ToString(), *NewAssetName.ToString(), MaxPath);
  425. continue;
  426. }
  427. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  428. #if UE_5_1_OR_LATER
  429. FAssetData Asset = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(NewAssetPath);
  430. #else
  431. FAssetData Asset = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(FName(NewAssetPath));
  432. #endif
  433. if(!Asset.IsValid())
  434. {
  435. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Renaming Wwise asset %s to %s."), *AssetData.AssetName.ToString(), *NewAssetName.ToString());
  436. // FIXME If the package has been changed, the asset remains in the old folder
  437. FAssetRenameData AssetRenameData(AkAudioAsset, AssetData.PackagePath.ToString(), NewAssetName.ToString());
  438. AssetsToRenameData.Add(AssetRenameData);
  439. }
  440. else
  441. {
  442. UE_LOG(LogWwiseReconcile, Warning, TEXT("Asset %s already exists at %s."), *NewAssetName.ToString(), *AssetData.PackagePath.ToString());
  443. }
  444. }
  445. auto AssetToolsModule = &FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
  446. if (!LIKELY(AssetToolsModule))
  447. {
  448. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not load the AssetTools Module"));
  449. return false;
  450. }
  451. if (AssetsToRenameData.Num() > 0 && !AssetToolsModule->Get().RenameAssets(AssetsToRenameData))
  452. {
  453. UE_LOG(LogWwiseReconcile, Error, TEXT("Failed to rename updated Wwise assets."));
  454. return false;
  455. }
  456. SlowTask.EnterProgressFrame((float)AssetsToRename.Num() / GetNumberOfAssets());
  457. return true;
  458. }
  459. int FWwiseReconcile::GetNumberOfAssets()
  460. {
  461. return AssetsToDelete.Num() + AssetsToCreate.Num() + AssetsToRename.Num() + AssetsToUpdate.Num();
  462. }
  463. int32 FWwiseReconcile::DeleteAssets(FScopedSlowTask& SlowTask)
  464. {
  465. check(IsInGameThread());
  466. if (AssetsToDelete.Num() == 0)
  467. {
  468. return 0;
  469. }
  470. TArray<UObject*> ObjectsToDelete;
  471. for (const auto& Asset : AssetsToDelete)
  472. {
  473. bool bReferenced = false;
  474. bool bReferencedByUndo = false;
  475. ObjectTools::GatherObjectReferencersForDeletion(Asset.GetAsset(), bReferenced, bReferencedByUndo);
  476. if(SlowTask.ShouldCancel())
  477. {
  478. return 0;
  479. }
  480. if(!bReferenced && !bReferencedByUndo)
  481. {
  482. ObjectsToDelete.Add(Asset.GetAsset());
  483. }
  484. else
  485. {
  486. UE_LOG(LogWwiseReconcile, Warning, TEXT("Asset %s is referenced and will not be deleted. Please delete this asset manually."), *Asset.AssetName.ToString());
  487. }
  488. }
  489. int32 NumDeletedObjects = ObjectTools::ForceDeleteObjects(ObjectsToDelete, false);
  490. SlowTask.EnterProgressFrame((float)ObjectsToDelete.Num() / (float)GetNumberOfAssets());
  491. if (NumDeletedObjects != ObjectsToDelete.Num())
  492. {
  493. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not delete assets. Verify that none of the assets are still being referenced."))
  494. }
  495. return NumDeletedObjects;
  496. }
  497. bool FWwiseReconcile::IsAssetOutOfDate(const FAssetData& AssetData, const FWwiseAnyRef& WwiseRef)
  498. {
  499. auto StringGuid = AssetData.TagsAndValues.FindTag(FName("WwiseGuid"));
  500. auto StringShortId = AssetData.TagsAndValues.FindTag(FName("WwiseShortId"));
  501. auto WwiseName = AssetData.TagsAndValues.FindTag(FName("WwiseName"));
  502. uint32 ShortId = 0;
  503. if(StringShortId.IsSet())
  504. {
  505. ShortId = uint32(FCString::Atoi(*StringShortId.GetValue()));
  506. }
  507. if (StringGuid.IsSet())
  508. {
  509. const auto AssetGuid = WwiseRef.GetGuid();
  510. const auto AssetId = WwiseRef.GetId();
  511. return AssetGuid != FGuid(StringGuid.AsString()) ||
  512. AssetId != ShortId ||
  513. WwiseName != WwiseRef.GetName().ToString();
  514. }
  515. return false;
  516. }
  517. void FWwiseReconcile::Init()
  518. {
  519. if (!Instance)
  520. {
  521. Instance = new FWwiseReconcile();
  522. }
  523. }
  524. void FWwiseReconcile::Terminate()
  525. {
  526. if (Instance)
  527. {
  528. delete Instance;
  529. }
  530. }
  531. FWwiseReconcile* FWwiseReconcile::Get()
  532. {
  533. return Instance;
  534. }
  535. TArray<FAssetData> FWwiseReconcile::CreateAssets(FScopedSlowTask& SlowTask)
  536. {
  537. check(IsInGameThread());
  538. TArray<UPackage*> PackagesToSave;
  539. TArray<FAssetData> NewAssets;
  540. for (const auto& Asset : AssetsToCreate)
  541. {
  542. if(SlowTask.ShouldCancel())
  543. {
  544. return NewAssets;
  545. }
  546. const FWwiseAnyRef WwiseRef = *Asset.WwiseAnyRef;
  547. FName AssetName = AkUnrealAssetDataHelper::GetAssetDefaultName(&WwiseRef);
  548. FString AssetPackagePath = AkUnrealAssetDataHelper::GetAssetDefaultPackagePath(&WwiseRef);
  549. UClass* NewAssetClass = GetUClassFromWwiseRefType(WwiseRef.GetType());
  550. if (!NewAssetClass)
  551. {
  552. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not determine which type of asset to create for '%s' in '%s'."), *AssetName.ToString(), *AssetPackagePath);
  553. continue;
  554. }
  555. int PackageLength = AssetViewUtils::GetPackageLengthForCooking(AssetPackagePath / AssetName.ToString(), FEngineBuildSettings::IsInternalBuild());
  556. int MaxPath = AssetViewUtils::GetMaxCookPathLen();
  557. if (PackageLength > MaxPath || PackageLength >= NAME_SIZE)
  558. {
  559. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not create asset '%s' at location '%s', path exceeds Platform Max Path Length (%i). Please import this asset manually."), *AssetName.ToString(), *AssetPackagePath, MaxPath);
  560. continue;
  561. }
  562. FString NewAssetPath = AssetPackagePath / AssetName.ToString() + "." + AssetName.ToString();
  563. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  564. #if UE_5_1_OR_LATER
  565. FAssetData AssetFound = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(NewAssetPath);
  566. #else
  567. FAssetData AssetFound = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(FName(NewAssetPath));
  568. #endif
  569. if(AssetFound.IsValid())
  570. {
  571. UE_LOG(LogWwiseReconcile, Warning, TEXT("Asset %s already exists at %s."), *AssetName.ToString(), *AssetPackagePath);
  572. continue;
  573. }
  574. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Creating new asset '%s' in '%s'."), *AssetName.ToString(), *AssetPackagePath);
  575. auto AssetToolsModule = &FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
  576. if (!LIKELY(AssetToolsModule))
  577. {
  578. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not load the AssetRegistry Module"));
  579. return {};
  580. }
  581. UAkAudioType* NewAkAudioObject = Cast<UAkAudioType>(
  582. AssetToolsModule->Get().CreateAsset(AssetName.ToString(), AssetPackagePath, NewAssetClass, nullptr));
  583. if (!NewAkAudioObject)
  584. {
  585. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not save asset %s"), *AssetName.ToString());
  586. continue;
  587. }
  588. NewAkAudioObject->FillInfo(WwiseRef);
  589. NewAssets.Add(FAssetData(NewAkAudioObject));
  590. UE_LOG(LogWwiseReconcile, Verbose, TEXT("Created asset %s"), *AssetName.ToString());
  591. PackagesToSave.Add(NewAkAudioObject->GetPackage());
  592. SlowTask.EnterProgressFrame();
  593. }
  594. if (!UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, false))
  595. {
  596. UE_LOG(LogWwiseReconcile, Error, TEXT("Could not save packages"));
  597. return {};
  598. }
  599. return NewAssets;
  600. }
  601. #undef LOCTEXT