You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp

322 lines
14 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Module/ModuleManagerBus.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Asset/AssetUtils.h>
#include <QString>
namespace AzToolsFramework::AssetUtils::Internal
{
constexpr const char* AssetProcessorSettingsKey{ "/Amazon/AssetProcessor/Settings" };
constexpr const char* AssetConfigPlatformDir = "AssetProcessorConfig";
constexpr const char* RestrictedPlatformDir = "restricted";
AZStd::vector<AZ::IO::Path> FindWildcardMatches(AZStd::string_view sourceFolder, AZStd::string_view relativeName)
{
if (relativeName.empty())
{
return {};
}
const int pathLen = sourceFolder.length() + 1;
AZ::IO::Path sourceWildcard{ sourceFolder };
AZStd::vector<AZ::IO::Path> returnList;
// Walks the sourceFolder and subdirectories to search for the relativeName as a wild card using a breathe-first search
AZStd::queue<AZ::IO::Path> searchFolders;
searchFolders.push(AZStd::move(sourceWildcard));
while (!searchFolders.empty())
{
const AZ::IO::Path& searchFolder = searchFolders.front();
AZ::IO::SystemFile::FindFileCB findFiles = [&returnList, &relativeName, &searchFolder, &searchFolders](AZStd::string_view fileView, bool isFile) -> bool
{
if (fileView == "." || fileView == "..")
{
return true;
}
if (isFile)
{
if (AZStd::wildcard_match(relativeName, fileView))
{
returnList.emplace_back(searchFolder / fileView);
}
}
else
{
// Use the current search directory to append the fileView directory wild card
searchFolders.push(searchFolder / fileView);
}
return true;
};
AZ::IO::SystemFile::FindFiles((searchFolder / "*").c_str(), findFiles);
searchFolders.pop();
}
return returnList;
}
void AddGemConfigFiles(const AZStd::vector<AzFramework::GemInfo>& gemInfoList, AZStd::vector<AZ::IO::Path>& configFiles)
{
const char* AssetProcessorGemConfigIni = "AssetProcessorGemConfig.ini";
const char* AssetProcessorGemConfigSetreg = "AssetProcessorGemConfig.setreg";
for (const AzFramework::GemInfo& gemElement : gemInfoList)
{
for (const AZ::IO::Path& gemAbsoluteSourcePath : gemElement.m_absoluteSourcePaths)
{
configFiles.push_back(gemAbsoluteSourcePath / AssetProcessorGemConfigIni);
configFiles.push_back(gemAbsoluteSourcePath / AssetProcessorGemConfigSetreg);
}
}
}
}
namespace AzToolsFramework::AssetUtils
{
// Visitor for reading the "/Amazon/AssetProcessor/Settings/Platforms" entries from the Settings Registry
struct EnabledPlatformsVisitor
: AZ::SettingsRegistryInterface::Visitor
{
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value);
AZStd::vector<AZStd::string> m_enabledPlatforms;
};
void EnabledPlatformsVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
{
if (value == "enabled")
{
m_enabledPlatforms.emplace_back(valueName);
}
else if (value == "disabled")
{
auto platformEntrySearch = [&valueName](AZStd::string_view platformEntry)
{
return valueName == platformEntry;
};
auto removeIt = AZStd::remove_if(m_enabledPlatforms.begin(), m_enabledPlatforms.end(), platformEntrySearch);
m_enabledPlatforms.erase(removeIt, m_enabledPlatforms.end());
}
}
void ReadEnabledPlatformsFromSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry,
AZStd::vector<AZStd::string>& enabledPlatforms)
{
// note that the current host platform is enabled by default.
enabledPlatforms.push_back(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
// in the setreg the platform can be missing (commented out)
// in which case it is disabled implicitly by not being there
// or it can be 'disabled' which means that it is explicitly disabled.
// or it can be 'enabled' which means that it is explicitly enabled.
EnabledPlatformsVisitor visitor;
settingsRegistry.Visit(visitor, AZ::SettingsRegistryInterface::FixedValueString(Internal::AssetProcessorSettingsKey) + "/Platforms");
enabledPlatforms.insert(enabledPlatforms.end(), AZStd::make_move_iterator(visitor.m_enabledPlatforms.begin()),
AZStd::make_move_iterator(visitor.m_enabledPlatforms.end()));
}
AZStd::vector<AZStd::string> GetEnabledPlatforms(AZ::SettingsRegistryInterface& settingsRegistry,
const AZStd::vector<AZ::IO::Path>& configFiles)
{
AZStd::vector<AZStd::string> enabledPlatforms;
for (const auto& configFile : configFiles)
{
if (AZ::IO::SystemFile::Exists(configFile.c_str()))
{
// If the config file is a settings registry file use the SettingsRegistryInterface MergeSettingsFile function
// otherwise use the SettingsRegistryMergeUtils MergeSettingsToRegistry_ConfigFile function to merge an INI-style
// file to the settings registry
if (configFile.Extension() == ".setreg")
{
settingsRegistry.MergeSettingsFile(configFile.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch);
}
else
{
AZ::SettingsRegistryMergeUtils::ConfigParserSettings configParserSettings;
configParserSettings.m_registryRootPointerPath = Internal::AssetProcessorSettingsKey;
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ConfigFile(settingsRegistry, configFile.Native(), configParserSettings);
}
}
}
ReadEnabledPlatformsFromSettingsRegistry(settingsRegistry, enabledPlatforms);
return enabledPlatforms;
}
constexpr const char* AssetProcessorPlatformConfigFileName = "AssetProcessorPlatformConfig.ini";
constexpr const char* AssetProcessorPlatformConfigSetreg = "AssetProcessorPlatformConfig.setreg";
bool AddPlatformConfigFilePaths(AZStd::string_view engineRoot, AZStd::vector<AZ::IO::Path>& configFilePaths)
{
auto restrictedRoot = AZ::IO::Path{ engineRoot } / Internal::RestrictedPlatformDir;
// first collect public platform configs
AZStd::vector<AZ::IO::Path> platformDirs{ AZ::IO::Path{ engineRoot } / Internal::AssetConfigPlatformDir };
// then collect restricted platform configs
// Append the AssetConfigPlatformDir value to each directory
AZ::IO::SystemFile::FindFileCB findRestrictedAssetConfigs = [&restrictedRoot, &platformDirs](AZStd::string_view fileView, bool isFile) -> bool
{
if (fileView != "." && fileView != "..")
{
if (!isFile)
{
platformDirs.push_back(restrictedRoot / fileView / Internal::AssetConfigPlatformDir);
}
}
return true;
};
AZ::IO::SystemFile::FindFiles((restrictedRoot / "*").c_str(), findRestrictedAssetConfigs);
// Iterator over all platform directories for platform config files
AZStd::vector<AZ::IO::Path> allPlatformConfigs;
for (const auto& platformDir : platformDirs)
{
for (const char* configPath : { AssetProcessorPlatformConfigFileName, AssetProcessorPlatformConfigSetreg })
{
AZStd::vector<AZ::IO::Path> platformConfigs = Internal::FindWildcardMatches(platformDir.Native(), configPath);
allPlatformConfigs.insert(allPlatformConfigs.end(), AZStd::make_move_iterator(platformConfigs.begin()), AZStd::make_move_iterator(platformConfigs.end()));
}
}
const bool platformConfigFilePathsAdded = !allPlatformConfigs.empty();
configFilePaths.insert(configFilePaths.end(), AZStd::make_move_iterator(allPlatformConfigs.begin()), AZStd::make_move_iterator(allPlatformConfigs.end()));
return platformConfigFilePathsAdded;
}
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath,
bool addPlatformConfigs, bool addGemsConfigs, AZ::SettingsRegistryInterface* settingsRegistry)
{
constexpr const char* AssetProcessorGamePlatformConfigFileName = "AssetProcessorGamePlatformConfig.ini";
constexpr const char* AssetProcessorGamePlatformConfigSetreg = "AssetProcessorGamePlatformConfig.setreg";
AZStd::vector<AZ::IO::Path> configFiles;
// Add the AssetProcessorPlatformConfig setreg file at the engine root
configFiles.push_back(AZ::IO::Path(engineRoot) / AssetProcessorPlatformConfigSetreg);
if (addPlatformConfigs)
{
AddPlatformConfigFilePaths(engineRoot, configFiles);
}
if (addGemsConfigs)
{
AZStd::vector<AzFramework::GemInfo> gemInfoList;
if (!AzFramework::GetGemsInfo(gemInfoList, *settingsRegistry))
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to read gems from project folder(%s).\n", projectPath);
return {};
}
Internal::AddGemConfigFiles(gemInfoList, configFiles);
}
AZ::IO::Path assetRootDir(assetRoot);
assetRootDir /= projectPath;
AZ::IO::Path projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigFileName;
configFiles.push_back(projectConfigFile);
// Add a file entry for the Project AssetProcessor setreg file
projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigSetreg;
configFiles.push_back(projectConfigFile);
return configFiles;
}
bool UpdateFilePathToCorrectCase(AZStd::string_view rootPath, AZStd::string& relPathFromRoot)
{
AZ::StringFunc::Path::Normalize(relPathFromRoot);
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
AZStd::string validatedPath;
if (rootPath.empty())
{
AzFramework::ApplicationRequests::Bus::BroadcastResult(validatedPath, &AzFramework::ApplicationRequests::GetEngineRoot);
}
else
{
validatedPath = rootPath;
}
bool success = true;
for (int idx = 0; idx < tokens.size(); idx++)
{
AZStd::string element = tokens[idx];
bool foundAMatch = false;
AZ::IO::FileIOBase::GetInstance()->FindFiles(validatedPath.c_str(), "*", [&](const char* file)
{
if (idx != tokens.size() - 1 && !AZ::IO::FileIOBase::GetInstance()->IsDirectory(file))
{
// only the last token is supposed to be a filename, we can skip filenames before that
return true;
}
AZStd::string absFilePath(file);
AZ::StringFunc::Path::Normalize(absFilePath);
auto found = absFilePath.rfind(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
size_t startingPos = found + 1;
if (found != AZStd::string::npos && absFilePath.size() > startingPos)
{
AZStd::string componentName = AZStd::string(absFilePath.begin() + startingPos, absFilePath.end());
if (AZ::StringFunc::Equal(componentName.c_str(), tokens[idx].c_str()))
{
tokens[idx] = componentName;
foundAMatch = true;
return false;
}
}
return true;
});
if (!foundAMatch)
{
success = false;
break;
}
AZStd::string absoluteFilePath;
AZ::StringFunc::Path::ConstructFull(validatedPath.c_str(), element.c_str(), absoluteFilePath);
validatedPath = absoluteFilePath; // go one step deeper.
}
if (success)
{
relPathFromRoot.clear();
AZ::StringFunc::Join(relPathFromRoot, tokens.begin(), tokens.end(), AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
}
return success;
}
} //namespace AzToolsFramework::AssetUtils