3
0

Utilities.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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 <AzCore/Serialization/SerializeContext.h> // Needs to be on top due to missing include in AssetSerializer.h
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Casting/numeric_cast.h>
  11. #include <AzCore/Debug/Trace.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/IO/SystemFile.h>
  14. #include <AzCore/Module/Module.h>
  15. #include <AzCore/Module/ModuleManagerBus.h>
  16. #include <AzCore/Serialization/Json/JsonSerialization.h>
  17. #include <AzCore/Serialization/Json/RegistrationContext.h>
  18. #include <AzCore/Settings/CommandLine.h>
  19. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  20. #include <AzCore/std/containers/queue.h>
  21. #include <AzCore/std/functional.h>
  22. #include <AzCore/std/string/wildcard.h>
  23. #include <AzCore/StringFunc/StringFunc.h>
  24. #include <Application.h>
  25. #include <Utilities.h>
  26. namespace AZ::SerializeContextTools
  27. {
  28. AZStd::string Utilities::ReadOutputTargetFromCommandLine(Application& application, const char* defaultFileOrFolder)
  29. {
  30. AZ::IO::Path outputPath;
  31. if (application.GetAzCommandLine()->HasSwitch("output"))
  32. {
  33. outputPath.Native() = application.GetAzCommandLine()->GetSwitchValue("output", 0);
  34. }
  35. else
  36. {
  37. outputPath = defaultFileOrFolder;
  38. }
  39. return AZStd::move(outputPath.Native());
  40. }
  41. AZStd::vector<AZStd::string> Utilities::ReadFileListFromCommandLine(Application& application, AZStd::string_view switchName)
  42. {
  43. AZStd::vector<AZStd::string> result;
  44. const AZ::CommandLine* commandLine = application.GetAzCommandLine();
  45. if (!commandLine)
  46. {
  47. AZ_Error("SerializeContextTools", false, "Command line not available.");
  48. return result;
  49. }
  50. if (!commandLine->HasSwitch(switchName))
  51. {
  52. AZ_Error("SerializeContextTools", false, "Missing command line argument '-%*s' which should contain the requested files.",
  53. aznumeric_cast<int>(switchName.size()), switchName.data());
  54. return result;
  55. }
  56. AZStd::vector<AZStd::string_view> fileList;
  57. auto AppendFileList = [&fileList](AZStd::string_view filename)
  58. {
  59. fileList.emplace_back(filename);
  60. };
  61. for (size_t switchIndex{}; switchIndex < commandLine->GetNumSwitchValues(switchName); ++switchIndex)
  62. {
  63. AZ::StringFunc::TokenizeVisitor(commandLine->GetSwitchValue(switchName, switchIndex), AppendFileList, ";");
  64. }
  65. return Utilities::ExpandFileList(".", fileList);
  66. }
  67. AZStd::vector<AZStd::string> Utilities::ExpandFileList(const char* root, const AZStd::vector<AZStd::string_view>& fileList)
  68. {
  69. AZStd::vector<AZStd::string> result;
  70. result.reserve(fileList.size());
  71. for (const AZStd::string_view& file : fileList)
  72. {
  73. if (HasWildCard(file))
  74. {
  75. AZ::IO::FixedMaxPath filterPath{ file };
  76. AZ::IO::FixedMaxPath parentPath{ filterPath.ParentPath() };
  77. if (filterPath.IsRelative())
  78. {
  79. parentPath = AZ::IO::FixedMaxPath(root) / parentPath;
  80. }
  81. AZ::IO::PathView filterFilename = filterPath.Filename();
  82. if (filterFilename.empty())
  83. {
  84. AZ_Error("SerializeContextTools", false, "Unable to get folder path for '%.*s'.",
  85. aznumeric_cast<int>(filterFilename.Native().size()), filterFilename.Native().data());
  86. continue;
  87. }
  88. AZStd::queue<AZ::IO::FixedMaxPath> pendingFolders;
  89. pendingFolders.push(AZStd::move(parentPath));
  90. while (!pendingFolders.empty())
  91. {
  92. const AZ::IO::FixedMaxPath& filterFolder = pendingFolders.front();
  93. auto callback = [&pendingFolders, &filterFolder, &filterFilename, &result](AZ::IO::PathView item, bool isFile) -> bool
  94. {
  95. if (item == "." || item == "..")
  96. {
  97. return true;
  98. }
  99. AZ::IO::FixedMaxPath fullPath = filterFolder / item;
  100. if (isFile)
  101. {
  102. if (AZStd::wildcard_match(filterFilename.Native(), item.Native()))
  103. {
  104. result.emplace_back(fullPath.c_str(), fullPath.Native().size());
  105. }
  106. }
  107. else
  108. {
  109. pendingFolders.push(AZStd::move(fullPath));
  110. }
  111. return true;
  112. };
  113. AZ::IO::SystemFile::FindFiles((filterFolder / "*").c_str(), callback);
  114. pendingFolders.pop();
  115. }
  116. }
  117. else
  118. {
  119. AZ::IO::FixedMaxPath filePath{ file };
  120. if (filePath.IsRelative())
  121. {
  122. filePath = AZ::IO::FixedMaxPath(root) / filePath;
  123. }
  124. result.emplace_back(filePath.c_str(), filePath.Native().size());
  125. }
  126. }
  127. return result;
  128. }
  129. bool Utilities::HasWildCard(AZStd::string_view string)
  130. {
  131. // Wild cards vary between platforms, but these are the most common ones.
  132. return string.find_first_of("*?[]!@#", 0) != AZStd::string_view::npos;
  133. }
  134. void Utilities::SanitizeFilePath(AZStd::string& filePath)
  135. {
  136. auto invalidCharacters = [](char letter)
  137. {
  138. return
  139. letter == ':' || letter == '"' || letter == '\'' ||
  140. letter == '{' || letter == '}' ||
  141. letter == '<' || letter == '>';
  142. };
  143. AZStd::replace_if(filePath.begin(), filePath.end(), invalidCharacters, '_');
  144. }
  145. bool Utilities::IsSerializationPrimitive(const AZ::Uuid& classId)
  146. {
  147. JsonRegistrationContext* registrationContext;
  148. AZ::ComponentApplicationBus::BroadcastResult(registrationContext, &AZ::ComponentApplicationBus::Events::GetJsonRegistrationContext);
  149. if (!registrationContext)
  150. {
  151. AZ_Error("SerializeContextTools", false, "Failed to retrieve json registration context.");
  152. return false;
  153. }
  154. return registrationContext->GetSerializerForType(classId) != nullptr;
  155. }
  156. AZStd::vector<AZ::Uuid> Utilities::GetSystemComponents(const Application& application)
  157. {
  158. AZStd::vector<AZ::Uuid> result = application.GetRequiredSystemComponents();
  159. auto getModuleSystemComponentsCB = [&result](const ModuleData& moduleData) -> bool
  160. {
  161. if (AZ::Module* module = moduleData.GetModule())
  162. {
  163. AZ::ComponentTypeList moduleRequiredComponents = module->GetRequiredSystemComponents();
  164. result.reserve(result.size() + moduleRequiredComponents.size());
  165. result.insert(result.end(), moduleRequiredComponents.begin(), moduleRequiredComponents.end());
  166. }
  167. return true;
  168. };
  169. ModuleManagerRequestBus::Broadcast(&ModuleManagerRequests::EnumerateModules, getModuleSystemComponentsCB);
  170. return result;
  171. }
  172. bool Utilities::InspectSerializedFile(
  173. const char* filePath,
  174. SerializeContext* sc,
  175. const ObjectStream::ClassReadyCB& classCallback,
  176. Data::AssetFilterCB assetFilterCallback)
  177. {
  178. if (!AZ::IO::FileIOBase::GetInstance()->Exists(filePath))
  179. {
  180. AZ_Error("Verify", false, "Unable to open file '%s' as it doesn't exist.", filePath);
  181. return false;
  182. }
  183. AZ::IO::HandleType fileHandle;
  184. auto openResult = AZ::IO::FileIOBase::GetInstance()->Open(filePath, AZ::IO::OpenMode::ModeRead, fileHandle);
  185. if (!openResult)
  186. {
  187. AZ_Error("Verify", false, "File '%s' could not be opened.", filePath);
  188. return false;
  189. }
  190. u64 fileLength = 0;
  191. auto sizeResult = AZ::IO::FileIOBase::GetInstance()->Size(fileHandle, fileLength);
  192. if (!sizeResult || (fileLength == 0))
  193. {
  194. AZ_Error("Verify", false, "File '%s' doesn't have content.", filePath);
  195. return false;
  196. }
  197. AZStd::vector<u8> data;
  198. data.resize_no_construct(fileLength);
  199. u64 bytesRead = 0;
  200. auto readResult = AZ::IO::FileIOBase::GetInstance()->Read(fileHandle, data.data(), fileLength, true, &bytesRead);
  201. if (!readResult || (bytesRead != fileLength))
  202. {
  203. AZ_Error("Verify", false, "Unable to read file '%s'.", filePath);
  204. return false;
  205. }
  206. AZ::IO::FileIOBase::GetInstance()->Close(fileHandle);
  207. AZ::IO::MemoryStream stream(data.data(), fileLength);
  208. ObjectStream::FilterDescriptor filter;
  209. // By default, never load dependencies. That's another file that would need to be processed
  210. // separately from this one.
  211. filter.m_assetCB = assetFilterCallback;
  212. if (!ObjectStream::LoadBlocking(&stream, *sc, classCallback, filter))
  213. {
  214. AZ_Printf("Verify", "Failed to deserialize '%s'\n", filePath);
  215. return false;
  216. }
  217. return true;
  218. }
  219. } // namespace AZ::SerializeContextTools