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/AssetBundler.cpp

900 lines
35 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 <AzToolsFramework/Asset/AssetBundler.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/string/wildcard.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/IO/FileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace
{
//! These struct are used because we have a use case where we only need to hash and
//! override the equals to method for AssetFileInfo class based only on AssetId, which might not be true for other usages of AssetFileInfo class.
struct AssetFileInfoComparatorByAssetId
{
bool operator()(const AzToolsFramework::AssetFileInfo& left, const AzToolsFramework::AssetFileInfo& right) const
{
return left.m_assetId == right.m_assetId;
}
};
struct AssetFileInfoHasherByAssetId
{
size_t operator() (const AzToolsFramework::AssetFileInfo& assetFileInfo) const
{
size_t hashValue = 0;
AZStd::hash_combine(hashValue, assetFileInfo.m_assetId);
return hashValue;
}
};
}
namespace AzToolsFramework
{
const char AssetBundleSettingsFileExtension[] = "bundlesettings";
const char BundleFileExtension[] = "pak";
const char ComparisonRulesFileExtension[] = "rules";
const char ErrorWindowName[] = "AssetBundler";
const char* AssetFileInfoListComparison::ComparisonTypeNames[] = { "delta", "union", "intersection", "complement", "filepattern", "intersectioncount" };
const char* AssetFileInfoListComparison::FilePatternTypeNames[] = { "wildcard", "regex" };
const char DefaultTypeName[] = "default";
const char TokenIdentifier = '$';
void AssetBundleSettings::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetBundleSettings>()
->Version(3)
->Field("AssetFileInfoListPath", &AssetBundleSettings::m_assetFileInfoListPath)
->Field("BundleFilePath", &AssetBundleSettings::m_bundleFilePath)
->Field("BundleVersion", &AssetBundleSettings::m_bundleVersion)
->Field("maxBundleSize", &AssetBundleSettings::m_maxBundleSizeInMB)
->Field("comment", &AssetBundleSettings::m_comment);
}
}
AZ::Outcome<AssetBundleSettings, AZStd::string> AssetBundleSettings::Load(const AZStd::string& filePath)
{
auto fileExtensionOutcome = ValidateBundleSettingsFileExtension(filePath);
if (!fileExtensionOutcome.IsSuccess())
{
return AZ::Failure(fileExtensionOutcome.GetError());
}
AssetBundleSettings assetBundleSettings;
if (!AZ::Utils::LoadObjectFromFileInPlace(filePath.c_str(), assetBundleSettings))
{
return AZ::Failure(AZStd::string::format("Failed to load AssetBundleSettings file (%s) from disk.\n", filePath.c_str()));
}
assetBundleSettings.m_platform = GetPlatformFromAssetInfoFilePath(assetBundleSettings);
return AZ::Success(assetBundleSettings);
}
bool AssetBundleSettings::Save(const AssetBundleSettings& assetBundleSettings, const AZStd::string& destinationFilePath)
{
auto fileExtensionOutcome = ValidateBundleSettingsFileExtension(destinationFilePath);
if (!fileExtensionOutcome.IsSuccess())
{
AZ_Error(ErrorWindowName, false, fileExtensionOutcome.GetError().c_str());
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->Exists(destinationFilePath.c_str()) && AZ::IO::FileIOBase::GetInstance()->IsReadOnly(destinationFilePath.c_str()))
{
AZ_Error(ErrorWindowName, false, "Unable to save bundle settings file (%s): file is marked Read-Only.\n", destinationFilePath.c_str());
return false;
}
if (!IsBundleSettingsFile(destinationFilePath))
{
AZ_Error(ErrorWindowName, false, "Failed to save file (%s) to disk. AssetBundleSettings files must have the extension: %s\n", destinationFilePath.c_str(), AssetBundleSettingsFileExtension);
return false;
}
if (AZ::Utils::SaveObjectToFile(destinationFilePath.c_str(), AZ::DataStream::StreamType::ST_XML, &assetBundleSettings))
{
return true;
}
AZ_Error(ErrorWindowName, false, "Failed to save file (%s) to disk.", destinationFilePath.c_str());
return false;
}
bool AssetBundleSettings::IsBundleSettingsFile(const AZStd::string& filePath)
{
return AzFramework::StringFunc::EndsWith(filePath, AssetBundleSettingsFileExtension);
}
const char* AssetBundleSettings::GetBundleSettingsFileExtension()
{
return AssetBundleSettingsFileExtension;
}
AZ::Outcome<void, AZStd::string> AssetBundleSettings::ValidateBundleSettingsFileExtension(const AZStd::string& path)
{
if (!AzFramework::StringFunc::EndsWith(path, AssetBundleSettingsFileExtension))
{
return AZ::Failure(AZStd::string::format(
"Invalid Bundle Settings file path ( %s ). Invalid file extension, Bundle Settings files can only have ( .%s ) extension.\n",
path.c_str(),
AssetBundleSettingsFileExtension));
}
return AZ::Success();
}
const char* AssetBundleSettings::GetBundleFileExtension()
{
return BundleFileExtension;
}
AZ::Outcome<void, AZStd::string> AssetBundleSettings::ValidateBundleFileExtension(const AZStd::string& path)
{
if (!AzFramework::StringFunc::EndsWith(path, BundleFileExtension))
{
return AZ::Failure(AZStd::string::format(
"Invalid Bundle file path ( %s ). Invalid file extension, Bundles can only have ( .%s ) extension.\n",
path.c_str(),
BundleFileExtension));
}
return AZ::Success();
}
AZ::u64 AssetBundleSettings::GetMaxBundleSizeInMB()
{
return MaxBundleSizeInMB;
}
AZStd::string AssetBundleSettings::GetPlatformFromAssetInfoFilePath(const AssetBundleSettings& assetBundleSettings)
{
return GetPlatformIdentifier(assetBundleSettings.m_assetFileInfoListPath);
}
bool AssetFileInfoListComparison::IsOutputPath(const AZStd::string& filePath)
{
return !filePath.empty() && !IsTokenFile(filePath);
}
bool AssetFileInfoListComparison::IsTokenFile(const AZStd::string& filePath)
{
return !filePath.empty() && filePath[0] == TokenIdentifier;
}
void AssetFileInfoListComparison::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
ComparisonData::Reflect(serializeContext);
serializeContext->Class<AssetFileInfoListComparison>()
->Version(1)
->Field("ComparisonDataListType", &AssetFileInfoListComparison::m_comparisonDataList);
}
}
bool AssetFileInfoListComparison::Save(const AZStd::string& destinationFilePath) const
{
if (!AzFramework::StringFunc::EndsWith(destinationFilePath, ComparisonRulesFileExtension))
{
AZ_Error(ErrorWindowName, false, "Unable to save comparison file (%s). Invalid file extension, comparison files can only have (.%s) extension.\n", destinationFilePath.c_str(), ComparisonRulesFileExtension);
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->Exists(destinationFilePath.c_str()) && AZ::IO::FileIOBase::GetInstance()->IsReadOnly(destinationFilePath.c_str()))
{
AZ_Error(ErrorWindowName, false, "Unable to save comparison file (%s): file is marked Read-Only.\n", destinationFilePath.c_str());
return false;
}
return AZ::Utils::SaveObjectToFile(destinationFilePath, AZ::DataStream::StreamType::ST_XML, this);
}
AZ::Outcome<AssetFileInfoListComparison, AZStd::string> AssetFileInfoListComparison::Load(const AZStd::string& filePath)
{
if (!AzFramework::StringFunc::EndsWith(filePath, ComparisonRulesFileExtension))
{
return AZ::Failure(AZStd::string::format("Unable to load comparison file (%s). Invalid file extension, comparison files can only have (.%s) extension.\n", filePath.c_str(), ComparisonRulesFileExtension));
}
AssetFileInfoListComparison assetFileInfoListComparison;
if (!AZ::Utils::LoadObjectFromFileInPlace(filePath.c_str(), assetFileInfoListComparison))
{
return AZ::Failure(AZStd::string::format("Failed to load AssetFileInfoComparison file (%s) from disk.\n", filePath.c_str()));
}
return AZ::Success(AZStd::move(assetFileInfoListComparison));
}
AZ::Outcome<void, AZStd::string> AssetFileInfoListComparison::CompareAndSaveResults(const AZStd::vector<AZStd::string>& intersectionCountAssetListFiles)
{
AZ::Outcome<AssetFileInfoList, AZStd::string> result = Compare(intersectionCountAssetListFiles);
if (!result.IsSuccess())
{
return AZ::Failure(result.TakeError());
}
return SaveResults();
}
AZ::Outcome<void, AZStd::string> AssetFileInfoListComparison::SaveResults() const
{
for (auto iter = m_assetFileInfoMap.begin(); iter != m_assetFileInfoMap.end(); iter++)
{
if (IsOutputPath(iter->first))
{
if (!AssetFileInfoList::Save(iter->second, iter->first))
{
return AZ::Failure(AZStd::string::format("Failed to save result of comparison operation for file %s.\n", iter->first.c_str()));
}
}
}
return AZ::Success();
}
AZStd::vector<AZStd::string> AssetFileInfoListComparison::GetDestructiveOverwriteFilePaths()
{
AZStd::vector<AZStd::string> existingPaths;
for (auto iter = m_assetFileInfoMap.begin(); iter != m_assetFileInfoMap.end(); iter++)
{
if (IsOutputPath(iter->first) && AZ::IO::FileIOBase::GetInstance()->Exists(iter->first.c_str()))
{
existingPaths.emplace_back(iter->first);
}
}
return existingPaths;
}
AssetFileInfoList AssetFileInfoListComparison::GetComparisonResults(const AZStd::string& comparisonKey)
{
auto comparisonResults = m_assetFileInfoMap.find(comparisonKey);
if (comparisonResults == m_assetFileInfoMap.end())
{
return AssetFileInfoList();
}
return comparisonResults->second;
}
const char* AssetFileInfoListComparison::GetComparisonRulesFileExtension()
{
return ComparisonRulesFileExtension;
}
const char* AssetFileInfoListComparison::GetComparisonTypeName(ComparisonType comparisonType)
{
if (comparisonType == ComparisonType::Default)
{
return DefaultTypeName;
}
return ComparisonTypeNames[aznumeric_cast<AZ::u8>(comparisonType)];
}
const char* AssetFileInfoListComparison::GetFilePatternTypeName(FilePatternType filePatternType)
{
if (filePatternType == FilePatternType::Default)
{
return DefaultTypeName;
}
return FilePatternTypeNames[aznumeric_cast<AZ::u8>(filePatternType)];
}
const char AssetFileInfoListComparison::GetTokenIdentifier()
{
return TokenIdentifier;
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::PopulateAssetFileInfo(const AZStd::string& assetFileInfoPath) const
{
AssetFileInfoList assetFileInfoList;
if (assetFileInfoPath.empty())
{
return AZ::Failure(AZStd::string::format("File path for the first asset file info list is empty.\n"));
}
if (IsOutputPath(assetFileInfoPath))
{
if (!AZ::IO::FileIOBase::GetInstance()->Exists(assetFileInfoPath.c_str()))
{
return AZ::Failure(AZStd::string::format("File ( %s ) does not exists on disk.\n", assetFileInfoPath.c_str()));
}
if (!AZ::Utils::LoadObjectFromFileInPlace(assetFileInfoPath.c_str(), assetFileInfoList))
{
return AZ::Failure(AZStd::string::format("Failed to deserialize file ( %s ).\n", assetFileInfoPath.c_str()));
}
}
else
{
auto found = m_assetFileInfoMap.find(assetFileInfoPath);
if (found != m_assetFileInfoMap.end())
{
assetFileInfoList = found->second;
}
else
{
return AZ::Failure(AZStd::string::format("Failed to find AssetFileInfoList that matches the TAG ( %s ).\n", assetFileInfoPath.c_str()));
}
}
if (!assetFileInfoList.m_fileInfoList.size())
{
return AZ::Failure(AZStd::string::format("File ( %s ) does not contain any assets.\n", assetFileInfoPath.c_str()));
}
return AZ::Success(assetFileInfoList);
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::Compare(const AZStd::vector<AZStd::string>& intersectionCountAssetListFiles)
{
AssetFileInfoList lastAssetFileInfoList;
AZ::Outcome<AssetFileInfoList, AZStd::string> result = AZ::Failure(AZStd::string());
if (m_comparisonDataList.empty())
{
return AZ::Failure(AZStd::string("Comparison failed: no Comparison Steps were provided."));
}
//IntersectionCount Operation cannot be combined with other compare operations
if (m_comparisonDataList[0].m_comparisonType == ComparisonType::IntersectionCount)
{
result = IntersectionCount(intersectionCountAssetListFiles);
if (result.IsSuccess())
{
lastAssetFileInfoList = result.GetValue();
m_assetFileInfoMap[m_comparisonDataList[0].m_output] = result.TakeValue();
}
return AZ::Success(lastAssetFileInfoList);
}
for (const ComparisonData& comparisonStep : m_comparisonDataList)
{
AZ::Outcome<AssetFileInfoList, AZStd::string> assetListOutcome = PopulateAssetFileInfo(comparisonStep.m_firstInput);
if (!assetListOutcome.IsSuccess())
{
return AZ::Failure(assetListOutcome.TakeError());
}
AssetFileInfoList firstAssetList = assetListOutcome.TakeValue();
AssetFileInfoList secondAssetList;
if (comparisonStep.m_comparisonType != ComparisonType::FilePattern)
{
assetListOutcome = PopulateAssetFileInfo(comparisonStep.m_secondInput);
if (!assetListOutcome.IsSuccess())
{
return AZ::Failure(assetListOutcome.TakeError());
}
secondAssetList = assetListOutcome.TakeValue();
}
switch (comparisonStep.m_comparisonType)
{
case ComparisonType::Delta:
{
result = Delta(firstAssetList, secondAssetList);
break;
}
case ComparisonType::Union:
{
result = Union(firstAssetList, secondAssetList);
break;
}
case ComparisonType::Intersection:
{
result = Intersection(firstAssetList, secondAssetList);
break;
}
case ComparisonType::Complement:
{
result = Complement(firstAssetList, secondAssetList);
break;
}
case ComparisonType::FilePattern:
{
result = FilePattern(firstAssetList, comparisonStep);
break;
}
default:
return AZ::Failure(AZStd::string::format("Invalid comparison type ( %s ) specified.\n", ComparisonTypeNames[static_cast<int>(comparisonStep.m_comparisonType)]));
}
if (!result.IsSuccess())
{
return result;
}
lastAssetFileInfoList = result.GetValue();
m_assetFileInfoMap[comparisonStep.m_output] = result.TakeValue();
}
return AZ::Success(lastAssetFileInfoList);
}
AssetFileInfoList AssetFileInfoListComparison::Delta(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the secondAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found != assetIdToAssetFileInfoMap.end())
{
bool isHashEqual = true;
// checking the file hash
for (int idx = 0; idx < AzToolsFramework::AssetFileInfo::s_arraySize; idx++)
{
if (found->second.m_hash[idx] != assetFileInfo.m_hash[idx])
{
isHashEqual = false;
break;
}
}
if (isHashEqual)
{
assetIdToAssetFileInfoMap.erase(found);
}
}
}
AssetFileInfoList assetFileInfoList;
for (auto iter = assetIdToAssetFileInfoMap.begin(); iter != assetIdToAssetFileInfoMap.end(); iter++)
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(iter->second));
}
return assetFileInfoList;
}
AssetFileInfoList AssetFileInfoListComparison::Union(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the secondAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found == assetIdToAssetFileInfoMap.end())
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
}
//populate AssetFileInfoList from map
AssetFileInfoList assetFileInfoList;
for (auto iter = assetIdToAssetFileInfoMap.begin(); iter != assetIdToAssetFileInfoMap.end(); iter++)
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(iter->second));
}
return assetFileInfoList;
}
AssetFileInfoList AssetFileInfoListComparison::Intersection(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the secondAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
AssetFileInfoList assetFileInfoList;
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found != assetIdToAssetFileInfoMap.end())
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(found->second));
}
}
return assetFileInfoList;
}
AssetFileInfoList AssetFileInfoListComparison::Complement(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the firstAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
AssetFileInfoList assetFileInfoList;
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found == assetIdToAssetFileInfoMap.end())
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(assetFileInfo));
}
}
return assetFileInfoList;
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::FilePattern(const AssetFileInfoList& assetFileInfoList, const ComparisonData& comparisonData) const
{
if (comparisonData.m_filePattern.empty())
{
return AZ::Failure(AZStd::string::format("Invalid Comparison Step: %s File Pattern value cannot be empty.\n", GetFilePatternTypeName(comparisonData.m_filePatternType)));
}
bool isWildCard = comparisonData.m_filePatternType == FilePatternType::Wildcard;
AssetFileInfoList assetFileInfoListResult;
for (const AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList)
{
if (isWildCard)
{
if (AZStd::wildcard_match(comparisonData.m_filePattern, assetFileInfo.m_assetRelativePath))
{
assetFileInfoListResult.m_fileInfoList.push_back(assetFileInfo);
}
}
else
{
AZStd::regex regex(comparisonData.m_filePattern.c_str(), AZStd::regex::extended);
if (AZStd::regex_match(assetFileInfo.m_assetRelativePath.c_str(), regex))
{
assetFileInfoListResult.m_fileInfoList.push_back(assetFileInfo);
}
}
}
return AZ::Success(assetFileInfoListResult);
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::IntersectionCount(const AZStd::vector<AZStd::string>& assetFileInfoPathList) const
{
AZStd::unordered_map<AssetFileInfo, unsigned int, AssetFileInfoHasherByAssetId, AssetFileInfoComparatorByAssetId> assetCountMap;
for (const auto& absoluteAssetFileInfoPath : assetFileInfoPathList)
{
AZ::Outcome<AssetFileInfoList, AZStd::string> firstResult = PopulateAssetFileInfo(absoluteAssetFileInfoPath);
if (!firstResult.IsSuccess())
{
return AZ::Failure(firstResult.GetError());
}
AssetFileInfoList firstAssetFileInfoList = firstResult.TakeValue();
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto assetFound = assetCountMap.find(assetFileInfo);
if (assetFound == assetCountMap.end())
{
assetCountMap.insert(AZStd::make_pair(assetFileInfo, 1));
}
else
{
assetFound->second++;
}
}
}
// Loop over the map and create a assetFileInfo of assets that appeared at least the number of times specified by the user
AssetFileInfoList outputAssetFileInfoList;
for (auto iter = assetCountMap.begin(); iter != assetCountMap.end(); iter++)
{
if (iter->second >= m_comparisonDataList[0].m_intersectionCount)
{
outputAssetFileInfoList.m_fileInfoList.emplace_back(iter->first);
}
}
return AZ::Success(outputAssetFileInfoList);
}
bool AssetFileInfoListComparison::AddComparisonStep(const ComparisonData& comparisonData)
{
return AddComparisonStep(comparisonData, GetNumComparisonSteps());
}
bool AssetFileInfoListComparison::AddComparisonStep(const ComparisonData& comparisonData, size_t destinationIndex)
{
if (destinationIndex >= m_comparisonDataList.size())
{
m_comparisonDataList.emplace_back(comparisonData);
}
else
{
m_comparisonDataList.insert(&m_comparisonDataList.at(destinationIndex), comparisonData);
}
return true;
}
bool AssetFileInfoListComparison::RemoveComparisonStep(size_t index)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList.erase(&m_comparisonDataList.at(index));
return true;
}
bool AssetFileInfoListComparison::MoveComparisonStep(size_t initialIndex, size_t destinationIndex)
{
size_t comparisonDataListSize = m_comparisonDataList.size();
// No need to check the destinationIndex, if it is out of bounds it will just be appended to the end
if (initialIndex >= comparisonDataListSize)
{
AZ_Error(ErrorWindowName, false, "Input indices ( %u, %u ) are invalid.", initialIndex, destinationIndex);
return false;
}
auto comparisonData = m_comparisonDataList.at(initialIndex);
m_comparisonDataList.erase(&m_comparisonDataList.at(initialIndex));
// Since we have modified the list, the indecies after the initialIndex have all shifted
size_t modifiedDestinationIndex = destinationIndex;
if (destinationIndex > initialIndex)
{
--modifiedDestinationIndex;
}
return AddComparisonStep(comparisonData, modifiedDestinationIndex);
}
size_t AssetFileInfoListComparison::GetNumComparisonSteps() const
{
return m_comparisonDataList.size();
}
AZStd::vector<AssetFileInfoListComparison::ComparisonData> AssetFileInfoListComparison::GetComparisonList() const
{
return m_comparisonDataList;
}
bool AssetFileInfoListComparison::SetComparisonType(size_t index, const ComparisonType comparisonType)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_comparisonType = comparisonType;
if (comparisonType != ComparisonType::FilePattern)
{
// Only FilePattern operations are allowed to have FilePatternType and FilePattern values
m_comparisonDataList[index].m_filePatternType = FilePatternType::Default;
m_comparisonDataList[index].m_filePattern.clear();
}
else
{
// FilePattern operations only take one input
m_comparisonDataList[index].m_secondInput.clear();
}
return true;
}
bool AssetFileInfoListComparison::SetFilePatternType(size_t index, const FilePatternType filePatternType)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
if (m_comparisonDataList[index].m_comparisonType != ComparisonType::FilePattern)
{
AZ_Error(ErrorWindowName, false,
"Unable to set File Pattern Type: Comparison Step must be of type ( %s ). Current Comparison Type is: %s",
ComparisonTypeNames[static_cast<int>(ComparisonType::FilePattern)],
ComparisonTypeNames[static_cast<int>(m_comparisonDataList[index].m_comparisonType)]);
return false;
}
m_comparisonDataList[index].m_filePatternType = filePatternType;
return true;
}
bool AssetFileInfoListComparison::SetFilePattern(size_t index, const AZStd::string& filePattern)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %i ) is invalid.", index);
return false;
}
if (m_comparisonDataList[index].m_comparisonType != ComparisonType::FilePattern)
{
AZ_Error(ErrorWindowName, false,
"Unable to set File Pattern: Comparison Step must be of type ( %s ). Current Comparison Type is: %s",
ComparisonTypeNames[static_cast<int>(ComparisonType::FilePattern)],
ComparisonTypeNames[static_cast<int>(m_comparisonDataList[index].m_comparisonType)]);
return false;
}
m_comparisonDataList[index].m_filePattern = filePattern;
return true;
}
bool AssetFileInfoListComparison::SetFirstInput(size_t index, const AZStd::string& firstInput)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %i ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_firstInput = firstInput;
return true;
}
bool AssetFileInfoListComparison::SetSecondInput(size_t index, const AZStd::string& secondInput)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %i ) is invalid.", index);
return false;
}
if (m_comparisonDataList[index].m_comparisonType == ComparisonType::FilePattern)
{
AZ_Error(ErrorWindowName, false,
"Unable to set Second Input value: Comparison Step is of type ( %s ), which only allows for one input.",
ComparisonTypeNames[static_cast<int>(ComparisonType::FilePattern)]);
return false;
}
m_comparisonDataList[index].m_secondInput = secondInput;
return true;
}
bool AssetFileInfoListComparison::SetOutput(size_t index, const AZStd::string& output)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_output = output;
return true;
}
bool AssetFileInfoListComparison::SetCachedFirstInputPath(size_t index, const AZStd::string& firstInputPath)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_cachedFirstInputPath = firstInputPath;
return true;
}
bool AssetFileInfoListComparison::SetCachedSecondInputPath(size_t index, const AZStd::string& secondInputPath)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_cachedSecondInputPath = secondInputPath;
return true;
}
void AssetFileInfoListComparison::FormatOutputToken(AZStd::string& tokenName)
{
if (!tokenName.starts_with(TokenIdentifier) && !tokenName.empty())
{
AzFramework::StringFunc::Prepend(tokenName, TokenIdentifier);
}
}
AssetFileInfoListComparison::ComparisonData::ComparisonData(const ComparisonType& type, const AZStd::string& destinationPath, const AZStd::string& filePattern, FilePatternType filePatternType, unsigned int intersectionCount)
: m_comparisonType(type)
, m_output(destinationPath)
, m_filePattern(filePattern)
, m_filePatternType(filePatternType)
, m_intersectionCount(intersectionCount)
{
}
void AssetFileInfoListComparison::ComparisonData::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ComparisonData>()
->Version(3)
->Field("comparisonType", &ComparisonData::m_comparisonType)
->Field("firstInput", &ComparisonData::m_firstInput)
->Field("secondInput", &ComparisonData::m_secondInput)
->Field("filePattern", &ComparisonData::m_filePattern)
->Field("filePatternType", &ComparisonData::m_filePatternType)
->Field("destinationPath", &ComparisonData::m_output)
->Field("intersectionCount", &ComparisonData::m_intersectionCount);
}
}
/*
* Asset bundler Path and file name utils
*/
void SplitFilename(const AZStd::string& filePath, AZStd::string& baseFileName, AZStd::string& platformIdentifier)
{
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(filePath.c_str(), fileName);
for (AZStd::string_view platformName : AzFramework::PlatformHelper::GetPlatformsInterpreted(AzFramework::PlatformFlags::AllNamedPlatforms))
{
AZStd::string appendedPlatform = AZStd::string::format("_%.*s", aznumeric_cast<int>(platformName.size()), platformName.data());
if (AzFramework::StringFunc::EndsWith(fileName, appendedPlatform))
{
AzFramework::StringFunc::RChop(fileName, appendedPlatform.size());
baseFileName = fileName;
platformIdentifier = platformName;
return;
}
}
}
void RemovePlatformIdentifier(AZStd::string& filePath)
{
AZStd::string baseFileName;
AZStd::string platform;
SplitFilename(filePath, baseFileName, platform);
if (platform.empty())
{
return;
}
AZStd::string extension;
AzFramework::StringFunc::Path::GetExtension(filePath.c_str(), extension);
AzFramework::StringFunc::Path::ReplaceFullName(filePath, baseFileName.c_str(), extension.c_str());
}
AZStd::string GetPlatformIdentifier(const AZStd::string& filePath)
{
AZStd::string baseFileName;
AZStd::string platform;
SplitFilename(filePath, baseFileName, platform);
return platform;
}
} // namespace AzToolsFramework