AssetUtils.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/RPI.Edit/Common/AssetUtils.h>
  9. #include <AzCore/IO/IOUtils.h>
  10. #include <AzCore/IO/Path/Path.h>
  11. #include <AzCore/IO/FileIO.h>
  12. #include <AzCore/std/string/regex.h>
  13. #include <AzCore/Utils/Utils.h>
  14. #include <AzFramework/StringFunc/StringFunc.h>
  15. #include <AzQtComponents/Components/Widgets/FileDialog.h>
  16. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  17. namespace AZ
  18. {
  19. namespace RPI
  20. {
  21. namespace AssetUtils
  22. {
  23. AZStd::string GetProductPathByAssetId(const AZ::Data::AssetId& assetId)
  24. {
  25. AZStd::string productPath;
  26. if (assetId.IsValid())
  27. {
  28. Data::AssetCatalogRequestBus::BroadcastResult(productPath, &Data::AssetCatalogRequests::GetAssetPathById, assetId);
  29. AZ::StringFunc::Path::Normalize(productPath);
  30. }
  31. return productPath;
  32. }
  33. AZStd::string GetSourcePathByAssetId(const AZ::Data::AssetId& assetId)
  34. {
  35. AZStd::string sourcePath;
  36. if (assetId.IsValid())
  37. {
  38. bool sourceFileFound = false;
  39. AZ::Data::AssetInfo assetInfo;
  40. AZStd::string watchFolder;
  41. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  42. sourceFileFound,
  43. &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourceUUID,
  44. assetId.m_guid,
  45. assetInfo,
  46. watchFolder);
  47. if (sourceFileFound)
  48. {
  49. AzFramework::StringFunc::Path::ConstructFull(
  50. watchFolder.c_str(), assetInfo.m_relativePath.c_str(), sourcePath, true);
  51. }
  52. }
  53. return sourcePath;
  54. }
  55. AZStd::string ResolvePathReference(
  56. const AZStd::string& originatingSourceFilePath, const AZStd::string& referencedSourceFilePath)
  57. {
  58. // Convert incoming paths containing aliases into absolute paths
  59. AZ::IO::FixedMaxPath originatingPath;
  60. AZ::IO::FileIOBase::GetInstance()->ReplaceAlias(originatingPath, AZ::IO::PathView{ originatingSourceFilePath });
  61. originatingPath = originatingPath.LexicallyNormal();
  62. AZ::IO::FixedMaxPath referencedPath;
  63. AZ::IO::FileIOBase::GetInstance()->ReplaceAlias(referencedPath, AZ::IO::PathView{ referencedSourceFilePath });
  64. referencedPath = referencedPath.LexicallyNormal();
  65. // If the referenced path is empty or absolute then the path does not need to be resolved and can be returned immediately
  66. if (referencedPath.empty() || referencedPath.IsAbsolute())
  67. {
  68. return referencedPath.String();
  69. }
  70. // Compose a path from the originating source file folder to the referenced source file
  71. AZ::IO::FixedMaxPath combinedPath = originatingPath.ParentPath();
  72. combinedPath /= referencedPath;
  73. combinedPath = combinedPath.LexicallyNormal();
  74. // Try to find the source file starting at the originatingSourceFilePath, and return the full path
  75. bool pathFound = false;
  76. AZ::Data::AssetInfo sourceInfo;
  77. AZStd::string rootFolder;
  78. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  79. pathFound,
  80. &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
  81. combinedPath.c_str(),
  82. sourceInfo,
  83. rootFolder);
  84. if (pathFound)
  85. {
  86. // Construct fails if either of the rootFolder or the referencedPath is empty. For some testing purposes, root can be
  87. // empty.
  88. AZStd::string fullSourcePath;
  89. if (AzFramework::StringFunc::Path::ConstructFull(rootFolder.c_str(), combinedPath.c_str(), fullSourcePath, true))
  90. {
  91. return fullSourcePath;
  92. }
  93. return combinedPath.String();
  94. }
  95. // Try to find the source file starting at the asset root, and return the full path
  96. pathFound = false;
  97. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  98. pathFound,
  99. &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
  100. referencedPath.c_str(),
  101. sourceInfo,
  102. rootFolder);
  103. if (pathFound)
  104. {
  105. // Construct fails if either of the rootFolder or the referencedPath is empty. For some testing purposes, root can be
  106. // empty.
  107. AZStd::string fullSourcePath;
  108. if (AzFramework::StringFunc::Path::ConstructFull(rootFolder.c_str(), referencedPath.c_str(), fullSourcePath, true))
  109. {
  110. return fullSourcePath;
  111. }
  112. return referencedPath.String();
  113. }
  114. // If no source file was found, return the original reference path. Something else will probably fail and report errors.
  115. return referencedPath.String();
  116. }
  117. Outcome<Data::AssetId> MakeAssetId(const AZStd::string& sourcePath, uint32_t productSubId, TraceLevel reporting)
  118. {
  119. AZ::IO::FixedMaxPath sourcePathNoAlias;
  120. AZ::IO::FileIOBase::GetInstance()->ReplaceAlias(sourcePathNoAlias, AZ::IO::PathView{ sourcePath });
  121. sourcePathNoAlias = sourcePathNoAlias.LexicallyNormal();
  122. bool assetFound = false;
  123. AZ::Data::AssetInfo sourceInfo;
  124. AZStd::string watchFolder;
  125. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  126. assetFound,
  127. &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath,
  128. sourcePathNoAlias.c_str(),
  129. sourceInfo,
  130. watchFolder);
  131. if (!assetFound)
  132. {
  133. AssetUtilsInternal::ReportIssue(
  134. reporting, AZStd::string::format("Could not find asset for source file [%s]", sourcePath.c_str()).c_str());
  135. return AZ::Failure();
  136. }
  137. return AZ::Success(AZ::Data::AssetId(sourceInfo.m_assetId.m_guid, productSubId));
  138. }
  139. Outcome<Data::AssetId> MakeAssetId(
  140. const AZStd::string& originatingSourcePath,
  141. const AZStd::string& referencedSourceFilePath,
  142. uint32_t productSubId,
  143. TraceLevel reporting)
  144. {
  145. const AZStd::string resolvedPath = ResolvePathReference(originatingSourcePath, referencedSourceFilePath);
  146. return MakeAssetId(resolvedPath, productSubId, reporting);
  147. }
  148. AZStd::string SanitizeFileName(AZStd::string filename)
  149. {
  150. filename = AZStd::regex_replace(filename, AZStd::regex{R"([^a-zA-Z0-9_\-\.]+)"}, "_"); // Replace unsupported characters
  151. filename = AZStd::regex_replace(filename, AZStd::regex{"\\.\\.+"}, "_"); // Don't allow multiple dots, that could mess up extensions
  152. filename = AZStd::regex_replace(filename, AZStd::regex{"__+"}, "_"); // Prevent multiple underscores being introduced by the above rules
  153. filename = AZStd::regex_replace(filename, AZStd::regex{"\\.+$"}, ""); // Don't allow dots at the end, that could mess up extensions
  154. // These rules should be compatible with those in FileDialog::GetSaveFileName, though the replacement rules here may be a bit more strict than the FileDialog validation.
  155. AZ_Assert(AzQtComponents::FileDialog::IsValidFileName(filename.c_str()), "The rules of AssetUtils::SanitizeFileName() must be compatible with AzQtComponents::FileDialog.");
  156. return filename;
  157. }
  158. } // namespace AssetUtils
  159. } // namespace RPI
  160. } // namespace AZ