Add support for configuring default Archive File Search Mode through a Cache Var (#5668)

* Renamed ArchiveLocationPriority enum to FileSearchPriority and made it a proper enum class

Added an ArchiveVars.cpp which checks the a new define: `LY_ARCHIVE_FILE_SEARCH_MODE_DEFAULT`
That define represents the default value to use for the Archive system search mode

Moved the FileSearchLocation enum to the ArchiveVars.h header

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated the AssetBundleComponent to use AZ::IO::Path for level dirs

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added a LY_ARCHIVE_FILE_SEARCH_MODE cache variable

The Cache Variable default value is to Archive File Search Mode to PakOnly
in Release. This can be overridden using a value for all configurations
by specifying a number of 0, 1 or 2.
Alternatively a generator expression can be used to set the Archive File
Search Mode in specific configurations.
For example to set the FileSearchMode to 1 in profile and 2 in release
the following LY_ARCHIVE_FILE_SEARCH_MODE value can be used
`$<$<CONFIG:profile>:1>$<$<CONFIG:release>:2>`

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Updated AssetBundler(Batch) VS Debugger arguments to populate the
project-path optoin if a single project is configured.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Add support for serializing older versions of the AssetBundleManifest

This is done by attaching the "ObjectStreamWriteElementOverride"
attribute to the AssetBundleManifest reflection.
That attribute contains a function which outputs an older serialized
version of the AssetBundleManifest based on the `m_bundleVersion` member
value.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* AZStd::variant Serialization fix

The AttributeData<T> type is no longer suitable for storing the
ObjectStreamWriterOverrideCB function

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
This commit is contained in:
lumberyard-employee-dm
2021-11-18 10:22:08 -06:00
committed by GitHub
parent 489877b82a
commit 9d22c98c26
31 changed files with 396 additions and 304 deletions
+1 -1
View File
@@ -1128,7 +1128,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*");
const QString oldLevelName = Path::GetFile(GetLevelPathName());
const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml");
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips);
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::FileSearchLocation::Any);
if (findHandle)
{
do
@@ -10,6 +10,7 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/bitset.h>
#include <AzCore/Outcome/Outcome.h>
@@ -129,7 +130,8 @@ namespace AZ
/// Remove a catalog from our delta list and rebuild the catalog from remaining items
virtual bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> /*deltaCatalog*/) { return true; }
/// Creates a manifest with the given DeltaCatalog name
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/, const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZStd::string>& /*levelDirs*/) { return false; }
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/,
const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZ::IO::Path>& /*levelDirs*/) { return false; }
/// Creates an instance of a registry containing info for just the specified files, and writes it out to a file at the specified path
virtual bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& /*files*/, const AZStd::string& /*filePath*/) { return false; }
@@ -54,12 +54,8 @@ namespace AZStd
namespace AZ
{
//template<class T>
//class ScriptProperty;
namespace Internal
{
template <class ValueType>
void SetupClassElementFromType(SerializeContext::ClassElement& classElement)
{
@@ -86,8 +82,7 @@ namespace AZ
{
auto uuid = AzTypeInfo<ValueType>::Uuid();
using ContainerType = AttributeContainerType<AZ::TypeId>;
classElement.m_attributes.emplace_back(AZ_CRC("EnumType", 0xb177e1b5), CreateModuleAttribute<ContainerType>(AZStd::move(uuid)));
classElement.m_attributes.emplace_back(AZ_CRC("EnumType", 0xb177e1b5), CreateModuleAttribute(AZStd::move(uuid)));
}
}
@@ -648,7 +643,6 @@ namespace AZ
// Register our key type within an lvalue to rvalue wrapper as an attribute
AZ::TypeId uuid = azrtti_typeid<WrappedKeyType>();
using ContainerType = AttributeContainerType<AZ::TypeId>;
/**
* This should technically bind the reference value from the GetCurrentSerializeContextModule() call
@@ -658,7 +652,7 @@ namespace AZ
*/
m_classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return GetCurrentSerializeContextModule().GetAllocator(); }));
m_classElement.m_attributes.emplace_back(AZ_CRC("KeyType", 0x15bc5303), CreateModuleAttribute<ContainerType>(AZStd::move(uuid)));
m_classElement.m_attributes.emplace_back(AZ_CRC("KeyType", 0x15bc5303), CreateModuleAttribute(AZStd::move(uuid)));
}
// Reflect our wrapped key and value types to serializeContext so that may later be used
@@ -1643,12 +1643,15 @@ namespace AZ
m_writeElementResultStack.push_back(WriteElement(ptr, classData, classElement));
return m_writeElementResultStack.back();
};
auto closeElementCB = [this, classData]()
auto closeElementCB = [this, classTypeId = classData->m_typeId]()
{
if (m_writeElementResultStack.empty())
{
AZ_UNUSED(classData); // Prevent unused warning in release builds
AZ_Error("Serialize", false, "CloseElement is attempted to be called without a corresponding WriteElement when writing class %s", classData->m_name);
// ClassData could be dangling pointer if it was unreflected by the ObjectStreamWriteOverrideCB
// So use the classTypeId instead
AZ_UNUSED(classTypeId);
AZ_Error("Serialize", false, "CloseElement is attempted to be called without a corresponding WriteElement when writing class %s",
classTypeId.ToString<AZStd::fixed_string<AZ::TypeId::MaxStringBuffer>>().c_str());
return true;
}
if (m_writeElementResultStack.back())
@@ -1665,16 +1668,14 @@ namespace AZ
SerializeContext::ENUM_ACCESS_FOR_READ,
&m_errorLogger
);
ObjectStreamWriteOverrideCB writeCB;
if (objectStreamWriteOverrideCB.Read<ObjectStreamWriteOverrideCB>(writeCB))
if (objectStreamWriteOverrideCB.Invoke<void>(callContext, objectPtr, *classData, classElement))
{
writeCB(callContext, objectPtr, *classData, classElement);
return false;
}
else
{
auto objectStreamError = AZStd::string::format("Unable to invoke ObjectStream Write Element Override for class element %s of class data %s",
classElement->m_name ? classElement->m_name : "", classData->m_name);
classElement && classElement->m_name ? classElement->m_name : "", classData->m_name);
m_errorLogger.ReportError(objectStreamError.c_str());
}
}
@@ -90,7 +90,7 @@ namespace AZ
using AttributePtr = AZStd::shared_ptr<Attribute>;
using AttributeSharedPair = AZStd::pair<AttributeId, AttributePtr>;
template <typename ContainerType, typename T>
template <typename T, typename ContainerType = AttributeContainerType<T>>
AttributePtr CreateModuleAttribute(T&& attrValue);
/**
@@ -540,6 +540,7 @@ namespace AZ
*/
struct ClassElement
{
AZ_TYPE_INFO(ClassElement, "{7D386902-A1D9-4525-8284-F68435FE1D05}");
enum Flags
{
FLG_POINTER = (1 << 0), ///< Element is stored as pointer (it's not a value).
@@ -563,22 +564,22 @@ namespace AZ
void ClearAttributes();
Attribute* FindAttribute(AttributeId attributeId) const;
const char* m_name; ///< Used in XML output and debugging purposes
u32 m_nameCrc; ///< CRC32 of m_name
Uuid m_typeId;
size_t m_dataSize;
size_t m_offset;
const char* m_name{ "" }; ///< Used in XML output and debugging purposes
u32 m_nameCrc{}; ///< CRC32 of m_name
Uuid m_typeId = AZ::TypeId::CreateNull();
size_t m_dataSize{};
size_t m_offset{};
IRttiHelper* m_azRtti; ///< Interface used to support RTTI.
IRttiHelper* m_azRtti{}; ///< Interface used to support RTTI.
GenericClassInfo* m_genericClassInfo = nullptr; ///< Valid when the generic class is set. So you don't search for the actual type in the class register.
Edit::ElementData* m_editData; ///< Pointer to edit data (generated by EditContext).
Edit::ElementData* m_editData{}; ///< Pointer to edit data (generated by EditContext).
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator> m_attributes{
AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return AZ::AllocatorInstance<AZ::SystemAllocator>::Get(); })
}; ///< Attributes attached to ClassElement. Lambda is required here as AZStdFunctorAllocator expects a function pointer
///< that returns an IAllocatorAllocate& and the AZ::AllocatorInstance<AZ::SystemAllocator>::Get returns an AZ::SystemAllocator&
/// which while it inherits from IAllocatorAllocate, does not work as function pointers do not support covariant return types
AttributeOwnership m_attributeOwnership = AttributeOwnership::Parent;
int m_flags; ///<
int m_flags{}; ///<
};
typedef AZStd::vector<ClassElement> ClassElementArray;
@@ -589,6 +590,8 @@ namespace AZ
class ClassData
{
public:
AZ_TYPE_INFO(ClassData, "{20EB8E2E-D807-4039-84E2-CE37D7647CD4}");
ClassData();
~ClassData() { ClearAttributes(); }
ClassData(ClassData&&) = default;
@@ -1040,6 +1043,7 @@ namespace AZ
*/
struct EnumerateInstanceCallContext
{
AZ_TYPE_INFO(EnumerateInstanceCallContext, "{FCC1DB4B-72BD-4D78-9C23-C84B91589D33}");
EnumerateInstanceCallContext(const BeginElemEnumCB& beginElemCB, const EndElemEnumCB& endElemCB, const SerializeContext* context, unsigned int accessflags, ErrorHandler* errorHandler);
BeginElemEnumCB m_beginElemCB; ///< Optional callback when entering an element's hierarchy.
@@ -2539,7 +2543,7 @@ namespace AZ
/// associated with current module
/// @param attrValue value to store within the attribute
/// @param ContainerType second parameter which is used for function parameter deduction
template <typename ContainerType, typename T>
template <typename T, typename ContainerType>
AttributePtr CreateModuleAttribute(T&& attrValue)
{
IAllocatorAllocate& moduleAllocator = GetCurrentSerializeContextModule().GetAllocator();
@@ -433,9 +433,7 @@ namespace AZ
m_classData.m_attributes.set_allocator(AZStd::move(dllAllocator));
// Create the ObjectStreamWriteOverrideCB in the current module
using ContainerType = AttributeData<AZStd::function<void(SerializeContext::EnumerateInstanceCallContext&,
const void*, const SerializeContext::ClassData&, const SerializeContext::ClassElement*)>>;
m_classData.m_attributes.emplace_back(AZ_CRC("ObjectStreamWriteElementOverride", 0x35eb659f), CreateModuleAttribute<ContainerType>(&ObjectStreamWriter));
m_classData.m_attributes.emplace_back(AZ_CRC("ObjectStreamWriteElementOverride", 0x35eb659f), CreateModuleAttribute(&ObjectStreamWriter));
}
SerializeContext::ClassData* GetClassData() override
@@ -34,7 +34,6 @@
#include <AzFramework/Archive/Archive.h>
#include <AzFramework/Archive/NestedArchive.h>
#include <AzFramework/Archive/ArchiveBus.h>
#include <AzFramework/Archive/ArchiveVars.h>
#include <AzFramework/Archive/MissingFileReport.h>
#include <AzFramework/Archive/ZipDirFind.h>
#include <AzFramework/Archive/ZipDirCacheFactory.h>
@@ -43,8 +42,10 @@
namespace AZ::IO
{
AZ_CVAR(int, sys_PakPriority, aznumeric_cast<int>(ArchiveVars{}.nPriority), nullptr, AZ::ConsoleFunctorFlags::Null,
"If set to 1, tells Archive to try to open the file in pak first, then go to file system");
AZ_CVAR(int, sys_PakPriority, aznumeric_cast<int>(ArchiveVars{}.m_fileSearchPriority), nullptr, AZ::ConsoleFunctorFlags::Null,
"If set to 0, tells Archive to try to open the file on the file system first othewise check mounted paks.\n"
"If set to 1, tells Archive to try to open the file in pak first, then go to file system.\n"
"If set to 2, tells the Archive to only open files from the pak");
AZ_CVAR(int, sys_PakMessageInvalidFileAccess, ArchiveVars{}.nMessageInvalidFileAccess, nullptr, AZ::ConsoleFunctorFlags::Null,
"Message Box synchronous file access when in game");
@@ -437,9 +438,9 @@ namespace AZ::IO
//////////////////////////////////////////////////////////////////////////
bool Archive::IsFileExist(AZStd::string_view sFilename, EFileSearchLocation fileLocation)
bool Archive::IsFileExist(AZStd::string_view sFilename, FileSearchLocation fileLocation)
{
const AZ::IO::ArchiveLocationPriority nVarPakPriority = GetPakPriority();
const AZ::IO::FileSearchPriority nVarPakPriority = GetPakPriority();
auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(sFilename);
if (!szFullPath)
@@ -450,25 +451,25 @@ namespace AZ::IO
switch(fileLocation)
{
case IArchive::eFileLocation_Any:
case FileSearchLocation::Any:
// Search for file based on pak priority
switch (nVarPakPriority)
{
case ArchiveLocationPriority::ePakPriorityFileFirst:
case FileSearchPriority::FileFirst:
return FileIOBase::GetDirectInstance()->Exists(szFullPath->c_str()) || FindPakFileEntry(szFullPath->Native());
case ArchiveLocationPriority::ePakPriorityPakFirst:
case FileSearchPriority::PakFirst:
return FindPakFileEntry(szFullPath->Native()) || IO::FileIOBase::GetDirectInstance()->Exists(szFullPath->c_str());
case ArchiveLocationPriority::ePakPriorityPakOnly:
case FileSearchPriority::PakOnly:
return FindPakFileEntry(szFullPath->Native());
default:
AZ_Assert(false, "PakPriority %d doesn't match a value in the ArchiveLocationPriority enum",
AZ_Assert(false, "PakPriority %d doesn't match a value in the FileSearchPriority enum",
aznumeric_cast<int>(nVarPakPriority));
}
break;
case IArchive::eFileLocation_InPak:
case FileSearchLocation::InPak:
return FindPakFileEntry(szFullPath->Native());
case IArchive::eFileLocation_OnDisk:
if (nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly)
case FileSearchLocation::OnDisk:
if (nVarPakPriority != FileSearchPriority::PakOnly)
{
return FileIOBase::GetDirectInstance()->Exists(szFullPath->c_str());
}
@@ -485,7 +486,7 @@ namespace AZ::IO
//////////////////////////////////////////////////////////////////////////
bool Archive::IsFolder(AZStd::string_view sPath)
{
AZStd::fixed_string<AZ::IO::MaxPathLength > filePath{ sPath };
AZ::IO::FixedMaxPath filePath{ sPath };
return AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath.c_str());
}
@@ -515,7 +516,7 @@ namespace AZ::IO
// get the priority into local variable to avoid it changing in the course of
// this function execution (?)
const ArchiveLocationPriority nVarPakPriority = GetPakPriority();
const FileSearchPriority nVarPakPriority = GetPakPriority();
AZ::IO::OpenMode nOSFlags = AZ::IO::GetOpenModeFromStringMode(szMode);
@@ -628,17 +629,17 @@ namespace AZ::IO
switch (nVarPakPriority)
{
case ArchiveLocationPriority::ePakPriorityFileFirst:
case FileSearchPriority::FileFirst:
{
AZ::IO::HandleType fileHandle = OpenFromFileSystem();
return fileHandle != AZ::IO::InvalidHandle ? fileHandle : OpenFromArchive();
}
case ArchiveLocationPriority::ePakPriorityPakFirst:
case FileSearchPriority::PakFirst:
{
AZ::IO::HandleType fileHandle = OpenFromArchive();
return fileHandle != AZ::IO::InvalidHandle ? fileHandle : OpenFromFileSystem();
}
case ArchiveLocationPriority::ePakPriorityPakOnly:
case FileSearchPriority::PakOnly:
{
return OpenFromArchive();
}
@@ -810,7 +811,7 @@ namespace AZ::IO
return 0;
}
if (GetPakPriority() == ArchiveLocationPriority::ePakPriorityFileFirst) // if the file system files have priority now..
if (GetPakPriority() == FileSearchPriority::FileFirst) // if the file system files have priority now..
{
IArchive::SignedFileSize nFileSize = GetFileSizeOnDisk(fullPath->Native());
if (nFileSize != IArchive::FILE_NOT_PRESENT)
@@ -825,7 +826,7 @@ namespace AZ::IO
return pFileEntry->desc.lSizeUncompressed;
}
if (bAllowUseFileSystem || GetPakPriority() == ArchiveLocationPriority::ePakPriorityPakFirst) // if the archive files had more priority, we didn't attempt fopen before- try it now
if (bAllowUseFileSystem || GetPakPriority() == FileSearchPriority::PakFirst) // if the archive files had more priority, we didn't attempt fopen before- try it now
{
IArchive::SignedFileSize nFileSize = GetFileSizeOnDisk(fullPath->Native());
if (nFileSize != IArchive::FILE_NOT_PRESENT)
@@ -1023,7 +1024,7 @@ namespace AZ::IO
//////////////////////////////////////////////////////////////////////////
AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, EFileSearchType searchType)
AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, FileSearchLocation searchType)
{
auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pDir);
if (!szFullPath)
@@ -1036,18 +1037,21 @@ namespace AZ::IO
bool bAllowUseFileSystem{};
switch (searchType)
{
case IArchive::eFileSearchType_AllowInZipsOnly:
bAllowUseFileSystem = false;
bScanZips = true;
break;
case IArchive::eFileSearchType_AllowOnDiskAndInZips:
bAllowUseFileSystem = true;
bScanZips = true;
break;
case IArchive::eFileSearchType_AllowOnDiskOnly:
bAllowUseFileSystem = true;
bScanZips = false;
break;
case FileSearchLocation::InPak:
bAllowUseFileSystem = false;
bScanZips = true;
break;
case FileSearchLocation::Any:
bAllowUseFileSystem = true;
bScanZips = true;
break;
case FileSearchLocation::OnDisk:
bAllowUseFileSystem = true;
bScanZips = false;
break;
default:
AZ_Assert(false, "Invalid search location value supplied");
break;
}
AZStd::intrusive_ptr pFindData = aznew AZ::IO::FindData();
@@ -1218,7 +1222,7 @@ namespace AZ::IO
else
{
// [LYN-2376] Remove once legacy slice support is removed
AZStd::vector<AZStd::string> levelDirs;
AZStd::vector<AZ::IO::Path> levelDirs;
if (addLevels)
{
@@ -1380,7 +1384,7 @@ namespace AZ::IO
return true;
}
if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, IArchive::eFileSearchType_AllowOnDiskOnly); fileIterator)
if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, FileSearchLocation::OnDisk); fileIterator)
{
AZStd::vector<AZ::IO::FixedMaxPath> files;
do
@@ -1955,15 +1959,15 @@ namespace AZ::IO
}
// gets the current archive priority
ArchiveLocationPriority Archive::GetPakPriority() const
FileSearchPriority Archive::GetPakPriority() const
{
int pakPriority = aznumeric_cast<int>(ArchiveVars{}.nPriority);
FileSearchPriority pakPriority = ArchiveVars{}.m_fileSearchPriority;
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
{
[[maybe_unused]] AZ::GetValueResult getCvarResult = console->GetCvarValue("sys_PakPriority", pakPriority);
[[maybe_unused]] AZ::GetValueResult getCvarResult = console->GetCvarValue("sys_PakPriority", reinterpret_cast<int&>(pakPriority));
AZ_Error("Archive", getCvarResult == AZ::GetValueResult::Success, "Lookup of 'sys_PakPriority console variable failed with error %s", AZ::GetEnumString(getCvarResult));
}
return static_cast<ArchiveLocationPriority>(pakPriority);
return pakPriority;
}
//////////////////////////////////////////////////////////////////////////
@@ -2030,13 +2034,13 @@ namespace AZ::IO
switch (GetPakPriority())
{
case ArchiveLocationPriority::ePakPriorityFileFirst:
case FileSearchPriority::FileFirst:
info.m_conflictResolution = AZ::IO::ConflictResolution::PreferFile;
break;
case ArchiveLocationPriority::ePakPriorityPakFirst:
case FileSearchPriority::PakFirst:
info.m_conflictResolution = AZ::IO::ConflictResolution::PreferArchive;
break;
case ArchiveLocationPriority::ePakPriorityPakOnly:
case FileSearchPriority::PakOnly:
info.m_conflictResolution = AZ::IO::ConflictResolution::UseArchiveOnly;
break;
}
@@ -2147,13 +2151,13 @@ namespace AZ::IO
return manifestInfo;
}
AZStd::vector<AZStd::string> Archive::ScanForLevels(ZipDir::CachePtr pZip)
AZStd::vector<AZ::IO::Path> Archive::ScanForLevels(ZipDir::CachePtr pZip)
{
AZStd::queue<AZStd::string> scanDirs;
AZStd::vector<AZStd::string> levelDirs;
AZStd::string currentDir = "levels";
AZStd::string currentDirPattern;
AZStd::string currentFilePattern;
AZStd::queue<AZ::IO::Path> scanDirs;
AZStd::vector<AZ::IO::Path> levelDirs;
AZ::IO::Path currentDir = "levels";
AZ::IO::Path currentDirPattern;
AZ::IO::Path currentFilePattern;
ZipDir::FindDir findDir(pZip);
findDir.FindFirst(currentDir.c_str());
@@ -2171,11 +2175,10 @@ namespace AZ::IO
scanDirs.pop();
}
currentDirPattern = currentDir + AZ_FILESYSTEM_SEPARATOR_WILDCARD;
currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "level.pak";
currentDirPattern = currentDir / "*";
currentFilePattern = currentDir / "level.pak";
ZipDir::FileEntry* fileEntry = findFile.FindExact(currentFilePattern.c_str());
if (fileEntry)
if (ZipDir::FileEntry* fileEntry = findFile.FindExact(currentFilePattern); fileEntry)
{
levelDirs.emplace_back(currentDir);
continue;
@@ -2183,9 +2186,7 @@ namespace AZ::IO
for (findDir.FindFirst(currentDirPattern.c_str()); findDir.GetDirEntry(); findDir.FindNext())
{
AZStd::string_view dirName = findDir.GetDirName();
AZStd::string dirToAdd = AZStd::string::format("%s/%.*s", currentDir.data(), aznumeric_cast<int>(dirName.size()), dirName.data());
scanDirs.push(dirToAdd);
scanDirs.push(currentDir / findDir.GetDirName());
}
} while (!scanDirs.empty());
@@ -207,7 +207,7 @@ namespace AZ::IO
uint64_t FTell(AZ::IO::HandleType handle) override;
int FFlush(AZ::IO::HandleType handle) override;
int FClose(AZ::IO::HandleType handle) override;
AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) override;
AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, FileSearchLocation searchType = FileSearchLocation::InPak) override;
AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override;
bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override;
int FEof(AZ::IO::HandleType handle) override;
@@ -219,7 +219,7 @@ namespace AZ::IO
bool RemoveDir(AZStd::string_view pName) override; // remove directory from FS (if supported)
bool IsAbsPath(AZStd::string_view pPath) override;
bool IsFileExist(AZStd::string_view sFilename, EFileSearchLocation fileLocation = eFileLocation_Any) override;
bool IsFileExist(AZStd::string_view sFilename, FileSearchLocation fileLocation = FileSearchLocation::Any) override;
bool IsFolder(AZStd::string_view sPath) override;
IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) override;
@@ -255,7 +255,7 @@ namespace AZ::IO
bool DisableRuntimeFileAccess(bool status, AZStd::thread_id threadId) override;
// gets the current archive priority
ArchiveLocationPriority GetPakPriority() const override;
FileSearchPriority GetPakPriority() const override;
uint64_t GetFileOffsetOnMedia(AZStd::string_view szName) const override;
@@ -305,7 +305,7 @@ namespace AZ::IO
AZStd::shared_ptr<AzFramework::AssetRegistry> GetBundleCatalog(ZipDir::CachePtr pZip, const AZStd::string& catalogName);
// [LYN-2376] Remove once legacy slice support is removed
AZStd::vector<AZStd::string> ScanForLevels(ZipDir::CachePtr pZip);
AZStd::vector<AZ::IO::Path> ScanForLevels(ZipDir::CachePtr pZip);
mutable AZStd::shared_mutex m_csOpenFiles;
ZipPseudoFileArray m_arrOpenFiles;
@@ -169,7 +169,7 @@ namespace AZ::IO
size = m_archive->FGetSize(filePath, true);
if (!size)
{
return m_archive->IsFileExist(filePath, IArchive::eFileLocation_Any) ? IO::ResultCode::Success : IO::ResultCode::Error;
return m_archive->IsFileExist(filePath, FileSearchLocation::Any) ? IO::ResultCode::Success : IO::ResultCode::Error;
}
return IO::ResultCode::Success;
@@ -78,9 +78,9 @@ namespace AZ::IO
{
// get the priority into local variable to avoid it changing in the course of
// this function execution
ArchiveLocationPriority nVarPakPriority = archive->GetPakPriority();
FileSearchPriority nVarPakPriority = archive->GetPakPriority();
if (nVarPakPriority == ArchiveLocationPriority::ePakPriorityFileFirst)
if (nVarPakPriority == FileSearchPriority::FileFirst)
{
// first, find the file system files
ScanFS(archive, szDir);
@@ -96,7 +96,7 @@ namespace AZ::IO
{
ScanZips(archive, szDir);
}
if (bAllowUseFS || nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly)
if (bAllowUseFS || nVarPakPriority != FileSearchPriority::PakOnly)
{
ScanFS(archive, szDir);
}
@@ -0,0 +1,22 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/Archive/ArchiveVars.h>
namespace AZ::IO
{
FileSearchPriority GetDefaultFileSearchPriority()
{
#if defined(LY_ARCHIVE_FILE_SEARCH_MODE_DEFAULT)
return FileSearchPriority{ LY_ARCHIVE_FILE_SEARCH_MODE_DEFAULT };
#else
return FileSearchPriority{};
#endif
}
}
@@ -13,13 +13,24 @@
namespace AZ::IO
{
enum class ArchiveLocationPriority
enum class FileSearchPriority
{
ePakPriorityFileFirst = 0,
ePakPriorityPakFirst = 1,
ePakPriorityPakOnly = 2
FileFirst,
PakFirst,
PakOnly
};
//file location enum used in isFileExist to control where the archive system looks for the file.
enum class FileSearchLocation
{
Any,
OnDisk,
InPak
};
FileSearchPriority GetDefaultFileSearchPriority();
// variables that control behavior of the Archive subsystem
struct ArchiveVars
{
@@ -28,8 +39,6 @@ namespace AZ::IO
#else
inline static constexpr bool IsReleaseConfig{};
#endif
public:
int nReadSlice{};
int nSaveTotalResourceList{};
int nSaveFastloadResourceList{};
@@ -42,9 +51,7 @@ namespace AZ::IO
int nLoadCache{};
int nLoadModePaks{};
int nStreamCache{ STREAM_CACHE_DEFAULT };
ArchiveLocationPriority nPriority{ IsReleaseConfig
? ArchiveLocationPriority::ePakPriorityPakOnly
: ArchiveLocationPriority::ePakPriorityFileFirst }; // Which file location to favor (loose vs. pak files)
FileSearchPriority m_fileSearchPriority{ GetDefaultFileSearchPriority()};
int nMessageInvalidFileAccess{};
int nLogInvalidFileAccess{ IsReleaseConfig ? 0 : 1 };
int nDisableNonLevelRelatedPaks{ 1 };
@@ -18,13 +18,13 @@
#include <AzCore/std/string/fixed_string.h>
#include <AzFramework/Archive/ArchiveFindData.h>
#include <AzFramework/Archive/ArchiveVars.h>
enum EStreamSourceMediaType : int32_t;
namespace AZ::IO
{
enum class ArchiveLocationPriority;
enum class FileSearchPriority;
struct IResourceList;
struct INestedArchive;
struct IArchive;
@@ -114,14 +114,6 @@ namespace AZ::IO
RFOM_NextLevel // used for level2level loading
};
//file location enum used in isFileExist to control where the archive system looks for the file.
enum EFileSearchLocation
{
eFileLocation_Any = 0,
eFileLocation_OnDisk,
eFileLocation_InPak,
};
enum EInMemoryArchiveLocation
{
eInMemoryPakLocale_Unload = 0,
@@ -130,12 +122,6 @@ namespace AZ::IO
eInMemoryPakLocale_PAK,
};
enum EFileSearchType
{
eFileSearchType_AllowInZipsOnly = 0,
eFileSearchType_AllowOnDiskAndInZips,
eFileSearchType_AllowOnDiskOnly
};
using SignedFileSize = int64_t;
@@ -213,7 +199,7 @@ namespace AZ::IO
virtual AZStd::intrusive_ptr<AZ::IO::MemoryBlock> PoolAllocMemoryBlock(size_t nSize, const char* sUsage, size_t nAlign = 1) = 0;
// Arguments:
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0;
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, FileSearchLocation searchType = FileSearchLocation::InPak) = 0;
virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0;
virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0;
//returns file modification time
@@ -221,7 +207,7 @@ namespace AZ::IO
// Description:
// Checks if specified file exist in filesystem.
virtual bool IsFileExist(AZStd::string_view sFilename, EFileSearchLocation = eFileLocation_Any) = 0;
virtual bool IsFileExist(AZStd::string_view sFilename, FileSearchLocation = FileSearchLocation::Any) = 0;
// Checks if path is a folder
virtual bool IsFolder(AZStd::string_view sPath) = 0;
@@ -283,7 +269,7 @@ namespace AZ::IO
virtual bool DisableRuntimeFileAccess(bool status, AZStd::thread_id threadId) = 0;
// gets the current pak priority
virtual ArchiveLocationPriority GetPakPriority() const = 0;
virtual FileSearchPriority GetPakPriority() const = 0;
// Summary:
// Return offset in archive file (ideally has to return offset on DVD) for streaming requests sorting
@@ -295,7 +281,7 @@ namespace AZ::IO
// Event sent when a archive file is opened that contains a level.pak
// @param const AZStd::vector<AZStd::string>& - Array of directories containing level.pak files
using LevelPackOpenEvent = AZ::Event<const AZStd::vector<AZStd::string>&>;
using LevelPackOpenEvent = AZ::Event<const AZStd::vector<AZ::IO::Path>&>;
virtual auto GetLevelPackOpenEvent()->LevelPackOpenEvent* = 0;
// Event sent when a archive contains a level.pak is closed
// @param const AZStd::string_view - Name of the pak file that was closed
@@ -7,23 +7,153 @@
*/
#include <AzFramework/Asset/AssetBundleManifest.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzFramework
{
const int AssetBundleManifest::CurrentBundleVersion = 2;
// Redirects writing of the AssetBundleManifest to an older version if the bundle version
// is not set to the current version
static void OldBundleManifestWriter(AZ::SerializeContext::EnumerateInstanceCallContext& callContext, const void* bundleManifestPointer,
const AZ::SerializeContext::ClassData&, const AZ::SerializeContext::ClassElement* assetBundleManifestClassElement);
static bool BundleManifestVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& rootElement);
const int AssetBundleManifest::CurrentBundleVersion = 3;
const char AssetBundleManifest::s_manifestFileName[] = "manifest.xml";
AssetBundleManifest::AssetBundleManifest() = default;
AssetBundleManifest::~AssetBundleManifest() = default;
void AssetBundleManifest::ReflectSerialize(AZ::SerializeContext* serializeContext)
{
if (serializeContext)
{
serializeContext->Class<AssetBundleManifest>()
->Version(2)
->Version(CurrentBundleVersion, &BundleManifestVersionConverter)
->Attribute(AZ::SerializeContextAttributes::ObjectStreamWriteElementOverride, &OldBundleManifestWriter)
->Field("BundleVersion", &AssetBundleManifest::m_bundleVersion)
->Field("CatalogName", &AssetBundleManifest::m_catalogName)
->Field("DependentBundleNames", &AssetBundleManifest::m_depedendentBundleNames)
->Field("DependentBundleNames", &AssetBundleManifest::m_dependentBundleNames)
->Field("LevelNames", &AssetBundleManifest::m_levelDirs);
// Make sure the AZStd::vector<AZStd::string> type is reflected so that it can be read
// using DataElement::GetChildData
serializeContext->RegisterGenericType<AZStd::vector<AZStd::string>>();
}
}
bool BundleManifestVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& rootElement)
{
if (rootElement.GetVersion() < 3)
{
static constexpr AZ::u32 levelNamesCrc = AZ_CRC_CE("LevelNames");
AZStd::vector<AZ::IO::Path> newLevelDirs;
if (AZStd::vector<AZStd::string> oldLevelNames; rootElement.GetChildData(levelNamesCrc, oldLevelNames))
{
newLevelDirs.insert(newLevelDirs.end(),
AZStd::make_move_iterator(oldLevelNames.begin()), AZStd::make_move_iterator(oldLevelNames.end()));
}
else
{
AZ_Error("AssetBundleManifest", false, R"(Unable to read "levelNames" from AssetBundleManifest version %u )",
rootElement.GetVersion());
}
rootElement.RemoveElementByName(levelNamesCrc);
rootElement.AddElementWithData(context, "LevelNames", newLevelDirs);
}
return true;
}
void OldBundleManifestWriter(AZ::SerializeContext::EnumerateInstanceCallContext& callContext, const void* bundleManifestPointer,
const AZ::SerializeContext::ClassData&, const AZ::SerializeContext::ClassElement* assetBundleManifestClassElement)
{
// Copy the AssetBundleManifest current version instance to the AssetBundleManifest V2 instance
auto assetBundleManifestCurrent = reinterpret_cast<const AssetBundleManifest*>(bundleManifestPointer);
if (assetBundleManifestCurrent->GetBundleVersion() <= 2)
{
auto serializeContext = const_cast<AZ::SerializeContext*>(callContext.m_context);
struct AssetBundleManifestV2
{
// Use the same ClassName and typeid as the AssetBundleManifest
AZ_TYPE_INFO(AssetBundleManifest, azrtti_typeid<AssetBundleManifest>());
AZStd::string m_catalogName;
AZStd::vector<AZStd::string> m_dependentBundleNames;
AZStd::vector<AZStd::string> m_levelDirs;
int m_bundleVersion{};
};
auto ReflectAssetBundleManifestV2 = [](AZ::SerializeContext* serializeContext)
{
serializeContext->Class<AssetBundleManifestV2>()
->Version(2)
->Field("BundleVersion", &AssetBundleManifestV2::m_bundleVersion)
->Field("CatalogName", &AssetBundleManifestV2::m_catalogName)
->Field("DependentBundleNames", &AssetBundleManifestV2::m_dependentBundleNames)
->Field("LevelNames", &AssetBundleManifestV2::m_levelDirs);
};
// Unreflect the AssetBundleManifest class at the version since it shares the same typeid
// as the older version and Reflect the V2 AssetBundlerManifest
serializeContext->EnableRemoveReflection();
AssetBundleManifest::ReflectSerialize(serializeContext);
serializeContext->DisableRemoveReflection();
ReflectAssetBundleManifestV2(serializeContext);
// Use the Current AssetBundleManifest instance to make a Version 2 AssetBundleManifest
AssetBundleManifestV2 assetBundleManifestV2;
assetBundleManifestV2.m_catalogName = assetBundleManifestCurrent->GetCatalogName();
assetBundleManifestV2.m_dependentBundleNames = assetBundleManifestCurrent->GetDependentBundleNames();
assetBundleManifestV2.m_bundleVersion = assetBundleManifestCurrent->GetBundleVersion();
for (const AZ::IO::Path& levelDir : assetBundleManifestCurrent->GetLevelDirectories())
{
assetBundleManifestV2.m_levelDirs.emplace_back(levelDir.Native());
}
const AZ::TypeId& assetBundlerManifestTypeId = azrtti_typeid<AssetBundleManifestV2>();
const auto assetBundleManifestV2ClassData = serializeContext->FindClassData(assetBundlerManifestTypeId);
// Create an AssetBundleManifest Version 2 Class Eleemnt
// It will copy over the name and nameCrc values of the current AssetBundleManifestelemnt
auto CreateAssetBundleManifestV2ClassElement = [&assetBundlerManifestTypeId](
const AZ::SerializeContext::ClassElement* currentVersionElement) -> AZ::SerializeContext::ClassElement
{
AZ::SerializeContext::ClassElement v2ClassElement;
// Copy over the name of he current
if (currentVersionElement)
{
v2ClassElement.m_name = currentVersionElement->m_name;
v2ClassElement.m_nameCrc = currentVersionElement->m_nameCrc;
}
v2ClassElement.m_dataSize = sizeof(AssetBundleManifest);
v2ClassElement.m_azRtti = AZ::GetRttiHelper<AssetBundleManifestV2>();
v2ClassElement.m_genericClassInfo = nullptr;
v2ClassElement.m_typeId = assetBundlerManifestTypeId;
v2ClassElement.m_editData = nullptr;
v2ClassElement.m_attributeOwnership = AZ::SerializeContext::ClassElement::AttributeOwnership::Self;
return v2ClassElement;
};
const auto assetBundleManifestV2ClassElement = CreateAssetBundleManifestV2ClassElement(assetBundleManifestClassElement);
serializeContext->EnumerateInstanceConst(&callContext, &assetBundleManifestV2, assetBundlerManifestTypeId,
assetBundleManifestV2ClassData, assetBundleManifestClassElement ? &assetBundleManifestV2ClassElement : nullptr);
// Unreflect the V2 AssetBundleManifest and Re-reflect the AssetBundleManifest class at the current version
serializeContext->EnableRemoveReflection();
ReflectAssetBundleManifestV2(serializeContext);
serializeContext->DisableRemoveReflection();
AssetBundleManifest::ReflectSerialize(serializeContext);
}
}
const AZStd::vector<AZ::IO::Path>& AssetBundleManifest::GetLevelDirectories() const
{
return m_levelDirs;
}
void AssetBundleManifest::SetLevelsDirectory(const AZStd::vector<AZ::IO::Path>& levelDirs)
{
m_levelDirs = levelDirs;
}
} // namespace AzFramework
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/string/string.h>
@@ -27,7 +28,8 @@ namespace AzFramework
AZ_TYPE_INFO(AssetBundleManifest, "{8628A669-7B19-4C48-A7CB-F670CC9586FD}");
AZ_CLASS_ALLOCATOR(AssetBundleManifest, AZ::SystemAllocator, 0);
AssetBundleManifest() = default;
AssetBundleManifest();
~AssetBundleManifest();
static void ReflectSerialize(AZ::SerializeContext* serializeContext);
@@ -35,21 +37,21 @@ namespace AzFramework
// of files within the AssetBundle in order to update the Asset Registry at runtime when
// loading the bundle
const AZStd::string& GetCatalogName() const { return m_catalogName; }
AZStd::vector<AZStd::string> GetDependentBundleNames() const { return m_depedendentBundleNames; }
AZStd::vector<AZStd::string> GetLevelDirectories() const { return m_levelDirs; }
AZStd::vector<AZStd::string> GetDependentBundleNames() const { return m_dependentBundleNames; }
const AZStd::vector<AZ::IO::Path>& GetLevelDirectories() const;
int GetBundleVersion() const { return m_bundleVersion; }
void SetCatalogName(const AZStd::string& catalogName) { m_catalogName = catalogName; }
void SetBundleVersion(int bundleVersion) { m_bundleVersion = bundleVersion; }
void SetDependentBundleNames(const AZStd::vector<AZStd::string>& dependentBundleNames) { m_depedendentBundleNames = dependentBundleNames; }
void SetLevelsDirectory(const AZStd::vector<AZStd::string>& levelDirs) { m_levelDirs = levelDirs; }
void SetDependentBundleNames(const AZStd::vector<AZStd::string>& dependentBundleNames) { m_dependentBundleNames = dependentBundleNames; }
void SetLevelsDirectory(const AZStd::vector<AZ::IO::Path>& levelDirs);
static const char s_manifestFileName[];
static const int CurrentBundleVersion;
private:
private:
AZStd::string m_catalogName;
AZStd::vector<AZStd::string> m_depedendentBundleNames;
AZStd::vector<AZStd::string> m_levelDirs;
AZStd::vector<AZStd::string> m_dependentBundleNames;
AZStd::vector<AZ::IO::Path> m_levelDirs;
int m_bundleVersion = CurrentBundleVersion;
};
@@ -1186,7 +1186,7 @@ namespace AzFramework
}
bool AssetCatalog::CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZStd::string>& levelDirs)
bool AssetCatalog::CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZ::IO::Path>& levelDirs)
{
if (bundleVersion > AzFramework::AssetBundleManifest::CurrentBundleVersion || bundleVersion < 0)
{
@@ -67,7 +67,7 @@ namespace AzFramework
bool InsertDeltaCatalogBefore(AZStd::shared_ptr<AzFramework::AssetRegistry> deltaCatalog, AZStd::shared_ptr<AzFramework::AssetRegistry> afterDeltaCatalog) override;
bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> deltaCatalog) override;
static bool SaveAssetBundleManifest(const char* assetBundleManifestFile, AzFramework::AssetBundleManifest* bundleManifest);
bool CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZStd::string>& levelDirs) override;
bool CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZ::IO::Path>& levelDirs) override;
bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& files, const AZStd::string& filePath) override;
void AddExtension(const char* extension) override;
@@ -21,6 +21,7 @@ set(FILES
Archive/ArchiveFindData.cpp
Archive/ArchiveFindData.h
Archive/ArchiveVars.h
Archive/ArchiveVars.cpp
Archive/Codec.h
Archive/IArchive.h
Archive/INestedArchive.h
+8 -1
View File
@@ -6,7 +6,6 @@
#
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
@@ -33,6 +32,14 @@ ly_add_target(
3rdParty::lz4
)
set(LY_SEARCH_MODE_DEFINE $<$<NOT:$<STREQUAL:"${LY_ARCHIVE_FILE_SEARCH_MODE}","">>:LY_ARCHIVE_FILE_SEARCH_MODE_DEFAULT=${LY_ARCHIVE_FILE_SEARCH_MODE}>)
ly_add_source_properties(
SOURCES
AzFramework/Archive/ArchiveVars.cpp
PROPERTY COMPILE_DEFINITIONS
VALUES ${LY_SEARCH_MODE_DEFINE})
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Tests/Platform/${PAL_PLATFORM_NAME})
@@ -273,7 +273,7 @@ namespace UnitTest
// Also enable extra verbosity in the AZ::IO::Archive code
CVarIntValueScope previousLocationPriority{ *console, "sys_pakPriority" };
CVarIntValueScope oldArchiveVerbosity{ *console, "az_archive_verbosity" };
console->PerformCommand("sys_PakPriority", { AZ::CVarFixedString::format("%d", aznumeric_cast<int>(AZ::IO::ArchiveLocationPriority::ePakPriorityPakOnly)) });
console->PerformCommand("sys_PakPriority", { AZ::CVarFixedString::format("%d", aznumeric_cast<int>(AZ::IO::FileSearchPriority::PakOnly)) });
console->PerformCommand("az_archive_verbosity", { "1" });
// ---- Archive FGetCachedFileDataTests (these leverage Archive CachedFile mechanism for caching data ---
@@ -459,7 +459,7 @@ namespace UnitTest
// Once the archive has been deleted it should no longer be searched
CVarIntValueScope previousLocationPriority{ *console, "sys_pakPriority" };
console->PerformCommand("sys_PakPriority", { AZ::CVarFixedString::format("%d", aznumeric_cast<int>(AZ::IO::ArchiveLocationPriority::ePakPriorityPakOnly)) });
console->PerformCommand("sys_PakPriority", { AZ::CVarFixedString::format("%d", aznumeric_cast<int>(AZ::IO::FileSearchPriority::PakOnly)) });
handle = archive->FindFirst("levels\\*");
EXPECT_FALSE(static_cast<bool>(handle));
@@ -785,7 +785,7 @@ namespace UnitTest
EXPECT_TRUE(archive->OpenPack("@usercache@", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@usercache@/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat", AZ::IO::FileSearchLocation::OnDisk));
EXPECT_FALSE(archive->IsFileExist("@usercache@/notfoundit.dat"));
EXPECT_TRUE(archive->ClosePack(realNameBuf));
@@ -793,7 +793,7 @@ namespace UnitTest
EXPECT_TRUE(archive->OpenPack("@products@", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@products@/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous location!
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::FileSearchLocation::OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat"));
EXPECT_TRUE(archive->ClosePack(realNameBuf));
@@ -802,8 +802,8 @@ namespace UnitTest
EXPECT_TRUE(archive->IsFileExist("@products@/mystuff/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat")); // do not find it in the previous locations!
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous locations!
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::FileSearchLocation::OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/foundit.dat", AZ::IO::FileSearchLocation::OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/notfoundit.dat")); // non-existent file
EXPECT_TRUE(archive->ClosePack(realNameBuf));
@@ -202,9 +202,9 @@ namespace AzToolsFramework
AZ_TracePrintf(logWindowName, "Creating new asset bundle manifest file \"%s\" for source pak \"%s\".\n", AzFramework::AssetBundleManifest::s_manifestFileName, sourcePak.c_str());
bool manifestSaved = false;
AZStd::string manifestDirectory;
AZStd::vector<AZStd::string> levelDirs;
AzFramework::StringFunc::Path::GetFullPath(sourcePak.c_str(), manifestDirectory);
AssetCatalogRequestBus::BroadcastResult(manifestSaved, &AssetCatalogRequestBus::Events::CreateBundleManifest, outCatalogPath, AZStd::vector<AZStd::string>(), manifestDirectory, AzFramework::AssetBundleManifest::CurrentBundleVersion, levelDirs);
AssetCatalogRequestBus::BroadcastResult(manifestSaved, &AssetCatalogRequestBus::Events::CreateBundleManifest, outCatalogPath,
AZStd::vector<AZStd::string>(), manifestDirectory, AzFramework::AssetBundleManifest::CurrentBundleVersion, AZStd::vector<AZ::IO::Path>{});
AZStd::string manifestPath;
AzFramework::StringFunc::Path::Join(manifestDirectory.c_str(), AzFramework::AssetBundleManifest::s_manifestFileName, manifestPath);
@@ -263,7 +263,7 @@ namespace AzToolsFramework
AZStd::string tempBundleFilePath = bundleFilePath.Native() + "_temp";
AZStd::vector<AZStd::string> dependentBundleNames;
AZStd::vector<AZStd::string> levelDirs;
AZStd::vector<AZ::IO::Path> levelDirs;
AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>> bundlePathDeltaCatalogPair;
bundlePathDeltaCatalogPair.emplace_back(AZStd::make_pair(tempBundleFilePath, DeltaCatalogName));
@@ -515,7 +515,7 @@ namespace AzToolsFramework
return true;
}
bool AssetBundleComponent::AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZStd::string>& levelDirs)
bool AssetBundleComponent::AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZ::IO::Path>& levelDirs)
{
if (!MakePath(bundleFolder))
{
@@ -86,7 +86,7 @@ namespace AzToolsFramework
//! Adds the manifest file to all the bundles
//! The parent bundle manifest file is special since it will contain information of all dependent bundles names.
bool AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZStd::string>& levelDirs);
bool AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZ::IO::Path>& levelDirs);
//! Adds the delta catalog and any remaining files to the bundle
//! We only create the delta catalog once we are sure about what all the files that will go in it.
@@ -143,7 +143,7 @@ namespace AzToolsFramework
return AssetCatalog::RemoveDeltaCatalog(deltaCatalog);
}
bool PlatformAddressedAssetCatalog::CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZStd::string>& levelDirs)
bool PlatformAddressedAssetCatalog::CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZ::IO::Path>& levelDirs)
{
return AssetCatalog::CreateBundleManifest(deltaCatalogPath, dependentBundleNames, fileDirectory, bundleVersion, levelDirs);
}
@@ -66,7 +66,7 @@ namespace AzToolsFramework
bool InsertDeltaCatalogBefore(AZStd::shared_ptr<AzFramework::AssetRegistry> deltaCatalog, AZStd::shared_ptr<AzFramework::AssetRegistry> afterDeltaCatalog) override;
bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> deltaCatalog) override;
bool CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZStd::string>& levelDirs) override;
bool CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZ::IO::Path>& levelDirs) override;
bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& files, const AZStd::string& filePath) override;
void AddExtension(const char* extension) override;
+4 -3
View File
@@ -11,6 +11,7 @@
#include <AzTest/AzTest.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzFramework/Archive/ArchiveVars.h>
#include <AzFramework/Archive/INestedArchive.h>
#include <AzFramework/Archive/IArchive.h>
#include <CryCommon/platform.h>
@@ -52,11 +53,11 @@ struct CryPakMock
MOCK_METHOD1(PoolMalloc, void*(size_t size));
MOCK_METHOD1(PoolFree, void(void* p));
MOCK_METHOD3(PoolAllocMemoryBlock, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> (size_t nSize, const char* sUsage, size_t nAlign));
MOCK_METHOD2(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, AZ::IO::IArchive::EFileSearchType));
MOCK_METHOD2(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, AZ::IO::FileSearchLocation));
MOCK_METHOD1(FindNext, AZ::IO::ArchiveFileIterator(AZ::IO::ArchiveFileIterator handle));
MOCK_METHOD1(FindClose, bool(AZ::IO::ArchiveFileIterator));
MOCK_METHOD1(GetModificationTime, AZ::IO::IArchive::FileTime(AZ::IO::HandleType f));
MOCK_METHOD2(IsFileExist, bool(AZStd::string_view sFilename, EFileSearchLocation));
MOCK_METHOD2(IsFileExist, bool(AZStd::string_view sFilename, AZ::IO::FileSearchLocation));
MOCK_METHOD1(IsFolder, bool(AZStd::string_view sPath));
MOCK_METHOD1(GetFileSizeOnDisk, AZ::IO::IArchive::SignedFileSize(AZStd::string_view filename));
MOCK_METHOD4(OpenArchive, AZStd::intrusive_ptr<AZ::IO::INestedArchive> (AZStd::string_view szPath, AZStd::string_view bindRoot, uint32_t nFlags, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData));
@@ -72,7 +73,7 @@ struct CryPakMock
MOCK_METHOD1(UnregisterFileAccessSink, void(AZ::IO::IArchiveFileAccessSink * pSink));
MOCK_METHOD1(DisableRuntimeFileAccess, void(bool status));
MOCK_METHOD2(DisableRuntimeFileAccess, bool(bool status, AZStd::thread_id threadId));
MOCK_CONST_METHOD0(GetPakPriority, AZ::IO::ArchiveLocationPriority());
MOCK_CONST_METHOD0(GetPakPriority, AZ::IO::FileSearchPriority());
MOCK_CONST_METHOD1(GetFileOffsetOnMedia, uint64_t(AZStd::string_view szName));
MOCK_CONST_METHOD1(GetFileMediaType, EStreamSourceMediaType(AZStd::string_view szName));
MOCK_METHOD0(GetLevelPackOpenEvent, auto()->LevelPackOpenEvent*);
@@ -203,24 +203,22 @@ CLevelSystem::CLevelSystem(ISystem* pSystem, const char* levelsFolder)
{
return;
}
auto pPak = gEnv->pCryPak;
auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
if (AZ::IO::IArchive::LevelPackOpenEvent* levelPakOpenEvent = pPak->GetLevelPackOpenEvent())
if (AZ::IO::IArchive::LevelPackOpenEvent* levelPakOpenEvent = archive->GetLevelPackOpenEvent())
{
m_levelPackOpenHandler = AZ::IO::IArchive::LevelPackOpenEvent::Handler([this](const AZStd::vector<AZStd::string>& levelDirs)
m_levelPackOpenHandler = AZ::IO::IArchive::LevelPackOpenEvent::Handler([this](const AZStd::vector<AZ::IO::Path>& levelDirs)
{
for (AZStd::string dir : levelDirs)
for (AZ::IO::Path levelDir : levelDirs)
{
AZ::StringFunc::Path::StripComponent(dir, true);
AZStd::string searchPattern = dir + AZ_FILESYSTEM_SEPARATOR_WILDCARD;
bool modFolder = false;
PopulateLevels(searchPattern, dir, gEnv->pCryPak, modFolder, false);
PopulateLevels((levelDir / "*").Native(), levelDir.Native(), AZ::Interface<AZ::IO::IArchive>::Get(), modFolder, false);
}
});
m_levelPackOpenHandler.Connect(*levelPakOpenEvent);
}
if (AZ::IO::IArchive::LevelPackCloseEvent* levelPakCloseEvent = pPak->GetLevelPackCloseEvent())
if (AZ::IO::IArchive::LevelPackCloseEvent* levelPakCloseEvent = archive->GetLevelPackCloseEvent())
{
m_levelPackCloseHandler = AZ::IO::IArchive::LevelPackCloseEvent::Handler([this](AZStd::string_view)
{
@@ -288,7 +286,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
AZStd::unordered_set<AZStd::string> pakList;
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::FileSearchLocation::OnDisk);
if (handle)
{
@@ -335,86 +333,85 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
}
void CLevelSystem::PopulateLevels(
AZStd::string searchPattern, AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly)
AZStd::string searchPattern, const AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly)
{
// allow this find first to actually touch the file system
// (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu)
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(),
fromFileSystemOnly ? AZ::IO::FileSearchLocation::OnDisk : AZ::IO::FileSearchLocation::InPak);
if (handle)
{
// allow this find first to actually touch the file system
// (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu)
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
if (handle)
do
{
do
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory ||
handle.m_filename == "." || handle.m_filename == "..")
{
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory ||
handle.m_filename == "." || handle.m_filename == "..")
{
continue;
}
continue;
}
AZStd::string levelFolder;
if (fromFileSystemOnly)
{
levelFolder =
(folder.empty() ? "" : (folder + "/")) + AZStd::string(handle.m_filename.data(), handle.m_filename.size());
}
else
{
AZStd::string levelName(AZ::IO::PathView(handle.m_filename).Filename().Native());
levelFolder = (folder.empty() ? "" : (folder + "/")) + levelName;
}
AZStd::string levelFolder;
if (fromFileSystemOnly)
{
levelFolder =
(folder.empty() ? "" : (folder + "/")) + AZStd::string(handle.m_filename.data(), handle.m_filename.size());
}
else
{
AZStd::string levelName(AZ::IO::PathView(handle.m_filename).Filename().Native());
levelFolder = (folder.empty() ? "" : (folder + "/")) + levelName;
}
AZStd::string levelPath;
if (AZ::StringFunc::StartsWith(levelFolder.c_str(), m_levelsFolder.c_str()))
AZStd::string levelPath;
if (AZ::StringFunc::StartsWith(levelFolder.c_str(), m_levelsFolder.c_str()))
{
levelPath = levelFolder;
}
else
{
levelPath = m_levelsFolder + "/" + levelFolder;
}
const AZStd::string levelPakName = levelPath + "/" + LevelPakName;
const AZStd::string levelInfoName = levelPath + "/levelinfo.xml";
if (!pPak->IsFileExist(
levelPakName.c_str(),
fromFileSystemOnly ? AZ::IO::FileSearchLocation::OnDisk : AZ::IO::FileSearchLocation::InPak) &&
!pPak->IsFileExist(
levelInfoName.c_str(),
fromFileSystemOnly ? AZ::IO::FileSearchLocation::OnDisk : AZ::IO::FileSearchLocation::InPak))
{
ScanFolder(levelFolder.c_str(), modFolder);
continue;
}
// With the level.pak workflow, levelPath and levelName will point to a directory.
// levelPath: levels/mylevel
// levelName: mylevel
CLevelInfo levelInfo;
levelInfo.m_levelPath = levelPath;
levelInfo.m_levelName = levelFolder;
levelInfo.m_isPak = !fromFileSystemOnly;
CLevelInfo* pExistingInfo = GetLevelInfoInternal(levelInfo.m_levelName);
// Don't add the level if it is already in the list
if (pExistingInfo == NULL)
{
m_levelInfos.push_back(levelInfo);
}
else
{
// Levels in bundles take priority over levels outside bundles.
if (!pExistingInfo->m_isPak && levelInfo.m_isPak)
{
levelPath = levelFolder;
}
else
{
levelPath = m_levelsFolder + "/" + levelFolder;
*pExistingInfo = levelInfo;
}
}
} while (handle = pPak->FindNext(handle));
const AZStd::string levelPakName = levelPath + "/" + LevelPakName;
const AZStd::string levelInfoName = levelPath + "/levelinfo.xml";
if (!pPak->IsFileExist(
levelPakName.c_str(),
fromFileSystemOnly ? AZ::IO::IArchive::eFileLocation_OnDisk : AZ::IO::IArchive::eFileLocation_InPak) &&
!pPak->IsFileExist(
levelInfoName.c_str(),
fromFileSystemOnly ? AZ::IO::IArchive::eFileLocation_OnDisk : AZ::IO::IArchive::eFileLocation_InPak))
{
ScanFolder(levelFolder.c_str(), modFolder);
continue;
}
// With the level.pak workflow, levelPath and levelName will point to a directory.
// levelPath: levels/mylevel
// levelName: mylevel
CLevelInfo levelInfo;
levelInfo.m_levelPath = levelPath;
levelInfo.m_levelName = levelFolder;
levelInfo.m_isPak = !fromFileSystemOnly;
CLevelInfo* pExistingInfo = GetLevelInfoInternal(levelInfo.m_levelName);
// Don't add the level if it is already in the list
if (pExistingInfo == NULL)
{
m_levelInfos.push_back(levelInfo);
}
else
{
// Levels in bundles take priority over levels outside bundles.
if (!pExistingInfo->m_isPak && levelInfo.m_isPak)
{
*pExistingInfo = levelInfo;
}
}
} while (handle = pPak->FindNext(handle));
pPak->FindClose(handle);
}
pPak->FindClose(handle);
}
}
@@ -116,7 +116,7 @@ private:
void ScanFolder(const char* subfolder, bool modFolder);
void PopulateLevels(
AZStd::string searchPattern, AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly);
AZStd::string searchPattern, const AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly);
void PrepareNextLevel(const char* levelName);
ILevel* LoadLevelInternal(const char* _levelName);
-38
View File
@@ -170,14 +170,6 @@ void CryEngineSignalHandler(int signal)
extern HMODULE gDLLHandle;
#endif
namespace
{
#if defined(AZ_PLATFORM_WINDOWS)
// on windows, we lock our cache using a lockfile. On other platforms this is not necessary since devices like ios, android, consoles cannot
// run more than one game process that uses the same folder anyway.
HANDLE g_cacheLock = INVALID_HANDLE_VALUE;
#endif
}
//static int g_sysSpecChanged = false;
@@ -339,9 +331,6 @@ bool CSystem::InitFileSystem()
m_pUserCallback->OnInitProgress("Initializing File System...");
}
// get the DirectInstance FileIOBase which should be the AZ::LocalFileIO
m_env.pFileIO = AZ::IO::FileIOBase::GetDirectInstance();
m_env.pCryPak = AZ::Interface<AZ::IO::IArchive>::Get();
m_env.pFileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(m_env.pCryPak, "CryPak has not been initialized on AZ::Interface");
@@ -365,33 +354,6 @@ bool CSystem::InitFileSystem()
void CSystem::ShutdownFileSystem()
{
#if defined(AZ_PLATFORM_WINDOWS)
if (g_cacheLock != INVALID_HANDLE_VALUE)
{
CloseHandle(g_cacheLock);
g_cacheLock = INVALID_HANDLE_VALUE;
}
#endif
using namespace AZ::IO;
FileIOBase* directInstance = FileIOBase::GetDirectInstance();
FileIOBase* pakInstance = FileIOBase::GetInstance();
if (directInstance == m_env.pFileIO)
{
// we only mess with file io if we own the instance that we installed.
// if we dont' own the instance, then we never configured fileIO and we should not alter it.
delete directInstance;
FileIOBase::SetDirectInstance(nullptr);
if (pakInstance != directInstance)
{
delete pakInstance;
FileIOBase::SetInstance(nullptr);
}
}
m_env.pFileIO = nullptr;
}
+4
View File
@@ -77,6 +77,10 @@ ly_add_target(
${additional_dependencies}
)
if(LY_DEFAULT_PROJECT_PATH)
set_property(TARGET AssetBundler AssetBundlerBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"")
endif()
# Adds a specialized .setreg to identify gems enabled in the active project.
# This associates the AssetBundler target with the .Builders gem variants.
ly_set_gem_variant_to_load(TARGETS AssetBundler VARIANTS Builders)
+5 -2
View File
@@ -10,5 +10,8 @@
set(LY_ASSET_DEPLOY_MODE "LOOSE" CACHE STRING "Set the Asset deployment when deploying to the target platform (LOOSE, PAK, VFS)")
set(LY_ASSET_OVERRIDE_PAK_FOLDER_ROOT "" CACHE STRING "Optional root path to where Pak file folders are stored. By default, blank will use a predefined 'paks' root.")
set(LY_ARCHIVE_FILE_SEARCH_MODE "$<$<CONFIG:release>:2>" CACHE STRING "Set the default file search mode to locate non-Pak files within the Archive System\n\
Valid values are:\n\
0 = Search FileSystem first, before searching within mounted Paks\n\
1 = Search mounted Paks first, before searching FileSystem\n\
2 = Search only mounted Paks(default in release)\n")
-30
View File
@@ -653,36 +653,6 @@ function(ly_add_source_properties)
endfunction()
#! ly_project_add_subdirectory: calls add_subdirectory() if the project name is in the project list
#
# This can be useful when including subdirs in the restricted folder only if the project is in the project list
# If you give it a second parameter it will add_subdirectory using that instead, if the project is in the project list
#
# add_subdirectory(AutomatedTesting) if Automatedtesting is in the project list
# EX. ly_project_add_subdirectory(AutomatedTesting)
#
# add_subdirectory(SamplesProject) if Automatedtesting is in the project list
# EX. ly_project_add_subdirectory(AutomatedTesting SamplesProject)
#
# \arg:project_name the name of the project that may be enabled
# \arg:binary_project_dir optional, if supplied that binary_project_dir will be added when project name is enabled.
#
function(ly_project_add_subdirectory project_name)
if(${project_name} IN_LIST LY_PROJECTS)
if(ARGC GREATER 1)
list(GET ARGN 0 subdir)
endif()
if(ARGC GREATER 2)
list(GET ARGN 1 binary_project_dir)
endif()
if(subdir)
add_subdirectory(${subdir} ${binary_project_dir})
else()
add_subdirectory(${project_name} ${binary_project_dir})
endif()
endif()
endfunction()
# given a target name, returns the "real" name of the target if its an alias.
# this function recursively de-aliases
function(ly_de_alias_target target_name output_variable_name)